discovery-media-player 0.1.28 → 0.1.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discovery-media-player",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
5
5
  "keywords": [
6
6
  "pdf-viewer",
@@ -4,4 +4,4 @@
4
4
  // Injecté par api/doc.js dans un <script nonce=…> de la visionneuse. Le code navigateur du
5
5
  // player vit en modules TypeScript testés sous player/src/ — c'est ici qu'il atterrit.
6
6
  "use strict";
7
- module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var z=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var xe=Object.prototype.hasOwnProperty;var p=(e,n)=>{for(var t in n)z(e,t,{get:n[t],enumerable:!0})},we=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Me(n))!xe.call(e,o)&&o!==t&&z(e,o,{get:()=>n[o],enumerable:!(r=ve(n,o))||r.enumerable});return e};var ke=e=>we(z({},\"__esModule\",{value:!0}),e);var gn={};p(gn,{bridge:()=>I,chat:()=>C,live:()=>A,presentation:()=>L,presentationState:()=>$,tracking:()=>S,viewer:()=>R});var I={};p(I,{onHostMessage:()=>Re,onPlayerMessage:()=>Le,parseHostMessage:()=>de,parsePlayerMessage:()=>ce,sendToHost:()=>Se,sendToPlayer:()=>Ae,toWire:()=>j});var V=\"3dd-doc-\",Te=/^[A-Za-z0-9_-]{1,64}$/,_e=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ee=[\"embed-denied\"],Pe=[\"present-invite\",\"present-handover\",\"present-switch\"],Ie=[\"handover-done\"];function j(e){let n={type:V+e.type};return\"slug\"in e&&e.slug&&(n.slug=e.slug),\"reason\"in e&&e.reason&&(n.reason=String(e.reason).slice(0,40)),n}function ue(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(V))return null;let o=r.slice(V.length);if(n.includes(o))return{type:o};if(Ee.includes(o)){let a=e.reason;return{type:o,reason:/^[a-z-]{1,40}$/.test(String(a||\"\"))?String(a):\"unknown\"}}if(!t.includes(o))return null;let i=e.slug;return typeof i!=\"string\"||!Te.test(i)?null:{type:o,slug:i}}function ce(e){return ue(e,_e,Pe)}function de(e){return ue(e,Ie,[])}function Se(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(j(e),\"*\")}catch(t){}}function Ae(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(j(n),\"*\")}catch(r){}}function Ce(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Le(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=i=>{if(!Ce(t,i.source))return;let a=ce(i.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function Re(e,n){var i;let t=n||(typeof window!=\"undefined\"?window:null);if(!t)return()=>{};let r=(i=t.parent)!=null?i:null,o=a=>{if(!r||r===t||a.source!==r)return;let d=de(a.data);d&&e(d)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var S={};p(S,{createTracker:()=>De});var $e=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function He(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(16));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}return Math.random().toString(36).slice(2)+Date.now().toString(36)}function We(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;o&&typeof o.sendBeacon==\"function\"?o.sendBeacon(e,new Blob([r],{type:\"application/json\"})):n.fetch(e,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:r,keepalive:!0})}catch(r){}}}function De(e={}){var ie,ae;let n=e.win||(typeof window!=\"undefined\"?window:null),t=e.doc||n&&n.document,r=e.now||(()=>Date.now()),o=e.endpoint||\"/api/doc\",i=(ie=e.idleMs)!=null?ie:6e4,a=(ae=e.sessionEveryMs)!=null?ae:12e3,d=e.slug||\"\",f=e.internal||null,H=e.sessionId||He(),ye=e.send||We(o,n),g={},v=0,W=0,M=0,ee=0,c=null,_=r(),x=!1,D=!1,E=[],he=()=>!!d||!!f,ne=s=>{if(he())try{ye(s)}catch(l){}},te=()=>!!t&&t.visibilityState===\"visible\"&&(typeof t.hasFocus==\"function\"?t.hasFocus():!0)&&!x,re=s=>{let l=r()-s,b=Math.max(0,_-s)+i;return Math.max(0,Math.min(l,b))},N=()=>{c!=null&&v>0&&(g[v]=(g[v]||0)+re(c)/1e3),c=te()?r():null},O=()=>{N(),c=null},U=()=>{c==null&&te()&&(c=r())},F=()=>{let s=0;for(let l in g)s+=g[l];return c!=null&&(s+=re(c)/1e3),Math.round(s)},oe=()=>{let s={};for(let l in g)s[l]=Math.round(g[l]);return s},w=()=>{_=r(),x&&(x=!1,U())},se=(s,l)=>{f||ne({slug:d,event:s,page:l||W,maxPage:M,seconds:F(),sessionId:H})},P=()=>{N();let s={event:\"session\",sessionId:H,numPages:ee,maxPage:M,totalSeconds:F(),pagesTime:oe()};f?(s.internal=!0,s.docId=f.docId,s.email=f.email,s.name=f.name):s.slug=d,ne(s)},m=(s,l,b,le)=>{!s||typeof s.addEventListener!=\"function\"||(s.addEventListener(l,b,le),E.push(()=>{s.removeEventListener(l,b,le)}))};return{sessionId:H,totalSeconds:F,maxPage:()=>M,pageTimes:oe,start(){if(D)return;D=!0,se(\"open\",1),v=1,c=r(),_=r(),m(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(O(),P()):(w(),U())}),m(n,\"blur\",O),m(n,\"focus\",()=>{w(),U()});for(let b of $e)m(n,b,w,{passive:!0});m(e.scrollElement,\"scroll\",w,{passive:!0}),m(n,\"beforeunload\",P);let s=n.setInterval(()=>{!x&&r()-_>i&&(x=!0,O())},5e3),l=n.setInterval(P,a);E.push(()=>{n.clearInterval(s),n.clearInterval(l)})},setPage(s){!s||s===W||(N(),v=s,W=s,s>M&&(M=s,se(\"page\",s)))},setPageCount(s){ee=s||0},noteActivity:w,flush:P,stop(){for(;E.length;){let s=E.pop();try{s==null||s()}catch(l){}}D=!1}}}var A={};p(A,{STORAGE_KEYS:()=>k,attendeeKey:()=>Fe,authorToken:()=>Ve,avatarHtml:()=>K,canModerate:()=>Z,escapeHtml:()=>u,flattenPresence:()=>Ne,formatMessageBody:()=>G,initials:()=>pe,isMentioned:()=>je,isMine:()=>y,reactorId:()=>B,shouldNotify:()=>Ke,unreadLabel:()=>Ge});var k={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function u(e){return String(e==null?\"\":e).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")}function pe(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function K(e,n){return e?`<img src=\"${u(e)}\" alt=\"\">`:u(pe(n))}function G(e){let n=u(e);return n=n.replace(/(https?:\\/\\/[^\\s<]+)/g,t=>`<a href=\"${t}\" target=_blank rel=noreferrer class=cm-link>${t}</a>`),n=n.replace(/(^|\\s)@([\\p{L}0-9_'.-]+)/gu,(t,r,o)=>`${r}<span class=cm-mention>@${o}</span>`),n}function Ne(e){let n={},t=[];for(let r in e||{})for(let o of(e||{})[r]||[]){let i=String(o.uid||o.email||o.name||\"\").toLowerCase()||`_${r}_${t.length}`;n[i]?o.role===\"presenter\"&&(n[i]=o):(n[i]=o,t.push(i))}return t.map(r=>n[r])}function Oe(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function Ue(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function Fe(e,n,t=\"anon\",r=()=>Math.random().toString(36).slice(2,10)){if(e!=null&&e.email)return String(e.email).toLowerCase();try{if(!n)return`anon-${t}`;let o=n.getItem(k.attendeeKey);if(o)return o;let i=`anon-${r()}`;return n.setItem(k.attendeeKey,i),i}catch(o){return`anon-${t}`}}function ze(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(24));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}try{console.warn(\"[player] crypto indisponible : jeton d'auteur affaibli\")}catch(n){}return Math.random().toString(36).slice(2)+Math.random().toString(36).slice(2)}function Ve(e,n=ze){let t=Oe(e,k.authorToken);if(t)return t;let r=n();return Ue(e,k.authorToken,r),r}function B(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function y(e,n){return!e||!n?!1:e.author_email&&n.email?e.author_email===n.email:!e.author_email&&!n.email?e.author_name===n.name:!1}function Z(e){return!!(e&&e.role===\"presenter\")}function je(e,n){if(!n||!n.name)return!1;let t=(n.name.split(/\\s+/)[0]||\"\").toLowerCase();return t?String(e&&e.body||\"\").toLowerCase().includes(`@${t}`)&&!y(e,n):!1}function Ke(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||y(n,t)?!1:o}function Ge(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}var C={};p(C,{messageClassName:()=>Ze,renderActions:()=>me,renderAttachment:()=>ge,renderMessage:()=>Be,renderReactions:()=>fe});function fe(e,n){let t=e&&e.reactions||{},r=B(n),o=\"\";for(let i in t){let a=t[i]||[];if(!a.length)continue;let d=a.indexOf(r)>=0;o+=`<button class=\"re-chip${d?\" on\":\"\"}\" data-e=\"${u(i)}\">${u(i)} ${a.length}</button>`}return o}function ge(e){if(!e||!e.url)return\"\";let n=u(e.url);if(e.kind===\"image\")return`<a class=cm-att href=\"${n}\" target=_blank rel=noreferrer><img src=\"${n}\" alt=\"\" loading=lazy></a>`;let t=u(e.name||\"Document.pdf\");return`<a class=cm-att-pdf href=\"${n}\" target=_blank rel=noreferrer data-pdf=\"${n}\"><span class=cm-att-ph></span><span class=cm-pdflabel>${t}</span></a>`}function me(e,n){let t=y(e,n.me),r=`<button class=cm-react title=\"R\\xE9agir\">${n.reactIcon||\"\"}</button><button class=cm-reply title=\"R\\xE9pondre\">\\u21A9</button>`;return t&&(r+='<button class=cm-edit title=\"Modifier\">\\u270E</button>'),(t||Z(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function Be(e,n){if(e.deleted)return'<span class=a></span><span class=b><div class=\"txt cm-del\">Message supprim\\xE9</div></span>';let t=e.reply_to?`<div class=cm-q><b>${u(e.reply_name||\"\")}</b> ${u(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${G(e.body)}${r}</div>`:\"\",i=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${K(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${u(e.author_name||\"Invit\\xE9\")}</b>${i}</div>`+t+o+ge(e.attachment)+`<div class=cm-re>${fe(e,n.me)}</div></span><span class=cm-act>${me(e,n)}</span>`}function Ze(e,n,t){return\"cm\"+(y(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var L={};p(L,{DEFAULT_CENTER:()=>X,DEFAULT_ZOOM:()=>J,MAP_TYPES:()=>T,cycleMapType:()=>Ye,initialMapContent:()=>Xe,mapTypeLabel:()=>qe,sanitizeContent:()=>Q});var T=[\"roadmap\",\"satellite\",\"hybrid\"],X=[46.6,2.5],J=6;function h(e){let n=Number(e);return Number.isFinite(n)?n:null}function Y(e){if(!Array.isArray(e))return null;let n=h(e[0]),t=h(e[1]);return n!=null&&t!=null?[n,t]:null}var q=(e,n,t)=>Math.max(n,Math.min(t,e));function Q(e){if(!e||typeof e!=\"object\")return null;let n=e;if(n.kind===\"pdf\"||n.kind==null)return null;if(n.kind===\"streetview\"){let t=Y(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:h(r.heading)||0,pitch:q(h(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:q(h(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:Y(n.center)||[...X],zoom:q(Math.trunc(h(n.zoom)||J),1,21),marker:Y(n.marker),mapType:T.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function Ye(e){let n=T.indexOf(e);return T[(n+1)%T.length]}function qe(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function Xe(){return{kind:\"map\",center:[...X],zoom:J,marker:null,mapType:null,label:null}}var R={};p(R,{CURRENT_PAGE_MARGIN:()=>rn,DEFAULT_ASPECT:()=>en,MAX_ZOOM:()=>Qe,MIN_PAGE_WIDTH:()=>nn,MIN_ZOOM:()=>Je,PRERENDER_MARGIN:()=>tn,arrowState:()=>an,averageColor:()=>cn,clampPage:()=>be,clampZoom:()=>on,fitWidth:()=>un,isImageDocument:()=>dn,progressPercent:()=>sn});var Je=.5,Qe=3,en=1.35,nn=280,tn=\"500px 0px\",rn=\"-48% 0px -48% 0px\";function on(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function be(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function sn(e,n){return n?Math.round(be(e,n)/n*100):0}function an(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var ln=28;function un(e){var a;let n=(a=e.zoom)!=null?a:1,t=Math.max(280,e.containerWidth||900)*n;if(!e.onePage)return t;let r=e.aspect||1.35,i=((e.containerHeight||600)-ln-(e.overlap||0)-(e.reserve||0))/r;return i>120?Math.min(t,i):t}function cn(e){let n=e.length/4;if(!n)return\"rgb(0,0,0)\";let t=0,r=0,o=0;for(let i=0;i<e.length;i+=4)t+=e[i],r+=e[i+1],o+=e[i+2];return`rgb(${Math.round(t/n)},${Math.round(r/n)},${Math.round(o/n)})`}function dn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var $={};p($,{presentationTransition:()=>pn,switchDocUrl:()=>fn});function pn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=Q(e.content);if(t)return[{kind:\"show-map\",content:t}];let r=[{kind:\"leave-map\"}];return e.file_url&&e.file_url!==n.docUrl?(r.push({kind:\"switch-doc\",url:e.file_url,name:e.file_name||void 0,title:e.doc_title||void 0,updatedAt:e.updated_at||void 0}),r):(e.current_page&&r.push({kind:\"show-page\",page:e.current_page}),r)}function fn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return ke(gn);})();", SOURCE_HASH: "951763e203aba2b4" };
7
+ module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var j=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var y=(e,n)=>{for(var t in n)j(e,t,{get:n[t],enumerable:!0})},xe=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Me(n))!we.call(e,o)&&o!==t&&j(e,o,{get:()=>n[o],enumerable:!(r=ve(n,o))||r.enumerable});return e};var ke=e=>xe(j({},\"__esModule\",{value:!0}),e);var gn={};y(gn,{bridge:()=>R,chat:()=>H,live:()=>$,presentation:()=>O,presentationState:()=>W,tracking:()=>L,viewer:()=>D});var R={};y(R,{onHostMessage:()=>Le,onPlayerMessage:()=>Re,parseHostMessage:()=>pe,parsePlayerMessage:()=>de,sendToHost:()=>Se,sendToPlayer:()=>Ae,toWire:()=>G});var K=\"3dd-doc-\",Te=/^[A-Za-z0-9_-]{1,64}$/,_e=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ee=[\"embed-denied\"],Ie=[\"present-invite\",\"present-handover\",\"present-switch\"],Pe=[\"handover-done\"];function G(e){let n={type:K+e.type};return\"slug\"in e&&e.slug&&(n.slug=e.slug),\"reason\"in e&&e.reason&&(n.reason=String(e.reason).slice(0,40)),n}function ce(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(K))return null;let o=r.slice(K.length);if(n.includes(o))return{type:o};if(Ee.includes(o)){let a=e.reason;return{type:o,reason:/^[a-z-]{1,40}$/.test(String(a||\"\"))?String(a):\"unknown\"}}if(!t.includes(o))return null;let s=e.slug;return typeof s!=\"string\"||!Te.test(s)?null:{type:o,slug:s}}function de(e){return ce(e,_e,Ie)}function pe(e){return ce(e,Pe,[])}function Se(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(G(e),\"*\")}catch(t){}}function Ae(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(G(n),\"*\")}catch(r){}}function Ce(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Re(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Ce(t,s.source))return;let a=de(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function Le(e,n){var s;let t=n||(typeof window!=\"undefined\"?window:null);if(!t)return()=>{};let r=(s=t.parent)!=null?s:null,o=a=>{if(!r||r===t||a.source!==r)return;let c=pe(a.data);c&&e(c)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var L={};y(L,{createTracker:()=>De});var $e=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function He(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(16));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}return Math.random().toString(36).slice(2)+Date.now().toString(36)}function Oe(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;o&&typeof o.sendBeacon==\"function\"?o.sendBeacon(e,new Blob([r],{type:\"application/json\"})):n.fetch(e,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:r,keepalive:!0})}catch(r){}}}function De(e={}){var ae,le;let n=e.win||(typeof window!=\"undefined\"?window:null),t=e.doc||n&&n.document,r=e.now||(()=>Date.now()),o=e.endpoint||\"/api/doc\",s=(ae=e.idleMs)!=null?ae:6e4,a=(le=e.sessionEveryMs)!=null?le:12e3,c=e.slug||\"\",f=e.internal||null,m=e.sessionId||He(),b=e.send||Oe(o,n),p={},h=0,M=0,u=0,v=0,g=null,S=r(),_=!1,N=!1,A=[],he=()=>!!c||!!f,te=i=>{if(he())try{b(i)}catch(l){}},re=()=>!!t&&t.visibilityState===\"visible\"&&(typeof t.hasFocus==\"function\"?t.hasFocus():!0)&&!_,oe=i=>{let l=r()-i,x=Math.max(0,S-i)+s;return Math.max(0,Math.min(l,x))},U=()=>{g!=null&&h>0&&(p[h]=(p[h]||0)+oe(g)/1e3),g=re()?r():null},F=()=>{U(),g=null},V=()=>{g==null&&re()&&(g=r())},z=()=>{let i=0;for(let l in p)i+=p[l];return g!=null&&(i+=oe(g)/1e3),Math.round(i)},ie=()=>{let i={};for(let l in p)i[l]=Math.round(p[l]);return i},E=()=>{S=r(),_&&(_=!1,V())},se=(i,l)=>{f||te({slug:c,event:i,page:l||M,maxPage:u,seconds:z(),sessionId:m})},C=()=>{U();let i={event:\"session\",sessionId:m,numPages:v,maxPage:u,totalSeconds:z(),pagesTime:ie()};f?(i.internal=!0,i.docId=f.docId,i.email=f.email,i.name=f.name):i.slug=c,te(i)},w=(i,l,x,ue)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(l,x,ue),A.push(()=>{i.removeEventListener(l,x,ue)}))};return{sessionId:m,totalSeconds:z,maxPage:()=>u,pageTimes:ie,start(){if(N)return;N=!0,se(\"open\",1),h=1,g=r(),S=r(),w(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(F(),C()):(E(),V())}),w(n,\"blur\",F),w(n,\"focus\",()=>{E(),V()});for(let x of $e)w(n,x,E,{passive:!0});w(e.scrollElement,\"scroll\",E,{passive:!0}),w(n,\"beforeunload\",C);let i=n.setInterval(()=>{!_&&r()-S>s&&(_=!0,F())},5e3),l=n.setInterval(C,a);A.push(()=>{n.clearInterval(i),n.clearInterval(l)})},setPage(i){!i||i===M||(U(),h=i,M=i,i>u&&(u=i,se(\"page\",i)))},setPageCount(i){v=i||0},noteActivity:E,flush:C,stop(){for(;A.length;){let i=A.pop();try{i==null||i()}catch(l){}}N=!1}}}var $={};y($,{STORAGE_KEYS:()=>I,attendeeKey:()=>Fe,authorToken:()=>ze,avatarHtml:()=>B,canModerate:()=>q,createScheduler:()=>Be,escapeHtml:()=>d,flattenPresence:()=>We,formatMessageBody:()=>Z,initials:()=>fe,isMentioned:()=>je,isMine:()=>k,reactorId:()=>Y,shouldNotify:()=>Ke,unreadLabel:()=>Ge});var I={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function d(e){return String(e==null?\"\":e).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")}function fe(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function B(e,n){return e?`<img src=\"${d(e)}\" alt=\"\">`:d(fe(n))}function Z(e){let n=d(e);return n=n.replace(/(https?:\\/\\/[^\\s<]+)/g,t=>`<a href=\"${t}\" target=_blank rel=noreferrer class=cm-link>${t}</a>`),n=n.replace(/(^|\\s)@([\\p{L}0-9_'.-]+)/gu,(t,r,o)=>`${r}<span class=cm-mention>@${o}</span>`),n}function We(e){let n={},t=[];for(let r in e||{})for(let o of(e||{})[r]||[]){let s=String(o.uid||o.email||o.name||\"\").toLowerCase()||`_${r}_${t.length}`;n[s]?o.role===\"presenter\"&&(n[s]=o):(n[s]=o,t.push(s))}return t.map(r=>n[r])}function Ne(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function Ue(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function Fe(e,n,t=\"anon\",r=()=>Math.random().toString(36).slice(2,10)){if(e!=null&&e.email)return String(e.email).toLowerCase();try{if(!n)return`anon-${t}`;let o=n.getItem(I.attendeeKey);if(o)return o;let s=`anon-${r()}`;return n.setItem(I.attendeeKey,s),s}catch(o){return`anon-${t}`}}function Ve(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(24));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}try{console.warn(\"[player] crypto indisponible : jeton d'auteur affaibli\")}catch(n){}return Math.random().toString(36).slice(2)+Math.random().toString(36).slice(2)}function ze(e,n=Ve){let t=Ne(e,I.authorToken);if(t)return t;let r=n();return Ue(e,I.authorToken,r),r}function Y(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function k(e,n){return!e||!n?!1:e.author_email&&n.email?e.author_email===n.email:!e.author_email&&!n.email?e.author_name===n.name:!1}function q(e){return!!(e&&e.role===\"presenter\")}function je(e,n){if(!n||!n.name)return!1;let t=(n.name.split(/\\s+/)[0]||\"\").toLowerCase();return t?String(e&&e.body||\"\").toLowerCase().includes(`@${t}`)&&!k(e,n):!1}function Ke(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||k(n,t)?!1:o}function Ge(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function Be(e,n={}){var M;let t=(M=n.minMs)!=null?M:300,r=n.now||(()=>Date.now()),o=n.setTimer||((u,v)=>setTimeout(u,v)),s=n.clearTimer||(u=>clearTimeout(u)),a=!1,c=!1,f=-1/0,m=null,b=!1;function p(){if(b||a){c=!0;return}let u=t-(r()-f);if(u>0){h(u);return}a=!0,c=!1,f=r();let v=!1;e(()=>{v||(v=!0,a=!1,c&&!b&&p())})}function h(u){b||m!==null||(m=o(()=>{m=null,p()},Math.max(0,u)))}return{signaler(){b||p()},maintenant(){b||(f=-1/0,p())},arreter(){b=!0,m!==null&&(s(m),m=null)}}}var H={};y(H,{messageClassName:()=>Ye,renderActions:()=>be,renderAttachment:()=>ge,renderMessage:()=>Ze,renderReactions:()=>me});function me(e,n){let t=e&&e.reactions||{},r=Y(n),o=\"\";for(let s in t){let a=t[s]||[];if(!a.length)continue;let c=a.indexOf(r)>=0;o+=`<button class=\"re-chip${c?\" on\":\"\"}\" data-e=\"${d(s)}\">${d(s)} ${a.length}</button>`}return o}function ge(e){if(!e||!e.url)return\"\";let n=d(e.url);if(e.kind===\"image\")return`<a class=cm-att href=\"${n}\" target=_blank rel=noreferrer><img src=\"${n}\" alt=\"\" loading=lazy></a>`;let t=d(e.name||\"Document.pdf\");return`<a class=cm-att-pdf href=\"${n}\" target=_blank rel=noreferrer data-pdf=\"${n}\"><span class=cm-att-ph></span><span class=cm-pdflabel>${t}</span></a>`}function be(e,n){let t=k(e,n.me),r=`<button class=cm-react title=\"R\\xE9agir\">${n.reactIcon||\"\"}</button><button class=cm-reply title=\"R\\xE9pondre\">\\u21A9</button>`;return t&&(r+='<button class=cm-edit title=\"Modifier\">\\u270E</button>'),(t||q(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function Ze(e,n){if(e.deleted)return'<span class=a></span><span class=b><div class=\"txt cm-del\">Message supprim\\xE9</div></span>';let t=e.reply_to?`<div class=cm-q><b>${d(e.reply_name||\"\")}</b> ${d(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${Z(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${B(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${d(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+ge(e.attachment)+`<div class=cm-re>${me(e,n.me)}</div></span><span class=cm-act>${be(e,n)}</span>`}function Ye(e,n,t){return\"cm\"+(k(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var O={};y(O,{DEFAULT_CENTER:()=>Q,DEFAULT_ZOOM:()=>ee,MAP_TYPES:()=>P,cycleMapType:()=>qe,initialMapContent:()=>Je,mapTypeLabel:()=>Xe,sanitizeContent:()=>ne});var P=[\"roadmap\",\"satellite\",\"hybrid\"],Q=[46.6,2.5],ee=6;function T(e){let n=Number(e);return Number.isFinite(n)?n:null}function X(e){if(!Array.isArray(e))return null;let n=T(e[0]),t=T(e[1]);return n!=null&&t!=null?[n,t]:null}var J=(e,n,t)=>Math.max(n,Math.min(t,e));function ne(e){if(!e||typeof e!=\"object\")return null;let n=e;if(n.kind===\"pdf\"||n.kind==null)return null;if(n.kind===\"streetview\"){let t=X(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:T(r.heading)||0,pitch:J(T(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:J(T(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:X(n.center)||[...Q],zoom:J(Math.trunc(T(n.zoom)||ee),1,21),marker:X(n.marker),mapType:P.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function qe(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function Xe(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function Je(){return{kind:\"map\",center:[...Q],zoom:ee,marker:null,mapType:null,label:null}}var D={};y(D,{CURRENT_PAGE_MARGIN:()=>on,DEFAULT_ASPECT:()=>nn,MAX_ZOOM:()=>en,MIN_PAGE_WIDTH:()=>tn,MIN_ZOOM:()=>Qe,PRERENDER_MARGIN:()=>rn,arrowState:()=>ln,averageColor:()=>dn,clampPage:()=>ye,clampZoom:()=>sn,fitWidth:()=>cn,isImageDocument:()=>pn,progressPercent:()=>an});var Qe=.5,en=3,nn=1.35,tn=280,rn=\"500px 0px\",on=\"-48% 0px -48% 0px\";function sn(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function ye(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function an(e,n){return n?Math.round(ye(e,n)/n*100):0}function ln(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var un=28;function cn(e){var a;let n=(a=e.zoom)!=null?a:1,t=Math.max(280,e.containerWidth||900)*n;if(!e.onePage)return t;let r=e.aspect||1.35,s=((e.containerHeight||600)-un-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function dn(e){let n=e.length/4;if(!n)return\"rgb(0,0,0)\";let t=0,r=0,o=0;for(let s=0;s<e.length;s+=4)t+=e[s],r+=e[s+1],o+=e[s+2];return`rgb(${Math.round(t/n)},${Math.round(r/n)},${Math.round(o/n)})`}function pn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var W={};y(W,{presentationTransition:()=>fn,switchDocUrl:()=>mn});function fn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=ne(e.content);if(t)return[{kind:\"show-map\",content:t}];let r=[{kind:\"leave-map\"}];return e.file_url&&e.file_url!==n.docUrl?(r.push({kind:\"switch-doc\",url:e.file_url,name:e.file_name||void 0,title:e.doc_title||void 0,updatedAt:e.updated_at||void 0}),r):(e.current_page&&r.push({kind:\"show-page\",page:e.current_page}),r)}function mn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return ke(gn);})();", SOURCE_HASH: "4238739f09f2f24c" };
package/server/handler.js CHANGED
@@ -241,7 +241,8 @@ var Live=(function(){
241
241
  function toggleMute(){ MUTED=!MUTED; try{ localStorage.setItem('3dd-present-mute',MUTED?'1':'0'); }catch(e){} if(MUTED)hidePeek(); applyMute(); }
242
242
  // Flèche « envoyer » : visible seulement si le champ contient du texte et qu'on peut poster.
243
243
  function toggleSend(){ var s=document.getElementById('chatSend'),t=document.getElementById('chatText'); if(!s||!t)return; var can=!(LOCKED&&!canMod()); s.classList.toggle('on', can && (t.value||'').trim().length>0); }
244
- function sendMap(p){try{if(ch)ch.send({type:'broadcast',event:'map',payload:p});}catch(e){}}
244
+ // Un SIGNAL, plus une position : la charge utile était crue par l'audience sur un canal public.
245
+ function sendMap(){try{if(ch)ch.send({type:'broadcast',event:'map',payload:{}});}catch(e){}}
245
246
  function onMap(fn){_onMap=fn;}
246
247
  // État de la présentation diffusé par le présentateur — même canal que la carte. Sert à se
247
248
  // passer de la lecture anonyme des tables : l'audience n'a plus besoin de lire la ligne.
@@ -459,13 +460,30 @@ var Live=(function(){
459
460
  .then(function(d){if(d&&d.ok)applique(d);})
460
461
  .catch(function(){});
461
462
  }
462
- var _relEtat=null,_relChat=null;
463
- // Groupé : dix diffusions d'affilée ne doivent pas produire dix requêtes.
464
- function relireEtat(){clearTimeout(_relEtat);_relEtat=setTimeout(function(){
465
- relire('&state=1',function(d){if(d.state)etatDuServeur(d.state);});
466
- },120);}
467
- function relireChat(){clearTimeout(_relChat);_relChat=setTimeout(function(){
468
- relire('&chat=1',function(d){
463
+ // ⚠️ ORDONNANCEUR BORNÉ, PAS UN DEBOUNCE. Ce qui était écrit ici repoussait l'échéance à
464
+ // chaque signal : un participant diffusant toutes les 100 ms empêchait la relecture
465
+ // INDÉFINIMENT. Toute la défense de 0.1.19 repose sur cette relecture — l'affamer ne
466
+ // falsifie rien, ça fige simplement l'audience, sans qu'aucune erreur ne le dise.
467
+ // Et à l'inverse, des signaux un peu plus espacés produisaient une requête chacun, POUR
468
+ // CHAQUE SPECTATEUR : le canal public devenait un amplificateur vers l'API.
469
+ // Détail et propriétés : src/live.ts + src/__tests__/ordonnanceur.test.ts. (audit P0-2)
470
+ function relireAvec(url,applique){
471
+ return Player.live.createScheduler(function(fini){
472
+ fetch('/api/doc?present='+encodeURIComponent(SLUG)+url)
473
+ .then(function(r){return r.json();})
474
+ .then(function(d){if(d&&d.ok)applique(d);})
475
+ .catch(function(){})
476
+ .then(fini,fini);
477
+ },{minMs:400});
478
+ }
479
+ var _ordEtat=relireAvec('&state=1',function(d){if(d.state)etatDuServeur(d.state);});
480
+ // ⚠️ Exposé hors de cette fermeture : la carte vit dans un AUTRE bloc de script et doit
481
+ // pouvoir déclencher la relecture. Même raison que window.__presAppliquerEtat — un nom
482
+ // référencé depuis la mauvaise portée part dans un catch muet, et l'audience se fige.
483
+ window.__presRelireEtat=function(){_ordEtat.signaler();};
484
+ function relireEtat(){_ordEtat.signaler();}
485
+ function relireChat(){_ordChat.signaler();}
486
+ var _ordChat=relireAvec('&chat=1',function(d){
469
487
  // ⚠️ NOTIFIER CE QUI VIENT D'ARRIVER, ET SEULEMENT ÇA. 'addMsg' rend faux pour un
470
488
  // message déjà connu — la relecture ramène tout l'historique, donc sans cette condition
471
489
  // la pastille de non-lus compterait chaque message à chaque relecture. Et sans l'appel,
@@ -473,8 +491,14 @@ var Live=(function(){
473
491
  // lisant le source de ce paquet, à travers la frontière de deux dépôts.
474
492
  if(d.messages)d.messages.forEach(function(m){if(addMsg(m))notifyMsg(m);else updateMsg(m);});
475
493
  if(typeof d.locked!=='undefined')applyLock(d.locked);
476
- });
477
- },120);}
494
+ });
495
+
496
+ // ⚠️ LE FILET. Borner la cadence ouvre la possibilité qu'un signal se perde — un WebSocket
497
+ // qui tombe, un onglet endormi, un message jamais délivré. Une resynchronisation lente
498
+ // rattrape ce cas : elle ne coûte presque rien et évite qu'une audience reste figée sur un
499
+ // état périmé en croyant être à jour. C'est l'inverse d'une optimisation : c'est le prix de
500
+ // la borne.
501
+ var _filet=setInterval(function(){_ordEtat.maintenant();_ordChat.maintenant();},25000);
478
502
 
479
503
  // ⚠️ LE TITRE VIENT D'ICI, PAS DE LA PRÉSENCE. La liste des participants tirait
480
504
  // « présentateur » de la charge de présence, que chacun compose lui-même : un
@@ -498,13 +522,16 @@ var Live=(function(){
498
522
  ch.on('broadcast',{event:'lock'},function(){relireChat();});
499
523
  ch.on('broadcast',{event:'state'},function(){relireEtat();});
500
524
  ch.on('broadcast',{event:'typing'},function(p){onTyping(p&&p.payload);});
501
- ch.on('broadcast',{event:'map'},function(p){if(_onMap&&p&&p.payload)_onMap(p.payload);});
525
+ // ⚠️ AUCUNE CHARGE NE PASSE. Elle est ignorée plus loin, mais s'arrêter là serait une défense
526
+ // par accident : le jour où quelqu'un rebranche un paramètre, la charge d'un canal public
527
+ // redeviendrait crue sans que rien ne le signale. On coupe le chemin, pas seulement l'usage.
528
+ ch.on('broadcast',{event:'map'},function(){if(_onMap)_onMap();});
502
529
  ch.subscribe(function(st){if(st==='SUBSCRIBED'){ch.track({name:me.name,email:me.email,avatar:me.avatar,role:me.role,member:!!me.member,uid:attKey(me)});sendAttend();}});
503
530
  _tyIv=setInterval(renderTyping,1500);
504
531
  _atIv=setInterval(sendAttend,25000);
505
532
  // Filet de sécurité : au déchargement de la page/iframe (fermeture, reload, switch), on retire la présence
506
533
  // → évite les fantômes (« je me vois deux fois » au retour). Une seule fois.
507
- if(!_phWired){_phWired=true;window.addEventListener('pagehide',function(){try{if(ch){ch.untrack();ch.unsubscribe();ch=null;}}catch(e){}});}
534
+ if(!_phWired){_phWired=true;window.addEventListener('pagehide',function(){try{clearInterval(_filet);_ordEtat.arreter();_ordChat.arreter();}catch(e){}try{if(ch){ch.untrack();ch.unsubscribe();ch=null;}}catch(e){}});}
508
535
  }catch(e){}}
509
536
  function disconnect(){try{clearInterval(_tyIv);}catch(e){}try{clearInterval(_atIv);}catch(e){}try{sendAttend();}catch(e){}try{if(ch){ch.untrack();ch.unsubscribe();ch=null;}}catch(e){}var pb=document.getElementById('presBtn');if(pb)pb.style.display='none';var cb=document.getElementById('chatBtn');if(cb)cb.style.display='none';var _fb=document.getElementById('chatFab');if(_fb)_fb.classList.remove('on');var pn=document.getElementById('chatPanel');if(pn)pn.classList.add('hidden');}
510
537
  // Membre de l'équipe reconnu via la session app (MÊME ORIGINE, localStorage) → avatar + nom auto.
@@ -573,8 +600,34 @@ var Map3DD=(function(){
573
600
  function state(){ if(!map)return null; if(useG){ var c=map.getCenter(); return {kind:'map',center:[c.lat(),c.lng()],zoom:map.getZoom(),marker:marker?[marker.getPosition().lat(),marker.getPosition().lng()]:null,mapType:map.getMapTypeId()}; }
574
601
  var c2=map.getCenter(); return {kind:'map',center:[c2.lat,c2.lng],zoom:map.getZoom(),marker:marker?[marker.getLatLng().lat,marker.getLatLng().lng]:null}; }
575
602
  function setCenterZoom(ll,z){ if(useG){ map.setCenter({lat:ll[0],lng:ll[1]}); map.setZoom(z); } else { map.setView(ll,z); } }
576
- function broadcast(){ var n=Date.now(); if(n-_bcT<100||!map)return; _bcT=n; var s=state(); if(s){try{ if(window.Live) Live.sendMap(s); }catch(e){}} }
577
- function schedPersist(){ clearTimeout(_psT); _psT=setTimeout(function(){ if(map&&persist)persist(state()); },700); }
603
+ // ⚠️ LA POSITION NE VOYAGE PLUS DANS LA DIFFUSION.
604
+ //
605
+ // Elle y voyageait, et l'audience l'appliquait telle quelle. Le canal étant public, n'importe quel
606
+ // participant déplaçait donc la carte de tout le monde, avec les coordonnées de son choix. C'était
607
+ // assumé en 0.1.19 au motif que le signal est « éphémère et sans vérité serveur » — l'argument ne
608
+ // tient pas : PENDANT UN MODE CARTE, CE SIGNAL EST L'IMAGE QUE VOIT L'AUDIENCE. 'typing' peut
609
+ // rester cosmétique, 'map' non. (audit P0-1)
610
+ //
611
+ // Le présentateur persiste sa position (route gatée par JWT), puis émet un signal VIDE. L'audience
612
+ // relit l'état et applique ce que le serveur lui donne. Un participant hostile peut toujours
613
+ // émettre le signal : il provoque une relecture, et n'obtient rien.
614
+ //
615
+ // ⚠️ ORDONNANCEUR ET NON DEBOUNCE, et c'est le cœur du problème. schedPersist repoussait
616
+ // l'écriture de 700 ms à chaque mouvement : pendant un déplacement CONTINU, elle ne partait
617
+ // jamais. C'est précisément pour ça que la position voyageait dans la diffusion. Un ordonnanceur
618
+ // écrit au plus une fois par 500 ms ET sert toujours la dernière position — donc l'audience suit
619
+ // pendant le mouvement, pas seulement à l'arrêt.
620
+ //
621
+ // Le suivi devient PAR PALIERS au lieu d'être continu (environ deux fois par seconde). C'est le
622
+ // prix pour que personne d'autre que le présentateur ne pilote l'écran de l'audience.
623
+ var _ordPersist=null;
624
+ function persistOrd(){ if(!_ordPersist&&window.Player&&Player.live&&Player.live.createScheduler){
625
+ _ordPersist=Player.live.createScheduler(function(fini){ try{ if(map&&persist)persist(state()); }catch(e){} fini(); },{minMs:500}); }
626
+ return _ordPersist; }
627
+ function broadcast(){ if(!map)return; var n=Date.now(); if(n-_bcT<200)return; _bcT=n;
628
+ var o=persistOrd(); if(o)o.signaler();
629
+ try{ if(window.Live) Live.sendMap(); }catch(e){} }
630
+ function schedPersist(){ var o=persistOrd(); if(o)o.signaler(); else { clearTimeout(_psT); _psT=setTimeout(function(){ if(map&&persist)persist(state()); },700); } }
578
631
  function enter(content,presenter,persistFn){ isPres=!!presenter; if(persistFn)persist=persistFn;
579
632
  if(content&&content.mapType)mapType=content.mapType;
580
633
  var wrap=document.getElementById('mapWrap'); var on=wrap&&wrap.classList.contains('on');
@@ -613,7 +666,14 @@ var Map3DD=(function(){
613
666
  function goSV(center){ if(!GMAPS_KEY){tempHint('Street View indisponible.');return;} tempHint('Recherche Street View…');
614
667
  loadGoogle(function(){ try{ new google.maps.StreetViewService().getPanorama({location:{lat:center[0],lng:center[1]},radius:80},function(data,status){ if(status==='OK'&&data&&data.location){ var ll=data.location.latLng; var content={kind:'streetview',position:[ll.lat(),ll.lng()],pov:{heading:0,pitch:0},zoom:1}; if(persist)persist(content); enterSV(content,true,persist); } else { tempHint('Pas de Street View à cet endroit.'); } }); }catch(e){ tempHint('Street View indisponible.'); } }); }
615
668
  function svState(){ if(!pano)return null; var p=pano.getPosition(),v=pano.getPov(); if(!p)return null; return {kind:'streetview',position:[p.lat(),p.lng()],pov:{heading:v.heading,pitch:v.pitch},zoom:pano.getZoom()}; }
616
- function svBcast(){ var n=Date.now(); if(n-_svBcT<120)return; _svBcT=n; var s=svState(); if(!s)return; try{ if(window.Live)Live.sendMap(s); }catch(e){} clearTimeout(_psT); _psT=setTimeout(function(){ if(persist&&pano)persist(svState()); },800); }
669
+ // Même règle qu'au-dessus : on persiste, on signale, on ne transporte pas la position.
670
+ var _ordSv=null;
671
+ function svOrd(){ if(!_ordSv&&window.Player&&Player.live&&Player.live.createScheduler){
672
+ _ordSv=Player.live.createScheduler(function(fini){ try{ if(persist&&pano)persist(svState()); }catch(e){} fini(); },{minMs:500}); }
673
+ return _ordSv; }
674
+ function svBcast(){ var n=Date.now(); if(n-_svBcT<200)return; _svBcT=n; if(!svState())return;
675
+ var o=svOrd(); if(o)o.signaler();
676
+ try{ if(window.Live)Live.sendMap(); }catch(e){} }
617
677
  function svApply(p){ if(!pano||!p)return; try{ if(p.position)pano.setPosition({lat:p.position[0],lng:p.position[1]}); if(p.pov)pano.setPov({heading:p.pov.heading||0,pitch:p.pov.pitch||0}); if(typeof p.zoom!=='undefined')pano.setZoom(p.zoom); }catch(e){} }
618
678
  function ensurePano(content){ var el=document.getElementById('svPano'); if(!el||!window.google||!window.google.maps)return;
619
679
  var pos=content&&content.position?{lat:content.position[0],lng:content.position[1]}:{lat:48.8584,lng:2.2945};
@@ -2355,7 +2415,10 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
2355
2415
  (function(){
2356
2416
  var slug=${JSON.stringify(pres.slug)};
2357
2417
  // Carte live : suivre en direct les mouvements du présentateur (broadcast).
2358
- try{ if(window.Live&&window.Map3DD) Live.onMap(function(p){ Map3DD.apply(p); }); }catch(e){}
2418
+ // ⚠️ ON NE CROIT PLUS LA CHARGE. Le signal dit « quelque chose a bougé » ; ce qui a bougé vient
2419
+ // de la relecture d'état, gatée par l'écriture du présentateur. Un participant hostile peut
2420
+ // émettre : il déclenche une relecture bornée, et n'obtient rien.
2421
+ try{ if(window.Live&&window.Map3DD) Live.onMap(function(){ if(window.__presRelireEtat)window.__presRelireEtat(); }); }catch(e){}
2359
2422
  // L'état arrive maintenant par DEUX voies : la table (historique) et la diffusion du
2360
2423
  // présentateur (nouvelle). Les deux passent par le même filtre, qui ignore un état déjà
2361
2424
  // appliqué — recevoir deux fois la même chose ne doit pas re-rendre la page.