| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
var _typoKeys = { |
| 4 |
family: 'font-family', weight: 'font-weight', style: 'font-style', |
| 5 |
decoration: 'text-decoration', transform: 'text-transform', |
| 6 |
sizeDesktop: 'font-size-d', sizeTablet: 'font-size-t', sizeMobile: 'font-size-m', |
| 7 |
lineHeightDesktop: 'line-height-d', lineHeightTablet: 'line-height-t', lineHeightMobile: 'line-height-m', |
| 8 |
letterSpacing: 'letter-spacing', wordSpacing: 'word-spacing' |
| 9 |
}; |
| 10 |
function typoCssVarsForEl(el, typo, prefix) { |
| 11 |
if (!typo || typeof typo !== 'object') return; |
| 12 |
Object.keys(typo).forEach(function (k) { |
| 13 |
var v = typo[k]; if (v === '' || v == null) return; |
| 14 |
var css = _typoKeys[k]; if (!css) return; |
| 15 |
if ((k === 'letterSpacing' || k === 'wordSpacing') && typeof v === 'number') v = v + 'px'; |
| 16 |
if ((/^(sizeDesktop|sizeTablet|sizeMobile)$/).test(k) && typeof v === 'number') v = v + 'px'; |
| 17 |
el.style.setProperty(prefix + css, '' + v); |
| 18 |
}); |
| 19 |
} |
| 20 |
|
| 21 |
function mk(tag, cls, styles) { var d = document.createElement(tag); if (cls) d.className = cls; if (styles) Object.keys(styles).forEach(function (k) { d.style[k] = styles[k]; }); return d; } |
| 22 |
function tx(tag, cls, text, styles) { var d = mk(tag, cls, styles); d.textContent = text; return d; } |
| 23 |
function ap(p) { Array.prototype.slice.call(arguments, 1).forEach(function (c) { if (c) p.appendChild(c); }); return p; } |
| 24 |
|
| 25 |
var typeIcons = { lesson: '📖', project: '🔨', quiz: '✏️', reading: '📄', video: '🎬' }; |
| 26 |
var difficultyLabels = { beginner: 'Beginner', intermediate: 'Intermediate', advanced: 'Advanced' }; |
| 27 |
|
| 28 |
function typeColor(t, a) { |
| 29 |
var map = { lesson: a.lessonColor, project: a.projectColor, quiz: a.quizColor, reading: a.readingColor, video: a.videoColor }; |
| 30 |
return map[t] || a.lessonColor; |
| 31 |
} |
| 32 |
|
| 33 |
function buildStep(step, idx, total, a) { |
| 34 |
var tc = typeColor(step.type, a); |
| 35 |
var stepEl = mk('div', 'bkbg-lp-step'); |
| 36 |
|
| 37 |
// Left column: number + connector |
| 38 |
var left = mk('div', 'bkbg-lp-step-left'); |
| 39 |
if (a.showStepNumbers) { |
| 40 |
var num = tx('div', 'bkbg-lp-step-num', String(idx + 1)); |
| 41 |
num.style.cssText = 'background:' + a.stepNumBg + ';color:' + a.stepNumColor; |
| 42 |
ap(left, num); |
| 43 |
} |
| 44 |
if (idx < total - 1) { |
| 45 |
var conn = mk('div', 'bkbg-lp-connector'); |
| 46 |
conn.style.background = a.connectorColor; |
| 47 |
ap(left, conn); |
| 48 |
} |
| 49 |
ap(stepEl, left); |
| 50 |
|
| 51 |
// Card |
| 52 |
var card = mk('div', 'bkbg-lp-step-card'); |
| 53 |
card.style.cssText = 'background:' + a.stepBg + ';border:1px solid ' + a.stepBorderColor; |
| 54 |
|
| 55 |
var header = mk('div', 'bkbg-lp-step-header'); |
| 56 |
var titleRow = mk('div', 'bkbg-lp-step-title-row'); |
| 57 |
var icon = tx('span', 'bkbg-lp-type-icon', typeIcons[step.type] || '📖'); |
| 58 |
icon.style.color = tc; |
| 59 |
var stepTitle = tx('span', 'bkbg-lp-step-title', step.title); |
| 60 |
stepTitle.style.color = a.stepTitleColor; |
| 61 |
ap(titleRow, icon, stepTitle); |
| 62 |
ap(header, titleRow); |
| 63 |
|
| 64 |
if (a.showDuration && step.duration) { |
| 65 |
var dur = tx('span', 'bkbg-lp-duration', '⏱ ' + step.duration); |
| 66 |
dur.style.color = a.durationColor; |
| 67 |
ap(header, dur); |
| 68 |
} |
| 69 |
ap(card, header); |
| 70 |
|
| 71 |
if (step.description) { |
| 72 |
var desc = tx('p', 'bkbg-lp-step-desc', step.description); |
| 73 |
desc.style.color = a.stepDescColor; |
| 74 |
ap(card, desc); |
| 75 |
} |
| 76 |
|
| 77 |
if (a.showSkills && step.skills && step.skills.length > 0) { |
| 78 |
var skills = mk('div', 'bkbg-lp-skills'); |
| 79 |
step.skills.forEach(function (s) { |
| 80 |
var tag = tx('span', 'bkbg-lp-skill-tag', s); |
| 81 |
tag.style.cssText = 'background:' + a.skillBg + ';color:' + a.skillColor; |
| 82 |
ap(skills, tag); |
| 83 |
}); |
| 84 |
ap(card, skills); |
| 85 |
} |
| 86 |
|
| 87 |
ap(stepEl, card); |
| 88 |
return stepEl; |
| 89 |
} |
| 90 |
|
| 91 |
function buildBlock(appEl) { |
| 92 |
if (appEl.dataset.rendered) return; |
| 93 |
appEl.dataset.rendered = '1'; |
| 94 |
|
| 95 |
var a = Object.assign({ |
| 96 |
pathTitle: 'Learning Path', description: '', showDescription: true, |
| 97 |
totalDuration: '', difficulty: 'intermediate', |
| 98 |
showSkills: true, showDuration: true, showStepNumbers: true, steps: [], |
| 99 |
fontSize: 14, titleFontSize: 24, stepTitleSize: 16, lineHeight: 160, borderRadius: 12, |
| 100 |
bgColor: '#ffffff', borderColor: '#e5e7eb', |
| 101 |
titleColor: '#111827', descColor: '#6b7280', |
| 102 |
stepBg: '#ffffff', stepBorderColor: '#e5e7eb', |
| 103 |
stepTitleColor: '#1f2937', stepDescColor: '#6b7280', |
| 104 |
stepNumBg: '#0f172a', stepNumColor: '#ffffff', |
| 105 |
connectorColor: '#e5e7eb', durationColor: '#6b7280', |
| 106 |
skillBg: '#f1f5f9', skillColor: '#475569', |
| 107 |
metaBg: '#f8fafc', metaBorderColor: '#e2e8f0', |
| 108 |
difficultyBg: '#ede9fe', difficultyColor: '#5b21b6', |
| 109 |
lessonColor: '#2563eb', projectColor: '#d97706', |
| 110 |
quizColor: '#7c3aed', readingColor: '#059669', videoColor: '#dc2626' |
| 111 |
}, JSON.parse(appEl.dataset.opts || '{}')); |
| 112 |
|
| 113 |
var lh = (a.lineHeight / 100).toFixed(2); |
| 114 |
|
| 115 |
var wrap = mk('div', 'bkbg-lp-wrap'); |
| 116 |
wrap.style.cssText = 'background:' + a.bgColor + ';border-radius:' + a.borderRadius + 'px;border:1px solid ' + a.borderColor + ';overflow:hidden;font-size:' + a.fontSize + 'px;line-height:' + lh; |
| 117 |
|
| 118 |
typoCssVarsForEl(wrap, a.titleTypo, '--bkbg-lp-tt-'); |
| 119 |
typoCssVarsForEl(wrap, a.descTypo, '--bkbg-lp-d-'); |
| 120 |
typoCssVarsForEl(wrap, a.stepTitleTypo, '--bkbg-lp-st-'); |
| 121 |
|
| 122 |
// Header |
| 123 |
var header = mk('div', 'bkbg-lp-header'); |
| 124 |
header.style.borderBottom = '1px solid ' + a.borderColor; |
| 125 |
var title = tx('h2', 'bkbg-lp-title', a.pathTitle); |
| 126 |
title.style.color = a.titleColor; |
| 127 |
ap(header, title); |
| 128 |
if (a.showDescription && a.description) { |
| 129 |
var desc = tx('p', 'bkbg-lp-desc', a.description); |
| 130 |
desc.style.color = a.descColor; |
| 131 |
ap(header, desc); |
| 132 |
} |
| 133 |
ap(wrap, header); |
| 134 |
|
| 135 |
// Meta bar |
| 136 |
var meta = mk('div', 'bkbg-lp-meta'); |
| 137 |
meta.style.cssText = 'background:' + a.metaBg + ';border-bottom:1px solid ' + a.metaBorderColor; |
| 138 |
if (a.totalDuration) { |
| 139 |
ap(meta, tx('span', 'bkbg-lp-meta-item', '⏱ ' + a.totalDuration)); |
| 140 |
} |
| 141 |
ap(meta, tx('span', 'bkbg-lp-meta-item', '📚 ' + a.steps.length + ' steps')); |
| 142 |
var diff = tx('span', 'bkbg-lp-difficulty', difficultyLabels[a.difficulty] || a.difficulty); |
| 143 |
diff.style.cssText = 'background:' + a.difficultyBg + ';color:' + a.difficultyColor; |
| 144 |
ap(meta, diff); |
| 145 |
ap(wrap, meta); |
| 146 |
|
| 147 |
// Steps |
| 148 |
var stepsEl = mk('div', 'bkbg-lp-steps'); |
| 149 |
a.steps.forEach(function (step, i) { |
| 150 |
ap(stepsEl, buildStep(step, i, a.steps.length, a)); |
| 151 |
}); |
| 152 |
ap(wrap, stepsEl); |
| 153 |
|
| 154 |
appEl.innerHTML = ''; |
| 155 |
appEl.appendChild(wrap); |
| 156 |
} |
| 157 |
|
| 158 |
function init() { document.querySelectorAll('.bkbg-learning-path-app').forEach(buildBlock); } |
| 159 |
if (document.readyState !== 'loading') { init(); } else { document.addEventListener('DOMContentLoaded', init); } |
| 160 |
})(); |
| 161 |
|