diff --git a/reference-game-cwg.html b/reference-game-cwg.html index 2792ebf..ef884c5 100644 --- a/reference-game-cwg.html +++ b/reference-game-cwg.html @@ -11,11 +11,14 @@ - - - - - + + + + + - - - - - + + + + + ` tags in `reference-game-hawkins.html` and + `reference-game-cwg.html` back to + `https://cdn.jsdelivr.net/npm/@jspsych-multiplayer/@/dist/index.browser.min.js`. +2. Delete `vendor/`. + +Do not edit these files by hand — rebuild from source instead. diff --git a/vendor/adapter-multiplayer-local.js b/vendor/adapter-multiplayer-local.js new file mode 100644 index 0000000..e7a695a --- /dev/null +++ b/vendor/adapter-multiplayer-local.js @@ -0,0 +1,2 @@ +var jsPsychAdapterMultiplayerLocal=function(){"use strict";function l(s,t){return`${s}:${t}:`}function u(s,t,e){return l(s,t)+e}function d(s,t,e){const n=l(s,t);return e.startsWith(n)?e.slice(n.length):null}function f(s,t,e,n){const i=s.getItem(u(t,e,n));if(i!==null)try{return JSON.parse(i)}catch(r){return}}function p(s,t,e){const n={},i=[];for(let r=0;r{for(const a of e)a()};let i=null;typeof BroadcastChannel!="undefined"&&(i=new BroadcastChannel(s),i.onmessage=()=>n());const r=typeof window!="undefined"&&typeof window.addEventListener=="function",o=a=>{(a.key===null||a.key.startsWith(t))&&n()};return r&&window.addEventListener("storage",o),{post(){i&&i.postMessage(0)},onChange(a){e.add(a)},close(){e.clear(),i&&(i.onmessage=null,i.close(),i=null),r&&window.removeEventListener("storage",o)}}}const h="mp_session",w="mp";class I{constructor(t={}){this.signal=null,this.connected=!1,this.subscribers=new Set,this.notifyScheduled=!1;var e,n,i,r;if(this.keyPrefix=(e=t.keyPrefix)!=null?e:w,this.storage=(n=t.storage)!=null?n:S(),this.sessionId=(i=t.sessionId)!=null?i:v(),this.participantId=(r=t.participantId)!=null?r:P(this.keyPrefix,this.sessionId,t.persistParticipant),this.sessionId.includes(":"))throw new Error(`LocalAdapter: sessionId must not contain ":" (got "${this.sessionId}") \u2014 it is the reserved storage-key namespace separator.`);if(this.participantId.includes(":"))throw new Error(`LocalAdapter: participantId must not contain ":" (got "${this.participantId}") \u2014 it is the reserved storage-key namespace separator.`);const o=t.signal;this.createSignal=o?()=>o:()=>m(`${this.keyPrefix}:${this.sessionId}`,l(this.keyPrefix,this.sessionId))}connect(){return this.connected||(this.connected=!0,this.signal=this.createSignal(),this.signal.onChange(()=>this.scheduleNotify())),Promise.resolve()}push(t){var e;if(!this.connected)return Promise.reject(new Error("LocalAdapter: push() called before connect(); call connect() first."));try{g(this.storage,this.keyPrefix,this.sessionId,this.participantId,t)}catch(n){return Promise.reject(n)}return this.scheduleNotify(),(e=this.signal)==null||e.post(),Promise.resolve()}getAll(){return p(this.storage,this.keyPrefix,this.sessionId)}get(t){return f(this.storage,this.keyPrefix,this.sessionId,t)}subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}disconnect(){var t,e;return this.connected&&(y(this.storage,this.keyPrefix,this.sessionId,this.participantId),(t=this.signal)==null||t.post()),(e=this.signal)==null||e.close(),this.signal=null,this.subscribers.clear(),this.connected=!1,Promise.resolve()}scheduleNotify(){this.notifyScheduled||(this.notifyScheduled=!0,queueMicrotask(()=>{this.notifyScheduled=!1;const t=this.getAll();for(const e of[...this.subscribers])try{e(t)}catch(n){console.error("LocalAdapter: a group-session subscriber threw",n)}}))}}function S(){if(typeof localStorage=="undefined")throw new Error("LocalAdapter: localStorage is not available in this environment. Serve the experiment over http(s) (e.g. `npx http-server`) rather than opening it from a file:// URL, or pass a `storage` option.");return localStorage}function v(){var s,t;if(typeof window=="undefined"||typeof window.location=="undefined")return c();try{const e=new URL(window.location.href),n=e.searchParams.get(h);if(n)return n;const i=c();return e.searchParams.set(h,i),(t=(s=window.history)==null?void 0:s.replaceState)==null||t.call(s,window.history.state,"",e.toString()),i}catch(e){return c()}}function P(s,t,e){if(!e||typeof sessionStorage=="undefined")return c();const n=`${s}:participant:${t}`;try{const i=sessionStorage.getItem(n);if(i)return i;const r=c();return sessionStorage.setItem(n,r),r}catch(i){return c()}}return I}(); +//# sourceMappingURL=index.browser.min.js.map diff --git a/vendor/plugin-multiplayer-reference-game.js b/vendor/plugin-multiplayer-reference-game.js new file mode 100644 index 0000000..447c5bd --- /dev/null +++ b/vendor/plugin-multiplayer-reference-game.js @@ -0,0 +1,127 @@ +var jsPsychMultiplayerReferenceGame=function(i){"use strict";var dt="0.1.0",mt=Object.defineProperty,ft=Object.defineProperties,pt=Object.getOwnPropertyDescriptors,Ne=Object.getOwnPropertySymbols,gt=Object.prototype.hasOwnProperty,yt=Object.prototype.propertyIsEnumerable,Se=(r,t,e)=>t in r?mt(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ie=(r,t)=>{for(var e in t||(t={}))gt.call(t,e)&&Se(r,e,t[e]);if(Ne)for(var e of Ne(t))yt.call(t,e)&&Se(r,e,t[e]);return r},bt=(r,t)=>ft(r,pt(t));function Ce(r,t){return`${r}#${t}`}function ht(r,t){const e=r==null?void 0:r[t];return Array.isArray(e)?e.filter(vt):[]}function vt(r){if(typeof r!="object"||r===null)return!1;const t=r;return typeof t.senderId=="string"&&typeof t.seq=="number"&&typeof t.text=="string"&&typeof t.ts=="number"&&(t.round===void 0||typeof t.round=="number")}function H(r,t){const e=[];for(const c of Object.keys(r))for(const f of ht(r[c],t))e.push(bt(Ie({},f),{id:Ce(f.senderId,f.seq)}));e.sort((c,f)=>c.ts-f.ts||$t(c.senderId,f.senderId)||c.seq-f.seq);const u=new Set,s=[];for(const c of e)u.has(c.id)||(u.add(c.id),s.push(c));return s}function _t(r,t,e,u,s,c){return[...r,Ie({id:Ce(e,u),senderId:e,seq:u,text:t,ts:s},c!=null&&{round:c})]}function $t(r,t){return rt?1:0}function Tt(r){var t;const e=r,u=(t=e.multiplayer)!=null?t:e.pluginAPI;if(!u||typeof u.getAll!="function")throw new Error("No multiplayer API found on the jsPsych instance. This plugin needs jsPsych core with multiplayer support (jsPsych#3694); see https://multiplayer.jspsych.org.");return u}var xt=Object.defineProperty,wt=Object.defineProperties,Pt=Object.getOwnPropertyDescriptors,ke=Object.getOwnPropertySymbols,Ot=Object.prototype.hasOwnProperty,Nt=Object.prototype.propertyIsEnumerable,Ee=(r,t,e)=>t in r?xt(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,re=(r,t)=>{for(var e in t||(t={}))Ot.call(t,e)&&Ee(r,e,t[e]);if(ke)for(var e of ke(t))Nt.call(t,e)&&Ee(r,e,t[e]);return r},Le=(r,t)=>wt(r,Pt(t));function St(r){let t=1779033703^r.length;for(let e=0;e>>19;return t>>>0}function It(r){return()=>{r|=0,r=r+1831565813|0;let t=Math.imul(r^r>>>15,1|r);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296}}function B(r,t){const e=It(St(t)),u=[...r];for(let s=u.length-1;s>0;s--){const c=Math.floor(e()*(s+1));[u[s],u[c]]=[u[c],u[s]]}return u}function Ct(r,t){if(r.length!==t.length)return!1;for(let e=0;e1&&Ct(v,_);)_=B(r,`${c}#${t}#${y}#${q++}`);return{[f]:v,[y]:_}}function Ae(r,t,e,u,s,c,f){const y=c!=null?c:"";switch(e){case"shared":return B(r,`${y}#${u}#shared`);case"matcher_only":return t==="matcher"?B(r,`${y}#${u}#${s}`):[...r];case"independent":default:return f!=null&&f!==s?kt(r,u,s,f,c)[s]:B(r,`${y}#${u}#${s}`)}}function Et(r,t,e,u){const s=re({},r),c=[];if(e===null)return r[t]!=null&&(delete s[t],c.push({t:u,action:"clear",slot:t,object_id:null})),{next:s,events:c};for(const[f,y]of Object.entries(r)){const v=Number(f);y===e&&v!==t&&(delete s[v],c.push({t:u,action:"clear",slot:v,object_id:null}))}return c.push({t:u,action:r[t]==null?"assign":"reassign",slot:t,object_id:e}),s[t]=e,{next:s,events:c}}function ne(r,t){for(let e=1;e<=t;e++)if(typeof r[e]!="string")return!1;return!0}function Lt(r,t,e=0){for(let u=1;u<=t;u++){const s=(e-1+u)%t+1;if(typeof r[s]!="string")return s}return null}function qe(r,t,e={}){var u,s;const c=t.length,f=(u=e.ordered)!=null?u:c>1,y=(s=e.scoring)!=null?s:"per_slot",v=At(r,t,f);let _;if(typeof y=="function"){const q=Number(y(r,t));_=Number.isFinite(q)?Math.min(Math.max(Math.round(q),0),c):0}else y==="all_or_nothing"?_=v===c?c:0:_=v;return{nCorrect:_,nTargets:c,accuracy:c>0?_/c:0,correct:c>0&&v===c}}function At(r,t,e){if(e){let f=0;return t.forEach((y,v)=>{r[v+1]===y&&f++}),f}const u=new Set(t),s=new Set(Object.values(r));let c=0;for(const f of s)u.has(f)&&c++;return Math.min(c,t.length)}function qt(r,t,e){const u=r==null?void 0:r[t];if(typeof u!="object"||u===null||Array.isArray(u))return;const s=u[String(e)];if(!(typeof s!="object"||s===null||Array.isArray(s)))return s}function Mt(r,t,e,u){const s=r[t],c=typeof s=="object"&&s!==null&&!Array.isArray(s)?s:{};return Le(re({},r),{[t]:Le(re({},c),{[e]:u})})}function Me(r,t,e,u){const s=qt(r[t],e,u),c=s==null?void 0:s.assignment;if(typeof c!="object"||c===null||Array.isArray(c))return;const f={};for(const[y,v]of Object.entries(c)){const _=Number(y);Number.isInteger(_)&&_>=1&&typeof v=="string"&&(f[_]=v)}return{assignment:f,rt:typeof s.rt=="number"?s.rt:void 0,timed_out:s.timed_out===!0}}function Rt(r,t){const e=r==null?void 0:r[t];if(typeof e!="object"||e===null||Array.isArray(e))return 0;let u=0;for(const s of Object.values(e))if(typeof s=="object"&&s!==null){const c=s.n_correct;typeof c=="number"&&Number.isFinite(c)&&(u+=c)}return u}var Gt=Object.defineProperty,Bt=Object.defineProperties,Dt=Object.getOwnPropertyDescriptors,Re=Object.getOwnPropertySymbols,zt=Object.prototype.hasOwnProperty,Ft=Object.prototype.propertyIsEnumerable,Ge=(r,t,e)=>t in r?Gt(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,D=(r,t)=>{for(var e in t||(t={}))zt.call(t,e)&&Ge(r,e,t[e]);if(Re)for(var e of Re(t))Ft.call(t,e)&&Ge(r,e,t[e]);return r},ae=(r,t)=>Bt(r,Dt(t));const Ht={name:"multiplayer-reference-game",version:dt,parameters:{stimuli:{type:i.ParameterType.OBJECT,array:!0,default:void 0},columns:{type:i.ParameterType.INT,default:6},rows:{type:i.ParameterType.INT,default:null},cell_size:{type:i.ParameterType.INT,default:null},scramble_mode:{type:i.ParameterType.STRING,default:"independent"},seed:{type:i.ParameterType.STRING,default:null},show_labels:{type:i.ParameterType.BOOL,default:!1},targets:{type:i.ParameterType.STRING,array:!0,default:void 0},ordered:{type:i.ParameterType.BOOL,default:null},scoring:{type:i.ParameterType.FUNCTION,default:"per_slot"},role:{type:i.ParameterType.STRING,default:void 0},role_labels:{type:i.ParameterType.OBJECT,default:{director:"Director",matcher:"Matcher"}},director_can_select:{type:i.ParameterType.BOOL,default:!1},reveal_target_to:{type:i.ParameterType.STRING,default:"director"},chat_enabled:{type:i.ParameterType.BOOL,default:!0},chat_role:{type:i.ParameterType.STRING,default:"both"},max_messages:{type:i.ParameterType.INT,default:null},max_length:{type:i.ParameterType.INT,default:null},require_message_before_response:{type:i.ParameterType.BOOL,default:!1},placeholder:{type:i.ParameterType.STRING,default:"Type a message\u2026"},chat_persists:{type:i.ParameterType.BOOL,default:!1},chat_position:{type:i.ParameterType.STRING,default:"below"},response_mode:{type:i.ParameterType.STRING,default:null},auto_submit:{type:i.ParameterType.BOOL,default:null},submit_label:{type:i.ParameterType.STRING,default:"Submit"},allow_change:{type:i.ParameterType.BOOL,default:!0},selection_timeout:{type:i.ParameterType.INT,default:null},feedback:{type:i.ParameterType.BOOL,default:!0},feedback_content:{type:i.ParameterType.OBJECT,default:{reveal_target:!0,show_score:!0,show_partner_choice:!0}},feedback_to:{type:i.ParameterType.STRING,default:"both"},feedback_duration:{type:i.ParameterType.INT,default:3e3},show_running_score:{type:i.ParameterType.BOOL,default:!1},prompt:{type:i.ParameterType.FUNCTION,default:""},round:{type:i.ParameterType.INT,default:void 0},data_key:{type:i.ParameterType.STRING,default:"reference_game"},partner_id:{type:i.ParameterType.STRING,default:null},save_orders:{type:i.ParameterType.BOOL,default:!0},save_transcript:{type:i.ParameterType.BOOL,default:!0},save_group:{type:i.ParameterType.BOOL,default:!1},save_interaction_history:{type:i.ParameterType.BOOL,default:!1},round_timeout:{type:i.ParameterType.INT,default:null}},data:{role:{type:i.ParameterType.STRING,default:void 0},round:{type:i.ParameterType.INT,default:void 0},targets:{type:i.ParameterType.STRING,array:!0,default:void 0},assignment:{type:i.ParameterType.COMPLEX,default:void 0},n_correct:{type:i.ParameterType.INT,default:void 0},n_targets:{type:i.ParameterType.INT,default:void 0},accuracy:{type:i.ParameterType.FLOAT,default:void 0},correct:{type:i.ParameterType.BOOL,default:void 0},rt:{type:i.ParameterType.INT,default:void 0},chat_transcript:{type:i.ParameterType.OBJECT,array:!0,default:void 0},message_count:{type:i.ParameterType.INT,default:void 0},messages_sent:{type:i.ParameterType.INT,default:void 0},my_order:{type:i.ParameterType.STRING,array:!0,default:void 0},partner_order:{type:i.ParameterType.STRING,array:!0,default:void 0},ended_by:{type:i.ParameterType.STRING,default:void 0},interaction_history:{type:i.ParameterType.OBJECT,array:!0,default:void 0},group:{type:i.ParameterType.OBJECT,default:void 0}},citations:{apa:"Hawkins, R. D., Frank, M. C., & Goodman, N. D. (2020). Characterizing the Dynamics of Learning in Repeated Reference Games. Cognitive Science, 44(6), e12845. https://doi.org/10.1111/cogs.12845 ",bibtex:"@article{Hawkins2020Characterizing, author = {Hawkins, Robert D. and Frank, Michael C. and Goodman, Noah D.}, journal = {Cognitive Science}, doi = {10.1111/cogs.12845}, number = {6}, year = {2020}, pages = {e12845}, title = {Characterizing the {Dynamics} of {Learning} in {Repeated} {Reference} {Games}}, volume = {44}, } "}},n="jspsych-multiplayer-reference-game";class Be{constructor(t){this.jsPsych=t}trial(t,e){var u,s,c,f,y,v,_,q,De,ze,Fe,He,Je;const T=Tt(this.jsPsych),b=T.participantId,le=this.jsPsych.pluginAPI,J=(u=e.stimuli)!=null?u:[],O=[...(s=e.targets)!=null?s:[]],U=J.map(l=>l==null?void 0:l.id),x=e.role,P=e.round,z=e.data_key,h=O.length;if(!Array.isArray(J)||J.length===0||U.some(l=>typeof l!="string"))throw new Error("multiplayer-reference-game: `stimuli` is required and every entry needs a string `id`.");if(h===0)throw new Error("multiplayer-reference-game: `targets` is required and must not be empty.");const Ue=new Set(U);if(Ue.size!==U.length)throw new Error("multiplayer-reference-game: `stimuli` ids must be unique.");for(const l of O)if(!Ue.has(l))throw new Error(`multiplayer-reference-game: target "${l}" does not match any stimulus id.`);if(new Set(O).size!==O.length)throw new Error("multiplayer-reference-game: `targets` must not contain duplicates.");if(x!=="director"&&x!=="matcher")throw new Error(`multiplayer-reference-game: \`role\` must be "director" or "matcher" (got ${JSON.stringify(e.role)}). Typically: role: () => jsPsychMultiplayerRole.getMyRole().`);if(typeof P!="number"||!Number.isFinite(P))throw new Error("multiplayer-reference-game: `round` is required and must be a number. Use a UNIQUE index per round, e.g. round: jsPsych.timelineVariable('round').");if(Me(T.getAll(),b,z,P))throw new Error(`multiplayer-reference-game: round ${P} already has a submitted assignment in this participant's slot \u2014 round indices must be unique across the timeline (did \`round\` default or repeat?).`);const je=(c=e.scramble_mode)!=null?c:"independent",oe=(f=e.ordered)!=null?f:h>1,Ye=e.scoring,ie=(y=e.response_mode)!=null?y:h===1?"click":"assign_slots",Qe=(v=e.auto_submit)!=null?v:h===1,Ve=D({director:"Director",matcher:"Matcher"},(_=e.role_labels)!=null?_:{}),Ut=e.reveal_target_to==="both"||e.reveal_target_to===x,j=!!e.chat_enabled,se=j&&(e.chat_role==="both"||e.chat_role===x),jt=!!e.feedback&&(e.feedback_to==="both"||e.feedback_to===x),ce=D({reveal_target:!0,show_score:!0,show_partner_choice:!0},(q=e.feedback_content)!=null?q:{}),M=e.chat_persists?`${z}_chat`:`${z}_chat_r${P}`,We=(De=e.seed)!=null?De:null,Yt=e.rows!=null&&e.rows>0?Math.ceil(J.length/e.rows):(ze=e.columns)!=null?ze:6,Ke=typeof e.round_timeout=="number"&&e.round_timeout>0,Qt=typeof e.selection_timeout=="number"&&e.selection_timeout>0;!Ke&&!Qt&&console.warn("multiplayer-reference-game: no `round_timeout` or `selection_timeout` set \u2014 if the partner never responds (or disconnects) this trial cannot end. Set `round_timeout` to bound the round."),e.require_message_before_response&&!j&&console.warn("multiplayer-reference-game: `require_message_before_response` needs a chat channel but `chat_enabled` is false \u2014 ignoring it (gating with no way to message would deadlock the matcher).");const ue=e.partner_id;let w;if(ue!=null)w=ue;else{const l=Object.keys(T.getAll()).filter(a=>a!==b);if(l.length>1)throw new Error(`multiplayer-reference-game: cannot auto-detect the partner \u2014 ${l.length} other participants are present. Set \`partner_id\` explicitly (e.g. from plugin-multiplayer-role) so the trial does not guess.`);w=l.length===1?l[0]:null}const Xe=Ae(U,x,je,P,b,We,w),Ze=`${n}-styles`;if(!document.getElementById(Ze)){const l=document.createElement("style");l.id=Ze,l.textContent=` + .${n} { max-width: 60em; margin: 0 auto; } + .${n}-main { display: flex; flex-direction: column; gap: 1em; } + .${n}.is-beside .${n}-main { flex-direction: row; align-items: flex-start; justify-content: center; } + .${n}.is-beside .${n}-chat { width: 18em; flex: none; } + .${n}-role { font-weight: bold; margin: 0.5em 0; } + .${n}-grid { + display: grid; + gap: 8px; + justify-content: center; + margin: 0 auto; + } + .${n}-cell { + position: relative; + border: 2px solid #ccc; + border-radius: 6px; + padding: 4px; + cursor: pointer; + background: #fff; + box-sizing: border-box; + user-select: none; + } + .${n}-cell-content { pointer-events: none; } + .${n}-cell-content img, .${n}-cell-content svg { max-width: 100%; max-height: 100%; display: block; margin: 0 auto; } + .${n}-cell-label { font-size: 0.75em; color: #555; text-align: center; pointer-events: none; } + .${n}-cell.is-target { border-color: #e8a417; box-shadow: 0 0 0 2px rgba(232, 164, 23, 0.35); } + .${n}-cell.is-assigned { border-color: #1971c2; box-shadow: 0 0 0 2px rgba(25, 113, 194, 0.3); } + .${n}-cell.is-selected { border-color: #7048e8; } + .${n}-cell.is-correct { border-color: #2b8a3e; box-shadow: 0 0 0 2px rgba(43, 138, 62, 0.35); } + .${n}-cell.is-wrong { border-color: #c92a2a; box-shadow: 0 0 0 2px rgba(201, 42, 42, 0.35); } + .${n}-badge { + position: absolute; + top: -8px; + right: -8px; + min-width: 18px; + height: 18px; + line-height: 18px; + padding: 0 3px; + border-radius: 9px; + background: #1971c2; + color: #fff; + font-size: 12px; + font-weight: bold; + text-align: center; + pointer-events: none; + } + .${n}-cell.is-target > .${n}-badge { background: #e8a417; } + .${n}-mark { + position: absolute; + top: -8px; + left: -8px; + min-width: 18px; + height: 18px; + line-height: 18px; + padding: 0 3px; + border-radius: 9px; + background: #1971c2; + color: #fff; + font-size: 12px; + font-weight: bold; + text-align: center; + pointer-events: none; + } + .${n}-mark.is-correct { background: #2b8a3e; } + .${n}-mark.is-wrong { background: #c92a2a; } + .${n}-slots { display: flex; gap: 6px; justify-content: center; margin: 0.75em 0; flex-wrap: wrap; } + .${n}-slot { + min-width: 2.2em; + padding: 4px 8px; + border: 2px solid #ccc; + border-radius: 6px; + background: #fff; + cursor: pointer; + font: inherit; + } + .${n}-slot.is-active { border-color: #1971c2; box-shadow: 0 0 0 2px rgba(25, 113, 194, 0.3); } + .${n}-slot.is-filled { background: #e7f0fa; } + .${n}-controls { margin: 0.5em 0; } + .${n}-submit, .${n}-continue { padding: 6px 18px; font: inherit; cursor: pointer; } + .${n}-submit:disabled { cursor: default; opacity: 0.6; } + .${n}-status { color: #555; min-height: 1.2em; margin: 0.4em 0; } + .${n}-feedback { font-weight: bold; min-height: 1.2em; margin: 0.4em 0; } + .${n}-feedback.is-correct { color: #2b8a3e; } + .${n}-feedback.is-wrong { color: #c92a2a; } + .${n}-chat-log { + max-height: 14em; + min-height: 6em; + margin: 0 auto 0.5em; + padding: 0.5em 0.75em; + overflow-y: auto; + text-align: left; + border: 1px solid #ccc; + border-radius: 4px; + } + .${n}-chat-message { display: block; margin-bottom: 0.4em; } + .${n}-chat-sender { font-weight: bold; margin-right: 0.4em; } + .${n}-chat-sender::after { content: ":"; } + .${n}-chat-message.is-self .${n}-chat-sender { color: #2a6; } + .${n}-chat-form { display: flex; gap: 0.5em; } + .${n}-chat-input { flex: 1; font: inherit; } + .${n}-chat-error { color: #c00; font-size: 0.9em; margin-top: 0.3em; } + .${n}-gate-hint { color: #a60; font-size: 0.9em; margin-bottom: 0.4em; font-style: italic; } + `,document.head.appendChild(l)}const X=e.prompt,et=String(typeof X=="function"?X(x):X!=null?X:"");t.innerHTML=` +
+ ${et?`
${et}
`:""} +
+
+
+
+ + +
+
+
+ ${j?`
+
+
+ + +
+
`:""} +
+
`;const Vt=t.querySelector(`.${n}-role`),k=t.querySelector(`.${n}-grid`),Y=t.querySelector(`.${n}-slots`),Wt=t.querySelector(`.${n}-controls`),Q=t.querySelector(`.${n}-submit`),de=t.querySelector(`.${n}-status`),me=t.querySelector(`.${n}-feedback`),R=t.querySelector(`.${n}-chat-log`),E=t.querySelector(`.${n}-chat-form`),G=t.querySelector(`.${n}-chat-input`);Vt.textContent=`You are the ${Ve[x]}.`,Q.textContent=e.submit_label,k.style.gridTemplateColumns=`repeat(${Yt}, ${e.cell_size!=null?`${e.cell_size}px`:"minmax(3em, 6em)"})`;const F=new Map;for(const l of Xe){const a=J.find(g=>g.id===l),o=document.createElement("div");o.className=`${n}-cell`,o.dataset.objectId=l,o.setAttribute("role","button"),o.tabIndex=0;const d=document.createElement("div");if(d.className=`${n}-cell-content`,typeof a.src=="string"&&a.src!==""){const g=document.createElement("img");g.src=a.src,g.alt=(Fe=a.label)!=null?Fe:a.id,d.appendChild(g)}else typeof a.html=="string"&&a.html!==""?d.innerHTML=a.html:d.textContent=(He=a.label)!=null?He:a.id;o.appendChild(d);const m=document.createElement("span");m.className=`${n}-badge`,m.hidden=!0,o.appendChild(m);const p=document.createElement("span");if(p.className=`${n}-mark`,p.hidden=!0,o.appendChild(p),e.show_labels){const g=document.createElement("div");g.className=`${n}-cell-label`,g.textContent=(Je=a.label)!=null?Je:a.id,o.appendChild(g)}k.appendChild(o),F.set(l,o)}const fe=l=>l.querySelector(`.${n}-badge`),pe=l=>l.querySelector(`.${n}-mark`);Ut&&O.forEach((l,a)=>{const o=F.get(l);if(o.classList.add("is-target"),h>1){const d=fe(o);d.textContent=String(a+1),d.hidden=!1}});const N=x==="matcher";if(N&&ie==="assign_slots"){Y.hidden=!1;for(let l=1;l<=h;l++){const a=document.createElement("button");a.type="button",a.className=`${n}-slot`,a.dataset.slot=String(l),a.textContent=String(l),Y.appendChild(a)}}N&&!Qe&&(Wt.hidden=!1),de.textContent=x==="director"?"Describe the highlighted object"+(h>1?"s, in badge order, ":" ")+"to your partner.":h>1?"Click a numbered slot, then the object your partner describes for it. Fill every slot, then submit.":"Click the object your partner describes.";const Kt=performance.now();let S=!1,L=!1,V=!1,ge=!1,$={},C=1;const ye=[];let A=null,Z=null,tt=null,ee="submit",be=null,he=null,W=null,K=null;const rt=()=>{var l;return H({[b]:(l=T.get(b))!=null?l:{}},M).filter(a=>a.senderId===b)};let Xt=rt().reduce((l,a)=>Math.max(l,a.seq),-1)+1;const Zt=l=>l===b?"You":w!=null&&l===w?Ve[x==="director"?"matcher":"director"]:l;function nt(l){ge||!e.require_message_before_response||!j||!H(l,M).some(a=>(w!=null?a.senderId===w:a.senderId!==b)&&(a.round===P||!e.chat_persists&&a.round==null))||(ge=!0,ot(!1))}function ve(l){if(!R)return;const a=H(l,M),o=R.scrollHeight-R.scrollTop-R.clientHeight<4;R.replaceChildren(...a.map(d=>{const m=document.createElement("div");m.className=`${n}-chat-message`,d.senderId===b&&m.classList.add("is-self");const p=document.createElement("span");p.className=`${n}-chat-sender`,p.textContent=Zt(d.senderId);const g=document.createElement("span");return g.className=`${n}-chat-text`,g.textContent=d.text,m.append(p,g),m})),o&&(R.scrollTop=R.scrollHeight)}const _e=()=>typeof e.max_messages=="number"&&e.max_messages>0&&rt().length>=e.max_messages,$e=()=>{if(!G||!E)return;const l=E.querySelector(`.${n}-chat-send`),a=!se||L||_e();G.disabled=a,l.disabled=a,se?_e()&&(G.placeholder="Message limit reached."):G.placeholder="You can only read messages."},at=l=>{var a;if(l.preventDefault(),S||!se||!G||_e())return;let o=G.value.trim();if(o==="")return;typeof e.max_length=="number"&&e.max_length>0&&(o=o.slice(0,e.max_length)),G.value="";const d=(a=T.get(b))!=null?a:{},m=H({[b]:d},M).filter(g=>g.senderId===b),p=_t(m,o,b,Xt++,Date.now(),P);ve(ae(D({},T.getAll()),{[b]:ae(D({},d),{[M]:p})})),$e(),T.push(ae(D({},d),{[M]:p})).catch(()=>lt("Couldn't send \u2014 please try again."))};function lt(l){let a=t.querySelector(`.${n}-chat-error`);a||(a=document.createElement("div"),a.className=`${n}-chat-error`,(E!=null?E:de).after(a)),a.textContent=l}function ot(l){let a=t.querySelector(`.${n}-gate-hint`);l?(a||(a=document.createElement("div"),a.className=`${n}-gate-hint`,a.setAttribute("role","status"),a.setAttribute("aria-live","polite"),k.before(a)),a.textContent="Wait for your partner's description before choosing."):a==null||a.remove()}const Te=()=>{var l;for(const[a,o]of F){const d=(l=Object.entries($).find(([,p])=>p===a))==null?void 0:l[0];o.classList.toggle("is-assigned",d!=null);const m=pe(o);d!=null&&h>1?(m.textContent=d,m.hidden=!1):m.hidden=!0}Y.querySelectorAll(`.${n}-slot`).forEach(a=>{const o=a,d=Number(o.dataset.slot);o.classList.toggle("is-active",d===C),o.classList.toggle("is-filled",typeof $[d]=="string")}),Q.disabled=!ne($,h)},xe=()=>Math.round(performance.now()-Kt),we=(l,a)=>{const{next:o,events:d}=Et($,l,a,xe());$=o,ye.push(...d)},Pe=l=>{var a;if(V||S||!N)return;V=!0;const o=xe(),d=qe($,O,{ordered:oe,scoring:Ye}),m=D({assignment:$,rt:o,n_correct:d.nCorrect,n_targets:h},l==="timeout"?{timed_out:!0}:{}),p=(a=T.get(b))!=null?a:{};T.push(Mt(p,z,P,m)).catch(()=>lt("Couldn't submit \u2014 connection trouble.")),ut({assignment:$,rt:o,timed_out:l==="timeout"})},Oe=l=>{var a,o;const d=(o=(a=l.target).closest)==null?void 0:o.call(a,`.${n}-cell`);if(!d||!k.contains(d)||S||L)return;const m=d.dataset.objectId;if(!N){e.director_can_select&&d.classList.toggle("is-selected");return}if(!V){if(e.require_message_before_response&&j&&!ge){ye.push({t:xe(),action:"gated_click",slot:ie==="click"?1:C,object_id:m}),ot(!0);return}if(ie==="click"){if(!e.allow_change&&$[1]!=null)return;we(1,$[1]===m?null:m)}else if($[C]===m){if(!e.allow_change)return;we(C,null)}else{const p=$[C]!=null||Object.values($).includes(m);if(!e.allow_change&&p)return;we(C,m);const g=Lt($,h,C);g!=null&&(C=g)}Te(),Qe&&ne($,h)&&Pe("submit")}},it=l=>{var a,o;const d=l.key;d!=="Enter"&&d!==" "||!((o=(a=l.target).closest)!=null&&o.call(a,`.${n}-cell`))||(l.preventDefault(),Oe(l))},st=l=>{var a,o;const d=(o=(a=l.target).closest)==null?void 0:o.call(a,`.${n}-slot`);!d||S||L||V||(C=Number(d.dataset.slot),Te())},ct=()=>{ne($,h)&&Pe("submit")};function er(l,a){for(const o of F.values()){const d=fe(o);d.hidden=!0,d.textContent="";const m=pe(o);m.hidden=!0,m.textContent="",m.classList.remove("is-correct","is-wrong")}if(ce.reveal_target&&O.forEach((o,d)=>{const m=F.get(o);if(m.classList.add("is-target"),h>1){const p=fe(m);p.textContent=String(d+1),p.hidden=!1}}),ce.show_partner_choice)for(const[o,d]of Object.entries(l.assignment)){const m=F.get(d);if(!m)continue;const p=Number(o),g=oe?O[p-1]===d:O.includes(d);if(m.classList.add(g?"is-correct":"is-wrong"),h>1){const I=pe(m);I.textContent=String(p),I.classList.add(g?"is-correct":"is-wrong"),I.hidden=!1}}if(ce.show_score){const o=h===1?a.correct?"Correct!":"Incorrect.":`Score: ${a.nCorrect} / ${a.nTargets}`,d=l.timed_out?" (time ran out)":"";let m="";if(e.show_running_score){const p=N?b:w;m=` \u2014 total so far: ${Rt(p?T.getAll()[p]:void 0,z)}`}me.textContent=`${o}${d}${m}`,me.classList.add(a.correct?"is-correct":"is-wrong")}de.textContent=""}function ut(l){if(L||S)return;if(L=!0,Z=l.assignment,A=qe(l.assignment,O,{ordered:oe,scoring:Ye}),ee=l.timed_out?"timeout":"submit",N&&(tt=typeof l.rt=="number"?l.rt:null),W!=null&&clearTimeout(W),K!=null&&clearTimeout(K),Q.disabled=!0,$e(),!jt){te(ee);return}er(l,A);const a=e.feedback_duration;if(typeof a=="number"&&a>0)he=le.setTimeout(()=>te(ee),a);else{const o=document.createElement("button");o.type="button",o.className=`${n}-continue`,o.textContent="Continue",o.addEventListener("click",()=>te(ee)),me.after(o)}}const te=l=>{var a,o,d,m;if(S)return;S=!0,be==null||be(),he!=null&&clearTimeout(he),W!=null&&clearTimeout(W),K!=null&&clearTimeout(K),k.removeEventListener("click",Oe),k.removeEventListener("keydown",it),Y.removeEventListener("click",st),Q.removeEventListener("click",ct),E==null||E.removeEventListener("submit",at);const p=T.getAll(),g=H(p,M),I={role:x,round:P,targets:[...O],assignment:Z==null?null:h===1?(a=Z[1])!=null?a:null:Z,n_correct:(o=A==null?void 0:A.nCorrect)!=null?o:null,n_targets:h,accuracy:(d=A==null?void 0:A.accuracy)!=null?d:null,correct:(m=A==null?void 0:A.correct)!=null?m:null,rt:tt,message_count:g.length,messages_sent:g.filter(tr=>tr.senderId===b).length,ended_by:l};e.save_transcript&&(I.chat_transcript=g),e.save_orders&&(I.my_order=Xe,I.partner_order=w?Ae(U,x==="director"?"matcher":"director",je,P,w,We,b):null),e.save_interaction_history&&N&&(I.interaction_history=ye),e.save_group&&(I.group=p),this.jsPsych.finishTrial(I)};k.addEventListener("click",Oe),k.addEventListener("keydown",it),Y.addEventListener("click",st),Q.addEventListener("click",ct),E==null||E.addEventListener("submit",at),N&&Te(),$e(),nt(T.getAll()),ve(T.getAll()),be=T.subscribe(l=>{if(!S){if(w==null&&ue==null){const a=Object.keys(l).filter(o=>o!==b);a.length===1&&(w=a[0])}try{nt(l)}catch(a){}try{ve(l)}catch(a){}try{if(!L){const a=N?b:w,o=a?Me(l,a,z,P):void 0;o&&ut(o)}}catch(a){}}}),N&&typeof e.selection_timeout=="number"&&e.selection_timeout>0&&(W=le.setTimeout(()=>{!V&&!L&&!S&&Pe("timeout")},e.selection_timeout)),Ke&&(K=le.setTimeout(()=>{!L&&!S&&te("timeout")},e.round_timeout))}}Be.info=Ht;function Jt(r){return String(r).replace(/&/g,"&").replace(/"/g,""").replace(//g,">")}return Be}(jsPsychModule); +//# sourceMappingURL=index.browser.min.js.map diff --git a/vendor/plugin-multiplayer-role.js b/vendor/plugin-multiplayer-role.js new file mode 100644 index 0000000..7954344 --- /dev/null +++ b/vendor/plugin-multiplayer-role.js @@ -0,0 +1,2 @@ +var jsPsychMultiplayerRole=function(a){"use strict";var F="0.1.0";const C="MultiplayerTimeoutError";function L(r){return r instanceof Error&&r.name===C}function $(r){var e;const t=r,i=(e=t.multiplayer)!=null?e:t.pluginAPI;if(!i||typeof i.getAll!="function")throw new Error("No multiplayer API found on the jsPsych instance. This plugin needs jsPsych core with multiplayer support (jsPsych#3694); see https://multiplayer.jspsych.org.");return i}function _(r){return Array.isArray(r)?[...r]:Object.entries(r).flatMap(([e,t])=>Array(t).fill(e))}function z(r){let e=1779033703^r.length;for(let t=0;t>>19;return e>>>0}function U(r,e){if(r<=0)return 0;const t=(e%r+r)%r;return t%2===0?t>>1:r-1-(t>>1)}function x(r){return()=>{r|=0,r=r+1831565813|0;let e=Math.imul(r^r>>>15,1|r);return e=e+Math.imul(e^e>>>7,61|e)^e,((e^e>>>14)>>>0)/4294967296}}function g(r,e){return re?1:0}function J(r,e,t){var i,p;const o=Object.keys(r).sort(g);if(e.rankBy)return[...o].sort((n,l)=>e.rankBy(r[l],l,t)-e.rankBy(r[n],n,t)||g(n,l));switch((i=e.strategy)!=null?i:"join_order"){case"join_order":return[...o].sort((n,l)=>{var s,u,d,y;return((u=(s=r[n])==null?void 0:s.joinedAt)!=null?u:0)-((y=(d=r[l])==null?void 0:d.joinedAt)!=null?y:0)||g(n,l)});case"rotate":{const n=[...o],l=n.length;if(l===0)return n;const s=e.balanced?U(l,t.round):(t.round%l+l)%l;return n.slice(s).concat(n.slice(0,s))}case"random":{const n=(p=e.seed)!=null?p:`${o.join("|")}#${t.round}`,l=x(z(n)),s=[...o];for(let u=s.length-1;u>0;u--){const d=Math.floor(l()*(u+1));[s[u],s[d]]=[s[d],s[u]]}return s}default:return o}}function O(r,e){var t,i;if(e.roles==null||Array.isArray(e.roles)&&e.roles.length===0)throw new Error('assignRoles: the `roles` option is required \u2014 pass an array like ["proposer","responder"] or a count map like { leader: 1, follower: 3 }.');if(!Array.isArray(e.roles)){const u=Object.entries(e.roles);if(u.length===0)throw new Error("assignRoles: `roles` is an empty object \u2014 declare at least one role, e.g. { leader: 1, follower: 3 } (an empty count map would send every participant to overflow).");for(const[d,y]of u)if(!Number.isInteger(y)||y<0)throw new Error(`assignRoles: the count for role "${d}" must be a non-negative integer, got ${y}.`)}const p=Object.keys(r).sort(g),o={ids:p,round:(t=e.round)!=null?t:0,seed:(i=e.seed)!=null?i:""};if(typeof e.strategy=="function")return e.strategy(r,o);if(e.roleFrom){const u=new Set(_(e.roles)),d={};for(const y of p){const m=e.roleFrom(r[y],y,o);if(!u.has(m))throw new Error(`assignRoles: role_from returned "${m}" for ${y}, which is not a declared role (${[...u].join(", ")}).`);d[y]={role:m}}return d}const n=J(r,e,o),l=_(e.roles),s={};return n.forEach((u,d)=>{if(d{try{return!!r()}catch(e){return!1}};function G(r){var e;const t=o=>r.groupSize==null||Object.keys(o).length===r.groupSize,i=o=>{var n,l;return{ids:Object.keys(o).sort(g),round:(n=r.round)!=null?n:0,seed:(l=r.seed)!=null?l:""}},p=o=>n=>t(n)&&Object.keys(n).every(l=>R(()=>o(n[l],l,i(n))));return r.ready?o=>t(o)&&R(()=>r.ready(o)):r.roleFrom?p((o,n,l)=>r.roleFrom(o,n,l)!=null):r.rankBy?p((o,n,l)=>Number.isFinite(r.rankBy(o,n,l))):((e=r.strategy)!=null?e:"join_order")==="join_order"?o=>t(o)&&Object.keys(o).every(n=>{var l;return((l=o[n])==null?void 0:l.joinedAt)!=null}):o=>t(o)}let h,T;function M(r,e){h=r,T=e}function H(){return h==null?void 0:h.role}function q(){return h}function D(){return T}function Y(r=T){var e,t;const i={};if(!r)return i;for(const[p,o]of Object.entries(r))((t=i[e=o.role])!=null?t:i[e]=[]).push(p);return i}var K=Object.defineProperty,Q=Object.defineProperties,V=Object.getOwnPropertyDescriptors,j=Object.getOwnPropertySymbols,W=Object.prototype.hasOwnProperty,X=Object.prototype.propertyIsEnumerable,k=(r,e,t)=>e in r?K(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,P=(r,e)=>{for(var t in e||(e={}))W.call(e,t)&&k(r,t,e[t]);if(j)for(var t of j(e))X.call(e,t)&&k(r,t,e[t]);return r},A=(r,e)=>Q(r,V(e));const Z={name:"multiplayer-role",version:F,parameters:{roles:{type:a.ParameterType.OBJECT,default:void 0},strategy:{type:a.ParameterType.FUNCTION,default:"join_order"},group_size:{type:a.ParameterType.INT,default:null},round:{type:a.ParameterType.INT,default:0},balanced:{type:a.ParameterType.BOOL,default:!1},seed:{type:a.ParameterType.STRING,default:null},rank_by:{type:a.ParameterType.FUNCTION,default:null},role_from:{type:a.ParameterType.FUNCTION,default:null},ready:{type:a.ParameterType.FUNCTION,default:null},overflow_role:{type:a.ParameterType.STRING,default:null},push_data:{type:a.ParameterType.OBJECT,default:{}},save_group:{type:a.ParameterType.BOOL,default:!1},timeout:{type:a.ParameterType.INT,default:3e4},on_timeout:{type:a.ParameterType.FUNCTION,default:null},message:{type:a.ParameterType.HTML_STRING,default:"

