fix: complete blame form buttons with styling and functionality
- Added missing CSS styles for all buttons (submit-btn, generate-btn, nuclear-option) - Implemented full JavaScript functionality for form submission, apology generation, and nuclear option - Added sound effects using Web Audio API - Improved form layout and sidebar styling - Added responsive design improvements - Fixed visual issues with rage-meter, shit-list, and footer elements - Added interactive features: auto-blame, clear log, sound toggle, easter egg
This commit is contained in:
456
www/index.html
456
www/index.html
@@ -96,6 +96,16 @@
|
||||
padding: 5px;
|
||||
animation: warning-blink 1s infinite;
|
||||
}
|
||||
.dustin-face {
|
||||
font-size: 3em;
|
||||
margin-bottom: 10px;
|
||||
animation: face-shake 3s infinite;
|
||||
}
|
||||
@keyframes face-shake {
|
||||
0%, 100% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(5deg); }
|
||||
75% { transform: rotate(-5deg); }
|
||||
}
|
||||
@keyframes warning-blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
@@ -197,6 +207,60 @@
|
||||
.stat-value.yellow {
|
||||
color: var(--terminal-yellow);
|
||||
}
|
||||
.rage-meter {
|
||||
height: 10px;
|
||||
background: rgba(255, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.rage-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--terminal-red), var(--terminal-amber));
|
||||
border-radius: 5px;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
.shit-list {
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid var(--terminal-green);
|
||||
}
|
||||
.shit-list-title {
|
||||
color: var(--terminal-yellow);
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.shit-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px dashed rgba(0, 255, 0, 0.3);
|
||||
color: var(--terminal-text);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.shit-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--terminal-green);
|
||||
color: var(--terminal-cyan);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.footer p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.easter-egg {
|
||||
color: var(--terminal-magenta);
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
margin-top: 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
.easter-egg:hover {
|
||||
color: var(--terminal-red);
|
||||
}
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
@@ -223,6 +287,20 @@
|
||||
max-height: 600px;
|
||||
}
|
||||
}
|
||||
.sidebar {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 2px solid var(--terminal-green);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
padding: 25px;
|
||||
}
|
||||
}
|
||||
.log-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -267,6 +345,122 @@
|
||||
background: var(--terminal-cyan);
|
||||
color: var(--terminal-bg);
|
||||
}
|
||||
.control-btn.danger {
|
||||
border-color: var(--terminal-red);
|
||||
color: var(--terminal-red);
|
||||
}
|
||||
.control-btn.danger:hover {
|
||||
background: var(--terminal-red);
|
||||
color: var(--terminal-bg);
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
color: var(--terminal-yellow);
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid var(--terminal-green);
|
||||
color: var(--terminal-text);
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--terminal-cyan);
|
||||
box-shadow: 0 0 10px var(--terminal-cyan);
|
||||
}
|
||||
.form-group textarea {
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
.blame-generator, .apology-generator {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 2px solid var(--terminal-green);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.blame-title, .apology-title {
|
||||
color: var(--terminal-red);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.2em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: 1px solid var(--terminal-green);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.apology-title {
|
||||
color: var(--terminal-magenta);
|
||||
}
|
||||
.apology-display {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border: 1px solid var(--terminal-magenta);
|
||||
padding: 15px;
|
||||
border-radius: 3px;
|
||||
min-height: 80px;
|
||||
margin-bottom: 15px;
|
||||
font-style: italic;
|
||||
color: var(--terminal-text);
|
||||
}
|
||||
.submit-btn, .generate-btn, .nuclear-option {
|
||||
background: var(--terminal-bg);
|
||||
border: 2px solid var(--terminal-red);
|
||||
color: var(--terminal-red);
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.submit-btn:hover {
|
||||
background: var(--terminal-red);
|
||||
color: var(--terminal-bg);
|
||||
box-shadow: 0 0 15px var(--terminal-red);
|
||||
}
|
||||
.generate-btn {
|
||||
border-color: var(--terminal-magenta);
|
||||
color: var(--terminal-magenta);
|
||||
}
|
||||
.generate-btn:hover {
|
||||
background: var(--terminal-magenta);
|
||||
color: var(--terminal-bg);
|
||||
box-shadow: 0 0 15px var(--terminal-magenta);
|
||||
}
|
||||
.nuclear-option {
|
||||
border-color: var(--terminal-amber);
|
||||
color: var(--terminal-amber);
|
||||
font-size: 1.1em;
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
animation: nuclear-pulse 2s infinite;
|
||||
}
|
||||
.nuclear-option:hover {
|
||||
background: var(--terminal-amber);
|
||||
color: var(--terminal-bg);
|
||||
box-shadow: 0 0 20px var(--terminal-amber);
|
||||
animation: none;
|
||||
}
|
||||
@keyframes nuclear-pulse {
|
||||
0%, 100% { box-shadow: 0 0 10px var(--terminal-amber); }
|
||||
50% { box-shadow: 0 0 25px var(--terminal-amber); }
|
||||
}
|
||||
.incident {
|
||||
margin-bottom: 15px;
|
||||
padding: 15px;
|
||||
@@ -381,7 +575,267 @@
|
||||
<p class="easter-egg" id="easter-egg">Click here if you actually like Dustin (you monster)</p>
|
||||
</div>
|
||||
<script>
|
||||
// JavaScript functionality as previously defined...
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// DOM Elements
|
||||
const blameForm = document.getElementById('blame-form');
|
||||
const generateApologyBtn = document.getElementById('generate-apology');
|
||||
const nuclearOptionBtn = document.getElementById('nuclear-option');
|
||||
const autoAddBtn = document.getElementById('auto-add');
|
||||
const clearLogBtn = document.getElementById('clear-log');
|
||||
const soundToggle = document.getElementById('sound-toggle');
|
||||
const logContainer = document.getElementById('log');
|
||||
const totalFuckups = document.getElementById('total-fuckups');
|
||||
const apologiesCount = document.getElementById('apologies-count');
|
||||
const hoursWasted = document.getElementById('hours-wasted');
|
||||
const apologyDisplay = document.getElementById('apology-display');
|
||||
const sincerityLevel = document.getElementById('sincerity-level');
|
||||
const easterEgg = document.getElementById('easter-egg');
|
||||
|
||||
// State
|
||||
let soundEnabled = true;
|
||||
let fuckupCount = 1247;
|
||||
let apologyCount = 42;
|
||||
let hoursCount = 3728;
|
||||
|
||||
// Incident templates
|
||||
const incidents = [
|
||||
"Dustin forgot to renew the SSL certificate — again.",
|
||||
"Dustin scheduled the database backup during peak hours.",
|
||||
"Dustin 'accidentally' pushed to production on a Friday afternoon.",
|
||||
"Dustin's 'quick fix' broke the entire login system.",
|
||||
"Dustin used the CEO's account to test the new spam filter.",
|
||||
"Dustin thought 'rm -rf /' was a good way to free up disk space.",
|
||||
"Dustin set the password policy to 'password123' for 'convenience'.",
|
||||
"Dustin uploaded the customer database to a public GitHub repo.",
|
||||
"Dustin replied to a phishing email with admin credentials.",
|
||||
"Dustin thought blockchain would solve our inventory tracking issues."
|
||||
];
|
||||
|
||||
// Apology templates
|
||||
const apologies = [
|
||||
"My bad, I thought that was the test environment. Whoops!",
|
||||
"In my defense, the documentation was unclear. And by unclear, I mean non-existent.",
|
||||
"That was a feature, not a bug. You just don't understand my vision.",
|
||||
"It worked on my machine. Must be a you problem.",
|
||||
"I was following industry best practices. From 1998.",
|
||||
"The AI told me to do it. Can't argue with the algorithm, right?",
|
||||
"I was trying to be proactive. Turns out, proactivity causes outages.",
|
||||
"It's not my fault — the system was designed to fail. I just... helped.",
|
||||
"I was optimizing for scalability. Current scalability: zero.",
|
||||
"Look, we all make mistakes. Mine just happen to take down entire systems."
|
||||
];
|
||||
|
||||
// Sound effects
|
||||
function playSound(type) {
|
||||
if (!soundEnabled) return;
|
||||
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
switch(type) {
|
||||
case 'blame':
|
||||
oscillator.frequency.setValueAtTime(440, audioContext.currentTime);
|
||||
oscillator.type = 'sawtooth';
|
||||
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
|
||||
oscillator.start();
|
||||
oscillator.stop(audioContext.currentTime + 0.5);
|
||||
break;
|
||||
case 'apology':
|
||||
oscillator.frequency.setValueAtTime(220, audioContext.currentTime);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.gain.setValueAtTime(0.05, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 1);
|
||||
oscillator.start();
|
||||
oscillator.stop(audioContext.currentTime + 1);
|
||||
break;
|
||||
case 'nuclear':
|
||||
oscillator.frequency.setValueAtTime(55, audioContext.currentTime);
|
||||
oscillator.frequency.exponentialRampToValueAtTime(880, audioContext.currentTime + 2);
|
||||
oscillator.type = 'square';
|
||||
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 2);
|
||||
oscillator.start();
|
||||
oscillator.stop(audioContext.currentTime + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add incident to log
|
||||
function addIncident(text, severity = 'critical') {
|
||||
const incident = document.createElement('div');
|
||||
incident.className = 'incident';
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const severityColors = {
|
||||
minor: 'var(--terminal-green)',
|
||||
warning: 'var(--terminal-yellow)',
|
||||
critical: 'var(--terminal-red)',
|
||||
apocalyptic: 'var(--terminal-magenta)'
|
||||
};
|
||||
|
||||
incident.innerHTML = `
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
|
||||
<strong style="color: ${severityColors[severity]}">[${severity.toUpperCase()}]</strong>
|
||||
<span style="color: var(--terminal-cyan); font-size: 0.9em;">${timestamp}</span>
|
||||
</div>
|
||||
<div>${text}</div>
|
||||
`;
|
||||
|
||||
logContainer.insertBefore(incident, logContainer.firstChild);
|
||||
|
||||
// Limit log to 20 items
|
||||
if (logContainer.children.length > 20) {
|
||||
logContainer.removeChild(logContainer.lastChild);
|
||||
}
|
||||
|
||||
// Update stats
|
||||
fuckupCount++;
|
||||
hoursCount += Math.floor(Math.random() * 5) + 1;
|
||||
updateStats();
|
||||
|
||||
// Play sound
|
||||
playSound('blame');
|
||||
}
|
||||
|
||||
// Update statistics display
|
||||
function updateStats() {
|
||||
totalFuckups.textContent = fuckupCount.toLocaleString();
|
||||
apologiesCount.textContent = apologyCount;
|
||||
hoursWasted.textContent = hoursCount.toLocaleString();
|
||||
}
|
||||
|
||||
// Generate random apology
|
||||
function generateApology() {
|
||||
const apology = apologies[Math.floor(Math.random() * apologies.length)];
|
||||
const sincerity = Math.floor(Math.random() * 101);
|
||||
|
||||
apologyDisplay.textContent = `"${apology}"`;
|
||||
sincerityLevel.textContent = `${sincerity}%`;
|
||||
|
||||
apologyCount++;
|
||||
updateStats();
|
||||
|
||||
// Play sound
|
||||
playSound('apology');
|
||||
|
||||
// Visual feedback
|
||||
apologyDisplay.style.animation = 'none';
|
||||
setTimeout(() => {
|
||||
apologyDisplay.style.animation = 'fadeIn 0.5s';
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// Nuclear option
|
||||
function activateNuclearOption() {
|
||||
if (!confirm("🚨 WARNING: This will fire Dustin permanently! 🚨\n\nAre you absolutely sure? (This action cannot be undone and will probably make things worse)")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the log
|
||||
logContainer.innerHTML = '';
|
||||
|
||||
// Add nuclear incident
|
||||
addIncident("🚨 NUCLEAR OPTION ACTIVATED: DUSTIN HAS BEEN TERMINATED 🚨", 'apocalyptic');
|
||||
addIncident("System stability: CRITICAL - Without Dustin to blame, reality is collapsing", 'apocalyptic');
|
||||
addIncident("Paradox detected: If Dustin is fired, who do we blame for firing Dustin?", 'apocalyptic');
|
||||
|
||||
// Update stats dramatically
|
||||
fuckupCount += 1000;
|
||||
hoursCount += 500;
|
||||
updateStats();
|
||||
|
||||
// Play nuclear sound
|
||||
playSound('nuclear');
|
||||
|
||||
// Visual effects
|
||||
document.body.style.animation = 'none';
|
||||
setTimeout(() => {
|
||||
document.body.style.animation = 'shake 0.5s';
|
||||
}, 10);
|
||||
|
||||
// Disable nuclear button
|
||||
nuclearOptionBtn.disabled = true;
|
||||
nuclearOptionBtn.textContent = "🚨 DUSTIN TERMINATED 🚨";
|
||||
nuclearOptionBtn.style.opacity = '0.5';
|
||||
nuclearOptionBtn.style.cursor = 'not-allowed';
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
blameForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const severity = document.getElementById('severity').value;
|
||||
const description = document.getElementById('description').value.trim();
|
||||
|
||||
if (!description) {
|
||||
alert("Please describe Dustin's mess-up. Be creative!");
|
||||
return;
|
||||
}
|
||||
|
||||
addIncident(description, severity);
|
||||
|
||||
// Reset form
|
||||
document.getElementById('description').value = '';
|
||||
document.getElementById('severity').value = 'critical';
|
||||
|
||||
// Visual feedback
|
||||
blameForm.style.animation = 'none';
|
||||
setTimeout(() => {
|
||||
blameForm.style.animation = 'fadeIn 0.5s';
|
||||
}, 10);
|
||||
});
|
||||
|
||||
generateApologyBtn.addEventListener('click', generateApology);
|
||||
nuclearOptionBtn.addEventListener('click', activateNuclearOption);
|
||||
|
||||
autoAddBtn.addEventListener('click', function() {
|
||||
const randomIncident = incidents[Math.floor(Math.random() * incidents.length)];
|
||||
const severities = ['minor', 'warning', 'critical', 'apocalyptic'];
|
||||
const randomSeverity = severities[Math.floor(Math.random() * severities.length)];
|
||||
|
||||
addIncident(randomIncident, randomSeverity);
|
||||
});
|
||||
|
||||
clearLogBtn.addEventListener('click', function() {
|
||||
if (confirm("Clear the entire incident log? (Dustin probably suggested this)")) {
|
||||
logContainer.innerHTML = '';
|
||||
addIncident("Incident log cleared. This was definitely Dustin's idea.", 'warning');
|
||||
}
|
||||
});
|
||||
|
||||
soundToggle.addEventListener('click', function() {
|
||||
soundEnabled = !soundEnabled;
|
||||
soundToggle.textContent = soundEnabled ? "🔊 Sound: ON" : "🔇 Sound: OFF";
|
||||
});
|
||||
|
||||
easterEgg.addEventListener('click', function() {
|
||||
alert("You monster! Dustin actually appreciates your support.\n\nHe's still at fault for everything, but thanks for trying.");
|
||||
addIncident("Someone actually clicked the 'like Dustin' button. Investigation required.", 'critical');
|
||||
});
|
||||
|
||||
// Add shake animation for nuclear option
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(10px); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Initialize with some incidents
|
||||
setTimeout(() => {
|
||||
addIncident("System initialized. Ready to blame Dustin for everything.", 'warning');
|
||||
addIncident("Dustin already blamed for today's coffee machine outage.", 'minor');
|
||||
addIncident("Dustin suspected in DNS resolution issues. Investigation ongoing.", 'critical');
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user