function autoFitText(element, text, maxFontSize = 10, minFontSize = 3.5, isMultiline = false, useEllipsis = false) { if (!element) return; element.textContent = text; // Add flex styling for consistent single-line alignment as in original script if (!isMultiline) { element.style.display = 'flex'; element.style.alignItems = 'center'; element.style.lineHeight = '1'; element.style.whiteSpace = 'nowrap'; } else { element.style.whiteSpace = 'normal'; element.style.wordBreak = 'break-word'; } element.style.fontSize = maxFontSize + 'pt'; element.style.overflow = 'hidden'; if (useEllipsis) { element.style.textOverflow = 'ellipsis'; element.classList.add('dynamic-text'); return; } element.style.textOverflow = 'clip'; // Temporarily force flex-start so scrollWidth measures properly (bypasses right-align overflow bugs) const originalJustify = element.style.justifyContent; if (!isMultiline) { element.style.justifyContent = 'flex-start'; } let fontSize = maxFontSize; while (fontSize >= minFontSize) { if (element.scrollWidth <= element.clientWidth + 1 && element.scrollHeight <= element.clientHeight + 1) { break; } fontSize -= 0.25; element.style.fontSize = fontSize + 'pt'; } // Restore original justification if (!isMultiline) { element.style.justifyContent = originalJustify; } element.classList.add('dynamic-text'); } function updatePreview(data) { if (!data) return; // Apply label font settings before measuring (affects autoFitText width calculations) const labelFont = document.getElementById('label-font-select')?.value || 'Arial'; const labelBold = document.getElementById('label-bold-toggle')?.checked ? 'bold' : 'normal'; const labelItalic = document.getElementById('label-italic-toggle')?.checked ? 'italic' : 'normal'; ['.label-preview', '.label-preview-large', '.label-preview-xlarge'].forEach(sel => { const el = document.querySelector(sel); if (el) { el.style.fontFamily = labelFont; el.style.fontWeight = labelBold; el.style.fontStyle = labelItalic; } }); // Un-hide parent containers to allow element dimensions to be calculated correctly const hiddenParents = []; const elementsToCheck = [document.querySelector('.label-preview'), document.querySelector('.label-preview-large'), document.querySelector('.label-preview-xlarge')]; elementsToCheck.forEach(el => { let current = el; while (current && current !== document.body) { const style = window.getComputedStyle(current); if (style.display === 'none') { hiddenParents.push({ el: current, display: current.style.display, visibility: current.style.visibility, position: current.style.position }); current.style.display = 'block'; current.style.visibility = 'hidden'; current.style.position = 'absolute'; } current = current.parentElement; } }); // Per-field style overrides from settings const lfs = window.labelFieldStyles || {}; const applyFieldStyle = (selector, fieldKey) => { const s = lfs[fieldKey] || {}; [selector, selector + '-large', selector + '-xlarge'].forEach(sel => { const el = document.querySelector(sel); if (!el) return; if (s.b) el.style.fontWeight = 'bold'; else el.style.fontWeight = ''; if (s.i) el.style.fontStyle = 'italic'; else el.style.fontStyle = ''; }); }; ['artist','title','genre','style','format','price','condition','comment','notes'].forEach(f => applyFieldStyle('.print-' + f, f)); applyFieldStyle('.print-bottom-info', 'info'); const updateAllSizes = (selector, text, maxFontSize = 10, minFontSize = 3.5, isMultiline = false, fieldKey = null) => { const elSmall = document.querySelector(selector); const elLarge = document.querySelector(selector + '-large'); const elXLarge = document.querySelector(selector + '-xlarge'); const useEllipsis = fieldKey ? !!(lfs[fieldKey] && lfs[fieldKey].e) : false; autoFitText(elSmall, text, maxFontSize, minFontSize, isMultiline, useEllipsis); autoFitText(elLarge, text, maxFontSize + 1.5, minFontSize, isMultiline, useEllipsis); autoFitText(elXLarge, text, maxFontSize + 3, minFontSize, isMultiline, useEllipsis); }; updateAllSizes('.print-artist', data.artist || '', 8.5, 3.5, false, 'artist'); updateAllSizes('.print-title', data.title || '', 8.5, 3.5, false, 'title'); updateAllSizes('.print-genre', data.genre || '', 8.5, 3.5, false, 'genre'); // Shorten 'Progressive' to 'Prog' for label preview const styleText = (data.style || '').replace(/Progressive/g, 'Prog'); updateAllSizes('.print-style', styleText, 8.5, 3.5, false, 'style'); let formatText; if (typeof data.format === 'string' && data.format.trim()) { formatText = data.format; } else { const format = (data.formats || [])[0] || {}; const formatDesc = format.descriptions ? format.descriptions.join(' ') : ''; formatText = `${data.format_quantity || 1}x ${format.name || ''} ${formatDesc}`.trim(); } // Use smaller base for size logic: description uses 6pt / 8pt max usually updateAllSizes('.print-description', formatText, 6.5, 3.5, true, 'format'); updateAllSizes('.print-comment', data.comment || '', 7, 3.5, false, 'comment'); updateAllSizes('.print-notes', data.notes || '', 6, 3.5, false, 'notes'); ['', '-large', '-xlarge'].forEach(suffix => { const bottomInfoDiv = document.querySelector(`.print-bottom-info${suffix}`); if (bottomInfoDiv) { const yearElement = bottomInfoDiv.querySelector('.year'); const countryElement = bottomInfoDiv.querySelector('.country'); const labelElement = bottomInfoDiv.querySelector('.label'); const hasYear = data.year && data.year !== 0; // Toggle no-year class to hide year span and its separator bottomInfoDiv.classList.toggle('no-year', !hasYear); if (yearElement) { yearElement.textContent = hasYear ? data.year.toString() : ''; } if (countryElement && labelElement) { const countryText = data.country || ''; // Remove ' Records' from label text for print preview only const labelText = (data.label || '').replace(/ Records$/, ''); countryElement.textContent = countryText; labelElement.textContent = labelText; // Dynamically fit the entire row by shrinking only the label bottomInfoDiv.style.whiteSpace = 'nowrap'; bottomInfoDiv.style.overflow = 'hidden'; let maxFont = suffix === '-xlarge' ? 9 : (suffix ? 8 : 7); let labelFontSize = maxFont; countryElement.style.fontSize = maxFont + 'pt'; labelElement.style.fontSize = labelFontSize + 'pt'; while (labelFontSize >= 3) { if (bottomInfoDiv.scrollWidth <= bottomInfoDiv.clientWidth + 1) { break; } labelFontSize -= 0.25; labelElement.style.fontSize = labelFontSize + 'pt'; } } } }); ['', '-large', '-xlarge'].forEach(suffix => { const conditionElement = document.querySelector(`.print-condition${suffix}`); if (conditionElement) { const mediaMatch = data.mediaCondition?.match(/\((.*?)\)/); const sleeveMatch = data.sleeveCondition?.match(/\((.*?)\)/); const mediaCondition = mediaMatch ? mediaMatch[1] : data.mediaCondition; const sleeveCondition = sleeveMatch ? sleeveMatch[1] : data.sleeveCondition; const condText = `${mediaCondition} | ${sleeveCondition}`; const condS = lfs['condition'] || {}; if (condS.b) conditionElement.style.fontWeight = 'bold'; else conditionElement.style.fontWeight = ''; if (condS.i) conditionElement.style.fontStyle = 'italic'; else conditionElement.style.fontStyle = ''; autoFitText(conditionElement, condText, suffix === '-xlarge' ? 8.5 : (suffix ? 7.5 : 6), 3.5, false, !!condS.e); } }); ['', '-large', '-xlarge'].forEach(suffix => { const priceElement = document.querySelector(`.print-price${suffix}`); if (priceElement && data.price) { const priceText = String(data.price).startsWith('$') ? data.price : `$${data.price}`; const priceS = lfs['price'] || {}; if (priceS.b) priceElement.style.fontWeight = 'bold'; else priceElement.style.fontWeight = ''; if (priceS.i) priceElement.style.fontStyle = 'italic'; else priceElement.style.fontStyle = ''; autoFitText(priceElement, priceText, suffix === '-xlarge' ? 15 : (suffix ? 13 : 10), 5, false, !!priceS.e); } }); ['', '-large', '-xlarge'].forEach(suffix => { const barcodeElement = document.querySelector(`.print-barcode${suffix}`); if (barcodeElement) { let qrData = data.sku != null ? data.sku : data.id; // Labels always show the bare integer SKU — strip -R and -A suffixes unconditionally if (typeof qrData === 'string') qrData = qrData.replace(/-[AR]$/i, ''); const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${qrData}`; barcodeElement.innerHTML = `${qrData}`; } }); // Restore hidden parents for (let i = hiddenParents.length - 1; i >= 0; i--) { const p = hiddenParents[i]; p.el.style.display = p.display; p.el.style.visibility = p.visibility; p.el.style.position = p.position; } } function displayReleaseInfo(data) { const releaseDetailsContainer = document.getElementById('releaseDetails'); if (!releaseDetailsContainer) return; const fields = [ { key: 'artist', value: data.artist }, { key: 'title', value: data.title }, { key: 'genre', value: data.genre }, { key: 'style', value: data.style }, { key: 'label', value: (data.label || '').replace(/ Records$/, '') }, { key: 'year', value: data.year === 0 ? '' : data.year }, { key: 'country', value: data.country || 'N/A' }, { key: 'format', value: `${data.format_quantity || 1} x ${data.formats.map(f => `${f.name} (${f.descriptions?.join(', ') || ''})`).join(', ')}` }, { key: 'catno', value: data.labels ? data.labels[0].catno : 'N/A' }, { key: 'releaseId', value: data.id }, { key: 'comment', value: data.comment }, { key: 'appleId', value: data.appleId || 'N/A' }, { key: 'collectionSku', value: data.collectionSku || '' }, ]; fields.forEach(field => { const fieldElement = releaseDetailsContainer.querySelector(`.info-field[data-field="${field.key}"] .value`); if (fieldElement) { fieldElement.textContent = field.value; } }); const marketInfoContainer = document.querySelector('.market-info'); if (marketInfoContainer) { marketInfoContainer.innerHTML = `

Market Information:

Have: ${data.community?.have || 0}
Want: ${data.community?.want || 0}
For Sale: ${data.num_for_sale || 0}
Price Range: ${data.sellerPriceRange || 'N/A'}
Lowest Price: ${data.lowPrice || 'N/A'}
Last Sold: ${data.lastSold || 'N/A'}
Low Price: ${data.lowPrice || 'N/A'}
Median Price: ${data.medianPrice || 'N/A'}
High Price: ${data.highPrice || 'N/A'}

Suggested Prices:

Mint (M): ${data.priceSuggestions?.['Mint (M)']?.value?.toFixed(2) || 'N/A'}
Near Mint (NM or M-): ${data.priceSuggestions?.['Near Mint (NM or M-)']?.value?.toFixed(2) || 'N/A'}
Very Good Plus (VG+): ${data.priceSuggestions?.['Very Good Plus (VG+)']?.value?.toFixed(2) || 'N/A'}
Very Good (VG): ${data.priceSuggestions?.['Very Good (VG)']?.value?.toFixed(2) || 'N/A'}
Good Plus (G+): ${data.priceSuggestions?.['Good Plus (G+)']?.value?.toFixed(2) || 'N/A'}
Good (G): ${data.priceSuggestions?.['Good (G)']?.value?.toFixed(2) || 'N/A'}
Fair (F): ${data.priceSuggestions?.['Fair (F)']?.value?.toFixed(2) || 'N/A'}
Poor (P): ${data.priceSuggestions?.['Poor (P)']?.value?.toFixed(2) || 'N/A'}
`; } const tracklistDiv = document.querySelector('.tracklist'); if (tracklistDiv) { const tracklistHTML = Array.isArray(data.tracklist) ? data.tracklist.map(entry => { if (entry.type_ === "heading") { return `
${entry.title}
`; } else if (entry.type_ === "track") { const artists = Array.isArray(entry.artists) ? entry.artists.map(artist => artist.name.replace(/\(\d+\)/, '')).join(', ') : ''; return `
Track ${entry.position}: ${entry.title}${artists ? ' - ' + artists : ''}
`; } return ''; }).join('') : ''; tracklistDiv.innerHTML = `

Tracklist:

${tracklistHTML} `; } } function generateLabel() { const labelWindow = window.open('', '_blank', 'width=600,height=600'); if (labelWindow) { const labelSize = document.getElementById('labelSize')?.value || 'large'; const cssFile = labelSize === 'xlarge' ? 'xlarge.css' : (labelSize === 'large' ? 'large.css' : 'small.css'); const previewSel = labelSize === 'xlarge' ? '.label-preview-xlarge' : (labelSize === 'large' ? '.label-preview-large' : '.label-preview'); const pageDims = labelSize === 'xlarge' ? '64mm 34mm' : (labelSize === 'large' ? '54mm 25mm' : '51mm 19mm'); const labelClass = labelSize === 'xlarge' ? 'print-label-xlarge' : (labelSize === 'large' ? 'print-label-large' : 'print-label'); const labelW = labelSize === 'xlarge' ? '64mm' : (labelSize === 'large' ? '54mm' : '51mm'); const labelH = labelSize === 'xlarge' ? '34mm' : (labelSize === 'large' ? '25mm' : '19mm'); const printFont = document.getElementById('label-font-select')?.value || '"MS Gothic","Hiragino Kaku Gothic Pro",Arial,sans-serif'; const printWeight = document.getElementById('label-bold-toggle')?.checked ? 'bold' : 'normal'; const printStyle = document.getElementById('label-italic-toggle')?.checked ? 'italic' : 'normal'; const labelContent = document.querySelector(previewSel).innerHTML; labelWindow.document.write(`
${labelContent}
`); labelWindow.document.close(); } } function generateLabel203dpi() { const labelWindow = window.open('', '_blank', 'width=600,height=600'); if (!labelWindow) return; const labelSize = document.getElementById('labelSize')?.value || 'large'; const cssFile = labelSize === 'xlarge' ? 'xlarge.css' : (labelSize === 'large' ? 'large.css' : 'small.css'); const previewSel = labelSize === 'xlarge' ? '.label-preview-xlarge' : (labelSize === 'large' ? '.label-preview-large' : '.label-preview'); const pageDims = labelSize === 'xlarge' ? '64mm 34mm' : (labelSize === 'large' ? '54mm 25mm' : '51mm 19mm'); const labelClass = labelSize === 'xlarge' ? 'print-label-xlarge' : (labelSize === 'large' ? 'print-label-large' : 'print-label'); const labelW = labelSize === 'xlarge' ? '64mm' : (labelSize === 'large' ? '54mm' : '51mm'); const labelH = labelSize === 'xlarge' ? '34mm' : (labelSize === 'large' ? '25mm' : '19mm'); const printFont = document.getElementById('label-font-select')?.value || '"MS Gothic","Hiragino Kaku Gothic Pro",Arial,sans-serif'; const printWeight = document.getElementById('label-bold-toggle')?.checked ? 'bold' : 'normal'; const printStyle = document.getElementById('label-italic-toggle')?.checked ? 'italic' : 'normal'; const labelContent = document.querySelector(previewSel).innerHTML; labelWindow.document.write(`
${labelContent}
`); labelWindow.document.close(); }