Varulv Rösträknare som bokmärke

Funkar för mig. Jag har döpt bokmärket på datorn till "varulvsräknare", och det dyker upp i förslagslistan (tillsammans med en hög faktiska speltrådar) om jag börjar skriva "varuosv... på mobilen. Kanske ett synk-issue, som du säger?
Iphone eller android?
 
Klickar jag på "Alla röster" så räknas alla röster ihop i sammanställningen, så det blir fel. Ie, just nu i spelet har jag tre röster på mig, klickar jag på alla röster har jag fyra (för att en spelare röstade på mig tidigare, men har flyttat).
 
Klickar jag på "Alla röster" så räknas alla röster ihop i sammanställningen, så det blir fel. Ie, just nu i spelet har jag tre röster på mig, klickar jag på alla röster har jag fyra (för att en spelare röstade på mig tidigare, men har flyttat).
Aj då. @damogn ?
 
Denna version ska vara korrigerad. Har med instruktionerna så att någon i framtiden vet vad de ska göra med koden.

1) Skapa ett bokmärke (på vilken sida som helst).
2) Editera det. Sätt namn till vad som helst (t.ex. Rösträknare).
3) I URL tar du bort allt som står och klistrar in allt detta (det är mycket viktigt att du får med exakt allt, på en dator kan du trippelklicka på texten för att markera hela raden):

