/* 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' }) '
license line[ · GM codes]right
' // K2BPrint.party.legal() / print() / dba() / addressLine() / address2() / phone() / license() / licenseNo() / addr1() / addr2() // K2BPrint.gm() { bac:'GM BAC ', code:'Dealer 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("", `