| 1 |
(function () { |
| 2 |
|
| 3 |
function hexLuma(hex) { |
| 4 |
if (!hex) return 0; |
| 5 |
var c = hex.replace('#',''); |
| 6 |
if (c.length === 3) c = c[0]+c[0]+c[1]+c[1]+c[2]+c[2]; |
| 7 |
var r = parseInt(c.substr(0,2),16), g = parseInt(c.substr(2,2),16), b = parseInt(c.substr(4,2),16); |
| 8 |
return 0.299*r + 0.587*g + 0.114*b; |
| 9 |
} |
| 10 |
|
| 11 |
function buildTree(nodes) { |
| 12 |
var map = {}; |
| 13 |
nodes.forEach(function (n) { map[n.id] = Object.assign({}, n, { children: [] }); }); |
| 14 |
var roots = []; |
| 15 |
nodes.forEach(function (n) { |
| 16 |
if (!n.parentId || !map[n.parentId]) { roots.push(map[n.id]); } |
| 17 |
else { map[n.parentId].children.push(map[n.id]); } |
| 18 |
}); |
| 19 |
return roots; |
| 20 |
} |
| 21 |
|
| 22 |
function buildNodeEl(node, opts, depth, allCards) { |
| 23 |
var wrap = document.createElement('div'); |
| 24 |
wrap.className = 'bkbg-oc-node-wrap'; |
| 25 |
wrap.setAttribute('data-id', node.id); |
| 26 |
|
| 27 |
/* connector line upward (drawn by parent actually) */ |
| 28 |
|
| 29 |
var card = document.createElement('div'); |
| 30 |
card.className = 'bkbg-oc-card'; |
| 31 |
card.style.borderColor = node.color || ''; |
| 32 |
|
| 33 |
/* Avatar */ |
| 34 |
if (opts.showAvatars) { |
| 35 |
var av = document.createElement('div'); |
| 36 |
av.className = 'bkbg-oc-avatar'; |
| 37 |
av.style.background = node.color || ''; |
| 38 |
if (node.avatarUrl) { |
| 39 |
var img = document.createElement('img'); |
| 40 |
img.src = node.avatarUrl; |
| 41 |
img.alt = node.name; |
| 42 |
av.appendChild(img); |
| 43 |
} else { |
| 44 |
var sp = document.createElement('span'); |
| 45 |
sp.className = 'bkbg-oc-avatar-initial'; |
| 46 |
sp.textContent = (node.name || '?').charAt(0).toUpperCase(); |
| 47 |
av.appendChild(sp); |
| 48 |
} |
| 49 |
card.appendChild(av); |
| 50 |
} |
| 51 |
|
| 52 |
var nameEl = document.createElement('p'); nameEl.className = 'bkbg-oc-name'; nameEl.textContent = node.name; card.appendChild(nameEl); |
| 53 |
var roleEl = document.createElement('p'); roleEl.className = 'bkbg-oc-role'; roleEl.textContent = node.role; card.appendChild(roleEl); |
| 54 |
if (opts.showDept && node.department) { |
| 55 |
var deptEl = document.createElement('p'); deptEl.className = 'bkbg-oc-dept'; deptEl.textContent = node.department; card.appendChild(deptEl); |
| 56 |
} |
| 57 |
|
| 58 |
/* Collapse toggle */ |
| 59 |
if (opts.collapse && node.children && node.children.length > 0) { |
| 60 |
card.style.cursor = 'pointer'; |
| 61 |
card.title = 'Click to expand/collapse'; |
| 62 |
card.addEventListener('click', function () { wrap.classList.toggle('is-collapsed'); }); |
| 63 |
} |
| 64 |
|
| 65 |
allCards.push(card); |
| 66 |
wrap.appendChild(card); |
| 67 |
|
| 68 |
if (node.children && node.children.length > 0) { |
| 69 |
/* vertical line down */ |
| 70 |
var vDown = document.createElement('div'); |
| 71 |
vDown.className = 'bkbg-oc-conn-v-down'; |
| 72 |
wrap.appendChild(vDown); |
| 73 |
|
| 74 |
if (node.children.length > 1) { |
| 75 |
var hLine = document.createElement('div'); |
| 76 |
hLine.className = 'bkbg-oc-conn-h'; |
| 77 |
wrap.appendChild(hLine); |
| 78 |
} |
| 79 |
|
| 80 |
var row = document.createElement('div'); |
| 81 |
row.className = 'bkbg-oc-children-row'; |
| 82 |
node.children.forEach(function (child) { |
| 83 |
var childWrap = buildNodeEl(child, opts, depth + 1, allCards); |
| 84 |
/* connector up for child */ |
| 85 |
var vUp = document.createElement('div'); |
| 86 |
vUp.className = 'bkbg-oc-conn-v-up'; |
| 87 |
childWrap.insertBefore(vUp, childWrap.firstChild); |
| 88 |
row.appendChild(childWrap); |
| 89 |
}); |
| 90 |
wrap.appendChild(row); |
| 91 |
} |
| 92 |
|
| 93 |
return wrap; |
| 94 |
} |
| 95 |
|
| 96 |
function initOrgChart(wrapper) { |
| 97 |
if (wrapper.dataset.initialized) return; |
| 98 |
wrapper.dataset.initialized = '1'; |
| 99 |
|
| 100 |
var treeEl = wrapper.querySelector('.bkbg-oc-tree'); |
| 101 |
if (!treeEl) return; |
| 102 |
|
| 103 |
var nodes = []; |
| 104 |
try { nodes = JSON.parse(treeEl.getAttribute('data-nodes') || '[]'); } catch (e) {} |
| 105 |
|
| 106 |
var opts = { |
| 107 |
showAvatars: treeEl.getAttribute('data-show-avatars') === '1', |
| 108 |
showDept: treeEl.getAttribute('data-show-dept') === '1', |
| 109 |
collapse: treeEl.getAttribute('data-collapse') === '1', |
| 110 |
}; |
| 111 |
|
| 112 |
treeEl.innerHTML = ''; |
| 113 |
treeEl.style.display = 'flex'; |
| 114 |
treeEl.style.flexDirection = 'column'; |
| 115 |
treeEl.style.alignItems = 'center'; |
| 116 |
treeEl.style.minWidth = 'max-content'; |
| 117 |
|
| 118 |
var allCards = []; |
| 119 |
var roots = buildTree(nodes); |
| 120 |
roots.forEach(function (root) { |
| 121 |
treeEl.appendChild(buildNodeEl(root, opts, 0, allCards)); |
| 122 |
}); |
| 123 |
|
| 124 |
/* Fade-in per card */ |
| 125 |
if ('IntersectionObserver' in window) { |
| 126 |
var obs = new IntersectionObserver(function (entries) { |
| 127 |
entries.forEach(function (e, idx) { |
| 128 |
if (e.isIntersecting) { |
| 129 |
setTimeout(function () { e.target.classList.add('is-visible'); }, idx * 80); |
| 130 |
obs.unobserve(e.target); |
| 131 |
} |
| 132 |
}); |
| 133 |
}, { threshold: 0.2 }); |
| 134 |
allCards.forEach(function (c) { obs.observe(c); }); |
| 135 |
} else { |
| 136 |
allCards.forEach(function (c) { c.classList.add('is-visible'); }); |
| 137 |
} |
| 138 |
} |
| 139 |
|
| 140 |
function init() { |
| 141 |
document.querySelectorAll('.bkbg-oc-wrapper').forEach(initOrgChart); |
| 142 |
} |
| 143 |
|
| 144 |
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } |
| 145 |
}()); |
| 146 |
|