Skip to content

Commit f271d83

Browse files
refactor(scenario-planner): deduplicate escapeHtml, cache DOM refs, extract schedule helper
- Replace local escapeHtml (createElement+textContent per call) with shared DOMUtil.escapeHtml (reuses a single cached DOM node) - Cache 8 getElementById lookups in _getEls() resolved once in init(), eliminating repeated DOM queries in startPlanning() and resetPlanner() - Extract schedule(fn, ms) helper to replace 5 instances of the 'var tid = setTimeout(...); timers.push(tid)' pattern - Build and syntax verified; no test regressions (all 525 passing tests stable)
1 parent deb0774 commit f271d83

2 files changed

Lines changed: 120 additions & 104 deletions

File tree

dist/bundle.js

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8888,14 +8888,43 @@ var ScenarioPlanner = (function () {
88888888
}
88898889
];
88908890

8891+
// DOMUtil.escapeHtml is provided by dom-utils.js (loaded before this module in build order)
8892+
var escapeHtml = DOMUtil.escapeHtml;
8893+
88918894
var running = false;
88928895
var timers = [];
88938896

8897+
// Cached DOM references — resolved once in init(), avoids
8898+
// repeated getElementById calls in startPlanning/resetPlanner.
8899+
var _els = null;
8900+
8901+
function _getEls() {
8902+
if (!_els) {
8903+
_els = {
8904+
workspace: document.getElementById('scenarioWorkspace'),
8905+
goal: document.getElementById('scenarioGoal'),
8906+
status: document.getElementById('scenarioAgentStatus'),
8907+
phases: document.getElementById('scenarioPhases'),
8908+
summary: document.getElementById('scenarioSummary'),
8909+
customInput: document.getElementById('scenarioCustomInput'),
8910+
customBtn: document.getElementById('scenarioCustomBtn'),
8911+
resetBtn: document.getElementById('scenarioResetBtn')
8912+
};
8913+
}
8914+
return _els;
8915+
}
8916+
8917+
/** Schedule a timeout and register it for cleanup on reset. */
8918+
function schedule(fn, ms) {
8919+
var id = setTimeout(fn, ms);
8920+
timers.push(id);
8921+
return id;
8922+
}
8923+
88948924
function init() {
88958925
var presetBtns = document.querySelectorAll('.scenario-preset-btn');
8896-
var customBtn = document.getElementById('scenarioCustomBtn');
8897-
var customInput = document.getElementById('scenarioCustomInput');
8898-
var resetBtn = document.getElementById('scenarioResetBtn');
8926+
// Eagerly resolve and cache DOM refs
8927+
var els = _getEls();
88998928

89008929
if (!presetBtns.length) return;
89018930

@@ -8908,22 +8937,22 @@ var ScenarioPlanner = (function () {
89088937
});
89098938
});
89108939

