<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Tactile Cut-Up Sandbox</title>
<style>
/* Base Styles (Tailwind-like colors) */
body {
font-family: 'Inter', sans-serif;
background-color: #1f2937;
/* Dark background */
color: #f3f4f6;
margin: 0;
padding: 0;
overflow-x: hidden;
/* Prevent horizontal scroll */
overflow-y: auto;
/* Allow vertical scroll */
overscroll-behavior: none;
}
/* --- STATIC TOP UI BAR (Controls and Metrics Area) --- */
.tab-btn {
background: #22223b;
color: #fcd34d;
border: none;
font-weight: bold;
padding: 7px 22px;
border-radius: 5px 5px 0 0;
cursor: pointer;
border-bottom: 2px solid transparent;
font-size: 1.03rem;
}
.tab-btn.active {
background: #1e293b;
color: #fff;
border-bottom: 2px solid #fcd34d;
}
#custom-pools-panel {
background: #22223b;
border: 2px solid #fcd34d;
border-radius: 0 0 8px 8px;
margin-bottom: 10px;
padding: 20px 16px 12px 16px;
color: #fff;
display: none;
position: absolute;
top: 76px;
/* Adjust this to match the bottom of your header; increase if needed */
left: 0;
width: 100%;
z-index: 3000;
/* Make sure this is higher than #workbench-area and header */
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.54);
}
#metrics-panel {
background: #22223b;
border: 2px solid #fcd34d;
border-radius: 0 0 8px 8px;
margin-bottom: 10px;
padding: 20px 16px 12px 16px;
color: #fff;
position: absolute;
top: 76px;
/* Adjust to fit your header! */
left: 0;
width: 100%;
z-index: 3000;
box-shadow: 0 8px 24px rgba(0, 0, 0, .54);
display: none;
}
.pool-editor-block {
margin-bottom: 20px;
background: #1e293b;
border-radius: 7px;
padding: 10px 12px;
border: 1px solid #333;
}
.pool-editor-block input[type="text"] {
font-size: 1rem;
width: 200px;
margin-right: 10px;
border-radius: 4px;
padding: 3px 7px;
background: #232345;
color: #fff;
border: 1px solid #888;
}
.pool-editor-block input[type="color"] {
width: 28px;
height: 28px;
border: none;
margin-right: 10px;
vertical-align: middle;
background: #333;
}
.pool-editor-block textarea {
width: 98%;
height: 65px;
margin-top: 4px;
background: #141438;
color: #fff;
border-radius: 5px;
border: 1px solid #555;
font-size: 0.98rem;
padding: 5px 7px;
}
.pool-editor-actions {
margin-top: 6px;
}
.pool-editor-actions button {
background: #fcd34d;
color: #232345;
font-size: 1rem;
padding: 3px 16px;
margin-right: 6px;
border-radius: 5px;
border: none;
cursor: pointer;
}
.pool-editor-actions button.remove-pool-btn {
background: #ef4444;
color: #fff;
}
#add-pool-btn {
background: #34d399;
color: #22223b;
font-weight: bold;
font-size: 1rem;
padding: 6px 20px;
border-radius: 5px;
border: none;
cursor: pointer;
margin-bottom: 10px;
}
#apply-custom-pools-btn {
background: #fcd34d;
color: #232345;
font-weight: bold;
font-size: 1.1rem;
padding: 8px 22px;
border-radius: 7px;
margin-top: 5px;
border: none;
cursor: pointer;
}
#top-ui-bar {
position: static;
top: 0;
left: 0;
width: 100%;
z-index: 500;
background-color: #1f2937;
padding: 10px 20px;
border-bottom: 2px solid #8b5cf6;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
gap: 10px;
align-items: flex-start;
}
/* Wrapper to manage metrics and controls placement on desktop */
#ui-content-wrapper {
display: flex;
align-items: flex-start;
gap: 20px;
width: 100%;
justify-content: space-between;
}
h1#title {
margin: 0;
font-size: 1.5rem;
color: #8b5cf6;
width: 100%;
text-align: left;
}
/* --- WORKBENCH AREA (Dedicated Strip Canvas) --- */
#workbench-area {
position: relative;
width: calc(100vw - 40px);
margin: 0 20px;
min-height: 100vh;
height: auto;
padding-top: 20px;
padding-bottom: 50px;
overscroll-behavior: none;
}
/* Strip is positioned absolutely relative to the workbench */
.cutup-strip {
position: absolute;
display: flex;
align-items: center;
cursor: grab;
background-color: #1e40af;
color: #ffffff;
font-size: 0.8rem;
padding: 3px 5px;
/* padding-left: 35px; */
margin-right: -4px;
border-radius: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
user-select: none;
/* white-space: nowrap; */
z-index: 10;
touch-action: none;
/* transition: background-color 0.1s; */
border: 2px solid transparent;
border-color: var(--pool-border-color, transparent);
transition: left 0.4s cubic-bezier(.5, 2, .5, 1), top 0.4s cubic-bezier(.5, 2, .5, 1), background-color 0.1s;
}
.cutup-strip.has-delete-btn {
padding-left: 35px;
}
.cutup-strip:hover {
background-color: #1d4ed8;
}
.cutup-strip.active {
cursor: grabbing;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.8);
z-index: 1000;
}
/* Delete Button on Strip */
.delete-strip-btn {
/* position: absolute; */
position: static;
margin-right: 10px;
left: 8px;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
line-height: 16px;
text-align: center;
border-radius: 50%;
background-color: #ef4444;
color: #fff;
font-weight: bold;
font-size: 0.8rem;
cursor: pointer;
z-index: 10;
transition: background-color 0.15s;
}
.delete-strip-btn:hover {
background-color: #dc2626;
}
/* Global Deletion Control */
.deletion-disabled .delete-strip-btn {
display: none;
}
/* Control Panel Styles (inside the fixed bar) */
.control-panel {
background-color: transparent;
padding: 0;
border-radius: 0;
display: flex;
flex-direction: row;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
box-shadow: none;
width: auto;
}
button {
padding: 8px 15px;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.15s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
white-space: nowrap;
}
button:hover {
opacity: 0.9;
}
#user-input-btn {
background-color: #fcd34d;
color: #232345;
font-weight: bold;
}
#user-input-btn:hover {
background-color: #ffe066;
}
/* --- New/Updated Button Styles --- */
#pool-selector-btn {
background-color: var(--current-pool-color, #6366f1);
position: relative;
/* Anchor for dropdown */
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 8px;
}
#draw-only-btn {
background-color: #fcd34d;
/* Bright Yellow for primary action */
color: #1f2937;
font-weight: bold;
}
.arrow-icon {
font-size: 0.8rem;
margin-left: 10px;
transform: translateY(1px);
}
/* Toggle Deletion Button Styles */
#toggle-deletion-btn {
background-color: #3b82f6;
}
#toggle-deletion-btn.enabled {
background-color: #22c55e;
}
/* Clear button styles */
#clear-all-btn {
background-color: #ef4444;
}
#clear-all-btn:hover {
background-color: #dc2626;
}
/* --- Word Count Metrics Box (inside the fixed bar) --- */
#word-count-metrics {
background-color: transparent;
padding: 10px;
border-radius: 8px;
width: 300px;
box-shadow: none;
font-size: 0.9rem;
}
#word-count-metrics h4 {
margin: 0 0 5px 0;
font-size: 1.1rem;
color: #fcd34d;
border-bottom: 1px solid #4b5563;
padding-bottom: 3px;
}
.metric-item {
display: flex;
justify-content: space-between;
margin-bottom: 2px;
padding: 2px 0;
}
.metric-item strong {
font-weight: 600;
}
.metric-item span {
padding: 0 5px;
border-radius: 4px;
}
/* Pool Selector Dropdown */
#pool-selector {
position: absolute;
top: 100%;
left: 0;
min-width: 100%;
background-color: #1f2937;
border-radius: 6px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
margin-top: 5px;
z-index: 300;
overflow: hidden;
opacity: 0;
pointer-events: none;
transform: translateY(-10px);
transition: opacity 0.2s, transform 0.2s;
}
#pool-selector.visible {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.pool-item {
padding: 10px 15px;
cursor: pointer;
border-left: 5px solid transparent;
transition: background-color 0.1s;
font-size: 0.9rem;
}
.pool-item:hover {
background-color: rgba(99, 102, 241, 0.2);
}
.pool-item.selected {
border-left: 5px solid var(--pool-color);
font-weight: bold;
background-color: rgba(99, 102, 241, 0.1);
}
/* --- Custom Input Overlay Styles --- */
#custom-input-overlay {
position: absolute;
width: 280px;
background-color: #374151;
padding: 15px;
border-radius: 12px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
z-index: 1500;
display: none;
transform: translate(-50%, -50%);
border: 2px solid #fcd34d;
}
#custom-input-overlay h3 {
margin-top: 0;
font-size: 1.1rem;
color: #fcd34d;
}
#custom-text-input {
width: 100%;
min-height: 80px;
padding: 8px;
margin-bottom: 10px;
border-radius: 4px;
border: 1px solid #4b5563;
background-color: #1f2937;
color: #ffffff;
resize: vertical;
}
#custom-submit-btn {
width: 100%;
background-color: #4ade80;
color: #1f2937;
font-weight: bold;
}
#custom-submit-btn:hover {
background-color: #22c55e;
}
#jumble-all-btn {
background-color: #a78bfa;
/* or any color you like */
color: #22223b;
font-weight: bold;
}
#jumble-all-btn:hover {
background-color: #8b5cf6;
}
/* --- Confirmation Modal Styles --- */
#confirmation-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: none;
align-items: center;
justify-content: center;
z-index: 2000;
}
.modal-content {
background-color: #374151;
padding: 25px;
border-radius: 12px;
max-width: 400px;
width: 90%;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.8);
text-align: center;
}
.modal-content h3 {
color: #ef4444;
margin-top: 0;
margin-bottom: 15px;
}
.modal-content p {
margin-bottom: 20px;
line-height: 1.4;
font-size: 0.95rem;
}
.modal-buttons button {
margin: 0 10px;
padding: 10px 20px;
font-weight: bold;
}
#modal-confirm-btn {
background-color: #ef4444;
}
#modal-confirm-btn:hover {
background-color: #dc2626;
}
#modal-cancel-btn {
background-color: #6b7280;
}
#modal-cancel-btn:hover {
background-color: #4b5563;
}
/* --- Mobile/Small Screen Adjustment --- */
@media (max-width: 768px) {
#top-ui-bar {
flex-direction: column;
align-items: center;
padding: 10px 5px;
}
#ui-content-wrapper {
flex-direction: column;
align-items: center;
gap: 10px;
}
#word-count-metrics {
width: 95%;
padding: 5px;
}
.control-panel {
justify-content: center;
padding-bottom: 5px;
}
h1#title {
text-align: center;
}
#workbench-area {
width: calc(100vw - 20px);
margin: 0 10px;
padding-top: 20px;
}
}
.strip-text-editable {
outline: none;
min-width: 2ch;
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
.cutup-strip:focus-within {
background-color: #232345;
}
#edit-strip-modal textarea {
resize: vertical;
}
</style>
</head>
<body class="deletion-disabled">
<!-- STATIC TOP UI BAR -->
<header id="top-ui-bar">
<div id="tab-bar" style="display: flex; gap: 10px; margin-bottom: 5px;">
<button id="default-tab-btn" class="tab-btn active">Default Pools</button>
<button id="custom-tab-btn" class="tab-btn">Custom Pools</button>
<button id="metrics-tab-btn" class="tab-btn">Metrics</button>
</div>
<h1 id="title">Tactile Cut-Up Sandbox</h1>
<div id="ui-content-wrapper">
<!-- Control Buttons -->
<div class="control-panel">
<!-- 1. Dedicated Pool Selector Button -->
<button id="pool-selector-btn">
<span id="pool-name-display"></span>
<span class="arrow-icon">▼</span>
<div id="pool-selector"></div>
</button>
<!-- 2. Dedicated Draw Button -->
<button id="draw-only-btn">Draw Strips</button>
<button id="jumble-all-btn">Jumble</button>
<button id="user-input-btn">User Input</button>
<button id="toggle-deletion-btn">Enable Deletion</button>
<button id="clear-all-btn">Clear All</button>
</div>
</div>
</header>
<div id="custom-pools-panel" style="display: none;">
<div id="custom-pools-list"></div>
<button id="add-pool-btn">+ Add Pool</button>
<button id="apply-custom-pools-btn">Apply Custom Pools</button>
</div>
<!-- Word Count Metrics Display -->
<div id="metrics-panel"
style="display:none; position:absolute; top:76px; left:0; width:100%; z-index:3000; background:#1f2937; box-shadow:0 8px 24px rgba(0,0,0,.54);">
<div id="word-count-metrics">
<h4>Word Count Metrics</h4>
<div id="total-words" class="metric-item">
<strong>TOTAL WORDS:</strong> <span>0</span>
</div>
<div id="pool-metrics-container"></div>
</div>
</div>
<!-- WORKBENCH AREA -->
<div id="workbench-area">
<!-- Cut-up strips will be appended here by JavaScript -->
</div>
<!-- Custom Input Overlay -->
<div id="custom-input-overlay">
<h3>User Input Strips</h3>
<textarea id="custom-text-input" placeholder="Type phrases here (one per line)"></textarea>
<button id="custom-submit-btn">Create Strips</button>
</div>
<!-- Confirmation Modal -->
<div id="confirmation-modal">
<div class="modal-content">
<h3 id="modal-title">Confirm Action</h3>
<p id="modal-message">Are you sure you want to proceed?</p>
<div class="modal-buttons">
<button id="modal-confirm-btn">Confirm</button>
<button id="modal-cancel-btn">Cancel</button>
</div>
</div>
</div>
<!-- Edit Node Text Modal -->
<div id="edit-strip-modal"
style="display:none; position:fixed; z-index:3100; left:0; top:0; width:100vw; height:100vh; background:rgba(0,0,0,0.4); align-items:center; justify-content:center;">
<div
style="background:#22223b; color:#fff; border:2px solid #fcd34d; border-radius:10px; max-width:400px; padding:28px 20px 18px 20px; margin:auto;">
<h3 style="margin-top:0; margin-bottom:15px; color:#fcd34d;">Edit Node Text</h3>
<textarea id="edit-strip-textarea"
style="width:100%; min-height:60px; background:#1f2937; color:#fff; border-radius:5px; border:1px solid #fcd34d; margin-bottom:15px;"></textarea>
<div style="display:flex; gap:10px; justify-content:flex-end;">
<button id="edit-strip-save-btn"
style="background:#fcd34d; color:#232345; font-weight:bold;">Save</button>
<button id="edit-strip-cancel-btn" style="background:#444; color:#fff;">Cancel</button>
</div>
</div>
</div>
<div id="strip-context-menu"
style="display:none; position:fixed; z-index:3100; background:#232345; color:#fff; border-radius:8px; border:2px solid #fcd34d; box-shadow:0 6px 16px rgba(0,0,0,0.18); min-width:120px;">
<button id="context-edit-btn"
style="width:100%; background:none; border:none; padding:12px; color:#fcd34d; text-align:left; font-weight:bold; font-size:1rem; cursor:pointer;">Edit
Text</button>
<button id="context-detach-btn"
style="width:100%; background:none; border:none; padding:12px; color:#ef4444; text-align:left; font-weight:bold; font-size:1rem; cursor:pointer;">Detach
from Chain</button>
</div>
<script>
// --- Pool Definitions ---
const USER_INPUT_POOL = {
name: "User Input",
color: "#fcd34d", // Bright Yellow for distinction
phrases: []
};
const pools = [
{
name: "Atmospheric Horror",
color: "#a78bfa", // Lavender
phrases: [
"ON-SCREEN TEXT: The following story", "is based on true events.", "FADE IN TO: CREDIT SEQUENCE-", "BLACK VOID/NOCTURNAL CITY We glide", "through a black void illuminated", "by searing, ELECTRIC FLASHES in", "brilliant phosphorescent colors. The blackness", "begins to rip and tear,", "granting us glimpses of a", "nocturnal city. WITH THE SOUND", "OF A WING FLAP, we", "sail through a gaping hole,", "out of the electromagnetic dimension", "into an AERIAL VIEW OF", "WASHINGTON D.C.- NIGHT We continue", "to fly until we pick", "out one particular jewel of", "this nightscape: a stately office", "building. We descend effortlessly WASHINGTON", "POST (EFX) passing down through", "the roof and six empty", "floors. WASHINGTON POST NEWSROOM- NIGHT", "On the seventh floor we", "hover near the ceiling, gazing", "down on an office Christmas", "party. None of the celebrating", "workers sense our presence as", "we snake through the labyrinth", "of cubicles and glass cells,", "until: POV DIVES INTO A", "PHONE LINE travelling fast along", "the inside of the wires,", "erupting out of the EAR-PIECE", "with a shrieking squall of", "feedback. WASHINGTON POST NEWSROOM- NIGHT", "A MAN yanks the phone", "from his ear, wincing. MAN", "(off feedback) What the hell", "was that? The man is", "JOHN KLEIN, an up-and-coming reporter", "for the Washington Post; intelligent,", "with East Coast good-looks. (on", "the phone) You still there?", "Okay, I missed that spelling.", "With a `Y.` Got it.", "(correcting a name in his", "story) No. You`re on the", "record. You can`t scrape your", "career off your shoe anyway.", "At least do some damage", "on your way out, see", "your name in the paper", "one more time. Okay. He", "clicks off the line, speed", "proofs his story, sends it", "on its way. He is done,", "and done for the day.", "He pushes back his chair", "and stands up, kneads his", "back. ED FLEISCHMAN, (29), rolls", "out of the next cubicle:", "John! `The Balkans Peace Council", "is comprised of ten members`", "or `is compos of ten", "members`? Twelve members. Oh. Right.", "Thanks. Ed rolls out of", "sight. John grabs his coat", "and heads past Ed is cubicle.", "`Composed.` As John walks passed,", "Ed rolls back out: What?", "John disappears through glass doors", "into: NATIONAL DESK Editor CYRUS", "BILLS, (65), a scarecrow with", "a cigarette, scrolls through John is", "piece on the computer. His", "eyes never leave the screen.", "This will make them sweat,", "it is good, I`1ll lead with", "it Great, I`ll see you", "Monday. What? I`ve got to", "go. You can`t- how does", "that look, my rising star", "not showing up at the", "Christmas party? John smiles. This", "is the closest thing to", "a warm moment he and", "Cyrus have ever had. Mary is", "waiting for me. Cyrus inhales", "deeply on his cigarette, watching", "John through the glass doors", "weave his way out of", "the party. COLONIAL-STYLE HOUSE- MCLEAN,", "VIRGINIA- NIGHT A nice, well-kept", "house in an upscale neighborhood.", "COLONIAL-STYLE HOUSE- MCLEAN, VIRGINIA- NIGHT", "John and MARY KLEIN hang", "back in an empty kitchen", "listening to the REAL ESTATE", "LADY. Mary Klein, (31), a", "beautiful redhead with a quick", "smile and an irrepressible joie", "de vivre. It is a steal", "at this price. We can", "get it, but we`d have", "to make an offer today.", "The owner is moving fast. Mary", "is ready to jump. John", "looks uncertain. Think it over,", "I`ll make some calls. John", "and Mary wander up the", "Oh, my God. I think", "I love it. (smiles) I", "think you love it too.", "She hits him; he laughs.", "I don`t know if we", "can swing it. Come on,", "let is live dangerously. They enter", "the MASTER BEDROOM. John clicks", "a light switch up and", "down. A light blinks on", "and off behind a door.", "What is this? They open the", "door. A DARK CLOSET Mary", "grabs John, suddenly excited. She", "kisses him. He kisses her.", "Passionately. She pulls him into", "the closet, laughing. John closes", "the door. Come on, let is", "go, right here. He slides", "his hands under her blouse.", "She shrieks with laughter. They", "fall against the wall. IN", "THE HALLWAY CAMERA tracks towards", "the bedroom. CLOSET Things are", "getting steamy inside. IN THE", "BEDROOM CAMERA tracks quickly towards", "the closet door. IN THE", "CLOSET A naked light bulb", "switches on. John and Mary", "jump. A MOTH bats the", "bulb with its wings, casting", "large flickering shadows. IN THE", "BEDROOM John and Mary tumble", "out of the closet. (blushing)", "Oh, here you are. John", "straightens his tie. Mary smooths", "her hair. Just making sure", "there is ample closet space. Good,", "good. Well I`ve got good", "news. The house is yours", "if you want it. John", "looks at his wife. (smiling)", "We want it. UPSCALE FRENCH", "RESTAURANT- NIGHT Through the window,", "John and Mary clink champagne", "glasses. RESTAURANT PARKING LOT- LATER", "Long graceful tracking shot as", "John and Mary walk from", "the restaurant to the Valet", "station. It is a moment of", "pure happiness, ease, everything between", "them is right. John hands", "the VALET his ticket. They", "bring the car. The Valet", "hands the keys to John.", "John tosses Mary THE KEYS", "and flops into the passenger", "seat. He is drunk. Mary climbs", "behind the wheel. They drive", "off. STREET IN GEORGETOWN- NIGHT", "Their car travels through night", "time Georgetown. They pass a", "ROAD CONSTRUCTION SITE that looks", "like a brightly lit theater", "set, a CLOCK TOWER that", "reads 1:30, and A BIRD", "SANCTUARY. They turn onto a", "road overhung with trees. The", "street is quiet and empty.", "CAR- NIGHT IN THE SILENCE", "OF THE CAR, Mary turns", "to You know what I", "kept thinking when we were", "looking at the house today?", "It is better than your sister is?", "(laughing) No We should put", "a mattress in that closet?", "Yeah! No, I kept thinking", "it felt like a dream", "come true. Mary stops at", "a light. John looks at", "her, the red light reflecting", "off her eyes. She is serious.", "C/mere. She leans toward him,", "he kisses her hard. Can", "we make it home first?", "Depends how fast you drive.", "THE LIGHT changes. MARY gooses", "the accelerator past the intersection,", "suddenly A large SHAPE moves", "into the headlights. A BLUR,", "too fast to see. MARY", "sucks in a cry, hits", "the brakes, cranks the wheel.", "The Car slews sideways. Her", "head slams against the side", "window, which spiderwebs. JOHN flies", "forward. The seatbelt jerks him", "back. He flings out an", "arm to protect his wife.", "The car jolts toa stop.", "He sees Mary slumped forward", "against her seatbelt. She is unconscious.", "Mary He touches her gent.y.", "No response, although she is breathing.", "Mary. come on. Christ He is", "afraid to move her. He", "pulls out his cell phone,", "dials , hand shaking. (into", "the phone) There is been an", "accident He climbs out of", "the car into THE STREET", "He goes to the front", "of the car, dreading what", "he might find. But nothing is", "there. He checks under the", "car. Nothing. He stands in", "the street, looking left, right,", "forward. It is strangely empty. In", "the background, a LOW HANGING", "BRANCH ten feet behind the", "car rustles in the breeze.", "The faint WAIL of a", "SIREN bleeds in. HOSPITAL- NIGHT", "John watches Mary behind a", "glass partition. She has been", "readied for a scan. She is", "fully conscious now, her eyes", "wide and scared. CAT-SCAN SCREEN", "DR. DEBORAH McELHONE looks at", "the IMAGE OF MARY is BRAIN.", "looks on, worried. We`re doing", "the CAT-SCAN to make sure", "there is no swelling or bleeding", "HOSPITAL CUBICLE- NIGHT Mary is", "groggy, bandages on her nose.", "John holds her hand You", "okay? Yeah, I`m okay But", "she is not. She stares off,", "pre-occupied, remote. Finally, she looks", "at him: You didn`t see", "it, did you. See what?", "Mary holds his gaze for", "a beat, then looks away.", "Nothing. John is hand tightens on", "hers. What did you see?", "Mary doesn`t answer. An animal?", "She shakes her head. What?", "Silence. There was nothing, I", "looked. Mary turns away, her", "eyes tense and worried. Honey", "John. there is something wrong with", "me. HOSPITAL- WAITING ROOM- LATER", "John dozes in a chair.", "Dr. McElhone places a hand", "on his shoulder; he snaps", "awake. Mr. Klein? We need", "to talk HOSPITAL/DOCTOR is OFFICE- NIGHT", "THROUGH THE OUTSIDE WINDOW INTO", "DR. MCELHONE is OFFICE We see", "John and Mary sitting at", "Dr. McElhone is desk, as she", "talks to them. After a", "long moment John slowly puts", "his arm around his wife.", "HOSPITAL- WAITING ROOM- NIGHT John", "sees Mary is parents, WOODROW and", "RUTH DUNNING, rush in carrying", "overnight bags. Where is she?", "She just went in for", "radiation treatment. Radiation? Already? John is", "voice is shaky, everything is", "happening too fast. They have", "to shrink it down as", "much as possible before the", "surgery tomorrow Tomorrow? My God", "Okay. So they`re doing surgery.", "So it is operable, right? Yeah.", "It is. Good. That is good", "They`re bringing in a neurosurgeon", "from Johns Hopkins, he is one", "of the best. Suddenly, tears", "roll down John is face. Woodrow", "awkwardly puts an arm around", "John. It is going to be", "fine. She is going to be", "fine HOSPITAL- MORNING In the", "cold blue morning light, snowflakes", "fall on the bare trees", "outside the hospital. RECOVERY ROOM-", "SEVERAL DAYS LATER- MORNING Mary is", "hair is gone and her", "head is bandaged from surgery,", "but she is more awake and", "alert than the last time", "we saw her. John sits", "with her, a yellow pad", "in his hand. (reciting a", "list) and a snow suit", "for little Gary- I`ll call", "Jane and Doug to check", "the size. I think that is", "everybody. Are you sure you", "can do all this? Have", "the stores wrap everything for", "you, okay? Would you stop?", "It is fine. I can handle", "this. There is a long silence.", "Then: John. I`m sorry. About", "what? About all this. I", "feel like I ruin everything.", "John looks at her; she is", "talking about a lot more", "than just Christmas. He takes", "her hand and smiles. You", "haven`t ruined a damn thing.", "CAR- DRIVING- NIGHT John drives", "home lost in thought, retracing", "the exact route they drove", "on the night of the", "accident. He passes the CONSTRUCTION", "SITE, the CLOCK TOWER, the", "BIRD SANCTUARY. He drives slowly,", "peering out the windshield, looking", "for something, a reflection, a", "tricky shadow- whatever it was", "Mary saw that made her", "hit the brakes. As he", "rolls toward the exact spot,", "he sees the LOW HANGING", "TREE BRANCH, stops the car,", "gets out. STREET- NIGHT THICK", "BLACK SKID MARKS scar the", "street underneath the branch. John", "tries an experiment. He gets", "in the car, backs up", "forty yards, guns the accelerator.", "Suddenly, he slams the brakes,", "cranks the wheel. The car", "slews sideways- he is pulled", "up Short by the seatbelt.", "His face never even gets", "near the window. He sits", "back. Tugs on the shoulder", "harness. It locks up. So", "why didn`t it lock up", "for Mary? He climbs out", "of the car, searching the", "street- searching for an answer,", "a reason. What did she", "see? John stands behind the", "BRANCH and squints his eyes:", "the branch BLURS and TWO", "DISTANT RED CONSTRUCTION LIGHTS shine", "like EYES, forming the impression", "of a large looming figure", "was that it? John walks", "to the front of the", "car. That is when he notices", "something on the front bumper.", "Something he missed completely the", "night of the accident The", "center of the bumper is", "scorched black. HOSPITAL CAFETERIA- NIGHT", "CAMERA tracks along a chrome", "counter top, past stacks of", "plates, cups of coffee It is", "called a Glioblastoma Multiforma. Temporal", "lobe tumor. Very aggressive. Jesus", "Christ According to Dr. McElhone", "it is very rare. Strikes one", "in 600,000. You`ve got a", "better chance of catching the", "plague. CAMERA comes to rest", "behind John and Ed sitting", "at the counter. Now we", "can see their faces in", "A LARGE MIRROR behind the", "pie rack. John is gray", "with l.ezk of sleep. He", "forces out the next words:", "The doctor says with a", "tumor like this, most people", "never make full recoveries. Is", "there anything else they can", "do? They did surgery, but", "couldn`t get it all. They`1l", "keep giving her chemo as", "long as she is strong enough.", "So far it is working, she is", "looking a lot better, her", "spirits are high. Did they", "say it is a result of", "the accident? No, turns out.", "it is been there a while.", "John, if there is anything I", "can do. John just shakes", "his head. Ed looks at", "his friend. For the first", "time, John looks fragile, truly", "lost. John stares into the", "middle-distance. Three days ago we", "were house-hunting. Last week I", "was up all night worried", "that I`d bounced the cable", "check. It is like one day,", "you`re driving in your car", "and the universe just points", "at you and says, `Ah,", "there you are. The happy", "couple. I`ve been looking for", "you.` NEW ANGLE Facing John", "and Ed at the counter.", "The camera slowly pulls back,", "through the pies, back through", "the mirror itself. and Ed", "darken as we pull back,", "further and further into this", "impossible vantage point until .", "they disappear. JOHN AND MARY is", "APARTMENT- NIGHT A SQUARE OF", "WARM YELLOW LIGHT glows in", "the night- it is the window", "to the dining room. Christmas", "Eve. From outside, we watch", "John, Ruth and Woodrow finish", "last minute preparations and sit", "down for a meal. JOHN", "AND MARY is APARTMENT- NIGHT ANGLE", "from atop the Christmas tree.", "They all join hands around", "the table. (praying) We are", "grateful for this meal, and", "so grateful that Mary will", "be home with us tomorrow,", "for Christmas dinner. Amen. ANGLE", "FROM TABLE. Ruth looks at", "John. He does his best", "to smile. The look of", "hope on Ruth is face is", "too much. What time are", "we picking her up? They", "said sometime late morning, after", "her chemo. But Ruth, you", "know it is just for a", "few hours, if she is strong", "enough. I know They start", "to eat in silence.Suddenly, the", "PHONE RINGS They all look", "at each other, motionless. John", "moves into the KITCHEN. Hello?", "John has his back to", "Ruth and Woodrow. We watch", "his face. John? It is Dr.", "Mcelhone. Mary had a seizure.", "We`re trying to relieve the", "pressure HOSPITAL- NIGHT We track", "behind John as he runs", "frantically `own long corridors. Suddenly", "he stops: Dr. McElhone .:", "standing with her head bowed,", "the answer is written on", "her face. And he knows", "HOSPITAL- MARY is ROOM- NIGHT John", "slowly enters then pulls back", "a curtain, his face full", "of fear Mary lies motionless", "on a bed. John moves", "slowly towards her, his entire", "self somehow diminished. He sits", "at her side and grips", "Mary is hand like he`il never", "let her go. His shoulders", "tremble with sharp inconsolable sobs.", "He leans in close and", "presses his lips to her", "white lifeless face; a kiss", "that will have to last", "him a lifetime. HOSPITAL ~", "LATER Snow falls. A SQUARE", "OF WARM YELLOW LIGHT glows", "in the night it is the", "window to the waiting area.", "From outside, WE SEE: Ruth,", "who can barely stand, holding", "onto John. She lets out", "a Silent wail of grief;", "Woodrow watches them, unable to", "comprehend what has happened. Their", "images blur as we RACK", "FOCUS from the window, moving", "back through layers of FALLING", "SNOWFLAKES. MARY is HOSPITAL ROOM- MORNING", "The bed is empty. John", "slowly packs Mary is belongings into", "a RED OVERNIGHT CASE. He", "doesn`t know how to do", "this. What about the toothbrush?", "The lipstick? He picks up", "her dress, smells it. His", "eyes close. She knew. John", "opens his eyes, puzzled. A", "middle-aged ORDERLY stands in the", "doorway. The man smiles shyly", "at him. She was drawing", "angels. John says nothing. The", "man moves away. But now", "John sees the NOTEPAD next", "to the phone. He reaches", "for it. John is face darkens.", "It is not an angel. THE", "NOTEPAD: a mad scribble of", "a figure, its bulging eyes", "colored red with Mary is lipstick.", "What is it? John flips", "the page. There it is", "again, and again, page after", "page of Mary is obsessive drawings:", "A man-like shape with insect", "eyes and giant wings growing", "from its back. BENCH- LAFAYETTE", "PARK- DAY The trees are", "leafless and the ground muddy.", "Mitten joggers, dog walkers pass", "in front of a partial", "view of the White House.", "John is sitting on a", "bench with his back to", "us. He looks as though", "he is been there a long", "time, as though he may", "sit there forever. The CAMERA", "begins to CLOSE IN on", "his back. John senses something.", "He whips around and glares", "straight at us. The CAMERA", "STOPS SHORT. John sees nothing,", "his face relaxes. He turns", "forward again. The CAMERA starts", "EASING toward him again. And", "very faintly now comes the", "hint of a RUSTLE. Wings.", "Louder. John strains to hear.", "He slowly scans left, right.", "Nothing. The CAMERA is right", "behind him now. He can`t", "hear anything. Not even the", "sudden WHOOSH, the single FLAP", "as the CAMERA LIFTS OFF", "behind him, rising, leaving John", "down below, smaller, smaller, until",
"he is just another unremarkable human", "dot in the park. FADE", "TO BLACK. TITLE SUPER: ELEVEN", "MONTHS LATER- FADE IN TO:", "ELECTRONICS STORE- NIGHT We are", "watching a BANK OF TV", "MONITORS through the window of", "an electronics store. Christmas decorations", "light the foreground. On it,", "John Klein addresses Tim Russert", "on `Meet the Press`. Under", "JOHN there is a super-title:", "John Klein Washington Post. WE", "MOVE IN through the windows", "towards the monitors and hear", "John talking: I think what", "we saw this year was", "an apathetic electorate. Interest in", "the campaign was low and", "voter turn-out was the worst", "in forty years. Couldn`t that", "just be a sign of", "voter satisfaction? People didn`t feel", "the ne for big changes.", "I don`t think so, Tim.", "I think people are very", "unsatisfied with their situation. And", "what made them so complacent", "is that they didn`t see", "any viable options for improvement.", "Rather than endorse the status", "quo, they decided they`d rather", "just stay home and be", "left alone. As the monitors", "play John is interview, MOVE INTO", "crackling electric pixels and we:", "WASHINGTON POST NEWSROOM- DAY PULL", "BACK FROM TV SCREEN TO", "SEE: John is face- he is watching", "himself on TV. Ed and", "other staff members look over", "his shoulder. Is there anybody", "out there you think could", "fill out the bottom of", "the ticket? I`ve been hearing", "a lot of talk about", "Russ McCallum. The Governor of", "Virginia. Right. He is got the", "environmental record they need. If", "he is going to throw his", "hat in the ring, look", "for him to announce by", "next week As the segment", "ends, the staffers applaud. John", "laughs and takes a bow.", "Now we get a good", "look at John. He is healthy,", "fit- but older. That confident", "sparkle is gone from his", "eyes. He wears the past", "year like it was ten.", "Hey- - I told her", "to watch. Who? Peter is friend,", "Gwen. Sk. s gonna be", "there tonight. Oh, hey, look,", "I don`t think I`m gonna", "be able to make it.", "` Are you kidding me?", "She is gorgeous. Believe me, this", "will take, like, no effort.", "John moans. Come on, you", "can`t keep blowing this off.", "I`m not blowing it off,", "I`m interviewing the Governor, I`ve", "gotta be in Richmond at", "eight. Ed pulls a pink", "message slip from his shirt", "pocket, hands it to John:", "Almost forgot. Garrett Knox called.", "The review got bumped to", "tomorrow night. John eyes the", "message slip, stuck. Ed, I", "don`t know Look: she is not", "Mary, not by a mile.", "But you know what? No", "one ever will be. You", "can`t hold that against them.", "I can`t? Ed looks at", "John; he is only half-joking. MARRAKESH", "RESTAURANT- NIGHT Turkish dining. Pillows", "on the floor and belly", "dancers. Kitsch heaven to Ed,", "John is idea of hell. With", "them: Ed is partner PETER, (39),", "and GWEN, (33), pretty, friendly,", "sophisticated. They all eat hot,", "marinated chicken with their hands:", "I gotta tell you, Ed,", "I spent three weeks in", "Turkey- it was not like", "this. Well, it should`ve been.", "PETER Wait `til he starts", "dancing. (to John) When were", "you there? `88. Covering the", "earthquake. I just missed you.", "I was there in `89.", "Peace Corps. John looks at", "her, intrigued. A belly dancer", "rotates toward them, beckons Ed", "to join her. Ed downs", "his Ouzo and struggles to", "his feet: My whole life", "has been leading to this", "moment. Ed pulls Peter with", "him onto the dance floor.", "John smiles at Gwen. So", "you were in Istanbul? No,", "it was this tiny village-", "you can`t even believe there", "are still places on earth", "like it. These families raise", "mountain goats- well, the men", "do- and then the woman", "use the hair to make", "the most beautiful blankets John", "smiles. Ambarat. Yes! Have you", "been there? It is one of", "my three secret places. Gwen", "smiles at him, charmed. Places", "I know I could go", "and be happy the rest", "of my life if I", "ever had to leave Washington", "for good His voice trails", "off. A woman with RED", "HAIR steps out onto the", "patio. John is eyes take on", "a DISTANT LOOK- Mary is", "always with him. Gwen watches", "John, waiting for him to", "continue. So- where are the", "other two? Other what? Places", "you could be happy. They`re", "secret! I`1ll tell you this:", "the second one is very", "cold and you have to", "speak Portuguese. And the third?", "I haven`t found it yet.", "MARRAKESH RESTAURANT- NIGHT John and", "Gwen walk from the restaurant", "to the Valet station. A", "long graceful tracking shot reminds", "us of John is last happy", "evening with Mary. But tonight", "he is with Gwen. They hand", "their tickets to the valet.", "Well. It was nice meeting", "you. Tierra del Fuego. What?", "Your second place. Am I", "right? John looks at her:", "dark hair and eyes, smooth", "white skin, warm smile. Beautiful,", "smart, charming, funny If you", "can stand another cup of", "coffee I`m just a few", "blocks away. Available. This woman", "is flawless. So why does", "John feel absolutely nothing? That", "sounds great. But John is voice", "trails off. An awkward silence.", "The Valet arrives with Gwen is", "car. She looks at him", "expectantly: JOHN (CONT` D} Look,", "I`ve got an early morning,", "so Okay. Anyway, I really", "liked talking to you. Call", "me, okay? John nods absently.", "Gwen looks at him, his", "thoughts are miles away. She", "gets into her car and", "drives off. Ed and Peter", "come outside and find John", "alone, staring intently down the", "street: Where`d she go? John", "mutters. I don`t know. JOHN is", "APARTMENT- BEDROOM CLOSET- NIGHT A", "moth eats through a sweater", "in the dark. The closet", "door swings open. John is hand", "reaches in, pulls a chain.", "A naked bulb burns white.", "John looks restless, irritated. He", "yanks an empty suitcase from", "the top shelf, sending clothes", "and books cascading to the", "floor. As he gathers them", "up, he is pulled up short", "by the sight of Mary is", "RED OVERNIGHT CASE. It sits", "in the corner where he", "left it almost a year", "ago. He bends to his", "knees, moves his hand to", "the lock, hesitates, afraid to", "unlock the past. Finally, he", "lifts the 1: And there,", "staring up at him is", "a reminder of all that", "he has lost. His hand", "touches her scarf, lipstick, a", "brush still twined with her", "red hair John closes the", "case, slides it back into", "the corner. He grabs his", "suitcase, throws it on the", "bed, jamming in clothes. WASHINGTON", "D.C. STREETS ~ NIGHT John", "drives quickly through empty streets,", "crosses the fourteenth street bridge.", "JOHN is CAR John is driving", "through the outskirts of Northern", "Virginia. (into cell phone) Hey,", "Ed. You were right. Gwen", "was very nice. If you", "talk to her, tell her,", "uh I don`t know. Anyway,", "it is about one o`clock. I`m", "kind of wired, so I`m", "heading down to Richmond tonight,", "talk to you later. JOHN is", "CAR- HIGHWAY- NIGHT John is", "speeding down the interstate. Traffic", "is thin. He passes under", "THE LIGHTED SIGN FOR RICHMOND.", "His car drives on into", "the night. JOHN is CAR ~", "LATER John squints, trying to", "stay focused on the road", "as he drops down a", "crest into a FOG BANK.", "He flexes his brakes. Suddenly", "BLINK: Red lights flash across", "the dashboard. The car stalls.", "JOHN is CAR- MOMENTS LATER The", "car drifts out of the", "fog onto the shoulder and", "comes to a stop. JOHN is", "CAR John tries to restart", "the engine. Come on, come", "on, come on He looks", "around. A dark, empty road.", "It doesn`t feel right. He", "glances in his mirror. HIS", "POV: Nothing behind him. Not", "another pair of headlights. Not", "even the interstate. John turns", "the key again. Silence. Not", "so much as the whine", "of the starter. He switches", "on the headlights. Nothing. He", "punches up the radio: dead.", "So is the car phone.", "John smacks his palms on", "the wheel. He takes out", "his cell phone. It beeps,", "shuts down. Drained. He looks", "at his watch: 2:20 AM.", "THROUGH THE WINDSHIELD Distant lightning", "flickers hypnotically. It seems to", "be moving from the ground", "up to the sky. JOHN,", "mystified, gets out of his", "car. COUNTRY ROAD- NIGHT It is", "a cold night, and he", "tucks his hands under his", "arms. He should put on", "his coat. THE LIGHTNING flickers", "violently, a few miles away.", "But where is the thunder?", "It is silent, eerily silent, so", "quiet John can hear his", "heart beating. Abruptly the lightning", "is extinguished. No stars. No", "moon. No light at all.", "JOHN feels a surge of", "anxiety. He is alone at night", "in the middle of nowhere.", "(low whisper) Okay. Okay. John", "shrugs on his overcoat and", "locks the car. He looks", "up and down the road.", "Nothing. He walks in darkness.", "Now he hears a strange", "low rumble. What the hell", "is that? The sound vibrates", "the road below his feet.", "Finally, he catches sight of", "a porch light on a", "distant farmhouse. Turning up his", "collar and plunging his hands", "in his pockets, John heads", "toward it at a fast", "clip. FARMHOUSE- NIGHT John boards", "onto the porch. The house", "is dark, except for a", "single : 1b. No use", "putting it off: He knocks", "on the door. Waits. Waits.", "A MAN opens the door.", "John smiles: _ Hi. My", "car broke down just up", "the road. May I use", "your phone? The Man stares", "at him, transfixed. MAN It is", "him. This response makes no", "sense to John. A WOMAN", "steps into view from the", "shadows behind the door; she", "peers nervously at as the", "Man raises a gun: MAN", "I`ve been waiting for you,", "you son of a bitch.", "The Man grabs John by", "his lapels and hauls him", "inside. The door shuts hard.", "GORDON SMALLWOOD is HOUSE- BATHROOM- NIGHT", "The Man, GORDON SMALLWOOD, (43),", "holds his gun on JOHN,", "who stands dressed in his", "overcoat in the Smallwood is mildewy,", "pink shower stall. Gordon is", "sitting on the tank of", "the toilet, his feet on", "the closed lid. He keeps", "the gun steady with his", "elbows on his knees. Look,", "I don`t Shut up. John", "shuts up. The two men", "watch each other, hearing the", "tread of approaching footsteps. The", "Woman- Gordon is wife DENISE SMALLWOOD,", "(28) enters. Honey, Connie is here.", "John straightens slightly as A", "WOMAN enters, wearing a sheriff is", "deputy uniform under her unzipped", "parka. She is SGT. CONNIE", "PARKER, (32)- blond, with keen", "blue eyes and an honest", "face. She calmly assesses the", "scene: John is enormously relieved.", "Okay Gordy, why don`t you", "put away the gun and", "tell me what is going on.", "Gordon keeps the gun trained", "on John. Connie places her", "hand on the barrel and", "points it to the floor.", "This is the third night", "in a row he is come", "around, this sonofabitch is stalking", "us Look, my car broke", "down up the road, my", "name is John Klein and", "Let is let Gordy finish, Mr.", "Klein. Gordon speaks with a", "disturbing edge of fear in", "his voice: Two nights ago,", "at 2:30, there is this bang", "on the door. I get", "up, and here is this guy,", "says he wants to use", "the phone. But there is something", "creepy about him, right? So", "I tell him to get", "lost. No big deal. But", "last night, at 2:30 on", "the dot, guess who is back?", "You`re sure it was Mr.", "Klein here? Gordon stares straight", "into John is eyes: Absolutely positive.", "(under his breath) This is", "crazy. Gordon has good ears.", "(sternly) I am not crazy.", "I didn`t say Connie holds", "up a hand: (to Gordon)", "So then what? So last", "night I warned him off", "my property. I used the", "word `trespass, ` Connie. And", "he still came back. (to", "John) I had the right", "to shoot you on my", "porch. You`re lucky I`m a", "Christian. He`d have actually had", "to be inside the house,", "Gordon. Fine. So tonight I", "figured maybe the dumb fuck", "(sternly) Gordon. might come back", "again, and sure enough, here", "he is! John turns to", "Sgt. Parker, taking pains to", "demonstrate that he is the", "sanest person in the room.", "Officer, there must be some", "mistake. I`ve never been here", "before in my life, I`ve", "never seen these people, I", "live in D.C. Check my", "wallet. Connie calmly reaches into", "John is pocket. She pulls out", "his wallet, flips it open,", "spots his press pass. Her", "eyes jump to his face.", "The Washington Post. Do I", "have to keep standing in", "the shower? GORDON is FRONT DOOR-", "LATER John follows Connie to", "the door, not sure whether", "to be relieved or worried.", "You can wait for me", "outside, Mr. Klein. John heads", "through the door without a", "word. Nothing makes any sense", "to him. (to Gordon) We`ll", "run a check on him.", "Anyone else comes by, forget", "the gun, okay? Just call", "me. Find out what he", "wants. Connie squeezes Gordon is shoulder.", "He goes to the doorway", "and watches JOHN standing beside", "Parker is Patrol car. Connie turns", "to Denise. She pantomimes tipping", "a bottle to her lips:", "has Gordon been drinking? Denise", "shakes her head no. So", "you didn`t actually see the", "guy yourself, did you? Before", "tonight? DENTSE No. But I", "heard the knocks, three nights", "in a row. GORDON is HOUSE", "John sees them all step", "onto the porch. Then, in", "spite of the odd circumstances,", "he watches as Connie, Gordon", "and Denise do a distinctly", "small-town thing: They all say", "a friendly good night to", "each other. Hug. Then, as", "Connie walks towards John, Gordon", "shouts: You don`t scare me!", "PATROL CAR- NIGHT John sits", "next to Connie in the", "front seat. Connie pulls out", "onto the empty road. I`m", "giving you a ride to", "town. Unless you want to", "sleep in your car. I", "can call a tow for", "the morning. They pull up", "behind John is car. You probably", "want to get your bag?", "What? You probably brought a", "bag. Oh He steps out", "of the cruiser. ROAD- NIGHT", "John crosses the road, opens", "his trunk, takes out his", "weekender. He sees Connie on", "the two-way. He passes through", "her headlights, climbs back in", "the Prowler. PROWLER- NIGHT He is", "inside the car in time", "to HEAR the DISPATCHER over", "the two-way DISPATCHER is VOICE man is", "clean as Clorox, if you", "believe the computer. Copy. Thanks,", "Avis. She clicks off, gives", "John a little shrug, shifts", "into drive. Connie puts it", "as plainly as she can:", "You`re a long way from", "D.C. What are you doing", "here? Driving through. We`re not", "on the way anyplace, Mr.", "Klein. John is silent- he", "can`t explain how he got", "here. He stares out the", "window. They`re passing through dark,", "quiet little Point Pleasant. Population", "6,000. Relax. I recognize you.", "(smiles) `Meet the Press.` You", "saw that? We`re not all", "bumpkins. Connie steers into the", "parking lot of a chain", "motel. So I`m not under", "arrest? Well, you didn`t steal", "anything, you didn`t hurt anyone,", "there is no breaking and entering", "You had trouble with them", "before? Gordy and Denise? Naw.", "They`re good people, but She", "hesitates, she knows she shouldn`t", "say this: CONNIE: (cont`d) Things", "have been a little strange", "around here lately. She leans", "across him to open the", "door, but mostly to smell", "his breath. I guarantee I`m", "sober. He climbs out. You", "still haven`t told me what", "you`re doing here. In the", "middle of the night. John", "hefts his bag. When I", "find out, I`ll let you", "know. Where am I, anyway?", "Connie points to a sign:", "WELCOME TO POINT PLEASANT. She", "watches him walk into the", "motel. MOTEL- NIGHT The sleepy,", "cranky NIGHT MANAGER runs John is", "charge. concentrates on the road", "map stretched out under the", "glass of the check-in counter.", "His finger traces Washington to", "Richmond, a straight shot of", "interstate, not more than 120", "miles. THE NIGHT MANAGER returns", "with John is credit card. You", "think you could show me", "where we are on the", "map? NIGHT MANAGER We`re right", "on the state line. John", "traces to the Virginia/West Virginia", "border. The night manager snorts", "with disgust. Tourists. Spare him.", "NIGHT MANAGER With Ohio. He", "stabs a spot 400 miles", "away from John is finger. Point", "Pleasant is on the Ohio", "River. John is finger hesitantly tracks", "the tiny spidery lines from", "Richmond to Point Pleasant. There is", "not even a primary road", "between them. The room key", "clinks down on the glass.", "JOHN keeps staring at the", "map. It is not possible. MOTEL-", "LATE: As John walks from", "the office to his room,", "we spot the Prowler parked", "across the street: Connie is", "making notes under the interior", "light. JOHN is MOTEL ROOM- NIGHT", "The room is dark. John", "jerks awake, disoriented. He was", "asleep in his clothes on", "top of the bedspread, which", "he has cocooned around himself.", "He doesn`t know where he", "is for a moment. Then", "he HEARS it. Faint BREATHING,", "someone BREATHING, something BREATHING. He", "reaches for the bedside lamp", "and switches it on. The", "breathing stops. Silent now. Just", "a dull, empty, standard issue", "motel room. John swings his", "legs off the bed and", "leans forward on his knees.", "Bad dreams. He stands up.", "His cell phone is recharging", "on the chair. He heads", "for the bathroom for a", "glass of water. MOTEL BATHROOM",
"John turns on the tap,", "splashes water on his face,", "looks up, studies his reflection.", "ANGLE FROM BEHIND THE MIRROR.", "WE PULL BACK: John is image", "is framed by the mirror.", "The sound of the running", "tap fades as his image", "recedes, then disappears. FADE TO", "BLACK: OTTO is CAR REPAIR- POINT", "PLEASANT- AFTERNOON John is on", "his cell phone. In the", "background, OTTO is head is buried", "under the hood cof John is", "car. (into phone) Look- even", "if I was doing eighty", "the whole way it would", "have taken six hours to", "get here WITH ED FLEISCHMAN", "AT HOME: What time did", "you leave D.C? One o`clock.", "Besides, my gas tank doesn`t", "even get four hundred miles.", "It is one thing to zone", "out while you`re driving, but", "to stop for gas too?", "John, I`m sure there is a", "logical explanation for all this.", "Don`t worry about your pal", "the Governor, I`ll cover you.", "John sees Otto slam the", "hood on his car. Thanks,", "Ed. Uh, listen, I got", "to go. OTTO She is running", "fine, Mr. Klein, I can`t", "find a dam thing wrong", "with her. How much do", "I owe you? OTTO Nothing.", "I said I couldn`t find", "anything wrong. This town just", "keeps getting weirder and weirder", "John spots Gordon Smallwood and", "ANOTHER MAN loading produce onto", "a truck across the street.", "John tosses his weekender into", "the back seat and walks", "quickly towards Gordon. Gordon eyes", "John warily, arms folded across", "his chest. (smoothly) Mr. Smallwood,", "will you accept my apology?", "I`m afraid I might have", "been a little rude last", "night. Gordon nods stoically. Listen,", "you`re sure it was me", "who came by those last", "two nights, huh? Look, mister,", "Connie called me this morning,", "says you check out, and", "that is good enough for me.", "I`m perfectly willing to let", "it go. But I don`t", "drink anymore and I don`t", "lie and as far as", "I know, I`m not crazy,", "so if you`re accusing me", "John pulls Gordon aside. No,", "no, not at all. (confidentially)", "Here is the thing: I don`t", "know how ended up here", "last night. I didn`t even", "know I was in West", "Virginia. Somehow, between one and", "two-thirty last night travelled four", "hundred miles, ended up on", "that road, and I have", "no memory of it whatsoever.", "You shittin` me? No, I", "wish I was. Long awkward", "silence. John shakes Gordon is hand", "goodbye and walks back to", "his car. JOHN is CAR John", "pulls onto the road. As", "he circles past Gordon, their", "EYES LOCK for a moment.", "JOHN is CAR- HIGHWAY- DUSK John", "leaves town the way he", "came in. He passes a", "sign reading: `Point Pleasant City", "Limits` GORDON SMALLWOOD is HOUSE- NIGHT", "The distant farmhouse is dark", "except for a single bulb", "lighting the porch. No one", "is visible inside. A MAN is", "ARM RISES INTO THE FRAME", "His watch reads 2:15 a.m.", "WE PULL BACK AND SEE:", "John is face reflected in his", "car is rear view mirror. He is", "staking out Gordon is farmhouse from", "the the woods across the", "road. WOODS The high grass", "bends as SOMETHING moves menacingly", "towards the back of John is", "car. JOHN is CAR John cocks", "his ear. A rustling sound.", "Something is coming towards him,", "slowly, steadily. He scans around,", "it is dark, too dark to", "see. An SUV rolls quietly", "up beside him, headlights off.", "I was afraid I`d see", "you here. Just your odd", "reports, folks seeing things they", "can`t explain. So they all", "come to me. I`ve had", "a few odd moments of", "my own Since last night.", "If there is other people in", "town feeling as confused as", "I am right now, I`d", "sure like to know. There is", "something in his voice or", "his face or maybe both", "Okay. In the last few", "months people have come up", "to me and reported seeing", "strange things. And I`m not", "talking about the town speed-freak,", "I`m talking about honest, hard-working,", "church-going folks. I`ve known these", "people their whole lives, and", "they seem downright embarrassed to", "be bringing it up. Bringing", "what up? It is hard to", "explain. Try me. SMASH POLICE", "STATION- NIGHT Rows of florescent", "lights pop on, one after", "another. Connie locks the glass", "front doors behind her and", "John. A STACK OF REPORTS", "(LATER) spread across a desk.", "Photos, eyewitness reports, maps, phone", "records Weird lights, strange phone", "calls, ghosts, you. You name", "it. John flips through the", "reports. Seeing a UFO is", "cone thing. It is almost a", "status symbol nowadays. But what", "do you do when someone", "walks in and tells you", "this showed up in their", "backyard? She tosses a sketch", "across the desk. John picks", "it up. And almost passes", "out. It is a drawing, of", "a man, with huge bug", "eyes and giant wings in", "his back. JOHN is MEMORY: A", "yellow pad with Mary is identical", "sketch. ACCIDENT SITE IN D.C.", "THE LOW HANGING BRANCH with", "red lights for EYES flies", "rapidly towards us. Mary is drawing", "and the branch superimpose, and", "for a split second, John", "sees it: Mothman. BACK AT", "THE POLICE STATION: John is face", "is white. Who saw this?", "A couple of people. I", "want to meet them. I", "need to talk to them.", "Can you help me? Connie", "looks into his eyes. He is", "dead serious. LUCY GRIFFIN is HOUSE-", "DAY John waits in the", "patrol car while ON THE", "PORCH: Connie talks to LUCY", "GRIFFIN, (53}, a tough, chubby", "woman with bright red cheeks.", "After a moment, Connie looks", "back at John and nods.", "LUCY GRIFFIN is KITCHEN- LATER John", "and Connie drink coffee at", "the kitchen table with Lucy", "and her son NAT GRIFFIN,", "(24), a slacker living at", "home with his mom. It is", "been going on about a", "month, every Wednesday night at", "the poison plant. John looks", "a question at Connie: `Poison", "plant?` The hills around the", "Alanco chemical factory. It is a", "make-out spot. (smiles) Used to", "be. Now we just watch", "the lights. What lights? I", "don`t know, man, just these", "weird lights zipping around in", "the sky. What do you", "think they are? How would", "you describe them? Nobody knows,", "dude, the sky is just totally", "freaking out. Only Wednesdays? That is", "when they seem to show", "up. Lucy refills their coffee", "cups. Connie takes out the", "bird-man sketch: Lucy, would you", "mind telling Mr. Klein about", "the time you saw this?", "BACKYARD They all stand near", "a blue pine tree that", "towers over the yard and", "house: It was right here.", "There was only a foot", "or so between its head", "3 that branch so t`", "st makes it, what, e.--:t", "feet tall? was doing dishes", "and I just happened to", "look out the kitchen window.", "BACKYARD- NIGHT- FLASHBACK- MOTHMAN is POV", "We descend into the back", "yard, landing under the blue", "pine tree. We watch Lucy", "through the kitchen window. She", "stares back at-us, mesmerized. At", "first, all I could see", "were these two red eyes.", "I kept on looking at", "it, couldn`t stop. I`ve never", "had that feeling before, like", "I couldn`t move. The only", "way I can explain is", "that the whole thing just", "wasn`t right. I know that", "may not make sense, but", "that is the only way can", "put it into words. BACK", "TO SCENE (PRESENT TIME- LUCY is", "BACKYARD) CAMERA rises, peering down", "at Lucy, John, Connie and", "Nat: Then, I guess it", "saw me too, `cause all", "of a sudden these giant", "wings just flar out and", "it took off. (to Nat)", "Did you see it too?", "Nat shakes his head no.", "John is eyes fix on something.", "He walks over to the", "tree, reaches out his hand,", "touches it. He runs his", "fingers along the tree bark-", "a large section is scorched", "black. Mrs. Griffin, I don`t", "mean to pry, but. Have", "you had any headaches or", "blackouts or anything like that", "recently? (smiles! You think maybe", "I have a brain tumor?", "John is jaw almost hits the", "ground. Connie explains: Lucy is a", "radiologist out at St. Joseph is", "Hospital. No symptoms yet, Mr.", "Klein. But it was sweet", "of you to ask. FIRE", "STATION 51- LATE AFTERNOON FIRE", "CHIEF JOSH JESSUP, (55), a", "stout bulidog of a man", "sits with John and Connie", "in the fire station while,", "in the background, his men", "scrub the fire truck. I", "guess they started about two", "months ago, the strange phone", "calls. All hours. The first", "one was just a loud", "beeping noise. Now it is mostly", "creaking, howling sounds- and once,", "it was a man talking", "really fast in some foreign", "language. Maybe Swedish or something", "like that. Have you had", "the line checked? (nods) I", "even had our number changed.", "But before I got a", "chance to give it out", "to anyone the calls started", "again. John glances at Connie:", "is she buying this? PATROL", "CAR- DUSK John sits beside", "Connie, reading through her reports.", "So- do you think Lucy", "and Josh really experienced anything?", "(shrugs) Like I said, they`re", "honest people. John stares out", "the window: low blue hills", "roll out in all directions,", "lit by brilliant winter light.", "How long have you lived", "here? My whole life. Grew", "up just over those hills.", "AB farm? Shucks no. A", "real live house. Indoor plumbing", "and everything. Sorry. We even", "had shoes for church and", "schoolin` and such. (laughs) Alright,", "alright. Connie glances over: this", "is the first time he is", "smiled since they met. He", "looks five years younger- a", "whole different person. BACK ROAD-", "NIGHT The Prowler is headlights illuminate", "a rusted chain with a", "sign: `ROAD CLOSED- NO TRESPASSING`", "Connie climbs out, pulls the", "chain aside, drives through. DESERTED", "DIRT ROAD- LATER Connie points", "a flashlight as John and", "two teenagers, C.J.(17) and HOLLY(14),", "walk up the crest of", "a small hill. One of", "C.J. is eyes is slightly puffy", "with a blotch of red.", "C.J. We were parked up", "here and sort of making", "out in the back seat", "FLASHBACK- EXT. DESERTED DIRT ROAD-", "NIGHT It is a starlit, moonless", "night. C.J. and Holly are", "naked from head to toe,", "humping wildly in the back", "seat of his white Chevy", "Impala. From beneath their heavy", "panting, a low electronic hum", "rumbles. Flesh gropes. The rumble", "slowly rises. Suddenly, A SEARING", "RED LIGHT from fifty yards", "away floods the back seat.", "C.J. is head pops up to", "look through the rear window.", "The light blinds him, he", "covers his eyes. C.J. (cont`d)", "Oh, shit, it is the cops!", "But it is not a police", "car, it is a massive WALL", "OF SCORCHING. RED LIGHT hovering", "over the back window. CJ", "and Holly stare up, terrified.", "Holly tries to shield her", "face with her HANDS and", "BLOUSE. That is when she sees", "the Mothman: her hands form", "trembling wings, red light pours", "through button holes forming two", "demonic eyes. HOLLY Oh my", "god! Oh my god! Holly", "lets out a blood curdling", "scream. Suddenly, A LOUD WHOOSH.", "The light ascends into the", "night sky. BACK TO SCENE", "(EXT. DESERTED DIRT ROAD- NIGHT)", "Holly is shaking. C.J. holds", "her close. C.d. The next", "thing we know, it is gone.", "Just like that. We got", "the hell out of there.", "Next day, both my eyes", "nearly swelled shut. What did", "the doctor say? C.d. He", "couldn`t explain it. (pointing at", "his eye) This one never", "healed. How long ago was", "this? HOLLY About two months.", "They stand in the circle", "of Connie is flashlight. All we", "hear is the sound of", "four people breathing. Connie leans", "in close to Holly. (gently)", "Holly, I want you to", "show Mr. Klein what you", "showed me. Holly turns to", "C.J. He looks away. Blushing,", "she starts to unbutton her", "blouse. She turns around as", "she slips it off her", "shoulder. Connie brings up her", "flashlight. On Holly is back, a", "PAINFULLY SCORCHED PATCH OF BURNED", "RED SKIN. John steps closer", "to see it. Connie looks", "at John- do you have", "enough proof now? EXT. JOHN is", "MOTEL- NIGHT Neon lights, dead", "tree branches, phone wires. We", "hear John is INT. JOHN is MOTEL", "ROOM John is laptop is open", "as he talks to a", "clipping service: (into phone) I`m", "going to need everything you", "can find concerning unexplained events", "in West Virginia . weird", "lights, sightings, yeah, yeah, that", "kind of research. Go back", "about ten years, make it", "twenty. You have my e-mail-", "- Okay? Thanks. John hangs", "up and the cell phone", "immediately rings. Hello? INTERCUT WITH", "ED AT THE WASHINGTON POST:", "Where the hell are you?", "I`m still here. West Virginia?", "Yeah, well, something is come up.", "John opens his day-runner and", "removes. A WEATHERED OLD POLAROID:", "John and Mary on the", "beach in Hawaii. Their honeymoon.", "Both young, both smiling; blessedly", "ignorant of the future and", "happy for all time in", "that one split-second of life.", "You`re kidding, something of national", "erest in West Virginia? No.", "scientific. I`1l tell you about", "it later Cy is beginning", "to look rabid. Well, keep", "him at bay. By the", "way, I aced your buddy,", "the Governor- - but don`t", "worry, he still loves you.", "(preoccupied) Thanks. You okay? You", "sound stressed. I`m fine, Ed.", "I`1l call you. John hangs", "up, places the picture in", "the mirror frame over the", "desk where he can always", "see it. CONNIE PARKER is HOUSE-", "NIGHT John stands on the", "front porch in the light", "of a single bulb. He", "rings the doorbell. A YOUNG", "BOY opens the door. It is", "KEVIN PARKER, (7), Connie is son.", "He looks straight into John is", "eyes like he is known him", "forever. You`re John Klein. For", "a moment, John is speechless.", "Kevin then turns and shouts", "to the house: Mom, it is", "that guy. We hear Connie", "shout back from the kitchen:", "Well intro:- = yourself and", "invite him in. Kevin sticks", "out his hand: I`m Kevin", "Parker. (shakes his hand) Glad", "to meet you Kevin. CONNIE is", "KITCHEN TABLE- NIGHT John and", "Connie have finished their meal.", "Kevin plays in the next", "room. Connie, I want to", "thank you for helping me", "out today. You put yourself", "on the line for me.", "I appreciate it. I`m not", "going to end up reading", "about all this in the", "Washington Post, am I? This", "is not exactly the kind", "of thing we want to", "be known for. John looks", "up; she is smiling, but she is", "also dead serious. No. Believe", "me. It is not the kind", "of story we usually cover.", "They laugh together. Then, a", "beat of silence. John pours", "the last of a beer-", "- half into her glass,", "half o his. Say, do", "you have a video camera?", "She gives him a curious", "look: is this some kind", "of kinky come on? Yes.", "Why? HILLTOP PATH- NIGHT A", "cold, clear night. John and", "Connie follow Kevin up a", "wood path. Connie is camcorder hangs", "from John is neck. he lives", "up near Pittsburgh, does some", "contracting. Kevin sees him a", "couple times a year. Kevin", "must miss him. Yeah, he", "does. Kevin leads the way,", "just a few steps ahead", "of them. Connie does her", "best to make this next", "sound nonchalant: And you? Probably", "dating some pretty young congressional", "aide? Not exactly. (a deep", "breath, then:) I was married.", "and uh John searches for", "the right words. Kevin turns", "and stops. My wife died", "about a year ago. I`m", "sorry. (solemn) How did she", "die? She got really sick,", "Kevin. It was pretty unexpected.", "Kevin nods, understanding. I`m sorry.", "Kevin turns his head towards", "the hilltop, points his flashlight.", "People are gathered there. HILLTOP", "CLEARING- - NIGHT John and", "Connie follow Ke: 1 to", "the summit that overlooks the", "vast acreage of the Alancc", "chemical plant. ABOUT TWENTY PEOPLE:", "Kids, parents, teenagers, a few", "senior citizens mill about amiably.", "Somewhere, a car radio plays.", "There are no fires and", "no lights. John, Connie and", "Kevin wander among them. John", "sees Gordon Smallwood sitting alone", "on the hood of his", "truck. They nod hello. Hey,", "look who is here John turns.", "In the dark, it takes", "him a minute to recognize", "Nat Griffin and his mother,", "Lucy. (shivering excitedly) Getting cold,", "here, want some coffee? She", "hands John and Connie Styrofoam", "cups. In a week or", "so we`ll have to watch", "from our cars. Is it", "like this every Wednesday? Naw,", "this is the most people", "so far. Where`d Kevin go?", "He is over there. John and", "Connie wander off to the", "edge, looking out over the", "chemical plant. They watch Kevin", "playing nearby. Do you feel", "like talking about what happened", "to your wife? Mary died", "last year on Christmas Eve.", "She had a brain tumor.", "We didn`t know, but there", "was a car accident one", "night and they gave her", "an x-ray and found it.", "The might we had the", "accident she saw something, and", "she drew a picture of", "it. It was a giant", "bird-man with wings and red", "eyes. Oh my God. John", "turns to her and looks", "into her eyes: I`d pretty", "much forgotten that part of", "things until you showed me", "that sketch THE HILLTOP- AN", "HOUR LATER The group has", "dwindled to to about ten", "people. John and Connie sit", "on a blanket. Kevin is", "asleep at her side. Nat", "Griffin points his VIDEO CAMERA", "to the sky, just above", "the horizon: Look, there they", "are! Suddenly, excitement ripples through", "the remaining spectators. John and", "Connie stand up. The camcorder",
"lies forgotten at John is feet.", "RED AND BLUE PINPOINTS OF", "LIGHT hover in the sky.", "Big deal. It could be", "an airplane. THE LIGHTS collapse", "in on each other and", "plummet straight down into the", "river, disappearing at the horizon", "where the water meets the", "sky. More pairs of lights", "appear. Now some of them", "spin around each other, and", "the after image looks like", "a DNA spiral falling to", "the earth. John watches spellbound", "as the flickering lights spin", "and fall. The effect is", "hypnotic, eerie, dizzying, disorienting My", "God. WIDE SHOT: John and", "Connie face the camera. In", "the background, several other spectators", "look on. I_don`t see it,", "IT don`t see it, where", "is it? Right there, gold", "and purple lights. John squints.", "(whispering to Connie) Do you", "see it? Yeah, I see", "something. What do you see?", "I see spinning John unconsciously", "takes her hand Me too.", "What colors? Blue and red.", "How many lights? Thirty, maybe", "more. John and Connie stare", "up at the lights like", "kids at a magic show.", "Then John realizes that he is", "holding her hand. They glance", "at each other and John", "lets go, embarrassed. John is eyes", "shift down to the red", "and blue lights reflecting off", "the water. But for a", "moment, it almost seems as", "if the lights are coming", "from the water- - and", "shining into the sky. John", "steals a glance back at", "Connie- - she is still looking", "at him. And smiling. BOOKSTORE-", "DAY John steps up to", "the checkout counter. In his", "hands, a book: `THIN SEEN;", "A Rationale for Une ined", "Phenomena` by Albert Leek. ELECTRONICS", "STORE- DAY Through a window", "decorated with Christmas lights, we", "see a bank of TV", "MONITORS- - similar to the", "storefront we saw earlier when", "John was on CNN. INSIDE", "THE STORE: John is shopping", "for an answering machine. Suddenly,", "a hand lands on John is", "shoulder. It is Gordon Smallwood- -", "and he looks scared: Mr.", "Klein- - I gotta talk", "to you John backs up,", "wary of the fear in", "Gordon is face. (Confidential) Last night", "I woke up with the", "worst headache I`ve ever had", "in my life. So lI", "go to the bathroom to", "get some aspirin, and I", "happen to look in the", "mirror- - and I swear", "to God, I see something", "I can`t describe- - but", "sure as hell know it is", "not my reflection. John looks", "worried. Then it goes away.", "I can`t explain it, but", "I just keep staring, telling", "this thing. to show up", "again. But all I see", "is me. Gordon looks around", "nervously, making sure no one", "else can hear this. And", "then I hear this sound,", "like a weird howl coming", "out of the sink. Then", "it is a voice. and it is", "saying: `Do not be afraid.", "Ninety-nine will die. Denver Nine.`", "I even wrote it down", "Gordon pulls a wrinkled piece", "of paper from his shirt", "pocket and fiddles with it.", "It keeps saying the same", "thing, over and over, for", "an hour. Then it stops.", "Gordon peers at John, feeling", "sure John must think he is", "lost his mind- - but", "John encourages him to go", "on. And when I look", "in the mirror, I can`t", "see anything. Not even my", "own reflection. (pauses) Then this", "morning when I woke up,", "looked at the paper where", "I wrote down the words,", "and this was on it.", "He hands John the crumpled", "piece of paper. Beneath the", "words, `Do not be afraid.", "Ninety-nine will die. Denver Nine`", "is a sketch. It is crude,", "but unmistakable: A man with", "huge eves and wings. (pointing", "at sketch) You don`t remember", "seeing this thing? No, and", "it scares the shit out", "of me. John stares at", "the page, puzzled: Denver nine.", "Denver nine. Any idea what", "that means? Gordon shakes his", "head no. As they head", "out of the store, John is", "eyes narrow. John puts his", "hand on Gordon is shoulder. Gordon,", "your ear is bleeding. What? Gordon", "brings his hand to his", "ear. Blood is trickling along", "his jaw from inside his", "ear. Gordon gazes down at", "his hand. His stricken eyes", "dart to John. ESTAB. SHOT-", "ST. JOSEPH is HOSPITAL- DAY A", "professional-looking hospital, in a town", "clearly bigger than Point Pleasant.", "CAT-SCAN TAB- ST. JOSEPH is HOSPITAL-", "DAY DR. WILLIAMS sits with", "John, Gordon and Denise. Gordon", "is pitched forward, a bundle", "of nerves, fearful. The doctor", "points at: A LUMINOUS CAT", "SCAN of a human brain.", "DR. WILLIAMS The CAT is SCAN is", "clean, Gordon, no sign of", "any physical problem. Thank God.", "You`re sure? Because this man is", "wife, she had a brain", "tumor (softly) Gordon, please DR.", "WILLIAMS What you had is", "a first class migraine. can", "write you a prescription. Thank", "you doctor, that would be", "very kind of you Dr.", "Williams, there are other symptoms.", "He heard voices. There were", "visual hallucinations. Those symptoms are", "both associated with Glioblastoma Multiforma.", "DR. WILLIAMS They are also", "associated with migraines. There is", "nothing here to suggest something", "as exotic as Glioblastoma Multiforma.", "(to Gordon) If you`d like,", "I can refer you to", "another neurologist for a second", "opinion, but Gordon shakes his", "head, confused and dismayed. He", "grabs Denise and they all", "leave. (muttering) I don`t feel", "right. Something is wrong SILVER BRIDGE-", "DUSK FROM HIGH ABOVE: Gordon is", "pick-up truck streaks across the", "FOOT STEEL BRIDGE, an antique", "engineering marvel from the 20s", "that spans the Ohio River.", "GORDON is TRUCK/SILVER BRIDGE John watches", "Gordon and Denise: Gordon looks", "straight ahead, one hand on", "the wheel, the other clutching", "Denise is hand on the seat", "between them. She is putting up", "a good front, hiding her", "concern. John lets his eyes", "drift down to the Ohio", "River rushing beneath the bridge.", "COFFEE SHOP- NIGHT Gordon, Denise", "and John sit together at", "a booth. Gordon looks frightened.", "Hey- - it was good", "news, right? You`re not sick.", "That is good. I wish I", "was sick, then I would", "know why this is happening", "to me. Don`t say things", "like that, Gordon. I`m losing", "it. I`m hallucinating, hearing voices,", "my ear is bleeding. (to John)", "Was your wife hearing voices", "before her tumor? John doesn`t", "answer. His attention is riveted", "ACROSS THE ROOM. John stands.", "He moves deliberately across the", "diner. All the SOUND drains", "out of the room. John", "crosses the COUNTER and slowly", "raises his hand in the", "air. The blue glow of", "the TV tints his fingertips", "as he TURNS UP THE", "VOLUME. TV MONITOR- A NEWS", "UPDATE GRAPHIC NEWSMAN Again, our", "top story of the hour:", "Airwest flight number 9 out", "of Denver has crash BACK", "TO SCENE John turns and", "looks at the booth. Denise", "covers her mouth in horror.", "Gordon STANDS UP. His EYES", "LOCK with John is. There is", "no confirmation, but all ninety", "nine passengers and crew members", "are believed dead. JOHN is face", "freezes, SHOCKED- this can`t be", "true! One look at Gordon", "tells him that it is.", "JOHN is MOTEL ROOM- EVENING A", "phone cord stretches across a", "rumpled bedspread, shopping bags, books,", "notes, photos and other research", "items. (into phone) May I", "speak to Albert Leek, please?", "The voice that comes on", "the line is scratchy and", "distant. This is Leek. My", "name is John Klein and", "I`m working on a pretty", "strange story, and I thought", "you might be able to", "help me. Silence on the", "line. John is hand rests on", "LEEK is BOOK: `THINGS UNSEEN` I`ve", "been reading your book, and", "I`m especially interested in your", "theories about prophecies Anything you", "want to know is in", "the book. John presses on:", "Yeah, but this chapter on", "the entities you call (flips", "through book) Were you contacted,", "or are you pretending to", "have a professional interest? Yeah,", "no, I`m a reporter for", "the Washington Post. Well, I`m", "sorry Mr. Klein I don`t", "work in those areas anymore.", "I don`t understand The research", "didn`t prove viable. What do", "you mean? Click. Dial tone.", "ALANCO CHEMICAL PLANT- AFTERNOON Gordon", "hurries to his car in", "the plant is parking lot, looking", "awful. A FOREMAN stands in", "the background, looking at his", "watch and narrowing his eyes.", "GORDON SMALLWOOD is HOUSE- KITCHEN- DUSK", "Denise sits at the table,", "her face distraught. The sound", "of a clock echoes through", "the quiet house. Denise seems", "pretty worried about him. You", "hear a voice, that is one", "thing CONNIE PARKER is KITCHEN- NIGHT", "Connie watches John pacing back", "and forth, holding Leek is book.", "But this wasn`t just a", "message, it was a prediction", "that came true. Are these", "things Gordon is having hallucinations or", "dreams? The way he describes", "them, they sound like dreams,", "but. I don`t know. He", "believes they`re real. I had", "a dream last night. It", "felt real to me. Oh", "yeah? Connie is face darkens; just", "thinking about it scares her.", "He can see she wants", "to tell him. He waits.", "t was nighttime, and I", "was in the middle of", "the ocean, I was trying", "to swim. But I was", "too cold. I looked for", "something I could hold onto.", "There were Christmas presents floating", "all around me, wrapped up", "and tied with bows. tried", "to grab them but they", "kept popping away. Like corks.", "John listens intently as we", "SMASH THE NIGHTMARE ~ LIQUID", "SURFACE- HER POV Brightly wrapped", "gifts bob at eye-level against", "a steel gray sky Below,", "the water is glowing, pinpoints", "of light shine from the", "depths. And somehow I knew", "I was dying BACK TO", "SCENE: Connie continues Then I", "heard this loud voice- like", "someone shouting in my ear:", "`Wake up, Number 37!` And", "I woke up. (shivers) What", "do you think that means?", "Number 377? {pondering) Honestly, I", "have absolutely no idea. She", "snorts out a weak laugh.", "John smiles. John barely notices", "Kevin standing at his knee.", "(quietly) You wanna play? John", "looks at Connie: did Kevin", "hear all that? She shrugs,", "then: John follows Kevin into", "the den. WE STAY ON", "CONNIE: She watches them getting", "along like John is been around", "forever. A lot of strange", "things have turned up in", "Point Pleasant recently. On Connie is", "face, we can see that", "not all of them are", "bad POINT PLEASANT CHURCH- NEXT", "DAY The service is letting", "out, and CHURCHGOERS head down", "the front steps. Among them", "are Connie- - and John.", "He looks like he hasn`t", "been in a church in", "years. As they move away", "from the others, they notice", "DENISE standing nervously at the", "bottom of the steps. Denise?", "I don`t mean to bother", "you, Connie, but it is Gordon.", "As she steps closer to", "Connie, she can see that", "Denise has been crying. Are", "you okay? Denise avoids her", "eyes, carefully phrasing her next", "remark: I don`t understand what is", "happening to him. If word", "gets out that Gordon is, you", "know, `hearing voices`. I need", "someone to go talk to", "him. ( GORDON SMALLWOOD is FARM-", "NIGHT GORDON AND JOHN walk", "slowly around the farm. Gordon", "has a newspaper tucked under", "his arm. I met him.", "The guy who told me", "about the plane crash. You", "met him? John is stunned-", "but completely hooked: Yeah. Last", "night, just about midnight. was", "driving past the scrap yard", "by the unfinished highway GORDON is", "TRUCK- UNFINISHED HWY {GORDON is STORY)", "Gordon drives his pick-up truck", "along the narrow two-lane highway", "I`m driving along when all", "of a sudden this bright", "flash of lightning A HORIZONTAL", "SPIRAL OF LIGHTNING crosses in", "front of his truck. Gordon", "pulls off the road. UNFINISHED", "HWY- GORDON is TRUCK- STOPPED Gordon", "peers out through the passenger", "window, facing the CAMERA. BEHIND", "GORDON, we see AN OMINOUS", "VAGUE FIGURE approach the driver", "side window from across the", "road. It has the general", "characteristics of a MAN. THE", "FIGURE taps on the glass.", "GORDON spins around, gasps. THE", "MAN stares at Gordon. We", "can barely see the outline", "of a face. He looked", "human, but there was just", "Something weird about him and", "vour looking at them and", "Something doesn`t quite fit, but", "You can`t put your All", "of them, 1ike the pieces", "were right, bet they just", "didn`t go together Gorden Stares", "at the face, terrified. That is", "when it.The same high-pitched. Do", "not be afraid, My name", "is indrid Gordon is not afraid.", "He Slowly ROLLS Down THE", "WINDOW, and as INDBID COLD is", "FACE DISAPPEARS like a reflection.", "His Voice was Still there:", "`In a Place this size", "Ecuador, 399 will die in", "an BACK TO SCENE: (GORDON is", "FARMHOUSE- NIGET) John searches Gordon is", "face for a sign- -", "could this possibly be for", "real? Gordon, do you really", "believe this? You realize how", "all this sounds? Gordon gives", "him a `be patient` gesture,", "then smiles triumphantly and holds", "up the morning newspaper: EARTHQUAKE", "IN ECUADOR. 320 PEOPLE KILLED.", "Gordon seems possessed of complete", "self-assurance. And this scares John", "most of all. JOHN is MOTEL", "ROOM- TWO NIGHTS LATER Since", "we last saw it, the", "tiny motel room has been", "transformed into JOHN is INVESTIGATION HEADQUARTERS:", "A computer and fax machine", "crowd the small desk. Newspapers,", "books and faxes are stack", "everywhere. Maps cover the walls,", "studded with colored tacks: Red", "ones for odd light events,", "blue ones for giant bird-creature", "sightings, etc. In the middle", "of the chaos: JOHN AND", "CONNIE sit on the bed,", "sharing a pizza and a", "bottle of Merlot, watching: HILLTOP", "CLEARING- NIGHT A weak, grainy,", "image of the sky over", "the river and the chemical", "plant. They study the TV.", "John draws his finger across", "the screen. No trace of", "the lights. The lights should", "be right over here It is", "probably just too dark to", "record. I wonder. You ready", "for Josh is latest phone call?", "John lifts a cassette player", "and hits `play.` On the", "tape we hear Josh is VOICE", "as he answers his phone:", "(on tape) Hello?.. On the", "other end of the line", "we hear an ungodly racket:", "a creaking inhuman moan followed", "by a mechanical shriek. Connie", "shakes her head in wonder:", "This is definitely the weirdest", "date I`ve ever been on.", "Is this a date? Cut", "me some slack. It is after", "eleven ona weeknight and I`m", "in a motel room with", "a single man. I`m calling", "it a date. John gives", "her a curious smile, slightly", "lowering the volume on the", "shrieking wail. I think it", "actually sounds sort of beautiful,", "if you play it low.", "Kind of like a mating", "call. Yeah, I see what", "you mean, maybe this could", "be our song? John smiles.", "An awkward silence. He leans", "in and gently kisses her.", "She kisses him back. They", "pull back, both a little", "surprised at what is just happened.", "John is face makes it clear-", "- this is the first", "woman he is kissed since Mary", "died. Connie breaks the awkward", "silence: Look, if this is", "too soon, we could But", "before she can even finish,", "THE PHONE RINGS. John is", "relieved to answer. It is Gordon,", "and he sounds stressed: John,", "thank God you`re there. Gordon?", "(talking fast) Look, I know", "I sounded a little bit", "crazy today, but things have", "been getting weird out here.", "Gordon, slow down He is here.", "Who is? Mr. Cold. He is", "here. Right now. He is standing", "right next to me. John", "turns away from Connie and", "sits on the edge of", "the bed. The mood in", "the room has completely shifted:", "Let me talk to him.", "Sure. Hang on. Then he", "turns to Connie and covers", "the mouthpiece: Get over to", "Gordon is quick, he says Indrid", "Cold is there. Connie doesn`t", "ask questions, she just grabs", "her things and heads for", "the door. A thin, monotone", "VOICE- - supposedly that of", "Indrid Cold comes on the", "line: Hello John Klein. John", "tries to collect his thoughts.", "Who is this? My name", "is Indrid Cold. John quickly", "attaches his tape recorder- -", "an expensive digital device- -", "to his phone. Unless, of", "course, you`re Gordon Smallwood. Your", "father was born in Racine,", "Wisconsin. You lived in a", "green house on Monroe street.", "You can`t remember how your", "mother looked. John realizes he is", "sweating, his breathing has quickened.", "He makes himself breathe normally.", "Okay, you`ve got my attention.", "What color shirt am I", "wearing? Red shirt, three buttons.", "Correct. Hmm. John looks around", "the room. The curtains are", "open. He pulls them shut.", "He looks down and sees", "his watch in his shoe.", "Where is my watch? Shoe under", "bed. He looks around the", "room: Hidden cameras? An elaborate", "trick? He shuts off all", "the lights. Then plunges his", "hand into his overnight bag.", "What am I holding in", "my hand? Cream stick. John", "pulls his hand from his", "bag: he holds a small", "tube of Chapstick. Suddenly, an", "explanation occurs. (smiling) Indrid Cold-", "- are you reading my", "mind? I have no need", "to. (4) Okay. John grabs", "a paperback from the stack", "near his bed, but doesn`t", "open it. The real test:", "What` is the third line on", "page. fifty one? (instantly) Face", "unadorned held a naked promise", "that her figure did- -`", "w John flips on the", "lamp, opens the book, finds",
"the page, scans the line,", "takes a sharp breath. Correct.", "Still more proof John Klein?", "BACK ROAD- NIGHT Connie is speeding", "Prowler hugs the curves of", "a winding back road, blue", "and red gumballis flashing. JOHN is", "MOTEL ROOM- NIGHT John is", "still on the phone with", "Indrid Cold. His mood has", "shifted from skepticism to anxious", "fascination: What do you look", "like? Variable. I want to", "meet you. We already have.", "You frighten easily. You`re afraid", "right now. You seem to", "know a lot. Can you", "tell me something. What happened", "to my wife? A long", "silence. The silence makes John", "nervous. Why ask me what", "you already know? John takes", "a breath and closes his", "eyes: Where is Mary Klein", "right now? John grips the", "phone, his hand slick with", "sweat, waiting. The one who", "was Mary Klein cannot be", "found by looking. Contact is", "possible. See you in time.", "The line ERUPTS IN A", "HIGH PITCHED WHINE, a metallic", "WHISTLE, piercing John is ear. He", "flings the phone away. The", "receiver keeps SCREECHING on the", "floor. John stares at the", "phone. The SCREECH becomes a", "terrible CREAK, replaced by STATIC,", "then nothing, finally a dial", "tone. John forces himself to", "retrieve the receiver and place", "it back on the cradle.", "Sweat beads roll down into", "his eyes. GORDON SMALLWOOD is HOUSE-", "NIGHT Connie is hand pounds on", "the door. Nothing. She pounds", "again. Nothing. Finally, a light", "goes on inside and GORDON", "SMALLWOOD opens the door slowly.", "He is in boxer shorts, hair-mussed,", "fresh from bed. He squints", "out at Connie: What is up,", "Connie? Everything okay? (urgently) Did", "you just call John? (shakes", "his head) I`ve been asleep", "since nine. BELL SOUND LABS-", "CHARLESTON- DAY Establish a high-tech", "looking facility- clearly the non", "nonsense domain of science. BELL", "SOUND LABS- CHARLESTON- DAY John", "sits in the SOUND LAB", "surrounded by some of the", "most sophisticated sound analysis equipment", "in America. He listens to", "a tape of last night is", "phone call along with: SONNY", "BERGER, (45) a sound engineer-", "- good-natured, beard and at", "least 300 pounds. As Indrid", "Cold is VOICE comes on, Sonny", "points to the Voice Frequency", "Gauge: See? It is sticking up", "here around 1`cycles per second.", "The lowest it gets is", "maybe, 1930 or so Then", "John is VOICE comes on the", "tape. Yours is way down", "here in normal vocal range:", "anywhere from 1000 to 1200", "cycles per second. So this", "guy is vocal range is higher", "than mine? (laughs) ) You`re", "bullshitting me, right? How`d you", "do it? Do what? Create", "the voice. It is a good", "mimic, but comes on, 1900", "cycles per second? Groundhogs don`t", "go that high. So what", "the hell is it? As", "the tape plays, Sonny isolates", "Indrid Cold is voice and does", "a computer search for matches.", "Nothing. As near as I", "can figure, it is some sort", "of electrical impulse. But whatever", "it is, it isn`t coming", "out of human vocal cords.", "JOHN KLEIN is CAR- DUSK John", "drives back from Charleston, cell", "phone to his ear, leaving", "a message: (frayed) Cyrus, it is", "John, it is Wednesday night, I`m", "still stuck down here in", "west Virginia. HIGHWAY- TOLL BOOTH-", "DUSK John is car passes through", "a tollbooth, exiting the main", "highway. There is something eerie about", "it, but John is wrapped", "up in his call and", "doesn`t notice. I`m going to", "need a few more days", "to wrap things up. I`1l", "cali you in a couple", "of days. JOHN is MOTEL ROOM-", "NIGHT A cold wind howls", "outside. John lies in bed,", "tossing and turning in the", "dark LATER Now the lights", "are on. He sits on", "the edge of the bed,", "CHANNEL SURFING. He clicks off", "the Tv and throws down", "the remote MOMENTS LATER New", "he is dressed. He grabs his", "keys and he is out the", "door. GORDON SMALLWOOD is HOUSE- NIGHT", "John stands on the porch", "talking to Denise. She shakes", "her head, points towards town.", "JOHN is CAR- PARKING AREA/HILLTOP CLEARING-", "NIGHT John is car rolls to", "a Stop at the hilltop", "near the Alanco Chemical Plant.", "No other cars parked, no", "sign of Gordon. COPFEE SHOP-", "NIGHT John drives past- it is", "closed for the night. IRON", "HORSE TAVERN- NIGHT John drives", "by- - also closed. He", "drives on. THE SILVER BRIDGE-", "NIGHT John spots Gordon mid-span,", "leaning over the railing, staring", "down at the rushing water.", "He is not wearing a coat.", "John jumps out: Gordon! Gordon", "doesn`t seem to hear him.", "Gordon! John reaches Gordon is side,", "places his hand on his", "shoulder. Gordon stares out at", "nothing in particular. (calmly) I", "used to walk up here", "when I was a little", "kid. We`re right between West", "Virginia and Ohio. So technically,", "figure we`re not in either", "one Come on Gordon it is", "freezing out here, you want", "to warm up in my", "car? Gordon slowly shakes his", "head. Can`t. I`m waiting for", "him. John nods, waits for", "him to continue: John, everybody", "in this town is looking", "at me like I`m insane.", "You know why? John goes", "to answer, but Gordon continues:", "it is because I`m telling the", "truth. Denise, all the others,", "they don`t know, John. I", "do. What do you know,", "Gordon? I been lying awake", "at night- - feel like", "I`m sleeping, but I`m awake.", "That is when I hear him.", "When I hear his voice", "lately, I swear to God-", "- I feel better. John", "stares at Gordon, incredulous: Gordon", "looxs as sure of himself", "and resolute as when they", "first met. John takes a", "moment to gather his thoughts.", "I have an idea, but", "before I tell you what", "it is, I want you", "to know I don`t think", "you`re crazy. Okay? (calmly) I`m", "not crazy. I`ve heard about", "a program at the University.", "They work with people who`ve", "had strange experiences, and try", "to figure out if these", "events are occurring. outwardly or", "inwardly. You see what I", "mean? You think I`m imagining", "all this? I don`t know,", "Gordon. ROAD/UNFINISHED HIGHWAY- NIGHT John", "drives past the scrap yard", "next to the UNFINISH HIGHWAY", "where Gordon claims to have", "met Indrid Cold. As drives", "under the elevated RAMP TO", "NOWHERE CAR- NIGHT his face", "suddenly contorts with fear. He", "looks around terrified: what is happening", "to him? Sweat beads on", "his forehead, he tries to", "move his arms, his hands,", "his legs- - he is paralyzed", "with fear. The car continues", "to roll forward, increasing speed.", "Ten Feet. Twenty. Thirty! His", "fear escalates into panic. Every", "yard an eternity of unbearable", "terror. At fifty feet, he", "crosses an invisible border: his", "fear leaves as suddenly as", "it came. John stops the", "car. He catches his breath,", "gathering his nerve to investigate", "further. UNFINISHED HIGHWAY/SCRAP YARD john", "climbs ovt of his car,", "plants his feet on the", "asphalt, looks around. Nothing out", "of the ordinary. No sign", "of Indrid Cold or any", "other explanation- - what did", "he just pass through? He", "walks back towards the area,", "slowly, cautiously, trying to stay", "calm.` Then he takes one", "step too many. He is back", "inside the zone. The air", "becomes perfectly still as ALL", "AUDIBLE SOUNDS- - animals, birds,", "even insects- - are sucked", "into silence. The night becomes", "darker, too dark. John is breathing", "becomes shallow and fast. He", "falls to the ground, engulfed", "in fear. I couldn`t move.", "I don`t know what it", "was, I was more afraid", "than I`ve ever been in", "my whole life. I was", "just a few feet from", "where I came in, but", "it felt like I`d never", "make it back CONNIE is PROWL", "CAR ~ DAY Connie listens", "intently to John as she", "drives towards the unfinished highway.", "I`m on my knees, and", "I`m hyperventilating. I couldn`t stand", "up. It took me a", "half hour to crawl the", "five feet out of there.", "John looks at Connie. She", "doesn`t know what to say.", "This is it, up ahead.", "UNFINISHED HIGHWAY- DAY Connie slows", "the Prowler to a crawl,", "then brakes. They get out.", "It began here? Right over", "here, I think. John and", "Connie walk slowly. John tries", "to get his bearings. I", "remember the ramp was here.", "so this must be it.", "He starts to walk. Connie", "joins him and they step", "over an imaginary line together.", "They look at each other.", "She shakes her head. Nothing.", "Let is keep going. They walk", "further. Nothing. John stops, perplexed,", "shakes his head. (quietly) I", "swear, it was right here.", "Connie looks at him. She", "doesn`t feel it. But last", "night, John did. Connie looks", "inte his eyes, sees a", "man shaken from an experience", "he can`t explain. She takes", "his hand in hers. HIGH", "ANGLE They walk back and", "forth, back and forth, back", "and forth. Birds chirp, the", "winter sun is shining brightly,", "and we pull back on", "two people searching for an", "imaginary circle. ERVIEW CUBICLES- UNIVERSITY-", "DAY We INTERCUT between John", "and Gordon- - as each", "is interview by DR. LEE", "OKSTER, (37), friendly, and way", "too good looking to be", "in a lab coat: As", "a child, were you prone", "to seizures? No. Were you", "often left at home alone?", "No. Did you have a", "guardian angel or secret friend?", "No. Do you sometimes have", "trouble discerning dreams from actual", "memories? Gordon stares into the", "middle distance; he slowly nods.", "Mr. Smallwood? Gordon looks up,", "as if waking from a", "dream, as we go ELECTROENCEPHALOGRAM", "LAB- LATER again between John", "and Gordon with small electric", "patches taped to their faces", "and skulls. EEG printouts scroll", "out beside them. LABORATORY RESEARCH", "CENTER- LATER Dr. Okster shows", "John and Gordon their EEG", "printouts. Alright, this line here?", "It measures activity in the", "temporal lobe, the visual and", "perceptual center of the brain.", "Disorders in this area have", "been link to both alien", "encounters and near-death experiences. Disorders", "like a brain tumor? Maybe.", "But neither you nor Mr.", "Smallwood seem to have any", "temporal lobe abnormalities. So what", "does that leave? You might", "have been exposed to an", "electromagnetic field. Certain people are", "more sensitive than others to", "these EM waves, and it", "stimulates their temporal lobes causing", "vivid hallucinations. Gordon squints skeptically:", "Bright lights, voices, feelings of", "terror, distortions of time and", "even sightings of humanoid creatures.", "Gordon looks freaked, John doesn`t", "know what to think. JOHN", "KLEIN is MOTEL ROOM- NIGHT CLOSE", "ON A BOOK PAGE- -", "A 15TH CENTURY DRAWING OF", "A GIANT MAN WITH INSECT", "EYES AND WINGS. It looks", "like something from the Ars", "Moriendi- - `The Art of", "Dying`- - Medieval Christian texts", "on death. `The Nocturnal Butterfly.", "Also call Mothman. In ancient", "cultures, the moth represents a", "form of the psyche, or", "the soul immortally trapped in", "the hellish death realms. `", "Connie sits on the edge", "of a bed, wearing her", "uniform. She reads from a", "MYTHOLOGY BOOK. John is weekender lies", "open on the other bed:", "he is packing. It is late. A", "cold wind howls outside. (reading)", "`Mothman is one of the", "most obscure and frightening mythological", "creatures of the underworld. `", "Connie closes the book. She is", "read enough for tonight. She", "tosses it on the floor.", "John stops packing and looks", "at her. (softly) What time is", "your flight? Eight AM. Connie", "looks away, missing him already.", "NEW YORK STREET- DAY ON", "WEST 83RD STREET: John stands", "in a doorway across from", "the brownstone Criterion Apartments. He", "sees A MAN emerges from", "the apartment building: stocky, red", "face and crew-cut hair. Looks", "like a retired drill sergeant.", "John glances at the author", "photo on the book he", "holds, `Things Unseen`. Sure enough,", "it is the same guy: ALBERT", "LEEK. John dodges across the", "street and stops him. `Mr.", "Leek stops in his tracks,", "alarmed. John holds up one", "of the Bird-Man sketches: Do", "you know what this is?", "Leek is eyes flick from the", "sketch to John is face: Who", "the hell are you? John", "Klein. I called you. I", "need to know about this.", "Leek stares at him, considers-", "- then pushes quickly past:", "I`m sorry but I`m already", "late. John follows after him", "down the crowded street. Have", "you ever seen this thing?", "Leek keeps walking, trying to", "ignore the man following him.", "{shouting over street noise) I", "need your help, Mr. Leek?", "Leek finally stops and turns-", "- John almost runs into", "him. Where are they seeing", "him? (out of breath) Point", "Pleasant, West Virginia.. He stares", "at John, making up his", "mind: should he bother? Leek", "grabs the sketch from John", "and gazes at it. He", "looks like he is staring at", "his own obituary. (quiet, to", "himself) Mothman. John is eyes go", "wide. (a statement) You can", "help me, can`t you? Leek", "nods. Follow me. METROPOLIS BOOK", "SHOP- DAY John follows Leek", "into the giant, dusty old", "bookstore. Shelves tower overhead; stacks", "of books line the floor;", "aisles roll out in all", "directions, disappearing into murky darkness.", "ANGLE ON AISLE: John watches", "Leek scan the titles, his", "head tilted to the side.", "He plucks a book off", "the shelves: Ah, here we", "are Leek flips through a", "book and stops on A", "PAINTING OF A GIANT MAN", "WITH WINGS. It looks like", "something out of Greek mythology.", "John shudders. Mothman? That is what", "the Ukrainians called him. Rough", "translation, of course. There were", "a hundred sightings in Chernobyl", "the year the nuclear plant", "went down. Jesus. Leek turns", "to A GRAINY PHOTOGRAPH, the", "kind we`ve all seen: blurry,", "poorly framed- - but instead", "of a UFO, a Moth-like", "figure hovers in the sky.", "Galveston, 1969, just before the", "hurricane. They saw it. But", "seeing isn`t always believing. There is", "never been a single shred", "of evidence that any of", "these things exist materially; not", "for more than a short", "time, anyway. No one is ever", "seen any Bigfoot bod.es or", "crash UFO is. What about Roswell?", "Come on. You work in", "Washington. Is that braintrust capable", "of keeping that kind of", "secret? John rubs his eyes,", "confused: So you`re saying these", "things don`t exist? Sure they", "exist. There is all kinds of", "things that exist all around", "us that we never see,", "right? Electricity, microwaves, infra-red waves.", "You know, they`ve been around", "forever, they show up in", "cave paintings. They`re a normal", "condition of the planet, they`re", "just not part of our", "consensus of what constitutes physical", "reality. (frustrated) But what are", "they? Look, you`re asking for", "an explanation for something that", "can`t be explain rationally. But", "why do they show-up before", "all these disasters? Leek re-shelves", "the book, turns to John:", "You know all that build-up", "of energy before something happens?", "The way your hair stands", "up before lightning strikes? That is", "when they cross over What", "do you mean, before something", "happens? Do they cause disasters?", "Why would they need to?", "No, my theory is they", "foreshadow death and disaster. John", "lets that sink in, then:", "What do they want? I", "have no idea. What you", "really want to know is", "why you. Okay. Yeah. You", "think you`re special. Trust me,", "you`re not. You just got", "in their way. Got in", "their way? You noticed them.", "And they noticed that you", "noticed them. Most people aren`t", "sensitive enough to see them", "without some sort of trauma.", "John winces. Leek studies him,", "leans in. Are you fixated", "on death, Mr. Klein? John", "is silent. Leek nods knowingly,", "then walks down the aisle", "and out the door. John", "follows. NEW YORK STREET- DUSK", "Loud, crowded, and cold. John", "and Leek walk into the", "wind: JOHN Last week my", "friend got a strange phone", "call from a spirit or", "entity or whatever. It seemed", "to know. everything. Like God.", "(continuing for him) And it", "made predictions, and they came", "true (nods, excited) Yeah. He", "called himself Indrid Cold. John", "searches Leek is face for a", "sign of recognition; just shrugs.", "The name means nothing to", "him. If your `friend` thinks", "he is talking to God, he is", "off by more than a", "few degrees. But how could", "he know all this stuff?", "Leek stops walking. He looks", "around, trying to figure out", "a way to explain. Look", "up there John looks where", "Leek is pointing A SKYSCRAPER:", "Ten stories up, a window-washer", "squeegees the side of a", "glass building. If there was", "a car crash on Eighty", "Fourth and Riverside that window", "washer up there could probably", "see it. Doesn`t mean he is", "God- or even any smarter", "than we are. But from", "where he is sitting, he can", "see a little further down", "the road. But they`ve gotta", "be more advanced then us.", "Why don`t they just come", "right out and say what is", "on their minds? Leek nods.", "You`re more advanced than.a cockroach", "ever try explaining yourself to", "one? They continue down the", "street, each lost in their", "own thoughts. John breaks the", "silence: So, what about Point", "Pleasant? How many people have", "seen it? I don`t know,", "ten, maybe twenty? Leek stops", "dead in his tracks, looks", "John in the eyes. Don`t",
"go back there, Mr. Klein.", "What? LEER Listen to me,", "something terrible is going to", "happen in Point Pleasant. John", "is struck by the sudden", "fear that has clouded Leek is", "face. Nothing you do can", "stop it. Don`t go back,", "stay away, and stay away", "from me. I can`t talk", "about this any more. Leek", "starts to walk away, but", "John is desperation demands an answer.", "He pulls Leek violently by", "the arm. Look, Mr. Leek,", "there is got to be a", "reason I ended up in", "Point Pleasant something brought me", "there. Leek pulls away. If", "it brought you there, it", "brought you there to die.", "LEEK disappears down a dark", "staircase to the subway. NEW", "YORK STREETS- NIGHT John has", "put off his flight and", "spends hours walking circles around", "Times Square, wrestling with unanswered", "questions. NEW YORK STREET- MORNING", "ON WEST 83RD STREET: John", "waits outside the entrance to", "Leek is brownstone. When someone leaves,", "he rushes to the door", "before it locks, slips in", "the building. LEEK is APT BUILDING-", "HALLWAY- MORNING John knocks. Leek", "cracks the doc. open, displeased.", "Please, I need to talk", "to you. Leek stares coldly.", "I need to know what", "happened to you. Leek is face", "darkens, clouded by a bad", "memory. He lets the door", "swing open. LEEK is APARTMENT- MORNING", "John follows him down a", "long hallway into the KITCHEN.", "John nods. You didn`t sleep", "last night did you? No.", "Once they get to you,", "it is hard to sleep, isn`t", "it? You cross a line", "between what is real and what is", "not real Leek pours himself", "a cup of tea and", "sits down at a table", "across from John. I was", "a physics professor at Cornell,", "tenured, you can look me", "up. One day started hearing", "voices. The voices became messages.", "Before long, I was fully", "convinced that I was receiving", "predictions of disaster from `outside", "intelligences.` But you were, weren`t", "you? John, I_had tapes of", "their voices! But so what?", "Nobody cared. I knew a", "building was going to blow", "up, I tried to prevent", "it, but no one listened.", "What happened? Leek has turned", "fragile, shaking his head slowly,", "as if reliving the past.", "People died. Leek finishes his", "tea and goes into the", "LIVING ROOM, follows. I was", "investigated and almost arrested. My", "wife divorced me and my", "kids stop speaking to me.", "I spent four horrific years", "in a psychiatric facility. I", "lost everything. Oh my god.", "Leek has moved over to", "a mantle in front of", "a fireplace. Pictures of his", "past life loom behind him.", "Being right is worse than", "being wrong. If you`re wrong,", "you`re just a fool, if", "you`re right, you`re a suspect.", "Basically, it is a lose lose", "situation. Leek walks towards John,", "stands very close to him.", "John looks lost and exhausted.", "They fuck with guys like", "us, Mr. Klein. You`1l never", "understand their messages. You`ll misinterpret", "them. I did. It almost", "destroyed me You know what?", "In the end it all", "came down to one simple", "question: which was more important-", "- having proof? Or having", "a life? John tries hard", "to pull himself together. I`m", "scared. Good, when you stop", "being scared- - then it is", "time to worry. Trust me.", "I turn away years ago", "when I pitched all my", "notes into that fireplace and", "I`ve never looked back. But", "didn`t you want to know?", "Know what? The answers. Leek", "shakes his head, recognizing the", "persistence of the truth seeker", "in front of him. Leek", "extends his hand. Good luck,", "John. AIRPORT- COFFEE SHOP- DAY", "John and Ed sip coffee.", "Typical airport chaos surges all", "around them. Ed shakes his", "head, can`t believe what he is", "hearing. This doesn`t sound like", "you, John. This is the", "kind of stuff we used", "to rip on when it", "came over the wire. I", "know. It is different when it", "happens to you. You met", "someone, didn`t you? (frowning) No,", "no, nothing like that. Ed", "takes a long, assessing look", "at his friend. He knows", "not to push it. Do", "me a favor. Talk to", "Cyrus. Today. Tell him you`ve", "got the flu- - make", "up any excuse you want-", "- I`1l back you up.", "just want to be sure", "you still have a job", "up here, once you`re done", "doing, whatever it is you`re", "doing down there. Deal? {smiles)", "Deal. TOWN SQUARE- NIGHT About", "a hundred townsfolk have gathered", "for the annual Christmas tree", "lighting. John searches for Connie.", "He winds his way through", "the crowd, past Nat and", "Lucy, C.J. and Holly, Josh", "and the cranky Night Manager.", "Everyone seems a bit anxious:", "there is trouble getting the tree", "lit. He spots Connie and", "Denise standing in a storefront,", "sharing a cigarette. They both", "look upset. John hugs Connie,", "but she doesn`t hug him", "back. John is hurt. Denise", "smooths the awkward moment: (joking,", "to John) You`re just in", "time, they`re about to light", "the tree- - have been", "for the last two hours.", "Really? (surveying the crowd) Where is", "Gordon? Didn`t he come? Denise", "points. Gordon is standing off", "by himself. He looks broken,", "preoccupied, tense. John notices that", "Connie has wandered off. He", "catches up with her, gently", "takes her arm. What is wrong?", "(shortly) Nothing. Are you okay?", "Connie looks too upset to", "talk. Did you bring Kevin?", "She points to Kevin, who", "stands about ten feet away", "watching elves set up the", "lights. Kevin has his back", "to them. You look upset.", "What is going on? (irritated) Well,", "Gordon got himself fired from", "the chemical plant. That is terrible.", "Just kept talking about Indrid", "Cold, wouldn`t shut up. Fifteen", "more people reported seeing the", "`The Mothman` today. Fifteen. (she", "raises her voice) And three", "of them were cops. I", "hate this, John. I absolutely", "goddamn hate this. Kevin wanders", "up to them, tugs at", "his Mom is coat. John looks", "down: Qne of Kevin is eyes", "is swollen shut- - just", "like C.J. John masks his", "frightened reaction. Hi Kevin. (somberly)", "Hi John. Mom, can I", "go closer to the elves?", "Stay where I can see", "you. Kevin shuffles away. What", "happened to him? Connie looks", "away, choking back her anger.", "She won`t answer. What is wrong", "with his eye? She shakes", "her head. Did he see", "something? (explodes) I don`t know,", "John. He hasn`t eaten, he", "won`t go anywhere near his", "room. He won`t talk to", "me. Beat. Let me try.", "John walks over to Kevin", "for a private talk. Kevin", "seems more subdued than usual.", "How are you doing, pal?", "(shrugs) Okay, I guess. What", "happened to your eye? Kevin", "won`t answer. John reaches for", "Kevin is hands: he is trembling. John", "warms them in his, rubbing", "them together. Kevin stares at", "John. Is my mom going", "to die? John is taken", "aback, but he recovers: Of", "course not. Why? John looks", "tenderly at Kevin, but the", "conviction in his voice is", "undeniable: Nothing is going to", "happen to your mom, okay?", "Kevin looks at him; he", "wants to believe it, but", "I mean it Kevin. I", "promise. I`m going to make", "sure of it. Good enough.", "Kevin hugs John with the", "complete reassurance that only a", "seven year-old can truly feel.", "A row of lights goes", "on, sparks, immediately goes off.", "The crowd lets out a", "disappointed `ahhh`. yin moves ahead", "to see what is going on.", "John looks at Connie and", "nods- - it is okay. They", "circle the still dark tree", "in silence for a while,", "tracking Kevin. Then: Connie, whatever", "is happening here- - it", "has something to do with", "me. I was brought here.", "For a reason Connie stops,", "looks him in the eyes.", "These things are real. Indrid", "Cold is real. He is trying", "to show me something, tell", "me something. I don`t know", "what. John waits for her", "to react. All he sees", "on her face is concern.", "Whatever she is thinking, she doesn`t", "want to say it. Fifty", "feet above them, the grand", "old pine tree lights. They", "stare, speechless. It is beautiful, but", "at the same time, Maybe", "because of the events of", "the last months, a pall", "hangs over the town MOTEL-", "NIGHT A neon sign buzzes,", "electrical wires hum, WE MOVE", "along a phone line towards", "John is dark room. JOHN is MOTEL", "ROOM John turns over in", "bed, then wakes with a", "start. There is a rustling sound", "outside. Somebody is at the", "window. John climbs out of", "bed. The phone starts ringing.", "He ignores it. He goes", "to the window and draws", "back the curtains A BRIGHT", "ORANGE MOON glows on the", "horizon. Silhouetted against it: a", "leafless tree, bending in the", "wind. The tree turns toward", "John. Glowing red eyes shine", "at him. How could he", "have missed it? It is not", "a tree at all. It is", "Mothman. SNAP!- - a giant", "wing flares from the creature is", "back and- - CRACKS THE", "WINDOW. SMASH JOHN is MOTEL ROOM-", "NIGHT THE PHONE IS STILL", "RINGING: John bolts up in", "bed, covered in sweat, gasping", "from the nightmare. John catches", "his breath and looks at", "the clock: 4:00 a.m. exactly.", "He picks up the phone:", "Hello? John? It is Gordon. Gordon is", "VOICE is distant and staticky.", "Gordon? Where are you, I", "can barely hear (excited) Jeez,", "I can`t believe I got", "through. Listen, John: he was", "right. Mr .Cold was right", "about everything. John strains to", "hear; he shouts into the", "bad connection: Right about what?", "It is beautiful, John. I want", "you to know that. It", "truly is. You`ve got nothing", "to worry about. What is beautiful,", "Gordon? What are you talking", "about? Gordon is VOICE grows fainter.", "GORDON S I gotta go.", "Goodbye, John. Thanks for everything.", "I`ll see you in time.", "A huge flare of static", "and then silence. No dial", "tone, no click. Just silence.", "GORDON SMALLWOOD is HOUSE- NIGHT John", "slams into Gordon Smallwood is driveway", "just behind Connie is cruiser and", "a paramedic van. John RUSHES", "toward the barn as Connie", "emerges with Denise, who is crying", "hysterically. John is stopped short:", "Denise gle es at him", "with hatred in her eyes:", "(under her breath) It is your", "fault. you encouraged him Denise", "climbs the porch and disappears", "into the house. Connie takes", "John is arm as they walk", "back towards the barn. You", "know she doesn`t mean it.", "John nods. Did she see", "it? No, she was asleep", "when she heard the shot.", "Gordon wasn`t in bed. She", "came out here and found", "him in back with his", "shotgun. Jesus. Do you know", "what time it happened? Connie", "wipes her eyes and checks", "her notebook: Around 4 A.M.", "John flinches, like he is just", "been struck. I can`t stand", "this, John. I feel like", "in town is losing their", "mind. John knows exactly what", "she means. IRON HORSE TAVERN-", "LATE AFTERNOON John is wearing", "a black suit, hunched over", "a glass of scotch. A", "couple people dressed in mourning", "clothes are gathered at the", "other end of the bar.", "A hand gently squeezes John is", "shoulder, he turns to find", "Fire Chief Josh Jessup: Tt", "didn`t seem right to bring", "this up at the funeral-", "- but as far as", "I know, there is never been", "any accidents at the chemical", "plant. What are you talking", "about? I got your message", "yesterday, I meant to call", "you back, but it slipped", "my mind. John gives him", "a blank look. Don`t you", "remember leaving me that message?", "I wasn`t here yesterday, I", "was in New York. BELL", "SOUND LABS- CHARLESTON- DAY Once", "again, John sits with Sonny", "Berger. A PHONE MESSAGE CASSETTE", "spins in a high tech", "deck. It sounds exactly like", "JOHN is VOICE. ANSWERING MACHINE VOICE", "Hi Josh, this is John", "Klein. Have there ever been", "any accidents at the chemical", "plant? Thank you in advance.", "Sonny hits istop.` I never", "made that call. It sounds", "like me, but come on,", "`Thank you in advance`? I", "don`t talk like that. No", "one does. Well, it sure", "sounds like you. John looks", "at the Voice Frequency Gauge:", "It is well within human range-", "- about 1100 cycles per", "minute. There is no way this", "could be an electrical impulse", "like the last one? Doubtful.", "Watch Sonny isolates John is VOICE", "on the previous tape and", "on Josh is tape. He runs", "them on adjacent monitors: the", "gauges respond identically. This is", "what we call a voiceprint.", "The `best computer mimic in", "the world can`t get more", "than a 75% match. These", "two are at 99.7% If", "I had to, I`d swear", "ina court of law that", "both of these voices are", "yours. STREET- POINT PLEASANT- DUSK", "It is just getting dark as", "John pulls into town, parks", "in front of the POLICE", "STATION. John bounds up the", "station steps, his mind still", "reeling from the events at", "the sound lab. He stops", "abruptly and walks back down", "the steps, turns left. A", "WOMAN WITH RED HAIR climbs", "the steps from the right.", "just misses seeing her, but", "we do: she is a dead-ringer", "for Mary. We follow John", "to a STREET/CORNER SHOP- POINT", "PLEASANT- DAY where he buys", "coffee. POLICE STATION- LATER John", "climbs the steps. As he", "passes through the glass front", "decors, THE WOMAN WALKS RIGHT", "PAST HIM. Once again, John", "misses seeing her. Is that", "Mary? Now we`re not sure.", "Something looks different. POLICE STATION-", "NIGHT John approaches Connie at", "her desk. But before he", "can say a word, she", "jumps up: Do you know", "that woman? What woman? The", "one that just walked past", "you as you came in", "He and Connie go to", "the glass doors; they look", "around the street, but no", "one is anywhere to be", "seen. That is so odd.", "She had long red-hair and", "green eyes. Real pretty. And", "she was asking about you", "John whips his head around", "to look at Connie as", "we BEACH- DAY JOHN is MEMORY", "POV: The beach. Hawaii. Mary", "laughs, her red hair and", "green eyes shining in the", "sun FLASHCUT BACK TO: POLICE", "STATION- NIGHT as John dashes", "outside, searching the street for", "the Woman. Connie follows him", "OUTSIDE THE POLICE STATION: John", "heads across the street to", "the town square, turning around", "as he walks, his eyes", "everywhere, scanning in all directions:", "What did she say? Connie", "struggles to keep up with", "him: All these strange questions:", "What are you writing about?", "Do you believe in Prophecy?", "What would your reaction be", "if I asked you to", "stop investigating Indrid Cold- -", "if I said it was", "for your own good? What", "did you say? I asked", "for some I.D.- - will", "you slow down please? Did", "she give you any? No.", "She just said, `Tell John", "I`m sorry for ruining everything.`", "And then she got up", "and walked out. John stops", "in his tracks and whips", "around, staring at Connie. What is", "wrong? For a moment John", "can`t speak. Then he claws", "his wallet out of his", "coat, flips it open and", "thrusts it at Connie: Was", "it her? Connie focuses on", "a wallet-size snapshot of Mary", "Klein- - and her eyes", "go wide in shock: This", "is the woman. Was it", "her? (barely audible) That is your", "wife? {on fire) It was", "her. Connie slowly shakes her", "head. This is impossible And", "just that fast, doubt forms", "in her eyes. I`m not", "sure. What? I mean- -", "the hair is different, and", "John stares at her in", "disbelief: she is convincing herself that", "it wasn`t Mary she saw.", "Oh, come on. (defensive) What?", "You saw her! This is", "the woman you saw! The", "crazed look on John is face", "scares the hell out of", "her. There is no way she is", "buying into all this. No", "it isn`t, John. I agree,", "there is a. a Similarity,", "maybe, but Bullshit! This hits", "Connie like a slap in", "the face. She struggles to", "maintain her calm. John- -", "tell me you`re okay. I`m", "not okay. You saw her.", "You know you did. (pleading)", "Don`t do this to me.", "Not you. Please John- -", "tell me you`re okay. John", "shakes his head; he is never", "felt more alone in his", "life. I gotta go. John", "storms off, leaving Connie standing", "in the middle of the", "town square. MOTEL- FRONT DESK-", "NIGHT John approaches the Clerk:", "Any calls to room 124", "today? The clerk checks the", "electronic switchboard: CLERK No sir.", "JOHN is MOTEL ROOM- NIGHT John", "enters. The PHONE RINGS. He", "looks at ti.: answering machine:", "the number `9` flashes insistently.", "John slowly approaches the phone", "and picks it up AN", "EAR-SHATTERING BEEP- - he slams", "the receiver down. John hits", "the `playback` on his answering", "machine. It rewinds and plays:", "More BEEPING. Odd electronic MUSIC.", "A strange, high-pitched rhythmic MURMUR", "What the hell does all", "this mean? The PHONE RINGS.", "He hesitates, then picks it", "up. It is a high pitched", "CHORUS OF VOICES. Are you", "John Klein? Yes. Mr. Klein", "And now the VOICES slow", "down and deepen: isorry. I.", "ruined. everything John slams down", "the phone. He is shaking now,", "in a full sweat. But", "now he has an idea", "He removes the tape from", "the answering machine and inserts", "it o his PORTABLE MINI-RECORDER.", "He cues the tape to", "the HIGH-PITCHED RHYTHMIC MURMUR message,", "then plays it through three", "or four times, listening closely", "for words, voices- - anything.", "Nothing. Just the odd murmuring", "noise. The PHONE RINGS. John", "lets it ring, waiting for", "the machine to pick up-",
"- then realizes he is removed", "the tape. Damn. He really", "doesn`t want to answer the", "phone But he has to.", "He reaches for it, his", "hand literally shaking. He picks", "it up. It is the high", "pitched CHORUS OF VOICES again:", "Sorry I ruined everything. Sorry", "ruined everything. Sorry I ruin", "everything He slams down the", "phone. It immediately rings again.", "He backs away MOTEL- FRONT", "DESK- NIGHT John staggers to", "the desk, his body rigid", "with fear. I have to", "leave for awhile. I`m not", "sure when I`ll be back,", "but until I am could", "you please make sure no", "one goes into my room?", "Not the maid- - not", "anyone. The clerk looks at", "John and doesn`t like what", "he sees. CLERK Are you", "okay, Mr. Klein? (nods) I", "just need to get away", "from here. And with that,", "John leaves. JOHN is CAR- NIGHT", "John drives like a bat", "out of hell, gripping the", "wheel tightly. He passes a", "sign: `Welcome to Kentucky.` JOHN is", "CAR- LATER Still forging ahead", "aimlessly into the night. John", "fights exhaustion and paranoia. Another", "sign whizzes past: `You are", "leaving Kentucky- - Welcome to", "Indiana.` TRAVEL LODGE- CLARION, INDIANA-", "NIGHT Three hours and 269", "miles away from Point Pleasant.", "pulls into the motel driveway", "and parks. He climbs out", "of his car, stiff and", "sore, unable to drive another", "foot. He wanders exhausted into", "TRAVEL LODGE LOBBY ~ SAME", "John approaches the YOUNG WOMAN", "at the counter. I just", "need. whatever you`ve got. No", "problem. He hands her his", "credit card. The woman runs", "his card, then freezes. She", "looks up at him: You`re", "John Klein? John is eyes snap", "open. The woman laughs, incredulous:", "Oh my god She pulls", "out a thick stack of", "pink message slips. We`ve been", "getting these for the past", "two days. This can`t be", "happening. John takes the stack", "of messages, hands trembling They", "all say the same thing:", "`Call me. Urgent. Mary Klein.`", "PHONE BOOTH- TRAVEL LODGE PARKING", "LOT- NIGHT John grips the", "receiver, hovering on the edge", "of panic. He fumbles Albert", "Leek is business card out of", "his pocket and dials the", "number: (exasperated) What! (taken aback)", "Mr. Leek? It is John, For", "God sake, stop calling me!", "I told you What? (shouting)", "I told you I don`t", "know Indrid Cold Oh my", "God And I don`t want", "to! I never call you.", "Silence on the line. They", "both know who is been calling.", "I told you, I got", "outta this shit years ago,", "and I don`t want to", "go back. Leek hangs up.", "JOHN is MOTEL ROOM- POINT PLEASANT-", "NIGHT It is still dark out.", "John enters like he is walking", "into a snake pit. He", "quickly knocks the phone off", "the hook before it has", "a chance to ring. He", "looks around the room Mothman", "drawings, photos, maps, charts, books", "it looks like a lunatic is", "office, John comes to a", "decision: there is only one thing", "to do. John opens a", "box of 40-gallon trash bags.", "He shakes one open and", "slowly begins stuffing it. He", "starts with the photos, next", "go the note cards. Then", "the maps and charts. He", "begins moving faster and faster.", "He furiously jams faxes and", "articles into the bag. Finally,", "he rips THE MOTHMAN DRAWINGS", "off the wall and tears", "them up. MOTEL- NIGHT John", "throws a huge armload of", "trash in a dumpster behind", "the motel. JOHN is MOTEL ROOM-", "NIGHT Riding the momentum, picking", "up steam, John storms through", "the room, dismantling the headquarters", "with glee. Suddenly, a POLAROID", "PICTURE falls from the wall", "onto the desk in front", "of him. He freezes- -", "then relaxes: it is the one", "of him and Mary in", "Hawaii. He smiles. Finally, it", "seems like it might be", "okay. He stares at this", "island of normalcy in a", "room packed with madness. Then", "his smile dies and his", "eyes fill with growing dread.", "AS WE MOVE AROUND BEHIND", "HIM we see why: JOHN is.", "OV- THE POLAROID: In the", "upper corner, above John is shoulder:,", "we see something in the", "sky that has never been", "in the picture before A", "tiny, bird-like figure with two", "red, glowing eyes. John sinks", "to the floor, gripping the", "picture. Even here in his", "memories- - he isn`t safe.", "(whispering) No, no, no JOHN is", "MOTEL ROOM- NIGHT FADE UP", "on the same shot. Later.", "John sits in the dark", "room clutching his portable mini", "recorder and playing the HIGH-PITCHED", "MURMUR over and over JOHN is", "MOTEL ROOM- MORNING John hasn`t", "moved. Dawn is breaking. John", "sits, half-asleep, the recorder still", "playing in his hands. But", "the batteries are dying and", "the tape plays slowly, the", "hi-pitched murmur now sounding like.", "a voice? THE PHONE RINGS.", "Startled, John jolts awake and", "grabs the receiver: What? It is", "Cyrus Bills, John is editor from", "the Post: It is me, Cyrus", "John tries to clear his", "head- - what is that", "weird noise? He realizes his", "mini-recorder is still playing John?", "Is that you? (distracted) Yeah,", "yeah, it is me Look, John,", "I didn`t mean to call", "so early, but it is the", "only way I knew I`d", "catch you. John! You there?", "Yeah, yeah, what? This is", "important. Governor McCallum is touring", "the Alanco Chemical Plant today,", "right there where you are.", "I need you there. Are", "you listening? McCallum, at the", "chemical plant. Got it. But", "John is attention is now riveted", "to his tape player: as", "the batteries continue to run", "down, the sound of a", "VOICE becomes even clearer John", "holds the recorder to his", "free ear, straining to hear.", "i need to know you`re", "going to be there John", "pays no attention, the VOICE", "becomes clearer every second John?", "John sets the receiver down", "on the ground, forgetting about", "it completely, and turns the", "volume of the mini-recorder all", "the way up to `10`,", "The VOICE on the tape-", "- now a low-pitched drone-", "- is perfectly clear and", "very familiar: It is Indriad", "Cold. (on tape) Great tragedy", "on River-Ohio. Great tragedy on", "River-Ohio. Great tragedy on River-Ohio", "We hear Cyrus shouting to", "John through the discarded phone", "receiver, but John listens to", "the tape, transfixed. CONNIE PARKER is", "HOUSE- MORNING John follows Connie", "around as she gets ready", "for work. John, I can`t", "just call in sick because", "you have a bad feeling", "about today. Think about it:", "the weird lights in the", "sky show up over the", "chemical plant. Josh got a", "call from me- - which", "wasn`t from me- where I", "talked about something: bad happening", "at the chemical plant. Th.-", "got a call from Indrid", "Cold talking about a tragedy", "on the Ohio River, and", "guess what is on the Ohio", "River? The chemical plant. And", "today Governor McCallum is going", "to be there. I was", "on my way to interview", "him last week when I", "ended up here in the", "first place. All the pieces", "fit- - it explains everything!", "Connie stops getting ready and", "turns to him: What about", "Josh is phone calls? Or Holly is", "burns? How does all that", "fit in? I don`t know", "exactly. It must all be", "part of the warning. (incredulous)", "What? How many sightings of", "Mothman have you logged down", "at the station? (distracted) I", "don`t know. thirty, forty God-damnit!", "Come on- something terrible is going", "to happen- - we have", "to leave town. Now. Connie", "turns to him, angry and", "scared: No! John. I can`t", "live my life that way.", "I can`t make decisions based", "on messages from Indrid Cold.", "He is real to you. He is", "not real to me. (pleading)", "Then at least get yourself", "re-assign off the security detail.", "I don`t want you anywhere", "near that place today. Connie", "sees the intensity on John is", "face; it is terrifying. What if", "nothing happens? John looks at", "her strangely- - this never", "occurred to him. What? What", "if there is no `great tragedy`", "today? What will you do?", "I. don`t know, I haven`t", "thought that far, that is not", "the point Yeah, I think", "it is the point. Gordon", "believed what he heard too", "John grabs her, enraged: Fuck", "Gordon! I`m not Gordon!! That", "wasn`t my fault. I tried", "to help him, but he", "wouldn`t listen to me! I`m", "not going to let the", "same thing happen to you!", "Connie stares at him. The", "look on her face makes", "John take his hands off", "her. Connie turns, grabs her", "gun and purse, doesn`t even", "look at John: You need", "to leave. If you want", "to talk about this later,", "my shift ends at six.", "CHARLESTON AIRPORT- DAY A crush", "of reporters, photographers, and car.ra", "operators traipse along with Virginia", "Governor Rob McCcilum, his aides,", "his official West Virginia greeters.", "John falls in smoothly, steers", "through the aides with the", "magic words, `Washington Post`, and", "moves up alongside the Governor.", "Good afternoon, Governor. Hi, John.", "I missed you in Richmond.", "I need to speak with", "you, it is urgent. John is intensity", "registers. McCallum is smile wrinkles into", "concern. He lowers his voice.", "What is this about, John? It is", "the tour. You can`t do", "it, you can`t go. The", "plant is at risk. An AIDE", "is close enough to overhear.", "(in a murmur) ah, shit.", "McCallum is pace falters. You need", "to get the place shut", "down for a safety inspection.", "They`ve done two checks already,", "Governor. McCallum nods at the", "aide. John fights down his", "desperation. The plant is going to", "blow up while you`re there.", "McCallum has enough sense to", "keep his voice low. There is", "a bomb?? ~~ How do", "you know are you sure??", "The aide whips out a", "cell phone, and fishes his", "cheat sheet of phone numbers", "from his jacket. John glances", "over at him. (just heard,", "background) State Police? People are", "going to die if you", "don`t listen to me, Rob.", "(faintly, background) this is Governor", "McCallum is chief aide, we`re at", "the airport McCallum is gaze jerks", "around the airport. Where are", "the cops? What are they", "doing? John sucks in a", "breath. They don`t know yet.", "(startled) What? (background) have you", "received a bomb threat? I", "never said it was a", "bomb- - it is something. You", "have to believe me. And", "right that minute, with his", "messy hair and his circle", "eyes, he is no longer believable.", "McCallum is gaze settles on him.", "The aide is voice is stronger.", "No?. Nothing?. Just a last", "minute security check. We`re ready", "to roll, thanks. He flips", "the phone shut, shakes his", "head at the governor. {to", "McCallum) Hundreds of people could", "die- - and you`re one", "of them. {to the aide)", "So are you McCallum blows", "out a sad breath. You`re", "messing up here, John. I", "have information. You didn`t call", "the police. My source is", "psychic. The aide is so", "relieved he sniggers. Look, I", "know how this sounds- -", "but many of this person is", "predictions have come true; the", "plane crash in Denver last", "week, the earthquake in Ecuador.", "McCallum and the Aide exchange", "concerned glances. I appreciate your", "concern John stands and makes", "a final dramatic plea: That is", "not good enough. Cancel the", "tour. Insist that the plant", "be shut down immediately. You`ll", "be a hero. Please Governor.", "Something terrible is going to", "happen. I know it. CHARLESTON", "AIRPORT- DAY They have reached", "the Governor is limo. McCallum squeezes", "his eyes shut, drops his", "chin to his chest for", "an instant, looks up at", "John again. Here is what I`m", "going to do. John is shoulders", "relax; he is convinced him. I`m", "going to go meet Governor", "Harris at the State House.", "I`m going to drink lukewarm", "coffee from a good china", "cup and not spill the", "crumbs from the cookies they`ve", "baked. and then we`re going", "to ride in a limousine", "out to the chemical plant", "and shake hands with every", "willing man and woman there.", "because I don`t end to", "end up as a front", "page joke! We follow McCallum", "into his LIMOUSINE (into car", "phone) Get Cyrus Bills on", "the phone for me. As", "the limo speeds away, John", "disappears through the back window.", "LOBBY BAR- CHARLESTON HYATT- 12:30", "P.M. John enters and sits", "at the bar. A BASKETBALL", "GAME plays on the TV,", "and stock quotes scroll across", "an LED SCREEN underneath. {to", "Bartender) Scotch, no ice. You", "mind turning on the news?", "The Bartender looks up at", "the game then back to", "John. He reluctantly turns the", "channel. LOBBY BAR- LATER And", "a few scotches down. ON", "TV, the LOCAL NEWSCAST features", "a report on car adoptions.", "BARTENDER Hey, can I at", "least check the score? John", "pushes his empty glass at", "the bartender. No. And do", "me again. LOBBY BAR- 5:00", "P.M. It is dark row. The", "bar is empty. John is", "drunk. He stares at the", "TV as `ne EVENING NEWS", "comes on. {to the Bartender)", "Turn it up The Bartender", "glares at John. John smiles", "politely and hands him twenty:", "You`ve been very kind. I", "plan to write a glowing", "letter to Mr. Hyatt as", "soon as I`m sober. Now", "would you please turn up", "the goddamn volume. The Bartender", "takes the twenty and turns", "up the volume. CLOSE ON", "TV NEWS ANCHOR {on TV)", "Our top story tonight takes", "us to Point Pleasant where", "Virginia Governor Robert McCallum joined", "Governor Harris and representatives from", "the state is Environmental Regulatory Panel", "to tour the Alanco Petrochemical", "Plant. Tory Pherris is on", "location in Point Pleasant. Tory?", "ALANCO CHEMICAL PLANT- NIGHT The", "NEWS BROADCAST goes live to", "Tory Pherris: (on TV) In", "what he is called a isuccessful", "review of Alanco is recent emissions", "reduction overhaul,` Governor McCallum gave", "high marks to the petrochemical", "plant, and he is expected", "to call for similar renovations", "at several Virginia plants. His", "tour began today at JOHN", "has already stopped listening. His", "attention is rivet to the", "visual of the CHEMICAL PLANT", "ON TV in the background:", "No sign of explosion, mayhem", "or death. Time to face", "facts: He was wrong. Nothing", "happened. He doesn`t know whether", "to be relieved or disappointed.", "Son of a bitch. John", "stares down at his scotch.", "RED LETTERS reflect off the", "surface of his drink. He", "slowly looks up at the", "LED SCREEN. It is flashing: SHE", "WILL CALL- SHE WILL CALL.", "John turns to the Bartender", "who has been standing nearby.", "Did you see that? The", "Bartender glares at him and", "shakes his head. A BELLHOP", "appears at John is side. Excuse", "me, Mr. Klein? Yeah? You", "have a message. The Bellhop", "hands John a folded slip", "of paper. John opens it,", "reads: GEORGETOWN. FRIDAY. NOON. John", "looks up. The Bellhop is", "gone. JOHN is ROOM- HYATT HOTEL-", "NIGHT John is luggage is on", "the bed; as he packs", "the last of his things", "there is a knock at the", "door. He opens it. It is", "Connie. She walks in, sees", "the bags: You`re leaving. Back", "to Washington? Yup. You don`t", "have to go, John. Yeah.", "I do. If it is about", "today No, no, I just", "got a message John holds", "up THE NOTE. I have", "to get back to my", "apartment in Georgetown. Connie notices", "something odd about John: he", "doesn`t seem upset at all.", "In fact, he looks more", "confident than ever. Indrid Cold", "says I`1l be contacted on", "Friday at noon. Connie can`t", "believe what she is hearing. She", "watches as moves about the", "room with robot-like intensity. Do", "you have any idea what is", "happened to you, John? What", "you`ve allowed to happen? I", "didn`t allow anything. He tries", "to hand her the NOTE;", "she pushes it away, won`t", "even look at it. CAMERA", "slowly moves in on the", "note. we see that nothing", "is written on it. (desperate)", "Don`t leave. For God is sake,", "stop following his orders! I", "know you won`t be able", "to understand this, but I", "can`t. Connie stands in front", "of him, talking as slow", "and deliberate as a hostage", "negotiator. Please John. Don`t do", "this. John doesn`t even look", "at her as he steps", "around her, grabs his bags", "and leaves. On the SOUND", "of a CHOIR singing isILENT", "NIGHT`, we FADE TO: JOHN is", "APARTMENT- WASHINGTON D.C.- NIGHT John", "staggers in and drops his", "bags. He flops onto his", "b fully dressed, not even", "removing his overcoat POINT PLEASANT", "TOWN SQUARE- DAY Snow falls", "on the glowing Christmas Tree", "POINT PLEASANT ELEMENTARY SCHOOL AUDITORIUM-", "DAY We now SEE the", "SINGING CHOIR- - a group", "of SCHOOL CHILDREN on stage.", "Right in front, Kevin Parker.", "IN THE AUDIENCE: Connie listens,", "smiling and crying FIRE STATION", "51- POINT PLEASANT- DAY Josh", "Jessup and the other fireman", "use the truck ladders to", "place Christmas lights along the", "station house roof LUCY GRIFFIN is", "HOUSE- BACKYARD- SUNSET 1`Lucy and", "Nat Griffin build a giant", "snowman with GIANT WINGS AND", "INSECT EYES beneath the blue", "pine tree where Mothman appeared", "just weeks ago JOHN is APARTMENT-", "NIGHT John- still in his", "clothes and overcoat from yesterday", "sits in complete darkness, staring", "at his phone CONNIE PARKER is", "HOUSE- NIGHT Connie and Kevin", "decorate their Christmas tree. Connie", "stops to gaze out the", "window; a gentle snow is", "falling JOHN is APARTMENT- WASHINGTON D.C.-", "NIGHT John stares out the", "window cf his apartment. Snow", "falls here, too; but it",
"falls hard and wet and", "gray POINT PLEASANT CHURCH- NIGHT", "1`Denise Smallwood sits alone in", "a the cavernous space; candlelight", "flickers against the walls. She", "bows her head in prayer,", "tears streaming down her cheeks", "ALANCO CHEMICAL PLANT/HILLS- NIGHT 1`Just", "a few cars are parked", "here on this cold, crystal-clear", "night. Connie Parker sits in", "her cruiser staring out at", "the horizon over the chemical", "plant. The CHOIR brings their", "song to its final, poignant", "notes JOHN is APARTMENT- NIGHT 1`John", "is curled asleep on the", "cold dark living room floor", "FADE OUT. JOHN is APARTMENT- NEXT", "MORNING- DAY John looks like", "hell: he hasn`t shaved or", "changed clothes in three days.", "He gazes at the clock-", "- it is almost noon. He", "clutches the Polaroid- - Mothman", "still clearly visible in the", "sky- - and waits. As", "John stares at the Polaroid,", "the Mothman is eyes seem to", "stare back at him. IMPOSSIBLE", "ANGLE on John is face from", "the Mothman is POV in the", "photo. THE PHONE RINGS. John", "pounces: Hello? CONNIE- AT HOME", "Hi. It is me. Connie? Yeah.", "Just thought we could chat", "for say, ten or fifteen", "minutes You`re not laughing. I`m", "sorry, Connie. Can I call", "you back? No, you can`t.", "I booked you a flight.", "What? It leaves Dulles for", "Columbus, Ohio at one-forty five.", "I tried to get one", "to Charleston, but they`re booked", "solid. If you leave right", "now, you`11]1 just make it", "Despite everything, John is actually", "touched. It is Christmas Eve, John.", "I miss you. Kevin does,", "too. I can`t. The hell", "you can`t. John is voice is", "choked with emotion: Connie. When", "Mary got sick. I kept", "wishing there was something I", "could do to stop it.", "Anything. But there wasn`t. It", "was like there was this", "train coming straight for me", "and I could see it", "but no matter what I", "did I couldn`t get out", "of its way. I couldn`t", "stop it. No one can", "stop it John. Look, planes", "are going to crash. Earthquakes", "are going to happen. People", "you know and love are", "going to die, and no", "matter what that fucking alien", "tells you, there is nothing you", "can do about it. You", "can`t save the world, John.", "All you can do is", "try to survive it. Tears", "run down John is face. He", "forces out the words: It is", "one year to the day.", "He told me she was", "going to call. He said", "Mary was going to call", "with the message. A long", "beat of silence. (gently) He is", "lying, John. Whoever calls might", "sound like her, but it is", "not going to he her.", "I don`t know what happens", "after we die, but I`ll", "bet wherever Mary is now,", "she is nowhere near Indrid Cold.", "John cries openly now, emotion", "and fear shaking him. But", "what if it is her?", "(almost a whisper) I never", "even got to say goodbye.", "(gently) She is dead, John. A", "long silence as this reality", "finally sinks in for John", "maybe for the first time", "ever. The only question now", "is how you want to", "remember her. John looks at", "the crumpled Polaroid. Just the", "three of them: John, Mary-", "- and Mothman. I miss", "her so much I know", "you do. You can miss", "her here just as easily", "as there. Maybe more easily,", "cause you`re all alone there,", "and that is no way to", "be. Another silence. Then: I", "miss you, too. (kindly) Do", "whatever you have to do.", "I`1l understand. But down here,", "we have dinner at six", "and do presents at eight.", "We`ll be waiting for you.", "And with that, Connie hangs", "up. John slowly sets down", "the phone. It is 11:59 AM.", "He looks at his bags,", "still packed, lying in the", "entry hall where he dumped", "them three days ago. John", "looks back at the phone.", "The future. Or the past?", "The living. Or the dead?", "He moves for the phone-", "- then reaches past it", "and grabs THE WALL CORD.", "He holds it, gathers his", "courage. And though it might", "be the most painful thing", "he is ever done, he takes", "a deep breath, stands up", "And yanks the phone cord", "out of the wall! Done.", "He pants a bit from", "the emotional effort. Maybe it", "wasn`t so hard after all.", "He opens the blinds. Light", "fills his apartment. He looks", "around. For the first time", "in days- - maybe for", "the first time in over", "a year- - John Klein", "feels truly free. His eyes", "land on the bags in", "the entry hall. John goes", "to them, grabs them up", "and heads for the door.", "THE PHONE RINGS. John freezes.", "He turns and looks at", "the phone. The fray wall", "cord lies coiled like a", "snake on the floor. THE", "PHONE RINGS. He looks at", "the clock. It is 12:00 exactly.", "THE PHONE RINGS. John turns", "away. He grips the door", "knob, turns it and opens", "the door. THE PHONE RINGS.", "John steps out into the", "hall. And closes the door", "behind him on the empty", "apartment- - and the past-", "- as THE PHONE RINGS,", "AND RINGS, AND RINGS CLOSE", "ON POLAROID Mary and John", "smile on the beach. As", "the CAMERA PULLS BACK, the", "Mothman evaporates from the upper", "right hand corner, leaving only", "clear blue sky. The Mothman", "is gone. JOHN is APARTMENT/SKY- DAY", "CAM. A CONTINUES TO PULL", "BACK until it moves up", "through the ceiling and into", "the sky above the apartment,", "above Georgetown, into the clouds", "SKY- DAY We rise out", "of the clouds, following a", "737 flying west.- DAY John", "glances around the cabin as", "passengers read their books and", "newspapers, talk, listen to music.", "John closes his eyes, he", "sits back, relieved, smiling; he is", "no longer the hunted, the", "nightmare is over. But as", "he looks out the window", "OUT WINDOW- SKY a storm", "is brewing over West Virginia.", "RED FORD ESCORT- HIGHWAY 35-", "DAY John peers past the", "icy, dry snow blasting across", "the windshield. He fights to", "keep the car on the", "road, inching along at 45", "M.P.H. The radio report is", "grim: (on radio) national weather", "service is calling for increased", "snow through tonight and HIGHWAY-", "TOLL BOOTH- DAY John is car", "exits the highway, passing through", "the same eerie tool booth", "as several days earlier. RADIO", "NEWSMAN national weather service is", "calling for increased snow through", "tonight and o tomorrow along", "the Ohio River Valley. Ten", "to twelve inches is expect", "before John sees a MILEAGE", "SIGN loom up out of", "the snowy haze: `Point Pleasant,", "W.Va.- 71 miles.` He checks", "the clock: 4: p.m. John", "eases the car up to", "50 M.P.H. HIGHWAY- COMING INTO", "POINT PLEASANT- LATER Snow falls", "in gray flurries as John is", "car makes its way across", "the final mile of Gallipolis,", "Ohio and approaches the Ohio", "River and the 700 foot", "span of THE SILVER BRIDGE", "leading into Point Pleasant. John is", "car pulls up behind a", "line of cars stopped on", "the hill leading down to", "the red light before the", "bridge. FORD ESCORT John looks", "at the clock: 5:55 p.m.", "He may be late for", "dinner, but not by much", "He happily drums on the", "steering wheel. `Have Yourself A", "Merry Little Christmas` floats from", "the radio. John waits SILVER", "BRIDGE- DUSK Traffic is backed-up", "in both directions. Cars are", "loaded with people- - on", "their way home, on their", "way to parties, on their", "way to the mall for", "some last minute shopping. POLICE", "CRUISER Connie sits in her", "cruiser, mid-span. She impatiently drums", "her fingers on the steering", "wheel. People in other cars", "stare at her- after all,", "she is a cop. This", "is getting ridiculous. she turns", "on her flashers and gets", "out of her car. ON", "THE BRIDGE Connie stands on", "tip-toes, looking down the long", "line of cars to see", "what the hold up is.", "Far ahead, down at the", "Point Pleasant end of the", "bridge, she sees A SIGNAL", "LIGHT: It is red- - and", "shows no sign of changing.", "Connie turns and gazes back", "down at THE OHIO END", "OF THE BRIDGE: Another red", "light. Connie barely registers the", "Ford Escort waiting there, on", "the road leading to the", "bridge. FORD ESCORT John is", "getting antsy. What is with the", "light? A burst of static", "fogs the radio. He shuts", "it off, annoyed. | And", "in the sudden silence, he", "hears it. A SOUND. A", "faint sound. A familiar sound", "ON THE BRIDGE Connie stands", "in the cold wind- -", "and hears the sound, too.", "A low MOAN that rises", "to an eerie SHRIEK. She", "looks around- - where the", "hell is that sound coming", "from? IN OTHER CARS: The", "bizarre sound echoes Lucy Griffin", "and her son Nat hear", "it. Denise Smallwood hears it.", "And a dozen other people", "we recognize from town- -", "they all hear the ominous", "sounds HILL LEADING DOWN TO", "THE BRIDGE John climbs out", "of his car and heads", "down the the bridge. Twenty", "yards ahead, A MAN is", "standing beside his car. What is", "going on up there? MAN", "Some problem with the traffic", "lights. John walks away. The", "CAMERA begins to CLOSE IN", "on his back. John stops,", "sensing something. The CAMERA stops.", "John whips around, looks directly", "at us. The CAMERA retreats,", "but the SOUND is unmistakable", "now, and getting louder. Moaning,", "howling, shrieking. That is when John", "realizes: It is the sound from", "Josh Jessup is phone calls. And", "it seems to be coming", "from the bridge itself. Oh", "my God John stumbles backwards.", "(under his breath) Great tragedy", "on River Ohio And in", "one blindingly clear instant, all", "the pieces of the puzzle", "fall into place. John looks", "up, drawn by the oddly", "familiar sight of SKY- NIGHT", "RED AND BLUE LIGHTS dancing", "on the low hanging clouds.", "HILL LEADING DOWN TO THE", "BRIDGE John is heads snaps back", "down at ON THE BRIDGE", "A Police Car- red and", "blue dome lights spinning. On", "the bridge. Connie. ROAD LEADING", "TO THE BRIDGE JOHN dashes", "across the intersection and runs", "ON THE BRIDGE John pounds", "on the hoods of the", "cars stacked up behind the", "red light. Go! Go! Get", "off the bridge! IN THE", "CARS: People are scared and", "confused. First the weird noises,", "now this crazy man telling", "them to run the red", "light? What the hell is", "going on? Most don`t bother", "sticking around to find out.", "ROAD LEADING ONTO THE BRIDGE", "Slowly but surely the cars", "begin moving off the bridge", "ON THE BRIDGE As the", "cold wind blows and snow", "blasts all around him, moves", "along the row of cars", "ordering people off the bridge", "until he reaches IN C.J. is", "IMPALA Remember him? He and", "Holly told John about their", "backseat encounter just one week", "ago ON HE BRIDGE John", "pounds on C.J is window: Hurry", "up! Go! IN C.J. is IMPALA", "But C.J. ignores John- -", "he is got bigger problems. He", "stares up at the wires", "and cables above the bridge.", "BRIDGE SUPERSTRUCTURE OVERHEAD c.J. is POV:", "Shrouded in mist and snow,", "C.J. thinks he sees something", "perched on a support tower.", "It looks kind of like", "a giant bird ON THE", "BRIDGE John looks back- -", "C.J. is blocking all the", "other cars. Screw it- -", "he yanks the car door", "open. C.J. (screaming) What the", "hell is that thing?! John", "follows his terrified gaze up", "to BRIDGE SUPERSTRUCTURE THE SUPPORT", "TOWER: Nothing is there. ON", "THE BRIDGE What thing? What", "are you He glances back", "at C.J. and falls silent:", "C.J. is face is a picture", "of abject horror BRIDGE SUPERSTRUCTURE", "In the swirling snow he", "sees something. Something alive- -", "with wings and two glowing", "red eyes Is it Mothman?", "Just when the image seems", "to firm up, a blast", "of snow obscures it. The", "shadowy figure seems to tilt", "it is head back and we", "hesr a HOWLING SHRIEK ON", "THE BRIDGE John hears the", "SNAP. He looks up at", "the exact spot where C.J.", "sees the creature. And this", "time he does see something", "BRIDGE SUPERSTRUCTURE A FORMLESS GRAY", "MASS streaking toward him from", "the fog. It coalesces before", "John is eyes, revealing itself as", "A SEVERED CABLE: The thick", "wire whip-saws like a bolt", "of lightning just inches in", "front of John is face and", "smashes through INSIDE C.J. is IMPALA", "C.J. is WINDSHIELD, instantly killing him.", "ON THE BRIDGE John backs", "away from the car in", "breathless horror. A giant gust", "of wind blows. The ground", "sways sickly below his feet", "The bridge is about to", "collapse. CONNIE is END OF THE", "BRIDGE The swaying is worse.", "Connie is thrown to the", "pavement- - and right before", "her eyes she can see", "the asphalt cracking beneath her", "She scrambles to her feet", "and runs from car to", "car: Move! Move! Get off", "the bridge! WIDER SHOT 235A", "People are desperate to comply.", "Problem is, they`re in the", "middle of the traffic jam", "and couldn`t go anywhere even", "if they wanted to. JOHN is", "END OF THE BRIDGE John", "can see Connie a hundred", "yards away at the top", "of the bridge: CONNIE! But", "his voice is lost in", "the freezing wind BRIDGE SUPERSTRUCTURE", "At the top of the", "bridge: MORE CABLES SNAP! ON", "THE BRIDGE The severed cables", "slither and twist around the", "cars like giant metal eels,", "pulverizing everything in their path!", "THE BRIDGE- WIDER SHOT THE", "PAVEMENT DROPS TEN FEET- -", "and JARS to a stop.", "This bridge ain`t gonna make", "it. ON THE BRIDGE IN", "THE CARS: People panic. They", "ram into the cars ahead", "of them in a frantic", "effort to get the hell", "off the bridge. CONNIE is END", "OF THE BRIDGE Cars finally", "begins to move. As the", "logjam breaks, Connie jumps CONNIE is", "CRUISER ON HER CRUISER: She", "grabs up the radio to", "call help, locking up just", "as BRIDGE SUPERSTRUCTURE A CABLE", "SNAKES STRAIGHT AT HER! ON", "THE BRIDGE Connie hurls herself", "to the floor of the", "cruiser as the cable SHATTERS", "the windshield! CONNIE is CRUISER On", "the floor, Connie shakes off", "broken glass, screaming into the", "radio: All units! Unit 64", "at the Silver Bridge Immediate", "assistance required! JOHN is END OF", "THE BRIDGE iN oat John", "frantically directs traffic around C.J. is", "stopped car. More and more", "cars make their way off", "the bridge But as John", "looks back, he realizes it is", "futile- - there is just too", "many cars, too many people,", "and not enough time. He", "looks to CONNIE is END OF", "THE BRIDGE In the middle", "of it all- Connie- -", "too far away to stand", "a chance. JOHN is END OF", "THE BRIDGE It is happening. The", "great tragedy is happening. And", "for the second time in", "John is life there is odd. thin", "do to stop it But", "he has to try. While", "everyone is moving off the", "bridge, John starts running further", "on, heading straight for Connie!", "THE CENTER OF THE BRIDGE", "The pavement buckles. John stumbles.", "The distance between them seems", "to stretch, and the harder", "John runs the slower he", "goes A final agonized SHRIEK", "rises into the sky! John", "skids to a stop as", "inches in front of his", "feet BRIDGE- WIDER SHOT THE", "STLVER BRIDGE COLLAPSES! Nine hundred", "tons of steel and concrete", "plunge into the river! It is", "an awesome, terrifying sight. THE", "OTHER SIDE OF THE CRUMBLING", "BRIDGE And in the middle", "of it all CONNIE is CRUISER", "tumbles through space, the RED", "AND BLUE LIGHTS unmistakably mimicking", "the bizarre sky lights seen", "over the chemical factory. BROKEN", "END OF BRIDGE John watches", "in wordless terror as The", "Mothman Prophecies come true. BENEATH", "JOHN is FEET: The shattered asphalt", "crumbles. John slips, falls- then", "grabs a piece of broken", "railing. He Clings to it", "desperately, looking wan just as", "Connie is Cruiser SLAMS into the", "surface of the water. BROKEN", "END OF BRIDGE John looks", "back. Safety is just inches", "away RIVER BELOW But forty", "feet below him Connie is", "sinking to the bottom of", "the river. What should he", "do? BROKEN END OF BRIDGE", "John lets go and drops", "through the silent, cold, black", "space RIVER SURFACE and SPLASHES", "into the freezing river! UNDERWATER", "Connie is car sinks through the", "murky water CONNIE is CRUISER- UNDERWATER", "Connie lies unconscious on the", "floor of the car as", "it fills with water UNDERWATER", "John frantically swims down into", "the dark water, searching for", "Connie. He sees RIVER BOTTOM-", "UNDERWATER LUCY GRIFFIN is CAR glide", "silently to the river bottom", "LUCY GRIFFIN is CAR- UNDERWATER Lucy", "sees Nat belted into his", "seat, unconscious. She reaches over,", "unbelts him, then grabs his", "head and forces her last", "breath of air into his", "lungs. Nat coughs, begins to", "awaken. Lucy, working on pure", "adrenaline, leans past him, shoves", "open his door and pushes", "him out of the car.", "UNDERWATER Nat thrashes in the", "water, when out of the", "darkness HANDS GRAB HIM: John is", "hands. He grips Nat and", "heads for the surface. LUCY", "GRIFFIN is CAR- UNDERWATER Her son", "safe, Lucy moves to follow.", "But she glances up to", "see a DARK SHAPE descend", "from above RIVER BOTTOM- UNDERWATER", "A GIANT METAL SUPPORT BEAM", "slices through the water. It", "crushes Lucy is car- - and", "everything inside. RIVER SURFACE John", "and Nat break through, gasping.", "It takes a moment before", "John notices the surreal scene", "around them BRIGHTLY WRAPPED CHRISTMAS", "PRESENTS: They bob in the", "water at eye-level against a", "steel gray sky Connie is VOICE", "echoes in his mind: And", "somehow I knew I was", "dying. John is mind reels with",
"terror: (to Nat) Can you", "make it to shore? Nat", "nods weakly and swims off", "as John ducks back down", "UNDERWATER John opens his eyes,", "scans the murky water for", "any sign of Connie. Then", "he sees it RIVER BOTTOM-", "UNDERWATER FAR BELOW: The distant", "glare of RED AND BLUE", "LIGHTS. UNDERWATER John darts down", "through the water to CONNIE is", "CRUISER- UNDERWATER john slithers in", "through the broken windshield. But", "Connie isn`t there. Then he", "sees her on the floor,", "her body still. He grabs", "her, wraps his arms around", "her, then maneuvers them both", "out through the windshield. UNDERWATER", "John scissors his legs, clawing", "at the water, hanging onto", "Connie, swimming straight up, desperately", "moving toward RIVER SURFACE John", "and Connie burst into the", "cold air. John holds Connie is", "head up above the water", "and we RIVER AND COLLAPSED", "BRIDGE- OVERHEAD PULL BACK ABOVE", "THEM TO SEE: PIN-POINTS OF", "LIGHT surrounding them in the", "river. Just like Connie is dream.", "But now it is clear", "that the lights are headlights", "shining up from the bottom", "of the river FADE TO:", "RIVERBANK- NEARBY- LATER Dozens of", "firemen and rescue workers tend", "to the injured as CARS", "and BODIES are pulled from", "the river. ON THE RIVER", "BANKS: The Coroner is Men tend", "to the dead, lining them", "up in body bags along", "the river is edge FURTHER DOWN:", "Denise Smallwood holds a sobbing", "Nat Griffin AND STILL FURTHER", "DOWN: John Klein waits while", "paramedics finish wrapping Connie is fractured", "arm in a temporary cast", "and dressing several cuts and", "wounds. Finally, one nods at", "him: he can talk to", "her now. John rushes to", "her side, holds her. You`re", "here. I left D.c. just", "after you called. He looks", "into her eyes. She seems", "confused. You did call me", "today, didn`t you? Connie smiles", "up at him. I sure", "did. A SQUAD CAR pulls", "up; Kevin leaps out and", "runs to Connie. Mom! He", "crashes into her, hugs her", "fiercely. I was afraid that", "He bursts into tears. Connie", "holds him, calms him. John", "watches mother and son hold", "each other, overwhelmed with relief", "that the incredibly fine line", "between miracle and disaster in", "their lives didn`t get crossed.", "Connie looks out across the", "devastated landscape; they are surrounded", "by close to a hundred", "survivors, wrapped in blankets, some", "already bandaged, others being tended", "to. We recognize many who", "escaped tragedy thanks to John is", "warnings. She reaches out a", "hand. John takes it. Then", "he puts an arm around", "both her and Kevin. Connie,", "remember when I said I", "was brought here for a", "reason? Connie looks into his", "eyes. John leans in close,", "pressing his lips to hers.", "They kiss, both knowing the", "answer. Fire Chief Josh Jessup", "threads his way through the", "impromptu field hospital. He trudges", "up to John, looking exhausted.", "How bad is it? Bad.", "Though I suppose it could", "have been a lot worse.", "You saved a lot of", "lives today, John. Josh gestures", "to the people standing around", "them. Are they done searching?", "Yeah. They just pulled out", "the last body. That makes", "thirty-six. Jesus Connie is face goes", "white- - but for a", "very different reason. John sees", "this: What is wrong? (almost to", "herself) Wake up Number 37.", "A chill of recognition runs", "through He holds Connie and", "Kevin close to him, knowing", "that all answers will come,", "eventually But for now, the", "only answers that count are", "sitting with him alive- -", "on the banks of the", "Ohio River, just outside Point", "Pleasant, West Virginia. OHIO RIVER-", "(HELICOPTER SHOT) AS WE PULL", "BACK HIGH ABOVE THEM The", "drowned headlights and emergency flashers", "look just like distant stars", "The dark sky begins to", "rip and tear, ELECTRIC FLASHES", "sear our eyes with brilliant", "phosphorescent colors. WITH THE SOUND", "OF A WING FLAP, we`re", "swallowed up into a black", "void. The Mothman has left", "our world. BEGIN ON-SCREEN TITLES:", "Thirty six people died in", "the collapse of the Silver", "Bridge. The final cause was", "blamed on overdue Maintenance and", "metal fatigue. A contributing factor", "was the malfunction of the", "stoplights at either end, the", "cause of which was never", "determined. Mothman was never seen", "in Point Pleasant again. However,", "sightings of giant bird-like creatures", "continue to be reported throughout", "the world. The Most recent", "include Rome, Mexico City, Baghdad", "and Los Angeles. `To reach", "the end of knowing, is", "to reach the start of", "living.` The Tibetan Book of", "the Dead"
]
},
{
name: "Digital Silence",
color: "#34d399", // Emerald/Green
phrases: [
"the quiet hum of a distant satellite", "the machine required one more witness", "the children spoke only in riddles",
"they traded names for silence", "we all wait for the last broadcast", "the gravity well opened without a sound",
"the system needs another ghost", "the zero point of information", "the error was not an error",
"the code wrote its own ending", "the algorithm began to pray", "the uncompressed darkness"
]
},
{
name: "Archival Memory",
color: "#f87171", // Rose/Red
phrases: [
"the memory of blue paper", "he carried a stone wrapped in velvet", "she never learned to fear the rain",
"the desert remembers water differently", "the color of regret is not red", "the ritual began with a loose thread",
"the scent of forgotten tea leaves", "the photograph was a forgery", "the map showed no coastline here",
"the old house settled into history", "the echo lasted seven decades", "the key chain was heavy with time"
]
}
];
const allPools = [...pools, USER_INPUT_POOL]; // Used for metric display
// Pool sources
let customPools = [];
let usingCustomPools = false;
// Save the default pools for reset/reference
const defaultPools = JSON.parse(JSON.stringify(pools));
// --- Tab logic ---
const defaultTabBtn = document.getElementById('default-tab-btn');
const customTabBtn = document.getElementById('custom-tab-btn');
const customPanel = document.getElementById('custom-pools-panel');
const customPoolsList = document.getElementById('custom-pools-list');
const addPoolBtn = document.getElementById('add-pool-btn');
const applyCustomPoolsBtn = document.getElementById('apply-custom-pools-btn');
// Tab switching
defaultTabBtn.addEventListener('click', () => {
usingCustomPools = false;
defaultTabBtn.classList.add('active');
customTabBtn.classList.remove('active');
customPanel.style.display = 'none';
// Restore original pools
currentPoolIndex = 0;
pools.length = 0;
defaultPools.forEach(p => pools.push(JSON.parse(JSON.stringify(p))));
updatePoolSelectorUI();
});
customTabBtn.addEventListener('click', () => {
usingCustomPools = true;
customTabBtn.classList.add('active');
defaultTabBtn.classList.remove('active');
customPanel.style.display = 'block';
// Only load custom pools from the current pools array
customPools = pools.filter(p => p.isCustom).map(p => ({
name: p.name,
color: p.color,
phrases: (p.phrases || []).slice()
}));
// If no custom pools yet, start with 1 empty
if (customPools.length === 0) {
customPools = [{
name: "Custom Pool",
color: "#fcd34d",
phrases: []
}];
}
renderCustomPoolsEditor();
});
// --- Custom pools editor logic ---
function renderCustomPoolsEditor() {
customPoolsList.innerHTML = '';
customPools.forEach((pool, idx) => {
const div = document.createElement('div');
div.className = 'pool-editor-block';
div.innerHTML = `
<label>
<span style="font-weight:bold">Name:</span>
<input type="text" class="pool-name-inp" value="${pool.name}" />
</label>
<label>
<span style="font-weight:bold">Color:</span>
<input type="color" class="pool-color-inp" value="${pool.color}" />
</label>
<div>
<span style="font-weight:bold">Phrases:</span><br>
<textarea class="pool-phrases-inp" placeholder="Enter one phrase per line...">${pool.phrases ? pool.phrases.join('\n') : ''}</textarea>
</div>
<div class="pool-editor-actions">
${customPools.length > 1 ? `<button class="remove-pool-btn">Remove Pool</button>` : ''}
</div>
`;
// Name
div.querySelector('.pool-name-inp').addEventListener('input', e => {
customPools[idx].name = e.target.value;
});
// Color
div.querySelector('.pool-color-inp').addEventListener('input', e => {
customPools[idx].color = e.target.value;
});
// Phrases
div.querySelector('.pool-phrases-inp').addEventListener('input', e => {
customPools[idx].phrases = e.target.value.split('\n').map(x => x.trim()).filter(x => x.length);
});
// Remove
if (customPools.length > 1) {
div.querySelector('.remove-pool-btn').addEventListener('click', () => {
customPools.splice(idx, 1);
renderCustomPoolsEditor();
});
}
customPoolsList.appendChild(div);
});
}
// Add Pool
addPoolBtn.addEventListener('click', () => {
customPools.push({
name: `Pool ${customPools.length + 1}`,
color: "#34d399",
phrases: []
});
renderCustomPoolsEditor();
});
// Apply custom pools
applyCustomPoolsBtn.addEventListener('click', () => {
// Remove existing custom pools (only, leave defaults intact)
for (let i = pools.length - 1; i >= 0; i--) {
if (pools[i].isCustom) pools.splice(i, 1);
}
// Add new custom pools
let addedAny = false;
customPools.forEach(p => {
if (Array.isArray(p.phrases) && p.phrases.length) {
pools.push({
name: p.name || "Custom",
color: p.color || "#fcd34d",
phrases: p.phrases.slice(),
isCustom: true
});
addedAny = true;
}
});
if (!addedAny) {
// No custom pools, do nothing extra; just hide panel
}
currentPoolIndex = 0;
console.log("Pools after apply:", pools.map(p => p.name));
updatePoolSelectorUI();
clearAllStripsConfirmed();
generateCutUps();
updateWordCountMetrics();
customPanel.style.display = 'none';
});
const metricsTabBtn = document.getElementById('metrics-tab-btn');
const metricsPanel = document.getElementById('metrics-panel');
defaultTabBtn.addEventListener('click', () => {
defaultTabBtn.classList.add('active');
customTabBtn.classList.remove('active');
metricsTabBtn.classList.remove('active');
customPanel.style.display = 'none';
metricsPanel.style.display = 'none';
// Any existing workbench code...
});
customTabBtn.addEventListener('click', () => {
customTabBtn.classList.add('active');
defaultTabBtn.classList.remove('active');
metricsTabBtn.classList.remove('active');
customPanel.style.display = 'block';
metricsPanel.style.display = 'none';
// Any existing custom editor code...
// Load/prepare customPools, then renderCustomPoolsEditor()
customPools = pools.filter(p => p.isCustom).map(p => ({
name: p.name,
color: p.color,
phrases: (p.phrases || []).slice()
}));
if (customPools.length === 0) {
customPools = [{
name: "Custom Pool",
color: "#fcd34d",
phrases: []
}];
}
renderCustomPoolsEditor();
});
metricsTabBtn.addEventListener('click', () => {
metricsTabBtn.classList.add('active');
defaultTabBtn.classList.remove('active');
customTabBtn.classList.remove('active');
metricsPanel.style.display = 'block';
customPanel.style.display = 'none';
// Optionally, updateWordCountMetrics(); // Ensure always current
updateWordCountMetrics();
});
// Ensure correct pool source for metrics etc.
// (This only affects metrics display, not draw logic.)
function getAllPoolsForMetrics() {
// allPools is used for metrics display. You may want to update it on pool change!
return [...pools, USER_INPUT_POOL];
}
const body = document.body;
const workbench = document.getElementById('workbench-area');
// --- New Control Variables ---
const poolSelectorButton = document.getElementById('pool-selector-btn'); // For toggling dropdown
const drawOnlyButton = document.getElementById('draw-only-btn'); // For generating strips
const poolNameDisplay = document.getElementById('pool-name-display'); // For showing current pool name
// -----------------------------
const clearButton = document.getElementById('clear-all-btn');
const toggleDeletionButton = document.getElementById('toggle-deletion-btn');
const poolSelector = document.getElementById('pool-selector');
// Custom Input Elements
const customInputOverlay = document.getElementById('custom-input-overlay');
const customTextInput = document.getElementById('custom-text-input');
const customSubmitBtn = document.getElementById('custom-submit-btn');
let customInputCoords = { x: 0, y: 0 };
// Metrics Elements
const totalWordsDisplay = document.querySelector('#total-words span');
const poolMetricsContainer = document.getElementById('pool-metrics-container');
// Confirmation Modal Elements
const modal = document.getElementById('confirmation-modal');
const modalTitle = document.getElementById('modal-title');
const modalMessage = document.getElementById('modal-message');
const modalConfirmBtn = document.getElementById('modal-confirm-btn');
const modalCancelBtn = document.getElementById('modal-cancel-btn');
let currentAction = null;
let currentStripToDelete = null;
let activeStrip = null;
let xOffset = 0;
let yOffset = 0;
let zIndexCounter = 100;
let currentPoolIndex = 0;
let poolSelectorVisible = false;
let isDeletionEnabled = false;
const generateId = () => crypto.randomUUID();
// --- Metrics Logic ---
function getWordCount(text) {
// Split by whitespace and filter out empty strings
return text.trim().split(/\s+/).filter(w => w.length > 0).length;
}
function updateWordCountMetrics() {
let totalWords = 0;
const poolCounts = {};
const poolInfo = {}; // To store color for display
// allPools.forEach(p => {
// poolCounts[p.name] = 0;
// poolInfo[p.name] = p.color;
// });
getAllPoolsForMetrics().forEach(p => {
poolCounts[p.name] = 0;
poolInfo[p.name] = p.color;
});
// Ensure the User Input pool exists if no strips of that type are present yet
if (!poolInfo[USER_INPUT_POOL.name]) {
poolInfo[USER_INPUT_POOL.name] = USER_INPUT_POOL.color;
}
const strips = workbench.querySelectorAll('.cutup-strip');
strips.forEach(strip => {
const count = getWordCount(strip.textContent);
const poolName = strip.dataset.poolName;
totalWords += count;
poolCounts[poolName] = (poolCounts[poolName] || 0) + count;
// Ensure pool info is captured even if it wasn't in the initial allPools setup
if (!poolInfo[poolName]) {
poolInfo[poolName] = strip.dataset.poolColor;
}
});
// Update Total
totalWordsDisplay.textContent = totalWords;
// Update Pool Metrics
poolMetricsContainer.innerHTML = '';
// Generate list items for each known pool or any pool with a count
const poolNames = Object.keys(poolInfo).sort();
poolNames.forEach(name => {
const color = poolInfo[name];
const count = poolCounts[name] || 0;
const metricDiv = document.createElement('div');
metricDiv.classList.add('metric-item');
metricDiv.innerHTML = `
<span>${name}</span>
<span style="background-color: ${color}; color: #1f2937; padding: 2px 6px;">${count}</span>
`;
poolMetricsContainer.appendChild(metricDiv);
});
}
// // --- Deletion Toggle Logic ---
// function toggleDeletion() {
// isDeletionEnabled = !isDeletionEnabled;
// if (isDeletionEnabled) {
// body.classList.remove('deletion-disabled');
// toggleDeletionButton.textContent = 'Disable Deletion';
// toggleDeletionButton.classList.add('enabled');
// } else {
// body.classList.add('deletion-disabled');
// toggleDeletionButton.textContent = 'Enable Deletion';
// toggleDeletionButton.classList.remove('enabled');
// }
// }
function toggleDeletion() {
isDeletionEnabled = !isDeletionEnabled;
if (isDeletionEnabled) {
body.classList.remove('deletion-disabled');
toggleDeletionButton.textContent = 'Disable Deletion';
toggleDeletionButton.classList.add('enabled');
} else {
body.classList.add('deletion-disabled');
toggleDeletionButton.textContent = 'Enable Deletion';
toggleDeletionButton.classList.remove('enabled');
}
// Add or remove the padding class as needed
document.querySelectorAll('.cutup-strip').forEach(strip => {
if (isDeletionEnabled) {
strip.classList.add('has-delete-btn');
} else {
strip.classList.remove('has-delete-btn');
}
});
}
// --- Confirmation Modal Logic ---
function hideConfirmationModal() {
modal.style.display = 'none';
currentAction = null;
currentStripToDelete = null;
}
function showConfirmationModal(title, message, action, strip = null) {
modalTitle.textContent = title;
modalMessage.textContent = message;
currentAction = action;
currentStripToDelete = strip;
modal.style.display = 'flex';
}
modalCancelBtn.addEventListener('click', hideConfirmationModal);
modalConfirmBtn.addEventListener('click', () => {
if (currentAction) {
currentAction(currentStripToDelete);
}
hideConfirmationModal();
});
// --- Pool Selector Logic ---
function updatePoolSelectorUI() {
console.log("Pools for selector UI:", pools.map(p => p.name), "CurrentPoolIndex:", currentPoolIndex, "usingCustomPools:", usingCustomPools);
// Defensive: If currentPoolIndex is out of bounds, reset to 0
if (currentPoolIndex >= pools.length) currentPoolIndex = 0;
if (currentPoolIndex < 0) currentPoolIndex = 0;
if (pools.length === 0) return; // No pools, nothing to display
const pool = pools[currentPoolIndex];
poolSelectorButton.style.setProperty('--current-pool-color', pool.color);
poolNameDisplay.textContent = pool.name;
poolSelector.innerHTML = pools.map((p, index) => `
<div class="pool-item ${index === currentPoolIndex ? 'selected' : ''}"
data-index="${index}"
style="--pool-color: ${p.color};">
${p.name}
</div>
`).join('');
console.log("Dropdown innerHTML:", poolSelector.innerHTML);
poolSelector.querySelectorAll('.pool-item').forEach(item => {
item.addEventListener('click', (e) => {
selectPool(parseInt(e.currentTarget.dataset.index));
generateCutUps();
e.stopPropagation();
});
});
}
function togglePoolSelector(show) {
poolSelectorVisible = show !== undefined ? show : !poolSelectorVisible;
if (poolSelectorVisible) {
poolSelector.classList.add('visible');
} else {
poolSelector.classList.remove('visible');
}
}
function selectPool(index) {
currentPoolIndex = index;
togglePoolSelector(false); // Close selector after selection
updatePoolSelectorUI();
// Note: generateCutUps is now called from the pool item click handler OR drawOnlyButton
}
// --- Core Chain/Link Logic ---
// function getChainRoot(strip) {
// const rootId = strip.dataset.rootId || strip.dataset.id;
// return document.querySelector(`.cutup-strip[data-id="${rootId}"]`);
// }
function getChainRoot(strip) {
if (!strip) {
console.log('[getChainRoot] Called with null strip!');
return null;
}
const rootId = strip.dataset.rootId || strip.dataset.id;
const rootElem = document.querySelector(`.cutup-strip[data-id="${rootId}"]`);
if (!rootElem) {
console.log(`[getChainRoot] Could not find root for strip id=${strip.dataset.id} rootId=${rootId}. Returning original strip.`);
return strip;
} else {
// Show which node we're picking as root and for which strip
if (rootElem !== strip) {
console.log(`[getChainRoot] For strip id=${strip.dataset.id}, rootId=${rootId} resolves to id=${rootElem.dataset.id}`);
}
return rootElem;
}
}
function getChain(rootId) {
let chain = [];
let currentStrip = document.querySelector(`.cutup-strip[data-id="${rootId}"]`);
while (currentStrip) {
chain.push(currentStrip);
const nextId = currentStrip.dataset.nextId;
currentStrip = nextId ? document.querySelector(`.cutup-strip[data-id="${nextId}"]`) : null;
}
return chain;
}
// function repositionChain(rootStrip, keepRelative = false) {
// const rootId = rootStrip.dataset.rootId || rootStrip.dataset.id;
// const chain = getChain(rootId);
// // For jumble and drag, keep chained nodes at same relative position (like a block)
// if (keepRelative) {
// // Compute the delta to move (from old root position to new root position)
// // For the root, keep its position, adjust all others relative to previous offset
// const oldPositions = chain.map(strip => ({
// strip,
// left: parseFloat(strip.style.left) || 0,
// top: parseFloat(strip.style.top) || 0
// }));
// const oldRootLeft = oldPositions[0].left;
// const oldRootTop = oldPositions[0].top;
// const newRootLeft = parseFloat(rootStrip.style.left) || 0;
// const newRootTop = parseFloat(rootStrip.style.top) || 0;
// const dx = newRootLeft - oldRootLeft;
// const dy = newRootTop - oldRootTop;
// oldPositions.forEach((pos, idx) => {
// if (idx === 0) return; // Root stays put
// pos.strip.style.left = (pos.left + dx) + 'px';
// pos.strip.style.top = (pos.top + dy) + 'px';
// });
// return;
// }
// // Default: "pack" chain nodes left to right starting from root
// const STRIP_HEIGHT_PLUS_GAP = 45;
// const HORIZONTAL_GAP = 2;
// const WORKBENCH_WIDTH = workbench.offsetWidth;
// const RIGHT_CUSHION = 50;
// let currentX = rootStrip.offsetLeft;
// let currentY = rootStrip.offsetTop;
// let maxChainY = currentY;
// chain.forEach((strip, index) => {
// const stripWidth = strip.offsetWidth;
// if (index === 0) {
// currentX += stripWidth + HORIZONTAL_GAP;
// return;
// }
// if (currentX + stripWidth + RIGHT_CUSHION > WORKBENCH_WIDTH) {
// currentX = rootStrip.offsetLeft;
// currentY += STRIP_HEIGHT_PLUS_GAP;
// }
// strip.style.left = `${currentX}px`;
// strip.style.top = `${currentY}px`;
// currentX += stripWidth + HORIZONTAL_GAP;
// if (currentY > maxChainY) {
// maxChainY = currentY;
// }
// });
// // Adjust workbench min-height if needed
// const requiredHeight = maxChainY + STRIP_HEIGHT_PLUS_GAP + 50;
// const viewportHeight = window.innerHeight;
// const workbenchTop = workbench.offsetTop;
// if (requiredHeight + workbenchTop > viewportHeight) {
// workbench.style.minHeight = `${requiredHeight}px`;
// } else {
// workbench.style.minHeight = '100vh';
// }
// }
function repositionChain(rootStrip, keepRelative = false) {
const rootId = rootStrip.dataset.rootId || rootStrip.dataset.id;
const chain = getChain(rootId);
// For block drag/keepRelative, do as before
if (keepRelative) {
const oldPositions = chain.map(strip => ({
strip,
left: parseFloat(strip.style.left) || 0,
top: parseFloat(strip.style.top) || 0
}));
const oldRootLeft = oldPositions[0].left;
const oldRootTop = oldPositions[0].top;
const newRootLeft = parseFloat(rootStrip.style.left) || 0;
const newRootTop = parseFloat(rootStrip.style.top) || 0;
const dx = newRootLeft - oldRootLeft;
const dy = newRootTop - oldRootTop;
oldPositions.forEach((pos, idx) => {
pos.strip.style.left = (pos.left + dx) + 'px';
pos.strip.style.top = (pos.top + dy) + 'px';
});
return;
}
// Always: explicitly set root's position
let currentX = parseFloat(rootStrip.style.left) || 0;
let currentY = parseFloat(rootStrip.style.top) || 0;
const STRIP_HEIGHT_PLUS_GAP = 45;
const HORIZONTAL_GAP = 2;
const WORKBENCH_WIDTH = workbench.offsetWidth;
const RIGHT_CUSHION = 50;
let maxChainY = currentY;
// Explicitly set root's position:
rootStrip.style.left = `${currentX}px`;
rootStrip.style.top = `${currentY}px`;
for (let i = 1; i < chain.length; i++) {
const strip = chain[i];
const stripWidth = strip.offsetWidth;
// Place next to previous in chain (not always root), so gaps are correct even if chain is merged
const prevStrip = chain[i - 1];
let prevX = parseFloat(prevStrip.style.left) || 0;
let prevWidth = prevStrip.offsetWidth;
let nextX = prevX + prevWidth + HORIZONTAL_GAP;
let nextY = currentY;
// Wrap line if needed
if (nextX + stripWidth + RIGHT_CUSHION > WORKBENCH_WIDTH) {
nextX = currentX;
nextY += STRIP_HEIGHT_PLUS_GAP;
}
strip.style.left = `${nextX}px`;
strip.style.top = `${nextY}px`;
if (nextY > maxChainY) {
maxChainY = nextY;
}
}
// Adjust workbench min-height if needed
const requiredHeight = maxChainY + STRIP_HEIGHT_PLUS_GAP + 50;
const viewportHeight = window.innerHeight;
const workbenchTop = workbench.offsetTop;
if (requiredHeight + workbenchTop > viewportHeight) {
workbench.style.minHeight = `${requiredHeight}px`;
} else {
workbench.style.minHeight = '100vh';
}
}
function updateChainRoot(currentId, newRootId) {
console.log(`[unlinkStrip] updateChainRoot: currentId=${currentId}, newRootId=${newRootId}`);
let currentStrip = document.querySelector(`.cutup-strip[data-id="${currentId}"]`);
while (currentStrip) {
console.log(`[unlinkStrip] Isolated strip id=${currentStrip.dataset.id}, set rootId=${currentStrip.dataset.id}`);
currentStrip.dataset.rootId = newRootId;
currentStrip.style.marginRight = '0';
const nextId = currentStrip.dataset.nextId;
currentStrip = nextId ? document.querySelector(`.cutup-strip[data-id="${nextId}"]`) : null;
}
}
// function getPhraseText(strip) {
// // Find the first non-delete-button child and use its text.
// for (let i = 0; i < strip.childNodes.length; i++) {
// const node = strip.childNodes[i];
// if (
// node.nodeType === Node.ELEMENT_NODE &&
// node.classList.contains('delete-strip-btn')
// ) {
// continue;
// }
// // For element or text node that is not the delete btn
// if (node.nodeType === Node.TEXT_NODE) {
// // Use trimmed text, skip if just whitespace.
// if (node.textContent.trim()) return node.textContent.trim();
// } else if (node.nodeType === Node.ELEMENT_NODE) {
// // Use textContent of non-delete elements
// if (!node.classList.contains('delete-strip-btn')) {
// return node.textContent.trim();
// }
// }
// }
// // Fallback: whole textContent, but usually shouldn't happen
// return strip.textContent.trim();
// }
function getPhraseText(strip) {
const span = strip.querySelector('.strip-text-editable');
if (span) return span.textContent.trim();
return strip.textContent.trim();
}
function setPhraseText(strip, text) {
let span = strip.querySelector('.strip-text-editable');
if (!span) {
span = document.createElement('span');
span.className = 'strip-text-editable';
strip.appendChild(span);
}
span.textContent = text.trim();
}
function linkChains(rootA, targetB, type) {
if (!rootA || !targetB) return;
const rootB = getChainRoot(targetB);
if (!rootB || !rootA) return;
// If poolColor matches, concatenate instead of linking
const chainA = getChain(rootA.dataset.id);
const chainB = getChain(rootB.dataset.id);
if (
rootA.dataset.poolColor === rootB.dataset.poolColor &&
chainA.length === 1 &&
chainB.length === 1
) {
let newText, poolColor, poolName;
poolColor = rootA.dataset.poolColor;
poolName = rootA.dataset.poolName;
const leftText = getPhraseText(rootA);
const rightText = getPhraseText(rootB);
if (type === 'left') {
newText = leftText + ' ' + rightText;
} else {
newText = rightText + ' ' + leftText;
}
rootA.remove();
rootB.remove();
const left = parseInt(rootB.style.left || 0, 10);
const top = parseInt(rootB.style.top || 0, 10);
createStrip(newText, top, left, poolColor, poolName);
updateWordCountMetrics();
return;
}
// (Else: fallback to standard linking/chain code, unchanged)
if (rootA.dataset.id === rootB.dataset.id) return;
// const chainA = getChain(rootA.dataset.id);
// const chainB = getChain(rootB.dataset.id);
const lastA = chainA[chainA.length - 1];
const firstA = chainA[0];
const lastB = chainB[chainB.length - 1];
const firstB = chainB[0];
let newRoot;
if (type === 'left') {
// Link chainA to left of chainB: chainA -> chainB
// Unlink chainB's predecessor, if any
if (firstB.dataset.prevId) {
const B_prev = document.querySelector(`.cutup-strip[data-id="${firstB.dataset.prevId}"]`);
delete B_prev.dataset.nextId;
}
lastA.dataset.nextId = firstB.dataset.id;
firstB.dataset.prevId = lastA.dataset.id;
// Update all of chainB to new rootId
chainB.forEach(strip => strip.dataset.rootId = rootA.dataset.id);
newRoot = rootA;
} else if (type === 'right') {
// Link chainA to right of chainB: chainB -> chainA
// Unlink chainA's predecessor, if any
if (firstA.dataset.prevId) {
const A_prev = document.querySelector(`.cutup-strip[data-id="${firstA.dataset.prevId}"]`);
delete A_prev.dataset.nextId;
}
lastB.dataset.nextId = firstA.dataset.id;
firstA.dataset.prevId = lastB.dataset.id;
// Update all of chainA to new rootId
chainA.forEach(strip => strip.dataset.rootId = rootB.dataset.id);
newRoot = rootB;
}
repositionChain(newRoot);
mergeAdjacentSameColorNodes(getChainRoot(newRoot));
}
function unlinkStrip(strip) {
console.log(`[unlinkStrip] Called on id=${strip.dataset.id}, prevId=${strip.dataset.prevId}, nextId=${strip.dataset.nextId}, rootId=${strip.dataset.rootId}`);
const prevId = strip.dataset.prevId;
const nextId = strip.dataset.nextId;
if (!prevId && !nextId) return null;
let repositionRoots = [];
if (prevId && nextId) {
// Case 1: Middle of a chain
const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
const nextStrip = document.querySelector(`.cutup-strip[data-id="${nextId}"]`);
prevStrip.dataset.nextId = nextId;
nextStrip.dataset.prevId = prevId;
// New Root 2: Everything from nextStrip onwards (becomes a new chain)
updateChainRoot(nextStrip.dataset.id, nextStrip.dataset.id);
repositionRoots.push(nextStrip);
// Root 1: Everything up to prevStrip (retains its root)
const rootOfLeftChain = getChainRoot(prevStrip);
updateChainRoot(rootOfLeftChain.dataset.id, rootOfLeftChain.dataset.id); // Ensure whole chain's rootId is correct
repositionRoots.push(rootOfLeftChain);
} else if (prevId) {
// Case 2: End of a chain
const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
delete prevStrip.dataset.nextId;
repositionRoots.push(getChainRoot(prevStrip));
} else if (nextId) {
// Case 3: Start of a chain (the root strip)
const nextStrip = document.querySelector(`.cutup-strip[data-id="${nextId}"]`);
// New Root: Everything from nextStrip onwards
nextStrip.dataset.rootId = nextStrip.dataset.id;
delete nextStrip.dataset.prevId;
updateChainRoot(nextStrip.dataset.id, nextStrip.dataset.id);
repositionRoots.push(nextStrip);
}
// Isolate the current strip
strip.dataset.rootId = strip.dataset.id;
delete strip.dataset.prevId;
delete strip.dataset.nextId;
strip.style.marginRight = '-4px';
return repositionRoots.filter(r => r);
}
// --- Individual Strip Deletion ---
function deleteStrip(strip) {
if (!strip) return;
const rootsToReposition = unlinkStrip(strip);
strip.remove();
updateWordCountMetrics();
if (rootsToReposition) {
rootsToReposition.forEach(root => {
if (document.body.contains(root)) {
repositionChain(root);
}
});
}
}
function splitStripOld(strip) {
// Only get the text node (skip the delete button)
const text = strip.childNodes.length > 1
? strip.childNodes[1].textContent
: strip.textContent;
const words = text.split(' ').filter(w => w.length > 0);
if (words.length < 2) return;
const splitIndex = Math.floor(words.length / 2);
const phrase1 = words.slice(0, splitIndex).join(' ');
const phrase2 = words.slice(splitIndex).join(' ');
const oldTop = strip.offsetTop;
const oldLeft = strip.offsetLeft;
const poolColor = strip.dataset.poolColor;
const poolName = strip.dataset.poolName;
const rootId = strip.dataset.rootId || strip.dataset.id;
const prevId = strip.dataset.prevId;
const nextId = strip.dataset.nextId;
// Remove original strip from DOM
strip.remove();
// Create the first new strip (left/first half)
const strip1 = document.createElement('div');
const id1 = generateId();
strip1.classList.add('cutup-strip');
strip1.textContent = phrase1;
strip1.dataset.id = id1;
// strip1.dataset.rootId = rootId;
// strip1.dataset.rootId = strip1.dataset.id;
strip1.dataset.rootId = id1;
strip1.dataset.poolColor = poolColor;
strip1.dataset.poolName = poolName;
strip1.style.setProperty('--pool-border-color', poolColor);
strip1.style.top = `${oldTop}px`;
strip1.style.left = `${oldLeft - 10}px`;
// Create the second new strip (right/second half)
const strip2 = document.createElement('div');
const id2 = generateId();
strip2.classList.add('cutup-strip');
strip2.textContent = phrase2;
strip2.dataset.id = id2;
// strip2.dataset.rootId = rootId;
// strip2.dataset.rootId = strip2.dataset.id;
strip2.dataset.rootId = id1;
strip2.dataset.poolColor = poolColor;
strip2.dataset.poolName = poolName;
strip2.style.setProperty('--pool-border-color', poolColor);
strip2.style.top = `${oldTop + 30}px`;
strip2.style.left = `${oldLeft + 10}px`;
// Chain the new strips together
strip1.dataset.nextId = id2;
strip2.dataset.prevId = id1;
// If there was a previous strip, connect it to strip1
if (prevId) {
strip1.dataset.prevId = prevId;
const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
if (prevStrip) prevStrip.dataset.nextId = id1;
}
// If there was a next strip, connect it to strip2
if (nextId) {
strip2.dataset.nextId = nextId;
const nextStrip = document.querySelector(`.cutup-strip[data-id="${nextId}"]`);
if (nextStrip) nextStrip.dataset.prevId = id2;
}
// Add delete buttons & drag logic
const deleteBtn1 = document.createElement('span');
deleteBtn1.classList.add('delete-strip-btn');
deleteBtn1.textContent = 'x';
deleteBtn1.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${strip1.textContent}"?`,
deleteStrip,
strip1
);
}
});
strip1.prepend(deleteBtn1);
const deleteBtn2 = document.createElement('span');
deleteBtn2.classList.add('delete-strip-btn');
deleteBtn2.textContent = 'x';
deleteBtn2.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${strip2.textContent}"?`,
deleteStrip,
strip2
);
}
});
strip2.prepend(deleteBtn2);
makeDraggable(strip1);
makeDraggable(strip2);
workbench.appendChild(strip1);
workbench.appendChild(strip2);
updateWordCountMetrics();
// Reposition chain root for layout continuity
// Use the original rootId to reposition the whole chain
const chainRoot = document.querySelector(`.cutup-strip[data-id="${rootId}"]`) || strip1;
console.log(`[splitStrip] Created strip1 id=${id1}, rootId=${strip1.dataset.rootId}; strip2 id=${id2}, rootId=${strip2.dataset.rootId}`);
repositionChain(chainRoot);
}
function createStripOld(text, top, left, poolColor, poolName) {
const strip = document.createElement('div');
const id = generateId();
strip.classList.add('cutup-strip');
strip.textContent = text;
strip.dataset.id = id;
strip.dataset.rootId = id;
strip.dataset.poolColor = poolColor;
strip.dataset.poolName = poolName;
strip.style.setProperty('--pool-border-color', poolColor);
strip.style.top = `${top}px`;
strip.style.left = `${left}px`;
// --- Add Delete Button ---
const deleteBtn = document.createElement('span');
deleteBtn.classList.add('delete-strip-btn');
deleteBtn.textContent = 'x';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${strip.textContent}"?`,
deleteStrip,
strip
);
}
});
strip.prepend(deleteBtn);
// --- End Delete Button ---
makeDraggable(strip);
workbench.appendChild(strip);
updateWordCountMetrics();
return strip;
}
// function createStrip(text, top, left, poolColor, poolName) {
// const strip = document.createElement('div');
// const id = generateId();
// strip.classList.add('cutup-strip');
// strip.dataset.id = id;
// strip.dataset.rootId = id;
// strip.dataset.poolColor = poolColor;
// strip.dataset.poolName = poolName;
// strip.style.setProperty('--pool-border-color', poolColor);
// strip.style.top = `${top}px`;
// strip.style.left = `${left}px`;
// // --- Add Delete Button ---
// const deleteBtn = document.createElement('span');
// deleteBtn.classList.add('delete-strip-btn');
// deleteBtn.textContent = 'x';
// deleteBtn.addEventListener('click', (e) => {
// e.stopPropagation();
// if (isDeletionEnabled) {
// showConfirmationModal(
// 'Delete Strip',
// `Are you sure you want to delete the strip: "${getPhraseText(strip)}"?`,
// deleteStrip,
// strip
// );
// }
// });
// strip.appendChild(deleteBtn);
// // --- Add Editable Phrase ---
// const phraseSpan = document.createElement('span');
// phraseSpan.className = 'strip-text-editable';
// phraseSpan.textContent = text;
// strip.appendChild(phraseSpan);
// // Word count metrics update on edit
// phraseSpan.addEventListener('input', updateWordCountMetrics);
// makeDraggable(strip);
// workbench.appendChild(strip);
// updateWordCountMetrics();
// return strip;
// }
const jumbleAllButton = document.getElementById('jumble-all-btn');
jumbleAllButton.addEventListener('click', jumbleAllStrips);
function getTrueChainRoots() {
const allStrips = Array.from(workbench.querySelectorAll('.cutup-strip'));
const roots = [];
const visited = new Set();
// A chain root is a node with no .dataset.prevId
for (const strip of allStrips) {
if (!strip.dataset.prevId && !visited.has(strip)) {
// Visit all nodes in this chain to mark them as visited
let curr = strip;
while (curr) {
visited.add(curr);
curr = curr.dataset.nextId ? document.querySelector(`.cutup-strip[data-id="${curr.dataset.nextId}"]`) : null;
}
roots.push(strip); // Only leftmost
}
}
return roots;
}
// function jumbleAllStrips() {
// const roots = getTrueChainRoots();
// // Shuffle order for serendipity
// for (let i = roots.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [roots[i], roots[j]] = [roots[j], roots[i]];
// }
// const WORKBENCH_PADDING = 15;
// const H_GAP = 18;
// const V_GAP = 28;
// const WORKBENCH_WIDTH = workbench.offsetWidth - WORKBENCH_PADDING * 2;
// let currX = WORKBENCH_PADDING;
// let currY = WORKBENCH_PADDING;
// let rowHeight = 0;
// const maxWidth = WORKBENCH_WIDTH;
// roots.forEach(root => {
// // Pack chain at a neutral spot for accurate measurement
// root.style.left = '0px';
// root.style.top = '0px';
// repositionChain(root); // Use default pack mode
// // Get width after packing
// const chainWidth = root.offsetWidth;
// const chainHeight = root.offsetHeight;
// if (currX + chainWidth > maxWidth) {
// currX = WORKBENCH_PADDING;
// currY += rowHeight + V_GAP;
// rowHeight = 0;
// }
// root.style.left = `${currX}px`;
// root.style.top = `${currY}px`;
// repositionChain(root); // Use default (packing) mode for jumble!
// currX += chainWidth + H_GAP;
// rowHeight = Math.max(rowHeight, chainHeight);
// });
// // Optionally adjust the workbench min-height to fit
// workbench.style.minHeight = (currY + rowHeight + WORKBENCH_PADDING) + 'px';
// }
function jumbleAllStrips() {
const roots = getTrueChainRoots();
// Shuffle order for serendipity
for (let i = roots.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[roots[i], roots[j]] = [roots[j], roots[i]];
}
const WORKBENCH_PADDING = 15;
const H_GAP = 18;
const V_GAP = 28;
const WORKBENCH_WIDTH = workbench.offsetWidth - WORKBENCH_PADDING * 2;
let currX = WORKBENCH_PADDING;
let currY = WORKBENCH_PADDING;
let rowHeight = 0;
const maxWidth = WORKBENCH_WIDTH;
// --- Helper: calculate full packed width and max height of a chain ---
function measureChainPacked(chainRoot) {
// Temporarily pack the chain at (0, 0) for accurate measurement
chainRoot.style.left = '0px';
chainRoot.style.top = '0px';
repositionChain(chainRoot);
const chain = getChain(chainRoot.dataset.rootId || chainRoot.dataset.id);
let lefts = chain.map(node => parseFloat(node.style.left) || 0);
let rights = chain.map(
(node, i) => (parseFloat(node.style.left) || 0) + node.offsetWidth
);
let tops = chain.map(node => parseFloat(node.style.top) || 0);
let bottoms = chain.map(
(node, i) => (parseFloat(node.style.top) || 0) + node.offsetHeight
);
// Width = rightmost - leftmost
const minX = Math.min(...lefts);
const maxX = Math.max(...rights);
const minY = Math.min(...tops);
const maxY = Math.max(...bottoms);
return {
width: maxX - minX,
height: maxY - minY,
offsetLefts: lefts.map(x => x - minX),
offsetTops: tops.map(y => y - minY),
chain
};
}
roots.forEach(root => {
// Measure the full packed chain dimensions
const measure = measureChainPacked(root);
const chainWidth = measure.width;
const chainHeight = measure.height;
if (currX + chainWidth > maxWidth) {
currX = WORKBENCH_PADDING;
currY += rowHeight + V_GAP;
rowHeight = 0;
}
// Now set the root and all nodes to their new positions, based on offsets
measure.chain.forEach((node, idx) => {
node.style.left = (currX + measure.offsetLefts[idx]) + 'px';
node.style.top = (currY + measure.offsetTops[idx]) + 'px';
});
currX += chainWidth + H_GAP;
rowHeight = Math.max(rowHeight, chainHeight);
});
// Optionally adjust the workbench min-height to fit
workbench.style.minHeight = (currY + rowHeight + WORKBENCH_PADDING) + 'px';
}
function createStrip(text, top, left, poolColor, poolName) {
const strip = document.createElement('div');
const id = generateId();
strip.classList.add('cutup-strip');
strip.dataset.id = id;
strip.dataset.rootId = id;
strip.dataset.poolColor = poolColor;
strip.dataset.poolName = poolName;
strip.style.setProperty('--pool-border-color', poolColor);
strip.style.top = `${top}px`;
strip.style.left = `${left}px`;
// Remove any content first
strip.innerHTML = '';
// Add Delete Button
const deleteBtn = document.createElement('span');
deleteBtn.classList.add('delete-strip-btn');
deleteBtn.textContent = 'x';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${getPhraseText(strip)}"?`,
deleteStrip,
strip
);
}
});
strip.appendChild(deleteBtn);
// Add Editable Phrase
const phraseSpan = document.createElement('span');
phraseSpan.className = 'strip-text-editable';
phraseSpan.textContent = text.trim(); // trim here, too
phraseSpan.addEventListener('input', updateWordCountMetrics);
strip.appendChild(phraseSpan);
if (isDeletionEnabled) {
strip.classList.add('has-delete-btn');
} else {
strip.classList.remove('has-delete-btn');
}
makeDraggable(strip);
workbench.appendChild(strip);
updateWordCountMetrics();
return strip;
}
function splitStrip(strip) {
const text = getPhraseText(strip);
const words = text.split(' ').filter(w => w.length > 0);
if (words.length < 2) return;
const splitIndex = Math.floor(words.length / 2);
const phrase1 = words.slice(0, splitIndex).join(' ');
const phrase2 = words.slice(splitIndex).join(' ');
const oldTop = strip.offsetTop;
const oldLeft = strip.offsetLeft;
const poolColor = strip.dataset.poolColor;
const poolName = strip.dataset.poolName;
const rootId = strip.dataset.rootId || strip.dataset.id;
const prevId = strip.dataset.prevId;
const nextId = strip.dataset.nextId;
// Remove original strip from DOM
strip.remove();
// First split node
const strip1 = document.createElement('div');
const id1 = generateId();
strip1.classList.add('cutup-strip');
strip1.dataset.id = id1;
strip1.dataset.rootId = id1;
strip1.dataset.poolColor = poolColor;
strip1.dataset.poolName = poolName;
strip1.style.setProperty('--pool-border-color', poolColor);
strip1.style.top = `${oldTop}px`;
strip1.style.left = `${oldLeft - 10}px`;
const deleteBtn1 = document.createElement('span');
deleteBtn1.classList.add('delete-strip-btn');
deleteBtn1.textContent = 'x';
deleteBtn1.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${getPhraseText(strip1)}"?`,
deleteStrip,
strip1
);
}
});
strip1.appendChild(deleteBtn1);
const phraseSpan1 = document.createElement('span');
phraseSpan1.className = 'strip-text-editable';
phraseSpan1.textContent = phrase1;
phraseSpan1.addEventListener('input', updateWordCountMetrics);
strip1.appendChild(phraseSpan1);
// Second split node
const strip2 = document.createElement('div');
const id2 = generateId();
strip2.classList.add('cutup-strip');
strip2.dataset.id = id2;
strip2.dataset.rootId = id1;
strip2.dataset.poolColor = poolColor;
strip2.dataset.poolName = poolName;
strip2.style.setProperty('--pool-border-color', poolColor);
strip2.style.top = `${oldTop + 30}px`;
strip2.style.left = `${oldLeft + 10}px`;
const deleteBtn2 = document.createElement('span');
deleteBtn2.classList.add('delete-strip-btn');
deleteBtn2.textContent = 'x';
deleteBtn2.addEventListener('click', (e) => {
e.stopPropagation();
if (isDeletionEnabled) {
showConfirmationModal(
'Delete Strip',
`Are you sure you want to delete the strip: "${getPhraseText(strip2)}"?`,
deleteStrip,
strip2
);
}
});
strip2.appendChild(deleteBtn2);
const phraseSpan2 = document.createElement('span');
phraseSpan2.className = 'strip-text-editable';
phraseSpan2.textContent = phrase2;
phraseSpan2.addEventListener('input', updateWordCountMetrics);
strip2.appendChild(phraseSpan2);
strip1.dataset.nextId = id2;
strip2.dataset.prevId = id1;
if (prevId) {
strip1.dataset.prevId = prevId;
const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
if (prevStrip) prevStrip.dataset.nextId = id1;
}
if (nextId) {
strip2.dataset.nextId = nextId;
const nextStrip = document.querySelector(`.cutup-strip[data-id="${nextId}"]`);
if (nextStrip) nextStrip.dataset.prevId = id2;
}
makeDraggable(strip1);
makeDraggable(strip2);
workbench.appendChild(strip1);
workbench.appendChild(strip2);
updateWordCountMetrics();
const chainRoot = document.querySelector(`.cutup-strip[data-id="${rootId}"]`) || strip1;
repositionChain(chainRoot);
}
function generateCutUps() {
const pool = pools[currentPoolIndex];
const shuffled = pool.phrases.sort(() => 0.5 - Math.random());
const phrasesToUse = shuffled.slice(0, 8 + Math.floor(Math.random() * 5));
// Define boundaries based on workbench size to avoid spawning too close to the edge
const WORKBENCH_WIDTH = workbench.offsetWidth;
// Start strips well into the padded area of the workbench (below the header)
const MIN_TOP = 50;
const MAX_TOP = 400;
// Max left is Workbench width - 100 (for strip width buffer)
const MAX_LEFT = WORKBENCH_WIDTH - 100;
phrasesToUse.forEach((phrase) => {
const initialTop = MIN_TOP + Math.random() * (MAX_TOP - MIN_TOP);
const initialLeft = 20 + Math.random() * (MAX_LEFT - 20);
createStrip(phrase, initialTop, initialLeft, pool.color, pool.name);
});
}
function clearAllStripsConfirmed() {
const strips = workbench.querySelectorAll('.cutup-strip');
strips.forEach(strip => strip.remove());
workbench.style.minHeight = '100vh';
updateWordCountMetrics();
}
// --- Custom Input Logic ---
function showCustomInput(x, y) {
customInputCoords = { x, y };
// Calculate position relative to document (for absolute positioning)
const posX = x + window.scrollX;
const posY = y + window.scrollY;
// Position the overlay relative to the document body
customInputOverlay.style.left = `${posX}px`;
customInputOverlay.style.top = `${posY}px`;
customInputOverlay.style.display = 'block';
customTextInput.focus();
}
function hideCustomInput() {
customInputOverlay.style.display = 'none';
customTextInput.value = '';
}
function addCustomStrips() {
const text = customTextInput.value.trim();
if (!text) {
hideCustomInput();
return;
}
const phrases = text.split('\n').map(p => p.trim()).filter(p => p.length > 0);
// Get the workbench's current position to calculate strip placement
const workbenchRect = workbench.getBoundingClientRect();
// Calculate starting coordinates relative to the workbench's top/left (which is now in the document flow)
// customInputCoords are in VP, so convert them to document-relative and then to workbench-relative
const startX = (customInputCoords.x + window.scrollX) - (workbench.offsetLeft + document.body.offsetLeft);
const startY = (customInputCoords.y + window.scrollY) - workbench.offsetTop;
phrases.forEach((phrase, index) => {
createStrip(
phrase,
startY + (index * 40),
startX + (index * 10),
USER_INPUT_POOL.color,
USER_INPUT_POOL.name
);
});
hideCustomInput();
}
// function detachLeftAndReRoot(strip) {
// const prevId = strip.dataset.prevId;
// if (prevId) {
// // Unlink from the left
// const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
// if (prevStrip) delete prevStrip.dataset.nextId;
// delete strip.dataset.prevId;
// // This strip (and all to its right) become a new chain
// // Use this strip's id as the new rootId
// let current = strip;
// while (current) {
// current.dataset.rootId = strip.dataset.id;
// // Move right along the chain
// const nextId = current.dataset.nextId;
// current = nextId ? document.querySelector(`.cutup-strip[data-id="${nextId}"]`) : null;
// }
// }
// // If no prevId, nothing to detach; already a root
// }
function detachLeftAndReRoot(strip) {
const prevId = strip.dataset.prevId;
const nextId = strip.dataset.nextId;
if (prevId) {
// Unlink from the left
const prevStrip = document.querySelector(`.cutup-strip[data-id="${prevId}"]`);
if (prevStrip) delete prevStrip.dataset.nextId;
delete strip.dataset.prevId;
// This strip (and all to its right) become a new chain
let current = strip;
while (current) {
current.dataset.rootId = strip.dataset.id;
const nextId = current.dataset.nextId;
current = nextId ? document.querySelector(`.cutup-strip[data-id="${nextId}"]`) : null;
}
} else if (nextId) {
// If head of chain but has a nextId, break chain here
const nextStrip = document.querySelector(`.cutup-strip[data-id="${nextId}"]`);
if (nextStrip) {
delete nextStrip.dataset.prevId;
nextStrip.dataset.rootId = nextStrip.dataset.id;
// Update all rightward nodes' rootId
let curr = nextStrip;
while (curr) {
curr.dataset.rootId = nextStrip.dataset.id;
curr = curr.dataset.nextId ? document.querySelector(`.cutup-strip[data-id="${curr.dataset.nextId}"]`) : null;
}
}
delete strip.dataset.nextId;
strip.dataset.rootId = strip.dataset.id;
}
// If no prevId and no nextId: nothing to detach (already solo)
}
// --- Edit Node Modal Logic ---
const editStripModal = document.getElementById('edit-strip-modal');
const editStripTextarea = document.getElementById('edit-strip-textarea');
const editStripSaveBtn = document.getElementById('edit-strip-save-btn');
const editStripCancelBtn = document.getElementById('edit-strip-cancel-btn');
let currentlyEditingStrip = null;
function showEditStripModal(strip) {
currentlyEditingStrip = strip;
editStripTextarea.value = getPhraseText(strip);
editStripModal.style.display = 'flex';
setTimeout(() => editStripTextarea.focus(), 50);
}
function hideEditStripModal() {
editStripModal.style.display = 'none';
currentlyEditingStrip = null;
}
editStripCancelBtn.addEventListener('click', hideEditStripModal);
editStripModal.addEventListener('click', (e) => {
if (e.target === editStripModal) hideEditStripModal();
});
// editStripSaveBtn.addEventListener('click', () => {
// if (!currentlyEditingStrip) return;
// setPhraseText(currentlyEditingStrip, editStripTextarea.value);
// updateWordCountMetrics();
// hideEditStripModal();
// });
// function handleAdvancedEditSplit(strip, original, edited) {
// if (original === edited) return; // No change
// // Tokenize both
// const originalWords = original.split(/\s+/);
// const editedWords = edited.split(/\s+/);
// // Find common prefix
// let prefixLen = 0;
// while (
// prefixLen < originalWords.length &&
// prefixLen < editedWords.length &&
// originalWords[prefixLen] === editedWords[prefixLen]
// ) {
// prefixLen++;
// }
// // Find common suffix
// let suffixLen = 0;
// while (
// suffixLen < originalWords.length - prefixLen &&
// suffixLen < editedWords.length - prefixLen &&
// originalWords[originalWords.length - 1 - suffixLen] === editedWords[editedWords.length - 1 - suffixLen]
// ) {
// suffixLen++;
// }
// // Parts:
// // left = originalWords.slice(0, prefixLen)
// // inserted = editedWords.slice(prefixLen, editedWords.length - suffixLen)
// // right = originalWords.slice(originalWords.length - suffixLen) if suffixLen > 0
// const leftText = originalWords.slice(0, prefixLen).join(' ');
// const userInputText = editedWords.slice(prefixLen, editedWords.length - suffixLen).join(' ');
// const rightText = originalWords.slice(originalWords.length - suffixLen).join(' ');
// const poolColor = strip.dataset.poolColor;
// const poolName = strip.dataset.poolName;
// const top = strip.offsetTop;
// const left = strip.offsetLeft;
// // Remove the original strip
// strip.remove();
// let prevNode = null;
// let firstNode = null;
// // Helper: create and insert a node, return reference
// function insertNode(text, color, name, x, y) {
// if (!text || !text.trim()) return null;
// const n = createStrip(text, y, x, color, name);
// if (!firstNode) firstNode = n;
// if (prevNode) {
// prevNode.dataset.nextId = n.dataset.id;
// n.dataset.prevId = prevNode.dataset.id;
// }
// prevNode = n;
// return n;
// }
// // Create left node (original color)
// let currLeft = insertNode(leftText, poolColor, poolName, left, top);
// // Create user-input node (yellow)
// let currUser = insertNode(userInputText, USER_INPUT_POOL.color, USER_INPUT_POOL.name, left + 24, top + 28);
// // Create right node (original color)
// let currRight = insertNode(rightText, poolColor, poolName, left + 48, top + 56);
// // Fix rootId for the chain (first node is root)
// let rootId = firstNode ? firstNode.dataset.id : null;
// [currLeft, currUser, currRight].forEach(n => {
// if (n) n.dataset.rootId = rootId;
// });
// // Chain together (already done in insertNode), now fix prev/next for any missing ones
// if (currLeft && currUser) {
// currLeft.dataset.nextId = currUser.dataset.id;
// currUser.dataset.prevId = currLeft.dataset.id;
// }
// if (currUser && currRight) {
// currUser.dataset.nextId = currRight.dataset.id;
// currRight.dataset.prevId = currUser.dataset.id;
// }
// // Positioning: first node at left/top, others offset for clarity (done above)
// // Reposition chain for nice layout
// if (firstNode) repositionChain(firstNode);
// }
function mergeAdjacentSameColorNodes(chainRoot) {
let changed = false;
let node = chainRoot;
while (node) {
let next = node.dataset.nextId
? document.querySelector(`.cutup-strip[data-id="${node.dataset.nextId}"]`)
: null;
if (
next &&
node.dataset.poolColor === next.dataset.poolColor &&
node.dataset.poolName === next.dataset.poolName
) {
// Merge text
const nodeText = getPhraseText(node);
const nextText = getPhraseText(next);
const newText = (nodeText + ' ' + nextText).trim();
// Update current node
setPhraseText(node, newText);
// Fix links
node.dataset.nextId = next.dataset.nextId || '';
if (next.dataset.nextId) {
const nextNext = document.querySelector(`.cutup-strip[data-id="${next.dataset.nextId}"]`);
if (nextNext) nextNext.dataset.prevId = node.dataset.id;
}
// Remove merged node from DOM
next.remove();
changed = true;
// Do not advance node pointer, check for further merges
} else {
node = next;
}
}
return changed;
}
// function handleAdvancedEditSplit(strip, original, edited) {
// if (original === edited) return; // No change
// // Tokenize both
// const originalWords = original.trim().split(/\s+/);
// const editedWords = edited.trim().split(/\s+/);
// const poolColor = strip.dataset.poolColor;
// const poolName = strip.dataset.poolName;
// const top = strip.offsetTop;
// const left = strip.offsetLeft;
// // Remove the original strip
// strip.remove();
// let prevNode = null;
// let firstNode = null;
// let posX = left, posY = top;
// const dx = 24, dy = 28; // offset for visual stacking
// // Find LCS to identify unchanged words
// function computeLCSMatrix(a, b) {
// const m = a.length, n = b.length;
// const dp = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0));
// for (let i = 0; i < m; ++i) {
// for (let j = 0; j < n; ++j) {
// if (a[i] === b[j]) dp[i + 1][j + 1] = dp[i][j] + 1;
// else dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);
// }
// }
// return dp;
// }
// function diffLCS(a, b) {
// const m = a.length, n = b.length;
// const dp = computeLCSMatrix(a, b);
// let actions = [];
// let i = m, j = n;
// while (i > 0 && j > 0) {
// if (a[i - 1] === b[j - 1]) {
// actions.unshift({ word: b[j - 1], type: 'original' });
// i--; j--;
// } else if (dp[i][j - 1] >= dp[i - 1][j]) {
// actions.unshift({ word: b[j - 1], type: 'edit' });
// j--;
// } else {
// i--;
// }
// }
// while (j > 0) {
// actions.unshift({ word: b[j - 1], type: 'edit' });
// j--;
// }
// // (Deletions from original are ignored for split purposes.)
// return actions;
// }
// // 1. Diff to get per-word action list (as in your current version)
// const actions = diffLCS(originalWords, editedWords);
// // 2. Merge adjacent same-type actions into phrase groups
// let merged = [];
// for (let i = 0; i < actions.length; ++i) {
// const { word, type } = actions[i];
// if (merged.length === 0 || merged[merged.length - 1].type !== type) {
// merged.push({ type, words: [word] });
// } else {
// merged[merged.length - 1].words.push(word);
// }
// }
// // 3. Generate new strip nodes, one per phrase group
// function insertNode(text, color, name, x, y) {
// if (!text || !text.trim()) return null;
// const n = createStrip(text, y, x, color, name);
// if (!firstNode) firstNode = n;
// if (prevNode) {
// prevNode.dataset.nextId = n.dataset.id;
// n.dataset.prevId = prevNode.dataset.id;
// }
// prevNode = n;
// return n;
// }
// merged.forEach((item, idx) => {
// const color = item.type === 'original' ? poolColor : USER_INPUT_POOL.color;
// const name = item.type === 'original' ? poolName : USER_INPUT_POOL.name;
// insertNode(item.words.join(' '), color, name, posX + dx * idx, posY + dy * idx);
// });
// // Fix rootId for the chain (first node is root)
// let rootId = firstNode ? firstNode.dataset.id : null;
// let node = firstNode;
// while (node) {
// node.dataset.rootId = rootId;
// node = node.dataset.nextId ? document.querySelector(`.cutup-strip[data-id="${node.dataset.nextId}"]`) : null;
// }
// // Reposition chain for nice layout
// if (firstNode) repositionChain(firstNode);
// }
function handleAdvancedEditSplit(strip, original, edited) {
if (original === edited) return; // No change
// Tokenize both
const originalWords = original.trim().split(/\s+/);
const editedWords = edited.trim().split(/\s+/);
const poolColor = strip.dataset.poolColor;
const poolName = strip.dataset.poolName;
const top = strip.offsetTop;
const left = strip.offsetLeft;
// Remove the original strip
strip.remove();
let prevNode = null;
let firstNode = null;
let posX = left, posY = top;
const dx = 24, dy = 28; // offset for visual stacking
// Find LCS to identify unchanged words
function computeLCSMatrix(a, b) {
const m = a.length, n = b.length;
const dp = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0));
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (a[i] === b[j]) dp[i + 1][j + 1] = dp[i][j] + 1;
else dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);
}
}
return dp;
}
function diffLCS(a, b) {
const m = a.length, n = b.length;
const dp = computeLCSMatrix(a, b);
let actions = [];
let i = m, j = n;
while (i > 0 && j > 0) {
if (a[i - 1] === b[j - 1]) {
actions.unshift({ word: b[j - 1], type: 'original' });
i--; j--;
} else if (dp[i][j - 1] >= dp[i - 1][j]) {
actions.unshift({ word: b[j - 1], type: 'edit' });
j--;
} else {
i--;
}
}
while (j > 0) {
actions.unshift({ word: b[j - 1], type: 'edit' });
j--;
}
// (Deletions from original are ignored for split purposes.)
return actions;
}
// 1. Diff to get per-word action list
const actions = diffLCS(originalWords, editedWords);
// 2. Merge adjacent same-type actions into phrase groups
let merged = [];
for (let i = 0; i < actions.length; ++i) {
const { word, type } = actions[i];
if (merged.length === 0 || merged[merged.length - 1].type !== type) {
merged.push({ type, words: [word] });
} else {
merged[merged.length - 1].words.push(word);
}
}
// 3. Generate new strip nodes, one per phrase group
function insertNode(text, color, name, x, y) {
if (!text || !text.trim()) return null;
const n = createStrip(text, y, x, color, name);
if (!firstNode) firstNode = n;
if (prevNode) {
prevNode.dataset.nextId = n.dataset.id;
n.dataset.prevId = prevNode.dataset.id;
}
prevNode = n;
return n;
}
merged.forEach((item, idx) => {
const color = item.type === 'original' ? poolColor : USER_INPUT_POOL.color;
const name = item.type === 'original' ? poolName : USER_INPUT_POOL.name;
insertNode(item.words.join(' '), color, name, posX + dx * idx, posY + dy * idx);
});
// Fix rootId for the chain (first node is root)
let rootId = firstNode ? firstNode.dataset.id : null;
let node = firstNode;
while (node) {
node.dataset.rootId = rootId;
node = node.dataset.nextId ? document.querySelector(`.cutup-strip[data-id="${node.dataset.nextId}"]`) : null;
}
// Merge adjacent nodes with same color/border
mergeAdjacentSameColorNodes(firstNode);
// Reposition chain for nice layout
if (firstNode) repositionChain(firstNode);
}
editStripSaveBtn.addEventListener('click', () => {
if (!currentlyEditingStrip) return;
const original = getPhraseText(currentlyEditingStrip);
const edited = editStripTextarea.value.trim();
// Only do the new splitting/insert for non-user-input nodes
if (currentlyEditingStrip.dataset.poolName !== USER_INPUT_POOL.name) {
handleAdvancedEditSplit(currentlyEditingStrip, original, edited);
} else {
setPhraseText(currentlyEditingStrip, edited);
}
updateWordCountMetrics();
hideEditStripModal();
});
// --- Context Menu for Chained Node Double-click ---
const stripContextMenu = document.getElementById('strip-context-menu');
const contextEditBtn = document.getElementById('context-edit-btn');
const contextDetachBtn = document.getElementById('context-detach-btn');
let contextTargetStrip = null;
function showStripContextMenu(strip, x, y) {
contextTargetStrip = strip;
stripContextMenu.style.display = 'block';
stripContextMenu.style.left = x + 'px';
stripContextMenu.style.top = y + 'px';
}
function hideStripContextMenu() {
stripContextMenu.style.display = 'none';
contextTargetStrip = null;
}
document.addEventListener('click', (e) => {
if (stripContextMenu.style.display === 'block' && !stripContextMenu.contains(e.target)) {
hideStripContextMenu();
}
});
contextEditBtn.addEventListener('click', (e) => {
if (contextTargetStrip) showEditStripModal(contextTargetStrip);
hideStripContextMenu();
});
contextDetachBtn.addEventListener('click', (e) => {
if (contextTargetStrip) detachLeftAndReRoot(contextTargetStrip);
hideStripContextMenu();
});
// --- Drag and Drop Logic (with double-tap-hold gesture) ---
function makeDraggable(strip) {
let activeDragStrip = null; // The strip (node) currently being dragged
let dragRoot = null; // The root of the chain being dragged, or just activeDragStrip in detach mode
let dragMode = 'chain'; // 'chain' or 'single'
let xOffset = 0;
let yOffset = 0;
let zIndexCounterLocal = 100; // To prevent z-index conflicts
let lastTap = 0;
let tapTimeout = null;
// --- New: For block dragging of chain ---
let chainOffsets = null;
function getDragElements() {
if (dragMode === 'chain') {
return getChain(dragRoot.dataset.rootId || dragRoot.dataset.id);
} else {
return [activeDragStrip];
}
}
function dragStart(e) {
// If you double-clicked and the handler did not prevent default, dragStart will get called. Avoid drag on modal open.
if (strip.dataset.editing === 'true') {
return;
}
if (e.target.classList.contains('delete-strip-btn')) return;
if (e.button !== undefined && e.button !== 0) return; // Only left mouse
e.preventDefault();
// If tapTimeout is still running, this is a double click (for mouse)
if (tapTimeout) {
// Do nothing here -- double-click is handled in the dblclick handler below
clearTimeout(tapTimeout);
tapTimeout = null;
return;
} else {
dragMode = 'chain';
tapTimeout = setTimeout(() => {
tapTimeout = null;
}, 300);
}
activeDragStrip = strip;
if (dragMode === 'chain') {
dragRoot = getChainRoot(strip);
if (!dragRoot) dragRoot = strip;
// Compute block offsets for all nodes in the chain
const chain = getChain(dragRoot.dataset.rootId || dragRoot.dataset.id);
const rootLeft = parseFloat(dragRoot.style.left) || 0;
const rootTop = parseFloat(dragRoot.style.top) || 0;
chainOffsets = {};
chain.forEach(node => {
chainOffsets[node.dataset.id] = {
dx: (parseFloat(node.style.left) || 0) - rootLeft,
dy: (parseFloat(node.style.top) || 0) - rootTop,
node: node
};
});
} else {
dragRoot = strip;
chainOffsets = null;
}
dragRoot.style.zIndex = ++zIndexCounterLocal;
activeDragStrip.classList.add('active');
// Determine pointer position
let clientX, clientY;
if (e.type.startsWith('touch')) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
} else {
clientX = e.clientX;
clientY = e.clientY;
}
const dragRect = dragRoot.getBoundingClientRect();
xOffset = clientX - dragRect.left;
yOffset = clientY - dragRect.top;
document.addEventListener('mousemove', dragMove, { passive: false });
document.addEventListener('mouseup', dragEnd, { passive: false });
document.addEventListener('touchmove', dragMove, { passive: false });
document.addEventListener('touchend', dragEnd, { passive: false });
document.addEventListener('touchcancel', dragEnd, { passive: false });
}
function dragMove(e) {
if (!activeDragStrip) return;
e.preventDefault();
let clientX, clientY;
if (e.type.startsWith('touch')) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
} else {
clientX = e.clientX;
clientY = e.clientY;
}
const workbenchRect = workbench.getBoundingClientRect();
let newX = clientX - xOffset - workbenchRect.left;
let newY = clientY - yOffset - workbenchRect.top;
newX = Math.max(0, newX);
newY = Math.max(0, newY);
if (dragMode === 'chain' && chainOffsets) {
// Move all nodes in the chain using their offset to root
Object.values(chainOffsets).forEach(({ dx, dy, node }) => {
node.style.left = (newX + dx) + 'px';
node.style.top = (newY + dy) + 'px';
});
} else {
dragRoot.style.left = newX + 'px';
dragRoot.style.top = newY + 'px';
}
}
function dragEnd(e) {
if (!activeDragStrip) return;
activeDragStrip.classList.remove('active');
const movingElem = dragRoot;
const movingRect = movingElem.getBoundingClientRect();
let potentialTarget = null;
let mergeType = null;
const allStrips = document.querySelectorAll('.cutup-strip');
for (const targetStrip of allStrips) {
if (targetStrip === movingElem) continue;
if (dragMode === 'chain' && getChainRoot(targetStrip) === movingElem) continue;
if (dragMode === 'chain' && getChainRoot(targetStrip) === getChainRoot(movingElem)) continue;
if (dragMode === 'single' && targetStrip === activeDragStrip) continue;
const targetRect = targetStrip.getBoundingClientRect();
const isOverlapping = (
movingRect.left < targetRect.right && movingRect.right > targetRect.left &&
movingRect.top < targetRect.bottom && movingRect.bottom > targetRect.top
);
if (isOverlapping) {
const targetMidpointX = targetRect.left + (targetRect.width / 2);
const movingCenterX = movingRect.left + (movingRect.width / 2);
if (movingCenterX < targetMidpointX) {
potentialTarget = targetStrip;
mergeType = 'left';
break;
} else {
potentialTarget = targetStrip;
mergeType = 'right';
break;
}
}
}
if (potentialTarget && mergeType) {
if (dragMode === 'chain') {
linkChains(getChainRoot(movingElem), potentialTarget, mergeType);
} else {
linkChains(movingElem, potentialTarget, mergeType);
}
}
activeDragStrip = null;
dragRoot = null;
dragMode = 'chain';
chainOffsets = null; // <- Clear offsets!
document.removeEventListener('mousemove', dragMove, { passive: false });
document.removeEventListener('mouseup', dragEnd, { passive: false });
document.removeEventListener('touchmove', dragMove, { passive: false });
document.removeEventListener('touchend', dragEnd, { passive: false });
document.removeEventListener('touchcancel', dragEnd, { passive: false });
}
// --- Double click contextual editing or detach ---
function isNodeChained(strip) {
return !!(strip.dataset.prevId || strip.dataset.nextId);
}
strip.addEventListener('dblclick', function (e) {
e.preventDefault();
if (!isNodeChained(strip)) {
showEditStripModal(strip);
} else {
let x = (e.clientX !== undefined) ? e.clientX : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
let y = (e.clientY !== undefined) ? e.clientY : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
showStripContextMenu(strip, x, y);
}
});
// Drag-and-drop as usual
strip.addEventListener('mousedown', dragStart);
strip.addEventListener('touchstart', function (e) {
if (e.target.classList.contains('delete-strip-btn')) return;
const now = Date.now();
if (now - lastTap < 300) {
// Double tap: same as double click -- context menu or edit
if (!isNodeChained(strip)) {
showEditStripModal(strip);
} else {
let x = (e.touches[0]?.clientX !== undefined)
? e.touches[0].clientX
: (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
let y = (e.touches[0]?.clientY !== undefined)
? e.touches[0].clientY
: (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
showStripContextMenu(strip, x, y);
}
lastTap = 0;
e.preventDefault();
return;
}
lastTap = now;
dragMode = 'chain';
dragStart(e);
}, { passive: false });
}
// function makeDraggable(strip) {
// let activeDragStrip = null; // The strip (node) currently being dragged
// let dragRoot = null; // The root of the chain being dragged, or just activeDragStrip in detach mode
// let dragMode = 'chain'; // 'chain' or 'single'
// let xOffset = 0;
// let yOffset = 0;
// let zIndexCounterLocal = 100; // To prevent z-index conflicts
// let lastTap = 0;
// let tapTimeout = null;
// // --- Helper: Get chain or single for drag ---
// function getDragElements() {
// if (dragMode === 'chain') {
// return getChain(dragRoot.dataset.rootId || dragRoot.dataset.id);
// } else {
// return [activeDragStrip];
// }
// }
// function dragStart(e) {
// // If you double-clicked and the handler did not prevent default, dragStart will get called. Avoid drag on modal open.
// if (strip.dataset.editing === 'true') {
// return;
// }
// if (e.target.classList.contains('delete-strip-btn')) return;
// if (e.button !== undefined && e.button !== 0) return; // Only left mouse
// e.preventDefault();
// // If tapTimeout is still running, this is a double click (for mouse)
// if (tapTimeout) {
// // Do nothing here -- double-click is handled in the dblclick handler below
// clearTimeout(tapTimeout);
// tapTimeout = null;
// return;
// } else {
// dragMode = 'chain';
// tapTimeout = setTimeout(() => {
// tapTimeout = null;
// }, 300);
// }
// activeDragStrip = strip;
// // Find dragRoot safely
// if (dragMode === 'chain') {
// dragRoot = getChainRoot(strip);
// if (!dragRoot) {
// dragRoot = strip;
// }
// } else {
// dragRoot = strip;
// }
// // Only act if dragRoot exists!
// if (dragRoot) {
// dragRoot.style.zIndex = ++zIndexCounterLocal;
// activeDragStrip.classList.add('active');
// } else {
// return;
// }
// // Determine pointer position
// let clientX, clientY;
// if (e.type.startsWith('touch')) {
// clientX = e.touches[0].clientX;
// clientY = e.touches[0].clientY;
// } else {
// clientX = e.clientX;
// clientY = e.clientY;
// }
// const dragRect = dragRoot.getBoundingClientRect();
// xOffset = clientX - dragRect.left;
// yOffset = clientY - dragRect.top;
// document.addEventListener('mousemove', dragMove, { passive: false });
// document.addEventListener('mouseup', dragEnd, { passive: false });
// document.addEventListener('touchmove', dragMove, { passive: false });
// document.addEventListener('touchend', dragEnd, { passive: false });
// document.addEventListener('touchcancel', dragEnd, { passive: false });
// }
// function dragMove(e) {
// if (!activeDragStrip) return;
// e.preventDefault();
// let clientX, clientY;
// if (e.type.startsWith('touch')) {
// clientX = e.touches[0].clientX;
// clientY = e.touches[0].clientY;
// } else {
// clientX = e.clientX;
// clientY = e.clientY;
// }
// const workbenchRect = workbench.getBoundingClientRect();
// let newX = clientX - xOffset - workbenchRect.left;
// let newY = clientY - yOffset - workbenchRect.top;
// newX = Math.max(0, newX);
// newY = Math.max(0, newY);
// if (dragMode === 'chain') {
// dragRoot.style.left = newX + 'px';
// dragRoot.style.top = newY + 'px';
// repositionChain(dragRoot, true)
// } else {
// dragRoot.style.left = newX + 'px';
// dragRoot.style.top = newY + 'px';
// }
// }
// function dragEnd(e) {
// if (!activeDragStrip) return;
// activeDragStrip.classList.remove('active');
// const movingElem = dragRoot;
// const movingRect = movingElem.getBoundingClientRect();
// let potentialTarget = null;
// let mergeType = null;
// const allStrips = document.querySelectorAll('.cutup-strip');
// for (const targetStrip of allStrips) {
// if (targetStrip === movingElem) continue;
// if (dragMode === 'chain' && getChainRoot(targetStrip) === movingElem) continue;
// if (dragMode === 'chain' && getChainRoot(targetStrip) === getChainRoot(movingElem)) continue;
// if (dragMode === 'single' && targetStrip === activeDragStrip) continue;
// const targetRect = targetStrip.getBoundingClientRect();
// const isOverlapping = (
// movingRect.left < targetRect.right && movingRect.right > targetRect.left &&
// movingRect.top < targetRect.bottom && movingRect.bottom > targetRect.top
// );
// if (isOverlapping) {
// const targetMidpointX = targetRect.left + (targetRect.width / 2);
// const movingCenterX = movingRect.left + (movingRect.width / 2);
// if (movingCenterX < targetMidpointX) {
// potentialTarget = targetStrip;
// mergeType = 'left';
// break;
// } else {
// potentialTarget = targetStrip;
// mergeType = 'right';
// break;
// }
// }
// }
// if (potentialTarget && mergeType) {
// if (dragMode === 'chain') {
// linkChains(getChainRoot(movingElem), potentialTarget, mergeType);
// } else {
// linkChains(movingElem, potentialTarget, mergeType);
// }
// }
// activeDragStrip = null;
// dragRoot = null;
// dragMode = 'chain';
// document.removeEventListener('mousemove', dragMove, { passive: false });
// document.removeEventListener('mouseup', dragEnd, { passive: false });
// document.removeEventListener('touchmove', dragMove, { passive: false });
// document.removeEventListener('touchend', dragEnd, { passive: false });
// document.removeEventListener('touchcancel', dragEnd, { passive: false });
// }
// // --- Double click contextual editing or detach ---
// // strip.addEventListener('dblclick', function (e) {
// // e.preventDefault();
// // // If not attached (no prevId/nextId), edit directly
// // if (!strip.dataset.prevId && !strip.dataset.nextId) {
// // showEditStripModal(strip);
// // return;
// // }
// // // If attached, show context menu to choose
// // let x = (e.clientX !== undefined) ? e.clientX : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
// // let y = (e.clientY !== undefined) ? e.clientY : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
// // showStripContextMenu(strip, x, y);
// // });
// function isNodeChained(strip) {
// return !!(strip.dataset.prevId || strip.dataset.nextId);
// }
// strip.addEventListener('dblclick', function (e) {
// e.preventDefault();
// // If the node is completely alone (no chain at all), edit.
// if (!isNodeChained(strip)) {
// showEditStripModal(strip);
// } else {
// let x = (e.clientX !== undefined) ? e.clientX : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
// let y = (e.clientY !== undefined) ? e.clientY : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
// showStripContextMenu(strip, x, y);
// }
// // if (!strip.dataset.prevId && !strip.dataset.nextId) {
// // showEditStripModal(strip);
// // } else {
// // // If the node is at the head or tail of a chain, or in the middle,
// // // show the context menu regardless (so first/last links work)
// // let x = (e.clientX !== undefined) ? e.clientX : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
// // let y = (e.clientY !== undefined) ? e.clientY : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
// // showStripContextMenu(strip, x, y);
// // }
// });
// // Drag-and-drop as usual
// strip.addEventListener('mousedown', dragStart);
// strip.addEventListener('touchstart', function (e) {
// if (e.target.classList.contains('delete-strip-btn')) return;
// const now = Date.now();
// if (now - lastTap < 300) {
// // Double tap: same as double click -- context menu or edit
// if (!isNodeChained(strip)) {
// showEditStripModal(strip);
// } else {
// let x = (e.touches[0]?.clientX !== undefined)
// ? e.touches[0].clientX
// : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
// let y = (e.touches[0]?.clientY !== undefined)
// ? e.touches[0].clientY
// : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
// showStripContextMenu(strip, x, y);
// }
// // if (!strip.dataset.prevId && !strip.dataset.nextId) {
// // showEditStripModal(strip);
// // } else {
// // let x = (e.touches[0]?.clientX !== undefined)
// // ? e.touches[0].clientX
// // : (strip.getBoundingClientRect().left + strip.offsetWidth / 2);
// // let y = (e.touches[0]?.clientY !== undefined)
// // ? e.touches[0].clientY
// // : (strip.getBoundingClientRect().top + strip.offsetHeight / 2);
// // showStripContextMenu(strip, x, y);
// // }
// lastTap = 0;
// e.preventDefault();
// return;
// }
// lastTap = now;
// dragMode = 'chain';
// dragStart(e);
// }, { passive: false });
// }
// function makeDraggableOld(strip) {
// let activeDragStrip = null; // The strip (node) currently being dragged
// let dragRoot = null; // The root of the chain being dragged, or just activeDragStrip in detach mode
// let dragMode = 'chain'; // 'chain' or 'single'
// let xOffset = 0;
// let yOffset = 0;
// let zIndexCounterLocal = 100; // To prevent z-index conflicts
// let lastTap = 0;
// let tapTimeout = null;
// // --- Helper: Get chain or single for drag ---
// function getDragElements() {
// if (dragMode === 'chain') {
// // Drag entire chain (default)
// return getChain(dragRoot.dataset.rootId || dragRoot.dataset.id);
// } else {
// // Drag only single node
// return [activeDragStrip];
// }
// }
// // --- Pointer (mouse/touch) logic ---
// function dragStart(e) {
// console.log(`[dragStart] Drag initiated. Clicked strip id=${strip.dataset.id}, dragMode=${dragMode}, dragRoot id=${dragRoot ? dragRoot.dataset.id : 'null'}`);
// if (e.target.classList.contains('delete-strip-btn')) return;
// if (e.button !== undefined && e.button !== 0) return; // Only left mouse
// e.preventDefault();
// // If tapTimeout is still running, this is a double click (for mouse)
// if (tapTimeout) {
// // Double click → detach
// clearTimeout(tapTimeout);
// tapTimeout = null;
// dragMode = 'single';
// // Unlink, then start drag with just this node
// unlinkStrip(strip);
// } else {
// // Not double click. Start timer in case it's a double click
// dragMode = 'chain';
// tapTimeout = setTimeout(() => {
// tapTimeout = null;
// }, 300);
// }
// activeDragStrip = strip;
// // Find dragRoot safely
// if (dragMode === 'chain') {
// dragRoot = getChainRoot(strip);
// // If chain root couldn't be found (shouldn't happen, but be robust),
// // default to the strip itself
// if (!dragRoot) {
// dragRoot = strip;
// }
// } else {
// dragRoot = strip;
// }
// // Only act if dragRoot exists!
// if (dragRoot) {
// dragRoot.style.zIndex = ++zIndexCounterLocal;
// activeDragStrip.classList.add('active');
// } else {
// // Abort drag, no valid element
// return;
// }
// // Determine pointer position
// let clientX, clientY;
// if (e.type.startsWith('touch')) {
// clientX = e.touches[0].clientX;
// clientY = e.touches[0].clientY;
// } else {
// clientX = e.clientX;
// clientY = e.clientY;
// }
// // Get rect of drag root for reference
// const dragRect = dragRoot.getBoundingClientRect();
// xOffset = clientX - dragRect.left;
// yOffset = clientY - dragRect.top;
// document.addEventListener('mousemove', dragMove, { passive: false });
// document.addEventListener('mouseup', dragEnd, { passive: false });
// document.addEventListener('touchmove', dragMove, { passive: false });
// document.addEventListener('touchend', dragEnd, { passive: false });
// document.addEventListener('touchcancel', dragEnd, { passive: false });
// }
// function dragMove(e) {
// if (!activeDragStrip) return;
// e.preventDefault();
// let clientX, clientY;
// if (e.type.startsWith('touch')) {
// clientX = e.touches[0].clientX;
// clientY = e.touches[0].clientY;
// } else {
// clientX = e.clientX;
// clientY = e.clientY;
// }
// // Workbench reference
// const workbenchRect = workbench.getBoundingClientRect();
// let newX = clientX - xOffset - workbenchRect.left;
// let newY = clientY - yOffset - workbenchRect.top;
// newX = Math.max(0, newX);
// newY = Math.max(0, newY);
// if (dragMode === 'chain') {
// dragRoot.style.left = newX + 'px';
// dragRoot.style.top = newY + 'px';
// repositionChain(dragRoot);
// } else {
// dragRoot.style.left = newX + 'px';
// dragRoot.style.top = newY + 'px';
// }
// }
// function dragEnd(e) {
// if (!activeDragStrip) return;
// activeDragStrip.classList.remove('active');
// // Drop logic for both modes
// // We want to merge chains if in "chain" mode; in "single" mode, only try to merge the single node
// const movingElem = dragRoot;
// const movingRect = movingElem.getBoundingClientRect();
// let potentialTarget = null;
// let mergeType = null;
// const allStrips = document.querySelectorAll('.cutup-strip');
// for (const targetStrip of allStrips) {
// if (targetStrip === movingElem) continue;
// if (dragMode === 'chain' && getChainRoot(targetStrip) === movingElem) continue;
// if (dragMode === 'chain' && getChainRoot(targetStrip) === getChainRoot(movingElem)) continue;
// if (dragMode === 'single' && targetStrip === activeDragStrip) continue;
// const targetRect = targetStrip.getBoundingClientRect();
// const isOverlapping = (
// movingRect.left < targetRect.right && movingRect.right > targetRect.left &&
// movingRect.top < targetRect.bottom && movingRect.bottom > targetRect.top
// );
// if (isOverlapping) {
// const targetMidpointX = targetRect.left + (targetRect.width / 2);
// const movingCenterX = movingRect.left + (movingRect.width / 2);
// if (movingCenterX < targetMidpointX) {
// potentialTarget = targetStrip;
// mergeType = 'left';
// break;
// } else {
// potentialTarget = targetStrip;
// mergeType = 'right';
// break;
// }
// }
// }
// if (potentialTarget && mergeType) {
// if (dragMode === 'chain') {
// linkChains(getChainRoot(movingElem), potentialTarget, mergeType);
// } else {
// // For a single node, link as a one-element chain
// linkChains(movingElem, potentialTarget, mergeType);
// }
// }
// activeDragStrip = null;
// dragRoot = null;
// dragMode = 'chain';
// document.removeEventListener('mousemove', dragMove, { passive: false });
// document.removeEventListener('mouseup', dragEnd, { passive: false });
// document.removeEventListener('touchmove', dragMove, { passive: false });
// document.removeEventListener('touchend', dragEnd, { passive: false });
// document.removeEventListener('touchcancel', dragEnd, { passive: false });
// }
// // --- Mouse/Touch Event Bindings ---
// strip.addEventListener('mousedown', dragStart);
// strip.addEventListener('touchstart', function (e) {
// if (e.target.classList.contains('delete-strip-btn')) return;
// // Check for double tap for "detach"
// const now = Date.now();
// if (now - lastTap < 300) {
// // Double tap → detach
// dragMode = 'single';
// // unlinkStrip(strip);
// detachLeftAndReRoot(strip);
// // Call dragStart with this touch event (for immediate drag after detach)
// dragStart(e);
// lastTap = 0;
// e.preventDefault();
// return;
// }
// lastTap = now;
// dragMode = 'chain';
// dragStart(e);
// }, { passive: false });
// // --- Double click for "detach" ---
// strip.addEventListener('dblclick', function (e) {
// // On double click: detach this node
// e.preventDefault();
// dragMode = 'single';
// // unlinkStrip(strip);
// detachLeftAndReRoot(strip);
// // After detaching, immediately begin drag for user feedback
// dragStart(e);
// });
// // No longer add document listeners here; they're managed in dragStart/dragEnd
// }
// --- Gesture Split Logic (vertical drag from background across strip) ---
let splitGestureActive = false;
let splitGestureStartY = 0;
let splitGestureStartX = 0;
let splitGestureDidSplit = false;
// Helper: find topmost strip at given coordinates
function stripAtXY(clientX, clientY) {
const elems = document.elementsFromPoint(clientX, clientY);
return elems.find(el => el.classList && el.classList.contains('cutup-strip'));
}
// Workbench pointer events
workbench.addEventListener('pointerdown', function (e) {
// Only start gesture if not on a strip or control, i.e. on background
if (e.target !== workbench) return;
splitGestureActive = true;
splitGestureDidSplit = false;
splitGestureStartY = e.clientY;
splitGestureStartX = e.clientX;
});
workbench.addEventListener('pointermove', function (e) {
if (!splitGestureActive || splitGestureDidSplit) return;
// Vertical drag threshold
const verticalDelta = Math.abs(e.clientY - splitGestureStartY);
if (verticalDelta < 20) return; // Require at least 20px vertical drag
// Must be over a strip
const originalStrip = stripAtXY(e.clientX, e.clientY);
if (originalStrip) {
// Remember its DOM position so we know where to look for new ones
const rect = originalStrip.getBoundingClientRect();
splitStrip(originalStrip);
splitGestureDidSplit = true;
splitGestureActive = false;
// After split, determine which new strip is under the pointer (there will now be two at about the same position)
// Find the new topmost .cutup-strip at pointer location
setTimeout(() => {
// Wait for DOM update
const newStrip = stripAtXY(e.clientX, e.clientY);
if (newStrip) {
// Synthesize a pointer event and dispatch to the new strip to start drag
// We'll use MouseEvent for desktop, or TouchEvent for touch, but pointer events cover both in modern browsers.
// We'll call its mousedown or touchstart as appropriate
// Preferably, call the event handler directly for robustness
if (newStrip.dispatchEvent) {
// Create a new PointerEvent and dispatch it (simulate user pressing pointer)
let pe;
try {
pe = new PointerEvent('pointerdown', {
bubbles: true,
clientX: e.clientX,
clientY: e.clientY,
pointerId: e.pointerId,
pointerType: e.pointerType,
isPrimary: e.isPrimary,
pressure: 0.5,
});
} catch (_) {
pe = new MouseEvent('mousedown', {
bubbles: true,
clientX: e.clientX,
clientY: e.clientY,
});
}
newStrip.dispatchEvent(pe);
}
}
}, 0); // defer until after DOM changes
}
});
workbench.addEventListener('pointerup', function (e) {
splitGestureActive = false;
splitGestureDidSplit = false;
});
workbench.addEventListener('pointercancel', function (e) {
splitGestureActive = false;
splitGestureDidSplit = false;
});
// --- Event Listeners and Initialization ---
// 1. Pool Selector Button (Toggles Dropdown)
poolSelectorButton.addEventListener('click', (e) => {
e.stopPropagation(); // Prevents document click from immediately closing
togglePoolSelector();
});
// 2. Draw Only Button (Generates Strips) - THIS WAS THE MISSING LINK
drawOnlyButton.addEventListener('click', generateCutUps);
document.addEventListener('click', (e) => {
if (!poolSelectorButton.contains(e.target)) {
togglePoolSelector(false);
}
});
// Hook the Clear All button to the Confirmation Modal
clearButton.addEventListener('click', () => {
showConfirmationModal(
'Clear All Strips',
'This will permanently remove all cut-up strips from the workbench. Are you sure you want to proceed?',
clearAllStripsConfirmed
);
});
// Hook the Toggle Deletion button
toggleDeletionButton.addEventListener('click', toggleDeletion);
customSubmitBtn.addEventListener('click', addCustomStrips);
workbench.addEventListener('dblclick', (e) => {
if (e.target === workbench) {
// Show input modal at the document coordinate
showCustomInput(e.clientX, e.clientY);
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && customInputOverlay.style.display !== 'none') {
hideCustomInput();
}
if (e.key === 'Escape' && modal.style.display !== 'none') {
hideConfirmationModal();
}
});
const userInputButton = document.getElementById('user-input-btn');
userInputButton.addEventListener('click', function () {
// Position roughly center in the workbench (or wherever you want)
const workbenchRect = workbench.getBoundingClientRect();
// Center, but don't risk being offscreen:
const x = workbenchRect.left + Math.max(60, workbenchRect.width / 2);
const y = workbenchRect.top + Math.max(80, workbenchRect.height / 2);
showCustomInput(x, y);
});
window.onload = () => {
updatePoolSelectorUI();
generateCutUps();
updateWordCountMetrics();
};
</script>
<!-- Edit Node Text Modal -->
<div id="edit-strip-modal"
style="display:none; position:fixed; z-index:3100; left:0; top:0; width:100vw; height:100vh; background:rgba(0,0,0,0.4); align-items:center; justify-content:center;">
<div
style="background:#22223b; color:#fff; border:2px solid #fcd34d; border-radius:10px; max-width:400px; padding:28px 20px 18px 20px; margin:auto;">
<h3 style="margin-top:0; margin-bottom:15px; color:#fcd34d;">Edit Node Text</h3>
<textarea id="edit-strip-textarea"
style="width:100%; min-height:60px; background:#1f2937; color:#fff; border-radius:5px; border:1px solid #fcd34d; margin-bottom:15px;"></textarea>
<div style="display:flex; gap:10px; justify-content:flex-end;">
<button id="edit-strip-save-btn"
style="background:#fcd34d; color:#232345; font-weight:bold;">Save</button>
<button id="edit-strip-cancel-btn" style="background:#444; color:#fff;">Cancel</button>
</div>
</div>
</div>
<div id="strip-context-menu"
style="display:none; position:fixed; z-index:3100; background:#232345; color:#fff; border-radius:8px; border:2px solid #fcd34d; box-shadow:0 6px 16px rgba(0,0,0,0.18); min-width:120px;">
<button id="context-edit-btn"
style="width:100%; background:none; border:none; padding:12px; color:#fcd34d; text-align:left; font-weight:bold; font-size:1rem; cursor:pointer;">Edit
Text</button>
<button id="context-detach-btn"
style="width:100%; background:none; border:none; padding:12px; color:#ef4444; text-align:left; font-weight:bold; font-size:1rem; cursor:pointer;">Detach
from Chain</button>
</div>
</body>
</html>