{"id":127,"date":"2026-02-13T23:56:02","date_gmt":"2026-02-13T23:56:02","guid":{"rendered":"https:\/\/www.relayed.net\/?post_type=tools&#038;p=127"},"modified":"2026-02-15T00:54:23","modified_gmt":"2026-02-15T00:54:23","slug":"new-analyse","status":"publish","type":"tools","link":"https:\/\/www.relayed.net\/?tools=new-analyse","title":{"rendered":"New Analyse"},"content":{"rendered":"\n <div class=\"ccaa\" data-ccaa>\n <textarea class=\"ccaa-ta\" placeholder=\"Paste CSV here...\" spellcheck=\"false\"><\/textarea>\n <div class=\"ccaa-bar\"> <button type=\"button\" class=\"ccaa-btn\">Analyse<\/button>\n <span class=\"ccaa-meta\"><\/span> <\/div>\n <div class=\"ccaa-out\"><\/div> <\/div>\n    <style>\n        .ccaa { max-width: 1100px; color: #000; } \/* Set base font color to black *\/\n        .ccaa-ta { width: 100%; height: 220px; padding: 10px; border: 1px solid #ccd; border-radius: 8px; color: #000; }\n        .ccaa-bar { display: flex; align-items: center; gap: 10px; margin: 10px 0 0; }\n        .ccaa-btn { padding: 8px 12px; border: 1px solid #334; border-radius: 8px; background: #111; color: #fff; cursor: pointer; }\n        .ccaa-btn:hover { opacity: .92; }\n        .ccaa-meta { color: #000; }\n        .ccaa-out { margin-top: 12px; }\n        .ccaa-card { border: 1px solid #dde; border-radius: 10px; padding: 12px; margin: 10px 0; color: #000; }\n        .ccaa-h { font-weight: 700; margin: 0 0 8px; color: #000; }\n        .ccaa-t { width: 100%; border-collapse: collapse; color: #000; }\n        .ccaa-t th, .ccaa-t td { border: 1px solid #dde; padding: 6px 8px; vertical-align: top; color: #000; }\n        .ccaa-t th { background: #f6f7fb; text-align: left; color: #000; }\n        .ccaa-bad { background: #fff2f2; }\n        .ccaa-warn { background: #fff8e6; }\n        .ccaa-ok { background: #f2fff5; }\n        .ccaa-small { font-size: 11px; color: #000; } \/* Changed from #556 to black *\/\n        .ccaa-pre { white-space: pre-wrap; word-break: break-word; color: #000; }\n    <\/style>\n\n <script>\n (function() {\n const root = document.querySelector('[data-ccaa]');\n if (!root) return;\n\n const ta = root.querySelector('.ccaa-ta'),\n btn = root.querySelector('.ccaa-btn'),\n out = root.querySelector('.ccaa-out'),\n meta = root.querySelector('.ccaa-meta');\n\n const c = (v, up) => { v = String(v == null ? '' : v).replace(\/\\r\/g, '').replace(\/\\u00a0\/g, ' ').trim();\n v = v.replace(\/\\s+\/g, ' ').trim(); return up ? v.toUpperCase() : v; };\n\n const num = (v) => { v = c(v, 0).replace(\/%\/g, '').replace(\/,\/g, '').replace(\/\\s+\/g, ''); if (!v) return 0; var n = parseFloat(v); return isFinite(n) ? n : 0; };\n\n const fmt = (n) => (Math.round((+n || 0) * 100) \/ 100).toFixed(2);\n\n const esc = (s) => String(s == null ? '' : s)\n .replace(\/&\/g, '&amp;')\n .replace(\/<\/g, '&lt;')\n .replace(\/>\/g, '&gt;')\n .replace(\/\"\/g, '&quot;')\n .replace(\/'\/g, '&#039;');\n\n const ALL_TERR = ['BE', 'DK', 'FI', 'IS', 'IE', 'NL', 'NO', 'SE', 'GB', 'EE', 'FR', 'DE', 'LV', 'LT', 'LU', 'US', 'AF', 'AL', 'DZ', 'AD', 'AO', 'AG', 'AR', 'AM', 'AU', 'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BZ', 'BJ', 'BT', 'BO', 'BA', 'BW', 'BR', 'BN', 'BG', 'BF', 'BI', 'KH', 'CM', 'CA', 'CV', 'CF', 'TD', 'CL', 'CN', 'CO', 'KM', 'CG', 'CD', 'CK', 'CR', 'CI', 'HR', 'CU', 'CY', 'CZ', 'DJ', 'DM', 'DO', 'EC', 'EG', 'SV', 'GQ', 'ER', 'ET', 'FJ', 'PF', 'GA', 'GM', 'GE', 'GH', 'GR', 'GD', 'GT', 'GN', 'GW', 'GY', 'HT', 'VA', 'HN', 'HK', 'HU', 'IN', 'ID', 'IR', 'IQ', 'IL', 'IT', 'JM', 'JP', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LB', 'LS', 'LR', 'LY', 'LI', 'MO', 'MK', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MR', 'MU', 'MX', 'FM', 'MD', 'MC', 'MN', 'ME', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'OM', 'PK', 'PW', 'PA', 'PG', 'PY', 'PE', 'PH', 'PL', 'PT', 'PR', 'QA', 'RO', 'RU', 'RW', 'KN', 'LC', 'VC', 'WS', 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SK', 'SI', 'SB', 'SO', 'ZA', 'SS', 'ES', 'LK', 'SD', 'SR', 'SZ', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TO', 'TV', 'TT', 'TN', 'TR', 'TM', 'TU', 'UG', 'UA', 'AE', 'UY', 'UZ', 'VU', 'VE', 'VN', 'EH', 'YE', 'ZM', 'ZW'];\n\n const TERR_GROUPS = {\n '2WL': ['*'], '2NC': ['DK', 'FI', 'IS', 'NO', 'SE'], '2BA': ['AL', 'BA', 'BG', 'ME', 'MK', 'RS'],\n '2BI': ['GB', 'IE'], '2BS': ['EE', 'LV', 'LT'], '2BE': ['BE', 'NL', 'LU'] };\n\n const csvParse = (text) => {\n text = String(text || '').replace(\/\\r\/g, '').trim();\n if (!text) return [];\n const rows = [];\n let i = 0,\n field = '',\n row = [],\n q = 0;\n const pushField = () => { row.push(field); field = ''; };\n const pushRow = () => { if (row.length || field.length) { pushField(); rows.push(row); row = []; }\n };\n while (i < text.length) {\n const ch = text[i++];\n if (q) {\n if (ch == '\"') {\n if (text[i] == '\"') {\n field += '\"';\n i++;\n } else q = 0;\n } else field += ch;\n } else {\n if (ch == '\"') q = 1;\n else if (ch == ',') pushField();\n else if (ch == '\\n') pushRow();\n else field += ch;\n }\n }\n pushRow();\n if (!rows.length) return [];\n const hdr = rows.shift().map(h => c(h, 0));\n const out = [];\n rows.forEach(r => {\n if (!r || !r.length) return;\n const o = {};\n for (let k = 0; k < hdr.length; k++) o[hdr[k]] = r[k] == null ? '' : r[k];\n out.push(o);\n });\n return out;\n };\n\n const parseDate = (v) => {\n v = c(v, 0);\n if (!v) return null;\n if (\/^indefinite$\/i.test(v)) return new Date(8640000000000000);\n if (\/^\\d{4}-\\d{2}-\\d{2}$\/.test(v)) {\n const p = v.split('-');\n return new Date(Date.UTC(+p[0], (+p[1] - 1), +p[2]));\n }\n if (\/^\\d{2}\\\/\\d{2}\\\/\\d{4}$\/.test(v)) {\n const p = v.split('\/');\n return new Date(Date.UTC(+p[2], (+p[1] - 1), +p[0]));\n }\n return null;\n };\n\n const isExpired = (endDate) => {\n const d = parseDate(endDate);\n if (!d) return false;\n const now = new Date(),\n todayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n return d < todayUTC;\n };\n\n const expandTerr = (code) => {\n code = c(code, 1);\n if (!code) return [];\n if (code === '*') return ALL_TERR.slice();\n const g = TERR_GROUPS[code];\n if (g) {\n if (g.length === 1 && g[0] === '*') return ALL_TERR.slice();\n return g.slice();\n }\n if (ALL_TERR.indexOf(code) >= 0) return [code];\n return [];\n };\n\n const terrSetFromExpr = (expr) => {\n expr = c(expr, 1).replace(\/\\s+\/g, '');\n if (!expr) return [];\n const toks = expr.match(\/[+\\-][A-Z0-9]{1,6}\/g) || [],\n set = Object.create(null);\n toks.forEach(t => {\n const s = t[0],\n code = t.slice(1),\n list = expandTerr(code);\n if (s == '+') list.forEach(cc => set[cc] = 1);\n else list.forEach(cc => {\n if (set[cc]) delete set[cc];\n });\n });\n return Object.keys(set);\n };\n\n const toksFromExpr = (expr) => {\n expr = c(expr, 1).replace(\/\\s+\/g, '');\n if (!expr) return [];\n return expr.match(\/[+\\-][A-Z0-9]{1,6}\/g) || [];\n };\n\n const overlapTokens = (expr) => {\n const toks = toksFromExpr(expr),\n plus = [];\n toks.forEach(t => {\n if (t[0] == '+') plus.push(t.slice(1));\n });\n const seen = Object.create(null),\n uniq = [];\n plus.forEach(x => {\n if (!seen[x]) {\n seen[x] = 1;\n uniq.push(x);\n }\n });\n const sets = uniq.map(code => ({\n code: code,\n list: expandTerr(code)\n })).filter(x => (x.list ? x.list.length : 0));\n const out = [];\n const isSubset = (small, big) => {\n if (small.length > big.length) return 0;\n const m = Object.create(null);\n big.forEach(x => m[x] = 1);\n for (let i = 0; i < small.length; i++)\n if (!m[small[i]]) return 0;\n return 1;\n };\n for (let i = 0; i < sets.length; i++)\n for (let j = i + 1; j < sets.length; j++) {\n const a = sets[i],\n b = sets[j],\n m = Object.create(null);\n a.list.forEach(x => m[x] = 1);\n const inter = [];\n b.list.forEach(x => {\n if (m[x]) inter.push(x);\n });\n if (!inter.length) continue;\n if (isSubset(b.list, a.list) || isSubset(a.list, b.list)) continue;\n out.push({\n a: a.code,\n b: b.code,\n inter: inter\n });\n }\n return out;\n };\n\n const rightsMask = (rights) => {\n rights = c(rights, 1);\n const hasPR = (\/\\bPR\\b\/.test(rights) || \/PERF\/.test(rights)),\n hasMR = (\/\\bMR\\b\/.test(rights) || \/MECH\/.test(rights));\n if (hasPR) {\n if (hasMR) return { pr: 1, mr: 1 };\n return { pr: 1, mr: 0 };\n }\n if (hasMR) return { pr: 0, mr: 1 };\n return { pr: 1, mr: 1 };\n };\n\n const isCreatorStart = (role) => {\n role = c(role, 1);\n if (!role) return 0;\n if (role === 'E' || role === 'SE') return 0;\n return 1;\n };\n\n const isPubRole = (role) => {\n role = c(role, 1);\n return role === 'E' || role === 'SE';\n };\n\n const sumByParty = (arr) => {\n const map = Object.create(null),\n out = [];\n (arr || []).forEach(x => {\n const key = (x.name || '') + '|' + (x.role || '') + '|' + (x.ipi || '');\n if (!map[key]) map[key] = {\n name: x.name,\n role: x.role,\n ipi: x.ipi,\n sum: 0\n };\n map[key].sum += (+x.val || 0);\n });\n Object.keys(map).forEach(k => out.push(map[k]));\n out.sort((a, b) => Math.abs(b.sum) - Math.abs(a.sum));\n return out;\n };\n\n const whoForOver = (arr, overAmt) => {\n if (!arr || !arr.length || !(overAmt > 0)) return '';\n const parties = sumByParty(arr),\n keep = [];\n let s = 0;\n for (let i = 0; i < parties.length; i++) {\n keep.push(parties[i]);\n s += Math.abs(parties[i].sum);\n if (s >= overAmt - 0.01) break;\n }\n return keep.map(o => o.name + (o.role ? ' (' + o.role + ')' : '') + ' ' + fmt(o.sum)).join(', ');\n };\n\n const whoForUnder = (arr) => {\n if (!arr || !arr.length) return '';\n const parties = sumByParty(arr);\n return parties.map(o => o.name + (o.role ? ' (' + o.role + ')' : '') + ' ' + fmt(o.sum)).join(', ');\n };\n\n const exprHasCode = (expr, code) => {\n expr = c(expr, 1).replace(\/\\s+\/g, '');\n code = c(code, 1);\n return expr.indexOf('+' + code) >= 0 || expr.indexOf('-' + code) >= 0;\n };\n\n const rowHasPubShare = (r) => {\n if (!isPubRole(r.role)) return 0;\n if (!r.mask) return 0;\n if (r.mask.pr) {\n if ((+r.perf || 0) > 0) return 1;\n }\n if (r.mask.mr) {\n if ((+r.mech || 0) > 0) return 1;\n }\n return 0;\n };\n\n const analyze = (rows) => {\n const live = [];\n rows.forEach(r => {\n const o = {\n role: c(r.role || r.Role, 1),\n name: c(r.name || r.Name, 0),\n ipi: c(r.ipi || r.Ipi, 0),\n pr_soc: c(r.pr_soc || r.Pr_soc || r['pr soc'] || '', 0),\n mr_soc: c(r.mr_soc || r.Mr_soc || r['mr soc'] || '', 0),\n prior_r: c(r.prior_r || r.Prior_r || '', 0),\n start_date: c(r.start_date || r.Start_date || '', 0),\n end_date: c(r.end_date || r.End_date || '', 0),\n ptc: c(r.ptc || r.Ptc || '', 0),\n territory: c(r.territory || r.Territory || '', 1),\n rights: c(r.rights || r.Rights || '', 0),\n perf: num(r.perf || r.Perf),\n mech: num(r.mech || r.Mech),\n alerts: c(r.alerts || r.Alerts || '', 0)\n };\n if (!o.role) {\n if (!o.name) {\n if (!o.territory) return;\n }\n }\n if (isExpired(o.end_date)) return;\n o.terrList = terrSetFromExpr(o.territory);\n o.mask = rightsMask(o.rights);\n o.overlaps = overlapTokens(o.territory);\n live.push(o);\n });\n\n const groups = [];\n let g = null;\n live.forEach((o, i) => {\n if (!g || isCreatorStart(o.role)) {\n g = { i0: i, rows: [] };\n groups.push(g);\n }\n g.rows.push(o);\n });\n\n const terrsMap = Object.create(null),\n overlapsAll = [];\n live.forEach(o => {\n (o.terrList || []).forEach(t => terrsMap[t] = 1);\n if (o.overlaps ? o.overlaps.length : 0) o.overlaps.forEach(x => overlapsAll.push({\n name: o.name,\n role: o.role,\n ipi: o.ipi,\n expr: o.territory,\n a: x.a,\n b: x.b,\n inter: x.inter\n }));\n });\n\n const gTotals = Object.create(null),\n gContrib = Object.create(null);\n const addContrib = (gi, t, kind, o, val) => {\n const k = gi + '|' + t + '|' + kind;\n if (!gContrib[k]) gContrib[k] = [];\n gContrib[k].push({\n name: o.name,\n role: o.role,\n ipi: o.ipi,\n val: val,\n terr: o.territory\n });\n };\n\n groups.forEach((g, gi) => {\n const hasPub = g.rows.some(r => rowHasPubShare(r));\n if (!hasPub) return;\n g.rows.forEach(o => {\n if (!isPubRole(o.role)) return;\n const pr = (o.mask && o.mask.pr) ? o.perf : 0,\n mr = (o.mask && o.mask.mr) ? o.mech : 0;\n (o.terrList || []).forEach(t => {\n const k = gi + '|' + t;\n if (!gTotals[k]) gTotals[k] = { pr: 0, mr: 0 };\n gTotals[k].pr += pr;\n gTotals[k].mr += mr;\n if (pr) addContrib(gi, t, 'PR', o, pr);\n if (mr) addContrib(gi, t, 'MR', o, mr);\n });\n });\n });\n\n const terrs = Object.keys(terrsMap).sort();\n const worst = Object.create(null);\n const noteWorst = (t, kind, tot, gi, arr) => {\n const key = t + '|' + kind;\n const over = (tot > 100.0001),\n under = (tot < 99.9999);\n if (!(over || under)) return;\n const issue = over ? 'over' : 'under',\n delta = over ? (tot - 100) : (100 - tot);\n const cur = worst[key];\n let take = 0;\n if (!cur) take = 1;\n else if (issue === 'over') {\n if (cur.issue !== 'over') take = 1;\n else if (delta > cur.delta) take = 1;\n } else {\n if (cur.issue === 'over') take = 0;\n else if (delta > cur.delta) take = 1;\n }\n if (!take) return;\n const who = over ? whoForOver(arr, delta) : whoForUnder(arr);\n worst[key] = { tot: tot, issue: issue, delta: delta, gi: gi, who: who };\n };\n\n terrs.forEach(t => {\n groups.forEach((g, gi) => {\n const k = gi + '|' + t;\n if (!gTotals[k]) return;\n noteWorst(t, 'PR', gTotals[k].pr, gi, gContrib[gi + '|' + t + '|PR'] || []);\n noteWorst(t, 'MR', gTotals[k].mr, gi, gContrib[gi + '|' + t + '|MR'] || []);\n });\n });\n\n const issues = [];\n terrs.forEach(t => {\n if (worst[t + '|PR'] || worst[t + '|MR']) issues.push(t);\n });\n return { live, groups, terrs, issues, overlapsAll, gTotals, gContrib, worst };\n };\n\n const suggestRemedies = (res) => {\n const out = [],\n uniq = Object.create(null);\n res.issues.forEach(t => {\n const wPR = res.worst[t + '|PR'],\n wMR = res.worst[t + '|MR'];\n const push = (kind, w) => {\n if (!w) return;\n if (w.issue === 'over') {\n const arr = (res.gContrib[w.gi + '|' + t + '|' + kind] || []);\n const offenders = sumByParty(arr).slice(0, 6);\n offenders.forEach(p => {\n const sug = 'Reduce overlap in publisher scope for ' + p.name + ' (group ' + (w.gi + 1) + '): add \"-' + t + '\" to the broad publisher territory expr that currently includes ' + t + ' (only if contractually excluded).';\n const k = t + '|' + kind + '|over|' + sug;\n if (!uniq[k]) {\n uniq[k] = 1;\n out.push({ terr: t, kind: kind, issue: 'over', why: 'Publisher total ' + fmt(w.tot) + ' (+' + fmt(w.delta) + ') in group ' + (w.gi + 1), suggest: sug });\n }\n });\n } else {\n const sug = 'Publisher scope missing coverage for ' + t + ' in group ' + (w.gi + 1) + ': add \"+' + t + '\" to the intended SE\/E appointment (only if contractually appointed).';\n const k = t + '|' + kind + '|under|' + sug;\n if (!uniq[k]) {\n uniq[k] = 1;\n out.push({ terr: t, kind: kind, issue: 'under', why: 'Publisher total ' + fmt(w.tot) + ' (-' + fmt(w.delta) + ') in group ' + (w.gi + 1), suggest: sug });\n }\n }\n };\n push('PR', wPR);\n push('MR', wMR);\n });\n return out;\n };\n\n const renderOverlaps = (items) => {\n if (!items || !items.length) return '';\n const map = Object.create(null),\n out = [];\n items.forEach(x => {\n const key = (x.expr || '') + '|' + (x.name || '') + '|' + (x.role || '') + '|' + (x.ipi || '');\n if (!map[key]) map[key] = { expr: x.expr, name: x.name, role: x.role, ipi: x.ipi, pairs: [] };\n map[key].pairs.push({ a: x.a, b: x.b, inter: x.inter });\n });\n Object.keys(map).forEach(k => out.push(map[k]));\n let html = '';\n html += '<div class=\"ccaa-card\"><div class=\"ccaa-h\">Overlap detection (within a single territory expression)<\/div>';\n html += '<div class=\"ccaa-small\">Only flags non-trivial overlaps.<\/div>';\n html += '<table class=\"ccaa-t\"><thead><tr><th>Party<\/th><th>Territory expression<\/th><th>Overlaps<\/th><\/tr><\/thead><tbody>';\n out.forEach(o => {\n const party = esc(o.name + (o.role ? ' (' + o.role + ')' : '') + (o.ipi ? ' ' + o.ipi : '')),\n expr = esc(o.expr || '');\n const ov = o.pairs.map(p => esc(p.a) + ' n ' + esc(p.b) + ' = ' + esc(p.inter.join(','))).join('\\n');\n html += '<tr class=\"ccaa-warn\"><td>' + party + '<\/td><td class=\"ccaa-pre\">' + expr + '<\/td><td class=\"ccaa-pre\">' + ov + '<\/td><\/tr>';\n });\n html += '<\/tbody><\/table><\/div>';\n return html;\n };\n\n const renderRemedies = (items) => {\n if (!items || !items.length) return '';\n let html = '';\n html += '<div class=\"ccaa-card\"><div class=\"ccaa-h\">Suggested remedies (publisher-scope, heuristic)<\/div>';\n html += '<div class=\"ccaa-small\">These suggestions are scoped to publisher-side roles (E\/SE) within each creator group.<\/div>';\n html += '<table class=\"ccaa-t\"><thead><tr><th>Territory<\/th><th>Right<\/th><th>Issue<\/th><th>Why<\/th><th>Suggestion<\/th><\/tr><\/thead><tbody>';\n items.forEach(r => {\n const cls = r.issue === 'over' ? 'ccaa-bad' : 'ccaa-warn';\n html += '<tr class=\"' + cls + '\"><td>' + esc(r.terr) + '<\/td><td>' + esc(r.kind) + '<\/td><td>' + esc(r.issue) + '<\/td><td>' + esc(r.why) + '<\/td><td class=\"ccaa-pre\">' + esc(r.suggest) + '<\/td><\/tr>';\n });\n html += '<\/tbody><\/table><\/div>';\n return html;\n };\n\n const render = (res) => {\n const terrs = res.terrs,\n nTerr = terrs.length,\n nIssues = res.issues.length;\n let html = '';\n html += '<div class=\"ccaa-card\"><div class=\"ccaa-h\">Summary<\/div>';\n html += '<div class=\"ccaa-small\">Territories found: ' + nTerr + ' | Territories with publisher-scope issues: ' + nIssues + '<\/div>';\n if (nIssues) html += '<div class=\"ccaa-small\">Issue territories: ' + esc(res.issues.join(', ')) + '<\/div>';\n html += '<\/div>';\n html += renderOverlaps(res.overlapsAll);\n const remedies = suggestRemedies(res);\n html += renderRemedies(remedies);\n html += '<div class=\"ccaa-card\"><div class=\"ccaa-h\">Totals by territory (publisher scope, worst group)<\/div>';\n html += '<table class=\"ccaa-t\"><thead><tr><th>Territory<\/th><th>PR total<\/th><th>MR total<\/th><th>Status<\/th><th>Who<\/th><\/tr><\/thead><tbody>';\n terrs.forEach(t => {\n const wPR = res.worst[t + '|PR'],\n wMR = res.worst[t + '|MR'];\n const pr = wPR ? wPR.tot : 100,\n mr = wMR ? wMR.tot : 100;\n const prOver = wPR ? (wPR.issue === 'over') : 0,\n prUnder = wPR ? (wPR.issue === 'under') : 0;\n const mrOver = wMR ? (wMR.issue === 'over') : 0,\n mrUnder = wMR ? (wMR.issue === 'under') : 0;\n let status = [];\n if (prOver) status.push('PR overclaim +' + fmt(wPR.delta) + ' (group ' + (wPR.gi + 1) + ')');\n else if (prUnder) status.push('PR underclaim -' + fmt(wPR.delta) + ' (group ' + (wPR.gi + 1) + ')');\n else status.push('PR ok');\n if (mrOver) status.push('MR overclaim +' + fmt(wMR.delta) + ' (group ' + (wMR.gi + 1) + ')');\n else if (mrUnder) status.push('MR underclaim -' + fmt(wMR.delta) + ' (group ' + (wMR.gi + 1) + ')');\n else status.push('MR ok');\n let cls = 'ccaa-ok';\n if (prOver || mrOver) cls = 'ccaa-bad';\n else if (prUnder || mrUnder) cls = 'ccaa-warn';\n let who = '';\n if (prOver || prUnder) who += 'PR: ' + esc(wPR ? wPR.who : '');\n if (mrOver || mrUnder) who += (who ? '\\n' : '') + 'MR: ' + esc(wMR ? wMR.who : '');\n html += '<tr class=\"' + cls + '\"><td>' + esc(t) + '<\/td><td>' + fmt(pr) + '<\/td><td>' + fmt(mr) + '<\/td><td>' + esc(status.join(' | ')) + '<\/td><td class=\"ccaa-pre\">' + who + '<\/td><\/tr>';\n });\n html += '<\/tbody><\/table><\/div>';\n return html;\n };\n\n btn.addEventListener('click', function() {\n out.innerHTML = '';\n meta.textContent = '';\n const text = ta.value || '',\n parsed = csvParse(text);\n if (!parsed.length) {\n out.innerHTML = '<div class=\"ccaa-card\"><div class=\"ccaa-h\">No data<\/div><div class=\"ccaa-small\">Paste CSV with headers, then click Analyse.<\/div><\/div>';\n return;\n }\n const res = analyze(parsed);\n meta.textContent = 'Rows parsed: ' + parsed.length + ' | Rows counted (not expired): ' + res.live.length + ' | Creator groups: ' + res.groups.length;\n out.innerHTML = render(res);\n });\n })();\n <\/script>\n\n","protected":false},"author":1,"featured_media":134,"template":"","meta":{"org":"","name":"","type":"","content":""},"class_list":["post-127","tools","type-tools","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/www.relayed.net\/index.php?rest_route=\/wp\/v2\/tools\/127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.relayed.net\/index.php?rest_route=\/wp\/v2\/tools"}],"about":[{"href":"https:\/\/www.relayed.net\/index.php?rest_route=\/wp\/v2\/types\/tools"}],"author":[{"embeddable":true,"href":"https:\/\/www.relayed.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.relayed.net\/index.php?rest_route=\/wp\/v2\/media\/134"}],"wp:attachment":[{"href":"https:\/\/www.relayed.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}