Siteβ―Builder
Editing:
businessPromptinator-PERFECT.js
writable 0666
/* Promptinator β 2025β07β22 (business edition + flexible flags + pencil toggle) ----------------------------------------------------------------------------- */ export default class Promptinator { /* βββββββββββ static helpers βββββββββββ */ static _lastMeta = []; static _lastState = []; static _hasE = false; static _escAttr (s = '') { return String(s) .replace(/&/g,'&').replace(/"/g,'"') .replace(/</g,'<').replace(/>/g,'>'); } /* βββββββββββ init βββββββββββ */ static init ({ mount, defaultPrompt }) { const prompt = mount.dataset.prompt || defaultPrompt || mount.textContent.trim(); let template = prompt; /* cmd A B C D E I */ const RX = /\[(?:([A-DEI]?)-)?([^~|\-\]]*)(?:-([^~\]]*))?(?:~([^~]+)~)?\]/ig; const state = {}, meta = []; /* ββ NEW: accept edit/think OR dataβedit/dataβthink ββ */ const valEdit = mount.dataset.edit ?? mount.getAttribute('edit') ?? '1'; const valThink = mount.dataset.think ?? (mount.hasAttribute('think') ? mount.getAttribute('think') : '1'); const allowEdit = valEdit !== '0' && valEdit !== 'false'; const allowThink = valThink !== '0' && valThink !== 'false'; /* ---------- business helpers ---------- */ const biz = window.businessData || null; /* try ?ph=β¦ first, else 10βdigit segment in path */ let phParam = new URLSearchParams(window.location.search).get('ph'); if (!phParam) { const segs = window.location.pathname.split('/').filter(Boolean); phParam = segs.find(s => /^\d{10}$/.test(s)) || null; } const isBiz = biz && phParam && biz.phone && biz.phone === phParam; function applyBiz (st, mt) { if (!isBiz) return; const nonEmpty = v => v !== null && v !== undefined && String(v).trim() !== ''; mt.forEach((m,i) => { const lab = (m.lab || '').toLowerCase(); const set = v => { if (nonEmpty(v)) st[i] = v; }; switch (lab) { case 'name': set(biz.name); return; case 'slogan': set(biz.slogan); return; case 'description': set(biz.description); return; case 'address': set(biz.address); return; case 'city': set(biz.city); return; case 'state': set(biz.state); return; case 'zip': set(biz.zip); return; case 'phone': set(biz.phone); return; case 'website': set(biz.website); return; case 'email': set(biz.email); return; } if (m.cmd === 'C') { if (lab.startsWith('service') && Array.isArray(biz.tags)) { m.opts = biz.tags; if (!st[i].length && biz.tags.length) st[i] = [biz.tags[0]]; } if (lab.startsWith('location') && Array.isArray(biz.location_tags)) { m.opts = biz.location_tags; if (!st[i].length && biz.location_tags.length) st[i] = [biz.location_tags[0]]; } } }); } /* ---------- renderer ---------- */ function render () { meta.length = 0; let idx = 0; template.replace(RX,(_,cmd='',lab='',ops='',def='')=>{ lab = lab.trim(); const opts = ops.split('|').filter(Boolean); if(!(idx in state)){ state[idx] = cmd==='C' ? (def?def.split(',').map(v=>v.trim()):opts.slice(0,1)) : (def||opts[0]||lab); } meta.push({cmd:cmd||'A',lab,opts,def}); idx++; return''; }); if(!render._bizApplied){ applyBiz(state,meta); render._bizApplied=true; } idx = 0; const html = template.replace(RX,(_,cmd='',lab='')=>{ lab = lab.trim(); const val = state[idx]; let out; if(cmd==='C'){ const list=(Array.isArray(val)?val:[val]).filter(Boolean); if(!list.length) list.push('+'); out=list.map(v=>`<span class="token" data-i="${idx}" data-label="${lab}">[${v}]</span>`).join(', '); }else if(cmd==='E'){ out=`<button class="token e-btn" data-i="${idx}" data-label="${lab}">${/^\S+@\S+\.\S+$/.test(val)?'Send':'Submit'}</button>`; }else{ out=`<span class="token" data-i="${idx}" data-label="${lab}">[${val}]</span>`; } idx++; return out; }); const hasE = meta.some(m=>m.cmd==='E'); Promptinator._hasE = hasE; const aiButtons = (allowThink&&!hasE)?` <div class="ai-buttons" style="margin-top:1em;display:flex;flex-wrap:wrap;gap:.6em;"> <a class="ai-btn chatgpt" target="_blank" style="display:inline-flex;align-items:center;gap:.3em;background:#5c3bff;color:#fff;font-weight:600;border-radius:7px;padding:.4em 1.2em;text-decoration:none;">π§ Β ChatGPT</a> <a class="ai-btn perplexity" target="_blank" style="display:inline-flex;align-items:center;gap:.3em;background:#22a8e5;color:#fff;font-weight:600;border-radius:7px;padding:.4em 1.2em;text-decoration:none;">πΒ Perplexity</a> <a class="ai-btn copilot" target="_blank" style="display:inline-flex;align-items:center;gap:.3em;background:#10c775;color:#fff;font-weight:600;border-radius:7px;padding:.4em 1.2em;text-decoration:none;">π€Β Copilot</a> </div>`:''; promptText.innerHTML = html.replace(/\n/g,'<br>') + aiButtons; if(allowThink&&!hasE){ let i=0; const full = template.replace(RX,()=>{ const v=state[i++]; return Array.isArray(v)?v.join(', '):v; }); promptText.querySelector('.chatgpt').href = 'https://chat.openai.com/?prompt=' + encodeURIComponent(full); promptText.querySelector('.perplexity').href = 'https://www.perplexity.ai/search?q=' + encodeURIComponent(full); promptText.querySelector('.copilot').href = 'https://copilot.microsoft.com/?q=' + encodeURIComponent(full); } } /* render */ /* ---------- editor & event wiring ---------- */ const editor=document.createElement('div'); editor.innerHTML=` <div style="display:flex;align-items:baseline;gap:.5em;"> ${allowEdit?'<span id="edit-link" style="cursor:pointer">βοΈ</span>':''} <div class="prompt-text" style="flex:1 1 auto;"></div> </div> ${allowEdit?`<form id="edit-form" style="display:none;margin-top:.6em;"> <textarea style="width:100%;min-height:90px;font-family:monospace;"></textarea> <div style="text-align:right;margin-top:.7em;"><button type="submit">SaveΒ &Β Update</button></div> </form>`:''}`; mount.innerHTML=''; mount.append(editor); const promptText=editor.querySelector('.prompt-text'); if(allowEdit){ const link=editor.querySelector('#edit-link'), form=editor.querySelector('#edit-form'), ta=form.querySelector('textarea'); /* ββ NEW: toggle open β close ββ */ link.onclick=()=>{ const closed = getComputedStyle(form).display === 'none'; if(closed){ form.style.display=''; ta.value=template; ta.focus(); }else{ form.style.display='none'; } }; form.onsubmit=e=>{ e.preventDefault(); template=ta.value.trim(); Object.keys(state).forEach(k=>delete state[k]); render._bizApplied=false; render(); form.style.display='none'; }; } render(); /* initial paint */ mount.addEventListener('click',e=>{ if(!e.target.matches('.token'))return; const i=+e.target.dataset.i, info=meta[i], val=state[i]; if(info.cmd==='E'){ Promptinator._lastMeta=meta.slice(); Promptinator._lastState=Object.assign([],state); } Promptinator._popup(info,Array.isArray(val)?val.join(', '):val,v=>{ state[i]=info.cmd==='C'?v.split(',').map(s=>s.trim()).filter(Boolean):v; render(); }); }); } /* init */ /* βββββββββββ popup (all token types) βββββββββββ */ static _popup (meta, cur, done) { /* single <dialog> reused */ let dlg = document.getElementById('promptinator-dlg'); if (!dlg) { dlg = document.createElement('dialog'); dlg.id = 'promptinator-dlg'; document.body.append(dlg); dlg.addEventListener('click', e => { if (e.target === dlg) dlg.close(); }); dlg.addEventListener('cancel', e => { e.preventDefault(); dlg.close(); }); } /* pretty label */ const prettify = s => s.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); const niceLabel = meta.lab && meta.lab.trim() ? prettify(meta.lab) : (cur || { A:'Text Input', B:'Choice', C:'Options', D:'Dropdown', E:'Send', I:'URL Picker' }[meta.cmd] || 'Input'); /* defaultβtext placeholder logic (only when block includes Eβtoken) */ const showPlaceholder = Promptinator._hasE && cur === meta.def; const phAttr = showPlaceholder ? ` placeholder="${Promptinator._escAttr(cur)}"` : ''; const valAttr = showPlaceholder ? '' : Promptinator._escAttr(cur); /* ---------- build inner UI ---------- */ let inner = ''; switch (meta.cmd) { /* ----- A (multiline text) ----- */ case 'A': inner = `<label>${niceLabel} <textarea style="width:100%;font-size:1rem;" rows="4"${phAttr}>${valAttr}</textarea> </label> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; break; /* ----- B (radio list) ----- */ case 'B': inner = `<fieldset><legend>${niceLabel}</legend>` + meta.opts.map(o => `<label><input type="radio" name="r" value="${o}" ${o === cur ? 'checked' : ''}> ${o}</label>` ).join('') + `</fieldset>`; break; /* ----- C (checkbox list) ----- */ case 'C': { let opts = meta.opts; const labLow = (meta.lab || '').toLowerCase(); /* π’ always pull from businessData if present */ if (window.businessData) { if (labLow.startsWith('service') && Array.isArray(window.businessData.tags)) { opts = window.businessData.tags; } if (labLow.startsWith('location') && Array.isArray(window.businessData.location_tags)) { opts = window.businessData.location_tags; } } inner = `<fieldset><legend>${niceLabel}</legend>` + opts.map(o => `<label><input type="checkbox" value="${o}" ${cur.split(', ').includes(o) ? 'checked' : ''}> ${o}</label>` ).join('') + `</fieldset> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; break; } /* ----- D (simple select) ----- */ case 'D': { let opts = meta.opts; if (opts.length === 1 && /^\d+$/.test(opts[0])) { // numeric shorthand opts = Array.from({ length: +opts[0] }, (_, i) => String(i + 1)); } inner = `<label>${niceLabel} <select>` + opts.map(o => `<option${o === cur ? ' selected' : ''}>${o}</option>`).join('') + `</select> </label>`; break; } /* ----- I (URL pick list + iframe preview) ----- */ case 'I': inner = `<label>${niceLabel} <select>` + (showPlaceholder ? `<option disabled selected>${Promptinator._escAttr(cur)}</option>` : '') + meta.opts.map(u => `<option${u === cur ? ' selected' : ''}>${u}</option>`).join('') + `</select> </label> <iframe hidden style="width:100%;height:300px;border:1px solid #ccc;margin-top:.6em;"></iframe> <div style="text-align:right;margin-top:.7em;"><button type="button">Use site</button></div>`; break; /* ----- E (Send / Submit button) ----- */ case 'E': { const isEmail = /^\S+@\S+\.\S+$/.test(cur); inner = `<div style="padding:1em 0;text-align:center;font-size:1.1rem;font-weight:600;">${niceLabel}</div> <div style="text-align:right;margin-top:.7em;"> <button type="button">${isEmail ? 'Send' : 'Submit'}</button> </div>`; break; } /* ----- default: singleβline input ----- */ default: inner = `<label>${niceLabel} <input type="text" style="width:100%;font-size:1rem;"${phAttr} value="${valAttr}"> </label> <div style="text-align:right;margin-top:.7em;"><button type="button">Done</button></div>`; } dlg.innerHTML = `<div class="popup-body">${inner}</div>`; dlg.showModal(); const pb = dlg.querySelector('.popup-body'); /* ---------- perβcommand behaviour ---------- */ /* Eβbutton: send eβmail or post form */ if (meta.cmd === 'E') { const btn = pb.querySelector('button'); const isEmail = /^\S+@\S+\.\S+$/.test(cur); if (isEmail) { /* build mailto body */ const bodyLines = Promptinator._lastMeta .map((m, i) => `${prettify(m.lab)}: ${Promptinator._lastState[i]}`) .join('\n'); btn.onclick = () => { window.location.href = `mailto:${cur}?subject=${encodeURIComponent(niceLabel)}&body=${encodeURIComponent(bodyLines)}`; dlg.close(); }; } else { /* build autoβPOST form */ const form = document.createElement('form'); form.method = 'post'; form.action = cur; /* hidden title field */ const titleInp = document.createElement('input'); titleInp.type = 'hidden'; titleInp.name = '_formTitle'; titleInp.value = niceLabel; form.append(titleInp); /* each field as hidden input */ Promptinator._lastMeta.forEach((m, i) => { const inp = document.createElement('input'); inp.type = 'hidden'; inp.name = prettify(m.lab).replace(/\s+/g, '-').toLowerCase(); inp.value = Promptinator._lastState[i]; form.append(inp); }); pb.append(form); btn.onclick = () => { form.submit(); dlg.close(); }; } return; // E handled } /* helpers */ const commit = v => { done(v); dlg.close(); }; /* A */ if (meta.cmd === 'A') { const ta = pb.querySelector('textarea'); ta.focus(); pb.querySelector('button').onclick = () => commit(ta.value); return; } /* B */ if (meta.cmd === 'B') { pb.onclick = e => { if (e.target.name === 'r') commit(e.target.value); }; return; } /* C */ if (meta.cmd === 'C') { pb.querySelector('button').onclick = () => { const vals = [...pb.querySelectorAll('input:checked')].map(i => i.value); commit(vals.join(', ') || cur); }; return; } /* D */ if (meta.cmd === 'D') { pb.querySelector('select').onchange = e => commit(e.target.value); return; } /* I */ if (meta.cmd === 'I') { const sel = pb.querySelector('select'); const fr = pb.querySelector('iframe'); sel.onchange = () => { fr.hidden = false; fr.src = sel.value; }; pb.querySelector('button').onclick = () => commit(sel.value || cur); return; } /* default singleβline */ const inp = pb.querySelector('input'); inp.focus(); pb.querySelector('button').onclick = () => commit(inp.value); inp.onkeydown = e => { if (e.key === 'Enter') commit(inp.value); }; } /* popup */ }
Save changes
Create folder
writable 0777
Create
Cancel