Code:
javascript:(async()=>{const t=location.href.match(/^https:\/\/www\.rollspel\.nu\/threads\/([^/?#]+?\.\d+)(?:[/?#]|$)/i);if(!t)return alert("Öppna bookmarkleten i en tråd på rollspel.nu.");const e=`https://www.rollspel.nu/threads/${t[1]}/`,a=/\bröst\s*:\s*/i,l=new DOMParser,n=[],r=[],o=t=>document.querySelector(t),i=t=>[...document.querySelectorAll(t)],s=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),c=t=>t?new Date(t).toLocaleString("sv-SE",{dateStyle:"short",timeStyle:"short"}):"–",d=t=>t.slice().sort((t,e)=>+new Date(t.ts)-+new Date(e.ts)||Number(t.post||0)-Number(e.post||0)),u=t=>{const e=new Map;return d(t).forEach(t=>e.set(t.from,t)),[...e.values()]},m=t=>`"${String(t??"").replace(/"/g,'""')}"`;try{for(let t=1;t<=500;t++){const a=await fetch(1===t?e:`${e}page-${t}`,{credentials:"include",cache:"no-store"});if(!a.ok)throw Error(`Sida ${t} gav HTTP ${a.status}`);const n=l.parseFromString(await a.text(),"text/html");r.push([t,n]);if(t>=Number(n.querySelector("input.js-pageJumpPage")?.max||1)&&!n.querySelector(".pageNav-jump--next"))break}}catch(t){return alert("Kunde inte läsa tråden: "+t.message)}const p=t=>t.replace(/<br\s*\/?\s*>|<\/p\s*>|<\/li\s*>/gi,"\n").split("\n").map(t=>t.trim()).filter(Boolean),f=t=>{const e=document.createElement("div");e.innerHTML=t;const l=document.createTreeWalker(e,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT);let n=!1,r="";for(;l.nextNode();){const t=l.currentNode;if(3===t.nodeType)r+=t.nodeValue||"",!n&&a.test(r)&&(n=!0),r.length>300&&(r=r.slice(-300));else if(n&&1===t.nodeType){const e=t.getAttribute?.("data-username");if(e)return e.replace(/^@/,"").trim()}}return""};for(const[t,e]of r)[...e.querySelectorAll("article[data-author]")].forEach((e,l)=>{if(1===t&&0===l)return;const r=(e.getAttribute("data-author")||"").trim(),o=(e.id||"").replace("js-post-","").trim(),i=e.querySelector("time.u-dt")?.getAttribute("datetime")||"";let s=e.querySelector(".message-content");if(r&&o&&s){s=s.cloneNode(!0),s.querySelectorAll("blockquote,.bbCodeBlock--quote,.bbCodeBlock--unfurl,[data-unfurl='true']").forEach(t=>t.remove());for(const e of p(s.innerHTML)){const l=document.createElement("div");if(l.innerHTML=e,!a.test((l.textContent||"").replace(/\s+/g," ").trim()))continue;const s=f(e);s&&n.push({from:r,to:s,ts:i,post:o,page:t})}}});n.splice(0,n.length,...d(n)),document.title="Varulv Rösträknare",document.head.innerHTML='<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{font-family:Arial,sans-serif;margin:2em;background:#f9f9f9;color:#333}label,button,select,input{margin:8px 10px 8px 0}select,button,input{padding:6px}button{cursor:pointer}table{border-collapse:collapse;width:100%;margin-top:12px;background:#fff;border:1px solid #ccc}th,td{padding:8px 10px;border:1px solid #ccc;text-align:center;vertical-align:top}th{background:#3c8dbc;color:#fff;cursor:pointer}.summary{font-weight:bold;font-size:18px;margin:12px 0}.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:8px 0}.small{font-size:12px;opacity:.8}details{margin:12px 0;background:#fff;border:1px solid #ccc;padding:10px}summary{font-weight:bold;cursor:pointer}.manual{display:flex;flex-wrap:wrap;gap:8px 16px}.manual label{display:inline-flex;align-items:center;gap:6px;margin:0}.manual input{margin:0}#chart{max-width:620px;width:100%;height:auto}#slider{padding:0;min-width:260px}@media(max-width:700px){body{margin:10px}th,td{padding:5px;font-size:12px}}</style>',document.body.innerHTML=`<h1>🐺 Varulv Rösträknare</h1><div class="small">Källa: <a target="_blank" rel="noopener" href="${s(e)}">${s(e)}</a></div><div id="status" class="small"></div><button id="exportBtn">Exportera CSV</button><details open><summary>Inställningar</summary><label><input type="radio" name="view" value="latest" checked> Endast senaste röst</label><label><input type="radio" name="view" value="all"> Alla röster</label><br><label>Filter per spelare <select id="filter"></select></label><label>Färger <select id="colorMode"><option value="auto">Auto</option><option value="bw">Svartvitt</option><option value="manual">Manuellt</option></select></label><div id="manual" class="manual" hidden></div><div class="row"><button id="play">▶ Animera röster</button><label>Hastighet <input id="delay" type="number" value="200" min="0" style="width:65px"> ms</label></div><div class="row"><label for="slider">Visa röster fram till:</label><input id="slider" type="range" min="0" max="0" value="0" list="sliderTicks"><datalist id="sliderTicks"></datalist><span id="sliderLabel">–</span></div></details><div id="summary" class="summary"></div><canvas id="chart" width="620" height="260"></canvas><h2>Röster</h2><table id="table"><thead><tr><th data-sort="from">Röstgivare</th><th>Röst</th><th data-sort="ts">Tidpunkt</th><th>Riskerar att åka ut</th><th>Därefter</th></tr></thead><tbody></tbody></table>`;const h={votes:n,players:[],colors:{},manual:{},colorMode:"auto",filter:"",sort:"",index:null,limit:null,timeline:[],timer:null},g={status:o("#status"),exp:o("#exportBtn"),views:i('input[name="view"]'),filter:o("#filter"),colorMode:o("#colorMode"),manual:o("#manual"),play:o("#play"),delay:o("#delay"),slider:o("#slider"),ticks:o("#sliderTicks"),sliderLabel:o("#sliderLabel"),summary:o("#summary"),tbody:o("#table tbody"),ths:i("#table th"),canvas:o("#chart")},b=()=>g.views.find(t=>t.checked)?.value||"latest",x=t=>"bw"===h.colorMode?"#000":"manual"===h.colorMode?h.manual[t]||h.colors[t]||"#000":h.colors[t]||"#000";function y(){h.players=[...new Set(h.votes.flatMap(t=>[t.from,t.to]))];const t=h.players.slice().sort((t,e)=>t.localeCompare(e,"sv"));h.colors={},t.forEach((e,a)=>h.colors[e]=`hsl(${Math.round(360*a/Math.max(1,t.length))},70%,42%)`),g.filter.innerHTML='<option value="">Alla</option>',t.forEach(t=>{const e=document.createElement("option");e.value=e.textContent=t,e.style.color=x(t),e.style.fontWeight="bold",g.filter.append(e)}),h.players.includes(h.filter)||(h.filter=""),g.filter.value=h.filter,g.manual.hidden="manual"!==h.colorMode,g.manual.innerHTML="","manual"===h.colorMode&&t.forEach(t=>{h.manual[t]||=h.colors[t];const e=document.createElement("label"),a=document.createElement("span"),l=document.createElement("input");a.textContent=t,l.type="color",l.dataset.player=t,l.value=h.manual[t],e.append(a,l),g.manual.append(e)}),g.status.textContent=`${n.length} taggröster, ${r.length} sidor lästa.`}function v(){h.timeline=d(h.votes.filter(t=>t.ts&&!isNaN(+new Date(t.ts)))).map(t=>new Date(t.ts)),g.ticks.innerHTML="",h.timeline.forEach((t,e)=>{const a=document.createElement("option");a.value=String(e),g.ticks.append(a)});const t=Math.max(0,h.timeline.length-1);g.slider.max=String(t),(null==h.index||h.index>t)&&(h.index=t),h.index<0&&(h.index=0),g.slider.value=String(h.index),h.limit=h.timeline.length?h.timeline[h.index]:null,g.sliderLabel.textContent=h.timeline.length?`${h.index+1}/${h.timeline.length} röster (${c(h.limit)})`:"–",$()}function w(){let t=h.votes;return h.limit&&(t=t.filter(t=>+new Date(t.ts)<=+h.limit)),"latest"===b()&&(t=u(t)),h.filter&&(t=t.filter(t=>t.from===h.filter)),t}function M(t){const e=g.canvas.getContext("2d"),a=g.canvas.width,l=g.canvas.height;if(e.clearRect(0,0,a,l),!t.length)return;const n=180,r=Math.max(14,Math.floor((l-20)/t.length)-3),o=Math.max(...t.map(t=>t[1]));e.font="16px Arial",t.forEach((t,l)=>{const i=10+l*(r+3),s=Math.floor((a-n-10)*t[1]/o),c=String(t[1]);e.fillStyle=x(t[0]),e.fillText(t[0],10,i+r-2),e.fillRect(n,i,s,r),e.fillStyle="#fff",e.fillText(c,Math.max(184,n+s-e.measureText(c).width-4),i+r-2)})}function $(t=null,a=null){const l=d(null!==a?a:h.votes.filter(t=>!h.limit||+new Date(t.ts)<=+h.limit)),n=null!==t?t:w(),r=u(l),o=new Map,m=new Map;d(r).forEach(t=>{o.set(t.to,(o.get(t.to)||0)+1),(!m.has(t.to)||+new Date(t.ts)<+new Date(m.get(t.to)))&&m.set(t.to,t.ts)});const p=[...o.entries()].sort((t,e)=>e[1]-t[1]||+new Date(m.get(t[0])||0)-+new Date(m.get(e[0])||0));if(!p.length)return g.summary.textContent="Inga röster att visa.",g.tbody.innerHTML="",void M([]);const[f,b]=p[0],y=r.reduce((t,e)=>!t||+new Date(e.ts)>+new Date(t)?e.ts:t,null);g.summary.textContent=`⚠️ Risk för utröstning: ${f} (${b} röster, sedan ${c(m.get(f))}). Senast röst lagd ${c(y)}.`,g.tbody.innerHTML="";const v=new Map;d(n).forEach(t=>{const a=function(t,e){const a=new Map,l=new Map,n=new Map;return t.filter(t=>{const a=+new Date(t.ts),l=+new Date(e.ts);return a<l||a===l&&Number(t.post||0)<=Number(e.post||0)}).forEach(t=>{const e=a.get(t.from);if(e){const a=(l.get(e)||0)-1;a<=0?(l.delete(e),n.delete(e)):(l.set(e,a),n.set(e,{ts:t.ts,post:Number(t.post||0)}))}a.set(t.from,t.to),l.set(t.to,(l.get(t.to)||0)+1),n.set(t.to,{ts:t.ts,post:Number(t.post||0)})}),[...l.entries()].sort((t,e)=>e[1]-t[1]||+new Date(n.get(t[0])?.ts||0)-+new Date(n.get(e[0])?.ts||0)||(n.get(t[0])?.post||0)-(n.get(e[0])?.post||0))}(l,t),n=a[0]?`${a[0][0]} (${a[0][1]})`:"–",r=a[1]?`${a[1][0]} (${a[1][1]})`:"–",o=v.get(t.from)||[];o.at(-1)!==t.to&&o.push(t.to),v.set(t.from,o);const i=o.map((a,l,n)=>l===n.length-1?`<a target="_blank" rel="noopener" href="${s(e+"post-"+t.post)}" style="color:${x(a)};font-weight:bold">${s(a)}</a>`:`<span style="color:${x(a)}">${s(a)}</span>`).join(" → "),d=document.createElement("tr");d.dataset.ts=t.ts||"",d.innerHTML=`<td style="color:${x(t.from)};font-weight:bold">${s(t.from)}</td><td>${i}</td><td>${c(t.ts)}</td><td>${s(n)}</td><td>${s(r)}</td>`,g.tbody.append(d)}),function(){if(!h.sort)return;const[t,e]=h.sort.split("-"),a=i("#table tbody tr");a.sort((a,l)=>{const n="from"===t?a.children[0].textContent.trim():a.dataset.ts||"",r="from"===t?l.children[0].textContent.trim():l.dataset.ts||"",o="from"===t?n.localeCompare(r,"sv"):n.localeCompare(r);return"desc"===e?-o:o}),g.tbody.replaceChildren(...a)}(),M(p)}function k(){h.timer&&(clearTimeout(h.timer),h.timer=null),g.play.disabled=!1}g.exp.onclick=()=>{const t=document.createElement("a"),e=["Röstgivare,Röst,Tidpunkt,Post,Page",...w().map(t=>[t.from,t.to,t.ts,t.post,t.page].map(m).join(","))].join("\n"),a=URL.createObjectURL(new Blob(["\ufeff"+e],{type:"text/csv;charset=utf-8"}));t.href=a,t.download="rostdata.csv",t.click(),setTimeout(()=>URL.revokeObjectURL(a),1e3)},g.views.forEach(t=>t.onchange=()=>{k(),h.index=null,v()}),g.filter.onchange=()=>{k(),h.filter=g.filter.value,h.index=null,v()},g.colorMode.onchange=()=>{h.colorMode=g.colorMode.value,y(),$()},g.manual.oninput=t=>{"color"===t.target.type&&(h.manual[t.target.dataset.player]=t.target.value,y(),$())},g.slider.oninput=()=>{k(),h.index=Number(g.slider.value),h.limit=h.timeline[h.index]||null,g.sliderLabel.textContent=h.timeline.length?`${h.index+1}/${h.timeline.length} röster (${c(h.limit)})`:"–",$()},g.play.onclick=()=>{k(),g.play.disabled=!0;const t=d(h.votes.filter(t=>!h.limit||+new Date(t.ts)<=+h.limit));let e=0;const a=Math.max(0,Number(g.delay.value)||200);!function l(){if(e>t.length)return k(),void $();const n=t.slice(0,e);let r="all"===b()?n:u(n);h.filter&&(r=r.filter(t=>t.from===h.filter)),$(r,n),e++,h.timer=setTimeout(l,a)}()},g.ths.forEach(t=>{const e=t.dataset.sort;e&&(t.onclick=()=>{h.sort=e+"-"+(h.sort===e+"-asc"?"desc":"asc"),$()})}),y(),v()})();

4) När du sedan öppnar en runda och trycker på bokmärket (och väntar några sekunder) så kommer den visa rösträknaren för dig.
OBS: Du måste alltså vara inne på själva rollspel.nu-tråden när du klickar bokmärket.

OBS 2: På mobil kör man bookmarklets (som det kallas) lite annorlunda. Istället för att öppna listan över bokmärken och trycka på det därifrån så får man trycka på adressfältet och börja skriva in namnet på sitt bookmarklet och trycka på det där (som när man skriver en adress till en sida och trycker på den). När man går in i menyn för bokmärken så tappar den bort vilken sida man befinner sig på.
 
Back
Top