| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function applyCSS(el, styles) { |
| 5 |
Object.keys(styles).forEach(function (k) { el.style[k] = styles[k]; }); |
| 6 |
} |
| 7 |
|
| 8 |
function makeEl(tag, className, styles) { |
| 9 |
var el = document.createElement(tag); |
| 10 |
if (className) el.className = className; |
| 11 |
if (styles) applyCSS(el, styles); |
| 12 |
return el; |
| 13 |
} |
| 14 |
|
| 15 |
function makeText(tag, className, text, styles) { |
| 16 |
var el = makeEl(tag, className, styles); |
| 17 |
el.textContent = text; |
| 18 |
return el; |
| 19 |
} |
| 20 |
|
| 21 |
function typoCssVarsForEl(typo, prefix, el) { |
| 22 |
if (!typo) return; |
| 23 |
if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif"); |
| 24 |
if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight); |
| 25 |
if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform); |
| 26 |
if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style); |
| 27 |
if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration); |
| 28 |
var su = typo.sizeUnit || 'px'; |
| 29 |
if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su); |
| 30 |
if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su); |
| 31 |
if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su); |
| 32 |
var lhu = typo.lineHeightUnit || ''; |
| 33 |
if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu); |
| 34 |
if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu); |
| 35 |
if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu); |
| 36 |
var lsu = typo.letterSpacingUnit || 'px'; |
| 37 |
if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu); |
| 38 |
if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu); |
| 39 |
if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu); |
| 40 |
var wsu = typo.wordSpacingUnit || 'px'; |
| 41 |
if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu); |
| 42 |
if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu); |
| 43 |
if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu); |
| 44 |
} |
| 45 |
|
| 46 |
function init() { |
| 47 |
document.querySelectorAll('.bkbg-cert-app').forEach(function (appEl) { |
| 48 |
if (appEl.dataset.rendered) return; |
| 49 |
appEl.dataset.rendered = '1'; |
| 50 |
|
| 51 |
var opts; |
| 52 |
try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; } |
| 53 |
var o = Object.assign({ |
| 54 |
style: 'classic', |
| 55 |
badgeEmoji: '🏆', |
| 56 |
showBadge: true, |
| 57 |
preTitle: 'Certificate of Completion', |
| 58 |
awardedTo: 'This certifies that', |
| 59 |
recipientName: 'Jane Doe', |
| 60 |
courseTitle: 'Advanced Web Development', |
| 61 |
courseDesc: 'has successfully completed the full course curriculum.', |
| 62 |
issuerName: 'Blockenberg Academy', |
| 63 |
issuerLogoUrl: '', |
| 64 |
completionDate: '', |
| 65 |
credentialId: '', |
| 66 |
showCredentialId: true, |
| 67 |
showDate: true, |
| 68 |
showSignature1: true, |
| 69 |
signature1Name: 'Alex Johnson', |
| 70 |
signature1Title: 'Course Director', |
| 71 |
showSignature2: false, |
| 72 |
signature2Name: '', |
| 73 |
signature2Title: '', |
| 74 |
fontFamily: 'serif', |
| 75 |
maxWidth: 800, |
| 76 |
bgColor: '#fffef7', |
| 77 |
borderColor: '#c9a84c', |
| 78 |
accentColor: '#8b6914', |
| 79 |
preTitleColor: '#8b6914', |
| 80 |
recipientColor: '#1e293b', |
| 81 |
courseTitleColor: '#1a3a6b', |
| 82 |
descColor: '#475569', |
| 83 |
issuerColor: '#1e293b', |
| 84 |
metaColor: '#64748b', |
| 85 |
borderWidth: 6, |
| 86 |
borderRadius: 4, |
| 87 |
innerBorder: true, |
| 88 |
shadowSize: 2 |
| 89 |
}, opts); |
| 90 |
|
| 91 |
var fontFamily = o.fontFamily === 'serif' |
| 92 |
? 'Georgia, "Times New Roman", serif' |
| 93 |
: 'system-ui, -apple-system, sans-serif'; |
| 94 |
|
| 95 |
var shadowVal = o.shadowSize > 0 |
| 96 |
? '0 ' + (o.shadowSize * 4) + 'px ' + (o.shadowSize * 16) + 'px rgba(0,0,0,.' + (o.shadowSize * 4) + ')' |
| 97 |
: 'none'; |
| 98 |
|
| 99 |
// Outer wrap |
| 100 |
var wrap = makeEl('div', 'bkbg-cert-wrap bkbg-cert-style-' + o.style); |
| 101 |
applyCSS(wrap, { |
| 102 |
background: o.bgColor, |
| 103 |
border: o.borderWidth + 'px solid ' + o.borderColor, |
| 104 |
borderRadius: o.borderRadius + 'px', |
| 105 |
maxWidth: o.maxWidth + 'px', |
| 106 |
margin: '0 auto', |
| 107 |
padding: '48px 56px', |
| 108 |
position: 'relative', |
| 109 |
fontFamily: fontFamily, |
| 110 |
boxShadow: shadowVal, |
| 111 |
boxSizing: 'border-box', |
| 112 |
textAlign: 'center' |
| 113 |
}); |
| 114 |
|
| 115 |
typoCssVarsForEl(o.typoRecipient, '--bkbg-cert-r-', wrap); |
| 116 |
typoCssVarsForEl(o.typoCourseTitle, '--bkbg-cert-ct-', wrap); |
| 117 |
typoCssVarsForEl(o.typoBody, '--bkbg-cert-bd-', wrap); |
| 118 |
|
| 119 |
// Inner border |
| 120 |
if (o.innerBorder) { |
| 121 |
var ib = makeEl('div', 'bkbg-cert-inner-border'); |
| 122 |
applyCSS(ib, { |
| 123 |
position: 'absolute', |
| 124 |
inset: '10px', |
| 125 |
border: '1px solid ' + o.borderColor, |
| 126 |
pointerEvents: 'none', |
| 127 |
borderRadius: o.borderRadius + 'px' |
| 128 |
}); |
| 129 |
wrap.appendChild(ib); |
| 130 |
} |
| 131 |
|
| 132 |
// Corner decorations (classic only) |
| 133 |
if (o.style === 'classic') { |
| 134 |
var cornerPositions = ['tl', 'tr', 'bl', 'br']; |
| 135 |
cornerPositions.forEach(function (pos) { |
| 136 |
var c = makeEl('span', 'bkbg-cert-corner ' + pos); |
| 137 |
c.textContent = '✦'; |
| 138 |
c.style.color = o.accentColor; |
| 139 |
wrap.appendChild(c); |
| 140 |
}); |
| 141 |
} |
| 142 |
|
| 143 |
// Body |
| 144 |
var body = makeEl('div', 'bkbg-cert-body'); |
| 145 |
body.style.position = 'relative'; |
| 146 |
body.style.zIndex = '1'; |
| 147 |
|
| 148 |
// Badge |
| 149 |
if (o.showBadge) { |
| 150 |
var badge = makeEl('div', 'bkbg-cert-badge'); |
| 151 |
applyCSS(badge, { |
| 152 |
borderColor: o.accentColor, |
| 153 |
color: o.accentColor, |
| 154 |
margin: '0 auto 16px' |
| 155 |
}); |
| 156 |
badge.textContent = o.badgeEmoji; |
| 157 |
body.appendChild(badge); |
| 158 |
} |
| 159 |
|
| 160 |
// Pre-title |
| 161 |
body.appendChild(makeText('div', 'bkbg-cert-pretitle', o.preTitle, { color: o.preTitleColor })); |
| 162 |
|
| 163 |
// Awarded to |
| 164 |
body.appendChild(makeText('div', 'bkbg-cert-awarded-to', o.awardedTo, { color: o.descColor })); |
| 165 |
|
| 166 |
// Recipient name |
| 167 |
body.appendChild(makeText('div', 'bkbg-cert-recipient', o.recipientName, { color: o.recipientColor })); |
| 168 |
|
| 169 |
// Course title |
| 170 |
body.appendChild(makeText('div', 'bkbg-cert-course', o.courseTitle, { color: o.courseTitleColor })); |
| 171 |
|
| 172 |
// Description |
| 173 |
if (o.courseDesc) { |
| 174 |
body.appendChild(makeText('div', 'bkbg-cert-desc', o.courseDesc, { color: o.descColor })); |
| 175 |
} |
| 176 |
|
| 177 |
// Divider |
| 178 |
var mkDivider = function () { |
| 179 |
var d = makeEl('div', 'bkbg-cert-divider'); |
| 180 |
d.style.setProperty('--bkbg-cert-accent', o.accentColor); |
| 181 |
d.style.background = o.accentColor; |
| 182 |
return d; |
| 183 |
}; |
| 184 |
body.appendChild(mkDivider()); |
| 185 |
|
| 186 |
// Signatures |
| 187 |
if (o.showSignature1 || o.showSignature2) { |
| 188 |
var sigsRow = makeEl('div', 'bkbg-cert-sigs-row'); |
| 189 |
|
| 190 |
function makeSig(name, title) { |
| 191 |
var sig = makeEl('div', 'bkbg-cert-sig'); |
| 192 |
var line = makeEl('div', 'bkbg-cert-sig-line'); |
| 193 |
line.style.borderColor = o.metaColor; |
| 194 |
sig.appendChild(line); |
| 195 |
sig.appendChild(makeText('div', 'bkbg-cert-sig-name', name, { color: o.issuerColor })); |
| 196 |
sig.appendChild(makeText('div', 'bkbg-cert-sig-title', title, { color: o.metaColor })); |
| 197 |
return sig; |
| 198 |
} |
| 199 |
|
| 200 |
if (o.showSignature1) sigsRow.appendChild(makeSig(o.signature1Name, o.signature1Title)); |
| 201 |
if (o.showSignature2) sigsRow.appendChild(makeSig(o.signature2Name, o.signature2Title)); |
| 202 |
body.appendChild(sigsRow); |
| 203 |
} |
| 204 |
|
| 205 |
body.appendChild(mkDivider()); |
| 206 |
|
| 207 |
// Issuer row |
| 208 |
var issuerRow = makeEl('div', 'bkbg-cert-issuer-row'); |
| 209 |
if (o.issuerLogoUrl) { |
| 210 |
var logo = document.createElement('img'); |
| 211 |
logo.src = o.issuerLogoUrl; |
| 212 |
logo.alt = o.issuerName; |
| 213 |
logo.className = 'bkbg-cert-logo'; |
| 214 |
issuerRow.appendChild(logo); |
| 215 |
} |
| 216 |
issuerRow.appendChild(makeText('div', 'bkbg-cert-issuer-name', o.issuerName, { color: o.issuerColor })); |
| 217 |
body.appendChild(issuerRow); |
| 218 |
|
| 219 |
// Meta row |
| 220 |
if (o.showDate || o.showCredentialId) { |
| 221 |
var metaRow = makeEl('div', 'bkbg-cert-meta'); |
| 222 |
metaRow.style.color = o.metaColor; |
| 223 |
|
| 224 |
if (o.showDate && o.completionDate) { |
| 225 |
var dateSpan = document.createElement('span'); |
| 226 |
dateSpan.className = 'bkbg-cert-date'; |
| 227 |
var dateLabel = makeText('span', 'bkbg-cert-meta-label', 'Date: '); |
| 228 |
var dateVal = document.createElement('span'); |
| 229 |
dateVal.textContent = o.completionDate; |
| 230 |
dateSpan.appendChild(dateLabel); |
| 231 |
dateSpan.appendChild(dateVal); |
| 232 |
metaRow.appendChild(dateSpan); |
| 233 |
} |
| 234 |
|
| 235 |
if (o.showCredentialId && o.credentialId) { |
| 236 |
var credSpan = document.createElement('span'); |
| 237 |
credSpan.className = 'bkbg-cert-cred'; |
| 238 |
var credLabel = makeText('span', 'bkbg-cert-meta-label', 'Credential ID: '); |
| 239 |
var credVal = document.createElement('span'); |
| 240 |
credVal.textContent = o.credentialId; |
| 241 |
credSpan.appendChild(credLabel); |
| 242 |
credSpan.appendChild(credVal); |
| 243 |
metaRow.appendChild(credSpan); |
| 244 |
} |
| 245 |
|
| 246 |
body.appendChild(metaRow); |
| 247 |
} |
| 248 |
|
| 249 |
wrap.appendChild(body); |
| 250 |
|
| 251 |
appEl.parentNode.insertBefore(wrap, appEl); |
| 252 |
appEl.style.display = 'none'; |
| 253 |
}); |
| 254 |
} |
| 255 |
|
| 256 |
if (document.readyState !== 'loading') { init(); } |
| 257 |
else { document.addEventListener('DOMContentLoaded', init); } |
| 258 |
})(); |
| 259 |
|