| Server IP : 172.67.201.108 / Your IP : 216.73.216.11 Web Server : Apache/2.4.68 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-69-123.ec2.internal 6.1.176-223.369.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Jul 24 13:34:27 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/banners/domains/stagemag/ |
Upload File : |
<?php
include 'header.php';
set_time_limit(600);
require_once __DIR__ . '/printpdf/pdf_settings.php';
$programid = resolve_programid();
require_program_access($programid, $getuser);
$marginPresets = ['tight', 'normal', 'wide'];
$fontScales = ['small', 'normal', 'large'];
$pageSizes = ['Letter', 'A4'];
$orientations = ['P', 'L'];
$strategies = ['flow', 'own_page', 'fit_one_page'];
$sectionList = [
'info-id' => 'Title page',
'extra-id' => 'Opening note / After-title extras',
'song-list-id' => 'Song list',
'cast-id' => 'Cast list',
'extra-content-id' => 'After-cast extras',
'creative-team-id' => 'Creative team',
'after-creative-id' => 'After-creative extras',
'meet-the-company-id' => 'Meet the company (bios)',
'donors-id' => 'Donors',
'closingnote-id' => 'Closing note',
];
$sectionDefaults = pdf_default_section_strategies();
$decodeJsonArray = function ($json) {
$arr = json_decode((string) $json, true);
if (!is_array($arr)) return [];
return array_values(array_filter(array_map(function ($s) {
$s = trim((string) $s);
return $s !== '' ? $s : null;
}, $arr)));
};
$decodeOrderInput = function ($json) {
$arr = json_decode((string) $json, true);
if (!is_array($arr)) return [];
return array_values(array_filter(array_map(function ($s) {
return preg_match('/^[a-z0-9_-]+$/i', (string) $s) ? (string) $s : null;
}, $arr)));
};
if (isset($_GET['editpdf'])) {
$sections = [];
foreach (array_keys($sectionList) as $sid) {
$val = $_POST['section_' . str_replace('-', '_', $sid)] ?? 'flow';
if (in_array($val, $strategies, true)) {
$sections[$sid] = $val;
}
}
$payload = [
'page_size' => in_array($_POST['page_size'] ?? 'Letter', $pageSizes, true) ? $_POST['page_size'] : 'Letter',
'orientation' => in_array($_POST['orientation'] ?? 'P', $orientations, true) ? $_POST['orientation'] : 'P',
'margin_preset' => in_array($_POST['margin_preset'] ?? 'normal', $marginPresets,true) ? $_POST['margin_preset'] : 'normal',
'font_scale' => in_array($_POST['font_scale'] ?? 'normal', $fontScales, true) ? $_POST['font_scale'] : 'normal',
'sections' => $sections,
'hide_selectors'=> $decodeJsonArray($_POST['hide_selectors'] ?? ''),
'break_before' => $decodeJsonArray($_POST['break_before'] ?? ''),
'break_after' => $decodeJsonArray($_POST['break_after'] ?? ''),
'header' => trim((string) ($_POST['header'] ?? '')),
'footer' => trim((string) ($_POST['footer'] ?? '')),
'watermark' => trim((string) ($_POST['watermark'] ?? '')),
'custom_css' => (function ($posted) {
$posted = (string) $posted;
// If the textarea was left at the unedited default, store empty so the editor
// keeps pre-filling from pdf_default_user_css() (and the JSON stays small).
return rtrim($posted) === rtrim(pdf_default_user_css()) ? '' : $posted;
})($_POST['custom_css'] ?? ''),
'section_order' => $decodeOrderInput($_POST['section_order'] ?? ''),
];
$stmt = db_write()->prepare('UPDATE stagemag.productions SET pdf_settings = ? WHERE id = ?');
$stmt->execute([json_encode($payload), $programid]);
}
$stmt = db_write()->prepare('SELECT * FROM stagemag.productions WHERE id = ?');
$stmt->execute([$programid]);
$geter = $stmt->fetch();
$current = json_decode($geter['pdf_settings'] ?? '', true) ?: [];
$cur = [
'page_size' => $current['page_size'] ?? 'Letter',
'orientation' => $current['orientation'] ?? 'P',
'margin_preset' => $current['margin_preset'] ?? 'normal',
'font_scale' => $current['font_scale'] ?? 'normal',
'sections' => is_array($current['sections'] ?? null) ? $current['sections'] : [],
'hide_selectors' => is_array($current['hide_selectors'] ?? null) ? $current['hide_selectors'] : [],
'break_before' => is_array($current['break_before'] ?? null) ? $current['break_before'] : [],
'break_after' => is_array($current['break_after'] ?? null) ? $current['break_after'] : [],
'section_order' => is_array($current['section_order'] ?? null) ? $current['section_order'] : [],
'header' => (string) ($current['header'] ?? ''),
'footer' => (string) ($current['footer'] ?? ''),
'watermark' => (string) ($current['watermark'] ?? ''),
'custom_css' => (string) ($current['custom_css'] ?? ''),
];
// Anchor picker — fetch modern HTML once and enumerate addressable anchors
$anchorChoices = [];
if (!empty($geter['seo_friendly'])) {
$modernHtml = @file_get_contents('https://stagemag.broadwayworld.com/modern/' . $geter['seo_friendly']);
if ($modernHtml) {
$anchorChoices = pdf_enumerate_anchors($modernHtml, (int) $programid);
}
}
$anchorGroups = [];
foreach ($anchorChoices as $a) { $anchorGroups[$a['group']][] = $a; }
$sectionAnchors = array_values(array_filter($anchorChoices, function ($a) { return $a['group'] === 'Sections'; }));
$extrapageAnchors = array_values(array_filter($anchorChoices, function ($a) { return $a['group'] === 'Extra pages'; }));
// Section-order list: sections + extra-pages from the live HTML, in current saved order if any
$orderable = [];
foreach ($sectionAnchors as $a) {
$sid = ltrim($a['value'], '#');
$cleanLabel = preg_replace('/\s*\([^()]*\)\s*$/', '', $a['label']);
$orderable[] = ['id' => $sid, 'label' => $cleanLabel !== '' ? $cleanLabel : $a['label']];
}
$savedOrder = $cur['section_order'];
if (!empty($savedOrder)) {
$byId = [];
foreach ($orderable as $o) { $byId[$o['id']] = $o; }
$reordered = [];
foreach ($savedOrder as $sid) { if (isset($byId[$sid])) { $reordered[] = $byId[$sid]; unset($byId[$sid]); } }
$orderable = array_merge($reordered, array_values($byId));
}
// Selector → friendly-label lookup (powered by the same enumerated anchors)
$selectorLabelMap = [];
foreach ($anchorChoices as $a) {
$cleanLabel = preg_replace('/\s*\([^()]*\)\s*$/', '', $a['label']);
$selectorLabelMap[$a['value']] = $cleanLabel !== '' ? $cleanLabel : $a['label'];
}
$renderChipPicker = function ($name, $current, $placeholder, $anchorGroups) use ($selectorLabelMap) {
?>
<select class="pdf-adv-picker" data-target="<?= htmlspecialchars($name) ?>">
<option value=""><?= htmlspecialchars($placeholder) ?></option>
<?php foreach ($anchorGroups as $group => $items): ?>
<optgroup label="<?= htmlspecialchars($group) ?>">
<?php foreach ($items as $a):
$cleanLabel = preg_replace('/\s*\([^()]*\)\s*$/', '', $a['label']);
?>
<option value="<?= htmlspecialchars($a['value']) ?>"><?= htmlspecialchars($cleanLabel) ?></option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select>
<div class="pdf-adv-chips" data-chips-for="<?= htmlspecialchars($name) ?>"></div>
<input type="hidden" name="<?= htmlspecialchars($name) ?>" data-chip-store="<?= htmlspecialchars($name) ?>" value="<?= htmlspecialchars(json_encode(array_values($current))) ?>">
<details class="pdf-adv-raw">
<summary>Add a custom CSS selector…</summary>
<div class="pdf-adv-raw-row">
<input type="text" placeholder="e.g. .my-class or div.something" data-raw-input="<?= htmlspecialchars($name) ?>">
<button type="button" data-raw-add="<?= htmlspecialchars($name) ?>">Add</button>
</div>
</details>
<?php
};
$slug = htmlspecialchars($geter['seo_friendly']);
$previewBase = '/printpdf/printable-v2.php/' . $slug;
?>
<style>
.feature-card { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.08); }
.feature-card-header { background: linear-gradient(135deg, #b20223 0%, #8a011a 100%); color: #fff; padding: 12px 20px; border-radius: 7px 7px 0 0; font-size: 18px; font-weight: 600; }
.feature-card-body { padding: 20px; }
.feature-card-body p { color: #555; margin-bottom: 15px; line-height: 1.5; }
.feature-card-body label { font-weight: 600; color: #333; display: block; margin-bottom: 5px; }
.feature-card-body select { padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; min-width: 140px; }
.share-link { color: #b20223; word-break: break-all; font-size: 13px; }
.embed-code { width: 100%; height: 80px; font-size: 11px; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; margin-top: 10px; }
.beta-badge { background: #b20223; color: white; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin-left: 10px; }
.pdf-page-row { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 14px; }
.pdf-page-row > div { flex: 1 1 140px; min-width: 140px; }
.pdf-form-row { margin-bottom: 12px; }
.pdf-section-list { border: 1px solid #eee; border-radius: 4px; padding: 4px 12px; }
.pdf-section-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #f0f0f0; gap: 12px; }
.pdf-section-row:last-child { border-bottom: none; }
.pdf-section-row span { font-size: 14px; color: #333; flex: 1; }
.pdf-preview-frame { width: 100%; height: 720px; border: 1px solid #ccc; border-radius: 4px; background: #f5f5f5; display: block; }
.pdf-advanced { margin: 12px 0; border: 1px dashed #ccc; border-radius: 4px; padding: 0 14px; background: #fafafa; }
.pdf-advanced > summary { padding: 10px 0; font-weight: 600; cursor: pointer; color: #333; }
.pdf-advanced[open] > summary { border-bottom: 1px solid #e5e5e5; margin-bottom: 12px; }
.pdf-adv-card { background: #fff; border: 1px solid #eee; border-radius: 4px; padding: 12px 14px; margin-bottom: 12px; }
.pdf-adv-card h4 { margin: 0 0 6px; font-size: 14px; color: #333; }
.pdf-adv-help { font-size: 12px; color: #777; margin: 0 0 10px; }
.pdf-adv-row { margin-bottom: 6px; }
.pdf-adv-picker { width: 100%; max-width: 480px; padding: 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; }
.pdf-adv-textarea { width: 100%; padding: 6px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; resize: vertical; box-sizing: border-box; }
.pdf-adv-input { width: 100%; max-width: 480px; padding: 7px 9px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; box-sizing: border-box; }
.pdf-adv-label { display: block; margin: 8px 0 4px; font-size: 13px; font-weight: 600; color: #444; }
.pdf-adv-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 32px; padding: 6px 4px; background: #fafafa; border: 1px dashed #e0e0e0; border-radius: 4px; }
.pdf-adv-chips:empty::before { content: 'Nothing selected'; color: #aaa; font-size: 12px; padding: 4px 6px; }
.pdf-adv-chip { display: inline-flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #d0d0d0; border-radius: 14px; padding: 3px 4px 3px 10px; font-size: 12px; color: #333; }
.pdf-adv-chip.is-custom { border-color: #b20223; color: #b20223; background: #fff5f6; }
.pdf-adv-chip-x { background: #eee; border: 0; border-radius: 50%; width: 18px; height: 18px; line-height: 1; cursor: pointer; color: #555; padding: 0; font-size: 12px; }
.pdf-adv-chip-x:hover { background: #b20223; color: #fff; }
.pdf-adv-raw { margin-top: 8px; }
.pdf-adv-raw > summary { font-size: 12px; color: #666; cursor: pointer; }
.pdf-adv-raw-row { display: flex; gap: 6px; margin-top: 6px; }
.pdf-adv-raw-row input { flex: 1; padding: 6px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; font-family: monospace; }
.pdf-adv-raw-row button { background: #f5f5f5; border: 1px solid #ccc; border-radius: 4px; padding: 4px 12px; cursor: pointer; font-size: 12px; }
.pdf-adv-raw-row button:hover { background: #e5e5e5; }
.pdf-adv-sortable { list-style: none; margin: 0; padding: 0; max-width: 480px; }
.pdf-adv-sortable li { background: #fff; border: 1px solid #ddd; border-radius: 4px; padding: 7px 10px; margin-bottom: 4px; cursor: grab; font-size: 13px; }
.pdf-adv-sortable li:hover { background: #f5f5f5; }
.pdf-adv-sortable li.sortable-chosen { opacity: 0.6; }
.pdf-adv-sortable .grip { color: #aaa; margin-right: 8px; user-select: none; }
.pdf-strategy-chart { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 6px 0 14px; }
.pdf-strategy-col { border: 1px solid #e5e5e5; border-radius: 6px; padding: 10px; background: #fafafa; }
.pdf-strategy-col h5 { margin: 0 0 8px; font-size: 13px; color: #333; }
.pdf-strategy-col p { margin: 8px 0 0; font-size: 12px; color: #666; line-height: 1.4; }
.pdf-strategy-pages { display: flex; gap: 4px; align-items: flex-start; }
.pdf-strategy-page { background: #fff; border: 1px solid #ccc; border-radius: 2px; width: 36px; height: 52px; padding: 3px; display: flex; flex-direction: column; gap: 2px; flex-shrink: 0; }
.pdf-strategy-line { height: 3px; background: #e0e0e0; border-radius: 1px; }
.pdf-strategy-line.is-section { background: #b20223; }
.pdf-strategy-line.is-empty { visibility: hidden; }
.pdf-strategy-page.is-fit { position: relative; justify-content: space-between; }
.pdf-strategy-page.is-fit .pdf-strategy-line.is-section { flex: 1 1 auto; min-height: 5px; }
.pdf-strategy-page.is-fit::before,
.pdf-strategy-page.is-fit::after { content: ''; position: absolute; left: 50%; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; transform: translateX(-50%); }
.pdf-strategy-page.is-fit::before { top: -7px; border-bottom: 5px solid #b20223; }
.pdf-strategy-page.is-fit::after { bottom: -7px; border-top: 5px solid #b20223; }
.pdf-adv-warning { background: #fff7e0; border: 1px solid #f0d27a; border-radius: 4px; padding: 8px 12px; font-size: 12px; color: #6a4d00; margin-bottom: 8px; line-height: 1.5; }
.pdf-save-btn { background: #b20223; color: #fff; border: 0; padding: 10px 20px; border-radius: 4px; font-weight: 600; cursor: pointer; font-size: 14px; }
.pdf-save-btn:hover { background: #8a011a; }
.pdf-refresh-btn { background: #fff; color: #b20223; border: 1px solid #b20223; padding: 8px 14px; border-radius: 4px; cursor: pointer; font-size: 13px; margin-left: 8px; }
</style>
<div id="content" style="max-width: 920px;">
<div id="content-header">
<h2> Share Program - PDF <span class="beta-badge">BETA</span></h2>
</div>
<div class="container-fluid">
<p style="margin-bottom: 20px;">Adjust page setup and per-section behavior — the preview below updates automatically. Click <em>Save settings</em> when you're happy to apply them to every PDF download. Per-section dropdowns are pre-selected with the current behavior. Have feedback? Please <a href="https://www.broadwayworld.com/contact.cfm" style="color: #b20223;">let us know</a>.</p>
<div class="feature-card">
<div class="feature-card-header">PDF Settings</div>
<div class="feature-card-body">
<form id="pdf-settings-form" action="programedit-sharepdf.php?editpdf=on&programid=<?= (int) $programid ?>" method="post">
<div class="pdf-page-row">
<div>
<label>Page size</label>
<select name="page_size" style="width:100%;">
<?php foreach ($pageSizes as $opt): ?>
<option value="<?= $opt ?>"<?= $cur['page_size'] === $opt ? ' selected' : '' ?>><?= $opt ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label>Orientation</label>
<select name="orientation" style="width:100%;">
<option value="P"<?= $cur['orientation'] === 'P' ? ' selected' : '' ?>>Portrait</option>
<option value="L"<?= $cur['orientation'] === 'L' ? ' selected' : '' ?>>Landscape</option>
</select>
</div>
<div>
<label>Margin preset</label>
<select name="margin_preset" style="width:100%;">
<option value="tight"<?= $cur['margin_preset'] === 'tight' ? ' selected' : '' ?>>Tight (10mm)</option>
<option value="normal"<?=$cur['margin_preset'] === 'normal' ? ' selected' : '' ?>>Normal (14mm)</option>
<option value="wide"<?= $cur['margin_preset'] === 'wide' ? ' selected' : '' ?>>Wide (20mm)</option>
</select>
</div>
<div>
<label>Font scale</label>
<select name="font_scale" style="width:100%;">
<option value="small"<?= $cur['font_scale'] === 'small' ? ' selected' : '' ?>>Small</option>
<option value="normal"<?=$cur['font_scale'] === 'normal' ? ' selected' : '' ?>>Normal</option>
<option value="large"<?= $cur['font_scale'] === 'large' ? ' selected' : '' ?>>Large</option>
</select>
</div>
</div>
<label style="margin-top: 6px;">Per-section behavior</label>
<div class="pdf-section-list">
<?php foreach ($sectionList as $sid => $sLabel):
$current_strategy = $cur['sections'][$sid] ?? $sectionDefaults[$sid] ?? 'flow';
$fieldName = 'section_' . str_replace('-', '_', $sid);
?>
<div class="pdf-section-row">
<span><?= htmlspecialchars($sLabel) ?></span>
<select name="<?= $fieldName ?>">
<option value="flow"<?= $current_strategy === 'flow' ? ' selected' : '' ?>>Flow</option>
<option value="own_page"<?= $current_strategy === 'own_page' ? ' selected' : '' ?>>Own page</option>
<option value="fit_one_page"<?= $current_strategy === 'fit_one_page' ? ' selected' : '' ?>>Fit one page</option>
</select>
</div>
<?php endforeach; ?>
</div>
<div class="pdf-strategy-chart">
<div class="pdf-strategy-col">
<h5>Flow</h5>
<div class="pdf-strategy-pages">
<div class="pdf-strategy-page">
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-empty"></div>
</div>
<div class="pdf-strategy-page">
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
</div>
</div>
<p>Paginates naturally — the section starts wherever the previous one ended and may split across pages.</p>
</div>
<div class="pdf-strategy-col">
<h5>Own page</h5>
<div class="pdf-strategy-pages">
<div class="pdf-strategy-page">
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
</div>
<div class="pdf-strategy-page">
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-empty"></div>
</div>
</div>
<p>Starts on a fresh page. May still spill onto the next page if the section is long.</p>
</div>
<div class="pdf-strategy-col">
<h5>Fit one page</h5>
<div class="pdf-strategy-pages">
<div class="pdf-strategy-page">
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
<div class="pdf-strategy-line is-empty"></div>
</div>
<div class="pdf-strategy-page is-fit">
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
<div class="pdf-strategy-line is-section"></div>
</div>
</div>
<p>Starts on a fresh page and tries to keep the whole section on a single page.</p>
</div>
</div>
<details class="pdf-advanced" <?= ($cur['hide_selectors'] || $cur['break_before'] || $cur['break_after'] || $cur['header'] || $cur['footer'] || $cur['watermark'] || $cur['custom_css'] || $cur['section_order']) ? 'open' : '' ?>>
<summary>Advanced settings</summary>
<p style="font-size:13px;color:#666;margin:8px 0 16px;">Escape hatches for fine-grained control. Most editors won't need these — the per-section options above cover the common cases.</p>
<div class="pdf-adv-card">
<h4>Hide elements</h4>
<p class="pdf-adv-help">Pick what to hide from the list. The element is removed from every PDF download.</p>
<?php $renderChipPicker('hide_selectors', $cur['hide_selectors'], 'Pick an element to hide…', $anchorGroups); ?>
</div>
<div class="pdf-adv-card">
<h4>Insert page breaks</h4>
<p class="pdf-adv-help">Force a page break before or after a specific element — useful for splitting content mid-section.</p>
<label class="pdf-adv-label">Break before</label>
<?php $renderChipPicker('break_before', $cur['break_before'], 'Pick an element to break before…', $anchorGroups); ?>
<label class="pdf-adv-label" style="margin-top:14px;">Break after</label>
<?php $renderChipPicker('break_after', $cur['break_after'], 'Pick an element to break after…', $anchorGroups); ?>
</div>
<div class="pdf-adv-card">
<h4>Header / Footer / Watermark</h4>
<p class="pdf-adv-help">Tokens: <code>{pageno}</code> <code>{pages}</code> <code>{title}</code></p>
<label class="pdf-adv-label">Header text</label>
<input name="header" type="text" class="pdf-adv-input" value="<?= htmlspecialchars($cur['header']) ?>" placeholder="e.g. {title}">
<label class="pdf-adv-label">Footer text</label>
<input name="footer" type="text" class="pdf-adv-input" value="<?= htmlspecialchars($cur['footer']) ?>" placeholder="e.g. Page {pageno} of {pages}">
<label class="pdf-adv-label">Watermark text</label>
<input name="watermark" type="text" class="pdf-adv-input" value="<?= htmlspecialchars($cur['watermark']) ?>" placeholder="e.g. DRAFT or REVIEW COPY">
</div>
<div class="pdf-adv-card">
<h4>Custom CSS</h4>
<div class="pdf-adv-warning">
<strong>⚠ Heads up:</strong> the textarea below is pre-filled with the default print CSS so you can see what's currently applied. Anything you change here is appended after the default rules and overrides them by cascade. Bad CSS can break the PDF — use <em>Reset to default</em> if it goes wrong.
</div>
<textarea name="custom_css" rows="22" class="pdf-adv-textarea pdf-css-area" style="font-family:monospace;font-size:12px; line-height:1.4;"><?= htmlspecialchars($cur['custom_css'] !== '' ? $cur['custom_css'] : pdf_default_user_css()) ?></textarea>
<div style="margin-top:6px; display:flex; gap:8px; align-items:center;">
<button type="button" id="pdf-css-reset" class="pdf-refresh-btn" style="margin-left:0;">Reset to default</button>
<span id="pdf-css-state" style="font-size:12px; color:#777;"></span>
</div>
</div>
<div class="pdf-adv-card">
<h4>Section order</h4>
<p class="pdf-adv-help">Drag to reorder sections in the PDF. Extra pages stay attached to their section.</p>
<ul id="pdf-section-order" class="pdf-adv-sortable">
<?php foreach ($orderable as $o): ?>
<li data-id="<?= htmlspecialchars($o['id']) ?>"><span class="grip">⋮⋮</span> <?= htmlspecialchars($o['label']) ?></li>
<?php endforeach; ?>
</ul>
<input type="hidden" name="section_order" id="pdf-section-order-input" value="<?= htmlspecialchars(json_encode(array_column($orderable, 'id'))) ?>">
</div>
</details>
<div style="margin: 16px 0;">
<button type="submit" class="pdf-save-btn">Save settings</button>
<span id="pdf-preview-status" style="font-size:12px;color:#777;margin-left:12px;"></span>
</div>
<p style="font-size:12px;color:#777;margin-top:-8px;">
<strong>Save settings</strong> writes these choices to the database so every PDF download (Standard, US Letter booklet, A4 booklet) uses them. The preview below updates automatically as you change options — saving is only needed once you're happy with the result.
</p>
</form>
<!-- Preview is driven by POST so the (potentially multi-KB) JSON blob doesn't have to fit
in a URL. The hidden form below targets the iframe by name; JS writes the payload
into the hidden input and submits the form on every change. -->
<form id="pdf-preview-form" method="post" action="<?= $previewBase ?>" target="pdf-preview-frame" style="margin:0;">
<input type="hidden" name="pdf" id="pdf-preview-input">
</form>
<label>Live preview (US Letter booklet generator)</label>
<iframe id="pdf-preview-frame" name="pdf-preview-frame" class="pdf-preview-frame"></iframe>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/Sortable.min.js"></script>
<script>
(function () {
var form = document.getElementById('pdf-settings-form');
var iframe = document.getElementById('pdf-preview-frame');
var status = document.getElementById('pdf-preview-status');
var sectionFields = <?= json_encode(array_keys($sectionList)) ?>;
var selectorLabels = <?= json_encode($selectorLabelMap) ?>;
var defaultCss = <?= json_encode(pdf_default_user_css()) ?>;
var debounceTimer = null;
function getStoreValue(name) {
var input = form.querySelector('input[data-chip-store="' + name + '"]');
if (!input) return [];
try { var arr = JSON.parse(input.value || '[]'); return Array.isArray(arr) ? arr : []; }
catch (e) { return []; }
}
function setStoreValue(name, arr) {
var input = form.querySelector('input[data-chip-store="' + name + '"]');
if (input) input.value = JSON.stringify(arr);
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return { '&':'&','<':'<','>':'>','"':'"',"'":''' }[c];
});
}
function renderChips(name) {
var box = form.querySelector('div.pdf-adv-chips[data-chips-for="' + name + '"]');
if (!box) return;
var arr = getStoreValue(name);
box.innerHTML = '';
arr.forEach(function (sel) {
var label = selectorLabels[sel];
var isCustom = !label;
if (!label) label = sel;
var chip = document.createElement('span');
chip.className = 'pdf-adv-chip' + (isCustom ? ' is-custom' : '');
chip.title = sel;
chip.innerHTML = escapeHtml(label) + ' <button type="button" class="pdf-adv-chip-x" aria-label="Remove">×</button>';
chip.querySelector('.pdf-adv-chip-x').addEventListener('click', function () {
var cur = getStoreValue(name).filter(function (x) { return x !== sel; });
setStoreValue(name, cur);
renderChips(name);
scheduleRefresh();
});
box.appendChild(chip);
});
}
function addToStore(name, value) {
value = (value || '').trim();
if (!value) return;
var arr = getStoreValue(name);
if (arr.indexOf(value) !== -1) return;
arr.push(value);
setStoreValue(name, arr);
renderChips(name);
scheduleRefresh();
}
// Initial render of all chip groups
['hide_selectors', 'break_before', 'break_after'].forEach(renderChips);
function getOrderArray() {
var ul = document.getElementById('pdf-section-order');
if (!ul) return [];
return Array.prototype.map.call(ul.children, function (li) { return li.getAttribute('data-id'); });
}
var previewForm = document.getElementById('pdf-preview-form');
var previewInput = document.getElementById('pdf-preview-input');
function buildPreviewPayload() {
var data = new FormData(form);
var payload = {
page_size: data.get('page_size'),
orientation: data.get('orientation'),
margin_preset: data.get('margin_preset'),
font_scale: data.get('font_scale'),
sections: {},
hide_selectors: getStoreValue('hide_selectors'),
break_before: getStoreValue('break_before'),
break_after: getStoreValue('break_after'),
header: data.get('header') || '',
footer: data.get('footer') || '',
watermark: data.get('watermark') || '',
custom_css: data.get('custom_css') || '',
section_order: getOrderArray()
};
sectionFields.forEach(function (sid) {
var fname = 'section_' + sid.replace(/-/g, '_');
payload.sections[sid] = data.get(fname) || 'flow';
});
return payload;
}
function refreshPreview() {
status.textContent = 'Refreshing preview…';
previewInput.value = JSON.stringify(buildPreviewPayload());
previewForm.submit();
}
iframe.addEventListener('load', function () {
// The empty about:blank load fires before any submit — only flip the message when
// we're paired with a "Refreshing preview…" we set ourselves.
if (status.textContent === 'Refreshing preview…') {
status.textContent = 'Preview up to date.';
}
});
function scheduleRefresh() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(refreshPreview, 1500);
}
// Picker dropdowns add to chip store on change
form.addEventListener('change', function (e) {
if (e.target.classList && e.target.classList.contains('pdf-adv-picker') && e.target.value) {
var targetName = e.target.getAttribute('data-target');
addToStore(targetName, e.target.value);
e.target.value = '';
return;
}
scheduleRefresh();
});
// Custom raw-selector add buttons
form.addEventListener('click', function (e) {
var name = e.target.getAttribute && e.target.getAttribute('data-raw-add');
if (!name) return;
var input = form.querySelector('input[data-raw-input="' + name + '"]');
if (!input) return;
addToStore(name, input.value);
input.value = '';
});
// Free-text inputs / textareas trigger debounced preview
form.addEventListener('input', function (e) {
if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT') return;
if (e.target.hasAttribute('data-raw-input')) return; // wait for explicit Add
scheduleRefresh();
});
// Custom CSS reset + state indicator
var cssArea = form.querySelector('textarea[name="custom_css"]');
var cssReset = document.getElementById('pdf-css-reset');
var cssState = document.getElementById('pdf-css-state');
function updateCssState() {
if (!cssArea || !cssState) return;
var atDefault = cssArea.value.replace(/\s+$/, '') === defaultCss.replace(/\s+$/, '');
cssState.textContent = atDefault ? 'Currently at default — no custom overrides saved.' : 'Customized — your edits will be applied.';
}
if (cssReset && cssArea) {
cssReset.addEventListener('click', function () {
cssArea.value = defaultCss;
updateCssState();
scheduleRefresh();
});
}
if (cssArea) {
cssArea.addEventListener('input', updateCssState);
updateCssState();
}
// Drag-drop section order
var orderUl = document.getElementById('pdf-section-order');
var orderInput = document.getElementById('pdf-section-order-input');
if (orderUl && window.Sortable) {
Sortable.create(orderUl, {
animation: 150,
handle: '.grip',
onEnd: function () {
orderInput.value = JSON.stringify(getOrderArray());
scheduleRefresh();
}
});
}
// Kick off the first preview render now that all controls are wired up.
refreshPreview();
})();
</script>
<div class="feature-card">
<div class="feature-card-header">Standard PDF</div>
<div class="feature-card-body">
<label>Direct Link:</label>
<a href="https://stagemag.broadwayworld.com/printpdf/<?= $slug ?>" target="_new" class="share-link">https://stagemag.broadwayworld.com/printpdf/<?= $slug ?></a>
<label style="margin-top: 15px;">Embed Code:</label>
<textarea class="embed-code"><iframe src="https://stagemag.broadwayworld.com/printpdf/<?= $slug ?>" style="width:800px;height:1000px;" width="800" height="1000"></iframe></textarea>
</div>
</div>
<div class="feature-card">
<div class="feature-card-header">Print Friendly Booklet PDF - US Letter</div>
<div class="feature-card-body">
<label>Direct Link:</label>
<a href="https://stagemag.broadwayworld.com/printpdf/printout-v2.php/<?= $slug ?>" target="_new" class="share-link">https://stagemag.broadwayworld.com/printpdf/printout-v2.php/<?= $slug ?></a>
<label style="margin-top: 15px;">Embed Code:</label>
<textarea class="embed-code"><iframe src="https://stagemag.broadwayworld.com/printpdf/printout-v2.php/<?= $slug ?>" style="width:800px;height:1000px;" width="800" height="1000"></iframe></textarea>
</div>
</div>
<div class="feature-card">
<div class="feature-card-header">Print Friendly Booklet PDF - A4</div>
<div class="feature-card-body">
<label>Direct Link:</label>
<a href="https://stagemag.broadwayworld.com/printpdf/printout.php/<?= $slug ?>" target="_new" class="share-link">https://stagemag.broadwayworld.com/printpdf/printout.php/<?= $slug ?></a>
<label style="margin-top: 15px;">Embed Code:</label>
<textarea class="embed-code"><iframe src="https://stagemag.broadwayworld.com/printpdf/printout.php/<?= $slug ?>" style="width:800px;height:1000px;" width="800" height="1000"></iframe></textarea>
</div>
</div>
<p>Have any problems or feedback? Please <a href="https://www.broadwayworld.com/contact.cfm" style="color: #b20223;">let us know</a>.</p>
</div>
<!--Footer-part-->
<br><br><br><br>
<?php include 'footer.php'; ?>