discovery-media-player 0.1.43 → 0.1.44
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.
|
|
3
|
+
"version": "0.1.44",
|
|
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 j=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var we=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var y=(e,n)=>{for(var t in n)j(e,t,{get:n[t],enumerable:!0})},Ee=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of we(n))!Te.call(e,o)&&o!==t&&j(e,o,{get:()=>n[o],enumerable:!(r=_e(n,o))||r.enumerable});return e};var Se=e=>Ee(j({},\"__esModule\",{value:!0}),e);var wn={};y(wn,{bridge:()=>C,chat:()=>N,live:()=>O,presentation:()=>$,presentationState:()=>U,tracking:()=>L,viewer:()=>H});var C={};y(C,{onHostMessage:()=>$e,onPlayerMessage:()=>Ne,parseHostMessage:()=>pe,parsePlayerMessage:()=>de,sendToHost:()=>Ce,sendToPlayer:()=>Le,toWire:()=>K});var G=\"3dd-doc-\",ke=/^[A-Za-z0-9_-]{1,64}$/,Re=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ie=[\"embed-denied\"],Pe=[\"present-invite\",\"present-handover\",\"present-switch\"],Ae=[\"handover-done\"];function K(e){let n={type:G+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(G))return null;let o=r.slice(G.length);if(n.includes(o))return{type:o};if(Ie.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\"||!ke.test(s)?null:{type:o,slug:s}}function de(e){return ce(e,Re,Pe)}function pe(e){return ce(e,Ae,[])}function Ce(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(K(e),\"*\")}catch(t){}}function Le(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(K(n),\"*\")}catch(r){}}function Oe(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Ne(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Oe(t,s.source))return;let a=de(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function $e(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:()=>Ke});var He=Math.ceil(300),fe=25,En=He*fe,me=18e4,Ue=25e3,De=Math.ceil(36e5/Ue),We=720,Ve=De+We,Sn=Ve*fe;var ze=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function je(){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 Ge(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 Ke(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:me,a=(le=e.sessionEveryMs)!=null?le:12e3,c=e.slug||\"\",d=e.internal||null,m=e.sessionId||je(),b=e.send||Ge(o,n),f={},h=0,M=0,u=0,v=0,g=null,T=r(),E=!1,D=!1,P=[],xe=()=>!!c||!!d,te=i=>{if(xe())try{b(i)}catch(l){}},W=()=>!!t&&t.visibilityState===\"visible\"&&!E,re=i=>{let l=r()-i,x=Math.max(0,T-i)+s;return Math.max(0,Math.min(l,x))},V=()=>{g!=null&&h>0&&(f[h]=(f[h]||0)+re(g)/1e3),g=W()?r():null},oe=()=>{V(),g=null},F=()=>{g==null&&W()&&(g=r())},z=()=>{let i=0;for(let l in f)i+=f[l];return g!=null&&(i+=re(g)/1e3),Math.round(i)},ie=()=>{let i={};for(let l in f)i[l]=Math.round(f[l]);return i},S=()=>{T=r(),E&&(E=!1,F())},se=(i,l)=>{d||te({slug:c,event:i,page:l||M,maxPage:u,seconds:z(),sessionId:m})},A=()=>{V();let i={event:\"session\",sessionId:m,numPages:v,maxPage:u,totalSeconds:z(),pagesTime:ie()};d?(i.internal=!0,i.docId=d.docId,i.email=d.email,i.name=d.name,d.it&&(i.it=d.it)):i.slug=c,te(i)},k=(i,l,x,ue)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(l,x,ue),P.push(()=>{i.removeEventListener(l,x,ue)}))};return{sessionId:m,totalSeconds:z,maxPage:()=>u,pageTimes:ie,start(){if(D)return;D=!0,se(\"open\",1),h=1,T=r(),g=W()?r():null,k(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(oe(),A()):(S(),F())}),k(n,\"focus\",()=>{S(),F()});for(let x of ze)k(n,x,S,{passive:!0});k(e.scrollElement,\"scroll\",S,{passive:!0}),k(n,\"beforeunload\",A);let i=n.setInterval(()=>{!E&&r()-T>s&&(E=!0,oe())},5e3),l=n.setInterval(A,a);P.push(()=>{n.clearInterval(i),n.clearInterval(l)})},setPage(i){!i||i===M||(T=r(),V(),h=i,M=i,i>u&&(u=i,se(\"page\",i)))},setPageCount(i){v=i||0},noteActivity:S,flush:A,stop(){for(;P.length;){let i=P.pop();try{i==null||i()}catch(l){}}D=!1}}}var O={};y(O,{STORAGE_KEYS:()=>R,attendeeKey:()=>qe,authorToken:()=>Xe,avatarHtml:()=>Y,canModerate:()=>q,createScheduler:()=>nn,escapeHtml:()=>p,flattenPresence:()=>Ye,formatMessageBody:()=>B,initials:()=>ge,isMentioned:()=>Qe,isMine:()=>_,reactorId:()=>Z,shouldNotify:()=>Je,unreadLabel:()=>en});var R={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function p(e){return String(e==null?\"\":e).replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\")}function ge(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function Y(e,n){return e?`<img src=\"${p(e)}\" alt=\"\">`:p(ge(n))}function B(e){let n=p(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 Ye(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 Be(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function Ze(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function qe(e,n=\"anon\",t=be){try{if(!e)return`anon-${n}`;let r=e.getItem(R.attendeeKey);if(r)return r;let o=`anon-${t()}`;return e.setItem(R.attendeeKey,o),o}catch(r){return`anon-${n}`}}function be(){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 Xe(e,n=be){let t=Be(e,R.authorToken);if(t)return t;let r=n();return Ze(e,R.authorToken,r),r}function Z(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function _(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 Qe(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}`)&&!_(e,n):!1}function Je(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||_(n,t)?!1:o}function en(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function nn(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,d=-1/0,m=null,b=!1;function f(){if(b||a){c=!0;return}let u=t-(r()-d);if(u>0){h(u);return}a=!0,c=!1,d=r();let v=!1;e(()=>{v||(v=!0,a=!1,c&&!b&&f())})}function h(u){b||m!==null||(m=o(()=>{m=null,f()},Math.max(0,u)))}return{signaler(){b||f()},maintenant(){b||(d=-1/0,f())},arreter(){b=!0,m!==null&&(s(m),m=null)}}}var N={};y(N,{messageClassName:()=>rn,renderActions:()=>ve,renderAttachment:()=>he,renderMessage:()=>tn,renderReactions:()=>ye});function ye(e,n){let t=e&&e.reactions||{},r=Z(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=\"${p(s)}\">${p(s)} ${a.length}</button>`}return o}function he(e){if(!e||!e.url)return\"\";let n=p(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=p(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 ve(e,n){let t=_(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 tn(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>${p(e.reply_name||\"\")}</b> ${p(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${B(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${Y(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${p(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+he(e.attachment)+`<div class=cm-re>${ye(e,n.me)}</div></span><span class=cm-act>${ve(e,n)}</span>`}function rn(e,n,t){return\"cm\"+(_(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var $={};y($,{DEFAULT_CENTER:()=>J,DEFAULT_ZOOM:()=>ee,MAP_TYPES:()=>I,cycleMapType:()=>on,initialMapContent:()=>an,mapTypeLabel:()=>sn,sanitizeContent:()=>ne});var I=[\"roadmap\",\"satellite\",\"hybrid\"],J=[46.6,2.5],ee=6;function w(e){let n=Number(e);return Number.isFinite(n)?n:null}function X(e){if(!Array.isArray(e))return null;let n=w(e[0]),t=w(e[1]);return n!=null&&t!=null?[n,t]:null}var Q=(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:w(r.heading)||0,pitch:Q(w(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:Q(w(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:X(n.center)||[...J],zoom:Q(Math.trunc(w(n.zoom)||ee),1,21),marker:X(n.marker),mapType:I.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function on(e){let n=I.indexOf(e);return I[(n+1)%I.length]}function sn(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function an(){return{kind:\"map\",center:[...J],zoom:ee,marker:null,mapType:null,label:null}}var H={};y(H,{CURRENT_PAGE_MARGIN:()=>fn,DEFAULT_ASPECT:()=>cn,MAX_ZOOM:()=>un,MIN_PAGE_WIDTH:()=>dn,MIN_ZOOM:()=>ln,PRERENDER_MARGIN:()=>pn,arrowState:()=>bn,averageColor:()=>vn,clampPage:()=>Me,clampZoom:()=>mn,fitWidth:()=>hn,isImageDocument:()=>Mn,progressPercent:()=>gn});var ln=.5,un=3,cn=1.35,dn=280,pn=\"500px 0px\",fn=\"-48% 0px -48% 0px\";function mn(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function Me(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function gn(e,n){return n?Math.round(Me(e,n)/n*100):0}function bn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var yn=28;function hn(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)-yn-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function vn(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 Mn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var U={};y(U,{presentationTransition:()=>xn,switchDocUrl:()=>_n});function xn(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 _n(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Se(wn);})();", SOURCE_HASH: "bcdddbf20bceaa1b" };
|
|
7
|
+
module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var C=Object.defineProperty,Ne=Object.defineProperties,$e=Object.getOwnPropertyDescriptor,He=Object.getOwnPropertyDescriptors,Ue=Object.getOwnPropertyNames,be=Object.getOwnPropertySymbols;var he=Object.prototype.hasOwnProperty,De=Object.prototype.propertyIsEnumerable;var ye=(e,n,t)=>n in e?C(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,K=(e,n)=>{for(var t in n||(n={}))he.call(n,t)&&ye(e,t,n[t]);if(be)for(var t of be(n))De.call(n,t)&&ye(e,t,n[t]);return e},ve=(e,n)=>Ne(e,He(n));var v=(e,n)=>{for(var t in n)C(e,t,{get:n[t],enumerable:!0})},We=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Ue(n))!he.call(e,o)&&o!==t&&C(e,o,{get:()=>n[o],enumerable:!(r=$e(n,o))||r.enumerable});return e};var Ve=e=>We(C({},\"__esModule\",{value:!0}),e);var Nn={};v(Nn,{bridge:()=>L,cadence:()=>O,chat:()=>H,live:()=>$,presentation:()=>U,presentationState:()=>W,tracking:()=>N,viewer:()=>D});var L={};v(L,{onHostMessage:()=>Xe,onPlayerMessage:()=>qe,parseHostMessage:()=>_e,parsePlayerMessage:()=>xe,sendToHost:()=>Be,sendToPlayer:()=>Ye,toWire:()=>Y});var B=\"3dd-doc-\",Fe=/^[A-Za-z0-9_-]{1,64}$/,ze=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],je=[\"embed-denied\"],Ge=[\"present-invite\",\"present-handover\",\"present-switch\"],Ke=[\"handover-done\"];function Y(e){let n={type:B+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 Me(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(B))return null;let o=r.slice(B.length);if(n.includes(o))return{type:o};if(je.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\"||!Fe.test(s)?null:{type:o,slug:s}}function xe(e){return Me(e,ze,Ge)}function _e(e){return Me(e,Ke,[])}function Be(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(Y(e),\"*\")}catch(t){}}function Ye(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(Y(n),\"*\")}catch(r){}}function Ze(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function qe(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Ze(t,s.source))return;let a=xe(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function Xe(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 l=_e(a.data);l&&e(l)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var N={};v(N,{createTracker:()=>rn});var O={};v(O,{PRESENTER_ACTIONS_PER_HOUR:()=>Se,PRESENT_QUOTA_PER_HOUR:()=>Je,PRESENT_READS_PER_HOUR:()=>ke,PRESENT_RESYNC_MS:()=>we,PRESENT_WRITE_TIMEOUT_MS:()=>Q,READERS_PER_EGRESS:()=>q,RESYNC_READS_PER_HOUR:()=>Ee,SESSION_IDLE_MS:()=>X,SESSION_INTERVAL_MS:()=>Z,SESSION_QUOTA_PER_HOUR:()=>Qe,SESSION_WRITES_PER_HOUR:()=>Te});var Z=12e3,Te=Math.ceil(300),q=25,Qe=Te*q,X=18e4,we=25e3,Ee=Math.ceil(36e5/we),Se=720,ke=Ee+Se,Je=ke*q,Q=1e4;var en=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function nn(){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 tn(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 rn(e={}){var fe,me;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=(fe=e.idleMs)!=null?fe:X,a=(me=e.sessionEveryMs)!=null?me:12e3,l=e.slug||\"\",p=e.internal||null,m=e.sessionId||nn(),b=e.send||tn(o,n),f={},u=0,y=0,c=0,M=0,h=null,w=r(),E=!1,V=!1,P=[],Oe=()=>!!l||!!p,le=i=>{if(Oe())try{b(i)}catch(d){}},F=()=>!!t&&t.visibilityState===\"visible\"&&!E,ue=i=>{let d=r()-i,x=Math.max(0,w-i)+s;return Math.max(0,Math.min(d,x))},z=()=>{h!=null&&u>0&&(f[u]=(f[u]||0)+ue(h)/1e3),h=F()?r():null},ce=()=>{z(),h=null},j=()=>{h==null&&F()&&(h=r())},G=()=>{let i=0;for(let d in f)i+=f[d];return h!=null&&(i+=ue(h)/1e3),Math.round(i)},de=()=>{let i={};for(let d in f)i[d]=Math.round(f[d]);return i},S=()=>{w=r(),E&&(E=!1,j())},pe=(i,d)=>{p||le({slug:l,event:i,page:d||y,maxPage:c,seconds:G(),sessionId:m})},A=()=>{z();let i={event:\"session\",sessionId:m,numPages:M,maxPage:c,totalSeconds:G(),pagesTime:de()};p?(i.internal=!0,i.docId=p.docId,i.email=p.email,i.name=p.name,p.it&&(i.it=p.it)):i.slug=l,le(i)},k=(i,d,x,ge)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(d,x,ge),P.push(()=>{i.removeEventListener(d,x,ge)}))};return{sessionId:m,totalSeconds:G,maxPage:()=>c,pageTimes:de,start(){if(V)return;V=!0,pe(\"open\",1),u=1,w=r(),h=F()?r():null,k(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(ce(),A()):(S(),j())}),k(n,\"focus\",()=>{S(),j()});for(let x of en)k(n,x,S,{passive:!0});k(e.scrollElement,\"scroll\",S,{passive:!0}),k(n,\"beforeunload\",A);let i=n.setInterval(()=>{!E&&r()-w>s&&(E=!0,ce())},5e3),d=n.setInterval(A,a);P.push(()=>{n.clearInterval(i),n.clearInterval(d)})},setPage(i){!i||i===y||(w=r(),z(),u=i,y=i,i>c&&(c=i,pe(\"page\",i)))},setPageCount(i){M=i||0},noteActivity:S,flush:A,stop(){for(;P.length;){let i=P.pop();try{i==null||i()}catch(d){}}V=!1}}}var $={};v($,{STORAGE_KEYS:()=>R,attendeeKey:()=>ln,authorToken:()=>un,avatarHtml:()=>J,canModerate:()=>te,createScheduler:()=>fn,escapeHtml:()=>g,fetchBorne:()=>mn,flattenPresence:()=>on,formatMessageBody:()=>ee,initials:()=>Re,isMentioned:()=>cn,isMine:()=>_,reactorId:()=>ne,shouldNotify:()=>dn,unreadLabel:()=>pn});var R={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function g(e){return String(e==null?\"\":e).replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\")}function Re(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function J(e,n){return e?`<img src=\"${g(e)}\" alt=\"\">`:g(Re(n))}function ee(e){let n=g(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 on(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 sn(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function an(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function ln(e,n=\"anon\",t=Ie){try{if(!e)return`anon-${n}`;let r=e.getItem(R.attendeeKey);if(r)return r;let o=`anon-${t()}`;return e.setItem(R.attendeeKey,o),o}catch(r){return`anon-${n}`}}function Ie(){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 un(e,n=Ie){let t=sn(e,R.authorToken);if(t)return t;let r=n();return an(e,R.authorToken,r),r}function ne(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function _(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 te(e){return!!(e&&e.role===\"presenter\")}function cn(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}`)&&!_(e,n):!1}function dn(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||_(n,t)?!1:o}function pn(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function fn(e,n={}){var y;let t=(y=n.minMs)!=null?y:300,r=n.now||(()=>Date.now()),o=n.setTimer||((c,M)=>setTimeout(c,M)),s=n.clearTimer||(c=>clearTimeout(c)),a=!1,l=!1,p=-1/0,m=null,b=!1;function f(){if(b||a){l=!0;return}let c=t-(r()-p);if(c>0){u(c);return}a=!0,l=!1,p=r();let M=!1;e(()=>{M||(M=!0,a=!1,l&&!b&&f())})}function u(c){b||m!==null||(m=o(()=>{m=null,f()},Math.max(0,c)))}return{signaler(){b||f()},maintenant(){b||(p=-1/0,f())},arreter(){b=!0,m!==null&&(s(m),m=null)}}}function mn(e,n,t=Q,r={}){let o=r.fetch||(typeof fetch==\"function\"?fetch:void 0);if(!o)return Promise.reject(new Error(\"fetch indisponible\"));let s=r.setTimer||((u,y)=>setTimeout(u,y)),a=r.clearTimer||(u=>clearTimeout(u)),l=null;try{l=new AbortController}catch(u){}let p=l?ve(K({},n||{}),{signal:l.signal}):K({},n||{}),m=null,b=new Promise((u,y)=>{m=s(()=>{try{l==null||l.abort()}catch(c){}y(new Error(\"delai\"))},t)}),f=()=>{try{a(m)}catch(u){}};return Promise.race([o(e,p),b]).then(u=>(f(),u),u=>{throw f(),u})}var H={};v(H,{messageClassName:()=>bn,renderActions:()=>Ce,renderAttachment:()=>Ae,renderMessage:()=>gn,renderReactions:()=>Pe});function Pe(e,n){let t=e&&e.reactions||{},r=ne(n),o=\"\";for(let s in t){let a=t[s]||[];if(!a.length)continue;let l=a.indexOf(r)>=0;o+=`<button class=\"re-chip${l?\" on\":\"\"}\" data-e=\"${g(s)}\">${g(s)} ${a.length}</button>`}return o}function Ae(e){if(!e||!e.url)return\"\";let n=g(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=g(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 Ce(e,n){let t=_(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||te(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function gn(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>${g(e.reply_name||\"\")}</b> ${g(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${ee(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${J(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${g(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+Ae(e.attachment)+`<div class=cm-re>${Pe(e,n.me)}</div></span><span class=cm-act>${Ce(e,n)}</span>`}function bn(e,n,t){return\"cm\"+(_(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var U={};v(U,{DEFAULT_CENTER:()=>ie,DEFAULT_ZOOM:()=>se,MAP_TYPES:()=>I,cycleMapType:()=>yn,initialMapContent:()=>vn,mapTypeLabel:()=>hn,sanitizeContent:()=>ae});var I=[\"roadmap\",\"satellite\",\"hybrid\"],ie=[46.6,2.5],se=6;function T(e){let n=Number(e);return Number.isFinite(n)?n:null}function re(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 oe=(e,n,t)=>Math.max(n,Math.min(t,e));function ae(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=re(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:oe(T(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:oe(T(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:re(n.center)||[...ie],zoom:oe(Math.trunc(T(n.zoom)||se),1,21),marker:re(n.marker),mapType:I.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function yn(e){let n=I.indexOf(e);return I[(n+1)%I.length]}function hn(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function vn(){return{kind:\"map\",center:[...ie],zoom:se,marker:null,mapType:null,label:null}}var D={};v(D,{CURRENT_PAGE_MARGIN:()=>En,DEFAULT_ASPECT:()=>_n,MAX_ZOOM:()=>xn,MIN_PAGE_WIDTH:()=>Tn,MIN_ZOOM:()=>Mn,PRERENDER_MARGIN:()=>wn,arrowState:()=>Rn,averageColor:()=>An,clampPage:()=>Le,clampZoom:()=>Sn,fitWidth:()=>Pn,isImageDocument:()=>Cn,progressPercent:()=>kn});var Mn=.5,xn=3,_n=1.35,Tn=280,wn=\"500px 0px\",En=\"-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 Le(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function kn(e,n){return n?Math.round(Le(e,n)/n*100):0}function Rn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var In=28;function Pn(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)-In-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function An(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 Cn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var W={};v(W,{presentationTransition:()=>Ln,switchDocUrl:()=>On});function Ln(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=ae(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 On(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Ve(Nn);})();", SOURCE_HASH: "23da6b6df4f91661" };
|
package/server/handler.js
CHANGED
|
@@ -474,9 +474,36 @@ var Live=(function(){
|
|
|
474
474
|
// Et à l'inverse, des signaux un peu plus espacés produisaient une requête chacun, POUR
|
|
475
475
|
// CHAQUE SPECTATEUR : le canal public devenait un amplificateur vers l'API.
|
|
476
476
|
// Détail et propriétés : src/live.ts + src/__tests__/ordonnanceur.test.ts. (audit P0-2)
|
|
477
|
+
// UNE REQUÊTE QUI NE REVIENT PAS BLOQUE TOUT CE QUI L'ATTEND.
|
|
478
|
+
//
|
|
479
|
+
// ⚠️ Ce risque est né d'un correctif. Avant 0.1.41 l'ordonnanceur appelait « fini() » tout de
|
|
480
|
+
// suite : le désordre était possible, le blocage non. En rendant les écritures séquentielles on a
|
|
481
|
+
// échangé un défaut de correction contre un risque de disponibilité — une requête suspendue ne
|
|
482
|
+
// règle jamais sa promesse, la file ne repart pas, et le présentateur pilote dans le vide sans
|
|
483
|
+
// qu'aucun message ne le dise. Un navigateur ne garantit aucun délai de son côté.
|
|
484
|
+
//
|
|
485
|
+
// ⚠️ Ce que ça restaure : la VIVACITÉ. Pas l'ORDRE — une requête abandonnée peut être arrivée au
|
|
486
|
+
// serveur et atterrir après celle qui l'a remplacée. L'ordre malgré un abandon demande un numéro
|
|
487
|
+
// de version porté par l'écriture, et c'est le chantier de la file unique.
|
|
488
|
+
//
|
|
489
|
+
// L'abandon est BEST-EFFORT : sans AbortController on laisse la requête vivre, on cesse seulement
|
|
490
|
+
// de l'attendre. Rendre la main compte plus que couper le fil.
|
|
491
|
+
function fetchBorne(url,opts,ms){
|
|
492
|
+
var limite=ms||(window.Player&&Player.cadence&&Player.cadence.PRESENT_WRITE_TIMEOUT_MS)||10000;
|
|
493
|
+
var ctl=null; try{ ctl=new AbortController(); }catch(e){}
|
|
494
|
+
var o=opts||{};
|
|
495
|
+
if(ctl){ o=Object.assign({},o); o.signal=ctl.signal; }
|
|
496
|
+
var minuteur=null;
|
|
497
|
+
var expiration=new Promise(function(_,ko){
|
|
498
|
+
minuteur=setTimeout(function(){ try{ if(ctl)ctl.abort(); }catch(e){} ko(new Error('delai')); },limite);
|
|
499
|
+
});
|
|
500
|
+
function rendreLaMain(v,echec){ try{ clearTimeout(minuteur); }catch(e){} if(echec)throw v; return v; }
|
|
501
|
+
return Promise.race([fetch(url,o),expiration])
|
|
502
|
+
.then(function(r){return rendreLaMain(r,false);},function(e){return rendreLaMain(e,true);});
|
|
503
|
+
}
|
|
477
504
|
function relireAvec(url,applique){
|
|
478
505
|
return Player.live.createScheduler(function(fini){
|
|
479
|
-
|
|
506
|
+
fetchBorne('/api/doc?present='+encodeURIComponent(SLUG)+url)
|
|
480
507
|
.then(function(r){return r.json();})
|
|
481
508
|
.then(function(d){if(d&&d.ok)applique(d);})
|
|
482
509
|
.catch(function(){})
|
|
@@ -2054,7 +2081,7 @@ ${LEGAL_CSS}
|
|
|
2054
2081
|
// (audit P0-3)
|
|
2055
2082
|
function diffuserEtat(){ if(!PRES||!window.Live)return; try{ Live.sendState(); }catch(e){} }
|
|
2056
2083
|
function pushPage(){ if(!PRES)return; clearTimeout(_pushT); _pushT=setTimeout(function(){ if(!PRES)return;
|
|
2057
|
-
|
|
2084
|
+
Player.live.fetchBorne('/api/doc',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'present-page',slug:PRES.slug,control:PRES.control,page:cur||1})})
|
|
2058
2085
|
.then(function(r){ if(r&&r.ok)diffuserEtat(); })
|
|
2059
2086
|
.catch(function(){});
|
|
2060
2087
|
},250); }
|
|
@@ -2090,7 +2117,7 @@ ${LEGAL_CSS}
|
|
|
2090
2117
|
if(!PRES) return Promise.resolve();
|
|
2091
2118
|
var p=PRES, btn=document.getElementById('pbarEnd'), libelle=btn?btn.textContent:'';
|
|
2092
2119
|
if(btn){ btn.disabled=true; btn.textContent='…'; }
|
|
2093
|
-
return
|
|
2120
|
+
return Player.live.fetchBorne('/api/doc',{method:'POST',headers:{'Content-Type':'application/json'},keepalive:true,
|
|
2094
2121
|
body:JSON.stringify({action:'present-end',slug:p.slug,control:p.control})})
|
|
2095
2122
|
.then(function(r){ if(!r||!r.ok) throw new Error('present-end'); })
|
|
2096
2123
|
.then(function(){
|
|
@@ -2151,7 +2178,7 @@ ${LEGAL_CSS}
|
|
|
2151
2178
|
// s'appliquait qu'à l'ordre des APPELS, pas à celui des écritures — plusieurs PATCH pouvaient
|
|
2152
2179
|
// voler ensemble, et une position ancienne atterrir après une récente.
|
|
2153
2180
|
function presentContent(content){ if(!PRES)return Promise.resolve(); var h={'Content-Type':'application/json'}; var tk=appToken(); if(tk) h['Authorization']='Bearer '+tk;
|
|
2154
|
-
return
|
|
2181
|
+
return Player.live.fetchBorne('/api/doc',{method:'POST',headers:h,body:JSON.stringify({action:'present-content',slug:PRES.slug,control:PRES.control,content:content})})
|
|
2155
2182
|
.then(function(r){ if(r&&r.ok)diffuserEtat(); })
|
|
2156
2183
|
.catch(function(){}); }
|
|
2157
2184
|
function showMap(){ if(!PRES||!window.Map3DD)return; var wrap=document.getElementById('mapWrap'); if(wrap&&wrap.classList.contains('on')){ Map3DD.enter(null,true,presentContent); return; } var init=Player.presentation.initialMapContent(); presentContent(init); Map3DD.enter(init,true,presentContent); }
|
package/server/presentations.js
CHANGED
|
@@ -77,6 +77,33 @@ async function listActivePresentations(email) {
|
|
|
77
77
|
return live.map((r) => ({ slug: r.slug, docId: r.doc_id, fileUrl: r.file_url, fileName: r.file_name, docTitle: r.doc_title, presenterName: r.presenter_name, ownerName: r.owner_name, ownerAvatar: r.owner_avatar, currentPage: r.current_page || 1, mine: !!(me && r.owner_email && r.owner_email === me), updatedAt: r.updated_at }));
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Terminer une présentation RÉVOQUE son jeton de contrôle.
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ SANS ÇA, « terminer » ne terminait rien de définitif. Les fonctions de pilotage écrivent
|
|
84
|
+
* « active: true » — c'est voulu, on y revient — et le jeton de contrôle survivait à la clôture. Un
|
|
85
|
+
* second onglet resté ouvert, ou une écriture encore en vol, remettait donc la présentation en ligne
|
|
86
|
+
* pour l'audience alors que le présentateur la croyait close. Le jeton est persisté en localStorage :
|
|
87
|
+
* ce n'était pas une course étroite, c'était une porte ouverte à volonté.
|
|
88
|
+
*
|
|
89
|
+
* ⚠️ POURQUOI RÉVOQUER LE JETON PLUTÔT QUE REFUSER TOUTE ÉCRITURE SUR « active=false » — la règle
|
|
90
|
+
* générale que l'audit proposait. Parce que « active:false » recouvre DEUX situations qui n'ont rien
|
|
91
|
+
* à voir :
|
|
92
|
+
*
|
|
93
|
+
* 1. une fin DÉCIDÉE (ces deux chemins) — plus rien ne doit piloter ;
|
|
94
|
+
* 2. une péremption CONSTATÉE (listActivePresentations, 3 min sans battement) — le portable du
|
|
95
|
+
* présentateur a dormi, et sa page suivante doit le remettre en ligne. La « résurrection » EST
|
|
96
|
+
* la reprise.
|
|
97
|
+
*
|
|
98
|
+
* Une règle qui refuse les deux condamnerait un présentateur ANONYME à ne jamais revenir : la
|
|
99
|
+
* reprise exige la propriété, et « present-start » n'exige aucune session. On distingue donc les
|
|
100
|
+
* deux cas par ce qui les sépare vraiment — la décision — plutôt que par l'état qu'ils partagent.
|
|
101
|
+
*
|
|
102
|
+
* Le propriétaire garde « present-reclaim » : reprendre une présentation close est une décision, et
|
|
103
|
+
* elle regénère un jeton frais.
|
|
104
|
+
*/
|
|
105
|
+
const CLOTURE = { active: false, control_hash: null };
|
|
106
|
+
|
|
80
107
|
// Clôture sans control_token → utilisée par le panneau « Présentations en direct » pour terminer à distance
|
|
81
108
|
// une présentation dont on a perdu l'onglet. Autorisée au PROPRIÉTAIRE (email du JWT) OU à un ADMIN (modération).
|
|
82
109
|
async function endPresentationByOwner(slug, email, isAdmin) {
|
|
@@ -84,7 +111,7 @@ async function endPresentationByOwner(slug, email, isAdmin) {
|
|
|
84
111
|
const row = await getPresentation(slug);
|
|
85
112
|
if (!row) return { ok: false, status: 404 };
|
|
86
113
|
if (!isAdmin && (!row.owner_email || row.owner_email !== lc(email))) return { ok: false, status: 403 };
|
|
87
|
-
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: {
|
|
114
|
+
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: { ...CLOTURE, updated_at: new Date().toISOString() } });
|
|
88
115
|
return { ok: true };
|
|
89
116
|
}
|
|
90
117
|
|
|
@@ -121,7 +148,7 @@ async function endPresentation(slug, control) {
|
|
|
121
148
|
const row = await getPresentation(slug);
|
|
122
149
|
if (!row) return { ok: false, status: 404 };
|
|
123
150
|
if (!tokenMatches(control, row.control_hash)) return { ok: false, status: 403 };
|
|
124
|
-
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: {
|
|
151
|
+
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: { ...CLOTURE, updated_at: new Date().toISOString() } });
|
|
125
152
|
return { ok: true };
|
|
126
153
|
}
|
|
127
154
|
|
|
@@ -349,6 +376,7 @@ async function switchPresentationDoc(slug, email, isAdmin, { fileUrl, fileName,
|
|
|
349
376
|
if (!slug || !fileUrl) return { ok: false, status: 400 };
|
|
350
377
|
const row = await getPresentation(slug);
|
|
351
378
|
if (!row) return { ok: false, status: 404 };
|
|
379
|
+
if (estClose(row)) return { ok: false, status: 409, error: "ended" };
|
|
352
380
|
if (!isAdmin && (!row.owner_email || row.owner_email !== lc(email))) return { ok: false, status: 403 };
|
|
353
381
|
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: {
|
|
354
382
|
file_url: String(fileUrl), file_name: fileName || null, doc_title: docTitle || null, doc_id: docId ? String(docId) : null,
|
|
@@ -364,6 +392,22 @@ async function switchPresentationDoc(slug, email, isAdmin, { fileUrl, fileName,
|
|
|
364
392
|
// finissaient par diverger, et une audience qui ne voit pas la bonne carte n'émet aucune erreur.
|
|
365
393
|
const { sanitizeContent } = require("./shared.generated.js");
|
|
366
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Une présentation close ne se pilote plus par le chemin PROPRIÉTAIRE.
|
|
397
|
+
*
|
|
398
|
+
* ⚠️ La révocation du jeton ferme le pilotage de qui détient un « control_token ». Elle ne ferme pas
|
|
399
|
+
* les deux actions qu'un propriétaire exerce avec sa seule session — changer le contenu affiché, et
|
|
400
|
+
* changer le document montré. Sans cette garde, celui qui vient de terminer sa présentation la
|
|
401
|
+
* remettrait en ligne en déplaçant une carte.
|
|
402
|
+
*
|
|
403
|
+
* ⚠️ Ici, et pas sur les chemins à jeton : un propriétaire dispose de « present-reclaim » pour
|
|
404
|
+
* rouvrir délibérément, ce qui n'est pas le cas d'un présentateur anonyme dont la présentation a
|
|
405
|
+
* simplement été marquée périmée.
|
|
406
|
+
*/
|
|
407
|
+
function estClose(row) {
|
|
408
|
+
return row && row.active === false;
|
|
409
|
+
}
|
|
410
|
+
|
|
367
411
|
/**
|
|
368
412
|
* Ce que la présentation AFFICHE : carte, Street View, ou rien.
|
|
369
413
|
*
|
|
@@ -390,6 +434,8 @@ async function setPresentationContent(slug, email, isAdmin, content, control) {
|
|
|
390
434
|
const pilote = control && tokenMatches(control, row.control_hash);
|
|
391
435
|
const proprietaire = isAdmin || (row.owner_email && row.owner_email === lc(email));
|
|
392
436
|
if (!pilote && !proprietaire) return { ok: false, status: 403 };
|
|
437
|
+
// Le pilote a déjà été filtré par la révocation du jeton ; c'est le propriétaire qu'on arrête ici.
|
|
438
|
+
if (!pilote && estClose(row)) return { ok: false, status: 409, error: "ended" };
|
|
393
439
|
await PLAYER.db.request(`doc_presentations?slug=eq.${enc(slug)}`, { method: "PATCH", headers: { Prefer: "return=minimal" }, body: { content: sanitizeContent(content), active: true, last_seen: new Date().toISOString(), updated_at: new Date().toISOString() } });
|
|
394
440
|
return { ok: true };
|
|
395
441
|
}
|
|
@@ -32,6 +32,7 @@ __export(shared_exports, {
|
|
|
32
32
|
PRESENT_QUOTA_PER_HOUR: () => PRESENT_QUOTA_PER_HOUR,
|
|
33
33
|
PRESENT_READS_PER_HOUR: () => PRESENT_READS_PER_HOUR,
|
|
34
34
|
PRESENT_RESYNC_MS: () => PRESENT_RESYNC_MS,
|
|
35
|
+
PRESENT_WRITE_TIMEOUT_MS: () => PRESENT_WRITE_TIMEOUT_MS,
|
|
35
36
|
READERS_PER_EGRESS: () => READERS_PER_EGRESS,
|
|
36
37
|
RESYNC_READS_PER_HOUR: () => RESYNC_READS_PER_HOUR,
|
|
37
38
|
SESSION_IDLE_MS: () => SESSION_IDLE_MS,
|
|
@@ -105,6 +106,7 @@ var RESYNC_READS_PER_HOUR = Math.ceil(36e5 / PRESENT_RESYNC_MS);
|
|
|
105
106
|
var PRESENTER_ACTIONS_PER_HOUR = 720;
|
|
106
107
|
var PRESENT_READS_PER_HOUR = RESYNC_READS_PER_HOUR + PRESENTER_ACTIONS_PER_HOUR;
|
|
107
108
|
var PRESENT_QUOTA_PER_HOUR = PRESENT_READS_PER_HOUR * READERS_PER_EGRESS;
|
|
109
|
+
var PRESENT_WRITE_TIMEOUT_MS = 1e4;
|
|
108
110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
109
111
|
0 && (module.exports = {
|
|
110
112
|
DEFAULT_CENTER,
|
|
@@ -114,6 +116,7 @@ var PRESENT_QUOTA_PER_HOUR = PRESENT_READS_PER_HOUR * READERS_PER_EGRESS;
|
|
|
114
116
|
PRESENT_QUOTA_PER_HOUR,
|
|
115
117
|
PRESENT_READS_PER_HOUR,
|
|
116
118
|
PRESENT_RESYNC_MS,
|
|
119
|
+
PRESENT_WRITE_TIMEOUT_MS,
|
|
117
120
|
READERS_PER_EGRESS,
|
|
118
121
|
RESYNC_READS_PER_HOUR,
|
|
119
122
|
SESSION_IDLE_MS,
|