Compare commits
4 Commits
1d851e23a6
...
f3cbc8e24b
Author | SHA1 | Date | |
---|---|---|---|
f3cbc8e24b | |||
7840029f8c | |||
ae583c7bb7 | |||
026c439f02 |
120
_src/js/data.js
Normal file
120
_src/js/data.js
Normal file
@ -0,0 +1,120 @@
|
||||
import "../lib/jsonld.esm.js.js";
|
||||
import jsonldRdfaParser from '../lib/jsonld8-rdfa.esm.js';
|
||||
|
||||
jsonld.registerRDFParser('text/html', jsonldRdfaParser);
|
||||
|
||||
export const GLOBAL_CONTEXT = {
|
||||
"@vocab": "http://schema.org/",
|
||||
"@language": "fr"
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch remote content data
|
||||
* @param {String|URL} documentUrl Document URL to fetch
|
||||
* @param {Object} context Optional context to format JSON
|
||||
* @returns JSON-LD of data extracted from the provided page
|
||||
*/
|
||||
export async function fetchDocumentData(documentUrl, context = GLOBAL_CONTEXT){
|
||||
let res = await fetch(documentUrl);
|
||||
|
||||
if(!res.ok)
|
||||
throw new Error(`Network error ${res.status} ${res.statusText}`)
|
||||
|
||||
if(!res.headers.get("Content-Type").startsWith("text/html"))
|
||||
throw new Error(`Invalid content type ${res.headers.get("Content-Type")}`)
|
||||
|
||||
let content = await res.text()
|
||||
|
||||
let remoteDoc = new DOMParser().parseFromString(content, "text/html");
|
||||
|
||||
let base = document.createElement("base")
|
||||
base.href = documentUrl
|
||||
remoteDoc.head.append(base)
|
||||
|
||||
return await getDocumentData(remoteDoc, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract data from a page document
|
||||
* @param {Document} sourceDocument Page document to extract data from
|
||||
* @param {Object} context Optional context to format JSON
|
||||
* @returns JSON-LD of data extracted from the page
|
||||
*/
|
||||
export async function getDocumentData(sourceDocument = document, context = GLOBAL_CONTEXT) {
|
||||
let resource = sourceDocument.body.getAttribute("resource")
|
||||
|
||||
if(resource)
|
||||
resource = new URL(resource, sourceDocument.baseURI)
|
||||
|
||||
return await extractData(
|
||||
sourceDocument.documentElement,
|
||||
resource,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JSON data from a RDFa Formatted element
|
||||
* @param {Element} sourceElement Source element to extract data from
|
||||
* @param {Object} context Optional context to format JSON
|
||||
* @returns JSON-LD of data extract from element
|
||||
*/
|
||||
export async function getData(sourceElement, context = GLOBAL_CONTEXT){
|
||||
let resource = sourceElement.getAttribute("resource");
|
||||
|
||||
if(resource)
|
||||
resource = new URL(resource, sourceElement.baseURI)
|
||||
|
||||
return await extractData(
|
||||
sourceElement,
|
||||
resource,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
export function takeFirst(self){
|
||||
if(!self){
|
||||
return self
|
||||
}
|
||||
|
||||
if(Array.isArray(self)){
|
||||
return self[0]
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
export function takeUrl(self){
|
||||
if(!self){
|
||||
return self
|
||||
}
|
||||
|
||||
let first = takeFirst(self)
|
||||
return first["@id"]
|
||||
}
|
||||
|
||||
async function extractData(baseElement, rootId, context){
|
||||
let sourceData = await jsonld.fromRDF(baseElement, {format: 'text/html'});
|
||||
|
||||
if(rootId) {
|
||||
|
||||
let frame = {
|
||||
"@id": rootId
|
||||
}
|
||||
|
||||
if(context) {
|
||||
frame["@context"] = context
|
||||
}
|
||||
|
||||
return await jsonld.frame(sourceData, frame)
|
||||
|
||||
} else if (context) {
|
||||
|
||||
return await jsonld.compact(sourceData, context)
|
||||
|
||||
} else {
|
||||
|
||||
return sourceData
|
||||
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
|
||||
header {
|
||||
margin-bottom: 10vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
@ -14,6 +17,8 @@
|
||||
|
||||
font-family: "Degheest Director";
|
||||
font-weight: 600;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
header h1 > a {
|
||||
@ -33,13 +38,37 @@
|
||||
header h1 small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
nav li, nav li a {
|
||||
color: #fff600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
nav li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
padding: none
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
<header>
|
||||
<h1><a href="/">
|
||||
<img src="/_src/img/logo_kiosque.svg" alt="Logo du site" />
|
||||
<span>Infokiosques du LOL <small>Brochures et autres textes</small></span>
|
||||
<span>Infokiosque du LOL <small>Brochures et autres textes</small></span>
|
||||
</a></h1>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="https://labolyon.fr">Labolyon.fr</a></li>
|
||||
<li><a href="https://wiki.labolyon.fr/doku.php?id=infokiosque">Wiki</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<slot></slot>
|
3413
_src/lib/jsonld.esm.js.js
Normal file
3413
_src/lib/jsonld.esm.js.js
Normal file
File diff suppressed because one or more lines are too long
2435
_src/lib/jsonld8-rdfa.esm.js
Normal file
2435
_src/lib/jsonld8-rdfa.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@ body > * {
|
||||
h1 {
|
||||
font-family: "Degheest Director";
|
||||
font-weight: 700;
|
||||
font-size: 3em;
|
||||
font-size: min(3em, 10vw);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
@ -103,9 +103,13 @@ ul li > a:first-child::before {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
margin-left: -1.5ex;
|
||||
margin-right: 0.5ex;
|
||||
}
|
||||
|
||||
i, ul li > a:first-child::before {
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
ul li > a:first-child:hover::before {
|
||||
@ -113,6 +117,111 @@ ul li > a:first-child:hover::before {
|
||||
transform: translateX(0.25ex);
|
||||
}
|
||||
|
||||
#publications ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#publications ul li > a:first-child::before {
|
||||
content: unset;
|
||||
}
|
||||
|
||||
#publications ul li > a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#publications .publication-card {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
body > section {
|
||||
margin-top: 10vh;
|
||||
}
|
||||
|
||||
/* home */
|
||||
|
||||
.home > h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home nav li a::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.home nav ul {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.home nav li {
|
||||
color: #fff600;
|
||||
}
|
||||
|
||||
.home nav ul li:not(:last-child)::after {
|
||||
content: "•";
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* publication card */
|
||||
|
||||
.publication-card {
|
||||
display: grid;
|
||||
grid-template-columns: 150px 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-auto-rows: min-content;
|
||||
column-gap: 15px;
|
||||
|
||||
text-decoration: none !important;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.publication-card > img:first-child {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / 3;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.publication-card h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.publication-card p {
|
||||
margin: 0;
|
||||
margin-top: 15px;
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
.publication-card nav {
|
||||
text-align: right;
|
||||
margin-top: 15px;
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.publication-card {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.publication-card h1 {
|
||||
font-size: 1.2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.publication-card > img:first-child {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
max-width: 150px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.publication-card p, .publication-card nav {
|
||||
grid-column: 1;
|
||||
}
|
||||
}
|
101
index.html
101
index.html
@ -4,18 +4,26 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Infokiosques LOL</title>
|
||||
<title>Infokiosque LOL</title>
|
||||
|
||||
<link rel="stylesheet" href="/_src/styles/global.css" />
|
||||
|
||||
</head>
|
||||
<body vocab="http://schema.org/" typeof="Website" resource="." >
|
||||
<body vocab="http://schema.org/" typeof="Website" resource="." class="home">
|
||||
|
||||
<img src="/_src/img/logo_kiosque.svg" alt="Logo du site" />
|
||||
|
||||
<h1 property="name">Inforkiosques du LOL <small>Brochures et autres textes</small></h1>
|
||||
<h1 property="name">Infokiosque du LOL <small>Brochures et autres textes</small></h1>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="https://labolyon.fr">Labolyon.fr</a></li>
|
||||
<li><a href="https://wiki.labolyon.fr/doku.php?id=infokiosque">Wiki</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<section id="publications"
|
||||
property="hasPart"
|
||||
typeof="Collection"
|
||||
resource="#publications" >
|
||||
|
||||
@ -28,14 +36,87 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Resources</h2>
|
||||
<template id="publication-card-tmplt">
|
||||
<article class="publication-card">
|
||||
<img property="image" src="" alt="Couverture" />
|
||||
<h1 property="name"></h1>
|
||||
<div class="meta">
|
||||
<span>Par <span property="author"></span></span>
|
||||
</div>
|
||||
<p property="abstract"></p>
|
||||
<nav>
|
||||
<a href="" property="url">Plus d'infos <i>🠺</i></a>
|
||||
</nav>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://labolyon.fr">labolyon.fr</a></li>
|
||||
<li><a href="https://equa.space/notes/pdfjam/">using <code>pdfjam</code> for zine creation and bookbinding</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<script type="module">
|
||||
import {fetchDocumentData, takeFirst, takeUrl} from "/_src/js/data.js"
|
||||
|
||||
init()
|
||||
|
||||
async function init() {
|
||||
for(let it of document.querySelectorAll("#publications ul > li a[href]")) {
|
||||
let data = null;
|
||||
|
||||
try {
|
||||
data = await fetchDocumentData(it.href)
|
||||
} catch(e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let card = null;
|
||||
try {
|
||||
card = makePublicationCard(data)
|
||||
} catch(e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
it.innerHTML = ""
|
||||
it.append(card)
|
||||
}
|
||||
}
|
||||
|
||||
function makePublicationCard(data){
|
||||
let template = document.getElementById("publication-card-tmplt").content.cloneNode(true);
|
||||
|
||||
let root = template.children[0]
|
||||
root.setAttribute("resource", takeFirst(data["@id"]))
|
||||
root.setAttribute("typeof", takeFirst(data["@type"]))
|
||||
|
||||
let name = takeFirst(data.name)
|
||||
if(name){
|
||||
template.querySelector('[property="name"]').textContent = name
|
||||
} else {
|
||||
throw new Error("Provided data doesn't have a \"name\" field");
|
||||
}
|
||||
|
||||
let author = takeFirst(data.author)
|
||||
if(author){
|
||||
template.querySelector('[property="author"]').textContent = author
|
||||
} else {
|
||||
template.querySelector('[property="author"]').parentElement.remove()
|
||||
}
|
||||
|
||||
let abstract = takeFirst(data.abstract)
|
||||
if(abstract){
|
||||
template.querySelector('[property="abstract"]').textContent = abstract
|
||||
} else {
|
||||
template.querySelector('[property="abstract"]').remove()
|
||||
}
|
||||
|
||||
let image = takeUrl(data.image)
|
||||
if(image){
|
||||
template.querySelector('[property="image"]').src = image
|
||||
} else {
|
||||
template.querySelector('[property="image"]').remove()
|
||||
}
|
||||
|
||||
let url = takeUrl(data.url)
|
||||
template.querySelector('[property="url"]').href = url || takeUrl(data)
|
||||
|
||||
return template
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Détruire le capitalisme de surveillance — Cory Doctorow — Infokiosques LOL</title>
|
||||
<title>Détruire le capitalisme de surveillance — Cory Doctorow — Infokiosque LOL</title>
|
||||
<link rel="stylesheet" href="/_src/styles/global.css" />
|
||||
<link rel="prefetch" href="/_src/js/layout.html" />
|
||||
<script defer src="/_src/js/layout.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user