@pmeweb/plugin-chat 1.3.4 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @pmeweb/plugin-chat
2
2
 
3
3
  A virtual assistant for a PmeWeb site: a bubble that answers visitors from the site's own content,
4
- takes a request (name, a way to reach them, the need) one question at a time, alerts the owner by
4
+ takes a request (a name and a way to reach them; the need asked for, never required) one question at a time, alerts the owner by
5
5
  email (and text), and lets the owner take over live.
6
6
 
7
7
  Three pieces:
@@ -119,7 +119,7 @@ file ».
119
119
  and after 3 sends without an echo shows « Non envoyé, Réessayer ». A 44xx close never reconnects.
120
120
  - **Fallback.** Daily cap reached, 15 visitor messages in the conversation, the visitor's 10
121
121
  conversations of the day used up (pmeweb-chat answers the start 409 `cap`), the model down, or
122
- the start refused: a three-field contact form that posts to `/api/chat/start` with `fallbackLead`
122
+ the start refused: a contact form (name and contact required, the request optional since 1.3.5) that posts to `/api/chat/start` with `fallbackLead`
123
123
  (never counted against the visitor's 10, and on the looser `CHAT_LIMIT`).
124
124
  - **Limits.** GLOBAL since 1.2.0, the same for every site, edited in the portal admin
125
125
  (« Assistant virtuel ») and applied by pmeweb-chat: messages per conversation, conversations per
@@ -1 +1 @@
1
- (()=>{function x({doc:o=document,hostname:n=globalThis.location?.hostname??""}={}){let a=o.getElementById("pmw-hosts");if(!a)return!1;let s;try{s=JSON.parse(a.textContent??"")}catch{return!1}if(!Array.isArray(s))return!1;let c=String(n).toLowerCase();return s.some(t=>typeof t=="string"&&t.toLowerCase()===c)}var v=["pointerdown","pointerenter","focusin","keydown"];function A(o,n,{events:a=v}={}){let s=!1,c=()=>{if(!s){s=!0;for(let t of a)o.removeEventListener(t,c);n()}};for(let t of a)o.addEventListener(t,c,{passive:!0});return{run:c}}var I="pmw-chat-opener",C=2e4,N=.3,P=[...v,"pointermove","scroll"];function y({doc:o=document,win:n=window,hostname:a=n.location.hostname,importLazy:s=t=>import(t),onInteraction:c=A}={}){let t=o.getElementById("pmw-chat");if(!t)return null;t.hidden=!1;let i=t.querySelector(".pmw-chat__teaser"),f=null,p=()=>f??(f=s(t.dataset.pmwLazy).then(e=>e.mountChat(t),e=>{throw f=null,e})),g=()=>p().catch(()=>{}),L=()=>p().then(e=>e.open(),()=>{}),d=()=>t.hasAttribute("data-open");c(o,g,{events:P});let m=x({doc:o,hostname:a});if(!m){let e=()=>{let r=new URLSearchParams(n.location.hash.slice(1)).get("pmw-chat");r==="panel"&&!d()?L():r==="bubble"&&f&&f.then(E=>E.close?.(),()=>{})};e(),n.addEventListener("hashchange",e)}let h=!1,b=(e=o.activeElement)=>{let r=h&&!d()&&!t.contains(e);t.toggleAttribute("inert",r)},T=o.querySelector("[data-pmw-chat-avoid]")??[...o.querySelectorAll("footer")].pop();m&&T&&(new n.IntersectionObserver(e=>{h=e.at(-1).isIntersecting,b()}).observe(T),t.addEventListener("focusout",e=>b(e.relatedTarget)));let S=n.sessionStorage,l=!1;try{l=!!S?.getItem(I)}catch{}let O=(e,r,E)=>e.addEventListener(r,_=>l||E(_),{passive:!0}),w=()=>{l=!0;try{S?.setItem(I,"1")}catch{}},u=()=>{l||h||!i||d()||(w(),i.hidden=!1,g())};return t.addEventListener("click",e=>{let r=e.target.closest?.("[data-pmw-chat-open],[data-pmw-chat-dismiss]");r&&(i&&(i.hidden=!0),w(),r.hasAttribute("data-pmw-chat-open")&&L())}),!l&&i&&m&&(n.setTimeout(u,C),O(n,"scroll",()=>{let e=o.documentElement.scrollHeight-n.innerHeight;e>0&&n.scrollY/e>=N&&u()}),n.matchMedia?.("(pointer: fine)").matches&&O(o,"mouseout",e=>{!e.relatedTarget&&e.clientY<=0&&u()})),{load:p,show:u}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>y()):y();})();
1
+ (()=>{function A({doc:o=document,hostname:n=globalThis.location?.hostname??""}={}){let a=o.getElementById("pmw-hosts");if(!a)return!1;let s;try{s=JSON.parse(a.textContent??"")}catch{return!1}if(!Array.isArray(s))return!1;let c=String(n).toLowerCase();return s.some(t=>typeof t=="string"&&t.toLowerCase()===c)}var y=["pointerdown","pointerenter","focusin","keydown"];function I(o,n,{events:a=y}={}){let s=!1,c=()=>{if(!s){s=!0;for(let t of a)o.removeEventListener(t,c);n()}};for(let t of a)o.addEventListener(t,c,{passive:!0});return{run:c}}var _="pmw-chat-opener",P=2e4,C=.3,M=3,R=[...y,"pointermove","scroll"];function g({doc:o=document,win:n=window,hostname:a=n.location.hostname,importLazy:s=t=>import(t),onInteraction:c=I}={}){let t=o.getElementById("pmw-chat");if(!t)return null;t.hidden=!1;let i=t.querySelector(".pmw-chat__teaser"),l=null,p=()=>l??(l=s(t.dataset.pmwLazy).then(e=>e.mountChat(t),e=>{throw l=null,e})),L=()=>p().catch(()=>{}),b=()=>p().then(e=>e.open(),()=>{}),d=()=>t.hasAttribute("data-open");c(o,L,{events:R});let m=A({doc:o,hostname:a});if(!m){let e=()=>{let r=new URLSearchParams(n.location.hash.slice(1)).get("pmw-chat");r==="panel"&&!d()?b():r==="bubble"&&l&&l.then(v=>v.close?.(),()=>{})};e(),n.addEventListener("hashchange",e)}let h=!1,T=(e=o.activeElement)=>{let r=h&&!d()&&!t.contains(e);t.toggleAttribute("inert",r)},S=o.querySelector("[data-pmw-chat-avoid]")??[...o.querySelectorAll("footer")].pop();m&&S&&(new n.IntersectionObserver(e=>{h=e.at(-1).isIntersecting,T()}).observe(S),t.addEventListener("focusout",e=>T(e.relatedTarget)));let O=n.sessionStorage,E=0;try{E=O?.getItem(_)}catch{}let f=E>=M,x=(e,r,v)=>e.addEventListener(r,N=>f||v(N),{passive:!0}),w=e=>{f=!0;try{O?.setItem(_,e)}catch{}},u=()=>{f||h||!i||d()||(w(+E+1),i.hidden=!1,L())};return t.addEventListener("click",e=>{let r=e.target.closest?.("[data-pmw-chat-open],[data-pmw-chat-dismiss]");r&&(i&&(i.hidden=!0),w(9),r.hasAttribute("data-pmw-chat-open")&&b())}),!f&&i&&m&&(n.setTimeout(u,P),x(n,"scroll",()=>{let e=o.documentElement.scrollHeight-n.innerHeight;e>0&&n.scrollY/e>=C&&u()}),n.matchMedia?.("(pointer: fine)").matches&&x(o,"mouseout",e=>{!e.relatedTarget&&e.clientY<=0&&u()})),{load:p,show:u}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>g()):g();})();
package/dist/widget.js CHANGED
@@ -1,2 +1,2 @@
1
1
  var dt=`.pmw-chat__panel{position:fixed;z-index:81;inset:0;display:flex;flex-direction:column;background:var(--pmw-chat-panel-bg, #ffffff);color:var(--pmw-chat-fg, #1f2328);font-family:var(--pmw-chat-font, inherit);font-size:16px;line-height:1.45}.pmw-chat__panel[hidden]{display:flex!important;opacity:0;pointer-events:none}.pmw-chat__panel [hidden]{display:none!important}.pmw-chat__panel{transform-origin:bottom right;transition:opacity .22s cubic-bezier(.2,.8,.2,1),transform .22s cubic-bezier(.2,.8,.2,1)}.pmw-chat__panel[hidden]{transform:translateY(24px);transition:opacity .16s ease-in,transform .16s ease-in}@media(min-width:640px){.pmw-chat__panel{inset:auto;right:calc(env(safe-area-inset-right,0px) + var(--pmw-chat-right, 16px));bottom:calc(env(safe-area-inset-bottom,0px) + var(--pmw-chat-bottom, 16px));width:380px;height:min(600px,calc(100dvh - 16px - env(safe-area-inset-bottom,0px) - var(--pmw-chat-bottom, 16px)));border:1px solid var(--pmw-chat-line, #e6e9f0);border-radius:20px;overflow:hidden;box-shadow:0 18px 48px #1018282e}.pmw-chat__panel[hidden]{transform:translateY(12px) scale(.96)}}@media(prefers-reduced-motion:reduce){.pmw-chat__panel,.pmw-chat__panel[hidden]{transform:none}}.pmw-chat__panel :where(button){font:inherit;cursor:pointer}.pmw-chat__panel :focus-visible{outline:3px solid var(--pmw-chat-focus, #0a1b3d);outline-offset:2px}.pmw-chat__head{display:flex;align-items:center;gap:12px;padding:14px 10px 14px 16px;border-bottom:1px solid var(--pmw-chat-line, #e6e9f0)}@media(max-width:639.98px){.pmw-chat__head{padding:12px 8px 12px 16px}}.pmw-chat__face{position:relative;flex:none;display:flex}.pmw-chat__head .pmw-chat__avatar{width:44px;height:44px;box-shadow:none}.pmw-chat__head .pmw-chat__avatar--icon{box-shadow:0 0 0 1.5px var(--pmw-chat-avatar-ring, transparent)}.pmw-chat__head .pmw-chat__avatar img{width:44px;height:44px}.pmw-chat__head .pmw-chat__avatar--logo img{width:32px;height:32px}.pmw-chat__head .pmw-chat__avatar svg{width:100%;height:100%}.pmw-chat__dot{position:absolute;right:-1px;bottom:-1px;width:12px;height:12px;border-radius:50%;background:var(--pmw-chat-live, #1a7f37);box-shadow:0 0 0 2px var(--pmw-chat-panel-bg, #ffffff)}.pmw-chat__who{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.pmw-chat__name{margin:0;font-weight:600;font-size:16px}.pmw-chat__role,.pmw-chat__notice{margin:0;font-size:13px;line-height:1.4;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__panel[data-mode=human] .pmw-chat__role{color:var(--pmw-chat-live, #1a7f37);font-weight:500}.pmw-chat__notice a{color:var(--pmw-chat-link, #0a1b3d);text-decoration:underline;text-underline-offset:2px}.pmw-chat__close{flex:none;width:44px;height:44px;padding:0;border:0;border-radius:50%;display:flex;align-items:center;justify-content:center;background:transparent;color:var(--pmw-chat-fg, #1f2328)}.pmw-chat__close:hover{background:var(--pmw-chat-thread-bg, #f7f8fb)}.pmw-chat__close svg{width:18px;height:18px}.pmw-chat__body{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto;padding:18px 16px;background:var(--pmw-chat-thread-bg, #f7f8fb)}.pmw-chat__log{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.pmw-chat__log>li{margin-top:4px}.pmw-chat__log>li:first-child,.pmw-chat__msg--bot+.pmw-chat__msg--bot,.pmw-chat__msg--bot+.pmw-chat__typing,.pmw-chat__msg--owner+.pmw-chat__msg--owner,.pmw-chat__msg--owner+.pmw-chat__typing,.pmw-chat__msg--visitor+.pmw-chat__msg--visitor{margin-top:0}.pmw-chat__msg{max-width:82%;padding:10px 14px;border-radius:18px;white-space:pre-wrap;overflow-wrap:anywhere}.pmw-chat__msg--bot,.pmw-chat__msg--owner{align-self:flex-start;border-bottom-left-radius:6px;color:var(--pmw-chat-fg, #1f2328)}.pmw-chat__msg--bot{background:var(--pmw-chat-other-bg, #ffffff);border:1px solid var(--pmw-chat-line, #e6e9f0)}.pmw-chat__msg--owner{background:var(--pmw-chat-owner-bg, #eef2fa);border:1px solid var(--pmw-chat-owner-border, #d5ddef)}.pmw-chat__msg--visitor{align-self:flex-end;background:var(--pmw-chat-accent, #0a1b3d);color:var(--pmw-chat-accent-fg, #ffffff);border-bottom-right-radius:6px}.pmw-chat__msg--bot+.pmw-chat__msg--bot,.pmw-chat__msg--owner+.pmw-chat__msg--owner{border-top-left-radius:6px}.pmw-chat__msg--visitor+.pmw-chat__msg--visitor{border-top-right-radius:6px}.pmw-chat__msg:has(>.pmw-chat__photo){padding:4px}.pmw-chat__msg:has(>.pmw-chat__photo) .pmw-chat__text:empty{display:none}.pmw-chat__msg.is-pending{opacity:.75}.pmw-chat__msg.is-failed{outline:2px solid var(--pmw-chat-error, #b42318);outline-offset:2px}.pmw-chat__retry{display:block;margin-top:4px;font-size:14px}.pmw-chat__retry button{padding:0;border:0;background:none;color:inherit;text-decoration:underline}.pmw-chat__typing{align-self:flex-start;display:flex;gap:5px;padding:14px 16px;border-radius:18px 18px 18px 6px;background:var(--pmw-chat-other-bg, #ffffff);border:1px solid var(--pmw-chat-line, #e6e9f0)}.pmw-chat__typing span{width:7px;height:7px;border-radius:50%;background:var(--pmw-chat-muted, #4b5563);animation:pmw-chat-dot 1.2s infinite ease-in-out}.pmw-chat__typing span:nth-child(2){animation-delay:.15s}.pmw-chat__typing span:nth-child(3){animation-delay:.3s}@keyframes pmw-chat-dot{0%,80%,to{opacity:.3}40%{opacity:1}}@media(prefers-reduced-motion:reduce){.pmw-chat__typing span{animation:none;opacity:.7}}.pmw-chat__log>.pmw-chat__relay,.pmw-chat__log>.pmw-chat__handback{align-self:stretch;margin:18px 0 8px;display:flex;align-items:center;gap:10px;font-size:13px;font-weight:500;line-height:1.4;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__relay:before,.pmw-chat__relay:after,.pmw-chat__handback:before,.pmw-chat__handback:after{content:"";flex:1;min-width:16px;height:1px;background:var(--pmw-chat-rule, #cfd5e1)}.pmw-chat__relay-label{display:flex;align-items:center;gap:6px;text-align:center}.pmw-chat__relay-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--pmw-chat-live, #1a7f37)}.pmw-chat__relay-dot--back{background:var(--pmw-chat-muted, #4b5563)}.pmw-chat__log>.pmw-chat__relay+li,.pmw-chat__log>.pmw-chat__handback+li{margin-top:0}.pmw-chat__state{margin:8px 0 0;font-size:14px;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__state:empty{display:none}.pmw-chat__ended{margin-top:22px;display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center}.pmw-chat__ended:before{content:"";align-self:stretch;height:1px;background:var(--pmw-chat-line, #e6e9f0)}.pmw-chat__ended p{margin:0;font-size:15px;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__restart{min-height:44px;padding:0 20px;border-radius:22px;border:1px solid var(--pmw-chat-link, #0a1b3d);background:var(--pmw-chat-panel-bg, #ffffff);color:var(--pmw-chat-link, #0a1b3d);font-weight:600}.pmw-chat__composer{--pmw-chat-gap: 8px;display:flex;flex-direction:column;gap:var(--pmw-chat-gap);padding:14px 16px 16px;border-top:1px solid var(--pmw-chat-line, #e6e9f0);background:var(--pmw-chat-panel-bg, #ffffff)}@media(max-width:639.98px){.pmw-chat__composer{padding:12px 16px calc(14px + env(safe-area-inset-bottom,0px))}}.pmw-chat__box{display:flex;align-items:flex-end;gap:6px;padding:4px;border-radius:24px;border:1px solid var(--pmw-chat-field-border, #7d8590);background:#fff;color:var(--pmw-chat-field-fg, #1f2328)}.pmw-chat__box textarea{flex:1;min-width:0;box-sizing:border-box;min-height:44px;max-height:132px;resize:none;margin:0;padding:10px 2px;border:0;border-radius:0;background:transparent;color:inherit;font:inherit;font-size:16px;line-height:1.5}.pmw-chat__box textarea::placeholder{color:#6b7280;opacity:1}@supports selector(:has(*)){.pmw-chat__box textarea:focus-visible{outline:none}.pmw-chat__box:has(textarea:focus-visible){outline:3px solid var(--pmw-chat-focus, #0a1b3d);outline-offset:2px}}.pmw-chat__send{flex:none;min-height:44px;padding:0 18px;border:0;border-radius:22px;font-weight:600;background:var(--pmw-chat-accent, #0a1b3d);color:var(--pmw-chat-accent-fg, #ffffff)}.pmw-chat__send:disabled,.pmw-chat__box textarea:disabled{cursor:not-allowed;opacity:.6}.pmw-chat__check{position:relative;display:grid;grid-template-rows:0fr;margin-bottom:calc(-1 * var(--pmw-chat-gap, 0px));transition:grid-template-rows .25s ease,margin-bottom .25s ease}.pmw-chat__check[data-ask]{grid-template-rows:1fr;margin-bottom:4px}.pmw-chat__check-in{min-height:0;overflow:hidden}.pmw-chat__check:not([data-ask]) .pmw-chat__check-in{position:absolute;top:0;left:0;right:0;overflow:visible;opacity:0;pointer-events:none}@media(prefers-reduced-motion:reduce){.pmw-chat__check{transition:none}}.pmw-chat__attach{flex:none;position:relative;width:44px;height:44px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:inherit}.pmw-chat__attach:hover{background:#eef0f4}.pmw-chat__attach svg{width:22px;height:22px}.pmw-chat__attach:focus-within{outline:3px solid var(--pmw-chat-focus, #0a1b3d);outline-offset:2px}.pmw-chat__hint{margin:0 8px;font-size:13px;line-height:1.4;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__composer .pmw-chat__photo-error{margin:0 8px}.pmw-chat__photo{display:block;max-width:100%;max-height:220px;border-radius:14px}.pmw-chat__fallback{--pmw-chat-gap: 14px;display:flex;flex-direction:column;gap:var(--pmw-chat-gap);margin-top:16px}.pmw-chat__fb-title{margin:0;font-size:20px;font-weight:600;line-height:1.3}.pmw-chat__fb-intro{margin:-8px 0 0;font-size:15px;color:var(--pmw-chat-muted, #4b5563)}.pmw-chat__field{display:flex;flex-direction:column;gap:4px}.pmw-chat__field label{font-size:14px;font-weight:600}.pmw-chat__field input,.pmw-chat__field textarea{font:inherit;font-size:16px;padding:10px 14px;border-radius:12px;resize:none;border:1px solid var(--pmw-chat-field-border, #7d8590);background:#fff;color:var(--pmw-chat-field-fg, #1f2328)}.pmw-chat__fallback .pmw-chat__send{align-self:stretch;min-height:48px;border-radius:24px}.pmw-chat__error{margin:0;font-size:14px;color:var(--pmw-chat-error, #b42318)}.pmw-chat__error:empty{display:none}.pmw-chat__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.pmw-chat[data-unread] .pmw-chat__bubble{position:relative}.pmw-chat__badge{position:absolute;top:-4px;left:30px;min-width:20px;height:20px;padding:0 6px;box-sizing:border-box;border-radius:10px;background:var(--pmw-chat-error, #b42318);color:#fff;box-shadow:0 0 0 2px #fff;font-size:12px;font-weight:700;line-height:20px;text-align:center}.pmw-chat__fb-done{margin:12px 0 0;padding:12px 14px;border-radius:12px;background:#e9f6ee;color:#14532d;border:1px solid #b7dfc4;font-weight:500}
2
- `;var Ht="400px 0px";var ht=new Set,me=null;function mt(r,{doc:c=document,attrs:n={}}={}){if(ht.has(r)||(ht.add(r),c.querySelector(`script[src="${r}"]`)))return;let t=c.createElement("script");t.src=r,t.async=!0;for(let[h,d]of Object.entries(n))t.setAttribute(h,d);c.head.append(t)}function ut({doc:r=document,hostname:c=globalThis.location?.hostname??""}={}){let n=r.getElementById("pmw-hosts");if(!n)return!1;let t;try{t=JSON.parse(n.textContent??"")}catch{return!1}if(!Array.isArray(t))return!1;let h=String(c).toLowerCase();return t.some(d=>typeof d=="string"&&d.toLowerCase()===h)}function Dt(r){return me||(me=new Promise(c=>{let n=null,t=null,h=0,d=r.PerformanceObserver?.supportedEntryTypes??[],u=typeof r.PerformanceObserver=="function"&&d.includes("paint"),p=()=>{n===null||u&&t===null||c(Math.max(n,t??0,h))};if(u)try{new r.PerformanceObserver(x=>{for(let v of x.getEntries())v.name==="first-contentful-paint"&&(t=v.startTime);p()}).observe({type:"paint",buffered:!0}),d.includes("largest-contentful-paint")&&new r.PerformanceObserver(x=>{for(let v of x.getEntries())h=Math.max(h,v.startTime)}).observe({type:"largest-contentful-paint",buffered:!0})}catch{t=0}let w=()=>{n=r.performance.now(),p()};r.document.readyState==="complete"?w():r.addEventListener("load",w,{once:!0})}),me)}function Ie(r,c,{win:n=window,rootMargin:t=Ht,settleMs:h=1500}={}){let d=null,u=!1,p=()=>{u||(u=!0,d?.disconnect(),r.removeEventListener("focusin",p),r.removeEventListener("pointerdown",p),c())};r.addEventListener("focusin",p),r.addEventListener("pointerdown",p);let w=Dt(n),x=async()=>{let v=await w;n.setTimeout(p,Math.max(0,v+h-n.performance.now()))};return typeof n.IntersectionObserver=="function"?(d=new n.IntersectionObserver(v=>{v.some(T=>T.isIntersecting)&&(d.disconnect(),x())},{rootMargin:t}),d.observe(r)):x(),{run:p}}var Ut="https://challenges.cloudflare.com/turnstile/v0/api.js";var ft=15e3;function wt(r=document){mt(Ut,{doc:r})}var _t={unauthorized:4401,expired:4410,rpc:4426,deleted:4404};var Bt={fr:{assistant:"Assistant virtuel",openLabel:"Assistant virtuel, ouvrir le clavardage",dismissTeaser:"Masquer ce message",close:"Fermer le clavardage",panelTitle:"Clavardage",automated:"R\xE9ponses automatiques, tir\xE9es du contenu de ce site.",privacy:"Confidentialit\xE9",you:"Vous",messageLabel:"Votre message",send:"Envoyer",live:"Vous r\xE9pond en direct",relay:"{name} a pris le relais",relayGeneric:"Une personne de l\u2019\xE9quipe a pris le relais",handback:"L\u2019assistant virtuel a repris la conversation",oneMoment:"Un instant\u2026",reconnecting:"Connexion perdue, reconnexion\u2026",notSent:"Non envoy\xE9.",retry:"R\xE9essayer",ended:"Cette conversation est termin\xE9e.",newConversation:"Nouvelle conversation",reload:"Rechargez la page pour continuer.",unavailable:"Un probl\xE8me est survenu. R\xE9essayez dans un moment.",turnstile:"L\u2019envoi n\u2019a pas abouti. R\xE9essayez.",tooMany:"Un instant, puis r\xE9essayez.",fallbackTitle:"Laissez-nous vos coordonn\xE9es",fallbackCap:"Laissez vos coordonn\xE9es et nous vous recontacterons.",fallbackDown:"Laissez vos coordonn\xE9es et nous vous recontacterons.",name:"Votre nom",contact:"T\xE9l\xE9phone ou courriel",need:"Votre demande",fallbackSend:"Envoyer la demande",fallbackSent:"Merci, votre demande a \xE9t\xE9 transmise.",fallbackMissing:"Remplissez les trois champs.",said:"{name}\xA0: {text}",unreadOne:"{n} nouveau message",unreadMany:"{n} nouveaux messages",attachPhoto:"Joindre une photo",photoHint:"\xC9vitez les photos de personnes ou de pi\xE8ces d\u2019identit\xE9.",photoAlt:"Votre photo",photoSent:"Photo envoy\xE9e",photoLimit:"Trois photos au plus par conversation.",photoTooLarge:"Cette photo est trop lourde.",photoUnreadable:"Ce fichier n\u2019est pas une photo lisible.",photoFailed:"La photo n\u2019a pas pu \xEAtre envoy\xE9e. R\xE9essayez.",demoAsk:"Pouvez-vous m\u2019en dire plus sur {topic}?",demoQuestion:"Quels sont vos d\xE9lais?",demoAnswer:"(D\xE9monstration) Ici, l\u2019assistant r\xE9pond \xE0 partir du contenu de votre site.",demoHint:"Aper\xE7u\xA0: la conversation est d\xE9sactiv\xE9e."},en:{assistant:"Virtual assistant",openLabel:"Virtual assistant, open the chat",dismissTeaser:"Hide this message",close:"Close the chat",panelTitle:"Chat",automated:"Automatic answers, drawn from this site\u2019s content.",privacy:"Privacy",you:"You",messageLabel:"Your message",send:"Send",live:"Replying live",relay:"{name} took over",relayGeneric:"A team member took over",handback:"The virtual assistant is back in the conversation",oneMoment:"One moment\u2026",reconnecting:"Connection lost, reconnecting\u2026",notSent:"Not sent.",retry:"Retry",ended:"This conversation has ended.",newConversation:"New conversation",reload:"Reload the page to continue.",unavailable:"Something went wrong. Try again in a moment.",turnstile:"Sending did not go through. Try again.",tooMany:"One moment, then try again.",fallbackTitle:"Leave us your details",fallbackCap:"Leave your details and we will get back to you.",fallbackDown:"Leave your details and we will get back to you.",name:"Your name",contact:"Phone or email",need:"Your request",fallbackSend:"Send the request",fallbackSent:"Thank you, your request was sent.",fallbackMissing:"Fill in all three fields.",said:"{name}: {text}",unreadOne:"{n} new message",unreadMany:"{n} new messages",attachPhoto:"Attach a photo",photoHint:"Avoid photos of people or of identity documents.",photoAlt:"Your photo",photoSent:"Photo sent",photoLimit:"Three photos at most per conversation.",photoTooLarge:"This photo is too large.",photoUnreadable:"This file is not a readable photo.",photoFailed:"The photo could not be sent. Try again.",demoAsk:"Can you tell me more about {topic}?",demoQuestion:"What are your lead times?",demoAnswer:"(Demo) Here, the assistant answers from your site\u2019s content.",demoHint:"Preview: the conversation is turned off."}},ue=r=>String(r??"").slice(0,2).toLowerCase()==="en"?"en":"fr";function ze(r,c={}){let n=Bt[ue(r)],t={...n};for(let[h,d]of Object.entries(c??{}))typeof d=="string"&&h in n&&(t[h]=d);return t}var J=(r,c)=>r.replace(/\{(\w+)\}/g,(n,t)=>t in c?String(c[t]):n);var fe="pmw-chat",bt=[1e3,2e3,4e3,8e3,16e3,3e4],xt=3,Ft="chat-lead",$t=3,Jt=1600,Vt=.85,jt=3e3,Gt=7200*1e3,Yt=8e3,Kt=40,Xt=2e3,Wt=3e3,vt=8e3,Qt='<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="3"/><circle cx="9" cy="10" r="1.6"/><path d="m21 16-5-5-8 8"/></svg>';async function Zt(r,c){let n=await r.createImageBitmap(c,{imageOrientation:"from-image"}),t=Math.min(1,Jt/Math.max(n.width,n.height)),h=Math.max(1,Math.round(n.width*t)),d=Math.max(1,Math.round(n.height*t)),u=r.document.createElement("canvas");return u.width=h,u.height=d,u.getContext("2d").drawImage(n,0,0,h,d),n.close?.(),new Promise((p,w)=>u.toBlob(x=>x?p(x):w(new Error("encode")),"image/jpeg",Vt))}var ea={fr:{nos:"vos",notre:"votre",nous:"vous"},en:{our:"your",us:"you",we:"you"}},ta={fr:/^une question sur (.+?)\s*\?$/i,en:/^(?:a |any )?questions? (?:about|on) (.+?)\s*\?$/i};function aa(r,c){let n=ue(c),t=ze(n),h=String(r??"").trim().match(ta[n]);if(!h)return t.demoQuestion;let d=h[1].replace(/[\p{L}’']+/gu,u=>ea[n][u.toLowerCase()]??u);return J(t.demoAsk,{topic:d})}var na='<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/></svg>';function oa(r){let c=null;return{unlock(){try{let n=r.AudioContext||r.webkitAudioContext;if(!n)return;c??(c=new n),c.state==="suspended"&&c.resume()}catch{c=null}},play(){if(c)try{c.state==="suspended"&&c.resume();let n=c.currentTime;for(let[t,h]of[[880,0],[1320,.12]]){let d=c.createOscillator(),u=c.createGain();d.type="sine",d.frequency.value=t,u.gain.setValueAtTime(1e-4,n+h),u.gain.exponentialRampToValueAtTime(.15,n+h+.02),u.gain.exponentialRampToValueAtTime(1e-4,n+h+.35),d.connect(u).connect(c.destination),d.start(n+h),d.stop(n+h+.4)}}catch{}}}}function i(r,c,n={},t){let h=r.createElement(c);for(let[d,u]of Object.entries(n))u!=null&&u!==!1&&h.setAttribute(d,u===!0?"":u);return t!==void 0&&(h.textContent=t),h}var ra=/^(?:300\d{3}|600\d{3}|110600|110620)$/,ia=r=>ra.test(String(r??"")),yt=200,sa=(r,c)=>{let n=!1,t=p=>{let w=r.turnstile;if(!(p.el.hasAttribute("data-pmw-rendered")||!p.shown())){p.el.setAttribute("data-pmw-rendered","");try{p.id=w.render(p.el,p.params)}catch{}}},h=()=>{if(!r.turnstile?.render)return!1;for(let p of c)t(p);return!0},d=()=>{if(wt(r.document),n||h())return;n=!0;let p=0,w=()=>{!h()&&++p<600&&r.setTimeout(w,100)};r.setTimeout(w,100)},u=p=>c.find(w=>w.form===p);for(let p of c)p.params["error-callback"]=w=>(p.ask(!1),ia(w)&&!p.retried&&p.id!==void 0?(p.retried=!0,r.setTimeout(()=>{try{r.turnstile?.reset(p.id)}catch{p.failed=!0}},0)):p.failed=!0,!0);return{load:d,async token(p){d();let w=u(p),x=0;for(;;){if(w?.failed)return null;let v=p.querySelector('input[name="cf-turnstile-response"]')?.value??"";if(v)return v;if(!w?.asking()){if(x>=ft)return"";x+=yt}await new Promise(T=>r.setTimeout(T,yt))}},reset(p){let w=u(p);!w||w.id===void 0||w.asking()||(w.failed=!1,w.retried=!1,r.turnstile?.reset(w.id))}}};function xa(r,c={}){if(r.pmwChat)return r.pmwChat;let n=c.win??window,t=n.document,h=c.WebSocket??n.WebSocket,d=c.fetch??((...e)=>n.fetch(...e)),u=[],p=c.turnstile??null,w=c.uuid??(()=>n.crypto.randomUUID()),x=c.shrink??(e=>Zt(n,e)),v=c.preview??(e=>new Promise((a,o)=>{let l=new n.FileReader;l.onload=()=>a(String(l.result)),l.onerror=()=>o(l.error),l.readAsDataURL(e)})),T={};try{T=JSON.parse(r.querySelector("[data-pmw-chat-config]")?.textContent??"{}")}catch{}let Q=ue(T.lang),s=ze(Q,T.strings),He=r.dataset.pmwChatOpener??"",Z=r.querySelector(".pmw-chat__bubble"),we=c.chime??oa(n),kt=c.now??(()=>Date.now()),g=c.demo??(T.demo===!0||!ut({doc:t,hostname:n.location.hostname}));t.getElementById("pmw-chat-style")||t.head.append(i(t,"style",{id:"pmw-chat-style"},dt));let y=i(t,"div",{class:"pmw-chat__panel",role:"dialog","aria-modal":"true","aria-labelledby":"pmw-chat-name",hidden:!0});y.inert=!0;let De=i(t,"div",{class:"pmw-chat__head"}),_e=r.querySelector(".pmw-chat__avatar")?.cloneNode(!0)??i(t,"span",{class:"pmw-chat__avatar","aria-hidden":"true"});for(let e of _e.querySelectorAll("[id]")){let a=e.id;e.id=`${a}-head`;for(let o of _e.querySelectorAll(`[mask="url(#${a})"]`))o.setAttribute("mask",`url(#${a}-head)`)}let Ue=i(t,"div",{class:"pmw-chat__who"}),Be=i(t,"p",{class:"pmw-chat__name",id:"pmw-chat-name"}),ee=i(t,"p",{class:"pmw-chat__role"}),Fe=i(t,"span",{class:"pmw-chat__face"}),$e=i(t,"span",{class:"pmw-chat__dot","aria-hidden":"true",hidden:!0}),te=i(t,"button",{type:"button",class:"pmw-chat__close","aria-label":s.close});te.innerHTML=na;let ae=i(t,"p",{class:"pmw-chat__notice"},s.automated);T.privacyHref&&(ae.append(" "),ae.append(i(t,"a",{href:T.privacyHref},s.privacy))),Fe.append(_e,$e),Ue.append(Be,ee,ae),De.append(Fe,Ue,te);let k=i(t,"ol",{class:"pmw-chat__log"}),ge=i(t,"li",{class:"pmw-chat__msg pmw-chat__msg--bot pmw-chat__msg--greeting"},He),A=i(t,"li",{class:"pmw-chat__typing","aria-hidden":"true",hidden:!0});A.append(i(t,"span"),i(t,"span"),i(t,"span")),k.append(ge);let be=i(t,"p",{class:"pmw-chat__state",role:"status"}),Je=i(t,"p",{class:"pmw-chat__sr","aria-live":"polite"}),Ve=(e,a)=>{let o=i(t,"div",{class:"pmw-chat__check"}),l=i(t,"div",{class:"pmw-chat__check-in"});l.inert=!0;let m=he=>{o.toggleAttribute("data-ask",he),l.inert=!he,C()},_=i(t,"div",{class:"pmw-chat__turnstile"}),O={el:_,form:e,shown:a,ask:m,asking:()=>o.hasAttribute("data-ask"),params:{sitekey:T.siteKey??"",action:"chat",theme:"light",language:Q,appearance:"interaction-only",size:"flexible","refresh-expired":"auto",callback:()=>{O.retried=!1,O.failed=!1,m(!1)},"before-interactive-callback":()=>m(!0)}};return u.push(O),l.append(_),o.append(l),o},S=i(t,"form",{class:"pmw-chat__composer",novalidate:!0}),L=i(t,"textarea",{id:"pmw-chat-input",name:"message",rows:"1",maxlength:String(2e3),placeholder:s.messageLabel,autocomplete:"off",disabled:g,"aria-describedby":g?"pmw-chat-demo-hint":null}),St=i(t,"button",{type:"submit",class:"pmw-chat__send",disabled:g},s.send),xe=i(t,"div",{class:"pmw-chat__box"});xe.append(i(t,"label",{class:"pmw-chat__sr",for:"pmw-chat-input"},s.messageLabel),L,St),g||S.append(Ve(S,()=>!0)),S.append(xe),g&&S.append(i(t,"p",{class:"pmw-chat__hint",id:"pmw-chat-demo-hint"},s.demoHint));let V=null,H=i(t,"p",{class:"pmw-chat__error pmw-chat__photo-error",role:"alert"});if(T.photos===!0){let e=i(t,"label",{class:"pmw-chat__attach"});V=i(t,"input",{type:"file",class:"pmw-chat__sr",accept:"image/*",multiple:!0,"aria-describedby":"pmw-chat-photo-hint",disabled:g}),e.innerHTML=Qt,e.prepend(V),e.append(i(t,"span",{class:"pmw-chat__sr"},s.attachPhoto)),xe.insertBefore(e,L),S.append(i(t,"p",{class:"pmw-chat__hint",id:"pmw-chat-photo-hint"},s.photoHint),H)}let ne=i(t,"div",{class:"pmw-chat__ended",hidden:!0}),je=i(t,"p",{},s.ended),oe=i(t,"button",{type:"button",class:"pmw-chat__restart"},s.newConversation);ne.append(je,oe);let b=i(t,"form",{class:"pmw-chat__fallback",novalidate:!0,hidden:!0,"aria-labelledby":"pmw-chat-fb-title"}),Ge=i(t,"p",{class:"pmw-chat__fb-intro"}),ve=(e,a,o)=>{let l=`pmw-chat-fb-${e}`,m=i(t,"div",{class:"pmw-chat__field"});return m.append(i(t,"label",{for:l},s[e]),i(t,a,{id:l,name:e,required:!0,...o})),m},j=i(t,"p",{class:"pmw-chat__error",role:"alert"});b.append(i(t,"p",{class:"pmw-chat__fb-title",id:"pmw-chat-fb-title"},s.fallbackTitle),Ge,ve("name","input",{autocomplete:"name",maxlength:"100"}),ve("contact","input",{autocomplete:"on",maxlength:"200"}),ve("need","textarea",{rows:"3",maxlength:"2000"}),j,i(t,"button",{type:"submit",class:"pmw-chat__send"},s.fallbackSend)),g||b.insertBefore(Ve(b,()=>!b.hidden),j);let M=i(t,"div",{class:"pmw-chat__body"});M.append(k,be,ne,b),y.append(De,M,S,Je),r.append(y),p??(p=sa(n,u));let G=!0,Ye=0,ye=0,Tt=()=>M.scrollHeight-M.scrollTop-M.clientHeight;M.addEventListener("scroll",()=>{let e=M.scrollTop;Tt()<=Kt?G=!0:e<Ye&&(G=!1),Ye=e},{passive:!0});let Lt=e=>n.requestAnimationFrame?n.requestAnimationFrame(e):n.setTimeout(e,16);function C(e=!1,a=!1){e&&(G=!0),!(!G||ye)&&(ye=Lt(()=>{if(ye=0,!G)return;let o=a||!!n.matchMedia?.("(prefers-reduced-motion: reduce)").matches;M.scrollTo({top:M.scrollHeight,behavior:o?"instant":"smooth"})}))}if(typeof n.ResizeObserver=="function"){let e=new n.ResizeObserver(()=>C());e.observe(k),e.observe(S)}let f=null;if(!g)try{f=JSON.parse(n.sessionStorage.getItem(fe)??"null")}catch{f=null}if(f&&typeof f.at=="number"&&!(Date.now()-f.at<Gt)){f=null;try{n.sessionStorage.removeItem(fe)}catch{}}let ke=()=>{try{n.sessionStorage.setItem(fe,JSON.stringify(f))}catch{}},Ct=()=>{f&&(f.at=Date.now(),ke())},E=null,Se=0,Te=null,Y=null,re=0,K="bot",R=null,D=!1,Ke=0,Le=null,Ce=new Set,q=new Map,ie=new Set,Ee=0,Xe=()=>new URLSearchParams(n.location.hash.slice(1)).get("pmw-chat-botName")?.trim()||null,We=g?Xe():null,Ae=()=>g?We:f?.botName||null;function X(){let e=K==="human";Be.textContent=e?R||s.assistant:Ae()||s.assistant,ee.textContent=e?s.live:Ae()?s.assistant:"",ee.hidden=ee.textContent==="",ae.hidden=e,$e.hidden=!e,y.dataset.mode=K}X();let N=e=>{be.textContent=e??"",be.hidden=!e};N("");let Qe=e=>e.role==="visitor"?s.you:e.role==="owner"?e.by||R||s.assistant:Ae()||s.assistant;function U(e,a){let o=i(t,"li",{class:`pmw-chat__msg pmw-chat__msg--${a}`,"data-id":e}),l=i(t,"span",{class:"pmw-chat__sr"}),m=i(t,"span",{class:"pmw-chat__text"});return o.append(l,m),k.insertBefore(o,A.parentNode===k?A:null),o}let Ze=()=>A.parentNode===k?A.previousElementSibling:k.lastElementChild,P=(e,a)=>e.classList.contains(a);function et(e,a,o,l){let m=i(t,"li",{class:e,role:"separator","aria-label":o}),_=i(t,"span",{class:"pmw-chat__relay-label","aria-hidden":"true"});_.append(i(t,"span",{class:a}),o),m.append(_),k.insertBefore(m,l??(A.parentNode===k?A:null)),C()}function tt(e,a){for(let o=e?e.previousElementSibling:Ze();o;o=o.previousElementSibling){if(P(o,"pmw-chat__relay")||P(o,"pmw-chat__msg--owner"))return;if(P(o,"pmw-chat__msg--bot")||P(o,"pmw-chat__handback"))break}et("pmw-chat__relay","pmw-chat__relay-dot",a?J(s.relay,{name:a}):s.relayGeneric,e)}function Oe(e){for(let a=e?e.previousElementSibling:Ze();;a=a.previousElementSibling){if(!a||P(a,"pmw-chat__handback")||P(a,"pmw-chat__msg--bot"))return;if(P(a,"pmw-chat__relay")||P(a,"pmw-chat__msg--owner"))break}et("pmw-chat__handback","pmw-chat__relay-dot pmw-chat__relay-dot--back",s.handback,e)}let at=e=>[...k.querySelectorAll("li[data-id]")].find(a=>a.dataset.id===e),I=(e,a,o)=>{e.firstChild.textContent=J(s.said,{name:Qe(a),text:""}),e.lastChild.textContent=o===""&&a.photos?.length&&!e.querySelector("img")?s.photoSent:o};function B(){A.hidden=!0,n.clearTimeout(Le)}function Me(e){A.hidden=!1,k.append(A),n.clearTimeout(Le),e&&(Le=n.setTimeout(B,e)),C()}function nt(e,{announce:a}){if(Ce.has(e.seq))return;Ce.add(e.seq),re=Math.max(re,e.seq);let o=at(e.id);o||(o=U(e.id,e.role),e.role==="owner"?tt(o,e.by||R):e.role==="bot"&&Oe(o)),o.classList.remove("is-streaming","is-pending","is-failed"),o.querySelector(".pmw-chat__retry")?.remove(),I(o,e,e.text),e.role==="visitor"?(q.delete(e.id),e.photos?.length&&ie.add(e.id)):(B(),a&&(Je.textContent=J(s.said,{name:Qe(e),text:e.text}),(y.hidden||t.visibilityState==="hidden")&&Et(),y.hidden&&(z++,At()))),C()}let z=0,F=null,ot=-1/0;function Et(){let e=kt();e-ot<Xt||(ot=e,we.play())}function At(){if(!Z)return;F||(F=i(t,"span",{class:"pmw-chat__badge"}),Z.append(F));let e=z>9?"9+":String(z);F.replaceChildren(i(t,"span",{"aria-hidden":"true"},e),i(t,"span",{class:"pmw-chat__sr"},J(z===1?s.unreadOne:s.unreadMany,{n:String(z)}))),r.setAttribute("data-unread",String(z))}function Ot(e,a){B();let o=at(e);o||(o=U(e,"bot"),Oe(o),o.classList.add("is-streaming"),I(o,{role:"bot"},"")),o.lastChild.textContent+=a,C()}if(g){I(U("demo-question","visitor"),{role:"visitor"},aa(He,Q));let e=U("demo-answer","bot");I(e,{role:"bot"},s.demoAnswer),n.addEventListener("hashchange",()=>{We=Xe(),X(),I(e,{role:"bot"},s.demoAnswer)})}function se(e){if(e.failed=!0,e.li.classList.remove("is-pending"),e.li.classList.add("is-failed"),e.li.querySelector(".pmw-chat__retry"))return;let a=i(t,"span",{class:"pmw-chat__retry"},`${s.notSent} `),o=i(t,"button",{type:"button"},s.retry);o.addEventListener("click",()=>{e.failed=!1,e.tries=0,a.remove(),e.li.classList.replace("is-failed","is-pending"),f?ce()?Ne(e):$():Re()}),a.append(o),e.li.append(a)}let ce=()=>!!E&&E.readyState===1;function Ne(e){if(!(e.failed||!ce())){if(e.tries>=xt)return se(e);e.tries++,E.send(JSON.stringify({type:"message",id:e.id,text:e.text}))}}function $(){if(!f||D||E)return;n.clearTimeout(Te);let e=n.location.protocol==="https:"?"wss:":"ws:",a=new URLSearchParams({c:f.c,k:f.k}),o=new h(`${e}//${n.location.host}/api/chat/ws?${a}`);E=o,o.onopen=()=>{Se=0,de&&(W(),Me(vt)),N(""),o.send(JSON.stringify({type:"hello",since:re}));for(let l of q.values())Ne(l)},o.onmessage=l=>{let m;try{m=JSON.parse(l.data)}catch{return}Nt(m)},o.onclose=l=>{E===o&&(E=null);for(let _ of q.values())_.tries>=xt&&se(_);if(l.code>=4e3&&l.code<5e3)return Mt(l.code);if(D)return;W(),N(s.reconnecting);let m=bt[Math.min(Se,bt.length-1)];Se++,Te=n.setTimeout($,m)}}function Mt(e){if(e===_t.rpc)return le(s.reload,!1);le(s.ended,!0)}function le(e,a){D=!0,n.clearTimeout(Te),N(""),je.textContent=e,oe.hidden=!a,ne.hidden=!1,S.hidden=!0;for(let o of q.values())se(o);C()}function rt(e){K=e.mode==="human"?"human":"bot",R=typeof e.ownerName=="string"?e.ownerName:null,X(),K==="human"?tt(null,R):Oe(null),e.closed&&le(s.ended,!0)}function Nt(e){switch(e?.type){case"history":typeof e.ownerName=="string"&&(R=e.ownerName);for(let a of e.messages??[])nt(a,{announce:!1});return rt(e);case"message":return Ct(),nt(e.msg,{announce:e.msg?.role!=="visitor"});case"delta":return Ot(e.id,e.text);case"typing":return e.role==="visitor"?void 0:Me(vt);case"status":return rt(e);case"fallback":return ct(e.reason==="cap"?s.fallbackCap:s.fallbackDown);case"lead":return st();case"error":(e.code==="closed"||e.code==="deleted")&&le(s.ended,!0)}}let it=!1;function st(){if(!(it||f?.lead)){it=!0,f&&(f.lead=!0,ke());try{n.pmw?.event(Ft)}catch{}}}let pe=new Set;async function Pe(e,a){let o="";if(pe.has(e)){pe.delete(e);try{p.reset(e)}catch{}}try{o=await p.token(e)}catch{o=""}if(o===null)return pe.add(e),{ok:!1,error:s.turnstile,cap:!1,turnstile:!0};let l,m=null;try{l=await d("/api/chat/start",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:o,lang:Q,page:n.location.pathname,...a})}),m=await l.json()}catch{l=null}return pe.add(e),l?.ok&&m?{ok:!0,data:m}:{ok:!1,error:l?.status===403?s.turnstile:l?.status===429?s.tooMany:null,cap:l?.status===409&&m?.error==="cap",turnstile:l?.status===403}}let de=null,W=()=>{n.clearTimeout(de),de=null};function Re(){if(Y||f)return Y;N(""),Me(),de=n.setTimeout(()=>N(s.oneMoment),Yt);let e=k.firstElementChild===ge?ge.textContent.trim():"";return Y=Pe(S,e?{opener:e}:{}).then(a=>{if(Y=null,a.ok&&a.data.conversationId&&a.data.visitorKey){f={c:a.data.conversationId,k:a.data.visitorKey,botName:typeof a.data.botName=="string"?a.data.botName:null,at:Date.now()},ke(),X(),$();return}W(),B();for(let o of q.values())se(o);if(a.error)return N(a.error);N(""),ct(a.cap?s.fallbackCap:s.fallbackDown)}),Y}function ct(e){B(),W(),N(""),Ge.textContent=e,b.hidden=!1,S.hidden=!0,g||p.load(),b.querySelector("input")?.focus()}b.addEventListener("submit",async e=>{e.preventDefault();let a=O=>b.elements[O].value.trim(),o={name:a("name"),contact:a("contact"),need:a("need")};if(!o.name||!o.contact||!o.need){j.textContent=s.fallbackMissing;return}j.textContent="";let l=b.querySelector('button[type="submit"]');if(l.disabled)return;l.disabled=!0;let m={fallbackLead:o,...f?.c&&f?.k?{c:f.c,k:f.k}:{}},_=await Pe(b,m);if(!_.ok&&_.turnstile&&(_=await Pe(b,m)),l.disabled=!1,!_.ok){j.textContent=_.error??s.unavailable;return}b.replaceChildren(i(t,"p",{class:"pmw-chat__fb-done",role:"status"},s.fallbackSent)),st()});let Pt={413:"photoTooLarge",415:"photoUnreadable",409:"photoLimit",429:"tooMany"};async function Rt(e){let a,o;try{a=await x(e),o=await v(a)}catch{H.textContent=s.photoUnreadable;return}let l=w(),m=U(l,"visitor");m.classList.add("is-pending");let _=i(t,"img",{class:"pmw-chat__photo",src:o,alt:s.photoAlt});_.addEventListener("load",()=>C(),{once:!0}),m.insertBefore(_,m.lastChild),I(m,{role:"visitor"},""),C(!0),f||await Re();let O=null;if(f){let he=new URLSearchParams({c:f.c,k:f.k,id:l});try{O=await d(`/api/chat/photo?${he}`,{method:"POST",headers:{"content-type":"image/jpeg"},body:a})}catch{O=null}}if(O?.ok){m.classList.remove("is-pending"),ie.add(l),E||$();return}m.remove(),H.textContent=s[Pt[O?.status]]??s.photoFailed}V?.addEventListener("change",async()=>{let e=[...V.files??[]];if(V.value="",H.textContent="",D||g||e.length===0)return;let a=$t-ie.size-Ee;e.length>a&&(H.textContent=s.photoLimit);let o=e.slice(0,Math.max(0,a));Ee+=o.length;for(let l of o)try{await Rt(l)}finally{Ee--}});function lt(){let e=L.value.trim();if(!e||D||g)return;we.unlock(),L.value="",pt();let a={id:w(),text:e.slice(0,2e3),tries:0,failed:!1};a.li=U(a.id,"visitor"),a.li.classList.add("is-pending"),I(a.li,{role:"visitor"},a.text),q.set(a.id,a),C(!0),f?ce()?Ne(a):$():Re()}S.addEventListener("submit",e=>{e.preventDefault(),lt()}),L.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),lt())});let pt=()=>{L.style.height="auto",L.style.height=L.value?`${Math.min(L.scrollHeight,132)}px`:""};L.addEventListener("input",()=>{pt();let e=Date.now();ce()&&e-Ke>Wt&&(Ke=e,E.send(JSON.stringify({type:"typing"})))}),oe.addEventListener("click",()=>{f=null;try{n.sessionStorage.removeItem(fe)}catch{}try{E?.close(1e3)}catch{}E=null,D=!1,re=0,Ce.clear(),q.clear(),ie.clear(),H.textContent="",K="bot",R=null,X();for(let e of[...k.querySelectorAll("li[data-id], .pmw-chat__relay, .pmw-chat__handback")])e.remove();B(),W(),ne.hidden=!0,S.hidden=!1,L.focus()});let qt=()=>[...y.querySelectorAll('a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])')].filter(e=>!e.disabled&&!e.closest("[hidden]"));y.addEventListener("keydown",e=>{if(e.key==="Escape"){e.preventDefault(),qe();return}if(e.key!=="Tab")return;let a=qt();if(a.length===0)return;let o=a[0],l=a[a.length-1];e.shiftKey&&t.activeElement===o?(e.preventDefault(),l.focus()):!e.shiftKey&&t.activeElement===l&&(e.preventDefault(),o.focus())}),te.addEventListener("click",e=>qe({keyboard:e.detail===0}));function It(){g||p.load(),y.hidden=!1,y.inert=!1,r.setAttribute("data-open",""),r.removeAttribute("data-unread"),z=0,F?.remove(),F=null,we.unlock(),f&&$(),(g?te:S.hidden?b.hidden?oe:b.querySelector("input"):L)?.focus(),C(!0,!0)}function qe({keyboard:e=!0}={}){y.hidden=!0,y.inert=!0,r.removeAttribute("data-open"),e&&Z?.focus(),r.dispatchEvent(new n.FocusEvent("focusout",{relatedTarget:e?Z:null}))}return g||Ie(r,()=>p.load(),{win:n,settleMs:jt}),r.pmwChat={open:It,close:qe,panel:y},r.pmwChat}export{bt as BACKOFF_MS,Xt as CHIME_GAP_MS,Ft as GOAL,Gt as IDLE_MS,$t as MAX_PHOTOS,xt as MAX_TRIES,Jt as PHOTO_EDGE,Vt as PHOTO_QUALITY,Kt as PIN_SLACK,fe as SESSION_KEY,Yt as SLOW_START_MS,jt as WARM_MS,aa as demoQuestion,xa as mountChat,ia as retryable,oa as webChime};
2
+ `;var Ht="400px 0px";var ht=new Set,ue=null;function ut(r,{doc:c=document,attrs:n={}}={}){if(ht.has(r)||(ht.add(r),c.querySelector(`script[src="${r}"]`)))return;let t=c.createElement("script");t.src=r,t.async=!0;for(let[h,d]of Object.entries(n))t.setAttribute(h,d);c.head.append(t)}function mt({doc:r=document,hostname:c=globalThis.location?.hostname??""}={}){let n=r.getElementById("pmw-hosts");if(!n)return!1;let t;try{t=JSON.parse(n.textContent??"")}catch{return!1}if(!Array.isArray(t))return!1;let h=String(c).toLowerCase();return t.some(d=>typeof d=="string"&&d.toLowerCase()===h)}function Dt(r){return ue||(ue=new Promise(c=>{let n=null,t=null,h=0,d=r.PerformanceObserver?.supportedEntryTypes??[],m=typeof r.PerformanceObserver=="function"&&d.includes("paint"),p=()=>{n===null||m&&t===null||c(Math.max(n,t??0,h))};if(m)try{new r.PerformanceObserver(x=>{for(let v of x.getEntries())v.name==="first-contentful-paint"&&(t=v.startTime);p()}).observe({type:"paint",buffered:!0}),d.includes("largest-contentful-paint")&&new r.PerformanceObserver(x=>{for(let v of x.getEntries())h=Math.max(h,v.startTime)}).observe({type:"largest-contentful-paint",buffered:!0})}catch{t=0}let w=()=>{n=r.performance.now(),p()};r.document.readyState==="complete"?w():r.addEventListener("load",w,{once:!0})}),ue)}function qe(r,c,{win:n=window,rootMargin:t=Ht,settleMs:h=1500}={}){let d=null,m=!1,p=()=>{m||(m=!0,d?.disconnect(),r.removeEventListener("focusin",p),r.removeEventListener("pointerdown",p),c())};r.addEventListener("focusin",p),r.addEventListener("pointerdown",p);let w=Dt(n),x=async()=>{let v=await w;n.setTimeout(p,Math.max(0,v+h-n.performance.now()))};return typeof n.IntersectionObserver=="function"?(d=new n.IntersectionObserver(v=>{v.some(T=>T.isIntersecting)&&(d.disconnect(),x())},{rootMargin:t}),d.observe(r)):x(),{run:p}}var Ut="https://challenges.cloudflare.com/turnstile/v0/api.js";var ft=15e3;function wt(r=document){ut(Ut,{doc:r})}var _t={unauthorized:4401,expired:4410,rpc:4426,deleted:4404};var Bt={fr:{assistant:"Assistant virtuel",openLabel:"Assistant virtuel, ouvrir le clavardage",dismissTeaser:"Masquer ce message",close:"Fermer le clavardage",panelTitle:"Clavardage",automated:"R\xE9ponses automatiques, tir\xE9es du contenu de ce site.",privacy:"Confidentialit\xE9",you:"Vous",messageLabel:"Votre message",send:"Envoyer",live:"Vous r\xE9pond en direct",relay:"{name} a pris le relais",relayGeneric:"Une personne de l\u2019\xE9quipe a pris le relais",handback:"L\u2019assistant virtuel a repris la conversation",oneMoment:"Un instant\u2026",reconnecting:"Connexion perdue, reconnexion\u2026",notSent:"Non envoy\xE9.",retry:"R\xE9essayer",ended:"Cette conversation est termin\xE9e.",newConversation:"Nouvelle conversation",reload:"Rechargez la page pour continuer.",unavailable:"Un probl\xE8me est survenu. R\xE9essayez dans un moment.",turnstile:"L\u2019envoi n\u2019a pas abouti. R\xE9essayez.",tooMany:"Un instant, puis r\xE9essayez.",fallbackTitle:"Laissez-nous vos coordonn\xE9es",fallbackCap:"Laissez vos coordonn\xE9es et nous vous recontacterons.",fallbackDown:"Laissez vos coordonn\xE9es et nous vous recontacterons.",name:"Votre nom",contact:"T\xE9l\xE9phone ou courriel",need:"Votre demande (facultatif)",fallbackSend:"Envoyer la demande",fallbackSent:"Merci, votre demande a \xE9t\xE9 transmise.",fallbackMissing:"Indiquez votre nom et un t\xE9l\xE9phone ou un courriel.",said:"{name}\xA0: {text}",unreadOne:"{n} nouveau message",unreadMany:"{n} nouveaux messages",attachPhoto:"Joindre une photo",photoHint:"\xC9vitez les photos de personnes ou de pi\xE8ces d\u2019identit\xE9.",photoAlt:"Votre photo",photoSent:"Photo envoy\xE9e",photoLimit:"Trois photos au plus par conversation.",photoTooLarge:"Cette photo est trop lourde.",photoUnreadable:"Ce fichier n\u2019est pas une photo lisible.",photoFailed:"La photo n\u2019a pas pu \xEAtre envoy\xE9e. R\xE9essayez.",demoAsk:"Pouvez-vous m\u2019en dire plus sur {topic}?",demoQuestion:"Quels sont vos d\xE9lais?",demoAnswer:"(D\xE9monstration) Ici, l\u2019assistant r\xE9pond \xE0 partir du contenu de votre site.",demoHint:"Aper\xE7u\xA0: la conversation est d\xE9sactiv\xE9e."},en:{assistant:"Virtual assistant",openLabel:"Virtual assistant, open the chat",dismissTeaser:"Hide this message",close:"Close the chat",panelTitle:"Chat",automated:"Automatic answers, drawn from this site\u2019s content.",privacy:"Privacy",you:"You",messageLabel:"Your message",send:"Send",live:"Replying live",relay:"{name} took over",relayGeneric:"A team member took over",handback:"The virtual assistant is back in the conversation",oneMoment:"One moment\u2026",reconnecting:"Connection lost, reconnecting\u2026",notSent:"Not sent.",retry:"Retry",ended:"This conversation has ended.",newConversation:"New conversation",reload:"Reload the page to continue.",unavailable:"Something went wrong. Try again in a moment.",turnstile:"Sending did not go through. Try again.",tooMany:"One moment, then try again.",fallbackTitle:"Leave us your details",fallbackCap:"Leave your details and we will get back to you.",fallbackDown:"Leave your details and we will get back to you.",name:"Your name",contact:"Phone or email",need:"Your request (optional)",fallbackSend:"Send the request",fallbackSent:"Thank you, your request was sent.",fallbackMissing:"Enter your name and a phone number or an email.",said:"{name}: {text}",unreadOne:"{n} new message",unreadMany:"{n} new messages",attachPhoto:"Attach a photo",photoHint:"Avoid photos of people or of identity documents.",photoAlt:"Your photo",photoSent:"Photo sent",photoLimit:"Three photos at most per conversation.",photoTooLarge:"This photo is too large.",photoUnreadable:"This file is not a readable photo.",photoFailed:"The photo could not be sent. Try again.",demoAsk:"Can you tell me more about {topic}?",demoQuestion:"What are your lead times?",demoAnswer:"(Demo) Here, the assistant answers from your site\u2019s content.",demoHint:"Preview: the conversation is turned off."}},me=r=>String(r??"").slice(0,2).toLowerCase()==="en"?"en":"fr";function ze(r,c={}){let n=Bt[me(r)],t={...n};for(let[h,d]of Object.entries(c??{}))typeof d=="string"&&h in n&&(t[h]=d);return t}var J=(r,c)=>r.replace(/\{(\w+)\}/g,(n,t)=>t in c?String(c[t]):n);var fe="pmw-chat",bt=[1e3,2e3,4e3,8e3,16e3,3e4],xt=3,$t="chat-lead",Ft=3,Jt=1600,Vt=.85,jt=3e3,Gt=7200*1e3,Yt=8e3,Kt=40,Xt=2e3,Wt=3e3,vt=8e3,Qt='<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="3"/><circle cx="9" cy="10" r="1.6"/><path d="m21 16-5-5-8 8"/></svg>';async function Zt(r,c){let n=await r.createImageBitmap(c,{imageOrientation:"from-image"}),t=Math.min(1,Jt/Math.max(n.width,n.height)),h=Math.max(1,Math.round(n.width*t)),d=Math.max(1,Math.round(n.height*t)),m=r.document.createElement("canvas");return m.width=h,m.height=d,m.getContext("2d").drawImage(n,0,0,h,d),n.close?.(),new Promise((p,w)=>m.toBlob(x=>x?p(x):w(new Error("encode")),"image/jpeg",Vt))}var ea={fr:{nos:"vos",notre:"votre",nous:"vous"},en:{our:"your",us:"you",we:"you"}},ta={fr:/^une question sur (.+?)\s*\?$/i,en:/^(?:a |any )?questions? (?:about|on) (.+?)\s*\?$/i};function aa(r,c){let n=me(c),t=ze(n),h=String(r??"").trim().match(ta[n]);if(!h)return t.demoQuestion;let d=h[1].replace(/[\p{L}’']+/gu,m=>ea[n][m.toLowerCase()]??m);return J(t.demoAsk,{topic:d})}var na='<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/></svg>';function oa(r){let c=null;return{unlock(){try{let n=r.AudioContext||r.webkitAudioContext;if(!n)return;c??(c=new n),c.state==="suspended"&&c.resume()}catch{c=null}},play(){if(c)try{c.state==="suspended"&&c.resume();let n=c.currentTime;for(let[t,h]of[[880,0],[1320,.12]]){let d=c.createOscillator(),m=c.createGain();d.type="sine",d.frequency.value=t,m.gain.setValueAtTime(1e-4,n+h),m.gain.exponentialRampToValueAtTime(.15,n+h+.02),m.gain.exponentialRampToValueAtTime(1e-4,n+h+.35),d.connect(m).connect(c.destination),d.start(n+h),d.stop(n+h+.4)}}catch{}}}}function i(r,c,n={},t){let h=r.createElement(c);for(let[d,m]of Object.entries(n))m!=null&&m!==!1&&h.setAttribute(d,m===!0?"":m);return t!==void 0&&(h.textContent=t),h}var ra=/^(?:300\d{3}|600\d{3}|110600|110620)$/,ia=r=>ra.test(String(r??"")),yt=200,sa=(r,c)=>{let n=!1,t=p=>{let w=r.turnstile;if(!(p.el.hasAttribute("data-pmw-rendered")||!p.shown())){p.el.setAttribute("data-pmw-rendered","");try{p.id=w.render(p.el,p.params)}catch{}}},h=()=>{if(!r.turnstile?.render)return!1;for(let p of c)t(p);return!0},d=()=>{if(wt(r.document),n||h())return;n=!0;let p=0,w=()=>{!h()&&++p<600&&r.setTimeout(w,100)};r.setTimeout(w,100)},m=p=>c.find(w=>w.form===p);for(let p of c)p.params["error-callback"]=w=>(p.ask(!1),ia(w)&&!p.retried&&p.id!==void 0?(p.retried=!0,r.setTimeout(()=>{try{r.turnstile?.reset(p.id)}catch{p.failed=!0}},0)):p.failed=!0,!0);return{load:d,async token(p){d();let w=m(p),x=0;for(;;){if(w?.failed)return null;let v=p.querySelector('input[name="cf-turnstile-response"]')?.value??"";if(v)return v;if(!w?.asking()){if(x>=ft)return"";x+=yt}await new Promise(T=>r.setTimeout(T,yt))}},reset(p){let w=m(p);!w||w.id===void 0||w.asking()||(w.failed=!1,w.retried=!1,r.turnstile?.reset(w.id))}}};function xa(r,c={}){if(r.pmwChat)return r.pmwChat;let n=c.win??window,t=n.document,h=c.WebSocket??n.WebSocket,d=c.fetch??((...e)=>n.fetch(...e)),m=[],p=c.turnstile??null,w=c.uuid??(()=>n.crypto.randomUUID()),x=c.shrink??(e=>Zt(n,e)),v=c.preview??(e=>new Promise((a,o)=>{let l=new n.FileReader;l.onload=()=>a(String(l.result)),l.onerror=()=>o(l.error),l.readAsDataURL(e)})),T={};try{T=JSON.parse(r.querySelector("[data-pmw-chat-config]")?.textContent??"{}")}catch{}let Q=me(T.lang),s=ze(Q,T.strings),He=r.dataset.pmwChatOpener??"",Z=r.querySelector(".pmw-chat__bubble"),we=c.chime??oa(n),kt=c.now??(()=>Date.now()),g=c.demo??(T.demo===!0||!mt({doc:t,hostname:n.location.hostname}));t.getElementById("pmw-chat-style")||t.head.append(i(t,"style",{id:"pmw-chat-style"},dt));let y=i(t,"div",{class:"pmw-chat__panel",role:"dialog","aria-modal":"true","aria-labelledby":"pmw-chat-name",hidden:!0});y.inert=!0;let De=i(t,"div",{class:"pmw-chat__head"}),_e=r.querySelector(".pmw-chat__avatar")?.cloneNode(!0)??i(t,"span",{class:"pmw-chat__avatar","aria-hidden":"true"});for(let e of _e.querySelectorAll("[id]")){let a=e.id;e.id=`${a}-head`;for(let o of _e.querySelectorAll(`[mask="url(#${a})"]`))o.setAttribute("mask",`url(#${a}-head)`)}let Ue=i(t,"div",{class:"pmw-chat__who"}),Be=i(t,"p",{class:"pmw-chat__name",id:"pmw-chat-name"}),ee=i(t,"p",{class:"pmw-chat__role"}),$e=i(t,"span",{class:"pmw-chat__face"}),Fe=i(t,"span",{class:"pmw-chat__dot","aria-hidden":"true",hidden:!0}),te=i(t,"button",{type:"button",class:"pmw-chat__close","aria-label":s.close});te.innerHTML=na;let ae=i(t,"p",{class:"pmw-chat__notice"},s.automated);T.privacyHref&&(ae.append(" "),ae.append(i(t,"a",{href:T.privacyHref},s.privacy))),$e.append(_e,Fe),Ue.append(Be,ee,ae),De.append($e,Ue,te);let k=i(t,"ol",{class:"pmw-chat__log"}),ge=i(t,"li",{class:"pmw-chat__msg pmw-chat__msg--bot pmw-chat__msg--greeting"},He),A=i(t,"li",{class:"pmw-chat__typing","aria-hidden":"true",hidden:!0});A.append(i(t,"span"),i(t,"span"),i(t,"span")),k.append(ge);let be=i(t,"p",{class:"pmw-chat__state",role:"status"}),Je=i(t,"p",{class:"pmw-chat__sr","aria-live":"polite"}),Ve=(e,a)=>{let o=i(t,"div",{class:"pmw-chat__check"}),l=i(t,"div",{class:"pmw-chat__check-in"});l.inert=!0;let u=he=>{o.toggleAttribute("data-ask",he),l.inert=!he,C()},_=i(t,"div",{class:"pmw-chat__turnstile"}),O={el:_,form:e,shown:a,ask:u,asking:()=>o.hasAttribute("data-ask"),params:{sitekey:T.siteKey??"",action:"chat",theme:"light",language:Q,appearance:"interaction-only",size:"flexible","refresh-expired":"auto",callback:()=>{O.retried=!1,O.failed=!1,u(!1)},"before-interactive-callback":()=>u(!0)}};return m.push(O),l.append(_),o.append(l),o},S=i(t,"form",{class:"pmw-chat__composer",novalidate:!0}),L=i(t,"textarea",{id:"pmw-chat-input",name:"message",rows:"1",maxlength:String(2e3),placeholder:s.messageLabel,autocomplete:"off",disabled:g,"aria-describedby":g?"pmw-chat-demo-hint":null}),St=i(t,"button",{type:"submit",class:"pmw-chat__send",disabled:g},s.send),xe=i(t,"div",{class:"pmw-chat__box"});xe.append(i(t,"label",{class:"pmw-chat__sr",for:"pmw-chat-input"},s.messageLabel),L,St),g||S.append(Ve(S,()=>!0)),S.append(xe),g&&S.append(i(t,"p",{class:"pmw-chat__hint",id:"pmw-chat-demo-hint"},s.demoHint));let V=null,H=i(t,"p",{class:"pmw-chat__error pmw-chat__photo-error",role:"alert"});if(T.photos===!0){let e=i(t,"label",{class:"pmw-chat__attach"});V=i(t,"input",{type:"file",class:"pmw-chat__sr",accept:"image/*",multiple:!0,"aria-describedby":"pmw-chat-photo-hint",disabled:g}),e.innerHTML=Qt,e.prepend(V),e.append(i(t,"span",{class:"pmw-chat__sr"},s.attachPhoto)),xe.insertBefore(e,L),S.append(i(t,"p",{class:"pmw-chat__hint",id:"pmw-chat-photo-hint"},s.photoHint),H)}let ne=i(t,"div",{class:"pmw-chat__ended",hidden:!0}),je=i(t,"p",{},s.ended),oe=i(t,"button",{type:"button",class:"pmw-chat__restart"},s.newConversation);ne.append(je,oe);let b=i(t,"form",{class:"pmw-chat__fallback",novalidate:!0,hidden:!0,"aria-labelledby":"pmw-chat-fb-title"}),Ge=i(t,"p",{class:"pmw-chat__fb-intro"}),ve=(e,a,o,l=!0)=>{let u=`pmw-chat-fb-${e}`,_=i(t,"div",{class:"pmw-chat__field"});return _.append(i(t,"label",{for:u},s[e]),i(t,a,{id:u,name:e,required:l,...o})),_},j=i(t,"p",{class:"pmw-chat__error",role:"alert"});b.append(i(t,"p",{class:"pmw-chat__fb-title",id:"pmw-chat-fb-title"},s.fallbackTitle),Ge,ve("name","input",{autocomplete:"name",maxlength:"100"}),ve("contact","input",{autocomplete:"on",maxlength:"200"}),ve("need","textarea",{rows:"3",maxlength:"2000"},!1),j,i(t,"button",{type:"submit",class:"pmw-chat__send"},s.fallbackSend)),g||b.insertBefore(Ve(b,()=>!b.hidden),j);let M=i(t,"div",{class:"pmw-chat__body"});M.append(k,be,ne,b),y.append(De,M,S,Je),r.append(y),p??(p=sa(n,m));let G=!0,Ye=0,ye=0,Tt=()=>M.scrollHeight-M.scrollTop-M.clientHeight;M.addEventListener("scroll",()=>{let e=M.scrollTop;Tt()<=Kt?G=!0:e<Ye&&(G=!1),Ye=e},{passive:!0});let Lt=e=>n.requestAnimationFrame?n.requestAnimationFrame(e):n.setTimeout(e,16);function C(e=!1,a=!1){e&&(G=!0),!(!G||ye)&&(ye=Lt(()=>{if(ye=0,!G)return;let o=a||!!n.matchMedia?.("(prefers-reduced-motion: reduce)").matches;M.scrollTo({top:M.scrollHeight,behavior:o?"instant":"smooth"})}))}if(typeof n.ResizeObserver=="function"){let e=new n.ResizeObserver(()=>C());e.observe(k),e.observe(S)}let f=null;if(!g)try{f=JSON.parse(n.sessionStorage.getItem(fe)??"null")}catch{f=null}if(f&&typeof f.at=="number"&&!(Date.now()-f.at<Gt)){f=null;try{n.sessionStorage.removeItem(fe)}catch{}}let ke=()=>{try{n.sessionStorage.setItem(fe,JSON.stringify(f))}catch{}},Ct=()=>{f&&(f.at=Date.now(),ke())},E=null,Se=0,Te=null,Y=null,re=0,K="bot",I=null,D=!1,Ke=0,Le=null,Ce=new Set,R=new Map,ie=new Set,Ee=0,Xe=()=>new URLSearchParams(n.location.hash.slice(1)).get("pmw-chat-botName")?.trim()||null,We=g?Xe():null,Ae=()=>g?We:f?.botName||null;function X(){let e=K==="human";Be.textContent=e?I||s.assistant:Ae()||s.assistant,ee.textContent=e?s.live:Ae()?s.assistant:"",ee.hidden=ee.textContent==="",ae.hidden=e,Fe.hidden=!e,y.dataset.mode=K}X();let N=e=>{be.textContent=e??"",be.hidden=!e};N("");let Qe=e=>e.role==="visitor"?s.you:e.role==="owner"?e.by||I||s.assistant:Ae()||s.assistant;function U(e,a){let o=i(t,"li",{class:`pmw-chat__msg pmw-chat__msg--${a}`,"data-id":e}),l=i(t,"span",{class:"pmw-chat__sr"}),u=i(t,"span",{class:"pmw-chat__text"});return o.append(l,u),k.insertBefore(o,A.parentNode===k?A:null),o}let Ze=()=>A.parentNode===k?A.previousElementSibling:k.lastElementChild,P=(e,a)=>e.classList.contains(a);function et(e,a,o,l){let u=i(t,"li",{class:e,role:"separator","aria-label":o}),_=i(t,"span",{class:"pmw-chat__relay-label","aria-hidden":"true"});_.append(i(t,"span",{class:a}),o),u.append(_),k.insertBefore(u,l??(A.parentNode===k?A:null)),C()}function tt(e,a){for(let o=e?e.previousElementSibling:Ze();o;o=o.previousElementSibling){if(P(o,"pmw-chat__relay")||P(o,"pmw-chat__msg--owner"))return;if(P(o,"pmw-chat__msg--bot")||P(o,"pmw-chat__handback"))break}et("pmw-chat__relay","pmw-chat__relay-dot",a?J(s.relay,{name:a}):s.relayGeneric,e)}function Oe(e){for(let a=e?e.previousElementSibling:Ze();;a=a.previousElementSibling){if(!a||P(a,"pmw-chat__handback")||P(a,"pmw-chat__msg--bot"))return;if(P(a,"pmw-chat__relay")||P(a,"pmw-chat__msg--owner"))break}et("pmw-chat__handback","pmw-chat__relay-dot pmw-chat__relay-dot--back",s.handback,e)}let at=e=>[...k.querySelectorAll("li[data-id]")].find(a=>a.dataset.id===e),q=(e,a,o)=>{e.firstChild.textContent=J(s.said,{name:Qe(a),text:""}),e.lastChild.textContent=o===""&&a.photos?.length&&!e.querySelector("img")?s.photoSent:o};function B(){A.hidden=!0,n.clearTimeout(Le)}function Me(e){A.hidden=!1,k.append(A),n.clearTimeout(Le),e&&(Le=n.setTimeout(B,e)),C()}function nt(e,{announce:a}){if(Ce.has(e.seq))return;Ce.add(e.seq),re=Math.max(re,e.seq);let o=at(e.id);o||(o=U(e.id,e.role),e.role==="owner"?tt(o,e.by||I):e.role==="bot"&&Oe(o)),o.classList.remove("is-streaming","is-pending","is-failed"),o.querySelector(".pmw-chat__retry")?.remove(),q(o,e,e.text),e.role==="visitor"?(R.delete(e.id),e.photos?.length&&ie.add(e.id)):(B(),a&&(Je.textContent=J(s.said,{name:Qe(e),text:e.text}),(y.hidden||t.visibilityState==="hidden")&&Et(),y.hidden&&(z++,At()))),C()}let z=0,$=null,ot=-1/0;function Et(){let e=kt();e-ot<Xt||(ot=e,we.play())}function At(){if(!Z)return;$||($=i(t,"span",{class:"pmw-chat__badge"}),Z.append($));let e=z>9?"9+":String(z);$.replaceChildren(i(t,"span",{"aria-hidden":"true"},e),i(t,"span",{class:"pmw-chat__sr"},J(z===1?s.unreadOne:s.unreadMany,{n:String(z)}))),r.setAttribute("data-unread",String(z))}function Ot(e,a){B();let o=at(e);o||(o=U(e,"bot"),Oe(o),o.classList.add("is-streaming"),q(o,{role:"bot"},"")),o.lastChild.textContent+=a,C()}if(g){q(U("demo-question","visitor"),{role:"visitor"},aa(He,Q));let e=U("demo-answer","bot");q(e,{role:"bot"},s.demoAnswer),n.addEventListener("hashchange",()=>{We=Xe(),X(),q(e,{role:"bot"},s.demoAnswer)})}function se(e){if(e.failed=!0,e.li.classList.remove("is-pending"),e.li.classList.add("is-failed"),e.li.querySelector(".pmw-chat__retry"))return;let a=i(t,"span",{class:"pmw-chat__retry"},`${s.notSent} `),o=i(t,"button",{type:"button"},s.retry);o.addEventListener("click",()=>{e.failed=!1,e.tries=0,a.remove(),e.li.classList.replace("is-failed","is-pending"),f?ce()?Ne(e):F():Ie()}),a.append(o),e.li.append(a)}let ce=()=>!!E&&E.readyState===1;function Ne(e){if(!(e.failed||!ce())){if(e.tries>=xt)return se(e);e.tries++,E.send(JSON.stringify({type:"message",id:e.id,text:e.text}))}}function F(){if(!f||D||E)return;n.clearTimeout(Te);let e=n.location.protocol==="https:"?"wss:":"ws:",a=new URLSearchParams({c:f.c,k:f.k}),o=new h(`${e}//${n.location.host}/api/chat/ws?${a}`);E=o,o.onopen=()=>{Se=0,de&&(W(),Me(vt)),N(""),o.send(JSON.stringify({type:"hello",since:re}));for(let l of R.values())Ne(l)},o.onmessage=l=>{let u;try{u=JSON.parse(l.data)}catch{return}Nt(u)},o.onclose=l=>{E===o&&(E=null);for(let _ of R.values())_.tries>=xt&&se(_);if(l.code>=4e3&&l.code<5e3)return Mt(l.code);if(D)return;W(),N(s.reconnecting);let u=bt[Math.min(Se,bt.length-1)];Se++,Te=n.setTimeout(F,u)}}function Mt(e){if(e===_t.rpc)return le(s.reload,!1);le(s.ended,!0)}function le(e,a){D=!0,n.clearTimeout(Te),N(""),je.textContent=e,oe.hidden=!a,ne.hidden=!1,S.hidden=!0;for(let o of R.values())se(o);C()}function rt(e){K=e.mode==="human"?"human":"bot",I=typeof e.ownerName=="string"?e.ownerName:null,X(),K==="human"?tt(null,I):Oe(null),e.closed&&le(s.ended,!0)}function Nt(e){switch(e?.type){case"history":typeof e.ownerName=="string"&&(I=e.ownerName);for(let a of e.messages??[])nt(a,{announce:!1});return rt(e);case"message":return Ct(),nt(e.msg,{announce:e.msg?.role!=="visitor"});case"delta":return Ot(e.id,e.text);case"typing":return e.role==="visitor"?void 0:Me(vt);case"status":return rt(e);case"fallback":return ct(e.reason==="cap"?s.fallbackCap:s.fallbackDown);case"lead":return st();case"error":(e.code==="closed"||e.code==="deleted")&&le(s.ended,!0)}}let it=!1;function st(){if(!(it||f?.lead)){it=!0,f&&(f.lead=!0,ke());try{n.pmw?.event($t)}catch{}}}let pe=new Set;async function Pe(e,a){let o="";if(pe.has(e)){pe.delete(e);try{p.reset(e)}catch{}}try{o=await p.token(e)}catch{o=""}if(o===null)return pe.add(e),{ok:!1,error:s.turnstile,cap:!1,turnstile:!0};let l,u=null;try{l=await d("/api/chat/start",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:o,lang:Q,page:n.location.pathname,...a})}),u=await l.json()}catch{l=null}return pe.add(e),l?.ok&&u?{ok:!0,data:u}:{ok:!1,error:l?.status===403?s.turnstile:l?.status===429?s.tooMany:null,cap:l?.status===409&&u?.error==="cap",turnstile:l?.status===403}}let de=null,W=()=>{n.clearTimeout(de),de=null};function Ie(){if(Y||f)return Y;N(""),Me(),de=n.setTimeout(()=>N(s.oneMoment),Yt);let e=k.firstElementChild===ge?ge.textContent.trim():"";return Y=Pe(S,e?{opener:e}:{}).then(a=>{if(Y=null,a.ok&&a.data.conversationId&&a.data.visitorKey){f={c:a.data.conversationId,k:a.data.visitorKey,botName:typeof a.data.botName=="string"?a.data.botName:null,at:Date.now()},ke(),X(),F();return}W(),B();for(let o of R.values())se(o);if(a.error)return N(a.error);N(""),ct(a.cap?s.fallbackCap:s.fallbackDown)}),Y}function ct(e){B(),W(),N(""),Ge.textContent=e,b.hidden=!1,S.hidden=!0,g||p.load(),b.querySelector("input")?.focus()}b.addEventListener("submit",async e=>{e.preventDefault();let a=O=>b.elements[O].value.trim(),o={name:a("name"),contact:a("contact"),...a("need")?{need:a("need")}:{}};if(!o.name||!o.contact){j.textContent=s.fallbackMissing;return}j.textContent="";let l=b.querySelector('button[type="submit"]');if(l.disabled)return;l.disabled=!0;let u={fallbackLead:o,...f?.c&&f?.k?{c:f.c,k:f.k}:{}},_=await Pe(b,u);if(!_.ok&&_.turnstile&&(_=await Pe(b,u)),l.disabled=!1,!_.ok){j.textContent=_.error??s.unavailable;return}b.replaceChildren(i(t,"p",{class:"pmw-chat__fb-done",role:"status"},s.fallbackSent)),st()});let Pt={413:"photoTooLarge",415:"photoUnreadable",409:"photoLimit",429:"tooMany"};async function It(e){let a,o;try{a=await x(e),o=await v(a)}catch{H.textContent=s.photoUnreadable;return}let l=w(),u=U(l,"visitor");u.classList.add("is-pending");let _=i(t,"img",{class:"pmw-chat__photo",src:o,alt:s.photoAlt});_.addEventListener("load",()=>C(),{once:!0}),u.insertBefore(_,u.lastChild),q(u,{role:"visitor"},""),C(!0),f||await Ie();let O=null;if(f){let he=new URLSearchParams({c:f.c,k:f.k,id:l});try{O=await d(`/api/chat/photo?${he}`,{method:"POST",headers:{"content-type":"image/jpeg"},body:a})}catch{O=null}}if(O?.ok){u.classList.remove("is-pending"),ie.add(l),E||F();return}u.remove(),H.textContent=s[Pt[O?.status]]??s.photoFailed}V?.addEventListener("change",async()=>{let e=[...V.files??[]];if(V.value="",H.textContent="",D||g||e.length===0)return;let a=Ft-ie.size-Ee;e.length>a&&(H.textContent=s.photoLimit);let o=e.slice(0,Math.max(0,a));Ee+=o.length;for(let l of o)try{await It(l)}finally{Ee--}});function lt(){let e=L.value.trim();if(!e||D||g)return;we.unlock(),L.value="",pt();let a={id:w(),text:e.slice(0,2e3),tries:0,failed:!1};a.li=U(a.id,"visitor"),a.li.classList.add("is-pending"),q(a.li,{role:"visitor"},a.text),R.set(a.id,a),C(!0),f?ce()?Ne(a):F():Ie()}S.addEventListener("submit",e=>{e.preventDefault(),lt()}),L.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),lt())});let pt=()=>{L.style.height="auto",L.style.height=L.value?`${Math.min(L.scrollHeight,132)}px`:""};L.addEventListener("input",()=>{pt();let e=Date.now();ce()&&e-Ke>Wt&&(Ke=e,E.send(JSON.stringify({type:"typing"})))}),oe.addEventListener("click",()=>{f=null;try{n.sessionStorage.removeItem(fe)}catch{}try{E?.close(1e3)}catch{}E=null,D=!1,re=0,Ce.clear(),R.clear(),ie.clear(),H.textContent="",K="bot",I=null,X();for(let e of[...k.querySelectorAll("li[data-id], .pmw-chat__relay, .pmw-chat__handback")])e.remove();B(),W(),ne.hidden=!0,S.hidden=!1,L.focus()});let Rt=()=>[...y.querySelectorAll('a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])')].filter(e=>!e.disabled&&!e.closest("[hidden]"));y.addEventListener("keydown",e=>{if(e.key==="Escape"){e.preventDefault(),Re();return}if(e.key!=="Tab")return;let a=Rt();if(a.length===0)return;let o=a[0],l=a[a.length-1];e.shiftKey&&t.activeElement===o?(e.preventDefault(),l.focus()):!e.shiftKey&&t.activeElement===l&&(e.preventDefault(),o.focus())}),te.addEventListener("click",e=>Re({keyboard:e.detail===0}));function qt(){g||p.load(),y.hidden=!1,y.inert=!1,r.setAttribute("data-open",""),r.removeAttribute("data-unread"),z=0,$?.remove(),$=null,we.unlock(),f&&F(),(g?te:S.hidden?b.hidden?oe:b.querySelector("input"):L)?.focus(),C(!0,!0)}function Re({keyboard:e=!0}={}){y.hidden=!0,y.inert=!0,r.removeAttribute("data-open"),e&&Z?.focus(),r.dispatchEvent(new n.FocusEvent("focusout",{relatedTarget:e?Z:null}))}return g||qe(r,()=>p.load(),{win:n,settleMs:jt}),r.pmwChat={open:qt,close:Re,panel:y},r.pmwChat}export{bt as BACKOFF_MS,Xt as CHIME_GAP_MS,$t as GOAL,Gt as IDLE_MS,Ft as MAX_PHOTOS,xt as MAX_TRIES,Jt as PHOTO_EDGE,Vt as PHOTO_QUALITY,Kt as PIN_SLACK,fe as SESSION_KEY,Yt as SLOW_START_MS,jt as WARM_MS,aa as demoQuestion,xa as mountChat,ia as retryable,oa as webChime};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmeweb/plugin-chat",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "PmeWeb chat plugin: the bubble, the lazy chat panel, the site Worker's /api/chat routes and the build-time context file. The conversations, the model and the alerts live in the pmeweb-chat service.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/strings.js CHANGED
@@ -39,10 +39,10 @@ export const STRINGS = {
39
39
  fallbackDown: 'Laissez vos coordonnées et nous vous recontacterons.',
40
40
  name: 'Votre nom',
41
41
  contact: 'Téléphone ou courriel',
42
- need: 'Votre demande',
42
+ need: 'Votre demande (facultatif)',
43
43
  fallbackSend: 'Envoyer la demande',
44
44
  fallbackSent: 'Merci, votre demande a été transmise.',
45
- fallbackMissing: 'Remplissez les trois champs.',
45
+ fallbackMissing: 'Indiquez votre nom et un téléphone ou un courriel.',
46
46
  said: `{name}${NBSP}: {text}`,
47
47
  unreadOne: '{n} nouveau message',
48
48
  unreadMany: '{n} nouveaux messages',
@@ -89,10 +89,10 @@ export const STRINGS = {
89
89
  fallbackDown: 'Leave your details and we will get back to you.',
90
90
  name: 'Your name',
91
91
  contact: 'Phone or email',
92
- need: 'Your request',
92
+ need: 'Your request (optional)',
93
93
  fallbackSend: 'Send the request',
94
94
  fallbackSent: 'Thank you, your request was sent.',
95
- fallbackMissing: 'Fill in all three fields.',
95
+ fallbackMissing: 'Enter your name and a phone number or an email.',
96
96
  said: '{name}: {text}',
97
97
  unreadOne: '{n} new message',
98
98
  unreadMany: '{n} new messages',
package/src/trigger.js CHANGED
@@ -14,7 +14,7 @@
14
14
  // (`pmw-chat-botName`) is the widget's to read. No opener is scheduled there: it would cover the
15
15
  // preview the portal is showing.
16
16
  //
17
- // The opener shows once per visit (sessionStorage), on the first of: 20 s on the page, 30 % of the
17
+ // The opener shows on up to 3 pages of a visit (sessionStorage), once per page, on the first of: 20 s on the page, 30 % of the
18
18
  // page scrolled, or exit intent (a fine pointer leaving through the top of the window). Never while
19
19
  // the panel is open, never again once dismissed or once the panel has been opened, and never while
20
20
  // the footer is in view (it waits for the next trigger then). On a phone it fades out after 8 s:
@@ -30,6 +30,7 @@ import { INTERACTION_EVENTS, isCanonical, onInteraction as interact } from '@pme
30
30
  export const OPENER_KEY = 'pmw-chat-opener'
31
31
  export const OPENER_DELAY_MS = 20000
32
32
  export const OPENER_SCROLL = 0.3
33
+ export const OPENER_MAX = 3
33
34
  /** The page-wide signs of a person: the loader's own, plus a pointer moving and a scroll. */
34
35
  export const PAGE_EVENTS = [...INTERACTION_EVENTS, 'pointermove', 'scroll']
35
36
 
@@ -68,19 +69,23 @@ export function startChat({
68
69
  root.addEventListener('focusout', (e) => sync(e.relatedTarget))
69
70
  }
70
71
 
72
+ // The opener shows on up to OPENER_MAX pages of a visit (once per page), never again once its x
73
+ // or the panel was used (operator, 2026-11-14): the stored value is the count shown, or 9 for
74
+ // « never again » (one comparison covers both: the inline budget is counted in bytes).
71
75
  const store = win.sessionStorage
72
- let done = false
73
- try { done = !!store?.getItem(OPENER_KEY) } catch { /* storage blocked: show once per page */ }
76
+ let n = 0
77
+ try { n = store?.getItem(OPENER_KEY) } catch { /* storage blocked: show once per page */ }
78
+ let done = n >= OPENER_MAX
74
79
  // The scroll and exit listeners stay after the opener is done: they return at once (no layout read).
75
80
  const on = (t, ev, f) => t.addEventListener(ev, (e) => done || f(e), { passive: true })
76
81
  // A timer that fires once the opener is done finds `done` and returns.
77
- const finish = () => {
82
+ const finish = (v) => {
78
83
  done = true
79
- try { store?.setItem(OPENER_KEY, '1') } catch { /* nothing to remember with */ }
84
+ try { store?.setItem(OPENER_KEY, v) } catch { /* nothing to remember with */ }
80
85
  }
81
86
  const show = () => {
82
87
  if (done || away || !teaser || isOpen()) return
83
- finish()
88
+ finish(+n + 1)
84
89
  teaser.hidden = false
85
90
  warm()
86
91
  }
@@ -88,7 +93,7 @@ export function startChat({
88
93
  const t = e.target.closest?.('[data-pmw-chat-open],[data-pmw-chat-dismiss]')
89
94
  if (!t) return
90
95
  if (teaser) teaser.hidden = true
91
- finish()
96
+ finish(9)
92
97
  if (t.hasAttribute('data-pmw-chat-open')) open()
93
98
  })
94
99
  if (!done && teaser && canonical) {
package/src/widget.js CHANGED
@@ -365,10 +365,11 @@ export function mountChat(root, deps = {}) {
365
365
 
366
366
  const fallback = el(doc, 'form', { class: 'pmw-chat__fallback', novalidate: true, hidden: true, 'aria-labelledby': 'pmw-chat-fb-title' })
367
367
  const fbIntro = el(doc, 'p', { class: 'pmw-chat__fb-intro' })
368
- const field = (key, tag, attrs) => {
368
+ // The request is optional (1.3.5): a name and a way to reach the visitor are enough.
369
+ const field = (key, tag, attrs, required = true) => {
369
370
  const id = `pmw-chat-fb-${key}`
370
371
  const wrap = el(doc, 'div', { class: 'pmw-chat__field' })
371
- wrap.append(el(doc, 'label', { for: id }, s[key]), el(doc, tag, { id, name: key, required: true, ...attrs }))
372
+ wrap.append(el(doc, 'label', { for: id }, s[key]), el(doc, tag, { id, name: key, required, ...attrs }))
372
373
  return wrap
373
374
  }
374
375
  const fbError = el(doc, 'p', { class: 'pmw-chat__error', role: 'alert' })
@@ -376,7 +377,7 @@ export function mountChat(root, deps = {}) {
376
377
  el(doc, 'p', { class: 'pmw-chat__fb-title', id: 'pmw-chat-fb-title' }, s.fallbackTitle), fbIntro,
377
378
  field('name', 'input', { autocomplete: 'name', maxlength: '100' }),
378
379
  field('contact', 'input', { autocomplete: 'on', maxlength: '200' }),
379
- field('need', 'textarea', { rows: '3', maxlength: '2000' }),
380
+ field('need', 'textarea', { rows: '3', maxlength: '2000' }, false),
380
381
  fbError, el(doc, 'button', { type: 'submit', class: 'pmw-chat__send' }, s.fallbackSend),
381
382
  )
382
383
  if (!demo) fallback.insertBefore(widget(fallback, () => !fallback.hidden), fbError)
@@ -802,8 +803,8 @@ export function mountChat(root, deps = {}) {
802
803
  fallback.addEventListener('submit', async (e) => {
803
804
  e.preventDefault()
804
805
  const v = (k) => fallback.elements[k].value.trim()
805
- const fields = { name: v('name'), contact: v('contact'), need: v('need') }
806
- if (!fields.name || !fields.contact || !fields.need) {
806
+ const fields = { name: v('name'), contact: v('contact'), ...(v('need') ? { need: v('need') } : {}) }
807
+ if (!fields.name || !fields.contact) {
807
808
  fbError.textContent = s.fallbackMissing
808
809
  return
809
810
  }
package/src/worker.js CHANGED
@@ -32,7 +32,7 @@ import { verifyTurnstile } from '@pmeweb/turnstile/worker'
32
32
  import { RPC_HEADER, RPC_VERSION } from './protocol.js'
33
33
 
34
34
  /** This package's version, reported by the health route (a test holds it equal to package.json). */
35
- export const VERSION = '1.3.4'
35
+ export const VERSION = '1.3.5'
36
36
  export { RPC_VERSION }
37
37
 
38
38
  const SERVICE = 'https://pmeweb-chat.internal'
@@ -72,13 +72,20 @@ async function allowed(request, env, { start = false } = {}) {
72
72
  }
73
73
  }
74
74
 
75
- /** The fallback form's three fields, trimmed; null when one is missing or too long. */
75
+ /**
76
+ * The fallback form's fields, trimmed; null when the name or the contact is missing, or a field is too
77
+ * long. The request (`need`) is optional since 1.3.5: left out when empty.
78
+ */
76
79
  function fallbackLead(v) {
77
80
  if (typeof v !== 'object' || v === null) return null
78
81
  const out = {}
79
82
  for (const [k, max] of Object.entries(LIMITS)) {
80
83
  const s = typeof v[k] === 'string' ? v[k].trim() : ''
81
- if (s === '' || s.length > max) return null
84
+ if (s.length > max) return null
85
+ if (s === '') {
86
+ if (k === 'need') continue
87
+ return null
88
+ }
82
89
  out[k] = s
83
90
  }
84
91
  return out