mirror of
https://github.com/restincode/restincode.git
synced 2026-09-11 17:19:30 +02:00
The references fix in #331 covered only one of the render paths. Apply the same DOM-construction pattern to personal info, obituary, social media, contributions, gallery and the help-link fallbacks, plus the people grid in index.html. maintext is still rendered as HTML to preserve existing formatted bios; flagged for a DOMPurify follow-up.
275 lines
9.3 KiB
HTML
275 lines
9.3 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>Rest In Code - In Memory of</title>
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta
|
|
name="author"
|
|
content="Connie 'Sunfire' Hill and Rest In Code contributors"
|
|
/>
|
|
|
|
<link rel="stylesheet" href="style.css" />
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
<link rel="shortcut icon" type="image/x-icon" href="/images/favicon.png" />
|
|
|
|
<script
|
|
src="https://code.jquery.com/jquery-3.7.0.min.js"
|
|
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g="
|
|
crossorigin="anonymous"
|
|
></script>
|
|
<script>
|
|
$(function () {
|
|
var person;
|
|
var params = new URLSearchParams(window.location.search);
|
|
if (params.get("name") != null) {
|
|
var name = params.get("name");
|
|
person = "/people/" + encodeURIComponent(name) + ".json";
|
|
} else {
|
|
person = "/people/person.json";
|
|
}
|
|
|
|
function safeUrl(url) {
|
|
try {
|
|
var u = new URL(url, window.location.origin);
|
|
return u.protocol === "http:" || u.protocol === "https:"
|
|
? u.href
|
|
: "#";
|
|
} catch (e) {
|
|
return "#";
|
|
}
|
|
}
|
|
|
|
function txt(value) {
|
|
return document.createTextNode(value == null ? "" : String(value));
|
|
}
|
|
|
|
$.getJSON(person, function (data) {
|
|
var fullname = data.firstname + " " + data.lastname;
|
|
if (data.handle != "") {
|
|
fullname += " (" + data.handle + ")";
|
|
}
|
|
|
|
document.title = document.title + fullname;
|
|
$("#subtitle").append(txt(fullname));
|
|
$("#memorial-name").text(fullname);
|
|
$(".birth").append(txt(data.birth));
|
|
$(".death").append(txt(data.death));
|
|
|
|
if (data.affiliations == null || data.affiliations == "") {
|
|
$(".affiliations").append(txt("None"));
|
|
} else {
|
|
$(".affiliations").append(txt(data.affiliations));
|
|
}
|
|
|
|
if (data.obituary != null && data.obituary != "") {
|
|
$(".obituary").append(
|
|
$("<a>").attr("href", safeUrl(data.obituary)).text("Obituary"),
|
|
);
|
|
}
|
|
|
|
var issueUrl;
|
|
if (data.issue == null || data.issue == "") {
|
|
issueUrl =
|
|
"https://github.com/restincode/restincode/blob/master/CONTRIBUTING.md";
|
|
} else {
|
|
issueUrl =
|
|
"https://github.com/restincode/restincode/issues/" +
|
|
encodeURIComponent(data.issue);
|
|
}
|
|
var safeIssueUrl = safeUrl(issueUrl);
|
|
|
|
function helpLink() {
|
|
return $("<a>").attr("href", safeIssueUrl).text("here.");
|
|
}
|
|
|
|
if (data.mainimage == "") {
|
|
$(".memorial-main-image")
|
|
.attr("src", "/images/face-silhouette-clipart.png")
|
|
.attr("alt", "placeholder photo");
|
|
} else {
|
|
$(".memorial-main-image")
|
|
.attr("src", safeUrl(data.mainimage))
|
|
.attr("alt", "Main photo of " + fullname);
|
|
}
|
|
|
|
if (data.maintext == "") {
|
|
$("#memorial-text").append(
|
|
$("<p>").append(
|
|
"No information has been submitted for this person. Help us by submitting ",
|
|
helpLink(),
|
|
),
|
|
);
|
|
} else {
|
|
// maintext is intentionally rendered as HTML for formatted bios.
|
|
// Residual XSS risk is mitigated by PR review; track follow-up
|
|
// to integrate DOMPurify for defense-in-depth.
|
|
$("#memorial-text").append(data.maintext);
|
|
}
|
|
|
|
if (
|
|
!Array.isArray(data.socialmedialinks) ||
|
|
!data.socialmedialinks.length
|
|
) {
|
|
$("#social-media").append(
|
|
$("<p>").append(
|
|
"No social media links have been submitted for this person. Help us by submitting ",
|
|
helpLink(),
|
|
),
|
|
);
|
|
} else {
|
|
for (var s = 0; s < data.socialmedialinks.length; s++) {
|
|
var sitename = data.socialmedialinks[s]["sitename"];
|
|
var siteurl = data.socialmedialinks[s]["siteurl"];
|
|
$("#social-media").append(
|
|
$("<a>").attr("href", safeUrl(siteurl)).text(sitename),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!Array.isArray(data.references) || !data.references.length) {
|
|
$("#references").append(
|
|
$("<p>").append(
|
|
"No references have been submitted for this person. Help us by submitting ",
|
|
helpLink(),
|
|
),
|
|
);
|
|
} else {
|
|
for (var r = 0; r < data.references.length; r++) {
|
|
$("#references").append(
|
|
$("<a>")
|
|
.attr("href", safeUrl(data.references[r]["url"]))
|
|
.text(data.references[r]["title"]),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (
|
|
!Array.isArray(data.contributions) ||
|
|
!data.contributions.length
|
|
) {
|
|
$("#contributions-list").append(
|
|
$("<li>").append(
|
|
"No contributions have been submitted for this person. Help us by submitting ",
|
|
helpLink(),
|
|
),
|
|
);
|
|
} else {
|
|
for (var x = 0; x < data.contributions.length; x++) {
|
|
var title = data.contributions[x]["title"];
|
|
var url = data.contributions[x]["url"];
|
|
var text = data.contributions[x]["description"];
|
|
var $li = $("<li>");
|
|
if (url == "") {
|
|
$li.append($("<b>").text(title));
|
|
} else {
|
|
$li.append($("<a>").attr("href", safeUrl(url)).text(title));
|
|
}
|
|
$li.append(txt(" - " + text));
|
|
$("#contributions-list").append($li);
|
|
}
|
|
}
|
|
|
|
if (!Array.isArray(data.gallery) || !data.gallery.length) {
|
|
$("#nogallery").append(
|
|
$("<p>").append(
|
|
"No images have been submitted for this person yet. Help us by submitting ",
|
|
helpLink(),
|
|
),
|
|
);
|
|
} else {
|
|
for (var i = 0; i < data.gallery.length; i++) {
|
|
var imgurl = safeUrl(data.gallery[i]["url"]);
|
|
var imgtitle = data.gallery[i]["title"];
|
|
var caption = data.gallery[i]["caption"];
|
|
$("#gallery").append(
|
|
$("<figure>")
|
|
.addClass("memorial-gallery")
|
|
.append(
|
|
$("<img>")
|
|
.attr("src", imgurl)
|
|
.attr("alt", imgtitle)
|
|
.attr("loading", "lazy"),
|
|
$("<figcaption>").text(caption),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
$(".loading-message").remove();
|
|
}).fail(function () {
|
|
$(".loading-message").replaceWith(
|
|
'<p class="error-message">Unable to load memorial data. The person may not exist or there was a network error.</p>',
|
|
);
|
|
});
|
|
});
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<a href="#main-content" class="skip-link">Skip to content</a>
|
|
|
|
<header>
|
|
<div class="header-inner">
|
|
<h1 id="main-title">
|
|
RestInCode - <span id="subtitle">In Memory of </span>
|
|
</h1>
|
|
<nav class="header-nav">
|
|
<a href="about.html">About</a>
|
|
<a href="https://github.com/restincode/restincode">Contribute</a>
|
|
<a href="https://github.com/restincode/restincode/issues">Contact</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<main id="main-content" class="memorial-layout">
|
|
<a href="index.html" class="back-link">← Back to memorials</a>
|
|
|
|
<h2 id="memorial-name"></h2>
|
|
|
|
<div class="memorial-content">
|
|
<article class="memorial-main">
|
|
<img src="#" alt="memorial image" class="memorial-main-image" />
|
|
<div id="memorial-text">
|
|
<p class="loading-message">Loading memorial...</p>
|
|
</div>
|
|
|
|
<hr />
|
|
<h3>Contributions</h3>
|
|
<ul id="contributions-list"></ul>
|
|
|
|
<hr />
|
|
<section id="gallery-section" aria-label="Photo gallery">
|
|
<h3>Gallery</h3>
|
|
<div id="nogallery">
|
|
<div id="gallery"></div>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
|
|
<aside>
|
|
<p class="birth"><b>Born:</b></p>
|
|
<p class="death"><b>Died:</b></p>
|
|
<p class="affiliations"><b>Affiliations:</b><br /></p>
|
|
<p class="obituary"></p>
|
|
<div id="social-media">
|
|
<h4>Social Media</h4>
|
|
</div>
|
|
<div id="references">
|
|
<h4>References</h4>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</main>
|
|
|
|
<footer>
|
|
<a href="index.html">← Back to memorials</a> | Memorial content
|
|
belongs to its respective creators and rights holders. We do our best to
|
|
identify and credit them.
|
|
</footer>
|
|
</body>
|
|
</html>
|