/* eslint-disable */ /* © 2026 K2B NETWORK SOLUTIONS LLC. All rights reserved. Proprietary and confidential. Unauthorized copying, distribution, or use of this file, via any medium, is strictly prohibited. Original work product. */ // K2B DMS — PrintKit (RC35, lane brd): the print-template framework. EVERY printout takes the store header, the store's names, // address, phone, license line and logo from HERE - from the active store's branding (window.K2BStore, Owner Panel > Stores). // No store name, address or logo is written in any form. Loaded after DMSCore.jsx.txt and before printables.jsx (SPEC 4.7). // // K2BPrint.brand() the store's branding (every field; a missing one is '' - never another store's value) // K2BPrint.esc(s) HTML-escape: EVERY branding value printed goes through it // K2BPrint.header(nameOverride | { nameOverride, logo }) the .dealer-head block (store 716 prints exactly what RC34 printed) // K2BPrint.foot(rightHtml, { gm: 'bac+code' | 'code' }) '
' } when the store prints GM codes, else null
// K2BPrint.classicHead(title, sub) the old-DMS report head (Classic Reports) with the store's print name
// K2BPrint.checkCompany(checkConfig) the company lines on checks: Check setup -> the store's check header -> print name + address
// K2BPrint.doc(bodyHtml, opts) the print window (popup, or the in-app overlay) - window.k2bPrint is this
// K2BPrint.register({ id, title, group, regulated, headerAllowed, render(data, brand) }) / print(id, data) / catalog()
// test hook: window.__k2bPrintCapture = true -> doc() opens no window; K2BPrint.lastDoc() returns the whole HTML
//
// Regulated forms (retail installment contract, odometer, FTC Buyers Guide, title / tax, lender forms, privacy notice) keep their
// official wording and layout: the framework fills only the dealer identity they always had (and the header only where RC34 had one).
const K2BPrint = (() => {
const S = () => window.K2BStore || null;
const esc = (s) => String(s == null ? "" : s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
const brand = () => (S() ? S().branding() : { dba: "K2B DMS", printName: "K2B DMS", legalName: "", code: "K2B", tagline: "", addr1: "", addr2: "", street: "", city: "", state: "", zip: "", phone: "", fax: "", web: "", licenseLine: "", licenseNo: "", ein: "", stateEmployerId: "", gm: { bac: "", dealerCode: "", zone: "", region: "" }, colors: { primary: "#0E1430", accent: "#FFD24A" }, logo: null, checkHeader: null, docHeader: { showLogo: true, showTagline: true, showGmCodes: true, footerLine: "" } });
const storeId = () => (S() && S().pageId()) || "";
const franchise = () => !!(S() && S().franchise());
/** the GM codes as RC34 printed them - only for a store that shows GM items and prints its codes */
const gm = (bo, fr) => {
const b = bo || brand();
if (!(fr === undefined ? franchise() : !!fr) || !b.docHeader.showGmCodes) return null;
return { bac: b.gm.bac ? "GM BAC " + b.gm.bac : "", code: b.gm.dealerCode ? "Dealer Code " + b.gm.dealerCode : "" };
};
/** the logo for a print window: the 716 seed's file path (exactly RC34), an uploaded logo as a data: URL, else the monogram */
const logoSrc = (bo) => {
const st = S();
if (!st) return "";
const b = bo || brand();
if (bo) return (b.logo && b.logo.kind === "asset") ? (b.logo.large || b.logo.small || "") : (b.logo && b.logo.previewUrl) ? b.logo.previewUrl : st.monogramUrl(110);
if (b.logo && b.logo.kind === "asset") return st.logoUrl("large") || "";
if (b.logo && b.logo.kind === "upload") { const d = st.logoDataUrlNow && st.logoDataUrlNow(); if (d) return d; }
return st.monogramUrl(110);
};
const party = {
legal: () => brand().legalName, print: () => brand().printName, dba: () => brand().dba,
addr1: () => brand().addr1, addr2: () => brand().addr2,
addressLine: () => [brand().addr1, brand().addr2].filter(Boolean).join(", "),
address2: () => [brand().addr1, brand().addr2].filter(Boolean).join("
"),
phone: () => brand().phone, license: () => brand().licenseLine, licenseNo: () => brand().licenseNo,
};
function header(arg) {
const o = (arg && typeof arg === "object") ? arg : { nameOverride: arg };
const b = o.brand && S() ? S().fill(o.brand) : brand(); // o.brand: the Stores card previews its draft
if (o.brand && o.brand.logo && o.brand.logo.previewUrl) b.logo = o.brand.logo;
const showLogo = o.logo !== false && b.docHeader.showLogo !== false;
const showTag = b.docHeader.showTagline !== false && !!b.tagline;
const g = o.brand ? gm(b, o.franchise) : gm();
const line3 = "Phone " + esc(b.phone) + (b.fax ? " · Fax " + esc(b.fax) : "");
const line4 = [esc(b.licenseLine)].concat(g ? [g.bac, g.code].filter(Boolean).map(esc) : []).filter(Boolean).join(" · ");
return `
${showLogo ? `
` : ""}
${o.nameOverride ? esc(o.nameOverride) : esc(b.printName)}
${showTag ? ` ${esc(b.tagline)}
` : ""}
${esc(b.addr1)}
${esc(b.addr2)}
${line3}
${line4}
`;
}
/** the footer's left part: the license line [ · GM BAC ... · Dealer Code ...] */
function footLeft(opts) {
const b = brand(); const g = gm(); const o = opts || {};
const parts = [esc(b.licenseLine)];
if (g && o.gm === "bac+code") { if (g.bac) parts.push(esc(g.bac)); if (g.code) parts.push(esc(g.code)); }
else if (g && o.gm === "code") { if (g.code) parts.push(esc(g.code)); }
return parts.join(" · ");
}
const foot = (rightHtml, opts) => `${footLeft(opts)}${rightHtml == null ? "" : rightHtml}`;
const classicHead = (title, sub, today, time) => `${today || ""}
K2B${esc(brand().printName)}
${esc(title)}${sub ? "
" + esc(sub) : ""}${time || ""}
PAGE: 1`;
function checkCompany(cfg) {
const c = (cfg && cfg.company && typeof cfg.company === "object") ? cfg.company : {};
const b = brand(); const h = b.checkHeader || null;
const pick = (k, fall) => (c[k] ? c[k] : (h && h[k]) ? h[k] : fall);
return { name: pick("name", b.printName), addr1: pick("addr1", b.addr1), addr2: pick("addr2", b.addr2),
phone: c.phone != null ? c.phone : (h && h.phone) ? h.phone : b.phone }; // a Check setup phone of "" = no phone line (as RC34)
}
/* ── the print shell (was window.k2bPrint in printables.jsx) ── */
const CSS = `
* { box-sizing: border-box; }
@page { size: Letter; margin: 0.4in 0.45in 0.45in 0.45in; }
html, body { margin: 0; padding: 0; background: #fff; color: #000;
font-family: "Helvetica Neue", Arial, sans-serif; font-size: 9.5pt; line-height: 1.32; }
.sheet { width: 7.6in; margin: 0 auto; padding: 0; }
.crest { width: 78px; height: 78px; }
.dealer-logo { width: 110px; height: auto; display: block; }
.dealer-head { display: flex; gap: 14px; align-items: center; border-bottom: 2px solid @P; padding-bottom: 10px; margin-bottom: 12px; }
.dealer-head .dealer-text { display: flex; flex-direction: column; }
.dealer-head .name { font-family: Georgia, serif; font-size: 22pt; font-weight: 700;
letter-spacing: 0.5px; color: @P; line-height: 1; }
.dealer-head .tag { font-size: 9pt; color: #555; letter-spacing: 2px; text-transform: uppercase; margin-top: 4px; }
.dealer-head .info { margin-left: auto; text-align: right; font-size: 8.5pt; line-height: 1.4; color: #222; }
.doc-title { text-align: center; font-size: 14pt; font-weight: 700; letter-spacing: 2px; margin: 6px 0 10px; color: @P; }
.doc-sub { text-align: center; font-size: 8pt; color: #666; margin-bottom: 14px; letter-spacing: 1px; }
.field-grid { display: grid; gap: 0; border: 1px solid #000; }
.fg-row { display: grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid #000; }
.fg-row:last-child { border-bottom: 0; }
.fg-cell { padding: 4px 7px; border-right: 1px solid #000; min-height: 28px; }
.fg-cell:last-child { border-right: 0; }
.fg-cell .lab { font-size: 7pt; letter-spacing: 1px; text-transform: uppercase; color: #555; display: block; margin-bottom: 1px; }
.fg-cell .val { font-size: 11pt; color: #000; min-height: 14px; }
.fg-cell .val.mono { font-family: "Courier New", monospace; }
h3.sect { font-size: 9pt; letter-spacing: 2px; text-transform: uppercase;
background: @P; color: #fff; padding: 3px 8px; margin: 14px 0 0; }
table.totals { width: 100%; border-collapse: collapse; margin-top: 0; border: 1px solid #000; border-top: 0; }
table.totals td { padding: 3px 8px; border-bottom: 1px solid #ccc; font-size: 10pt; }
table.totals td.lab { width: 70%; }
table.totals td.val { text-align: right; font-family: "Courier New", monospace; }
table.totals tr.total td { font-weight: 700; font-size: 12pt; background: #f3edd9; border-top: 2px solid @P; }
.sig-row { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; margin-top: 28px; }
.sig-box { border-top: 1px solid #000; padding-top: 4px; font-size: 8pt; color: #555; letter-spacing: 1px; text-transform: uppercase; }
.legalese { font-size: 7pt; color: #444; line-height: 1.35; margin-top: 14px; text-align: justify; }
.foot { margin-top: 18px; display: flex; justify-content: space-between; font-size: 7pt; color: #888; letter-spacing: 1px; border-top: 1px solid #ccc; padding-top: 5px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; }
.grid-3 .fg-cell { border-right: 1px solid #000; border-bottom: 1px solid #000; }
.grid-3 .fg-cell:nth-child(3n) { border-right: 0; }
.print-only-btn { margin: 10px auto; display: block; padding: 8px 18px;
background: @A; color: @P; border: 1px solid @P; font-weight: 700;
font-family: Arial, sans-serif; font-size: 10pt; cursor: pointer; letter-spacing: 1px; }
@media print {
.print-only-btn { display: none !important; }
}
`;
/** the print stylesheet in the store's two colors (716's seed colors = exactly RC34's) */
const css = () => { const c = brand().colors; return CSS.replace(/@P/g, c.primary).replace(/@A/g, c.accent); };
let __last = "";
function doc(bodyHtml, opts = {}) {
const ov = window.jbFormOverrides ? window.jbFormOverrides(opts.formKey) : {};
let ovCss = "";
if (ov.fontScale && +ov.fontScale !== 1) ovCss += `.sheet{zoom:${+ov.fontScale};}`;
if (ov.topMargin) ovCss += `.sheet{padding-top:${+ov.topMargin}mm !important;}`;
if (ov.hideHeader) ovCss += `.dealer-head{display:none !important;}`;
const e2 = (s) => String(s).replace(/&/g, "&").replace(//g, ">");
if (ov.dealerText) bodyHtml += `${e2(ov.dealerText)}
`;
if (ov.footerLine) bodyHtml += `${e2(ov.footerLine)}`;
const title = opts.title || "Print Preview";
const baseHref = window.location.href;
const sid = storeId();
const docHtml = `
${title} — ${esc(brand().dba)}
${bodyHtml}`;
__last = docHtml;
if (window.__k2bPrintCapture) return;
// Try a real popup first (best for actual printing). If blocked, fall back
// to an in-app preview overlay with an embedded iframe — so a print view
// ALWAYS appears, even when pop-ups are disabled or we're inside a sandbox.
let w = null;
try { w = window.open("", "_blank", "width=900,height=1100"); } catch (e) { w = null; }
if (w && w.document) {
w.document.write(docHtml.replace("