| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function typoCssVarsForEl(typo, prefix, el) { |
| 5 |
if (!typo || typeof typo !== 'object') return; |
| 6 |
var map = { |
| 7 |
family: 'font-family', weight: 'font-weight', style: 'font-style', |
| 8 |
decoration: 'text-decoration', transform: 'text-transform', |
| 9 |
sizeDesktop: 'font-size-d', sizeTablet: 'font-size-t', sizeMobile: 'font-size-m', |
| 10 |
lineHeightDesktop: 'line-height-d', lineHeightTablet: 'line-height-t', lineHeightMobile: 'line-height-m', |
| 11 |
letterSpacingDesktop: 'letter-spacing-d', letterSpacingTablet: 'letter-spacing-t', letterSpacingMobile: 'letter-spacing-m', |
| 12 |
wordSpacingDesktop: 'word-spacing-d', wordSpacingTablet: 'word-spacing-t', wordSpacingMobile: 'word-spacing-m' |
| 13 |
}; |
| 14 |
Object.keys(map).forEach(function (k) { |
| 15 |
if (typo[k] !== undefined && typo[k] !== '') { |
| 16 |
var v = typo[k]; |
| 17 |
if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) { |
| 18 |
v = v + (typo.sizeUnit || 'px'); |
| 19 |
} else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) { |
| 20 |
v = v + (typo.lineHeightUnit || ''); |
| 21 |
} else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) { |
| 22 |
v = v + (typo.letterSpacingUnit || 'px'); |
| 23 |
} else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) { |
| 24 |
v = v + (typo.wordSpacingUnit || 'px'); |
| 25 |
} |
| 26 |
el.style.setProperty(prefix + map[k], String(v)); |
| 27 |
} |
| 28 |
}); |
| 29 |
} |
| 30 |
|
| 31 |
var SYMBOLS = ['*', '†', '‡', '§', '‖', '¶', '#', '**', '††', '‡‡']; |
| 32 |
|
| 33 |
function getMarker(style, i) { |
| 34 |
if (style === 'letter') return String.fromCharCode(97 + i); |
| 35 |
if (style === 'roman') { |
| 36 |
var nums = [1,4,5,9,10,40,50,90,100,400,500,900,1000]; |
| 37 |
var syms = ['i','iv','v','ix','x','xl','l','xc','c','cd','d','cm','m']; |
| 38 |
var n = i + 1, out = ''; |
| 39 |
for (var k = nums.length - 1; k >= 0; k--) { |
| 40 |
while (n >= nums[k]) { out += syms[k]; n -= nums[k]; } |
| 41 |
} |
| 42 |
return out; |
| 43 |
} |
| 44 |
if (style === 'symbol') return SYMBOLS[i % SYMBOLS.length]; |
| 45 |
return String(i + 1); |
| 46 |
} |
| 47 |
|
| 48 |
function init() { |
| 49 |
document.querySelectorAll('.bkbg-fn-app').forEach(function (appEl) { |
| 50 |
if (appEl.dataset.rendered) return; |
| 51 |
appEl.dataset.rendered = '1'; |
| 52 |
|
| 53 |
var opts; |
| 54 |
try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; } |
| 55 |
var o = Object.assign({ |
| 56 |
notes: [], |
| 57 |
title: 'Notes', |
| 58 |
showTitle: true, |
| 59 |
markerStyle: 'number', |
| 60 |
showTooltip: true, |
| 61 |
listStyle: 'default', |
| 62 |
dividerEnabled: true, |
| 63 |
fontSize: 14, |
| 64 |
titleSize: 16, |
| 65 |
borderRadius: 6, |
| 66 |
bgColor: '#f8fafc', |
| 67 |
borderColor: '#e2e8f0', |
| 68 |
titleColor: '#0f172a', |
| 69 |
textColor: '#475569', |
| 70 |
markerColor: '#3b82f6', |
| 71 |
markerBg: '#eff6ff', |
| 72 |
dividerColor: '#e2e8f0', |
| 73 |
tooltipBg: '#1e293b', |
| 74 |
tooltipColor: '#f1f5f9' |
| 75 |
}, opts); |
| 76 |
|
| 77 |
// ── build block ────────────────────────────────────────────────────── |
| 78 |
var block = document.createElement('div'); |
| 79 |
block.className = 'bkbg-fn-block'; |
| 80 |
block.style.background = o.bgColor; |
| 81 |
block.style.borderRadius = o.borderRadius + 'px'; |
| 82 |
block.style.padding = '20px 24px'; |
| 83 |
block.style.boxSizing = 'border-box'; |
| 84 |
|
| 85 |
typoCssVarsForEl(o.typoTitle, '--bkbg-fn-tt-', block); |
| 86 |
typoCssVarsForEl(o.typoNote, '--bkbg-fn-nt-', block); |
| 87 |
|
| 88 |
if (o.dividerEnabled) { |
| 89 |
var div = document.createElement('div'); |
| 90 |
div.className = 'bkbg-fn-divider'; |
| 91 |
div.style.borderTopColor = o.dividerColor; |
| 92 |
block.appendChild(div); |
| 93 |
} |
| 94 |
|
| 95 |
if (o.showTitle && o.title) { |
| 96 |
var titleEl = document.createElement('div'); |
| 97 |
titleEl.className = 'bkbg-fn-title'; |
| 98 |
titleEl.textContent = o.title; |
| 99 |
titleEl.style.color = o.titleColor; |
| 100 |
block.appendChild(titleEl); |
| 101 |
} |
| 102 |
|
| 103 |
var list = document.createElement('ol'); |
| 104 |
list.className = 'bkbg-fn-list bkbg-fn-list-' + o.listStyle; |
| 105 |
list.style.listStyle = 'none'; |
| 106 |
list.style.margin = '0'; |
| 107 |
list.style.padding = '0'; |
| 108 |
|
| 109 |
o.notes.forEach(function (note, i) { |
| 110 |
var li = document.createElement('li'); |
| 111 |
li.className = 'bkbg-fn-item'; |
| 112 |
li.id = 'fn-' + note.id; |
| 113 |
li.style.color = o.textColor; |
| 114 |
if (o.listStyle === 'card') li.style.borderColor = o.borderColor; |
| 115 |
|
| 116 |
var marker = document.createElement('span'); |
| 117 |
marker.className = 'bkbg-fn-marker'; |
| 118 |
marker.textContent = getMarker(o.markerStyle, i); |
| 119 |
marker.style.background = o.markerBg; |
| 120 |
marker.style.color = o.markerColor; |
| 121 |
|
| 122 |
var textSpan = document.createElement('span'); |
| 123 |
textSpan.className = 'bkbg-fn-text'; |
| 124 |
textSpan.textContent = note.text; |
| 125 |
|
| 126 |
li.appendChild(marker); |
| 127 |
li.appendChild(textSpan); |
| 128 |
list.appendChild(li); |
| 129 |
}); |
| 130 |
|
| 131 |
block.appendChild(list); |
| 132 |
|
| 133 |
appEl.parentNode.insertBefore(block, appEl); |
| 134 |
appEl.style.display = 'none'; |
| 135 |
|
| 136 |
// ── inject inline markers into page (if showTooltip) ────────────────── |
| 137 |
// Looks for [fn:1] [fn:2] shortcodes already in the page content |
| 138 |
// and replaces them with superscript markers with tooltips |
| 139 |
if (o.showTooltip && o.notes.length) { |
| 140 |
var body = document.body; |
| 141 |
var walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, false); |
| 142 |
var nodesToReplace = []; |
| 143 |
var node; |
| 144 |
while ((node = walker.nextNode())) { |
| 145 |
if (/\[fn:\d+\]/.test(node.textContent)) { |
| 146 |
nodesToReplace.push(node); |
| 147 |
} |
| 148 |
} |
| 149 |
nodesToReplace.forEach(function (textNode) { |
| 150 |
var parent = textNode.parentNode; |
| 151 |
if (!parent) return; |
| 152 |
var frag = document.createDocumentFragment(); |
| 153 |
var parts = textNode.textContent.split(/(\[fn:\d+\])/); |
| 154 |
parts.forEach(function (part) { |
| 155 |
var match = part.match(/^\[fn:(\d+)\]$/); |
| 156 |
if (match) { |
| 157 |
var noteId = parseInt(match[1], 10); |
| 158 |
var noteObj = o.notes.filter(function (n) { return n.id === noteId; })[0]; |
| 159 |
if (noteObj) { |
| 160 |
var ref = document.createElement('span'); |
| 161 |
ref.className = 'bkbg-fn-ref'; |
| 162 |
ref.style.color = o.markerColor; |
| 163 |
ref.style.background = o.markerBg; |
| 164 |
ref.setAttribute('tabindex', '0'); |
| 165 |
var idx = o.notes.indexOf(noteObj); |
| 166 |
ref.textContent = getMarker(o.markerStyle, idx); |
| 167 |
ref.setAttribute('href', '#fn-' + noteId); |
| 168 |
|
| 169 |
var tooltip = document.createElement('span'); |
| 170 |
tooltip.className = 'bkbg-fn-tooltip'; |
| 171 |
tooltip.textContent = noteObj.text; |
| 172 |
tooltip.style.background = o.tooltipBg; |
| 173 |
tooltip.style.color = o.tooltipColor; |
| 174 |
tooltip.style.setProperty('border-top-color', o.tooltipBg); |
| 175 |
ref.appendChild(tooltip); |
| 176 |
frag.appendChild(ref); |
| 177 |
} else { |
| 178 |
frag.appendChild(document.createTextNode(part)); |
| 179 |
} |
| 180 |
} else { |
| 181 |
frag.appendChild(document.createTextNode(part)); |
| 182 |
} |
| 183 |
}); |
| 184 |
parent.replaceChild(frag, textNode); |
| 185 |
}); |
| 186 |
} |
| 187 |
}); |
| 188 |
} |
| 189 |
|
| 190 |
if (document.readyState !== 'loading') { init(); } |
| 191 |
else { document.addEventListener('DOMContentLoaded', init); } |
| 192 |
})(); |
| 193 |
|