| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
var _typoKeys = { |
| 5 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 6 |
decoration:'text-decoration', transform:'text-transform', |
| 7 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 8 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 9 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 10 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 11 |
}; |
| 12 |
function typoCssVarsForEl(el, obj, prefix) { |
| 13 |
if (!obj || typeof obj !== 'object') return; |
| 14 |
Object.keys(_typoKeys).forEach(function (k) { |
| 15 |
var v = obj[k]; |
| 16 |
if (v === undefined || v === '' || v === null) return; |
| 17 |
if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px'); |
| 18 |
else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || ''); |
| 19 |
else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px'); |
| 20 |
else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px'); |
| 21 |
el.style.setProperty(prefix + _typoKeys[k], String(v)); |
| 22 |
}); |
| 23 |
} |
| 24 |
|
| 25 |
var LOREM_WORDS = [ |
| 26 |
'lorem','ipsum','dolor','sit','amet','consectetur','adipiscing','elit', |
| 27 |
'sed','do','eiusmod','tempor','incididunt','ut','labore','et','dolore', |
| 28 |
'magna','aliqua','enim','ad','minim','veniam','quis','nostrud','exercitation', |
| 29 |
'ullamco','laboris','nisi','aliquip','ex','ea','commodo','consequat', |
| 30 |
'duis','aute','irure','in','reprehenderit','voluptate','velit','esse', |
| 31 |
'cillum','eu','fugiat','nulla','pariatur','excepteur','sint','occaecat', |
| 32 |
'cupidatat','non','proident','sunt','culpa','qui','officia','deserunt', |
| 33 |
'mollit','anim','id','est','laborum','perspiciatis','unde','omnis','iste', |
| 34 |
'natus','error','accusantium','doloremque','laudantium','totam','rem', |
| 35 |
'aperiam','eaque','ipsa','quae','ab','illo','inventore','veritatis', |
| 36 |
'architecto','beatae','vitae','dicta','explicabo','nemo','ipsam','quia', |
| 37 |
'voluptas','aspernatur','odit','fugit','accusamus','iusto','odio','dignissimos', |
| 38 |
'blanditiis','praesentium','voluptatum','deleniti','atque','corrupti', |
| 39 |
'quos','dolores','quas','molestias','excepturi','occaecati','cupiditate', |
| 40 |
'provident','similique','mollitia','animi','dolorem','porro','quisquam','eum', |
| 41 |
'nihil','quo','maxime','placeat','facere','possimus','omnis','voluptatem', |
| 42 |
'suscipit','eligendi','optio','cumque','impedit','quo','minus','illum','vero' |
| 43 |
]; |
| 44 |
|
| 45 |
var LOREM_CLASSIC_START = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; |
| 46 |
|
| 47 |
function rand(min, max) { |
| 48 |
return Math.floor(Math.random() * (max - min + 1)) + min; |
| 49 |
} |
| 50 |
|
| 51 |
function randomWord() { |
| 52 |
return LOREM_WORDS[Math.floor(Math.random() * LOREM_WORDS.length)]; |
| 53 |
} |
| 54 |
|
| 55 |
function generateSentence(wordsMin, wordsMax) { |
| 56 |
var n = rand(wordsMin || 8, wordsMax || 18); |
| 57 |
var words = []; |
| 58 |
for (var i = 0; i < n; i++) words.push(randomWord()); |
| 59 |
var sentence = words.join(' '); |
| 60 |
sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1); |
| 61 |
var endings = ['.', '.', '.', '!', '?']; |
| 62 |
return sentence + endings[Math.floor(Math.random() * endings.length)]; |
| 63 |
} |
| 64 |
|
| 65 |
function generateParagraph(sentenceCountMin, sentenceCountMax) { |
| 66 |
var n = rand(sentenceCountMin || 4, sentenceCountMax || 7); |
| 67 |
var sents = []; |
| 68 |
for (var i = 0; i < n; i++) sents.push(generateSentence()); |
| 69 |
return sents.join(' '); |
| 70 |
} |
| 71 |
|
| 72 |
function buildOutput(opts, mode, count, useLorem, useHtml) { |
| 73 |
var html = useHtml; |
| 74 |
if (mode === 'paragraphs') { |
| 75 |
var paras = []; |
| 76 |
for (var i = 0; i < count; i++) { |
| 77 |
var p = (i === 0 && useLorem) ? LOREM_CLASSIC_START + ' ' + generateParagraph(3, 6) : generateParagraph(); |
| 78 |
paras.push(html ? '<p>' + p + '</p>' : p); |
| 79 |
} |
| 80 |
return html ? paras.join('\n') : paras.join('\n\n'); |
| 81 |
} |
| 82 |
if (mode === 'sentences') { |
| 83 |
var sents = []; |
| 84 |
for (var i = 0; i < count; i++) { |
| 85 |
var s = (i === 0 && useLorem) ? LOREM_CLASSIC_START : generateSentence(); |
| 86 |
sents.push(s); |
| 87 |
} |
| 88 |
return sents.join(html ? '\n' : '\n'); |
| 89 |
} |
| 90 |
if (mode === 'words') { |
| 91 |
var first = useLorem ? 'Lorem ipsum dolor sit amet consectetur adipiscing elit'.split(' ') : []; |
| 92 |
var words = first.slice(0, Math.min(first.length, count)); |
| 93 |
while (words.length < count) words.push(randomWord()); |
| 94 |
return words.slice(0, count).join(' '); |
| 95 |
} |
| 96 |
if (mode === 'list') { |
| 97 |
var items = []; |
| 98 |
var startWords = useLorem ? ['Lorem','Ipsum','Dolor','Sit','Amet','Consectetur','Adipiscing','Elit'] : []; |
| 99 |
for (var i = 0; i < count; i++) { |
| 100 |
var itemWords = []; |
| 101 |
var len = rand(3, 7); |
| 102 |
if (i < startWords.length && useLorem) itemWords.push(startWords[i]); |
| 103 |
while (itemWords.length < len) itemWords.push(randomWord()); |
| 104 |
var item = itemWords.join(' '); |
| 105 |
item = item.charAt(0).toUpperCase() + item.slice(1); |
| 106 |
items.push(item); |
| 107 |
} |
| 108 |
if (html) { |
| 109 |
return '<ul>\n' + items.map(function(it){ return ' <li>' + it + '</li>'; }).join('\n') + '\n</ul>'; |
| 110 |
} |
| 111 |
return items.map(function(it, idx){ return (idx + 1) + '. ' + it; }).join('\n'); |
| 112 |
} |
| 113 |
return ''; |
| 114 |
} |
| 115 |
|
| 116 |
function plainText(mode, count, useLorem) { |
| 117 |
return buildOutput(null, mode, count, useLorem, false); |
| 118 |
} |
| 119 |
|
| 120 |
function htmlText(mode, count, useLorem) { |
| 121 |
return buildOutput(null, mode, count, useLorem, true); |
| 122 |
} |
| 123 |
|
| 124 |
function countWords(text) { |
| 125 |
var plain = text.replace(/<[^>]+>/g, ' '); |
| 126 |
var cleaned = plain.trim().replace(/\s+/g, ' '); |
| 127 |
return cleaned ? cleaned.split(' ').length : 0; |
| 128 |
} |
| 129 |
|
| 130 |
function countChars(text) { |
| 131 |
return text.replace(/<[^>]+>/g, '').length; |
| 132 |
} |
| 133 |
|
| 134 |
document.querySelectorAll('.bkbg-lig-app').forEach(function (root) { |
| 135 |
var opts = {}; |
| 136 |
try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); } catch(e) {} |
| 137 |
|
| 138 |
/* Apply typography CSS vars */ |
| 139 |
typoCssVarsForEl(root, opts.titleTypo, '--bkbg-lig-tt-'); |
| 140 |
typoCssVarsForEl(root, opts.subtitleTypo, '--bkbg-lig-st-'); |
| 141 |
typoCssVarsForEl(root, opts.outputTypo, '--bkbg-lig-out-'); |
| 142 |
|
| 143 |
var mode = opts.defaultMode || 'paragraphs'; |
| 144 |
var count = parseInt(opts.defaultCount) || 3; |
| 145 |
var showHtmlToggle = opts.showHtmlToggle !== false; |
| 146 |
var showStats = opts.showStats !== false; |
| 147 |
var startWithLorem = opts.startWithLorem !== false; |
| 148 |
var outputHeight = parseInt(opts.outputHeight) || 260; |
| 149 |
|
| 150 |
var accentColor = opts.accentColor || '#6c3fb5'; |
| 151 |
var sectionBg = opts.sectionBg || '#f5f3ff'; |
| 152 |
var cardBg = opts.cardBg || '#ffffff'; |
| 153 |
var outputBg = opts.outputBg || '#fafafa'; |
| 154 |
var outputBorder = opts.outputBorder || '#e5e7eb'; |
| 155 |
var outputTextColor= opts.outputTextColor|| '#374151'; |
| 156 |
var titleColor = opts.titleColor || '#111827'; |
| 157 |
var subtitleColor = opts.subtitleColor || '#6b7280'; |
| 158 |
var labelColor = opts.labelColor || '#374151'; |
| 159 |
var tabBg = opts.tabBg || '#f3f4f6'; |
| 160 |
|
| 161 |
var useHtml = false; |
| 162 |
var generatedPlain = ''; |
| 163 |
var generatedHtml = ''; |
| 164 |
|
| 165 |
// Build HTML |
| 166 |
root.innerHTML = |
| 167 |
'<div class="bkbg-lig-card" style="background:' + cardBg + ';border-radius:16px;">' + |
| 168 |
'<h2 class="bkbg-lig-title" style="color:' + titleColor + ';">' + (opts.title || 'Lorem Ipsum Generator') + '</h2>' + |
| 169 |
'<p class="bkbg-lig-subtitle" style="color:' + subtitleColor + ';">' + (opts.subtitle || 'Generate placeholder text for your designs and documents') + '</p>' + |
| 170 |
'<div class="bkbg-lig-tabs" style="background:' + tabBg + ';" id="lig-tabs"></div>' + |
| 171 |
'<div class="bkbg-lig-controls">' + |
| 172 |
'<div class="bkbg-lig-count-wrap" style="background:' + sectionBg + ';color:' + labelColor + ';">' + |
| 173 |
'<span style="font-size:13px;font-weight:600;margin-right:4px;">Count:</span>' + |
| 174 |
'<button class="bkbg-lig-count-btn" id="lig-dec">−</button>' + |
| 175 |
'<span class="bkbg-lig-count-val" id="lig-count-val">' + count + '</span>' + |
| 176 |
'<button class="bkbg-lig-count-btn" id="lig-inc">+</button>' + |
| 177 |
'</div>' + |
| 178 |
'<button class="bkbg-lig-btn" id="lig-generate" style="background:' + accentColor + ';color:#fff;">Generate</button>' + |
| 179 |
'<button class="bkbg-lig-btn bkbg-lig-btn-sec" id="lig-copy">📋 Copy</button>' + |
| 180 |
'<span class="bkbg-lig-copied" id="lig-copied">Copied!</span>' + |
| 181 |
'</div>' + |
| 182 |
(showHtmlToggle ? |
| 183 |
'<div class="bkbg-lig-opts">' + |
| 184 |
'<label class="bkbg-lig-check-lbl" style="color:' + labelColor + ';">' + |
| 185 |
'<input type="checkbox" id="lig-lorem-chk"' + (startWithLorem ? ' checked' : '') + '> Start with "Lorem ipsum…"' + |
| 186 |
'</label>' + |
| 187 |
'<label class="bkbg-lig-check-lbl" style="color:' + labelColor + ';">' + |
| 188 |
'<input type="checkbox" id="lig-html-chk"> HTML output' + |
| 189 |
'</label>' + |
| 190 |
'</div>' |
| 191 |
: '') + |
| 192 |
'<div class="bkbg-lig-output" id="lig-output" style="background:' + outputBg + ';color:' + outputTextColor + ';border-color:' + outputBorder + ';min-height:' + outputHeight + 'px;" tabindex="0"></div>' + |
| 193 |
(showStats ? '<div class="bkbg-lig-stats" id="lig-stats"></div>' : '') + |
| 194 |
'</div>'; |
| 195 |
|
| 196 |
var MODES = [ |
| 197 |
{ key: 'paragraphs', label: 'Paragraphs' }, |
| 198 |
{ key: 'sentences', label: 'Sentences' }, |
| 199 |
{ key: 'words', label: 'Words' }, |
| 200 |
{ key: 'list', label: 'List' } |
| 201 |
]; |
| 202 |
|
| 203 |
var tabsEl = root.querySelector('#lig-tabs'); |
| 204 |
var countVal = root.querySelector('#lig-count-val'); |
| 205 |
var decBtn = root.querySelector('#lig-dec'); |
| 206 |
var incBtn = root.querySelector('#lig-inc'); |
| 207 |
var genBtn = root.querySelector('#lig-generate'); |
| 208 |
var copyBtn = root.querySelector('#lig-copy'); |
| 209 |
var copiedEl = root.querySelector('#lig-copied'); |
| 210 |
var outputEl = root.querySelector('#lig-output'); |
| 211 |
var statsEl = root.querySelector('#lig-stats'); |
| 212 |
var loremChk = root.querySelector('#lig-lorem-chk'); |
| 213 |
var htmlChk = root.querySelector('#lig-html-chk'); |
| 214 |
|
| 215 |
function renderTabs() { |
| 216 |
tabsEl.innerHTML = ''; |
| 217 |
MODES.forEach(function(m) { |
| 218 |
var btn = document.createElement('button'); |
| 219 |
btn.className = 'bkbg-lig-tab'; |
| 220 |
btn.textContent = m.label; |
| 221 |
var active = m.key === mode; |
| 222 |
btn.style.background = active ? accentColor : 'transparent'; |
| 223 |
btn.style.color = active ? '#fff' : labelColor; |
| 224 |
btn.setAttribute('data-mode', m.key); |
| 225 |
tabsEl.appendChild(btn); |
| 226 |
}); |
| 227 |
} |
| 228 |
|
| 229 |
function generate() { |
| 230 |
var useLorem = loremChk ? loremChk.checked : startWithLorem; |
| 231 |
generatedPlain = plainText(mode, count, useLorem); |
| 232 |
generatedHtml = htmlText(mode, count, useLorem); |
| 233 |
useHtml = htmlChk ? htmlChk.checked : false; |
| 234 |
renderOutput(); |
| 235 |
} |
| 236 |
|
| 237 |
function renderOutput() { |
| 238 |
if (useHtml) { |
| 239 |
outputEl.innerHTML = generatedHtml; |
| 240 |
} else { |
| 241 |
// Use innerHTML with <br> for newlines so it looks right |
| 242 |
var escaped = generatedPlain.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); |
| 243 |
outputEl.innerHTML = escaped.replace(/\n\n/g,'</p><p>').replace(/\n/g,'<br>'); |
| 244 |
var d = document.createElement('div'); |
| 245 |
d.innerHTML = '<p>' + escaped.replace(/\n\n/g,'</p><p>').replace(/\n/g,'<br>') + '</p>'; |
| 246 |
outputEl.innerHTML = '<p>' + escaped.replace(/\n\n/g,'</p><p>').replace(/\n/g,'<br>') + '</p>'; |
| 247 |
} |
| 248 |
if (statsEl) { |
| 249 |
var raw = useHtml ? generatedHtml : generatedPlain; |
| 250 |
var wc = countWords(raw); |
| 251 |
var cc = countChars(raw); |
| 252 |
statsEl.innerHTML = |
| 253 |
'<span>Words: <strong>' + wc + '</strong></span>' + |
| 254 |
'<span>Characters: <strong>' + cc + '</strong></span>'; |
| 255 |
} |
| 256 |
} |
| 257 |
|
| 258 |
tabsEl.addEventListener('click', function(e) { |
| 259 |
var btn = e.target.closest('[data-mode]'); |
| 260 |
if (!btn) return; |
| 261 |
mode = btn.getAttribute('data-mode'); |
| 262 |
renderTabs(); |
| 263 |
generate(); |
| 264 |
}); |
| 265 |
|
| 266 |
decBtn.addEventListener('click', function() { |
| 267 |
if (count > 1) { count--; countVal.textContent = count; } |
| 268 |
}); |
| 269 |
incBtn.addEventListener('click', function() { |
| 270 |
if (count < 50) { count++; countVal.textContent = count; } |
| 271 |
}); |
| 272 |
genBtn.addEventListener('click', generate); |
| 273 |
|
| 274 |
if (loremChk) loremChk.addEventListener('change', generate); |
| 275 |
if (htmlChk) htmlChk.addEventListener('change', function() { useHtml = htmlChk.checked; renderOutput(); }); |
| 276 |
|
| 277 |
copyBtn.addEventListener('click', function() { |
| 278 |
var text = useHtml ? generatedHtml : generatedPlain; |
| 279 |
if (navigator.clipboard) { |
| 280 |
navigator.clipboard.writeText(text).then(showCopied).catch(fallbackCopy.bind(null, text)); |
| 281 |
} else { |
| 282 |
fallbackCopy(text); |
| 283 |
} |
| 284 |
}); |
| 285 |
|
| 286 |
function fallbackCopy(text) { |
| 287 |
var ta = document.createElement('textarea'); |
| 288 |
ta.value = text; |
| 289 |
ta.style.position = 'fixed'; ta.style.top = '-9999px'; |
| 290 |
document.body.appendChild(ta); |
| 291 |
ta.select(); |
| 292 |
try { document.execCommand('copy'); showCopied(); } catch(e) {} |
| 293 |
document.body.removeChild(ta); |
| 294 |
} |
| 295 |
|
| 296 |
function showCopied() { |
| 297 |
if (!copiedEl) return; |
| 298 |
copiedEl.style.display = 'inline-block'; |
| 299 |
setTimeout(function() { copiedEl.style.display = 'none'; }, 2000); |
| 300 |
} |
| 301 |
|
| 302 |
root.addEventListener('keydown', function(e) { |
| 303 |
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { |
| 304 |
e.preventDefault(); |
| 305 |
generate(); |
| 306 |
} |
| 307 |
}); |
| 308 |
|
| 309 |
renderTabs(); |
| 310 |
generate(); |
| 311 |
}); |
| 312 |
|
| 313 |
})(); |
| 314 |
|