Yeeesh. Listen, I’ll gladly take the W and nod along with the “found a way to win it” crowd. But this game did very little to assuage the nerves I wrote about after the Mizzou win. These close games feel unsustainable (though, I also said that a few weeks, and a few wins, ago).
I’ll give you the good news first. Well, actually, here’s the good news before the good news: Alabama managed to win this game due to some strange, arcane trajectory. Roll Tide.
The regular good news is that Alabama’s advanced metrics at least somewhat suggest that this team was better down-by-down and arguably “deserved” to win this game. For anyone that watched it live, that notion was very much in doubt even at 00:00 in the 4th, so I welcome any signal that suggests we didn’t simply luck out in this one.
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492281028-dady1knlz .data-definitions li {
margin-bottom: 4px;
}
Overall Team Performance
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492281028_dady1knlz() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492281028-dady1knlz’);
const caret = document.getElementById(‘caret_cfb-chart-1761492281028-dady1knlz’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492281028-dady1knlz’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492281028-dady1knlz’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Alabama”,
“South Carolina”
],
“datasets”: [
{
“label”: “Explosiveness Rate (XR)”,
“data”: [
0.09090909090909091,
0.07246376811594203
],
“backgroundColor”: [
“rgba(101, 0, 20, 0.8)”,
“#1e2938CC”
],
“stack”: “SRXR”,
“datalabels”: {
“display”: false
}
},
{
“label”: “Success Rate (SR)”,
“data”: [
0.42424242424242425,
0.391304347826087
],
“backgroundColor”: [
“rgba(175, 40, 60, 0.8)”,
“#64748BCC”
],
“stack”: “SRXR”,
“datalabels”: {
“display”: true
},
“playCountData”: [
66,
69
]
},
{
“type”: “line”,
“data”: [
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamColors”: {
“success”: “rgba(175, 40, 60, 0.8)”,
“explosive”: “rgba(101, 0, 20, 0.8)”,
“light”: “rgba(245, 229, 233, 0.8)”
},
“opponentColors”: {
“success”: “#64748BCC”,
“explosive”: “#1e2938CC”,
“light”: “#F1F5F9CC”,
“color”: “#64748B”,
“colorDark”: “#1e2938”
},
“teamPlayCount”: 66,
“opponentPlayCount”: 69,
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘overall-team-performance’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘overall-team-performance’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘overall-team-performance’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘overall-team-performance’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘overall-team-performance’.includes(‘top-rushers’) || ‘overall-team-performance’.includes(‘top-passers’) || ‘overall-team-performance’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘overall-team-performance’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘overall-team-performance’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘overall-team-performance’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘overall-team-performance’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492281028-dady1knlz’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
And by “deserved to win” I’m referring to that whoppin’ 3% net SR advantage and 2% XR advantage. Hah. Yep, it was another close one, folks.
But the play counts were relatively even, the Bama defense held another opponent to below-average SR and XR (as well as evening things back out with turnovers), and the offense put together drives at just the right times.
But it was rocky:
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492242485-9yk527tss .data-definitions li {
margin-bottom: 4px;
}
Win Probability
Alabama vs. South Carolina • Oct 25, 2025
- Win Probability: Likelihood of winning based on game situation
- Line Color: Gradient reflects which team is favored
- 50% Line: Dashed line indicates even odds
- Data Source: CollegeFootballData.com win probability models
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492242485_9yk527tss() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492242485-9yk527tss’);
const caret = document.getElementById(‘caret_cfb-chart-1761492242485-9yk527tss’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492242485-9yk527tss’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492242485-9yk527tss’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
126,
127,
128,
129,
130,
131,
132,
133,
134,
135,
136,
137,
138,
139,
140,
141,
142,
143,
144,
145,
146,
147,
148,
149,
150,
151,
152,
153,
154,
155,
156,
157
],
“datasets”: [
{
“label”: “Win Probability”,
“data”: [
{
“x”: 0,
“y”: 42.15526580810547
},
{
“x”: 1,
“y”: 44.322049617767334
},
{
“x”: 2,
“y”: 42.35164523124695
},
{
“x”: 3,
“y”: 38.86256217956543
},
{
“x”: 4,
“y”: 39.973437786102295
},
{
“x”: 5,
“y”: 38.944703340530396
},
{
“x”: 6,
“y”: 35.61159372329712
},
{
“x”: 7,
“y”: 32.56925940513611
},
{
“x”: 8,
“y”: 33.395713567733765
},
{
“x”: 9,
“y”: 34.909600019454956
},
{
“x”: 10,
“y”: 32.04835057258606
},
{
“x”: 11,
“y”: 33.948081731796265
},
{
“x”: 12,
“y”: 30.391454696655273
},
{
“x”: 13,
“y”: 34.11886692047119
},
{
“x”: 14,
“y”: 36.44614815711975
},
{
“x”: 15,
“y”: 31.31236433982849
},
{
“x”: 16,
“y”: 38.83746862411499
},
{
“x”: 17,
“y”: 41.21514558792114
},
{
“x”: 18,
“y”: 38.54444622993469
},
{
“x”: 19,
“y”: 36.68826222419739
},
{
“x”: 20,
“y”: 55.664196610450745
},
{
“x”: 21,
“y”: 55.736708641052246
},
{
“x”: 22,
“y”: 61.56398057937622
},
{
“x”: 23,
“y”: 56.084296107292175
},
{
“x”: 24,
“y”: 57.383689284324646
},
{
“x”: 25,
“y”: 60.48196256160736
},
{
“x”: 26,
“y”: 58.9338481426239
},
{
“x”: 27,
“y”: 60.166165232658386
},
{
“x”: 28,
“y”: 58.2489937543869
},
{
“x”: 29,
“y”: 55.63254654407501
},
{
“x”: 30,
“y”: 55.48655688762665
},
{
“x”: 31,
“y”: 59.845033288002014
},
{
“x”: 32,
“y”: 47.226399183273315
},
{
“x”: 33,
“y”: 48.088061809539795
},
{
“x”: 34,
“y”: 53.44211161136627
},
{
“x”: 35,
“y”: 51.702260971069336
},
{
“x”: 36,
“y”: 65.75185358524323
},
{
“x”: 37,
“y”: 60.47482192516327
},
{
“x”: 38,
“y”: 57.99338519573212
},
{
“x”: 39,
“y”: 56.255897879600525
},
{
“x”: 40,
“y”: 57.82458484172821
},
{
“x”: 41,
“y”: 67.13235676288605
},
{
“x”: 42,
“y”: 68.31838190555573
},
{
“x”: 43,
“y”: 54.64305281639099
},
{
“x”: 44,
“y”: 49.712228775024414
},
{
“x”: 45,
“y”: 51.59927308559418
},
{
“x”: 46,
“y”: 51.597124338150024
},
{
“x”: 47,
“y”: 52.64238715171814
},
{
“x”: 48,
“y”: 47.70805239677429
},
{
“x”: 49,
“y”: 52.94292867183685
},
{
“x”: 50,
“y”: 57.16926455497742
},
{
“x”: 51,
“y”: 61.607176065444946
},
{
“x”: 52,
“y”: 48.90644550323486
},
{
“x”: 53,
“y”: 51.988452672958374
},
{
“x”: 54,
“y”: 48.90644550323486
},
{
“x”: 55,
“y”: 52.81991958618164
},
{
“x”: 56,
“y”: 53.09106111526489
},
{
“x”: 57,
“y”: 57.14867115020752
},
{
“x”: 58,
“y”: 51.575419306755066
},
{
“x”: 59,
“y”: 48.084402084350586
},
{
“x”: 60,
“y”: 49.50265884399414
},
{
“x”: 61,
“y”: 52.00361907482147
},
{
“x”: 62,
“y”: 52.10144221782684
},
{
“x”: 63,
“y”: 49.495720863342285
},
{
“x”: 64,
“y”: 56.12340867519379
},
{
“x”: 65,
“y”: 54.77755963802338
},
{
“x”: 66,
“y”: 52.41632163524628
},
{
“x”: 67,
“y”: 70.84279358386993
},
{
“x”: 68,
“y”: 72.69540727138519
},
{
“x”: 69,
“y”: 76.25795751810074
},
{
“x”: 70,
“y”: 77.30607092380524
},
{
“x”: 71,
“y”: 78.87708246707916
},
{
“x”: 72,
“y”: 76.88715755939484
},
{
“x”: 73,
“y”: 75.885771214962
},
{
“x”: 74,
“y”: 76.2028157711029
},
{
“x”: 75,
“y”: 77.94379740953445
},
{
“x”: 76,
“y”: 77.11517065763474
},
{
“x”: 77,
“y”: 77.5869533419609
},
{
“x”: 78,
“y”: 76.75086110830307
},
{
“x”: 79,
“y”: 78.87355387210846
},
{
“x”: 80,
“y”: 77.05426961183548
},
{
“x”: 81,
“y”: 69.87288296222687
},
{
“x”: 82,
“y”: 77.04029828310013
},
{
“x”: 83,
“y”: 76.90393924713135
},
{
“x”: 84,
“y”: 75.79723745584488
},
{
“x”: 85,
“y”: 77.95905768871307
},
{
“x”: 86,
“y”: 79.37893569469452
},
{
“x”: 87,
“y”: 77.68842279911041
},
{
“x”: 88,
“y”: 77.24185734987259
},
{
“x”: 89,
“y”: 49.71565008163452
},
{
“x”: 90,
“y”: 51.15083456039429
},
{
“x”: 91,
“y”: 48.99203181266785
},
{
“x”: 92,
“y”: 47.2136914730072
},
{
“x”: 93,
“y”: 46.36914134025574
},
{
“x”: 94,
“y”: 54.85349893569946
},
{
“x”: 95,
“y”: 56.52587115764618
},
{
“x”: 96,
“y”: 57.44107961654663
},
{
“x”: 97,
“y”: 52.498602867126465
},
{
“x”: 98,
“y”: 53.124046325683594
},
{
“x”: 99,
“y”: 60.24300456047058
},
{
“x”: 100,
“y”: 56.72388672828674
},
{
“x”: 101,
“y”: 50.706714391708374
},
{
“x”: 102,
“y”: 53.04785668849945
},
{
“x”: 103,
“y”: 46.423834562301636
},
{
“x”: 104,
“y”: 47.06753492355347
},
{
“x”: 105,
“y”: 46.222984790802
},
{
“x”: 106,
“y”: 54.32405769824982
},
{
“x”: 107,
“y”: 54.23210859298706
},
{
“x”: 108,
“y”: 51.81010067462921
},
{
“x”: 109,
“y”: 52.55621373653412
},
{
“x”: 110,
“y”: 46.95979356765747
},
{
“x”: 111,
“y”: 46.65535092353821
},
{
“x”: 112,
“y”: 50.191983580589294
},
{
“x”: 113,
“y”: 45.71557044982908
},
{
“x”: 114,
“y”: 49.493688344955636
},
{
“x”: 115,
“y”: 46.70282602310159
},
{
“x”: 116,
“y”: 49.65910911560055
},
{
“x”: 117,
“y”: 34.108710288999845
},
{
“x”: 118,
“y”: 33.529084920883356
},
{
“x”: 119,
“y”: 31.161600351332773
},
{
“x”: 120,
“y”: 30.494034290319416
},
{
“x”: 121,
“y”: 29.413139820091207
},
{
“x”: 122,
“y”: 37.55573034300309
},
{
“x”: 123,
“y”: 36.32325530064045
},
{
“x”: 124,
“y”: 37.25196123235801
},
{
“x”: 125,
“y”: 42.75588393558907
},
{
“x”: 126,
“y”: 27.229321002956652
},
{
“x”: 127,
“y”: 28.958827255330878
},
{
“x”: 128,
“y”: 29.86444832484578
},
{
“x”: 129,
“y”: 27.38486531068235
},
{
“x”: 130,
“y”: 27.589482358592075
},
{
“x”: 131,
“y”: 9.05816572806566
},
{
“x”: 132,
“y”: 13.064524706007251
},
{
“x”: 133,
“y”: 13.827443074817813
},
{
“x”: 134,
“y”: 13.113923284862118
},
{
“x”: 135,
“y”: 11.869942515796172
},
{
“x”: 136,
“y”: 10.838494363424257
},
{
“x”: 137,
“y”: 10.154864967193511
},
{
“x”: 138,
“y”: 10.658014367711488
},
{
“x”: 139,
“y”: 10.925002165069708
},
{
“x”: 140,
“y”: 10.995719084859545
},
{
“x”: 141,
“y”: 11.114496236964932
},
{
“x”: 142,
“y”: 11.38500182160509
},
{
“x”: 143,
“y”: 9.903846772983727
},
{
“x”: 144,
“y”: 11.63120143874271
},
{
“x”: 145,
“y”: 60.43355492989817
},
{
“x”: 146,
“y”: 41.90048575401306
},
{
“x”: 147,
“y”: 41.119443100836136
},
{
“x”: 148,
“y”: 33.66186708194554
},
{
“x”: 149,
“y”: 72.00937991824507
},
{
“x”: 150,
“y”: 69.26181697123599
},
{
“x”: 151,
“y”: 55.00218006269695
},
{
“x”: 152,
“y”: 52.30086754324488
},
{
“x”: 153,
“y”: 84.07519422006287
},
{
“x”: 154,
“y”: 96.43955003245553
},
{
“x”: 155,
“y”: 94.99226437074573
},
{
“x”: 156,
“y”: 96.44931165959058
},
{
“x”: 157,
“y”: 100
}
],
“borderColor”: “#af283c”,
“backgroundColor”: “transparent”,
“pointBackgroundColor”: [
“rgb(100, 116, 139)”,
“rgb(109, 107, 129)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(166, 49, 70)”,
“rgb(166, 49, 69)”,
“rgb(175, 40, 60)”,
“rgb(168, 47, 67)”,
“rgb(174, 41, 61)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(166, 49, 70)”,
“rgb(165, 50, 71)”,
“rgb(175, 40, 60)”,
“rgb(124, 92, 114)”,
“rgb(128, 88, 110)”,
“rgb(155, 61, 81)”,
“rgb(146, 69, 91)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(169, 46, 67)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(161, 54, 75)”,
“rgb(136, 79, 101)”,
“rgb(145, 70, 91)”,
“rgb(145, 70, 91)”,
“rgb(151, 65, 86)”,
“rgb(126, 90, 112)”,
“rgb(152, 63, 84)”,
“rgb(173, 42, 62)”,
“rgb(175, 40, 60)”,
“rgb(132, 84, 105)”,
“rgb(147, 68, 89)”,
“rgb(132, 84, 105)”,
“rgb(152, 64, 85)”,
“rgb(153, 62, 83)”,
“rgb(173, 42, 62)”,
“rgb(145, 70, 91)”,
“rgb(128, 88, 110)”,
“rgb(135, 81, 102)”,
“rgb(148, 68, 89)”,
“rgb(148, 67, 88)”,
“rgb(135, 81, 102)”,
“rgb(168, 47, 67)”,
“rgb(161, 54, 74)”,
“rgb(150, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(136, 79, 101)”,
“rgb(143, 72, 93)”,
“rgb(132, 83, 105)”,
“rgb(124, 92, 114)”,
“rgb(119, 96, 119)”,
“rgb(162, 53, 74)”,
“rgb(170, 45, 65)”,
“rgb(175, 40, 60)”,
“rgb(150, 65, 86)”,
“rgb(153, 62, 83)”,
“rgb(175, 40, 60)”,
“rgb(171, 44, 64)”,
“rgb(141, 74, 96)”,
“rgb(153, 63, 83)”,
“rgb(120, 96, 118)”,
“rgb(123, 93, 115)”,
“rgb(119, 97, 119)”,
“rgb(159, 56, 77)”,
“rgb(159, 57, 77)”,
“rgb(147, 69, 90)”,
“rgb(150, 65, 86)”,
“rgb(122, 93, 116)”,
“rgb(121, 95, 117)”,
“rgb(138, 77, 98)”,
“rgb(116, 100, 122)”,
“rgb(135, 81, 102)”,
“rgb(121, 95, 117)”,
“rgb(136, 80, 101)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(101, 115, 138)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(163, 53, 73)”,
“rgb(149, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”
],
“pointBorderColor”: [
“rgb(100, 116, 139)”,
“rgb(109, 107, 129)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(166, 49, 70)”,
“rgb(166, 49, 69)”,
“rgb(175, 40, 60)”,
“rgb(168, 47, 67)”,
“rgb(174, 41, 61)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(166, 49, 70)”,
“rgb(165, 50, 71)”,
“rgb(175, 40, 60)”,
“rgb(124, 92, 114)”,
“rgb(128, 88, 110)”,
“rgb(155, 61, 81)”,
“rgb(146, 69, 91)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(169, 46, 67)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(161, 54, 75)”,
“rgb(136, 79, 101)”,
“rgb(145, 70, 91)”,
“rgb(145, 70, 91)”,
“rgb(151, 65, 86)”,
“rgb(126, 90, 112)”,
“rgb(152, 63, 84)”,
“rgb(173, 42, 62)”,
“rgb(175, 40, 60)”,
“rgb(132, 84, 105)”,
“rgb(147, 68, 89)”,
“rgb(132, 84, 105)”,
“rgb(152, 64, 85)”,
“rgb(153, 62, 83)”,
“rgb(173, 42, 62)”,
“rgb(145, 70, 91)”,
“rgb(128, 88, 110)”,
“rgb(135, 81, 102)”,
“rgb(148, 68, 89)”,
“rgb(148, 67, 88)”,
“rgb(135, 81, 102)”,
“rgb(168, 47, 67)”,
“rgb(161, 54, 74)”,
“rgb(150, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(136, 79, 101)”,
“rgb(143, 72, 93)”,
“rgb(132, 83, 105)”,
“rgb(124, 92, 114)”,
“rgb(119, 96, 119)”,
“rgb(162, 53, 74)”,
“rgb(170, 45, 65)”,
“rgb(175, 40, 60)”,
“rgb(150, 65, 86)”,
“rgb(153, 62, 83)”,
“rgb(175, 40, 60)”,
“rgb(171, 44, 64)”,
“rgb(141, 74, 96)”,
“rgb(153, 63, 83)”,
“rgb(120, 96, 118)”,
“rgb(123, 93, 115)”,
“rgb(119, 97, 119)”,
“rgb(159, 56, 77)”,
“rgb(159, 57, 77)”,
“rgb(147, 69, 90)”,
“rgb(150, 65, 86)”,
“rgb(122, 93, 116)”,
“rgb(121, 95, 117)”,
“rgb(138, 77, 98)”,
“rgb(116, 100, 122)”,
“rgb(135, 81, 102)”,
“rgb(121, 95, 117)”,
“rgb(136, 80, 101)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(101, 115, 138)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(163, 53, 73)”,
“rgb(149, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”
],
“pointRadius”: 0,
“pointHoverRadius”: 4,
“tension”: 0.15,
“borderWidth”: 2.2,
“fill”: false,
“selectedTeam”: “Alabama”,
“opponentTeam”: “South Carolina”,
“isSelectedTeamHome”: false,
“playTexts”: [
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers pass complete to Rahsul Faison for 6 yds to the SC 26”,
“Alabama Penalty, Defensive Offside (5 Yards) to the SC 31 for a 1ST down”,
“Rahsul Faison run for 2 yds to the SC 33”,
“LaNorris Sellers run for 7 yds to the SC 40”,
“LaNorris Sellers run for 10 yds to the 50 yard line for a 1ST down”,
“LaNorris Sellers pass complete to Rahsul Faison for 12 yds to the ALA 38 for a 1ST down”,
“LaNorris Sellers pass complete to Matt Fuller for 8 yds to the ALA 30”,
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers run for 2 yds to the ALA 28 for a 1ST down”,
“LaNorris Sellers run for 1 yd to the ALA 27”,
“LaNorris Sellers pass complete to Donovan Murph for 15 yds to the ALA 12 for a 1ST down”,
“Matt Fuller run for 4 yds to the ALA 8”,
“Matt Fuller run for 4 yds to the ALA 4”,
“Rahsul Faison run for a loss of 2 yards to the ALA 6”,
“William Joyce 24 yd FG GOOD”,
“Germie Bernard run for 4 yds to the ALA 39”,
“Jam Miller run for 2 yds to the ALA 41”,
“Ty Simpson sacked by Bryan Thomas Jr. for a loss of 1 yard to the ALA 40”,
“Blake Doud punt for 51 yds , Vicari Swain returns for 9 yds to the SC 18”,
“LaNorris Sellers pass intercepted DaShawn Jones return for 18 yds for a TD (Conor Talty KICK)”,
“Matt Fuller run for a loss of 3 yards to the SC 22”,
“Jayden Sellers run for 9 yds to the SC 31”,
“LaNorris Sellers pass incomplete”,
“Mason Love punt for 49 yds, fair catch by Cole Adams at the ALA 20”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Rico Scott for 6 yds to the ALA 26”,
“Ty Simpson pass incomplete”,
“Blake Doud punt for 59 yds , Vicari Swain returns for 8 yds to the SC 23”,
“Rahsul Faison run for 13 yds to the SC 36 for a 1ST down”,
“South Carolina Penalty, False Start (-5 Yards) to the SC 31”,
“LaNorris Sellers pass complete to Rahsul Faison for 33 yds to the ALA 36 for a 1ST down”,
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers run for 1 yd to the ALA 35”,
“LaNorris Sellers pass complete to Donovan Murph for 6 yds to the ALA 29”,
“William Joyce 47 yd FG MISSED”,
“Germie Bernard run for a loss of 6 yards to the ALA 23”,
“Jam Miller run for 2 yds to the ALA 25 Alabama Penalty, Offensive Holding (-10 Yards) to the ALA 15”,
“Ty Simpson pass incomplete”,
“South Carolina Penalty, Substitution Infraction (5 Yards) to the ALA 20”,
“Ty Simpson pass complete to Daniel Hill for 21 yds to the ALA 41 for a 1ST down”,
“Ty Simpson pass complete to Ryan Williams for 25 yds to the SC 34 for a 1ST down”,
“Ty Simpson sacked by Vicari Swain for a loss of 8 yards to the SC 42 Ty Simpson fumbled, recovered by SC Troy Pikes , return for 0 yards”,
“LaNorris Sellers pass complete to Brady Hunt for 14 yds to the ALA 44 for a 1ST down”,
“Matt Fuller run for 1 yd to the ALA 43”,
“LaNorris Sellers pass complete to Brady Hunt for 8 yds to the ALA 35”,
“Matt Fuller run for no gain to the ALA 35”,
“Matt Fuller run for 2 yds to the ALA 33 for a 1ST down”,
“LaNorris Sellers run for a loss of 4 yards to the ALA 37”,
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers run for 2 yds to the ALA 35”,
“LaNorris Sellers pass complete to Jayden Sellers for 22 yds to the ALA 13 for a 1ST down”,
“South Carolina Penalty, Offensive Holding (-10 Yards) to the ALA 23”,
“LaNorris Sellers pass complete to Donovan Murph for no gain to the ALA 23 for a 1ST down Alabama Penalty, Unnecessary Roughness (12 Yards) to the ALA 11 for a 1ST down”,
“South Carolina Penalty, Offensive Holding (-10 Yards) to the ALA 21”,
“Matt Fuller run for a loss of 3 yards to the ALA 24”,
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers pass complete to Jordan Dingle for 8 yds to the ALA 16”,
“William Joyce 34 yd FG GOOD”,
“Ty Simpson pass complete to Germie Bernard for 11 yds to the ALA 30 for a 1ST down”,
“Ty Simpson pass complete to Ryan Williams for 18 yds to the ALA 48 for a 1ST down”,
“Jam Miller run for 4 yds to the SC 48”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Daniel Hill for 41 yds to the SC 7 for a 1ST down”,
“Ty Simpson run for 4 yds to the SC 3”,
“Kadyn Proctor run for no gain to the SC 3”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Josh Cuevas for 3 yds for a TD (Conor Talty KICK)”,
“LaNorris Sellers sacked by Justin Jefferson for a loss of 9 yards to the SC 16”,
“LaNorris Sellers pass incomplete”,
“LaNorris Sellers pass incomplete”,
“Mason Love punt for 49 yds, punt out-of-bounds at the ALA 35”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Josh Cuevas for 8 yds to the ALA 43”,
“Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 48 for a 1ST down”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Daniel Hill for 9 yds to the SC 43”,
“Ty Simpson pass incomplete”,
“Ty Simpson run for 2 yds to the SC 41 for a 1ST down”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass complete to Germie Bernard for 23 yds to the SC 18 for a 1ST down”,
“Jam Miller run for 4 yds to the ALA 39”,
“Jam Miller run for 3 yds to the ALA 42”,
“Ty Simpson pass complete to Germie Bernard for 3 yds to the ALA 45 for a 1ST down”,
“Ty Simpson run for 9 yds to the SC 46”,
“Ty Simpson pass incomplete”,
“Daniel Hill run for no gain to the SC 46”,
“Ty Simpson pass incomplete to Ryan Williams”,
“LaNorris Sellers pass complete to Nyck Harbor for 54 yds for a TD Timeout South Carolina, clock 10:59 (Two-Point Conversion failed)”,
“Daniel Hill run for a loss of 1 yard to the ALA 24”,
“Ty Simpson pass incomplete”,
“Ty Simpson pass incomplete”,
“Blake Doud punt for 60 yds, downed at the SC 16”,
“LaNorris Sellers pass incomplete”,
“Rahsul Faison run for 3 yds to the SC 19”,
“LaNorris Sellers pass complete to Jordan Dingle for 12 yds to the SC 31 for a 1ST down”,
“Rahsul Faison run for 1 yd to the SC 32”,
“LaNorris Sellers pass incomplete”,
“Mason Love punt for 43 yds, punt out-of-bounds at the ALA 25”,
“LaNorris Sellers pass incomplete”,
“Ty Simpson pass complete to Ryan Williams for 7 yds to the ALA 32”,
“Simpson, Ty pass complete to Miller, Jam for 5 yards to the ALA37, PENALTY ALA personal foul 15 yards to the ALA17, NO PLAY.”,
“Ty Simpson pass complete to Jam Miller for 7 yds to the ALA 24”,
“Ty Simpson pass incomplete”,
“Blake Doud punt for 51 yds, punt out-of-bounds at the SC 25”,
“LaNorris Sellers run for 5 yds to the SC 30”,
“LaNorris Sellers pass complete to Andrew Colasurdo for 5 yds to the SC 35 for a 1ST down”,
“LaNorris Sellers run for 2 yds to the SC 37”,
“LaNorris Sellers run for 17 yds to the ALA 46 for a 1ST down”,
“LaNorris Sellers pass complete to Jayden Sellers for 7 yds to the ALA 39”,
“LaNorris Sellers pass incomplete”,
“Oscar Adaway III run for 6 yds to the ALA 33 for a 1ST down”,
“South Carolina Penalty, Offensive Holding (-10 Yards) to the ALA 43”,
“LaNorris Sellers run for 12 yds to the ALA 31”,
“Oscar Adaway III run for 2 yds to the ALA 29”,
“LaNorris Sellers pass incomplete”,
“William Joyce 47 yd FG GOOD”,
“Jam Miller run for a loss of 2 yards to the ALA 23”,
“Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 28”,
“Ty Simpson pass incomplete to Germie Bernard”,
“Blake Doud punt for 56 yds, downed at the SC 16”,
“LaNorris Sellers run for 6 yds to the SC 22”,
“LaNorris Sellers run for no gain to the SC 22”,
“LaNorris Sellers pass complete to Rahsul Faison for a loss of 4 yards to the SC 18”,
“Mason Love punt for 47 yds , Keon Sabb returns for a loss of 5 yards to the ALA 30 Keon Sabb fumbled, recovered by SC Nyck Harbor”,
“Matt Fuller run for 2 yds to the ALA 28”,
“LaNorris Sellers run for 7 yds to the ALA 21”,
“Matt Fuller run for 2 yds to the ALA 19 for a 1ST down”,
“Matt Fuller run for 8 yds to the ALA 11”,
“Matt Fuller run for 1 yd to the ALA 10”,
“LaNorris Sellers run for 10 yds for a TD (William Joyce KICK)”,
“Jam Miller run for 3 yds to the ALA 24”,
“Ty Simpson pass incomplete to Germie Bernard”,
“Ty Simpson pass complete to Germie Bernard for 13 yds to the ALA 37 for a 1ST down”,
“Jam Miller run for 6 yds to the ALA 43”,
“Ty Simpson pass incomplete to Germie Bernard”,
“Ty Simpson pass complete to Josh Cuevas for 5 yds to the ALA 48 for a 1ST down”,
“Daniel Hill run for 11 yds to the SC 41 for a 1ST down”,
“Ty Simpson pass complete to Ryan Williams for 11 yds to the SC 30 for a 1ST down”,
“Ty Simpson pass complete to Daniel Hill for 5 yds to the SC 25”,
“Ty Simpson pass complete to Jam Miller for 10 yds to the SC 15 for a 1ST down”,
“Ty Simpson run for 7 yds to the SC 8”,
“Ty Simpson pass complete to Jam Miller for 6 yds to the SC 2 for a 1ST down”,
“Jam Miller run for a loss of 2 yards to the SC 4”,
“Ty Simpson pass complete to Germie Bernard for 4 yds for a TD (Ty Simpson pass to Josh Cuevas for Two-Point Conversion)”,
“Matt Fuller run for 1 yd to the SC 31”,
“Sellers, LaNorris pass incomplete, PENALTY ALA offside (Smith, James) 5 yards to the SC36, NO PLAY.”,
“LaNorris Sellers run for 2 yds to the SC 38 LaNorris Sellers fumbled, forced by Deontae Lawson, recovered by ALA Tim Keenan III”,
“Jam Miller run for 7 yds to the SC 31”,
“Ty Simpson pass complete to Rico Scott for 6 yds to the SC 25 for a 1ST down”,
“Ty Simpson pass complete to Ryan Williams for no gain to the SC 25”,
“Ty Simpson pass incomplete to Rico Scott”,
“Germie Bernard run for 25 yds for a TD (Conor Talty KICK)”,
“LaNorris Sellers pass complete to Brady Hunt for 7 yds to the SC 16”,
“TEAM pass incomplete”,
“LaNorris Sellers sacked by Keon Keeley for a loss of 4 yards to the SC 12”,
“Game ended”
],
“segmentColors”: [
“rgb(100, 116, 139)”,
“rgb(109, 107, 129)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(166, 49, 70)”,
“rgb(166, 49, 69)”,
“rgb(175, 40, 60)”,
“rgb(168, 47, 67)”,
“rgb(174, 41, 61)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(166, 49, 70)”,
“rgb(165, 50, 71)”,
“rgb(175, 40, 60)”,
“rgb(124, 92, 114)”,
“rgb(128, 88, 110)”,
“rgb(155, 61, 81)”,
“rgb(146, 69, 91)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(169, 46, 67)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(161, 54, 75)”,
“rgb(136, 79, 101)”,
“rgb(145, 70, 91)”,
“rgb(145, 70, 91)”,
“rgb(151, 65, 86)”,
“rgb(126, 90, 112)”,
“rgb(152, 63, 84)”,
“rgb(173, 42, 62)”,
“rgb(175, 40, 60)”,
“rgb(132, 84, 105)”,
“rgb(147, 68, 89)”,
“rgb(132, 84, 105)”,
“rgb(152, 64, 85)”,
“rgb(153, 62, 83)”,
“rgb(173, 42, 62)”,
“rgb(145, 70, 91)”,
“rgb(128, 88, 110)”,
“rgb(135, 81, 102)”,
“rgb(148, 68, 89)”,
“rgb(148, 67, 88)”,
“rgb(135, 81, 102)”,
“rgb(168, 47, 67)”,
“rgb(161, 54, 74)”,
“rgb(150, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(136, 79, 101)”,
“rgb(143, 72, 93)”,
“rgb(132, 83, 105)”,
“rgb(124, 92, 114)”,
“rgb(119, 96, 119)”,
“rgb(162, 53, 74)”,
“rgb(170, 45, 65)”,
“rgb(175, 40, 60)”,
“rgb(150, 65, 86)”,
“rgb(153, 62, 83)”,
“rgb(175, 40, 60)”,
“rgb(171, 44, 64)”,
“rgb(141, 74, 96)”,
“rgb(153, 63, 83)”,
“rgb(120, 96, 118)”,
“rgb(123, 93, 115)”,
“rgb(119, 97, 119)”,
“rgb(159, 56, 77)”,
“rgb(159, 57, 77)”,
“rgb(147, 69, 90)”,
“rgb(150, 65, 86)”,
“rgb(122, 93, 116)”,
“rgb(121, 95, 117)”,
“rgb(138, 77, 98)”,
“rgb(116, 100, 122)”,
“rgb(135, 81, 102)”,
“rgb(121, 95, 117)”,
“rgb(136, 80, 101)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(101, 115, 138)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(100, 116, 139)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(163, 53, 73)”,
“rgb(149, 66, 87)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”,
“rgb(175, 40, 60)”
]
},
{
“label”: “50% Line”,
“data”: [
{
“x”: 0,
“y”: 50
},
{
“x”: 157,
“y”: 50
}
],
“borderColor”: “rgba(0,0,0,0.3)”,
“borderWidth”: 1.5,
“borderDash”: [
5,
5
],
“backgroundColor”: “transparent”,
“pointRadius”: 0,
“showLine”: true,
“fill”: false,
“tension”: 0,
“order”: 90,
“datalabels”: {
“display”: false
}
},
{
“label”: “Quarters”,
“data”: [
{
“x”: 35,
“y”: 0
},
{
“x”: 35,
“y”: 100
},
{
“x”: 157,
“y”: 100
},
{
“x”: 157,
“y”: 0
},
{
“x”: 82,
“y”: 0
},
{
“x”: 82,
“y”: 100
},
{
“x”: 157,
“y”: 100
},
{
“x”: 157,
“y”: 0
},
{
“x”: 123,
“y”: 0
},
{
“x”: 123,
“y”: 100
},
{
“x”: 157,
“y”: 100
},
{
“x”: 157,
“y”: 0
}
],
“borderColor”: “rgba(0,0,0,0.1)”,
“borderWidth”: 1,
“pointRadius”: 0,
“showLine”: true,
“fill”: false,
“tension”: 0,
“order”: 100,
“datalabels”: {
“display”: false
}
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘line’ === ‘line’ ? ‘win-probability’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘win-probability’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘win-probability’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘line’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘win-probability’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘win-probability’.includes(‘top-rushers’) || ‘win-probability’.includes(‘top-passers’) || ‘win-probability’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘win-probability’ === ‘win-probability’ ? {
display: false
} : ‘line’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘win-probability’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘win-probability’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘win-probability’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘line’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492242485-9yk527tss’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
The “Game Excitement” index calls this game a 6.9, which is very high. Compare that to …
- 4.6 against Tennessee
- 6.8 against Mizzou
- 5.8 against Vandy
- 6.3 against Georgia
- 3.0 against Wisconsin (lol)
And the Win Probability chart spends plenty of time near the 50/50 of the chart. Alabama had a chance to close this thing out coming out of halftime (heard that one before?), but SC quickly got back into it and had a decisive advantage late.
In fact, it reminds me vaguely of the Win Probability chart from last year’s game, which I posted to The Feed earlier this week.
The offense was a stuggle bus
So let’s get into the tough stuff: this offense didn’t really get off the mat until pretty late in the game.
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions li {
margin-bottom: 4px;
}
SR by Play Type: Alabama
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- Gray area: Represents 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761442365383_3yhzpwjan() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761442365383-3yhzpwjan’);
const caret = document.getElementById(‘caret_cfb-chart-1761442365383-3yhzpwjan’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“datasets”: [
{
“label”: “NCAA Avg SR”,
“data”: [
{
“x”: 1,
“y”: 0
},
{
“x”: 1,
“y”: 0.42
},
{
“x”: 66,
“y”: 0.42
},
{
“x”: 66,
“y”: 0
}
],
“backgroundColor”: “rgba(0,0,0,0.03)”,
“borderColor”: “transparent”,
“pointRadius”: 0,
“fill”: true,
“tension”: 0,
“showLine”: true,
“datalabels”: {
“display”: false
}
},
{
“data”: [
{
“x”: 1,
“y”: 0,
“text”: “Germie Bernard run for 4 yds to the ALA 39”
},
{
“x”: 2,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 41”
},
{
“x”: 3,
“y”: 0,
“text”: “Ty Simpson sacked by Bryan Thomas Jr. for a loss of 1 yard to the ALA 40”
},
{
“x”: 4,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 5,
“y”: 0,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the ALA 26”
},
{
“x”: 6,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 7,
“y”: 0,
“text”: “Germie Bernard run for a loss of 6 yards to the ALA 23”
},
{
“x”: 8,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 25 Alabama Penalty, Offensive Holding (-10 Yards) to the ALA 15”
},
{
“x”: 9,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 10,
“y”: 0,
“text”: “Ty Simpson pass complete to Daniel Hill for 21 yds to the ALA 41 for a 1ST down”
},
{
“x”: 11,
“y”: 0,
“text”: “Ty Simpson pass complete to Ryan Williams for 25 yds to the SC 34 for a 1ST down”
},
{
“x”: 12,
“y”: 0,
“text”: “Ty Simpson sacked by Vicari Swain for a loss of 8 yards to the SC 42 Ty Simpson fumbled, recovered by SC Troy Pikes , return for 0 yards”
},
{
“x”: 13,
“y”: 0,
“text”: “Ty Simpson pass complete to Germie Bernard for 11 yds to the ALA 30 for a 1ST down”
},
{
“x”: 14,
“y”: 0,
“text”: “Ty Simpson pass complete to Ryan Williams for 18 yds to the ALA 48 for a 1ST down”
},
{
“x”: 15,
“y”: 0,
“text”: “Jam Miller run for 4 yds to the SC 48”
},
{
“x”: 16,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 17,
“y”: 0,
“text”: “Ty Simpson pass complete to Daniel Hill for 41 yds to the SC 7 for a 1ST down”
},
{
“x”: 18,
“y”: 0.16666666666666666,
“text”: “Ty Simpson run for 4 yds to the SC 3”
},
{
“x”: 19,
“y”: 0.14285714285714285,
“text”: “Kadyn Proctor run for no gain to the SC 3”
},
{
“x”: 20,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 21,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Josh Cuevas for 3 yds for a TD (Conor Talty KICK)”
},
{
“x”: 22,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 23,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Josh Cuevas for 8 yds to the ALA 43”
},
{
“x”: 24,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 25,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 26,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Daniel Hill for 9 yds to the SC 43”
},
{
“x”: 27,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 28,
“y”: 0.25,
“text”: “Ty Simpson run for 2 yds to the SC 41 for a 1ST down”
},
{
“x”: 29,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 30,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 31,
“y”: 0.25,
“text”: “Ty Simpson pass complete to Germie Bernard for 23 yds to the SC 18 for a 1ST down”
},
{
“x”: 32,
“y”: 0.2222222222222222,
“text”: “Jam Miller run for 4 yds to the ALA 39”
},
{
“x”: 33,
“y”: 0.2,
“text”: “Jam Miller run for 3 yds to the ALA 42”
},
{
“x”: 34,
“y”: 0.2,
“text”: “Ty Simpson pass complete to Germie Bernard for 3 yds to the ALA 45 for a 1ST down”
},
{
“x”: 35,
“y”: 0.2727272727272727,
“text”: “Ty Simpson run for 9 yds to the SC 46”
},
{
“x”: 36,
“y”: 0.2727272727272727,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 37,
“y”: 0.25,
“text”: “Daniel Hill run for no gain to the SC 46”
},
{
“x”: 38,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete to Ryan Williams”
},
{
“x”: 39,
“y”: 0.23076923076923078,
“text”: “Daniel Hill run for a loss of 1 yard to the ALA 24”
},
{
“x”: 40,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 41,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 42,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass complete to Ryan Williams for 7 yds to the ALA 32”
},
{
“x”: 43,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass complete to Jam Miller for 7 yds to the ALA 24”
},
{
“x”: 44,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 45,
“y”: 0.21428571428571427,
“text”: “Jam Miller run for a loss of 2 yards to the ALA 23”
},
{
“x”: 46,
“y”: 0.21428571428571427,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 28”
},
{
“x”: 47,
“y”: 0.21428571428571427,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 48,
“y”: 0.2,
“text”: “Jam Miller run for 3 yds to the ALA 24”
},
{
“x”: 49,
“y”: 0.2,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 50,
“y”: 0.2,
“text”: “Ty Simpson pass complete to Germie Bernard for 13 yds to the ALA 37 for a 1ST down”
},
{
“x”: 51,
“y”: 0.25,
“text”: “Jam Miller run for 6 yds to the ALA 43”
},
{
“x”: 52,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 53,
“y”: 0.25,
“text”: “Ty Simpson pass complete to Josh Cuevas for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 54,
“y”: 0.29411764705882354,
“text”: “Daniel Hill run for 11 yds to the SC 41 for a 1ST down”
},
{
“x”: 55,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Ryan Williams for 11 yds to the SC 30 for a 1ST down”
},
{
“x”: 56,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Daniel Hill for 5 yds to the SC 25”
},
{
“x”: 57,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Jam Miller for 10 yds to the SC 15 for a 1ST down”
},
{
“x”: 58,
“y”: 0.3333333333333333,
“text”: “Ty Simpson run for 7 yds to the SC 8”
},
{
“x”: 59,
“y”: 0.3333333333333333,
“text”: “Ty Simpson pass complete to Jam Miller for 6 yds to the SC 2 for a 1ST down”
},
{
“x”: 60,
“y”: 0.3157894736842105,
“text”: “Jam Miller run for a loss of 2 yards to the SC 4”
},
{
“x”: 61,
“y”: 0.3157894736842105,
“text”: “Ty Simpson pass complete to Germie Bernard for 4 yds for a TD (Ty Simpson pass to Josh Cuevas for Two-Point Conversion)”
},
{
“x”: 62,
“y”: 0.35,
“text”: “Jam Miller run for 7 yds to the SC 31”
},
{
“x”: 63,
“y”: 0.35,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the SC 25 for a 1ST down”
},
{
“x”: 64,
“y”: 0.35,
“text”: “Ty Simpson pass complete to Ryan Williams for no gain to the SC 25”
},
{
“x”: 65,
“y”: 0.35,
“text”: “Ty Simpson pass incomplete to Rico Scott”
},
{
“x”: 66,
“y”: 0.38095238095238093,
“text”: “Germie Bernard run for 25 yds for a TD (Conor Talty KICK)”
}
],
“label”: “Alabama Rush SR”,
“borderColor”: “rgba(101, 0, 20, 0.8)”,
“backgroundColor”: [
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”
],
“borderWidth”: 2,
“pointStyle”: “circle”,
“pointRadius”: [
4,
4,
0,
0,
0,
0,
4,
4,
0,
0,
0,
0,
0,
0,
4,
0,
0,
4,
4,
0,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
4,
4,
0,
4,
0,
4,
0,
4,
0,
0,
0,
0,
0,
4,
0,
0,
4,
0,
0,
4,
0,
0,
4,
0,
0,
0,
4,
0,
4,
0,
4,
0,
0,
0,
4
],
“pointBorderWidth”: 1,
“pointBorderColor”: “rgba(101, 0, 20, 0.8)”,
“showLine”: true
},
{
“data”: [
{
“x”: 1,
“y”: 0,
“text”: “Germie Bernard run for 4 yds to the ALA 39”
},
{
“x”: 2,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 41”
},
{
“x”: 3,
“y”: 0,
“text”: “Ty Simpson sacked by Bryan Thomas Jr. for a loss of 1 yard to the ALA 40”
},
{
“x”: 4,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 5,
“y”: 0,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the ALA 26”
},
{
“x”: 6,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 7,
“y”: 0,
“text”: “Germie Bernard run for a loss of 6 yards to the ALA 23”
},
{
“x”: 8,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 25 Alabama Penalty, Offensive Holding (-10 Yards) to the ALA 15”
},
{
“x”: 9,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 10,
“y”: 0.16666666666666666,
“text”: “Ty Simpson pass complete to Daniel Hill for 21 yds to the ALA 41 for a 1ST down”
},
{
“x”: 11,
“y”: 0.2857142857142857,
“text”: “Ty Simpson pass complete to Ryan Williams for 25 yds to the SC 34 for a 1ST down”
},
{
“x”: 12,
“y”: 0.25,
“text”: “Ty Simpson sacked by Vicari Swain for a loss of 8 yards to the SC 42 Ty Simpson fumbled, recovered by SC Troy Pikes , return for 0 yards”
},
{
“x”: 13,
“y”: 0.3333333333333333,
“text”: “Ty Simpson pass complete to Germie Bernard for 11 yds to the ALA 30 for a 1ST down”
},
{
“x”: 14,
“y”: 0.4,
“text”: “Ty Simpson pass complete to Ryan Williams for 18 yds to the ALA 48 for a 1ST down”
},
{
“x”: 15,
“y”: 0.4,
“text”: “Jam Miller run for 4 yds to the SC 48”
},
{
“x”: 16,
“y”: 0.36363636363636365,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 17,
“y”: 0.4166666666666667,
“text”: “Ty Simpson pass complete to Daniel Hill for 41 yds to the SC 7 for a 1ST down”
},
{
“x”: 18,
“y”: 0.4166666666666667,
“text”: “Ty Simpson run for 4 yds to the SC 3”
},
{
“x”: 19,
“y”: 0.4166666666666667,
“text”: “Kadyn Proctor run for no gain to the SC 3”
},
{
“x”: 20,
“y”: 0.38461538461538464,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 21,
“y”: 0.42857142857142855,
“text”: “Ty Simpson pass complete to Josh Cuevas for 3 yds for a TD (Conor Talty KICK)”
},
{
“x”: 22,
“y”: 0.4,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 23,
“y”: 0.4375,
“text”: “Ty Simpson pass complete to Josh Cuevas for 8 yds to the ALA 43”
},
{
“x”: 24,
“y”: 0.47058823529411764,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 25,
“y”: 0.4444444444444444,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 26,
“y”: 0.47368421052631576,
“text”: “Ty Simpson pass complete to Daniel Hill for 9 yds to the SC 43”
},
{
“x”: 27,
“y”: 0.45,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 28,
“y”: 0.45,
“text”: “Ty Simpson run for 2 yds to the SC 41 for a 1ST down”
},
{
“x”: 29,
“y”: 0.42857142857142855,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 30,
“y”: 0.4090909090909091,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 31,
“y”: 0.43478260869565216,
“text”: “Ty Simpson pass complete to Germie Bernard for 23 yds to the SC 18 for a 1ST down”
},
{
“x”: 32,
“y”: 0.43478260869565216,
“text”: “Jam Miller run for 4 yds to the ALA 39”
},
{
“x”: 33,
“y”: 0.43478260869565216,
“text”: “Jam Miller run for 3 yds to the ALA 42”
},
{
“x”: 34,
“y”: 0.4583333333333333,
“text”: “Ty Simpson pass complete to Germie Bernard for 3 yds to the ALA 45 for a 1ST down”
},
{
“x”: 35,
“y”: 0.4583333333333333,
“text”: “Ty Simpson run for 9 yds to the SC 46”
},
{
“x”: 36,
“y”: 0.44,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 37,
“y”: 0.44,
“text”: “Daniel Hill run for no gain to the SC 46”
},
{
“x”: 38,
“y”: 0.4230769230769231,
“text”: “Ty Simpson pass incomplete to Ryan Williams”
},
{
“x”: 39,
“y”: 0.4230769230769231,
“text”: “Daniel Hill run for a loss of 1 yard to the ALA 24”
},
{
“x”: 40,
“y”: 0.4074074074074074,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 41,
“y”: 0.39285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 42,
“y”: 0.41379310344827586,
“text”: “Ty Simpson pass complete to Ryan Williams for 7 yds to the ALA 32”
},
{
“x”: 43,
“y”: 0.4,
“text”: “Ty Simpson pass complete to Jam Miller for 7 yds to the ALA 24”
},
{
“x”: 44,
“y”: 0.3870967741935484,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 45,
“y”: 0.3870967741935484,
“text”: “Jam Miller run for a loss of 2 yards to the ALA 23”
},
{
“x”: 46,
“y”: 0.375,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 28”
},
{
“x”: 47,
“y”: 0.36363636363636365,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 48,
“y”: 0.36363636363636365,
“text”: “Jam Miller run for 3 yds to the ALA 24”
},
{
“x”: 49,
“y”: 0.35294117647058826,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 50,
“y”: 0.37142857142857144,
“text”: “Ty Simpson pass complete to Germie Bernard for 13 yds to the ALA 37 for a 1ST down”
},
{
“x”: 51,
“y”: 0.37142857142857144,
“text”: “Jam Miller run for 6 yds to the ALA 43”
},
{
“x”: 52,
“y”: 0.3611111111111111,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 53,
“y”: 0.3783783783783784,
“text”: “Ty Simpson pass complete to Josh Cuevas for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 54,
“y”: 0.3783783783783784,
“text”: “Daniel Hill run for 11 yds to the SC 41 for a 1ST down”
},
{
“x”: 55,
“y”: 0.39473684210526316,
“text”: “Ty Simpson pass complete to Ryan Williams for 11 yds to the SC 30 for a 1ST down”
},
{
“x”: 56,
“y”: 0.41025641025641024,
“text”: “Ty Simpson pass complete to Daniel Hill for 5 yds to the SC 25”
},
{
“x”: 57,
“y”: 0.425,
“text”: “Ty Simpson pass complete to Jam Miller for 10 yds to the SC 15 for a 1ST down”
},
{
“x”: 58,
“y”: 0.425,
“text”: “Ty Simpson run for 7 yds to the SC 8”
},
{
“x”: 59,
“y”: 0.43902439024390244,
“text”: “Ty Simpson pass complete to Jam Miller for 6 yds to the SC 2 for a 1ST down”
},
{
“x”: 60,
“y”: 0.43902439024390244,
“text”: “Jam Miller run for a loss of 2 yards to the SC 4”
},
{
“x”: 61,
“y”: 0.4523809523809524,
“text”: “Ty Simpson pass complete to Germie Bernard for 4 yds for a TD (Ty Simpson pass to Josh Cuevas for Two-Point Conversion)”
},
{
“x”: 62,
“y”: 0.4523809523809524,
“text”: “Jam Miller run for 7 yds to the SC 31”
},
{
“x”: 63,
“y”: 0.46511627906976744,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the SC 25 for a 1ST down”
},
{
“x”: 64,
“y”: 0.45454545454545453,
“text”: “Ty Simpson pass complete to Ryan Williams for no gain to the SC 25”
},
{
“x”: 65,
“y”: 0.4444444444444444,
“text”: “Ty Simpson pass incomplete to Rico Scott”
},
{
“x”: 66,
“y”: 0.4444444444444444,
“text”: “Germie Bernard run for 25 yds for a TD (Conor Talty KICK)”
}
],
“label”: “Alabama Pass SR”,
“borderColor”: “rgba(101, 0, 20, 0.8)”,
“backgroundColor”: [
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”
],
“borderWidth”: 2,
“pointStyle”: “triangle”,
“pointRadius”: [
0,
0,
6,
6,
6,
6,
0,
0,
6,
6,
6,
6,
6,
6,
0,
6,
6,
0,
0,
6,
6,
6,
6,
6,
6,
6,
6,
0,
6,
6,
6,
0,
0,
6,
0,
6,
0,
6,
0,
6,
6,
6,
6,
6,
0,
6,
6,
0,
6,
6,
0,
6,
6,
0,
6,
6,
6,
0,
6,
0,
6,
0,
6,
6,
6,
0
],
“pointBorderWidth”: 1,
“pointBorderColor”: “rgba(101, 0, 20, 0.8)”,
“borderDash”: [
4,
4
],
“showLine”: true
},
{
“label”: “Quarters”,
“data”: [
{
“x”: 1,
“y”: 0
},
{
“x”: 1,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 7,
“y”: 0
},
{
“x”: 7,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 32,
“y”: 0
},
{
“x”: 32,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 48,
“y”: 0
},
{
“x”: 48,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
}
],
“borderColor”: “rgba(0,0,0,0.1)”,
“borderWidth”: 1,
“tension”: 0,
“fill”: false,
“pointRadius”: 0,
“showLine”: true,
“datalabels”: {
“display”: false
}
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘line’ === ‘line’ ? ‘team-play-type-lines’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘team-play-type-lines’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘team-play-type-lines’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘line’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘team-play-type-lines’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘team-play-type-lines’.includes(‘top-rushers’) || ‘team-play-type-lines’.includes(‘top-passers’) || ‘team-play-type-lines’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘team-play-type-lines’ === ‘win-probability’ ? {
display: false
} : ‘line’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘team-play-type-lines’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘team-play-type-lines’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘team-play-type-lines’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘line’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
Yep, that skinny, empty, desert-like area sliver on the left side of the chart is something that we, uh, data scientists call “Alabama’s first quarter.” There were zero successful plays of any type in the 1Q — though a few close 4-yard gainers on 1st — and zero Tide first downs to be seen.
The passing game, of course, started to show life first, even hitting a few explosives early in the second quarter. And finally, Ty scrambled a few yards to give Alabama it’s first successful rush of the day midway through the second quarter. Only two more would follow in this game before things broke open in the 4th; so even with the latter we’re talking eight total successful Bama rushes in this game. Truly, the Stallings days are far behind us.
Do you want to see the Quarters version of this chart? Well, I know you don’t, but it’s important and I’m gonna show it to you anyway. You’ll thank me for this when you’re older.
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492307865-dpruuvrll .data-definitions li {
margin-bottom: 4px;
}
SR and XR by Quarter
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492307865_dpruuvrll() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492307865-dpruuvrll’);
const caret = document.getElementById(‘caret_cfb-chart-1761492307865-dpruuvrll’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492307865-dpruuvrll’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492307865-dpruuvrll’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Q1”,
“Q2”,
“Q3”,
“Q4”
],
“datasets”: [
{
“data”: [
0,
0.2,
0,
0.05263157894736842
],
“stack”: “Team”,
“label”: “Alabama XR”,
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0,
0.48,
0.1875,
0.6842105263157895
],
“stack”: “Team”,
“label”: “Alabama SR”,
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“datalabels”: {
“display”: true
}
},
{
“data”: [
0.08695652173913043,
0.0625,
0.1111111111111111,
0
],
“stack”: “Opponent”,
“label”: “South Carolina XR”,
“backgroundColor”: “#1e2938CC”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.34782608695652173,
0.3125,
0.5,
0.4166666666666667
],
“stack”: “Opponent”,
“label”: “South Carolina SR”,
“backgroundColor”: “#64748BCC”,
“datalabels”: {
“display”: true
}
},
{
“type”: “line”,
“data”: [
0.42,
0.42,
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null,
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamCounts”: [
6,
25,
16,
19
],
“oppCounts”: [
23,
16,
18,
12
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘quarter-bars’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘quarter-bars’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘quarter-bars’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘quarter-bars’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘quarter-bars’.includes(‘top-rushers’) || ‘quarter-bars’.includes(‘top-passers’) || ‘quarter-bars’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘quarter-bars’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘quarter-bars’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘quarter-bars’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘quarter-bars’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492307865-dpruuvrll’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
Ugh. To confirm, that is a zero successful plays recorded in the first quarter. Sure, if a few of those 4-yard gains on 1st woulda gained 1 more yard, that could’ve at least put some ink on the chart here. But it was a bad 1st quarter for the offense, no way around it.
The funny thing is, we can usually rely on a first half and second half narrative with this team, or with at least the teams from the last few years. But here, we’ve got this Jekyll and Hyde-ing between the quarters themselves:
- Zeroing out (literally) Q1.
- Putting together a really respectable Q2!
- Bottoming out on Q3.
- Hero-mode comeback in Q4!?
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492381420-2pybzsjqg .data-definitions li {
margin-bottom: 4px;
}
SR and XR by Down
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492381420_2pybzsjqg() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492381420-2pybzsjqg’);
const caret = document.getElementById(‘caret_cfb-chart-1761492381420-2pybzsjqg’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492381420-2pybzsjqg’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492381420-2pybzsjqg’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“1st Down”,
“2nd Down”,
“3rd Down”,
“4th Down”
],
“datasets”: [
{
“data”: [
0.07692307692307693,
0,
0.25,
0
],
“stack”: “Team”,
“label”: “Alabama XR”,
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.46153846153846156,
0.2857142857142857,
0.5,
0.6666666666666666
],
“stack”: “Team”,
“label”: “Alabama SR”,
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“datalabels”: {
“display”: true
}
},
{
“data”: [
0.06896551724137931,
0.09523809523809523,
0,
0.5
],
“stack”: “Opponent”,
“label”: “South Carolina XR”,
“backgroundColor”: “#1e2938CC”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.4482758620689655,
0.2857142857142857,
0.35294117647058826,
1
],
“stack”: “Opponent”,
“label”: “South Carolina SR”,
“backgroundColor”: “#64748BCC”,
“datalabels”: {
“display”: true
}
},
{
“type”: “line”,
“data”: [
0.42,
0.42,
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null,
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamCounts”: [
26,
21,
16,
3
],
“oppCounts”: [
29,
21,
17,
2
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘down-bars’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘down-bars’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘down-bars’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘down-bars’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘down-bars’.includes(‘top-rushers’) || ‘down-bars’.includes(‘top-passers’) || ‘down-bars’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘down-bars’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘down-bars’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘down-bars’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘down-bars’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492381420-2pybzsjqg’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
And part of that wackiness does come from the Tides’ tendencies to putter around a little bit on at least first or second down each game before trying to make the most of late downs. Honestly, this chart isn’t even that dissimilar to most of the ones we’ve seen from Alabama in 2025 – except that the second downs were just especially weak for some reason in Saturday’s game (for both teams).
Again, I keep saying that leaning on late downs is not sustainable for this team to win games, but here we are again after another win, and I’ve still yet to be proven prophetic on that one. Plus, as a reminder, other teams can also succeed on late downs when we do. In this case, South Carolina almost made up for their deficiency on third down by putting in 2 for 2 on 4th down, including an explosive play. Now that is how an underdog sticks around in a tough game.
But, then, a patented drive
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761442377797-9ib1v7x60 .data-definitions li {
margin-bottom: 4px;
}
SR, XR, and Play Count by Drive: Alabama
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- Drive Metrics: Success and explosiveness rates calculated per drive
- Play counts: Gray bars show number of plays in each drive
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761442377797_9ib1v7x60() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761442377797-9ib1v7x60’);
const caret = document.getElementById(‘caret_cfb-chart-1761442377797-9ib1v7x60’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761442377797-9ib1v7x60’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761442377797-9ib1v7x60’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Drive 2”,
“Drive 5”,
“Drive 7”,
“Drive 9”,
“Drive 11”,
“Drive 12”,
“Drive 14”,
“Drive 16”,
“Drive 18”,
“Drive 21”,
“Drive 23”
],
“datasets”: [
{
“label”: “Alabama XR”,
“data”: [
0,
0,
0.3333333333333333,
0.2222222222222222,
0.1,
0,
0,
0,
0,
0,
0.2
],
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“stack”: “SRXR”,
“yAxisID”: “y”,
“datalabels”: {
“display”: false
}
},
{
“label”: “Alabama SR”,
“data”: [
0,
0,
0.3333333333333333,
0.5555555555555556,
0.5,
0.2857142857142857,
0,
0.3333333333333333,
0,
0.7142857142857143,
0.6
],
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“stack”: “SRXR”,
“yAxisID”: “y”,
“datalabels”: {
“display”: false
}
},
{
“label”: “Plays in drive”,
“data”: [
3,
3,
6,
9,
10,
7,
3,
3,
3,
14,
5
],
“backgroundColor”: “rgba(148, 148, 148, 0.8)”,
“stack”: “Plays”,
“yAxisID”: “y1”,
“datalabels”: {
“display”: true
}
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘team-drive-metrics’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘team-drive-metrics’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘team-drive-metrics’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘team-drive-metrics’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘team-drive-metrics’.includes(‘top-rushers’) || ‘team-drive-metrics’.includes(‘top-passers’) || ‘team-drive-metrics’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘team-drive-metrics’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘team-drive-metrics’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘team-drive-metrics’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘team-drive-metrics’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761442377797-9ib1v7x60’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
This drive chart is pretty horrible this week. Obviously, anyone watching the game is well aware that Ty Simpson and company delivered a bunch of three-and-outs. This looked a lot more like the 2024 team in that way. And even worse, only one of these three-and-outs even contained a single successful play. The others were the types of three-and-outs where you’re like, “Yeah, none of that had any chance of working.”
We also didn’t deliver a lot of double-digit drives, which was a luxury we’d gotten used to in the last several weeks. That is, until that second to last drive, where Alabama put up a 71% success rate across 14 plays to drive down the field and tie up this game. (At the time, I thought we’d burned up too much clock).
And, with that resurgence, we entered into a territory that was something other than “below average and shockingly bad.” In fact, let’s revisit a previous chart to tell the other part of the story:
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761442365383-3yhzpwjan .data-definitions li {
margin-bottom: 4px;
}
SR by Play Type: Alabama
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- Gray area: Represents 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761442365383_3yhzpwjan() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761442365383-3yhzpwjan’);
const caret = document.getElementById(‘caret_cfb-chart-1761442365383-3yhzpwjan’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“datasets”: [
{
“label”: “NCAA Avg SR”,
“data”: [
{
“x”: 1,
“y”: 0
},
{
“x”: 1,
“y”: 0.42
},
{
“x”: 66,
“y”: 0.42
},
{
“x”: 66,
“y”: 0
}
],
“backgroundColor”: “rgba(0,0,0,0.03)”,
“borderColor”: “transparent”,
“pointRadius”: 0,
“fill”: true,
“tension”: 0,
“showLine”: true,
“datalabels”: {
“display”: false
}
},
{
“data”: [
{
“x”: 1,
“y”: 0,
“text”: “Germie Bernard run for 4 yds to the ALA 39”
},
{
“x”: 2,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 41”
},
{
“x”: 3,
“y”: 0,
“text”: “Ty Simpson sacked by Bryan Thomas Jr. for a loss of 1 yard to the ALA 40”
},
{
“x”: 4,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 5,
“y”: 0,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the ALA 26”
},
{
“x”: 6,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 7,
“y”: 0,
“text”: “Germie Bernard run for a loss of 6 yards to the ALA 23”
},
{
“x”: 8,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 25 Alabama Penalty, Offensive Holding (-10 Yards) to the ALA 15”
},
{
“x”: 9,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 10,
“y”: 0,
“text”: “Ty Simpson pass complete to Daniel Hill for 21 yds to the ALA 41 for a 1ST down”
},
{
“x”: 11,
“y”: 0,
“text”: “Ty Simpson pass complete to Ryan Williams for 25 yds to the SC 34 for a 1ST down”
},
{
“x”: 12,
“y”: 0,
“text”: “Ty Simpson sacked by Vicari Swain for a loss of 8 yards to the SC 42 Ty Simpson fumbled, recovered by SC Troy Pikes , return for 0 yards”
},
{
“x”: 13,
“y”: 0,
“text”: “Ty Simpson pass complete to Germie Bernard for 11 yds to the ALA 30 for a 1ST down”
},
{
“x”: 14,
“y”: 0,
“text”: “Ty Simpson pass complete to Ryan Williams for 18 yds to the ALA 48 for a 1ST down”
},
{
“x”: 15,
“y”: 0,
“text”: “Jam Miller run for 4 yds to the SC 48”
},
{
“x”: 16,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 17,
“y”: 0,
“text”: “Ty Simpson pass complete to Daniel Hill for 41 yds to the SC 7 for a 1ST down”
},
{
“x”: 18,
“y”: 0.16666666666666666,
“text”: “Ty Simpson run for 4 yds to the SC 3”
},
{
“x”: 19,
“y”: 0.14285714285714285,
“text”: “Kadyn Proctor run for no gain to the SC 3”
},
{
“x”: 20,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 21,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Josh Cuevas for 3 yds for a TD (Conor Talty KICK)”
},
{
“x”: 22,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 23,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Josh Cuevas for 8 yds to the ALA 43”
},
{
“x”: 24,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 25,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 26,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass complete to Daniel Hill for 9 yds to the SC 43”
},
{
“x”: 27,
“y”: 0.14285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 28,
“y”: 0.25,
“text”: “Ty Simpson run for 2 yds to the SC 41 for a 1ST down”
},
{
“x”: 29,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 30,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 31,
“y”: 0.25,
“text”: “Ty Simpson pass complete to Germie Bernard for 23 yds to the SC 18 for a 1ST down”
},
{
“x”: 32,
“y”: 0.2222222222222222,
“text”: “Jam Miller run for 4 yds to the ALA 39”
},
{
“x”: 33,
“y”: 0.2,
“text”: “Jam Miller run for 3 yds to the ALA 42”
},
{
“x”: 34,
“y”: 0.2,
“text”: “Ty Simpson pass complete to Germie Bernard for 3 yds to the ALA 45 for a 1ST down”
},
{
“x”: 35,
“y”: 0.2727272727272727,
“text”: “Ty Simpson run for 9 yds to the SC 46”
},
{
“x”: 36,
“y”: 0.2727272727272727,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 37,
“y”: 0.25,
“text”: “Daniel Hill run for no gain to the SC 46”
},
{
“x”: 38,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete to Ryan Williams”
},
{
“x”: 39,
“y”: 0.23076923076923078,
“text”: “Daniel Hill run for a loss of 1 yard to the ALA 24”
},
{
“x”: 40,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 41,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 42,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass complete to Ryan Williams for 7 yds to the ALA 32”
},
{
“x”: 43,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass complete to Jam Miller for 7 yds to the ALA 24”
},
{
“x”: 44,
“y”: 0.23076923076923078,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 45,
“y”: 0.21428571428571427,
“text”: “Jam Miller run for a loss of 2 yards to the ALA 23”
},
{
“x”: 46,
“y”: 0.21428571428571427,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 28”
},
{
“x”: 47,
“y”: 0.21428571428571427,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 48,
“y”: 0.2,
“text”: “Jam Miller run for 3 yds to the ALA 24”
},
{
“x”: 49,
“y”: 0.2,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 50,
“y”: 0.2,
“text”: “Ty Simpson pass complete to Germie Bernard for 13 yds to the ALA 37 for a 1ST down”
},
{
“x”: 51,
“y”: 0.25,
“text”: “Jam Miller run for 6 yds to the ALA 43”
},
{
“x”: 52,
“y”: 0.25,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 53,
“y”: 0.25,
“text”: “Ty Simpson pass complete to Josh Cuevas for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 54,
“y”: 0.29411764705882354,
“text”: “Daniel Hill run for 11 yds to the SC 41 for a 1ST down”
},
{
“x”: 55,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Ryan Williams for 11 yds to the SC 30 for a 1ST down”
},
{
“x”: 56,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Daniel Hill for 5 yds to the SC 25”
},
{
“x”: 57,
“y”: 0.29411764705882354,
“text”: “Ty Simpson pass complete to Jam Miller for 10 yds to the SC 15 for a 1ST down”
},
{
“x”: 58,
“y”: 0.3333333333333333,
“text”: “Ty Simpson run for 7 yds to the SC 8”
},
{
“x”: 59,
“y”: 0.3333333333333333,
“text”: “Ty Simpson pass complete to Jam Miller for 6 yds to the SC 2 for a 1ST down”
},
{
“x”: 60,
“y”: 0.3157894736842105,
“text”: “Jam Miller run for a loss of 2 yards to the SC 4”
},
{
“x”: 61,
“y”: 0.3157894736842105,
“text”: “Ty Simpson pass complete to Germie Bernard for 4 yds for a TD (Ty Simpson pass to Josh Cuevas for Two-Point Conversion)”
},
{
“x”: 62,
“y”: 0.35,
“text”: “Jam Miller run for 7 yds to the SC 31”
},
{
“x”: 63,
“y”: 0.35,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the SC 25 for a 1ST down”
},
{
“x”: 64,
“y”: 0.35,
“text”: “Ty Simpson pass complete to Ryan Williams for no gain to the SC 25”
},
{
“x”: 65,
“y”: 0.35,
“text”: “Ty Simpson pass incomplete to Rico Scott”
},
{
“x”: 66,
“y”: 0.38095238095238093,
“text”: “Germie Bernard run for 25 yds for a TD (Conor Talty KICK)”
}
],
“label”: “Alabama Rush SR”,
“borderColor”: “rgba(101, 0, 20, 0.8)”,
“backgroundColor”: [
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”
],
“borderWidth”: 2,
“pointStyle”: “circle”,
“pointRadius”: [
4,
4,
0,
0,
0,
0,
4,
4,
0,
0,
0,
0,
0,
0,
4,
0,
0,
4,
4,
0,
0,
0,
0,
0,
0,
0,
0,
4,
0,
0,
0,
4,
4,
0,
4,
0,
4,
0,
4,
0,
0,
0,
0,
0,
4,
0,
0,
4,
0,
0,
4,
0,
0,
4,
0,
0,
0,
4,
0,
4,
0,
4,
0,
0,
0,
4
],
“pointBorderWidth”: 1,
“pointBorderColor”: “rgba(101, 0, 20, 0.8)”,
“showLine”: true
},
{
“data”: [
{
“x”: 1,
“y”: 0,
“text”: “Germie Bernard run for 4 yds to the ALA 39”
},
{
“x”: 2,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 41”
},
{
“x”: 3,
“y”: 0,
“text”: “Ty Simpson sacked by Bryan Thomas Jr. for a loss of 1 yard to the ALA 40”
},
{
“x”: 4,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 5,
“y”: 0,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the ALA 26”
},
{
“x”: 6,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 7,
“y”: 0,
“text”: “Germie Bernard run for a loss of 6 yards to the ALA 23”
},
{
“x”: 8,
“y”: 0,
“text”: “Jam Miller run for 2 yds to the ALA 25 Alabama Penalty, Offensive Holding (-10 Yards) to the ALA 15”
},
{
“x”: 9,
“y”: 0,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 10,
“y”: 0.16666666666666666,
“text”: “Ty Simpson pass complete to Daniel Hill for 21 yds to the ALA 41 for a 1ST down”
},
{
“x”: 11,
“y”: 0.2857142857142857,
“text”: “Ty Simpson pass complete to Ryan Williams for 25 yds to the SC 34 for a 1ST down”
},
{
“x”: 12,
“y”: 0.25,
“text”: “Ty Simpson sacked by Vicari Swain for a loss of 8 yards to the SC 42 Ty Simpson fumbled, recovered by SC Troy Pikes , return for 0 yards”
},
{
“x”: 13,
“y”: 0.3333333333333333,
“text”: “Ty Simpson pass complete to Germie Bernard for 11 yds to the ALA 30 for a 1ST down”
},
{
“x”: 14,
“y”: 0.4,
“text”: “Ty Simpson pass complete to Ryan Williams for 18 yds to the ALA 48 for a 1ST down”
},
{
“x”: 15,
“y”: 0.4,
“text”: “Jam Miller run for 4 yds to the SC 48”
},
{
“x”: 16,
“y”: 0.36363636363636365,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 17,
“y”: 0.4166666666666667,
“text”: “Ty Simpson pass complete to Daniel Hill for 41 yds to the SC 7 for a 1ST down”
},
{
“x”: 18,
“y”: 0.4166666666666667,
“text”: “Ty Simpson run for 4 yds to the SC 3”
},
{
“x”: 19,
“y”: 0.4166666666666667,
“text”: “Kadyn Proctor run for no gain to the SC 3”
},
{
“x”: 20,
“y”: 0.38461538461538464,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 21,
“y”: 0.42857142857142855,
“text”: “Ty Simpson pass complete to Josh Cuevas for 3 yds for a TD (Conor Talty KICK)”
},
{
“x”: 22,
“y”: 0.4,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 23,
“y”: 0.4375,
“text”: “Ty Simpson pass complete to Josh Cuevas for 8 yds to the ALA 43”
},
{
“x”: 24,
“y”: 0.47058823529411764,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 25,
“y”: 0.4444444444444444,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 26,
“y”: 0.47368421052631576,
“text”: “Ty Simpson pass complete to Daniel Hill for 9 yds to the SC 43”
},
{
“x”: 27,
“y”: 0.45,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 28,
“y”: 0.45,
“text”: “Ty Simpson run for 2 yds to the SC 41 for a 1ST down”
},
{
“x”: 29,
“y”: 0.42857142857142855,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 30,
“y”: 0.4090909090909091,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 31,
“y”: 0.43478260869565216,
“text”: “Ty Simpson pass complete to Germie Bernard for 23 yds to the SC 18 for a 1ST down”
},
{
“x”: 32,
“y”: 0.43478260869565216,
“text”: “Jam Miller run for 4 yds to the ALA 39”
},
{
“x”: 33,
“y”: 0.43478260869565216,
“text”: “Jam Miller run for 3 yds to the ALA 42”
},
{
“x”: 34,
“y”: 0.4583333333333333,
“text”: “Ty Simpson pass complete to Germie Bernard for 3 yds to the ALA 45 for a 1ST down”
},
{
“x”: 35,
“y”: 0.4583333333333333,
“text”: “Ty Simpson run for 9 yds to the SC 46”
},
{
“x”: 36,
“y”: 0.44,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 37,
“y”: 0.44,
“text”: “Daniel Hill run for no gain to the SC 46”
},
{
“x”: 38,
“y”: 0.4230769230769231,
“text”: “Ty Simpson pass incomplete to Ryan Williams”
},
{
“x”: 39,
“y”: 0.4230769230769231,
“text”: “Daniel Hill run for a loss of 1 yard to the ALA 24”
},
{
“x”: 40,
“y”: 0.4074074074074074,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 41,
“y”: 0.39285714285714285,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 42,
“y”: 0.41379310344827586,
“text”: “Ty Simpson pass complete to Ryan Williams for 7 yds to the ALA 32”
},
{
“x”: 43,
“y”: 0.4,
“text”: “Ty Simpson pass complete to Jam Miller for 7 yds to the ALA 24”
},
{
“x”: 44,
“y”: 0.3870967741935484,
“text”: “Ty Simpson pass incomplete”
},
{
“x”: 45,
“y”: 0.3870967741935484,
“text”: “Jam Miller run for a loss of 2 yards to the ALA 23”
},
{
“x”: 46,
“y”: 0.375,
“text”: “Ty Simpson pass complete to Ryan Williams for 5 yds to the ALA 28”
},
{
“x”: 47,
“y”: 0.36363636363636365,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 48,
“y”: 0.36363636363636365,
“text”: “Jam Miller run for 3 yds to the ALA 24”
},
{
“x”: 49,
“y”: 0.35294117647058826,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 50,
“y”: 0.37142857142857144,
“text”: “Ty Simpson pass complete to Germie Bernard for 13 yds to the ALA 37 for a 1ST down”
},
{
“x”: 51,
“y”: 0.37142857142857144,
“text”: “Jam Miller run for 6 yds to the ALA 43”
},
{
“x”: 52,
“y”: 0.3611111111111111,
“text”: “Ty Simpson pass incomplete to Germie Bernard”
},
{
“x”: 53,
“y”: 0.3783783783783784,
“text”: “Ty Simpson pass complete to Josh Cuevas for 5 yds to the ALA 48 for a 1ST down”
},
{
“x”: 54,
“y”: 0.3783783783783784,
“text”: “Daniel Hill run for 11 yds to the SC 41 for a 1ST down”
},
{
“x”: 55,
“y”: 0.39473684210526316,
“text”: “Ty Simpson pass complete to Ryan Williams for 11 yds to the SC 30 for a 1ST down”
},
{
“x”: 56,
“y”: 0.41025641025641024,
“text”: “Ty Simpson pass complete to Daniel Hill for 5 yds to the SC 25”
},
{
“x”: 57,
“y”: 0.425,
“text”: “Ty Simpson pass complete to Jam Miller for 10 yds to the SC 15 for a 1ST down”
},
{
“x”: 58,
“y”: 0.425,
“text”: “Ty Simpson run for 7 yds to the SC 8”
},
{
“x”: 59,
“y”: 0.43902439024390244,
“text”: “Ty Simpson pass complete to Jam Miller for 6 yds to the SC 2 for a 1ST down”
},
{
“x”: 60,
“y”: 0.43902439024390244,
“text”: “Jam Miller run for a loss of 2 yards to the SC 4”
},
{
“x”: 61,
“y”: 0.4523809523809524,
“text”: “Ty Simpson pass complete to Germie Bernard for 4 yds for a TD (Ty Simpson pass to Josh Cuevas for Two-Point Conversion)”
},
{
“x”: 62,
“y”: 0.4523809523809524,
“text”: “Jam Miller run for 7 yds to the SC 31”
},
{
“x”: 63,
“y”: 0.46511627906976744,
“text”: “Ty Simpson pass complete to Rico Scott for 6 yds to the SC 25 for a 1ST down”
},
{
“x”: 64,
“y”: 0.45454545454545453,
“text”: “Ty Simpson pass complete to Ryan Williams for no gain to the SC 25”
},
{
“x”: 65,
“y”: 0.4444444444444444,
“text”: “Ty Simpson pass incomplete to Rico Scott”
},
{
“x”: 66,
“y”: 0.4444444444444444,
“text”: “Germie Bernard run for 25 yds for a TD (Conor Talty KICK)”
}
],
“label”: “Alabama Pass SR”,
“borderColor”: “rgba(101, 0, 20, 0.8)”,
“backgroundColor”: [
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(101, 0, 20, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”,
“rgba(255,255,255,0.9)”
],
“borderWidth”: 2,
“pointStyle”: “triangle”,
“pointRadius”: [
0,
0,
6,
6,
6,
6,
0,
0,
6,
6,
6,
6,
6,
6,
0,
6,
6,
0,
0,
6,
6,
6,
6,
6,
6,
6,
6,
0,
6,
6,
6,
0,
0,
6,
0,
6,
0,
6,
0,
6,
6,
6,
6,
6,
0,
6,
6,
0,
6,
6,
0,
6,
6,
0,
6,
6,
6,
0,
6,
0,
6,
0,
6,
6,
6,
0
],
“pointBorderWidth”: 1,
“pointBorderColor”: “rgba(101, 0, 20, 0.8)”,
“borderDash”: [
4,
4
],
“showLine”: true
},
{
“label”: “Quarters”,
“data”: [
{
“x”: 1,
“y”: 0
},
{
“x”: 1,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 7,
“y”: 0
},
{
“x”: 7,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 32,
“y”: 0
},
{
“x”: 32,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
},
{
“x”: 48,
“y”: 0
},
{
“x”: 48,
“y”: 1
},
{
“x”: 66,
“y”: 1
},
{
“x”: 66,
“y”: 0
}
],
“borderColor”: “rgba(0,0,0,0.1)”,
“borderWidth”: 1,
“tension”: 0,
“fill”: false,
“pointRadius”: 0,
“showLine”: true,
“datalabels”: {
“display”: false
}
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘line’ === ‘line’ ? ‘team-play-type-lines’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘team-play-type-lines’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘team-play-type-lines’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘line’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘team-play-type-lines’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘team-play-type-lines’.includes(‘top-rushers’) || ‘team-play-type-lines’.includes(‘top-passers’) || ‘team-play-type-lines’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘team-play-type-lines’ === ‘win-probability’ ? {
display: false
} : ‘line’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘team-play-type-lines’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘team-play-type-lines’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘team-play-type-lines’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘line’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761442365383-3yhzpwjan’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
So by now you know all about that first quarter offense—and all about, ahem, “rushing offense” from the first three quarters. But given the hole they’d dug themselves into, I gotta give credit to an offense that managed to creep back to pretty much league average in their overall efficiencies in this game.
A resurgent 4th quarter had just the ingredients that does a Gump’s body good:
- Seven successful passes in a row, amongst 8-of-10 passing success (80% Passing SR) to start the 4th quarter
- 5 of 7 rushing success (71% Rushing SR) in the 4th quarter
- Game-cumulative “league average” efficiency reached (lol)
I guess “find a way to win” is one way to do it, but I’d rather we leave the jack-in-the-box game planning at home next time, at least for my own mental health.
Kadyn Proctor loses his 100% SR
Alas, our most efficient skill player is not infallible: Kadyn Proctor got another carry in this one—a 2nd-and-3—but a defender had studied enough tape to know to just go low, and he was cut down at the LOS. Sigh.
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-content.top-rushers { height: 372px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-content {
padding: 12px 16px 20px !important;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761495000215-zi70qeqwt .data-definitions li {
margin-bottom: 4px;
}
Top rushers
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761495000215_zi70qeqwt() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761495000215-zi70qeqwt’);
const caret = document.getElementById(‘caret_cfb-chart-1761495000215-zi70qeqwt’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761495000215-zi70qeqwt’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761495000215-zi70qeqwt’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Jam Miller”,
“Ty Simpson”,
“Germie Bernard”,
“Daniel Hill”,
“Kadyn Proctor”,
“LaNorris Sell…”,
“Matt Fuller”,
“Rahsul Faison”,
“Oscar Adaway III”,
“Jaen Sellers”
],
“datasets”: [
{
“label”: “Explosive rushes”,
“data”: [
0,
0,
1,
0,
0,
1,
0,
0,
0,
0
],
“backgroundColor”: [
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”
],
“borderColor”: “#374151”,
“borderWidth”: 1
},
{
“label”: “Successful rushes”,
“data”: [
2,
4,
0,
1,
0,
8,
3,
1,
1,
0
],
“backgroundColor”: [
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”
],
“borderColor”: “#374151”,
“borderWidth”: 1
},
{
“label”: “Unsuccessful rushes”,
“data”: [
8,
0,
2,
2,
1,
6,
9,
4,
1,
1
],
“backgroundColor”: “#FFFFFF”,
“borderColor”: “#374151”,
“borderWidth”: 1
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘top-rushers’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘top-rushers’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘top-rushers’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘top-rushers’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘top-rushers’.includes(‘top-rushers’) || ‘top-rushers’.includes(‘top-passers’) || ‘top-rushers’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘top-rushers’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘top-rushers’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘top-rushers’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘top-rushers’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761495000215-zi70qeqwt’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
Otherwise, things were … about how you’d expect if you watched this game. Jalen Miller was inefficient on his leading number of carries (10). Though, again, I will give some credit to 4-yard gains on 1st downs (and similar plays that count as “unsuccessful), which he puts up in seemingly every game. In general, I think Jam runs some of those middle yardage gains even when put in a bad situations; so, I think his efficiency here understates his effectiveness at least slightly.
Still, we’ve got weird flashbacks to “Milroe Mode” in this chart, both on the Alabama side—Ty was actually 4-for-4 on what technically counted as rushes (not sacks) … and I would’ve happily taken that in the FSU game!
But the bigger “Milroe Mode” in this one came from the other sideline: QB LaNorris Sellers was a bear to bring down, faster than he looks (and he looks fast), and he put up the best rushing line of any back in the game. Alabama actually managed to stop the RB’s pretty well, but Sellers nearly did enough to get the upset on his own.
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492319612-ouqcd7c5o .data-definitions li {
margin-bottom: 4px;
}
SR and XR by Play Type (Bar Chart)
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492319612_ouqcd7c5o() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492319612-ouqcd7c5o’);
const caret = document.getElementById(‘caret_cfb-chart-1761492319612-ouqcd7c5o’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492319612-ouqcd7c5o’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492319612-ouqcd7c5o’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Rush”,
“Pass”
],
“datasets”: [
{
“data”: [
0.047619047619047616,
0.1111111111111111
],
“stack”: “Team”,
“label”: “Alabama XR”,
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.38095238095238093,
0.4444444444444444
],
“stack”: “Team”,
“label”: “Alabama SR”,
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“datalabels”: {
“display”: true
}
},
{
“data”: [
0.02857142857142857,
0.11764705882352941
],
“stack”: “Opponent”,
“label”: “South Carolina XR”,
“backgroundColor”: “#1e2938CC”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.4,
0.38235294117647056
],
“stack”: “Opponent”,
“label”: “South Carolina SR”,
“backgroundColor”: “#64748BCC”,
“datalabels”: {
“display”: true
}
},
{
“type”: “line”,
“data”: [
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamCounts”: [
21,
45
],
“oppCounts”: [
35,
34
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘play-type-bars’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘play-type-bars’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘play-type-bars’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘play-type-bars’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘play-type-bars’.includes(‘top-rushers’) || ‘play-type-bars’.includes(‘top-passers’) || ‘play-type-bars’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘play-type-bars’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘play-type-bars’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘play-type-bars’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘play-type-bars’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492319612-ouqcd7c5o’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
Which … does make this chart feel kinda funny, right? The Gamecocks were slightly more efficient than Alabama on the ground, but that whole bar is basically just “QB rushes.” And on the other side of this chart, that QB couldn’t do enough in the passing game to finish the job: Ty Simpson once again on the winning end of the Passing SR line.
Tailbacks are receivers now
But, why even bother rushing? These big boys can catch, let’s just throw the ball to them!
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-content.top-receivers { height: 624px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-content {
padding: 12px 16px 20px !important;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761495162305-vlm3alh7j .data-definitions li {
margin-bottom: 4px;
}
Top receivers
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761495162305_vlm3alh7j() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761495162305-vlm3alh7j’);
const caret = document.getElementById(‘caret_cfb-chart-1761495162305-vlm3alh7j’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761495162305-vlm3alh7j’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761495162305-vlm3alh7j’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Ryan Williams”,
“Germie Bernard”,
“Daniel Hill”,
“Josh Cuevas”,
“Jam Miller”,
“Rico Scott”,
“Rahsul Faison”,
“Donovan Murph”,
“Brady Hunt”,
“Jaen Sellers”,
“Jordan Dingle”,
“Matt Fuller”,
“Nyck Harbor”,
“Andrew Colasu…”
],
“datasets”: [
{
“label”: “Explosive catches”,
“data”: [
2,
1,
2,
0,
0,
0,
1,
1,
0,
1,
0,
0,
1,
0
],
“backgroundColor”: [
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#3c000cCC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”,
“#0e1520CC”
],
“borderColor”: “#374151”,
“borderWidth”: 1
},
{
“label”: “Successful catches”,
“data”: [
3,
4,
2,
3,
2,
1,
1,
1,
3,
1,
1,
1,
0,
1
],
“backgroundColor”: [
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“rgba(175, 40, 60, 0.8)”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”,
“#64748BCC”
],
“borderColor”: “#374151”,
“borderWidth”: 1
},
{
“label”: “Other catches”,
“data”: [
2,
0,
0,
0,
1,
1,
2,
1,
0,
0,
1,
0,
0,
0
],
“backgroundColor”: [
“rgba(245, 229, 233, 0.8)”,
“rgba(245, 229, 233, 0.8)”,
“rgba(245, 229, 233, 0.8)”,
“rgba(245, 229, 233, 0.8)”,
“rgba(245, 229, 233, 0.8)”,
“rgba(245, 229, 233, 0.8)”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”,
“#F1F5F9CC”
],
“borderColor”: “#374151”,
“borderWidth”: 1
}
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘top-receivers’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘top-receivers’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘top-receivers’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘top-receivers’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘top-receivers’.includes(‘top-rushers’) || ‘top-receivers’.includes(‘top-passers’) || ‘top-receivers’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘top-receivers’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘top-receivers’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘top-receivers’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘top-receivers’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761495162305-vlm3alh7j’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
Yep, you’ve got your two primary WR’s up at the top this week—kudos to Ryan Williams for what was still a continued comeback game despite a nearly-game-breaking drop on a tough ball. Germie Bernard also continued his job as “Skill Player Leatherman” to bring in a few scores—vibes of Waddle on that crosser, anyone?—and help us seal (and even “steal”) a win.
But then, the big boys fill this chart: Daniel Hill only had a few rushes, but got 4 passes thrown his way for a 100% receiving SR and a 50% receiving XR. Jam Miller had three receptions of his own. And then my “he’s my favorite player at least twice in each game” TE Josh Cuevas continues to have the best hands on the team, with his 3/3 successful catches.
Inverted themes
A few interesting ones to call out after the trends of the last several weeks:
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492352103-4itv7blzr .data-definitions li {
margin-bottom: 4px;
}
SR and XR by Red Zone
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492352103_4itv7blzr() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492352103-4itv7blzr’);
const caret = document.getElementById(‘caret_cfb-chart-1761492352103-4itv7blzr’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492352103-4itv7blzr’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492352103-4itv7blzr’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Red Zone”,
“Other”
],
“datasets”: [
{
“data”: [
0,
0.10344827586206896
],
“stack”: “Team”,
“label”: “Alabama XR”,
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.625,
0.39655172413793105
],
“stack”: “Team”,
“label”: “Alabama SR”,
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“datalabels”: {
“display”: true
}
},
{
“data”: [
0,
0.07936507936507936
],
“stack”: “Opponent”,
“label”: “South Carolina XR”,
“backgroundColor”: “#1e2938CC”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.3333333333333333,
0.3968253968253968
],
“stack”: “Opponent”,
“label”: “South Carolina SR”,
“backgroundColor”: “#64748BCC”,
“datalabels”: {
“display”: true
}
},
{
“type”: “line”,
“data”: [
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamCounts”: [
8,
58
],
“oppCounts”: [
6,
63
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘red-zone-bars’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘red-zone-bars’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘red-zone-bars’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘red-zone-bars’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘red-zone-bars’.includes(‘top-rushers’) || ‘red-zone-bars’.includes(‘top-passers’) || ‘red-zone-bars’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘red-zone-bars’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘red-zone-bars’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘red-zone-bars’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘red-zone-bars’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492352103-4itv7blzr’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
For one, the Tide didn’t really spend much time in the Red Zone—aforementioned problems discussed—but when they did, they were pretty efficient! In fact, they were much more efficient than they were the rest of the game, which is a definite reversal of trends.
And heck, the Gamecocks didn’t spend much time in the Red Zone either (recall their 54-yard TD), but the Bama defense did a nice job of holding up there (which is not really a reversal of trends).
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
margin: 0;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-container {
background: white;
border-radius: 12px;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-header {
padding: 18px 24px 14px;
border-bottom: 1px solid #e5e5e5;
background: white;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-title {
font-size: 18px;
font-weight: 600;
color: #171717;
margin: 0;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-subtitle {
font-size: 11px;
font-weight: 400;
color: #737373;
margin: 4px 0 0 0;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-content {
padding: 20px 24px 24px !important;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-content { height: 325px; }
@media (max-width: 640px) {
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-content {
padding: 12px 16px 20px !important;
height: 280px !important;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .chart-header {
padding: 12px 16px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .embed-footer-top {
padding: 8px 12px !important;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions {
padding: 12px !important;
}
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .embed-footer {
border-top: 1px solid #e5e5e5;
font-size: 12px;
color: #737373;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .embed-footer-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .embed-footer-link {
color: #737373;
text-decoration: none;
font-weight: 500;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .embed-footer-link:hover {
color: #525252;
text-decoration: underline;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions-toggle {
background: none;
border: none;
color: #737373;
font-size: 12px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions-toggle:hover {
color: #525252;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .caret {
transition: transform 0.2s ease;
font-size: 10px;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .caret.expanded {
transform: rotate(180deg);
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions {
display: none;
padding: 16px;
background: #fafafa;
border-top: 1px solid #e5e5e5;
font-size: 12px;
line-height: 1.4;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions.expanded {
display: block;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.cfb-chart-embed-cfb-chart-1761492360089-4zplka07k .data-definitions li {
margin-bottom: 4px;
}
SR and XR by Distance to Go
Alabama vs. South Carolina • Oct 25, 2025
- Based roughly on the SP+ analytic system
- Successful play: Gains enough needed yards (50% 1st down, 70% on 2nd, 100% on 3rd/4th)
- Success Rate (SR): Percentage of plays that were successful
- Explosiveness Rate (XR): Percentage of plays gaining 15+ yards
- # Plays: Numbers shown in bars represent total play counts
- NCAA Avg: Dashed line shows 42% (roughly NCAA average) success rate
// Toggle data definitions accordion – unique function per embed
function toggleDefinitions_cfb_chart_1761492360089_4zplka07k() {
const definitions = document.getElementById(‘dataDefinitions_cfb-chart-1761492360089-4zplka07k’);
const caret = document.getElementById(‘caret_cfb-chart-1761492360089-4zplka07k’);
if (definitions.classList.contains(‘expanded’)) {
definitions.classList.remove(‘expanded’);
caret.classList.remove(‘expanded’);
} else {
definitions.classList.add(‘expanded’);
caret.classList.add(‘expanded’);
}
}
// Sequential script loading for better reliability
(function() {
‘use strict’;
let retryCount = 0;
const maxRetries = 50; // 5 seconds total
// Load scripts sequentially
function loadScript(url, callback) {
const script = document.createElement(‘script’);
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(‘Failed to load script:’, url);
showError(‘Failed to load required chart library’);
};
document.head.appendChild(script);
}
function showError(message) {
const canvas = document.getElementById(‘cfb-chart-1761492360089-4zplka07k’);
if (canvas && canvas.parentNode) {
canvas.parentNode.innerHTML = ‘
‘;
}
}
function initChart() {
retryCount++;
// Check if Chart.js is available
if (typeof Chart === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart library failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if datalabels plugin is available
if (typeof ChartDataLabels === ‘undefined’) {
if (retryCount >= maxRetries) {
showError(‘Chart plugin failed to load. Please refresh the page.’);
return;
}
setTimeout(initChart, 100);
return;
}
// Check if canvas element exists
const canvas = document.getElementById(‘cfb-chart-1761492360089-4zplka07k’);
if (!canvas) {
console.warn(‘Canvas element not found yet, retrying…’);
setTimeout(initChart, 100);
return;
}
// Prevent multiple chart instances
if (canvas.chartInstance) {
console.log(‘Chart already initialized’);
return;
}
try {
// Register the datalabels plugin
Chart.register(ChartDataLabels);
// Embed actual chart data directly
const chartData = {
“labels”: [
“Long (8+)”,
“Medium (4-7)”,
“Short (1-3)”
],
“datasets”: [
{
“data”: [
0.1282051282051282,
0.07142857142857142,
0
],
“stack”: “Team”,
“label”: “Alabama XR”,
“backgroundColor”: “rgba(101, 0, 20, 0.8)”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.41025641025641024,
0.42857142857142855,
0.46153846153846156
],
“stack”: “Team”,
“label”: “Alabama SR”,
“backgroundColor”: “rgba(175, 40, 60, 0.8)”,
“datalabels”: {
“display”: true
}
},
{
“data”: [
0.10204081632653061,
0,
0
],
“stack”: “Opponent”,
“label”: “South Carolina XR”,
“backgroundColor”: “#1e2938CC”,
“datalabels”: {
“display”: false
}
},
{
“data”: [
0.3877551020408163,
0.2857142857142857,
0.46153846153846156
],
“stack”: “Opponent”,
“label”: “South Carolina SR”,
“backgroundColor”: “#64748BCC”,
“datalabels”: {
“display”: true
}
},
{
“type”: “line”,
“data”: [
0.42,
0.42,
0.42
],
“label”: “NCAA Avg SR”,
“borderColor”: “#757575”,
“borderWidth”: 2,
“borderDash”: [
3,
3
],
“pointRadius”: 0,
“datalabels”: {
“display”: false
}
},
{
“type”: “line”,
“data”: [
null,
null,
null
],
“label”: “# Plays”,
“backgroundColor”: “rgba(0, 0, 0, 0)”,
“borderColor”: “rgba(0, 0, 0, 0)”,
“borderWidth”: 0,
“pointRadius”: 0,
“showLine”: false,
“fill”: false,
“datalabels”: {
“display”: false
}
}
],
“teamCounts”: [
39,
14,
13
],
“oppCounts”: [
49,
7,
13
],
“currentParams”: {
“year”: 2025,
“week”: 9,
“seasonType”: “regular”,
“team”: “Alabama”,
“gameId”: “401752751”
}
};
// Chart options (WordPress-safe)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 1 // Minimal animation to trigger layout calculation (fixes label positioning)
},
elements: ‘bar’ === ‘line’ ? ‘distance-bars’.includes(‘play-map’) ? {
line: {
tension: 0,
borderWidth: 0
}
} : ‘distance-bars’ === ‘win-probability’ ? {
line: {
tension: 0.15,
borderWidth: 2.2,
fill: false
},
point: {
pointRadius: 0,
pointHoverRadius: 4
}
} : {
line: {
tension: 0.25,
borderWidth: 2.2
},
point: {
pointRadius: ‘distance-bars’.includes(‘team-lines’) ? 0 : undefined
}
} : {},
plugins: {
datalabels: {
display: function(context) {
// Suppress data labels on line charts
if (‘bar’ === ‘line’) {
return false;
}
return context.dataset.datalabels && context.dataset.datalabels.display === true;
},
formatter: function(value, context) {
// Special handling for Overall Team Performance chart
if (‘distance-bars’ === ‘overall-team-performance’ && context.dataset.label === ‘Success Rate (SR)’) {
// Use the stored play count data
if (context.dataset.playCountData && context.dataset.playCountData[context.dataIndex]) {
return context.dataset.playCountData[context.dataIndex];
}
// Fallback to percentage if play count data not available
return Math.round(value * 100) + ‘%’;
}
// Handle bar charts with count data (play-type, quarter, down, etc.)
if (context.dataset.label && context.dataset.label.includes(‘ SR’) &&
(chartData.teamCounts || chartData.oppCounts)) {
// Find the first team SR dataset in the chart to determine team order
const allDatasets = context.chart.data.datasets;
const teamSRDataset = allDatasets.find(d => d.label && d.label.includes(‘ SR’) && !d.label.includes(‘NCAA’));
// If this is the first team’s SR dataset, use teamCounts
if (teamSRDataset && context.dataset.label === teamSRDataset.label && chartData.teamCounts) {
return chartData.teamCounts[context.dataIndex] || 0;
}
// Otherwise, use oppCounts for the second team
else if (chartData.oppCounts) {
return chartData.oppCounts[context.dataIndex] || 0;
}
}
// For player charts, show value only if > 0 (matches non-embedded behavior)
if (‘distance-bars’.includes(‘top-rushers’) || ‘distance-bars’.includes(‘top-passers’) || ‘distance-bars’.includes(‘top-receivers’)) {
// Hide data labels for zero or negative values, show actual value for positive values
return value > 0 ? value : null;
}
// For other charts, show values based on type
if (typeof value === ‘number’) {
// If value is between 0 and 1, treat as percentage
if (value >= 0 && value 0 ? ‘#26262660’ : ‘transparent’;
},
borderColor: function(context) {
const value = context.dataset.data[context.dataIndex];
return value > 0 ? ‘rgba(255, 255, 255, 0.2)’ : ‘transparent’;
},
borderRadius: 4,
align: ‘center’,
anchor: ‘center’
},
legend: ‘distance-bars’ === ‘win-probability’ ? {
display: false
} : ‘bar’ === ‘line’ ? {
position: ‘top’,
align: ‘start’,
labels: ‘distance-bars’.includes(‘play-map’) ? {
usePointStyle: true,
generateLabels: function(chart) {
// Call the original generateLabels to get default styling
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter and customize each label
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘ {
const dataset = chart.data.datasets[label.datasetIndex];
if (dataset && dataset.label) {
if (dataset.label.includes(‘Rush’)) {
label.pointStyle = ‘circle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else if (dataset.label.includes(‘Pass’)) {
label.pointStyle = ‘triangle’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
} else {
label.pointStyle = ‘rect’;
label.pointStyleWidth = 4;
label.fillStyle = ‘white’;
}
}
});
return filteredLabels;
},
boxWidth: 20,
padding: 12
} : {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
generateLabels: function(chart) {
const original = Chart.defaults.plugins.legend.labels.generateLabels;
const labels = original.call(this, chart);
// Filter out reference areas and ensure white fill
const filteredLabels = labels.filter(label => {
return !label.text.includes(‘NCAA Avg SR’) &&
!label.text.includes(’50/50′) &&
!label.text.includes(‘Quarters’);
});
// Ensure white fill for all line chart legend boxes
filteredLabels.forEach((label) => {
label.fillStyle = ‘white’;
});
return filteredLabels;
}
}
} : {
position: ‘top’,
align: ‘start’,
labels: {
usePointStyle: false,
boxWidth: 12,
boxHeight: 12,
padding: 12,
filter: function(legendItem, chartData) {
return !legendItem.text.includes(‘NCAA Avg SR’) &&
!legendItem.text.includes(‘Quarters’) &&
!legendItem.text.includes(’50/50′);
},
generateLabels: function(chart) {
const data = chart.data;
if (data.datasets.length) {
return data.datasets.map((dataset, i) => {
// Handle backgroundColor arrays (like in Overall Team Performance chart)
let fillColor = dataset.backgroundColor;
if (dataset.label === ‘# Plays’) {
fillColor = ‘white’;
} else if (Array.isArray(dataset.backgroundColor)) {
// For datasets with backgroundColor arrays, use the first color for legend
fillColor = dataset.backgroundColor[0];
}
return {
text: dataset.label,
fillStyle: fillColor,
strokeStyle: dataset.label === ‘# Plays’ ? ‘#666’ : dataset.borderColor,
lineWidth: dataset.label === ‘# Plays’ ? 1 : dataset.borderWidth,
hidden: !chart.isDatasetVisible(i),
datasetIndex: i
};
}).filter((item, index) => {
// Apply the same filter logic as above
const dataset = chart.data.datasets[index];
if (!dataset || !dataset.data) return false;
if (dataset.label === ‘# Plays’) return true; // Always show # Plays
if (dataset.label && (dataset.label.includes(‘NCAA Avg SR’) ||
dataset.label.includes(‘Quarters’) ||
dataset.label.includes(’50/50′))) return false;
return dataset.data.some((value) => value > 0);
});
}
return [];
}
}
},
tooltip: ‘distance-bars’ === ‘win-probability’ ? {
mode: ‘index’,
intersect: false,
callbacks: {
title: function(tooltipItems) {
if (tooltipItems && tooltipItems[0]) {
return ‘Play ‘ + (tooltipItems[0].dataIndex + 1);
}
return ”;
},
label: function(context) {
const selectedTeamWinProb = context.parsed.y;
const opponentWinProb = 100 – selectedTeamWinProb;
const selectedTeam = context.dataset.selectedTeam || ‘Team’;
const opponentTeam = context.dataset.opponentTeam || ‘Opponent’;
return [
selectedTeam + ‘: ‘ + selectedTeamWinProb.toFixed(1) + ‘%’,
opponentTeam + ‘: ‘ + opponentWinProb.toFixed(1) + ‘%’
];
},
afterLabel: function(context) {
if (context.dataset.playTexts && context.dataset.playTexts[context.dataIndex]) {
return ‘n’ + context.dataset.playTexts[context.dataIndex];
}
return ”;
}
}
} : {
filter: function(tooltipItem) {
if (‘distance-bars’.includes(‘play-map’)) {
return !tooltipItem.dataset.label.includes(‘< 0’) &&
!tooltipItem.dataset.label.includes(‘Quarters’) &&
!tooltipItem.dataset.label.includes(‘Drive’);
}
return !tooltipItem.dataset.label.includes(‘NCAA Avg SR’) &&
!tooltipItem.dataset.label.includes(’50/50′) &&
!tooltipItem.dataset.label.includes(‘ ds.label === ‘Win Probability’);
if (wpDataset && wpDataset.segmentColors) {
wpDataset.segment = {
borderColor: function(ctx) {
// Use p1DataIndex (ending point) so the line inherits the destination color
// This makes momentum shifts more visually intuitive
const index = ctx.p1DataIndex;
if (index !== undefined && wpDataset.segmentColors[index]) {
return wpDataset.segmentColors[index];
}
return wpDataset.borderColor || ‘#8B0000’;
}
};
}
}
// Initialize the chart
const ctx = canvas.getContext(‘2d’);
const chart = new Chart(ctx, {
type: ‘bar’,
data: chartData,
options: chartOptions
});
// Store reference to prevent re-initialization
canvas.chartInstance = chart;
console.log(‘CFB Chart initialized successfully’);
} catch (error) {
console.error(‘Error initializing CFB chart:’, error);
// Fallback: show error message in canvas container
const container = document.getElementById(‘cfb-chart-1761492360089-4zplka07k’).parentNode;
if (container) {
container.innerHTML = ‘
‘;
}
}
}
// Start loading scripts sequentially
function startLoading() {
// First, check if scripts are already loaded (multiple embeds on same page)
if (typeof Chart !== ‘undefined’ && typeof ChartDataLabels !== ‘undefined’) {
initChart();
return;
}
// Load Chart.js first
if (typeof Chart === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js’, function() {
// Then load ChartDataLabels
if (typeof ChartDataLabels === ‘undefined’) {
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
});
} else if (typeof ChartDataLabels === ‘undefined’) {
// Chart.js loaded but not ChartDataLabels
loadScript(‘https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0’, function() {
initChart();
});
} else {
initChart();
}
}
// Initialize when DOM is ready
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, startLoading);
} else {
startLoading();
}
})();
But the Bama defense did reverse an unfortunate trend lately where opposing offenses are very efficient from short yardage. Maybe this is partially on the Gamecocks for having a weak rushing offense (outside of QB scrambles), but Alabama was able to keep their short yardage conversion low. (Unfortunately, they still managed to convert a few 4th downs to negate some of that advantage of 3rd-and-short stops).
(Takes a breath). Ok. Well. It’s the bye week!
So, the games are not always pretty, but some of us just want to see this team make the playoff and rebuild to more persistent success. If it takes a bounces our way, a few “gritty wins” to get there, so be it. If every game is going to be close, all we can ask for is for them to win them.
And hopefully some bye week rest—and maybe even a little tinkering—helps them get over the hump and start winning with some separation. (Not that the schedule really suggests that’s likely).
All that said, I suspect the Tide are a little overrated going into the LSU game (and likely will be by the committee as well). Time will tell if they can get the wins they need to reach the SEC Championship and early that top-3-or-4 ranking; but that these games are always so close—“tough, gritty wins”—has to mean something. And SP+ right now has Alabama at #9, which sounds about right to me.
Roll Tide, y’all. Rest up.