Braziers from a 2,500-year-old cemetery give the earliest clear evidence of cannabis smoked for its effect
Science Advances — The origins of cannabis smoking, chemical residue evidence from the first millennium BCE in the Pamirs (2019) 1 unchanged
Changelog 66b46ac
6 history entries edited11 articles written4 files of code, design or docs +1,870 −1,440 lines
Science Advances — The origins of cannabis smoking, chemical residue evidence from the first millennium BCE in the Pamirs (2019) 1 unchanged
The Guardian / Associated Press — Albanian cannabis growers and 800 police battle in Lazarat (17 June 2014) 1 unchanged
Rasquera, a Catalan village of 960 people, held a referendum on leasing land to ABCDA, a B … The plan was said to bring about 40 jobs, and it needed 75 per cent approval to go ahead. It won 56.3 per cent, so it missed that threshold. The national drug office said growing on that scale would break the law and pledged to blo … that a town's financial trouble does not make such contracts a matter of public interest.
El País — The mayor of Rasquera throws in the towel (12 April 2012) 2 unchanged
1989-06-29
The Bhutanese — 41 dead and counting, Bhutan's growing drug problem (2013) 1 unchanged
Forces of Necessity: Lay Advocacy and the Remedicalization of Cannabis in the UK, 1973–2004 1 unchanged
Addiction — Conversation with Raphael Mechoulam (2007) Journal of Cannabis Research — The father of cannabis research, Q&A with Raphael Mechoulam (30 January 2020) 2 unchanged
source-archive.json +7 Not shown: a picture, a generated file, or past the length shown per change.
src/components/ProjectNumbers.astro +1 @@ -22,5 +22,5 @@ const stats = [ [num(dated.filter((ch) => ch.score).length), "Of them moved a score", `${base}history/?only=scored`], [num(articles.size), "Articles", `${base}articles/`], - [num(sourceKeys.size), "Sources cited", `${base}data/`], + [num(sourceKeys.size), "Sources cited", `${base}sources/`], [num(explanations), "Written explanations", `${base}data/`], [num(+DATA_SNAPSHOT - oldest), `Years covered, from ${oldest}`, `${base}history/?order=oldest`],
src/pages/sources/index.astro +39 @@ -6,4 +6,5 @@ import Base from "../../layouts/Base.astro"; import { getCollection } from "astro:content"; import { places } from "../../lib/countries.js"; +import { archivedUrl } from "../../lib/archive.js"; const base = import.meta.env.BASE_URL; @@ -66,8 +67,15 @@ const localRows = [...local] .filter((x) => x.c) .sort((x, y) => x.c.name.localeCompare(y.c.name)); -const urlCount = new Set(cites.filter((x) => x.s.url).map((x) => x.s.url)).size; +// Site icons come from DuckDuckGo's icon service and are not stored here; a missing one is hidden. +const icon = (d) => `https://icons.duckduckgo.com/ip3/${d}.ico`; +const urls = new Set(cites.filter((x) => x.s.url).map((x) => x.s.url)); +const urlCount = urls.size; +// pages with a copy recorded in the Internet Archive (see source-archive.json) +const archivedOf = (set) => [...set].filter((u) => archivedUrl(u)).length; +const archived = archivedOf(urls); const stats = [ [num(cites.length), "Citations"], [num(urlCount), "Web pages cited"], + [`${num(archived)}`, `Archived (${Math.round((archived / urlCount) * 100)}%)`], [num(all.length), "Websites"], [num(wide.length), "Cited for several countries"], @@ -82,4 +90,5 @@ const stats = [ <h1>What the index is built on</h1> <p class="lede">Every score, explanation and history entry cites where it comes from. This page counts those sources: books first, then websites, with the local ones grouped under the country they were used for. A citation is one source listed once by a country profile, a history entry or an article.</p> + <p class="lede">{num(archived)} of the {num(urlCount)} web pages have a copy saved in the Internet Archive, so a dead link does not take the evidence with it. The rest are being saved in batches.</p> </section> @@ -91,22 +100,28 @@ const stats = [ <div class="section-head"><div> <h2>Books</h2> - <p class="sub">Printed sources, with the chapters that are cited and how often.</p> + <p class="sub">Printed sources, most cited first. Open a book to see which chapters are cited and how often.</p> </div></div> - {bookList.map((b) => ( - <div class="src-book"> - <h3>{b.title}</h3> - <p class="src-meta">{[b.editors && `Edited by ${b.editors.join(" and ")}`, b.authors && b.authors.join(", "), b.publisher, b.year, b.isbn && `ISBN ${b.isbn}`].filter(Boolean).join(" · ")} <b>{b.n} {b.n === 1 ? "citation" : "citations"}</b></p> - {b.chapters.size > 0 && ( - <table class="src-table"> - <thead><tr><th>Chapter</th><th>Author</th><th class="num">Cited</th></tr></thead> - <tbody> - {[...b.chapters.values()].sort((x, y) => x.page - y.page).map((ch) => ( - <tr><td>{ch.title}</td><td>{ch.authors.join(", ")}</td><td class="num">{ch.n}</td></tr> - ))} - </tbody> - </table> - )} - </div> - ))} + <div class="src-books"> + {bookList.map((b) => ( + <details class="src-book"> + <summary> + <span class="src-book-title">{b.title}</span> + <span class="src-book-by">{[b.editors && `Ed. ${b.editors.join(" and ")}`, b.authors && b.authors.join(", "), b.publisher, b.year].filter(Boolean).join(" · ")}</span> + <span class="src-book-n">{b.chapters.size > 0 && `${b.chapters.size} chapters · `}{b.n} {b.n === 1 ? "citation" : "citations"}</span> + </summary> + {b.isbn && <p class="src-meta">ISBN {b.isbn}</p>} + {b.chapters.size > 0 && ( + <table class="src-table"> + <thead><tr><th>Chapter</th><th>Author</th><th class="num">Cited</th></tr></thead> + <tbody> + {[...b.chapters.values()].sort((x, y) => x.page - y.page).map((ch) => ( + <tr><td>{ch.title}</td><td>{ch.authors.join(", ")}</td><td class="num">{ch.n}</td></tr> + ))} + </tbody> + </table> + )} + </details> + ))} + </div> </section> @@ -117,8 +132,8 @@ const stats = [ </div></div> <table class="src-table"> - <thead><tr><th>Website</th><th class="num">Pages</th><th class="num">Citations</th><th class="num">Countries</th></tr></thead> + <thead><tr><th>Website</th><th class="num">Pages</th><th class="num">Archived</th><th class="num">Citations</th><th class="num">Countries</th></tr></thead> <tbody> {wide.map((r) => ( - <tr><td><a href={`https://${r.domain}/`} target="_blank" rel="noopener noreferrer">{r.domain}</a></td><td class="num">{num(r.urls.size)}</td><td class="num">{num(r.n)}</td><td class="num">{[...r.where.keys()].filter(Boolean).length}</td></tr> + <tr><td><a href={`https://${r.domain}/`} target="_blank" rel="noopener noreferrer"><img class="src-ico" src={icon(r.domain)} alt="" width="16" height="16" loading="lazy" decoding="async" referrerpolicy="no-referrer" onerror="this.style.visibility='hidden'" />{r.domain}</a></td><td class="num">{num(r.urls.size)}</td><td class="num">{num(archivedOf(r.urls))}</td><td class="num">{num(r.n)}</td><td class="num">{[...r.where.keys()].filter(Boolean).length}</td></tr> ))} </tbody> @@ -132,10 +147,12 @@ const stats = [ </div></div> <table class="src-table src-local"> - <thead><tr><th>Country</th><th>Websites</th><th class="num">Citations</th></tr></thead> + <thead><tr><th>Country</th><th>Websites</th><th class="num">Pages</th><th class="num">Archived</th><th class="num">Citations</th></tr></thead> <tbody> {localRows.map(({ c, rows, n }) => ( <tr> <th scope="row"><a href={`${base}country/${c.slug}/`}>{c.flag} {c.name}</a></th> - <td>{rows.map((r, i) => <><a href={`https://${r.domain}/`} target="_blank" rel="noopener noreferrer">{r.domain}</a>{r.n > 1 && <small> {r.n}</small>}{i < rows.length - 1 && <span class="sep"> · </span>}</>)}</td> + <td>{rows.map((r, i) => <><a class="src-site" href={`https://${r.domain}/`} target="_blank" rel="noopener noreferrer"><img class="src-ico" src={icon(r.domain)} alt="" width="16" height="16" loading="lazy" decoding="async" referrerpolicy="no-referrer" onerror="this.style.visibility='hidden'" />{r.domain}</a>{r.n > 1 && <small> {r.n}</small>}{i < rows.length - 1 && <span class="sep"> · </span>}</>)}</td> + <td class="num">{rows.reduce((a, r) => a + r.urls.size, 0)}</td> + <td class="num">{rows.reduce((a, r) => a + archivedOf(r.urls), 0)}</td> <td class="num">{n}</td> </tr>
src/styles/global.css +19 @@ -1271,5 +1271,5 @@ details.segrow .segrow-top > span, .segrow:not(details) .segrow-top > span { fon .src-table th, .src-table td { padding: 9px 16px 9px 0; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; } .src-table thead th { font: 400 10.5px var(--mono); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); cursor: default; } -.src-table .num { text-align: right; padding-right: 0; font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; } +.src-table .num { text-align: right; padding: 9px 0 9px 22px; font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; } .src-table .mono { font-family: var(--mono); font-size: 12.5px; white-space: nowrap; } .src-table td a:hover, .src-table th a:hover { text-decoration: underline; text-underline-offset: 3px; } @@ -1278,7 +1278,22 @@ details.segrow .segrow-top > span, .segrow:not(details) .segrow-top > span { fon .src-local td small { font: 400 10.5px var(--mono); color: var(--muted); } .src-local .sep { color: var(--border); } -.src-book + .src-book { margin-top: 40px; } -.src-book h3 { font: 400 30px/1.1 var(--display); margin: 0; } -.src-meta { margin: 8px 0 16px; color: var(--text-2); font-size: 13.5px; } +.src-ico { width: 16px; height: 16px; margin-right: 9px; vertical-align: -3px; border-radius: 3px; } +.src-site { white-space: nowrap; } +.src-site .src-ico { width: 14px; height: 14px; margin-right: 6px; } +/* books: one ruled line each; the chapters open underneath */ +.src-books { border-top: 1px solid var(--text); } +.src-book { border-bottom: 1px solid var(--border); } +.src-book summary { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 4fr) auto; gap: 4px 24px; align-items: baseline; padding: 10px 0; cursor: pointer; list-style: none; } +.src-book summary::-webkit-details-marker { display: none; } +.src-book-title { font: 400 20px/1.2 var(--display); } +.src-book summary:hover .src-book-title { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; } +.src-book-by { color: var(--text-2); font-size: 13.5px; } +.src-book-n { font: 400 10.5px var(--mono); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); white-space: nowrap; text-align: right; } +.src-book-n::after { content: " +"; } +.src-book[open] .src-book-n::after { content: " −"; } +.src-book .src-table { border-top: 0; margin: 0 0 14px; font-size: 13.5px; } +.src-book .src-table tr:last-child td { border-bottom: 0; } +.src-meta { margin: 0 0 6px; color: var(--text-2); font-size: 13px; } +@media (max-width: 760px) { .src-book summary { grid-template-columns: 1fr; } .src-book-n { text-align: left; } } .src-meta b { font: 400 10.5px var(--mono); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-left: 10px; } .sources-page .hist-stats { margin-top: 8px; }