1const ATTRIBUTE_LIBRARY=[{id:"regulatory_approach",label:"Regulatory approach"},{id:"binding_ai_law",label:"Binding AI law"},{id:"risk_tiering",label:"Risk tiering"},{id:"transparency_duties",label:"Transparency duties"},{id:"penalties",label:"Penalties"},{id:"oversight_body",label:"Oversight body"},{id:"extraterritorial_scope",label:"Extraterritorial scope"},{id:"foundation_model_rules",label:"Foundation model rules"}];function classifyCell(e){return e&&!0!==e.gap&&Array.isArray(e.sources)&&0!==e.sources.length?"profile"===e.origin?"profile":"ai"===e.assessed?"ai-assessed":"sourced":"gap"}function cellRefusal(e,r){const a=new Error(e);return a.reason=r,a}function validateCell(e,r,a){const t=`${a}/${r}`,l=classifyCell(e);if(!e||!0===e.gap)return!0;if(!Array.isArray(e.sources)||0===e.sources.length)throw cellRefusal(`${t}: this cell has no source. Add a titled source or mark it as a gap.`,"source");for(const r of e.sources)if(!r||!r.title||!r.url)throw cellRefusal(`${t}: every source needs a title and a url.`,"source");if(0===String(e.comment||"").trim().split(/\s+/).filter(Boolean).length)throw cellRefusal(`${t}: a ${l} cell must carry a comment.`,"comment");return!0}const GAP_COPY="Not enough information yet. See the country profile or ask AI.";function escapeCompareHtml(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function isSafeCompareUrl(e){return"string"==typeof e&&/^https?:\/\//i.test(e.replace(/[\u0000-\u0020]/g,""))}
1function renderSourceLine(e){const r=escapeCompareHtml(e.title);return isSafeCompareUrl(e.url)?`<p class="compare-sources">â <a href="${escapeCompareHtml(e.url)}" target="_blank" rel="noopener noreferrer">${r}</a></p>`:`<p class="compare-sources">â ${r}</p>`}function compareLinkHtml(e,r){const a=r.slice().sort((e,r)=>e.start-r.start);let t="",l=0;for(const r of a)r.start>l&&(t+=escapeCompareHtml(e.slice(l,r.start))),t+=`<a href="${escapeCompareHtml(r.url)}" target="_blank" rel="noopener noreferrer">${escapeCompareHtml(e.slice(r.start,r.end))}</a>`,l=r.end;return l<e.length&&(t+=escapeCompareHtml(e.slice(l))),t}function linkifyCompareParagraphs(e,r){const a=(Array.isArray(e)?e:[]).map(e=>String(null==e?"":e)),t=Array.isArray(r)?r:[],l=a.map(()=>[]),o=[];for(const e of t){const r=e&&e.title?String(e.title):"",t=e&&e.url?String(e.url):"";if(!r)continue;if(!isSafeCompareUrl(t)){o.push(e);continue}let s=!1;for(let e=0;e<a.length&&!s;e+=1){const o=a[e];let i=0;for(;i<=o.length;){const a=o.indexOf(r,i);if(-1===a)break;const n=a+r.length;if(!l[e].some(e=>a<e.end&&n>e.start)){l[e].push({start:a,end:n,url:t}),s=!0;break}i=a+1}}s||o.push(e)}return{htmls:a.map((e,r)=>compareLinkHtml(e,l[r])),residual:o}}function linkifyCompareComment(e,r){const a=linkifyCompareParagraphs([e],r);return{html:a.htmls[0]||"",residual:a.residual}}function renderResidualSources(e){return e&&0!==e.length?'<p class="compare-sources-head">More sources</p>'+e.map(renderSourceLine).join(""):""}function compareCellParagraphs(e){return e&&Array.isArray(e.paragraphs)&&e.paragraphs.length?e.paragraphs:String(e&&e.comment||"").split("\n").filter(e=>e.trim().length>0)}const CELL_CLAMP_CHARS=420;function compareCellNeedsClamp(e){return compareCellParagraphs(e).join(" ").length>420}const PROFILE_NOTE_MARK=/^[\s\uFE0F]*[\u2757\u2755\u26A0\u2049\u203C]/;function compareParagraphIsNote(e){return PROFILE_NOTE_MARK.test(String(e))}function renderCellFragment(e,r,a,t){const l=classifyCell(e);if("gap"===l)return`<div class="compare-cell compare-cell-gap"><p class="compare-gap-copy">${GAP_COPY}</p><div class="compare-pill-row"><button class="compare-pill" data-action="profile" data-code="${escapeCompareHtml(a)}">Profile</button><button class="compare-pill compare-pill-ai" data-action="askai" data-code="${escapeCompareHtml(a)}" data-rid="${escapeCompareHtml(r)}">Ask AI</button></div></div>`;const o=compareCellNeedsClamp(e)&&!t,s=compareCellNeedsClamp(e)?`<button class="compare-pill" data-action="expandcell" data-code="${escapeCompareHtml(a)}" data-rid="${escapeCompareHtml(r)}">${o?"Read all":"Show less"}</button>`:"";if("profile"===l){const r=compareCellParagraphs(e),t=linkifyCompareParagraphs(r,e.sources);return`<div class="compare-cell compare-cell-profile${o?" compare-cell-clamped":""}"><div class="compare-cell-body" data-editable="1">${r.map((e,r)=>`<p class="compare-comment${compareParagraphIsNote(e)?" compare-comment-note":""}">${t.htmls[r]}</p>`).join("")}</div>`+renderResidualSources(t.residual)+`<div class="compare-pill-row">${s}`+`<button class="compare-pill" data-action="profile" data-code="${escapeCompareHtml(a)}">Open profile</button></div></div>`}const i=linkifyCompareComment(e.comment,e.sources),n=`<div class="compare-cell-body" data-editable="1"><p class="compare-comment">${i.html}</p></div>`,c=renderResidualSources(i.residual);if("ai-assessed"===l){const r=!0===e.web;return`<div class="compare-cell compare-cell-ai${o?" compare-cell-clamped":""}">${r?'<p class="compare-web-note">Based on web search, not the tracker.</p>':""}${n}${c}${`<p class="compare-ai-fence">AI assessed · ${r?"web · ":""}${escapeCompareHtml(e.assessedAt||"")}</p>`}`+(s?`<div class="compare-pill-row">${s}</div>`:"")+"</div>"}return`<div class="compare-cell compare-cell-sourced">${n}${c}</div>`}function attributeLabel(e){const r=ATTRIBUTE_LIBRARY.find(r=>r.id===e);if(!r)throw new Error(`Unknown attribute id: ${e}.`);return r.label}function normalizeRow(e){if("string"==typeof e)return{rid:e,kind:"attribute",key:e,label:attributeLabel(e)};if(!e||!e.kind)throw new Error("A compare row needs a kind.");return"attribute"===e.kind?{rid:e.rid||e.key,kind:"attribute",ke
1y:e.key,label:attributeLabel(e.key)}:{rid:e.rid,kind:e.kind,key:e.key||null,label:String(e.label||"")}}function cellKey(e){return"attribute"===e.kind?e.key:"row:"+e.rid}function buildCompareModel(e,r,a){return{columns:r.map(r=>{const a=e.jurisdictions[r];if(!a)throw new Error(`No coded data for jurisdiction ${r}.`);return{code:r,name:a.name}}),rows:a.map(normalizeRow).map(a=>{const t=cellKey(a),l=r.map(r=>{const a=e.jurisdictions[r].cells[t]||{gap:!0};return validateCell(a,t,r),{code:r,kind:classifyCell(a),cell:a}});return{rid:a.rid,kind:a.kind,attributeId:"attribute"===a.kind?a.key:null,label:a.label,cells:l}})}}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.