School Safety Drill Procedure Sheet
1. Drill Identification
2. Step-by-Step Procedure (For Teachers/Staff)
Add actions in sequential order.
Add actions in sequential order.
'; return; } procedureSteps.forEach((step, index) => { const li = document.createElement('li'); li.className = 'ssdp-list-item'; li.innerHTML = ` ${index + 1}. ${step} `; li.querySelector('button').addEventListener('click', () => removeStep(index)); stepList.appendChild(li); }); } function addStep() { const stepText = stepInput.value.trim(); if (stepText) { procedureSteps.push(stepText); stepInput.value = ''; renderStepList(); } } function removeStep(index) { procedureSteps.splice(index, 1); renderStepList(); } // --- Preset Loading --- function loadPresetSteps(type) { switch(type) { case 'Fire/Evacuation': procedureSteps = [ "Immediately pull the nearest fire alarm and/or use the specified public address signal.", "Direct students to silence and immediately stand up.", "Check all areas (restrooms, storage) within the classroom.", "Lead students to the designated primary exit route quietly and in single file.", "Close classroom door and windows, but DO NOT lock.", "Proceed to the designated exterior assembly area (e.g., football field).", "Take attendance at the assembly area and report missing students to the Command Post." ]; triggerInput.value = "Continuous Fire Alarm Bell"; notesTextarea.value = "Goal: Rapid, safe evacuation from the building to a pre-determined assembly point."; break; case 'Lockdown': procedureSteps = [ "Initiate the 'LOCKDOWN' procedure via PA/Intercom (Code Red or similar).", "Immediately clear all students from hallways and sightlines.", "Lock the classroom door and cover the door window completely.", "Students move away from the door and windows, getting down on the floor or behind cover.", "Maintain absolute silence and ignore all external announcements/alarms until the official 'ALL CLEAR' is announced by administration/law enforcement." ]; triggerInput.value = "PA Announcement: 'Lockdown! Lock the doors, turn off the lights.'"; notesTextarea.value = "Goal: Secure students inside classrooms away from danger, typically an internal or external human threat."; break; case 'Shelter in Place (Severe Weather)': procedureSteps = [ "Announce 'SHELTER IN PLACE' over the PA system (Severe Weather Code).", "Direct students to move quickly to the designated interior shelter area (hallways, interior rooms, away from windows).", "Have students crouch down, covering their heads/necks.", "Take attendance at the shelter location. Keep students calm until the 'ALL CLEAR' is announced." ]; triggerInput.value = "PA Announcement: 'Attention, Attention, Severe Weather Warning. Shelter in Place.'"; notesTextarea.value = "Goal: Seek immediate protection inside the building from external environmental hazards (e.g., tornado, chemical spill)."; break; default: procedureSteps = []; triggerInput.value = ""; notesTextarea.value = ""; } renderStepList(); } // --- PDF Generation (Ensured functionality) --- function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } if (typeof window.jspdf.jsPDF.autoTable === 'undefined') { alert('Error: jsPDF-AutoTable library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); let currentY = margin; // Gather data const schoolName = schoolNameInput.value || 'N/A'; const drillType = drillTypeSelect.options[drillTypeSelect.selectedIndex].text; const trigger = triggerInput.value || 'N/A'; const notes = notesTextarea.value || 'N/A'; // --- Header --- doc.setFontSize(20); doc.setFont('helvetica', 'bold'); doc.setTextColor(249, 115, 22); // orange-600 doc.text(drillType + " Procedure", pageWidth / 2, currentY, { align: 'center' }); currentY += 15; doc.setFontSize(12); doc.text(schoolName, pageWidth / 2, currentY, { align: 'center' }); currentY += 30; // --- Section 1: Identification (Table) --- doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text("1. Activation Details", margin, currentY); currentY += 5; const detailsData = [ ["Drill Type:", drillType], ["Activation Signal:", trigger], ]; doc.autoTable({ startY: currentY, body: detailsData, theme: 'striped', styles: { cellPadding: 3, fontSize: 10 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [255, 247, 237], cellWidth: 80 } } }); currentY = doc.autoTable.previous.finalY + 10; // Scope Notes doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.text("Purpose/Scope Notes:", margin, currentY); currentY += 12; doc.setFont('helvetica', 'normal'); const notesLines = doc.splitTextToSize(notes, pageWidth - margin * 2); doc.text(notesLines, margin, currentY); currentY += notesLines.length * 12 + 15; // --- Section 2: Step-by-Step Procedure --- doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text("2. Step-by-Step Procedure", margin, currentY); currentY += 15; if (procedureSteps.length > 0) { const stepData = procedureSteps.map((step, index) => [index + 1, step]); doc.autoTable({ startY: currentY, head: [['#', 'Required Action for Staff/Teachers']], body: stepData, theme: 'grid', headStyles: { fillColor: [234, 88, 12], textColor: [255, 255, 255] }, // orange-600 styles: { fontSize: 10, minCellHeight: 15, overflow: 'linebreak' }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 20, halign: 'center' } } }); currentY = doc.autoTable.previous.finalY + 20; } else { doc.setFontSize(10); doc.setTextColor(148, 163, 184); doc.text("No procedure steps defined.", margin, currentY); currentY += 20; } // --- Footer --- doc.setFontSize(8); doc.text("NOTE: This procedure should be customized to local law enforcement and district protocols. Review with site security prior to use.", margin, doc.internal.pageSize.getHeight() - 15); doc.save('school-drill-procedure.pdf'); } // --- Event Listeners and Initial Load --- addStepBtn.addEventListener('click', addStep); stepInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); addStep(); } }); drillTypeSelect.addEventListener('change', (e) => loadPresetSteps(e.target.value)); pdfBtn.addEventListener('click', downloadPDF); // Initial Load loadPresetSteps(drillTypeSelect.value); // Load Fire/Evacuation preset });