Skip to content

Commit 82db9f8

Browse files
authored
Add files via upload
1 parent 9f466f6 commit 82db9f8

1 file changed

Lines changed: 223 additions & 0 deletions

File tree

index.html

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Advanced Cre-Lox Simulator</title>
6+
<style>
7+
:root {
8+
--loxp: #0072B2; --l22: #E69F00; --l511: #56B4E9; --ln: #F0E442;
9+
--orf: #CC79A7; --pA: #000000;
10+
}
11+
body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; padding: 20px; }
12+
.container { max-width: 1100px; margin: auto; background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
13+
14+
.piece-wrapper {
15+
position: relative; padding: 10px 5px; cursor: grab; display: flex; align-items: center;
16+
}
17+
.piece-wrapper:active { cursor: grabbing; }
18+
19+
.dna-block {
20+
height: 38px; min-width: 85px; padding: 0 12px; color: white;
21+
font-weight: bold; display: flex; align-items: center; justify-content: center;
22+
font-size: 11px; user-select: none;
23+
clip-path: polygon(0% 0%, 85% 0%, 100% 50%, 85% 100%, 0% 100%);
24+
}
25+
.piece-wrapper.rev .dna-block { clip-path: polygon(15% 0%, 100% 0%, 100% 100%, 15% 100%, 0% 50%); }
26+
27+
.l22, .ln { color: #000 !important; }
28+
.p { background: var(--loxp); } .l22 { background: var(--l22); }
29+
.l511 { background: var(--l511); } .ln { background: var(--ln); }
30+
.orf { background: var(--orf); } .pa { background: var(--pA); }
31+
32+
.toolbox, .strand {
33+
display: flex; align-items: center; padding: 15px; border-radius: 8px; margin-bottom: 20px;
34+
}
35+
.toolbox { background: #eee; gap: 5px; flex-wrap: wrap; }
36+
.strand { min-height: 100px; border: 2px dashed #ccc; gap: 2px; background: #fff; overflow-x: auto; }
37+
.strand.drag-over { background: #e3f2fd; border-color: var(--loxp); }
38+
39+
.delete-btn {
40+
position: absolute; top: 0; right: 0; color: black; font-size: 16px;
41+
cursor: pointer; font-weight: bold; display: none; z-index: 20; text-shadow: 0 0 2px white;
42+
}
43+
.piece-wrapper:hover .delete-btn { display: block; }
44+
45+
.share-box { display: flex; gap: 10px; margin-bottom: 20px; background: #f9f9f9; padding: 15px; border-radius: 8px; }
46+
input#sequence-string { flex-grow: 1; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; }
47+
48+
.summary-table { width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #ddd; }
49+
.summary-table th, .summary-table td { padding: 10px; border: 1px solid #ddd; text-align: left; }
50+
.summary-table th { background: #f4f4f4; }
51+
52+
.outcome-box { border: 1px solid #ddd; padding: 10px; margin-bottom: 8px; display: flex; align-items: center; gap: 10px; border-radius: 6px; font-size: 11px; }
53+
.express-tag { padding: 2px 8px; border-radius: 4px; background: #333; color: #fff; font-weight: bold; min-width: 90px; text-align: center; }
54+
55+
button { cursor: pointer; padding: 10px 20px; border-radius: 6px; border: none; font-weight: bold; }
56+
.btn-primary { background: #2c3e50; color: #fff; }
57+
</style>
58+
</head>
59+
<body>
60+
61+
<div class="container">
62+
<h2>🧬 Dynamic Cre-Lox Simulator</h2>
63+
64+
<div class="share-box">
65+
<input type="text" id="sequence-string" placeholder="Construct code (e.g. P1,A1,P1)">
66+
<button onclick="loadFromCode()">Load Code</button>
67+
<button onclick="updateCode()">Refresh Code</button>
68+
</div>
69+
70+
<div class="toolbox" id="toolbox">
71+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-source="toolbox" data-id="P"><div class="dna-block p">LoxP</div></div>
72+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-source="toolbox" data-id="L2"><div class="dna-block l22">Lox2272</div></div>
73+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-source="toolbox" data-id="L5"><div class="dna-block l511">Lox511</div></div>
74+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-source="toolbox" data-id="LN"><div class="dna-block ln">LoxN</div></div>
75+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-source="toolbox" data-id="PA"><div class="dna-block pa">polyA</div></div>
76+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-id="A"><div class="dna-block orf">ORF-A</div></div>
77+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-id="B"><div class="dna-block orf">ORF-B</div></div>
78+
<div class="piece-wrapper" draggable="true" ondragstart="drag(event)" data-id="C"><div class="dna-block orf">ORF-C</div></div>
79+
</div>
80+
81+
<div id="strand" class="strand" ondragover="allowDrop(event)" ondragleave="dragLeave(event)" ondrop="drop(event)"></div>
82+
83+
<div style="display:flex; gap:10px;">
84+
<button class="btn-primary" onclick="runSimulation()">Simulate Outcomes</button>
85+
<button onclick="clearStrand()">Reset Strand</button>
86+
</div>
87+
88+
<div id="summary-section"></div>
89+
<div id="results"></div>
90+
</div>
91+
92+
<script>
93+
const strand = document.getElementById('strand');
94+
let draggedData = null;
95+
96+
const lookup = {
97+
"P": {name: "LoxP", type: "LOX", css: "p"}, "L2": {name: "Lox2272", type: "LOX", css: "l22"},
98+
"L5": {name: "Lox511", type: "LOX", css: "l511"}, "LN": {name: "LoxN", type: "LOX", css: "ln"},
99+
"PA": {name: "polyA", type: "PA", css: "pa"}, "A": {name: "ORF-A", type: "ORF", css: "orf"},
100+
"B": {name: "ORF-B", type: "ORF", css: "orf"}, "C": {name: "ORF-C", type: "ORF", css: "orf"}
101+
};
102+
103+
function allowDrop(ev) { ev.preventDefault(); strand.classList.add('drag-over'); }
104+
function dragLeave(ev) { strand.classList.remove('drag-over'); }
105+
106+
function drag(ev) {
107+
const isFromStrand = ev.currentTarget.parentElement.id === 'strand';
108+
draggedData = {
109+
id: ev.currentTarget.dataset.id,
110+
dir: ev.currentTarget.dataset.dir || 1,
111+
index: isFromStrand ? Array.from(strand.children).indexOf(ev.currentTarget) : -1
112+
};
113+
}
114+
115+
function drop(ev) {
116+
ev.preventDefault();
117+
strand.classList.remove('drag-over');
118+
if (draggedData.index !== -1) {
119+
// Reordering internal
120+
const pieces = [...strand.querySelectorAll('.piece-wrapper')];
121+
const target = pieces.find(s => ev.clientX < s.getBoundingClientRect().left + s.getBoundingClientRect().width/2);
122+
strand.insertBefore(pieces[draggedData.index], target || null);
123+
} else {
124+
// New from toolbox
125+
addPiece(draggedData.id, 1, ev.clientX);
126+
}
127+
updateCode();
128+
}
129+
130+
function addPiece(id, dir, mouseX) {
131+
const info = lookup[id];
132+
const wrapper = document.createElement('div');
133+
wrapper.className = `piece-wrapper placed ${dir == -1 ? 'rev' : ''}`;
134+
wrapper.dataset.id = id; wrapper.dataset.dir = dir;
135+
wrapper.draggable = true;
136+
wrapper.ondragstart = drag;
137+
wrapper.innerHTML = `<div class="dna-block ${info.css}">${info.name}</div><div class="delete-btn" onclick="this.parentElement.remove(); updateCode();">×</div>`;
138+
wrapper.onclick = (e) => { if(e.target.className === 'delete-btn') return; wrapper.dataset.dir = wrapper.dataset.dir == 1 ? -1 : 1; wrapper.classList.toggle('rev'); updateCode(); };
139+
const siblings = [...strand.querySelectorAll('.piece-wrapper')];
140+
const next = siblings.find(s => mouseX < s.getBoundingClientRect().left + s.getBoundingClientRect().width/2);
141+
strand.insertBefore(wrapper, next || null);
142+
updateCode();
143+
}
144+
145+
function updateCode() { document.getElementById('sequence-string').value = [...strand.querySelectorAll('.piece-wrapper')].map(el => `${el.dataset.id}${el.dataset.dir}`).join(','); }
146+
147+
function loadFromCode() {
148+
strand.innerHTML = '';
149+
const code = document.getElementById('sequence-string').value;
150+
if(!code) return;
151+
code.split(',').forEach(part => {
152+
const match = part.match(/([A-Z0-9]+)(-?\d+)/);
153+
if(match) addPiece(match[1], parseInt(match[2]), 99999);
154+
});
155+
}
156+
157+
function clearStrand() { strand.innerHTML = ''; document.getElementById('sequence-string').value = ''; document.getElementById('results').innerHTML = ''; document.getElementById('summary-section').innerHTML = ''; }
158+
159+
function runSimulation() {
160+
const initial = [...strand.querySelectorAll('.piece-wrapper')].map(p => ({ ...lookup[p.dataset.id], id: p.dataset.id, dir: parseInt(p.dataset.dir) }));
161+
if(!initial.length) return;
162+
163+
let visited = new Map();
164+
let queue = [{seq: initial, steps: 0}];
165+
visited.set(JSON.stringify(initial), 0);
166+
let outcomes = [];
167+
168+
while(queue.length > 0 && outcomes.length < 150) {
169+
let {seq, steps} = queue.shift();
170+
let expr = "None";
171+
for(let s of seq) {
172+
if(s.type === 'PA' && s.dir === 1) { expr = "Blocked"; break; }
173+
if(s.type === 'ORF' && s.dir === 1) { expr = s.name; break; }
174+
}
175+
outcomes.push({seq, steps, expr});
176+
177+
let loxIdx = seq.map((s, i) => s.type === 'LOX' ? i : -1).filter(i => i !== -1);
178+
for(let i=0; i < loxIdx.length; i++) {
179+
for(let j=i+1; j < loxIdx.length; j++) {
180+
if(seq[loxIdx[i]].name !== seq[loxIdx[j]].name) continue;
181+
let nextS = [];
182+
if(seq[loxIdx[i]].dir !== seq[loxIdx[j]].dir) {
183+
let mid = seq.slice(loxIdx[i]+1, loxIdx[j]).reverse().map(s => ({...s, dir: s.dir * -1}));
184+
nextS = [...seq.slice(0, loxIdx[i]+1), ...mid, ...seq.slice(loxIdx[j])];
185+
} else {
186+
// EXCISION: Fuse the two Lox sites into one
187+
nextS = [...seq.slice(0, loxIdx[i]), seq[loxIdx[i]], ...seq.slice(loxIdx[j] + 1)];
188+
}
189+
if(!visited.has(JSON.stringify(nextS))) { visited.set(JSON.stringify(nextS), steps+1); queue.push({seq: nextS, steps: steps+1}); }
190+
}
191+
}
192+
}
193+
render(outcomes);
194+
}
195+
196+
function render(outcomes) {
197+
const stats = {};
198+
outcomes.forEach(o => {
199+
if (!stats[o.expr]) stats[o.expr] = new Set();
200+
stats[o.expr].add(o.steps);
201+
});
202+
203+
let summaryHtml = `<h3>Phenotype Summary</h3><table class="summary-table"><thead><tr><th>Resulting Expression</th><th>Steps to Achieve (All Paths)</th></tr></thead><tbody>`;
204+
for (let key in stats) {
205+
let stepsArr = Array.from(stats[key]).sort((a,b) => a-b);
206+
summaryHtml += `<tr><td><strong>${key}</strong></td><td>${stepsArr.join(', ')}</td></tr>`;
207+
}
208+
summaryHtml += `</tbody></table>`;
209+
document.getElementById('summary-section').innerHTML = summaryHtml;
210+
211+
const res = document.getElementById('results');
212+
res.innerHTML = `<h3>Possible DNA States (${outcomes.length})</h3>`;
213+
outcomes.forEach(o => {
214+
const div = document.createElement('div');
215+
div.className = 'outcome-box';
216+
const dna = o.seq.map(s => `<div class="dna-block ${s.css}" style="transform: scale(0.65); min-width:70px; clip-path: polygon(${s.dir==1 ? '0% 0%, 85% 0%, 100% 50%, 85% 100%, 0% 100%' : '15% 0%, 100% 0%, 100% 100%, 15% 100%, 0% 50%'})">${s.id}</div>`).join('');
217+
div.innerHTML = `<div style="min-width:70px">Steps: ${o.steps}</div> <div style="display:flex; flex-grow:1; overflow-x:auto;">${dna}</div> <div class="express-tag">${o.expr}</div>`;
218+
res.appendChild(div);
219+
});
220+
}
221+
</script>
222+
</body>
223+
</html>

0 commit comments

Comments
 (0)