|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Technology Tree Evolution</title> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> |
|
<style> |
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background-color: #f0f2f5; |
|
color: #333; |
|
overflow-y: auto; |
|
overflow-x: hidden; |
|
} |
|
|
|
header { |
|
background: linear-gradient(135deg, #2c3e50, #4ca1af); |
|
color: white; |
|
padding: 2rem 0; |
|
text-align: center; |
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
h1 { |
|
font-size: 2.5rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.subtitle { |
|
font-size: 1.2rem; |
|
opacity: 0.9; |
|
} |
|
|
|
.container { |
|
max-width: 1200px; |
|
margin: 2rem auto; |
|
padding: 0 1rem; |
|
display: flex; |
|
} |
|
|
|
.controls { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: flex-start; |
|
margin-bottom: 2rem; |
|
flex-wrap: wrap; |
|
gap: 1rem; |
|
width: 200px; |
|
margin-right: 2rem; |
|
flex-direction: column; |
|
} |
|
|
|
.era-navigation { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 0.5rem; |
|
width: 100%; |
|
} |
|
|
|
.add-tech-btn { |
|
padding: 0.8rem 1.5rem; |
|
border: none; |
|
border-radius: 30px; |
|
font-weight: bold; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
background-color: #9d4edd; |
|
color: white; |
|
font-size: 0.9rem; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
width: 100%; |
|
} |
|
|
|
.add-tech-btn:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
|
background-color: #7b2cbf; |
|
} |
|
|
|
.era-button { |
|
padding: 0.8rem 1.5rem; |
|
border: none; |
|
border-radius: 30px; |
|
font-weight: bold; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
font-size: 0.9rem; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
width: 100%; |
|
text-align: left; |
|
} |
|
|
|
.era-button:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
|
} |
|
|
|
.era-ancient { |
|
background-color: #e9c46a; |
|
color: #333; |
|
} |
|
|
|
.era-classical { |
|
background-color: #f4a261; |
|
color: white; |
|
} |
|
|
|
.era-medieval { |
|
background-color: #e76f51; |
|
color: white; |
|
} |
|
|
|
.era-industrial { |
|
background-color: #2a9d8f; |
|
color: white; |
|
} |
|
|
|
.era-modern { |
|
background-color: #1d3557; |
|
color: white; |
|
} |
|
|
|
.era-future { |
|
background-color: #9d4edd; |
|
color: white; |
|
} |
|
|
|
.tech-tree-container { |
|
width: calc(100% - 220px); |
|
height: 800px; |
|
overflow: auto; |
|
background: white; |
|
border-radius: 12px; |
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); |
|
padding: 2rem; |
|
position: relative; |
|
margin-bottom: 3rem; |
|
} |
|
|
|
.tech-tree { |
|
position: relative; |
|
width: 100%; |
|
height: 3500px; |
|
margin: 0 auto; |
|
} |
|
|
|
.era-line { |
|
position: absolute; |
|
width: 100%; |
|
height: 2px; |
|
background-color: rgba(0, 0, 0, 0.1); |
|
z-index: 0; |
|
left: 0; |
|
} |
|
|
|
.ancient-line { top: 150px; } |
|
.classical-line { top: 450px; } |
|
.medieval-line { top: 750px; } |
|
.industrial-line { top: 1050px; } |
|
.modern-line { top: 1350px; } |
|
.future-line { top: 1650px; } |
|
|
|
.era-labels { |
|
position: absolute; |
|
left: 0; |
|
width: 100px; |
|
display: flex; |
|
flex-direction: column; |
|
gap: 300px; |
|
z-index: 5; |
|
font-weight: bold; |
|
color: #555; |
|
text-transform: uppercase; |
|
font-size: 0.9rem; |
|
letter-spacing: 1px; |
|
top: 0; |
|
padding-left: 20px; |
|
} |
|
|
|
.tech-node { |
|
position: absolute; |
|
width: 100px; |
|
height: 100px; |
|
border-radius: 50%; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: center; |
|
text-align: center; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
z-index: 10; |
|
transform: scale(0.95); |
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
.tech-node:hover { |
|
transform: scale(1.05); |
|
z-index: 20; |
|
} |
|
|
|
.tech-icon { |
|
font-size: 1.8rem; |
|
margin-bottom: 0.3rem; |
|
} |
|
|
|
.tech-name { |
|
font-weight: bold; |
|
font-size: 0.7rem; |
|
} |
|
|
|
.tech-era { |
|
font-size: 0.6rem; |
|
opacity: 0.8; |
|
margin-top: 2px; |
|
} |
|
|
|
|
|
.ancient { |
|
background-color: #e9c46a; |
|
color: #333; |
|
} |
|
|
|
.classical { |
|
background-color: #f4a261; |
|
color: white; |
|
} |
|
|
|
.medieval { |
|
background-color: #e76f51; |
|
color: white; |
|
} |
|
|
|
.industrial { |
|
background-color: #2a9d8f; |
|
color: white; |
|
} |
|
|
|
.modern { |
|
background-color: #1d3557; |
|
color: white; |
|
} |
|
|
|
.future { |
|
background-color: #9d4edd; |
|
color: white; |
|
} |
|
|
|
|
|
.connection { |
|
position: absolute; |
|
background-color: #aaa; |
|
height: 2px; |
|
z-index: 1; |
|
transform-origin: 0 0; |
|
} |
|
|
|
.node-popup { |
|
position: fixed; |
|
top: 50%; |
|
left: 50%; |
|
transform: translate(-50%, -50%) scale(0.9); |
|
background: white; |
|
padding: 2rem; |
|
border-radius: 12px; |
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); |
|
z-index: 100; |
|
max-width: 400px; |
|
opacity: 0; |
|
pointer-events: none; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.node-popup.active { |
|
transform: translate(-50%, -50%) scale(1); |
|
opacity: 1; |
|
pointer-events: auto; |
|
} |
|
|
|
.node-popup h3 { |
|
margin-bottom: 0.5rem; |
|
color: #444; |
|
font-size: 1.5rem; |
|
} |
|
|
|
.node-popup .era { |
|
font-size: 0.9rem; |
|
margin-bottom: 1rem; |
|
font-weight: bold; |
|
padding: 0.3rem 0.8rem; |
|
background-color: #f0f0f0; |
|
display: inline-block; |
|
border-radius: 20px; |
|
color: #555; |
|
} |
|
|
|
.node-popup .description { |
|
margin-bottom: 1.5rem; |
|
line-height: 1.6; |
|
color: #555; |
|
} |
|
|
|
.node-popup .prerequisites { |
|
font-size: 0.9rem; |
|
color: #666; |
|
padding: 0.8rem; |
|
background-color: #f8f8f8; |
|
border-radius: 6px; |
|
border-left: 3px solid #9d4edd; |
|
} |
|
|
|
.close-popup { |
|
position: absolute; |
|
top: 10px; |
|
right: 15px; |
|
font-size: 1.5rem; |
|
cursor: pointer; |
|
color: #888; |
|
transition: all 0.2s; |
|
} |
|
|
|
.close-popup:hover { |
|
color: #333; |
|
transform: rotate(90deg); |
|
} |
|
|
|
.add-node-form { |
|
position: fixed; |
|
top: 50%; |
|
left: 50%; |
|
transform: translate(-50%, -50%) scale(0.9); |
|
background: white; |
|
padding: 1rem; |
|
border-radius: 12px; |
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); |
|
z-index: 100; |
|
width: 90%; |
|
max-height: 80vh; |
|
overflow-y: auto; |
|
max-width: 320px; |
|
opacity: 0; |
|
pointer-events: none; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.add-node-form.active { |
|
transform: translate(-50%, -50%) scale(1); |
|
opacity: 1; |
|
pointer-events: auto; |
|
} |
|
|
|
.add-node-form h2 { |
|
margin-bottom: 0.8rem; |
|
color: #444; |
|
font-size: 1.1rem; |
|
padding-bottom: 0.5rem; |
|
border-bottom: 1px solid #eee; |
|
} |
|
|
|
.form-group { |
|
margin-bottom: 0.6rem; |
|
} |
|
|
|
.form-group label { |
|
display: block; |
|
margin-bottom: 0.2rem; |
|
font-weight: bold; |
|
color: #555; |
|
font-size: 0.8rem; |
|
} |
|
|
|
.form-group input, |
|
.form-group select, |
|
.form-group textarea { |
|
width: 100%; |
|
padding: 0.5rem; |
|
border: 1px solid #ddd; |
|
border-radius: 6px; |
|
font-family: inherit; |
|
font-size: 0.8rem; |
|
transition: all 0.2s; |
|
} |
|
|
|
.form-group input:focus, |
|
.form-group select:focus, |
|
.form-group textarea:focus { |
|
border-color: #9d4edd; |
|
outline: none; |
|
box-shadow: 0 0 0 3px rgba(157, 78, 221, 0.2); |
|
} |
|
|
|
.form-group textarea { |
|
min-height: 60px; |
|
resize: vertical; |
|
} |
|
|
|
.form-actions { |
|
display: flex; |
|
justify-content: flex-end; |
|
gap: 0.6rem; |
|
margin-top: 0.8rem; |
|
padding-top: 0.8rem; |
|
border-top: 1px solid #eee; |
|
} |
|
|
|
.form-actions button { |
|
padding: 0.5rem 1rem; |
|
border: none; |
|
border-radius: 6px; |
|
cursor: pointer; |
|
font-weight: bold; |
|
font-size: 0.8rem; |
|
transition: all 0.2s; |
|
} |
|
|
|
.form-cancel { |
|
background-color: #f0f0f0; |
|
color: #555; |
|
} |
|
|
|
.form-cancel:hover { |
|
background-color: #e0e0e0; |
|
} |
|
|
|
.form-submit { |
|
background-color: #9d4edd; |
|
color: white; |
|
} |
|
|
|
.form-submit:hover { |
|
background-color: #7b2cbf; |
|
} |
|
|
|
.checkbox-group { |
|
margin-top: 0.3rem; |
|
max-height: 100px; |
|
overflow-y: auto; |
|
border: 1px solid #eee; |
|
padding: 0.3rem; |
|
border-radius: 4px; |
|
font-size: 0.75rem; |
|
} |
|
|
|
.checkbox-item { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 0.2rem; |
|
font-size: 0.75rem; |
|
padding: 0.2rem; |
|
border-radius: 3px; |
|
transition: all 0.2s; |
|
} |
|
|
|
.checkbox-item:hover { |
|
background-color: #f5f5f5; |
|
} |
|
|
|
.checkbox-item input { |
|
width: auto; |
|
margin-right: 0.3rem; |
|
accent-color: #9d4edd; |
|
} |
|
|
|
.icon-selecter { |
|
display: grid; |
|
grid-template-columns: repeat(4, 1fr); |
|
gap: 0.3rem; |
|
margin-top: 0.3rem; |
|
max-height: 120px; |
|
overflow-y: auto; |
|
padding: 0.3rem; |
|
border: 1px solid #eee; |
|
border-radius: 4px; |
|
} |
|
|
|
.icon-option { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
padding: 0.3rem; |
|
border: 1px solid #ddd; |
|
border-radius: 4px; |
|
cursor: pointer; |
|
transition: all 0.2s; |
|
font-size: 0.7rem; |
|
} |
|
|
|
.icon-option:hover { |
|
border-color: #9d4edd; |
|
background-color: #f9f0ff; |
|
} |
|
|
|
.icon-option.selected { |
|
border-color: #9d4edd; |
|
background-color: #f0d6ff; |
|
} |
|
|
|
.icon-option i { |
|
font-size: 1rem; |
|
margin-bottom: 0.1rem; |
|
} |
|
|
|
footer { |
|
text-align: center; |
|
padding: 2rem 0; |
|
background-color: #2c3e50; |
|
color: white; |
|
} |
|
|
|
.timeline-bar { |
|
position: absolute; |
|
top: 0; |
|
right: 0; |
|
width: 4px; |
|
height: 100%; |
|
background: linear-gradient(to bottom, #e9c46a, #f4a261, #e76f51, #2a9d8f, #1d3557, #9d4edd); |
|
border-radius: 0 0 12px 12px; |
|
} |
|
|
|
.tech-path { |
|
position: absolute; |
|
width: 4px; |
|
height: 0; |
|
background-color: rgba(255, 255, 255, 0.8); |
|
z-index: 2; |
|
top: 0; |
|
right: 0; |
|
transition: height 0.5s ease-in-out; |
|
} |
|
|
|
.drag-handle { |
|
position: absolute; |
|
top: -5px; |
|
right: -5px; |
|
width: 20px; |
|
height: 20px; |
|
background-color: rgba(255, 255, 255, 0.8); |
|
border-radius: 50%; |
|
border: 1px solid #ccc; |
|
cursor: move; |
|
display: none; |
|
z-index: 30; |
|
font-size: 0.7rem; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
color: #555; |
|
} |
|
|
|
.tech-node.editable .drag-handle { |
|
display: flex; |
|
} |
|
|
|
.delete-node { |
|
position: absolute; |
|
top: -5px; |
|
left: -5px; |
|
width: 20px; |
|
height: 20px; |
|
background-color: #ff6b6b; |
|
border-radius: 50%; |
|
cursor: pointer; |
|
display: none; |
|
z-index: 30; |
|
font-size: 0.7rem; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
color: white; |
|
opacity: 0; |
|
transition: opacity 0.2s; |
|
} |
|
|
|
.tech-node.editable:hover .delete-node { |
|
opacity: 1; |
|
} |
|
|
|
.editable-mode { |
|
border: 2px dashed #9d4edd; |
|
} |
|
|
|
.toggle-edit-mode { |
|
padding: 0.8rem 1.5rem; |
|
border: none; |
|
border-radius: 30px; |
|
font-weight: bold; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
font-size: 0.9rem; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
background-color: #ff6b6b; |
|
color: white; |
|
width: 100%; |
|
text-align: left; |
|
} |
|
|
|
.toggle-edit-mode:hover { |
|
background-color: #ff5252; |
|
} |
|
|
|
.toggle-edit-mode.active { |
|
background-color: #4CAF50; |
|
} |
|
|
|
|
|
.controls-section { |
|
background-color: white; |
|
padding: 1rem; |
|
border-radius: 8px; |
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.controls-title { |
|
font-size: 0.9rem; |
|
font-weight: bold; |
|
color: #555; |
|
margin-bottom: 0.5rem; |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
} |
|
|
|
.controls-title i { |
|
font-size: 1rem; |
|
} |
|
|
|
|
|
@keyframes nodeAppear { |
|
0% { |
|
transform: scale(0.5) rotate(0deg); |
|
opacity: 0; |
|
} |
|
100% { |
|
transform: scale(1) rotate(0deg); |
|
opacity: 1; |
|
} |
|
} |
|
|
|
.new-node { |
|
animation: nodeAppear 0.3s ease-out forwards; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<header> |
|
<h1>Technology Evolution Tree</h1> |
|
<div class="subtitle">Explore the progression of human innovation through history</div> |
|
</header> |
|
|
|
<div class="container"> |
|
<div class="controls"> |
|
<div class="controls-section"> |
|
<div class="controls-title"><i class="fas fa-clock"></i> Era Navigation</div> |
|
<div class="era-navigation"> |
|
<button class="era-button era-ancient">Ancient</button> |
|
<button class="era-button era-classical">Classical</button> |
|
<button class="era-button era-medieval">Medieval</button> |
|
<button class="era-button era-industrial">Industrial</button> |
|
<button class="era-button era-modern">Modern</button> |
|
<button class="era-button era-future">Future</button> |
|
</div> |
|
</div> |
|
|
|
<div class="controls-section"> |
|
<div class="controls-title"><i class="fas fa-cog"></i> Controls</div> |
|
<button class="add-tech-btn" id="addTechBtn"> |
|
<i class="fas fa-plus"></i> Add Technology |
|
</button> |
|
<button class="toggle-edit-mode" id="toggleEditMode"> |
|
<i class="fas fa-edit"></i> Toggle Edit Mode |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div class="tech-tree-container" id="techTreeContainer"> |
|
<div class="tech-tree" id="techTree"> |
|
|
|
<div class="era-line ancient-line"></div> |
|
<div class="era-line classical-line"></div> |
|
<div class="era-line medieval-line"></div> |
|
<div class="era-line industrial-line"></div> |
|
<div class="era-line modern-line"></div> |
|
<div class="era-line future-line"></div> |
|
|
|
<div class="era-labels"> |
|
<div>Ancient</div> |
|
<div>Classical</div> |
|
<div>Medieval</div> |
|
<div>Industrial</div> |
|
<div>Modern</div> |
|
<div>Future</div> |
|
</div> |
|
|
|
<div class="timeline-bar"> |
|
<div class="tech-path"></div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="node-popup" id="nodePopup"> |
|
<span class="close-popup">×</span> |
|
<h3 id="popupTitle">Technology Name</h3> |
|
<div class="era" id="popupEra">Ancient Era</div> |
|
<div class="description" id="popupDescription">Description of technology...</div> |
|
<div class="prerequisites" id="popupPrereqs">Prerequisites: None</div> |
|
</div> |
|
|
|
|
|
<div class="add-node-form" id="addNodeForm"> |
|
<h2>Add New Technology</h2> |
|
<form id="techForm"> |
|
<div class="form-group"> |
|
<label for="techName">Technology Name</label> |
|
<input type="text" id="techName" required placeholder="e.g., Quantum Computing"> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label for="techEra">Era</label> |
|
<select id="techEra" required> |
|
<option value="ancient">Ancient</option> |
|
<option value="classical">Classical</option> |
|
<option value="medieval">Medieval</option> |
|
<option value="industrial">Industrial</option> |
|
<option value="modern">Modern</option> |
|
<option value="future">Future</option> |
|
</select> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label for="techIcon">Icon</label> |
|
<div class="icon-selecter" id="iconSelector"> |
|
|
|
</div> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label for="techDescription">Description</label> |
|
<textarea id="techDescription" required placeholder="Describe the technology and its significance..."></textarea> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label>Prerequisites</label> |
|
<div class="checkbox-group" id="prerequisiteCheckboxes"> |
|
|
|
</div> |
|
</div> |
|
|
|
<div class="form-actions"> |
|
<button type="button" class="form-cancel" id="cancelForm">Cancel</button> |
|
<button type="submit" class="form-submit">Add Technology</button> |
|
</div> |
|
</form> |
|
</div> |
|
|
|
<footer> |
|
<p>Explore the technological advancements that shaped human civilization</p> |
|
</footer> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
const techTree = document.getElementById('techTree'); |
|
const techTreeContainer = document.getElementById('techTreeContainer'); |
|
const nodePopup = document.getElementById('nodePopup'); |
|
const closePopup = document.querySelector('.close-popup'); |
|
const techPath = document.querySelector('.tech-path'); |
|
const addTechBtn = document.getElementById('addTechBtn'); |
|
const addNodeForm = document.getElementById('addNodeForm'); |
|
const techForm = document.getElementById('techForm'); |
|
const cancelForm = document.getElementById('cancelForm'); |
|
const iconSelector = document.getElementById('iconSelector'); |
|
const prerequisiteCheckboxes = document.getElementById('prerequisiteCheckboxes'); |
|
const toggleEditMode = document.getElementById('toggleEditMode'); |
|
|
|
let selectedIcon = 'fas fa-question'; |
|
let techData = []; |
|
let nextId = 1; |
|
let draggingNode = null; |
|
let offsetX, offsetY; |
|
let isEditMode = false; |
|
|
|
|
|
const commonIcons = [ |
|
'fas fa-fire', 'fas fa-wheel', 'fas fa-seedling', 'fas fa-book', |
|
'fas fa-square-root-alt', 'fas fa-hammer', 'fas fa-print', |
|
'fas fa-compass', 'fas fa-train', 'fas fa-bolt', 'fas fa-satellite-dish', |
|
'fas fa-car', 'fas fa-plane', 'fas fa-computer', 'fas fa-network-wired', |
|
'fas fa-robot', 'fas fa-atom', 'fas fa-dna', 'fas fa-rocket', |
|
'fas fa-microchip', 'fas fa-brain', 'fas fa-vr-cardboard', |
|
'fas fa-biohazard', 'fas fa-solar-panel', 'fas fa-wind', 'fas fa-eye', |
|
'fas fa-bullseye', 'fas fa-tools', 'fas fa-flask', 'fas fa-microscope', |
|
'fas fa-disease', 'fas fa-virus', 'fas fa-dove', 'fas fa-globe', |
|
'fas fa-magnet', 'fas fa-lightbulb', 'fas fa-broadcast-tower', |
|
'fas fa-satellite', 'fas fa-shield-alt', 'fas fa-cogs', 'fas fa-brain', |
|
'fas fa-fan', 'fas fa-chart-line', 'fas fa-industry', 'fas fa-city', |
|
'fas fa-battery-full', 'fas fa-cloud', 'fas fa-database' |
|
]; |
|
|
|
|
|
initializeTechData(); |
|
|
|
|
|
drawTechTree(); |
|
|
|
|
|
function initializeTechData() { |
|
techData = [ |
|
|
|
{ |
|
id: nextId++, |
|
name: "Fire", |
|
era: "ancient", |
|
icon: "fas fa-fire", |
|
description: "Mastery of fire allowed for cooking, warmth, protection and became the foundation for later metallurgy and energy technologies.", |
|
x: 100, |
|
y: 150, |
|
prerequisites: [] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Wheel", |
|
era: "ancient", |
|
icon: "fas fa-wheel", |
|
description: "The invention of the wheel revolutionized transportation and mechanical applications, enabling carts, potter's wheels, and later complex machinery.", |
|
x: 300, |
|
y: 150, |
|
prerequisites: [] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Agriculture", |
|
era: "ancient", |
|
icon: "fas fa-seedling", |
|
description: "The domestication of plants led to settled communities, food surplus, and the development of complex societies and specialization.", |
|
x: 500, |
|
y: 150, |
|
prerequisites: [1] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Writing", |
|
era: "ancient", |
|
icon: "fas fa-book", |
|
description: "Early writing systems like cuneiform and hieroglyphics emerged for record keeping, enabling complex administration and knowledge transmission.", |
|
x: 700, |
|
y: 150, |
|
prerequisites: [3] |
|
}, |
|
|
|
|
|
{ |
|
id: nextId++, |
|
name: "Mathematics", |
|
era: "classical", |
|
icon: "fas fa-square-root-alt", |
|
description: "Systematic development of mathematics including geometry, algebra, and number theory that became foundational for all sciences.", |
|
x: 100, |
|
y: 450, |
|
prerequisites: [4] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Architecture", |
|
era: "classical", |
|
icon: "fas fa-archway", |
|
description: "Advanced techniques in construction enabled monumental buildings like temples, aqueducts and coliseums using arches, domes and concrete.", |
|
x: 300, |
|
y: 450, |
|
prerequisites: [5] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Metalworking", |
|
era: "classical", |
|
icon: "fas fa-hammer", |
|
description: "Advanced techniques in smelting and forging enabled better tools, weapons, armor and the beginning of mechanical engineering.", |
|
x: 500, |
|
y: 450, |
|
prerequisites: [1] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Currency", |
|
era: "classical", |
|
icon: "fas fa-coins", |
|
description: "Standardized coinage systems enabled complex trade networks and economic systems beyond simple barter.", |
|
x: 700, |
|
y: 450, |
|
prerequisites: [4, 3] |
|
}, |
|
|
|
|
|
{ |
|
id: nextId++, |
|
name: "Printing", |
|
era: "medieval", |
|
icon: "fas fa-print", |
|
description: "Movable type printing enabled mass production of books, spreading literacy and enabling the Renaissance and scientific revolution.", |
|
x: 100, |
|
y: 750, |
|
prerequisites: [4] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Compass", |
|
era: "medieval", |
|
icon: "fas fa-compass", |
|
description: "The magnetic compass enabled reliable navigation at sea, facilitating global exploration, trade and cultural exchange.", |
|
x: 300, |
|
y: 750, |
|
prerequisites: [7] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Gunpowder", |
|
era: "medieval", |
|
icon: "fas fa-bomb", |
|
description: "Revolutionized warfare and mining, leading to new military strategies and eventually industrial explosives.", |
|
x: 500, |
|
y: 750, |
|
prerequisites: [7] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Mechanical Clock", |
|
era: "medieval", |
|
icon: "fas fa-clock", |
|
description: "Accurate timekeeping enabled better navigation, scheduling of economic activities, and scientific measurements.", |
|
x: 700, |
|
y: 750, |
|
prerequisites: [7] |
|
}, |
|
|
|
|
|
{ |
|
id: nextId++, |
|
name: "Steam Engine", |
|
era: "industrial", |
|
icon: "fas fa-train", |
|
description: "Powered the Industrial Revolution, transforming transportation, manufacturing and energy production.", |
|
x: 100, |
|
y: 1050, |
|
prerequisites: [7] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Electricity", |
|
era: "industrial", |
|
icon: "fas fa-bolt", |
|
description: "Harnessing electricity transformed every aspect of life from lighting and communication to industrial production.", |
|
x: 300, |
|
y: 1050, |
|
prerequisites: [13] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Telegraph", |
|
era: "industrial", |
|
icon: "fas fa-broadcast-tower", |
|
description: "First rapid long-distance communication system that shrank the world and transformed business, news and diplomacy.", |
|
x: 500, |
|
y: 1050, |
|
prerequisites: [14] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Vaccination", |
|
era: "industrial", |
|
icon: "fas fa-syringe", |
|
description: "Scientific immunization methods dramatically reduced mortality from infectious diseases, increasing global population health.", |
|
x: 700, |
|
y: 1050, |
|
prerequisites: [] |
|
}, |
|
|
|
|
|
{ |
|
id: nextId++, |
|
name: "Computing", |
|
era: "modern", |
|
icon: "fas fa-computer", |
|
description: "Digital computing revolutionized information processing, leading to the Information Age and transforming society.", |
|
x: 100, |
|
y: 1350, |
|
prerequisites: [14] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Automobile", |
|
era: "modern", |
|
icon: "fas fa-car", |
|
description: "Mass production of affordable personal vehicles transformed transportation, urban design and modern lifestyles.", |
|
x: 300, |
|
y: 1350, |
|
prerequisites: [13, 14] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Aviation", |
|
era: "modern", |
|
icon: "fas fa-plane", |
|
description: "Powered flight revolutionized global transportation, warfare and connected the world more tightly than ever before.", |
|
x: 500, |
|
y: 1350, |
|
prerequisites: [13, 14] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Internet", |
|
era: "modern", |
|
icon: "fas fa-network-wired", |
|
description: "Global digital network revolutionized communication, commerce, education and access to information.", |
|
x: 700, |
|
y: 1350, |
|
prerequisites: [17] |
|
}, |
|
|
|
|
|
{ |
|
id: nextId++, |
|
name: "AI", |
|
era: "future", |
|
icon: "fas fa-robot", |
|
description: "Artificial Intelligence promises to automate complex tasks, enhance human capabilities and solve problems beyond human-scale processing.", |
|
x: 100, |
|
y: 1650, |
|
prerequisites: [17, 20] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Fusion Power", |
|
era: "future", |
|
icon: "fas fa-atom", |
|
description: "Nuclear fusion could provide nearly limitless clean energy, solving humanity's energy needs while reducing environmental impact.", |
|
x: 300, |
|
y: 1650, |
|
prerequisites: [14, 17] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Quantum Computing", |
|
era: "future", |
|
icon: "fas fa-brain", |
|
description: "Harnesses quantum mechanics to perform calculations impossible for classical computers, revolutionizing cryptography, simulation and optimization.", |
|
x: 500, |
|
y: 1650, |
|
prerequisites: [17] |
|
}, |
|
{ |
|
id: nextId++, |
|
name: "Space Colonization", |
|
era: "future", |
|
icon: "fas fa-rocket", |
|
description: "Establishing permanent human settlements beyond Earth would ensure species survival and open new frontiers for exploration and resources.", |
|
x: 700, |
|
y: 1650, |
|
prerequisites: [14, 22] |
|
} |
|
]; |
|
} |
|
|
|
|
|
function drawTechTree() { |
|
|
|
techTree.innerHTML = ''; |
|
|
|
|
|
techTree.innerHTML += ` |
|
<div class="era-line ancient-line"></div> |
|
<div class="era-line classical-line"></div> |
|
<div class="era-line medieval-line"></div> |
|
<div class="era-line industrial-line"></div> |
|
<div class="era-line modern-line"></div> |
|
<div class="era-line future-line"></div> |
|
|
|
<div class="era-labels"> |
|
<div style="top: 150px;">Ancient</div> |
|
<div style="top: 450px;">Classical</div> |
|
<div style="top: 750px;">Medieval</div> |
|
<div style="top: 1050px;">Industrial</div> |
|
<div style="top: 1350px;">Modern</div> |
|
<div style="top: 1650px;">Future</div> |
|
</div> |
|
`; |
|
|
|
|
|
techData.forEach(tech => { |
|
if (tech.prerequisites && tech.prerequisites.length) { |
|
tech.prerequisites.forEach(prereqId => { |
|
const prereqTech = techData.find(t => t.id === prereqId); |
|
if (prereqTech) { |
|
drawConnection( |
|
prereqTech.x + 50, prereqTech.y + 50, |
|
tech.x + 50, tech.y + 50 |
|
); |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
|
|
techData.forEach(tech => { |
|
createTechNode(tech); |
|
}); |
|
} |
|
|
|
|
|
function createTechNode(tech) { |
|
const node = document.createElement('div'); |
|
node.className = `tech-node ${tech.era}`; |
|
node.style.left = `${tech.x}px`; |
|
node.style.top = `${tech.y}px`; |
|
node.dataset.id = tech.id; |
|
|
|
node.innerHTML = ` |
|
<div class="tech-icon"> |
|
<i class="${tech.icon}"></i> |
|
</div> |
|
<div class="tech-name">${tech.name}</div> |
|
<div class="tech-era">${tech.era.charAt(0).toUpperCase() + tech.era.slice(1)}</div> |
|
<div class="drag-handle">↔</div> |
|
<div class="delete-node">×</div> |
|
`; |
|
|
|
node.addEventListener('click', (e) => { |
|
|
|
if (isEditMode || e.target.classList.contains('delete-node') || e.target.classList.contains('drag-handle')) { |
|
return; |
|
} |
|
|
|
showTechPopup(tech); |
|
animateTechPath(tech.era); |
|
}); |
|
|
|
|
|
node.addEventListener('mousedown', (e) => { |
|
if (!isEditMode) return; |
|
|
|
|
|
if (e.target.classList.contains('delete-node')) { |
|
return; |
|
} |
|
|
|
e.preventDefault(); |
|
draggingNode = node; |
|
|
|
|
|
const rect = node.getBoundingClientRect(); |
|
offsetX = e.clientX - rect.left; |
|
offsetY = e.clientY - rect.top; |
|
|
|
|
|
node.style.position = 'absolute'; |
|
|
|
|
|
node.style.zIndex = '1000'; |
|
|
|
document.addEventListener('mousemove', dragNode); |
|
document.addEventListener('mouseup', stopDrag); |
|
}); |
|
|
|
|
|
const deleteBtn = node.querySelector('.delete-node'); |
|
deleteBtn.addEventListener('click', (e) => { |
|
if (!isEditMode) return; |
|
|
|
e.stopPropagation(); |
|
if (confirm(`Delete "${tech.name}"?`)) { |
|
|
|
techData.forEach(t => { |
|
t.prerequisites = t.prerequisites.filter(id => id !== tech.id); |
|
}); |
|
|
|
|
|
techData = techData.filter(t => t.id !== tech.id); |
|
|
|
|
|
drawTechTree(); |
|
} |
|
}); |
|
|
|
|
|
updateNodeEditControls(node); |
|
|
|
techTree.appendChild(node); |
|
return node; |
|
} |
|
|
|
|
|
function updateNodeEditControls(node) { |
|
const dragHandle = node.querySelector('.drag-handle'); |
|
const deleteBtn = node.querySelector('.delete-node'); |
|
|
|
if (isEditMode) { |
|
node.classList.add('editable'); |
|
dragHandle.style.display = 'flex'; |
|
deleteBtn.style.display = 'flex'; |
|
} else { |
|
node.classList.remove('editable'); |
|
dragHandle.style.display = 'none'; |
|
deleteBtn.style.display = 'none'; |
|
} |
|
} |
|
|
|
|
|
function drawConnection(x1, y1, x2, y2) { |
|
const line = document.createElement('div'); |
|
line.className = 'connection'; |
|
|
|
|
|
const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); |
|
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI; |
|
|
|
|
|
line.style.width = `${length}px`; |
|
line.style.left = `${x1}px`; |
|
line.style.top = `${y1}px`; |
|
line.style.transform = `rotate(${angle}deg)`; |
|
|
|
techTree.appendChild(line); |
|
return line; |
|
} |
|
|
|
|
|
function dragNode(e) { |
|
if (!draggingNode) return; |
|
|
|
|
|
const containerRect = techTreeContainer.getBoundingClientRect(); |
|
let newX = e.clientX - containerRect.left - offsetX; |
|
let newY = e.clientY - containerRect.top - offsetY; |
|
|
|
|
|
newX = Math.max(0, Math.min(newX, techTreeContainer.scrollWidth - 100)); |
|
newY = Math.max(0, Math.min(newY, techTreeContainer.scrollHeight - 100)); |
|
|
|
|
|
draggingNode.style.left = `${newX}px`; |
|
draggingNode.style.top = `${newY}px`; |
|
|
|
|
|
const techId = parseInt(draggingNode.dataset.id); |
|
const tech = techData.find(t => t.id === techId); |
|
if (tech) { |
|
tech.x = newX; |
|
tech.y = newY; |
|
} |
|
|
|
|
|
redrawConnections(); |
|
} |
|
|
|
|
|
function stopDrag() { |
|
if (!draggingNode) return; |
|
|
|
draggingNode.style.zIndex = '10'; |
|
draggingNode = null; |
|
document.removeEventListener('mousemove', dragNode); |
|
document.removeEventListener('mouseup', stopDrag); |
|
} |
|
|
|
|
|
function redrawConnections() { |
|
|
|
const connections = document.querySelectorAll('.connection'); |
|
connections.forEach(c => c.remove()); |
|
|
|
|
|
techData.forEach(tech => { |
|
if (tech.prerequisites && tech.prerequisites.length) { |
|
tech.prerequisites.forEach(prereqId => { |
|
const prereqTech = techData.find(t => t.id === prereqId); |
|
if (prereqTech) { |
|
drawConnection( |
|
prereqTech.x + 50, prereqTech.y + 50, |
|
tech.x + 50, tech.y + 50 |
|
); |
|
} |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
|
|
function showTechPopup(tech) { |
|
document.getElementById('popupTitle').textContent = tech.name; |
|
|
|
|
|
const eraElement = document.getElementById('popupEra'); |
|
eraElement.textContent = `${tech.era.charAt(0).toUpperCase() + tech.era.slice(1)} Era`; |
|
eraElement.className = `era ${tech.era}`; |
|
|
|
document.getElementById('popupDescription').textContent = tech.description; |
|
|
|
if (tech.prerequisites && tech.prerequisites.length) { |
|
const prereqNames = tech.prerequisites.map(id => { |
|
const prereq = techData.find(t => t.id === id); |
|
return prereq ? prereq.name : 'Unknown Technology'; |
|
}).join(', '); |
|
document.getElementById('popupPrereqs').textContent = |
|
`Prerequisites: ${prereqNames}`; |
|
} else { |
|
document.getElementById('popupPrereqs').textContent = |
|
'Prerequisites: None (starting technology)'; |
|
} |
|
|
|
|
|
nodePopup.classList.add('active'); |
|
} |
|
|
|
|
|
closePopup.addEventListener('click', () => { |
|
nodePopup.classList.remove('active'); |
|
}); |
|
|
|
|
|
window.addEventListener('click', (e) => { |
|
if (e.target === nodePopup) { |
|
nodePopup.classList.remove('active'); |
|
} |
|
}); |
|
|
|
|
|
addTechBtn.addEventListener('click', () => { |
|
|
|
iconSelector.innerHTML = ''; |
|
commonIcons.forEach(icon => { |
|
const iconOption = document.createElement('div'); |
|
iconOption.className = `icon-option ${icon === selectedIcon ? 'selected' : ''}`; |
|
iconOption.dataset.icon = icon; |
|
iconOption.innerHTML = `<i class="${icon}"></i>`; |
|
|
|
iconOption.addEventListener('click', () => { |
|
selectedIcon = icon; |
|
document.querySelectorAll('.icon-option').forEach(opt => { |
|
opt.classList.remove('selected'); |
|
}); |
|
iconOption.classList.add('selected'); |
|
}); |
|
|
|
iconSelector.appendChild(iconOption); |
|
}); |
|
|
|
|
|
prerequisiteCheckboxes.innerHTML = ''; |
|
techData.forEach(tech => { |
|
const checkboxItem = document.createElement('div'); |
|
checkboxItem.className = 'checkbox-item'; |
|
|
|
const checkbox = document.createElement('input'); |
|
checkbox.type = 'checkbox'; |
|
checkbox.id = `prereq-${tech.id}`; |
|
checkbox.value = tech.id; |
|
|
|
const label = document.createElement('label'); |
|
label.htmlFor = `prereq-${tech.id}`; |
|
label.textContent = tech.name; |
|
|
|
checkboxItem.appendChild(checkbox); |
|
checkboxItem.appendChild(label); |
|
prerequisiteCheckboxes.appendChild(checkboxItem); |
|
}); |
|
|
|
|
|
addNodeForm.classList.add('active'); |
|
techForm.reset(); |
|
}); |
|
|
|
|
|
cancelForm.addEventListener('click', () => { |
|
addNodeForm.classList.remove('active'); |
|
techForm.reset(); |
|
}); |
|
|
|
|
|
techForm.addEventListener('submit', (e) => { |
|
e.preventDefault(); |
|
|
|
const name = document.getElementById('techName').value; |
|
const era = document.getElementById('techEra').value; |
|
const description = document.getElementById('techDescription').value; |
|
|
|
|
|
const prerequisites = []; |
|
document.querySelectorAll('#prerequisiteCheckboxes input:checked').forEach(checkbox => { |
|
prerequisites.push(parseInt(checkbox.value)); |
|
}); |
|
|
|
|
|
let x, y; |
|
const eraTechs = techData.filter(t => t.era === era); |
|
|
|
if (eraTechs.length > 0) { |
|
|
|
const lastTech = eraTechs[eraTechs.length - 1]; |
|
x = lastTech.x + 200; |
|
y = lastTech.y; |
|
} else { |
|
|
|
switch(era) { |
|
case 'ancient': y = 150; break; |
|
case 'classical': y = 450; break; |
|
case 'medieval': y = 750; break; |
|
case 'industrial': y = 1050; break; |
|
case 'modern': y = 1350; break; |
|
case 'future': y = 1650; break; |
|
default: y = 150; |
|
} |
|
x = 100; |
|
} |
|
|
|
|
|
const newTech = { |
|
id: nextId++, |
|
name, |
|
era, |
|
icon: selectedIcon, |
|
description, |
|
x, |
|
y, |
|
prerequisites |
|
}; |
|
|
|
|
|
techData.push(newTech); |
|
|
|
|
|
drawTechTree(); |
|
|
|
|
|
addNodeForm.classList.remove('active'); |
|
techForm.reset(); |
|
|
|
|
|
techTreeContainer.scrollTo({ |
|
top: y - 100, |
|
behavior: 'smooth' |
|
}); |
|
}); |
|
|
|
|
|
function animateTechPath(era) { |
|
let height = 0; |
|
switch(era) { |
|
case 'ancient': height = 7; break; |
|
case 'classical': height = 20; break; |
|
case 'medieval': height = 35; break; |
|
case 'industrial': height = 50; break; |
|
case 'modern': height = 70; break; |
|
case 'future': height = 100; break; |
|
} |
|
techPath.style.height = `${height}%`; |
|
} |
|
|
|
|
|
document.querySelectorAll('.era-button').forEach(button => { |
|
button.addEventListener('click', () => { |
|
const era = button.textContent.toLowerCase(); |
|
scrollToEra(era); |
|
animateTechPath(era); |
|
}); |
|
}); |
|
|
|
|
|
function scrollToEra(era) { |
|
let scrollTop; |
|
switch(era) { |
|
case 'ancient': scrollTop = 0; break; |
|
case 'classical': scrollTop = 300; break; |
|
case 'medieval': scrollTop = 600; break; |
|
case 'industrial': scrollTop = 900; break; |
|
case 'modern': scrollTop = 1200; break; |
|
case 'future': scrollTop = 1500; break; |
|
default: scrollTop = 0; |
|
} |
|
|
|
techTreeContainer.scrollTo({ |
|
top: scrollTop, |
|
behavior: 'smooth' |
|
}); |
|
} |
|
|
|
|
|
toggleEditMode.addEventListener('click', () => { |
|
isEditMode = !isEditMode; |
|
|
|
|
|
if (isEditMode) { |
|
toggleEditMode.classList.add('active'); |
|
toggleEditMode.innerHTML = '<i class="fas fa-check"></i> Edit Mode ON'; |
|
techTreeContainer.classList.add('editable-mode'); |
|
} else { |
|
toggleEditMode.classList.remove('active'); |
|
toggleEditMode.innerHTML = '<i class="fas fa-edit"></i> Toggle Edit Mode'; |
|
techTreeContainer.classList.remove('editable-mode'); |
|
} |
|
|
|
|
|
document.querySelectorAll('.tech-node').forEach(node => { |
|
updateNodeEditControls(node); |
|
}); |
|
}); |
|
|
|
|
|
animateTechPath('ancient'); |
|
}); |
|
</script> |
|
</body> |
|
</html> |