Assigning roles\u2026

"}},data:{role:{type:a.ParameterType.STRING},role_map:{type:a.ParameterType.OBJECT},assigned_self:{type:a.ParameterType.BOOL},timed_out:{type:a.ParameterType.BOOL},group:{type:a.ParameterType.OBJECT}},citations:{apa:"Tsukamoto, H. (n.d.). jsPsychMultiplayerRole (0.0.1). ",bibtex:"@misc{TsukamotojsPsychMultiplayerRole, author = {Tsukamoto, Hannah}, title = {jsPsychMultiplayerRole}, } "}};class f{constructor(e){this.jsPsych=e}trial(e,t,i){var p,o,n,l,s,u,d;const y=$(this.jsPsych),m=y.participantId;if(m==null)throw new Error("plugin-multiplayer-role: no participantId \u2014 the multiplayer adapter must be connected (await jsPsych.multiplayer.connect(adapter)) before this trial runs.");if(typeof t.strategy=="function"&&t.ready==null)throw new Error("plugin-multiplayer-role: a custom `strategy` function requires an explicit `ready` predicate (the readiness gate cannot be derived from an opaque strategy).");t.group_size==null&&t.ready==null&&console.warn("plugin-multiplayer-role: no `group_size` and no custom `ready` \u2014 readiness can resolve as soon as this client has pushed, assigning over a partial group. Set `group_size` (the exact count) or supply a `ready` predicate unless an upstream barrier guarantees all peers have already pushed into this session.");const b=(p=y.get(m))!=null?p:{},ee=A(P({},b),{joinedAt:(o=b.joinedAt)!=null?o:Date.now(),rounds:A(P({},(n=b.rounds)!=null?n:{}),{[t.round]:t.push_data})});e.innerHTML=t.message,i==null||i();const re=G({groupSize:t.group_size,strategy:t.strategy,rankBy:(l=t.rank_by)!=null?l:void 0,roleFrom:(s=t.role_from)!=null?s:void 0,ready:(u=t.ready)!=null?u:void 0,round:t.round,seed:(d=t.seed)!=null?d:void 0});return y.push(ee).then(()=>{var c;return y.wait(re,(c=t.timeout)!=null?c:void 0)}).then(c=>{var E,I,N,B,S;const w=O(c,{roles:t.roles,strategy:t.strategy,seed:(E=t.seed)!=null?E:void 0,round:t.round,balanced:t.balanced,rankBy:(I=t.rank_by)!=null?I:void 0,roleFrom:(N=t.role_from)!=null?N:void 0,overflowRole:(B=t.overflow_role)!=null?B:void 0}),v=w[m];M(v,w),this.jsPsych.finishTrial(P({role:(S=v==null?void 0:v.role)!=null?S:null,role_map:w,assigned_self:v!=null,timed_out:!1},t.save_group?{group:c}:{}))},c=>{if(!L(c))throw c;return this.handleTimeout(t)})}handleTimeout(e){M(void 0);try{e.on_timeout&&e.on_timeout(this.jsPsych)}catch(t){console.error("plugin-multiplayer-role: on_timeout hook threw",t)}this.jsPsych.finishTrial({role:null,role_map:null,assigned_self:!1,timed_out:!0})}}return f.info=Z,f.assignRoles=O,f.getMyRole=H,f.getMyAssignment=q,f.getRoleMap=D,f.participantsByRole=Y,f}(jsPsychModule); +//# sourceMappingURL=index.browser.min.js.map diff --git a/vendor/plugin-multiplayer-sync.js b/vendor/plugin-multiplayer-sync.js new file mode 100644 index 0000000..604f7ca --- /dev/null +++ b/vendor/plugin-multiplayer-sync.js @@ -0,0 +1,2 @@ +var jsPsychMultiplayerSync=function(r){"use strict";var f="0.1.0";const d="MultiplayerTimeoutError";function T(n){return n instanceof Error&&n.name===d}function P(n){var i;const e=n,u=(i=e.multiplayer)!=null?i:e.pluginAPI;if(!u||typeof u.getAll!="function")throw new Error("No multiplayer API found on the jsPsych instance. This plugin needs jsPsych core with multiplayer support (jsPsych#3694); see https://multiplayer.jspsych.org.");return u}var c=(n,i,e)=>new Promise((u,l)=>{var p=a=>{try{o(e.next(a))}catch(s){l(s)}},y=a=>{try{o(e.throw(a))}catch(s){l(s)}},o=a=>a.done?u(a.value):Promise.resolve(a.value).then(p,y);o((e=e.apply(n,i)).next())});const w={name:"multiplayer-sync",version:f,parameters:{wait_for:{type:r.ParameterType.FUNCTION,default:void 0},push_data:{type:r.ParameterType.OBJECT,default:null},message:{type:r.ParameterType.HTML_STRING,default:"

Waiting for other players\u2026

"},timeout:{type:r.ParameterType.INT,default:null},on_timeout:{type:r.ParameterType.FUNCTION,default:null},minimum_wait:{type:r.ParameterType.INT,default:0}},data:{group:{type:r.ParameterType.OBJECT,default:void 0},wait_time:{type:r.ParameterType.INT,default:void 0},timed_out:{type:r.ParameterType.BOOL,default:!1},wait_error:{type:r.ParameterType.STRING,default:null}},citations:{apa:"Tsukamoto, H. (n.d.). jsPsychMultiplayerSync (0.0.1). https://github.com/jspsych/jspsych-multiplayer/tree/main/packages/plugin-multiplayer-sync ",bibtex:"@misc{TsukamotojsPsychMultiplayerSync, author = {Tsukamoto, Hannah}, title = {jsPsychMultiplayerSync}, url = {https://github.com/jspsych/jspsych-multiplayer/tree/main/packages/plugin-multiplayer-sync}, howpublished = {https://github.com/jspsych/jspsych-multiplayer/tree/main/packages/plugin-multiplayer-sync}, } "}};class h{constructor(i){this.jsPsych=i}trial(i,e,u){return c(this,null,function*(){const l=P(this.jsPsych);if(typeof e.wait_for!="function")throw new Error("multiplayer-sync: the `wait_for` parameter is required and must be a function (a predicate over the group session).");i.innerHTML=`
${e.message}
`,u==null||u();const p=performance.now(),y=(t,m,g)=>{this.jsPsych.finishTrial({group:t,wait_time:Math.round(performance.now()-p),timed_out:m,wait_error:g})},o=()=>c(this,null,function*(){const t=performance.now()-p;tthis.jsPsych.pluginAPI.setTimeout(m,e.minimum_wait-t)))}),a=()=>{try{return l.getAll()}catch(t){return{}}};e.push_data!=null&&(yield l.push(e.push_data));const s=typeof e.timeout=="number"&&e.timeout>0?e.timeout:void 0;try{const t=yield l.wait(e.wait_for,s);yield o(),y(t,!1,null)}catch(t){if(!T(t))throw t;typeof e.on_timeout=="function"&&e.on_timeout(t),yield o(),y(a(),!0,t.message)}})}}return h.info=w,h}(jsPsychModule); +//# sourceMappingURL=index.browser.min.js.map