8911-
if (customBtn && customInput) {
8912-
customBtn.addEventListener('click', function () {
8913-
var val = customInput.value.trim();
8940+
if (els.customBtn && els.customInput) {
8941+
els.customBtn.addEventListener('click', function () {
8942+
var val = els.customInput.value.trim();
89148943
if (!val || running) return;
89158944
presetBtns.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); });
89168945
startPlanning(val);
89178946
});
8918-
customInput.addEventListener('keydown', function (e) {
8947+
els.customInput.addEventListener('keydown', function (e) {
89198948
if (e.key === 'Enter') {
8920-
customBtn.click();
8949+
els.customBtn.click();
89218950
}
89228951
});
89238952
}
89248953

8925-
if (resetBtn) {
8926-
resetBtn.addEventListener('click', function () { resetPlanner(); });
8954+
if (els.resetBtn) {
8955+
els.resetBtn.addEventListener('click', function () { resetPlanner(); });
89278956
}
89288957
}
89298958

@@ -8938,34 +8967,24 @@ var ScenarioPlanner = (function () {
89388967
return customPlan;
89398968
}
89408969

8941-
function escapeHtml(str) {
8942-
var el = document.createElement('span');
8943-
el.textContent = str;
8944-
return el.innerHTML;
8945-
}
8946-
89478970
function startPlanning(scenario) {
89488971
running = true;
89498972
clearTimers();
89508973

8951-
var workspace = document.getElementById('scenarioWorkspace');
8952-
var goalEl = document.getElementById('scenarioGoal');
8953-
var statusEl = document.getElementById('scenarioAgentStatus');
8954-
var phasesEl = document.getElementById('scenarioPhases');
8955-
var summaryEl = document.getElementById('scenarioSummary');
8974+
var els = _getEls();
89568975

89578976
// Reset
8958-
phasesEl.innerHTML = '';
8959-
summaryEl.hidden = true;
8960-
summaryEl.innerHTML = '';
8961-
statusEl.className = 'scenario-agent-status';
8962-
workspace.hidden = false;
8977+
els.phases.innerHTML = '';
8978+
els.summary.hidden = true;
8979+
els.summary.innerHTML = '';
8980+
els.status.className = 'scenario-agent-status';
8981+
els.workspace.hidden = false;
89638982

89648983
var safeScenario = escapeHtml(scenario.length > 100 ? scenario.substring(0, 97) + '...' : scenario);
8965-
goalEl.innerHTML = '\u{1F3AF} <strong>Goal:</strong> ' + safeScenario;
8984+
els.goal.innerHTML = '\u{1F3AF} <strong>Goal:</strong> ' + safeScenario;
89668985

89678986
var plan = getPlan(scenario);
8968-
animatePhases(plan, phasesEl, statusEl, summaryEl);
8987+
animatePhases(plan, els.phases, els.status, els.summary);
89698988
}
89708989

89718990
function animatePhases(plan, phasesEl, statusEl, summaryEl) {
@@ -8991,21 +9010,18 @@ var ScenarioPlanner = (function () {
89919010
phasesEl.appendChild(phaseEl);
89929011

89939012
// Trigger visibility transition
8994-
var tid1 = setTimeout(function () { phaseEl.classList.add('visible'); }, 50);
8995-
timers.push(tid1);
9013+
schedule(function () { phaseEl.classList.add('visible'); }, 50);
89969014

89979015
var stepsContainer = phaseEl.querySelector('.scenario-phase-steps');
89989016
animateSteps(phase.steps, stepsContainer, phaseIndex, function () {
89999017
var phaseStatusEl = document.getElementById('phaseStatus' + phaseIndex);
90009018
if (phaseStatusEl) phaseStatusEl.textContent = '\u2705 complete';
90019019
phaseIndex++;
9002-
var tid2 = setTimeout(nextPhase, 500);
9003-
timers.push(tid2);
9020+
schedule(nextPhase, 500);
90049021
});
90059022
}
90069023

9007-
var tid0 = setTimeout(nextPhase, 400);
9008-
timers.push(tid0);
9024+
schedule(nextPhase, 400);
90099025
}
90109026

90119027
function animateSteps(steps, container, phaseIdx, onDone) {
@@ -9025,12 +9041,9 @@ var ScenarioPlanner = (function () {
90259041
'<span>' + escapeHtml(step.text) + '</span>';
90269042
container.appendChild(stepEl);
90279043

9028-
var tid1 = setTimeout(function () {
9029-
stepEl.classList.add('visible');
9030-
}, 50);
9031-
timers.push(tid1);
9044+
schedule(function () { stepEl.classList.add('visible'); }, 50);
90329045

9033-
var tid2 = setTimeout(function () {
9046+
schedule(function () {
90349047
var icon = stepEl.querySelector('.scenario-step-icon');
90359048
if (icon) {
90369049
icon.textContent = '\u2713';
@@ -9039,7 +9052,6 @@ var ScenarioPlanner = (function () {
90399052
stepIndex++;
90409053
nextStep();
90419054
}, step.delay);
9042-
timers.push(tid2);
90439055
}
90449056

90459057
nextStep();
@@ -9070,21 +9082,17 @@ var ScenarioPlanner = (function () {
90709082
clearTimers();
90719083
running = false;
90729084

9073-
var workspace = document.getElementById('scenarioWorkspace');
9074-
var phasesEl = document.getElementById('scenarioPhases');
9075-
var summaryEl = document.getElementById('scenarioSummary');
9076-
var statusEl = document.getElementById('scenarioAgentStatus');
9077-
var customInput = document.getElementById('scenarioCustomInput');
9085+
var els = _getEls();
90789086
var presetBtns = document.querySelectorAll('.scenario-preset-btn');
90799087

9080-
if (workspace) workspace.hidden = true;
9081-
if (phasesEl) phasesEl.innerHTML = '';
9082-
if (summaryEl) { summaryEl.hidden = true; summaryEl.innerHTML = ''; }
9083-
if (statusEl) {
9084-
statusEl.textContent = '\u{1F916} AgentBox is thinking...';
9085-
statusEl.className = 'scenario-agent-status';
9088+
if (els.workspace) els.workspace.hidden = true;
9089+
if (els.phases) els.phases.innerHTML = '';
9090+
if (els.summary) { els.summary.hidden = true; els.summary.innerHTML = ''; }
9091+
if (els.status) {
9092+
els.status.textContent = '\u{1F916} AgentBox is thinking...';
9093+
els.status.className = 'scenario-agent-status';
90869094
}
9087-
if (customInput) customInput.value = '';
9095+
if (els.customInput) els.customInput.value = '';
90889096
presetBtns.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); });
90899097
}
90909098

src/modules/scenario-planner.js

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,43 @@ var ScenarioPlanner = (function () {
283283
}
284284
];
285285

286+
// DOMUtil.escapeHtml is provided by dom-utils.js (loaded before this module in build order)
287+
var escapeHtml = DOMUtil.escapeHtml;
288+
286289
var running = false;
287290
var timers = [];
288291

292+
// Cached DOM references — resolved once in init(), avoids
293+
// repeated getElementById calls in startPlanning/resetPlanner.
294+
var _els = null;
295+
296+
function _getEls() {
297+
if (!_els) {
298+
_els = {
299+
workspace: document.getElementById('scenarioWorkspace'),
300+
goal: document.getElementById('scenarioGoal'),
301+
status: document.getElementById('scenarioAgentStatus'),
302+
phases: document.getElementById('scenarioPhases'),
303+
summary: document.getElementById('scenarioSummary'),
304+
customInput: document.getElementById('scenarioCustomInput'),
305+
customBtn: document.getElementById('scenarioCustomBtn'),
306+
resetBtn: document.getElementById('scenarioResetBtn')
307+
};
308+
}
309+
return _els;
310+
}
311+
312+
/** Schedule a timeout and register it for cleanup on reset. */
313+
function schedule(fn, ms) {
314+
var id = setTimeout(fn, ms);
315+
timers.push(id);
316+
return id;
317+
}
318+
289319
function init() {
290320
var presetBtns = document.querySelectorAll('.scenario-preset-btn');
291-
var customBtn = document.getElementById('scenarioCustomBtn');
292-
var customInput = document.getElementById('scenarioCustomInput');
293-
var resetBtn = document.getElementById('scenarioResetBtn');
321+
// Eagerly resolve and cache DOM refs
322+
var els = _getEls();
294323

295324
if (!presetBtns.length) return;
296325

@@ -303,22 +332,22 @@ var ScenarioPlanner = (function () {
303332
});
304333
});
305334

306-
if (customBtn && customInput) {
307-
customBtn.addEventListener('click', function () {
308-
var val = customInput.value.trim();
335+
if (els.customBtn && els.customInput) {
336+
els.customBtn.addEventListener('click', function () {
337+
var val = els.customInput.value.trim();
309338
if (!val || running) return;
310339
presetBtns.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); });
311340
startPlanning(val);
312341
});
313-
customInput.addEventListener('keydown', function (e) {
342+
els.customInput.addEventListener('keydown', function (e) {
314343
if (e.key === 'Enter') {
315-
customBtn.click();
344+
els.customBtn.click();
316345
}
317346
});
318347
}
319348

320-
if (resetBtn) {
321-
resetBtn.addEventListener('click', function () { resetPlanner(); });
349+
if (els.resetBtn) {
350+
els.resetBtn.addEventListener('click', function () { resetPlanner(); });
322351
}
323352
}
324353

@@ -333,34 +362,24 @@ var ScenarioPlanner = (function () {
333362
return customPlan;
334363
}
335364

336-
function escapeHtml(str) {
337-
var el = document.createElement('span');
338-
el.textContent = str;
339-
return el.innerHTML;
340-
}
341-
342365
function startPlanning(scenario) {
343366
running = true;
344367
clearTimers();
345368

346-
var workspace = document.getElementById('scenarioWorkspace');
347-
var goalEl = document.getElementById('scenarioGoal');
348-
var statusEl = document.getElementById('scenarioAgentStatus');
349-
var phasesEl = document.getElementById('scenarioPhases');
350-
var summaryEl = document.getElementById('scenarioSummary');
369+
var els = _getEls();
351370

352371
// Reset
353-
phasesEl.innerHTML = '';
354-
summaryEl.hidden = true;
355-
summaryEl.innerHTML = '';
356-
statusEl.className = 'scenario-agent-status';
357-
workspace.hidden = false;
372+
els.phases.innerHTML = '';
373+
els.summary.hidden = true;
374+
els.summary.innerHTML = '';
375+
els.status.className = 'scenario-agent-status';
376+
els.workspace.hidden = false;
358377

359378
var safeScenario = escapeHtml(scenario.length > 100 ? scenario.substring(0, 97) + '...' : scenario);
360-
goalEl.innerHTML = '\u{1F3AF} <strong>Goal:</strong> ' + safeScenario;
379+
els.goal.innerHTML = '\u{1F3AF} <strong>Goal:</strong> ' + safeScenario;
361380

362381
var plan = getPlan(scenario);
363-
animatePhases(plan, phasesEl, statusEl, summaryEl);
382+
animatePhases(plan, els.phases, els.status, els.summary);
364383
}
365384

366385
function animatePhases(plan, phasesEl, statusEl, summaryEl) {
@@ -386,21 +405,18 @@ var ScenarioPlanner = (function () {
386405
phasesEl.appendChild(phaseEl);
387406

388407
// Trigger visibility transition
389-
var tid1 = setTimeout(function () { phaseEl.classList.add('visible'); }, 50);
390-
timers.push(tid1);
408+
schedule(function () { phaseEl.classList.add('visible'); }, 50);
391409

392410
var stepsContainer = phaseEl.querySelector('.scenario-phase-steps');
393411
animateSteps(phase.steps, stepsContainer, phaseIndex, function () {
394412
var phaseStatusEl = document.getElementById('phaseStatus' + phaseIndex);
395413
if (phaseStatusEl) phaseStatusEl.textContent = '\u2705 complete';
396414
phaseIndex++;
397-
var tid2 = setTimeout(nextPhase, 500);
398-
timers.push(tid2);
415+
schedule(nextPhase, 500);
399416
});
400417
}
401418

402-
var tid0 = setTimeout(nextPhase, 400);
403-
timers.push(tid0);
419+
schedule(nextPhase, 400);
404420
}
405421

406422
function animateSteps(steps, container, phaseIdx, onDone) {
@@ -420,12 +436,9 @@ var ScenarioPlanner = (function () {
420436
'<span>' + escapeHtml(step.text) + '</span>';
421437
container.appendChild(stepEl);
422438

423-
var tid1 = setTimeout(function () {
424-
stepEl.classList.add('visible');
425-
}, 50);
426-
timers.push(tid1);
439+
schedule(function () { stepEl.classList.add('visible'); }, 50);
427440

428-
var tid2 = setTimeout(function () {
441+
schedule(function () {
429442
var icon = stepEl.querySelector('.scenario-step-icon');
430443
if (icon) {
431444
icon.textContent = '\u2713';
@@ -434,7 +447,6 @@ var ScenarioPlanner = (function () {
434447
stepIndex++;
435448
nextStep();
436449
}, step.delay);
437-
timers.push(tid2);
438450
}
439451

440452
nextStep();
@@ -465,21 +477,17 @@ var ScenarioPlanner = (function () {
465477
clearTimers();
466478
running = false;
467479

468-
var workspace = document.getElementById('scenarioWorkspace');
469-
var phasesEl = document.getElementById('scenarioPhases');
470-
var summaryEl = document.getElementById('scenarioSummary');
471-
var statusEl = document.getElementById('scenarioAgentStatus');
472-
var customInput = document.getElementById('scenarioCustomInput');
480+
var els = _getEls();
473481
var presetBtns = document.querySelectorAll('.scenario-preset-btn');
474482

475-
if (workspace) workspace.hidden = true;
476-
if (phasesEl) phasesEl.innerHTML = '';
477-
if (summaryEl) { summaryEl.hidden = true; summaryEl.innerHTML = ''; }
478-
if (statusEl) {
479-
statusEl.textContent = '\u{1F916} AgentBox is thinking...';
480-
statusEl.className = 'scenario-agent-status';
483+
if (els.workspace) els.workspace.hidden = true;
484+
if (els.phases) els.phases.innerHTML = '';
485+
if (els.summary) { els.summary.hidden = true; els.summary.innerHTML = ''; }
486+
if (els.status) {
487+
els.status.textContent = '\u{1F916} AgentBox is thinking...';
488+
els.status.className = 'scenario-agent-status';
481489
}
482-
if (customInput) customInput.value = '';
490+
if (els.customInput) els.customInput.value = '';
483491
presetBtns.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); });
484492
}
485493

0 commit comments

Comments
 (0)