kcl-digital-humanities-garden/statement-of-intent.html

401 lines
16 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Statement of Intent 🌱 Digital Garden</title>
<meta name="description" content="&lt;p&gt;Hyphas practice is situated across many topics that are present in the theme of &lt;em&gt;Adaptive Reuse &amp;amp; Creative Misuse&lt;/em&gt;. Drawing from our collective experiences, histories, and methodologies, our goal for the micro-residency to investigate how notions of digital infrastructure can be reused, reinterpreted, and reconfigured, to realize a kind of public space. Our approach to this theme will be composed of a few, very preliminary, subjects that will ground the residency: the situated histories of digital infrastructure, the implications of [[protocols]] for publishing ([[Hypertext]], [[RSS]], Peer-to-peer) in defining public spaces, and the possibilities of cooperative approaches to maintenance and repair. Our intent is to make the process of this investigation public through online tools mapping our thinking about the theme (Open channels in Are.na as one example) and cultivating a Digital Public Garden as part of Hyphas contributions to the initiative (a resyndicatable adaptive online notebook). The outputs from the micro-residency will be a written contribution to the &lt;a href=&quot;https://www.are.na/from-later/field-guide-to-the-digital-real&quot;&gt;&lt;em&gt;Field Guide to the Digital Real&lt;/em&gt;&lt;/a&gt; and a micro-website containing the synthesis of our investigations and our evolving practice. The outputs will be textual and visual, and draw from our collaborative practices as a cooperative. They will explore ways to represent relationships with existing and emergent technologies within our communities. Through our micro-residency we will capture a poetic interpretation of the theme and provide prompts for institutions in the city on how they could reconfigure technology to create radically creative platforms.&lt;/p&gt;
">
<link rel="stylesheet" href="/assets/css/style.css"><link type="application/atom+xml" rel="alternate" href="/feed.xml" title="Digital Garden" /></head><body class="dark-green garamond links-dark-green bg-washed-green"><header class="f-6 flex"><a class="no-underline-hover" rel="author" href="/">Digital Garden</a><nav>, <a class="no-underline-hover" href="/about/">About</a></nav></header><main aria-label="Content">
<div class="flex">
<article class="w-50">
<div>
<h1>Statement of Intent</h1>
<time datetime="2021-03-11T03:45:14+00:00">
Last updated on March 11, 2021
</time>
</div>
<div id="notes-entry-container">
<content>
<p>Hyphas practice is situated across many topics that are present in the theme of <em>Adaptive Reuse &amp; Creative Misuse</em>. Drawing from our collective experiences, histories, and methodologies, our goal for the micro-residency to investigate how notions of digital infrastructure can be reused, reinterpreted, and reconfigured, to realize a kind of public space. Our approach to this theme will be composed of a few, very preliminary, subjects that will ground the residency: the situated histories of digital infrastructure, the implications of <a class="internal-link" href="./protocols">protocols</a> for publishing (<a class="internal-link" href="./hypertext">Hypertext</a>, <a class="internal-link" href="./rss">RSS</a>, Peer-to-peer) in defining public spaces, and the possibilities of cooperative approaches to maintenance and repair. Our intent is to make the process of this investigation public through online tools mapping our thinking about the theme (Open channels in Are.na as one example) and cultivating a Digital Public Garden as part of Hyphas contributions to the initiative (a resyndicatable adaptive online notebook). The outputs from the micro-residency will be a written contribution to the <a href="https://www.are.na/from-later/field-guide-to-the-digital-real"><em>Field Guide to the Digital Real</em></a> and a micro-website containing the synthesis of our investigations and our evolving practice. The outputs will be textual and visual, and draw from our collaborative practices as a cooperative. They will explore ways to represent relationships with existing and emergent technologies within our communities. Through our micro-residency we will capture a poetic interpretation of the theme and provide prompts for institutions in the city on how they could reconfigure technology to create radically creative platforms.</p>
</content>
<side style="font-size: 0.9em">
<h3 style="margin-bottom: 1em">Notes mentioning this note</h3>
<div>
<p>
There are no notes linking to this note.
</p>
</div>
</side>
</div>
</article>
<div class="w-50">
<p>Here are all the notes in this garden, along with their links, visualized as a graph.</p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"
integrity="sha512-FHsFVKQ/T1KWJDGSbrUhTJyS1ph3eRrxI228ND0EGaEp6v4a/vGwPWd3Dtd/+9cI7ccofZvl/wulICEurHN1pg=="
crossorigin="anonymous"></script>
<div id="zoom"></div>
<div id="graph-wrapper">
<script>
const MINIMAL_NODE_SIZE = 8;
const MAX_NODE_SIZE = 12;
const ACTIVE_RADIUS_FACTOR = 1.5;
const STROKE = 1;
const FONT_SIZE = 16;
const TICKS = 200;
const FONT_BASELINE = 40;
const MAX_LABEL_LENGTH = 50;
const graphData = {"edges":[{"source":"229320080329284392969231993","target":"50678562812897"},{"source":"229320080329284392969231993","target":"72697617314704"},{"source":"229320080329284392969231993","target":"36028"}],"nodes":[{"id":"50678562812897","path":"/hypertext","label":"Hypertext"},{"id":"72697617314704","path":"/protocols","label":"Protocols"},{"id":"36028","path":"/rss","label":"RSS"},{"id":"229320080329284392969231993","path":"/statement-of-intent","label":"Statement of Intent"}]}
let nodesData = graphData.nodes;
let linksData = graphData.edges;
const nodeSize = {};
const updateNodeSize = () => {
nodesData.forEach((el) => {
let weight =
3 *
Math.sqrt(
linksData.filter((l) => l.source === el.id || l.target === el.id)
.length + 1
);
if (weight < MINIMAL_NODE_SIZE) {
weight = MINIMAL_NODE_SIZE;
} else if (weight > MAX_NODE_SIZE) {
weight = MAX_NODE_SIZE;
}
nodeSize[el.id] = weight;
});
};
const onClick = (d) => {
window.location = d.path
};
const onMouseover = function (d) {
const relatedNodesSet = new Set();
linksData
.filter((n) => n.target.id == d.id || n.source.id == d.id)
.forEach((n) => {
relatedNodesSet.add(n.target.id);
relatedNodesSet.add(n.source.id);
});
node.attr("class", (node_d) => {
if (node_d.id !== d.id && !relatedNodesSet.has(node_d.id)) {
return "inactive";
}
return "";
});
link.attr("class", (link_d) => {
if (link_d.source.id !== d.id && link_d.target.id !== d.id) {
return "inactive";
}
return "";
});
link.attr("stroke-width", (link_d) => {
if (link_d.source.id === d.id || link_d.target.id === d.id) {
return STROKE * 4;
}
return STROKE;
});
text.attr("class", (text_d) => {
if (text_d.id !== d.id && !relatedNodesSet.has(text_d.id)) {
return "inactive";
}
return "";
});
};
const onMouseout = function (d) {
node.attr("class", "");
link.attr("class", "");
text.attr("class", "");
link.attr("stroke-width", STROKE);
};
const sameNodes = (previous, next) => {
if (next.length !== previous.length) {
return false;
}
const map = new Map();
for (const node of previous) {
map.set(node.id, node.label);
}
for (const node of next) {
const found = map.get(node.id);
if (!found || found !== node.title) {
return false;
}
}
return true;
};
const sameEdges = (previous, next) => {
if (next.length !== previous.length) {
return false;
}
const set = new Set();
for (const edge of previous) {
set.add(`${edge.source.id}-${edge.target.id}`);
}
for (const edge of next) {
if (!set.has(`${edge.source}-${edge.target}`)) {
return false;
}
}
return true;
};
const graphWrapper = document.getElementById('graph-wrapper')
const element = document.createElementNS("http://www.w3.org/2000/svg", "svg");
element.setAttribute("width", graphWrapper.getBoundingClientRect().width);
element.setAttribute("height", window.innerHeight * 0.8);
graphWrapper.appendChild(element);
const reportWindowSize = () => {
element.setAttribute("width", window.innerWidth);
element.setAttribute("height", window.innerHeight);
};
window.onresize = reportWindowSize;
const svg = d3.select("svg");
const width = Number(svg.attr("width"));
const height = Number(svg.attr("height"));
let zoomLevel = 1;
const simulation = d3
.forceSimulation(nodesData)
.force("forceX", d3.forceX().x(width / 2))
.force("forceY", d3.forceY().y(height / 2))
.force("charge", d3.forceManyBody())
.force(
"link",
d3
.forceLink(linksData)
.id((d) => d.id)
.distance(70)
)
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collision", d3.forceCollide().radius(80))
.stop();
const g = svg.append("g");
let link = g.append("g").attr("class", "links").selectAll(".link");
let node = g.append("g").attr("class", "nodes").selectAll(".node");
let text = g.append("g").attr("class", "text").selectAll(".text");
const resize = () => {
if (d3.event) {
const scale = d3.event.transform;
zoomLevel = scale.k;
g.attr("transform", scale);
}
const zoomOrKeep = (value) => (zoomLevel >= 1 ? value / zoomLevel : value);
const font = Math.max(Math.round(zoomOrKeep(FONT_SIZE)), 1);
text.attr("font-size", (d) => font);
text.attr("y", (d) => d.y - zoomOrKeep(FONT_BASELINE) + 8);
link.attr("stroke-width", zoomOrKeep(STROKE));
node.attr("r", (d) => {
return zoomOrKeep(nodeSize[d.id]);
});
svg
.selectAll("circle")
.filter((_d, i, nodes) => d3.select(nodes[i]).attr("active"))
.attr("r", (d) => zoomOrKeep(ACTIVE_RADIUS_FACTOR * nodeSize[d.id]));
document.getElementById("zoom").innerHTML = zoomLevel.toFixed(2);
};
const ticked = () => {
node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
text
.attr("x", (d) => d.x)
.attr("y", (d) => d.y - (FONT_BASELINE - nodeSize[d.id]) / zoomLevel);
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
};
const restart = () => {
updateNodeSize();
node = node.data(nodesData, (d) => d.id);
node.exit().remove();
node = node
.enter()
.append("circle")
.attr("r", (d) => {
return nodeSize[d.id];
})
.on("click", onClick)
.on("mouseover", onMouseover)
.on("mouseout", onMouseout)
.merge(node);
link = link.data(linksData, (d) => `${d.source.id}-${d.target.id}`);
link.exit().remove();
link = link.enter().append("line").attr("stroke-width", STROKE).merge(link);
text = text.data(nodesData, (d) => d.label);
text.exit().remove();
text = text
.enter()
.append("text")
.text((d) => shorten(d.label.replace(/_*/g, ""), MAX_LABEL_LENGTH))
.attr("font-size", `${FONT_SIZE}px`)
.attr("text-anchor", "middle")
.attr("alignment-baseline", "central")
.on("click", onClick)
.on("mouseover", onMouseover)
.on("mouseout", onMouseout)
.merge(text);
node.attr("active", (d) => isCurrentPath(d.path) ? true : null);
text.attr("active", (d) => isCurrentPath(d.path) ? true : null);
simulation.nodes(nodesData);
simulation.force("link").links(linksData);
simulation.alpha(1).restart();
simulation.stop();
for (let i = 0; i < TICKS; i++) {
simulation.tick();
}
ticked();
};
const zoomHandler = d3.zoom().scaleExtent([0.2, 3]).on("zoom", resize);
zoomHandler(svg);
restart();
function isCurrentPath(notePath) {
return window.location.pathname.includes(notePath)
}
function shorten(str, maxLen, separator = ' ') {
if (str.length <= maxLen) return str;
return str.substr(0, str.lastIndexOf(separator, maxLen)) + '...';
}
</script>
</div>
</div>
</div>
</main><footer>
</footer><!-- That file is not particularly elegant. This will need a refactor at some point. -->
<div style="opacity: 0; display: none;" id='tooltip-wrapper'>
<div id='tooltip-content'>
</div>
</div>
<iframe style="display: none; height: 0; width: 0;" id='link-preview-iframe' src="">
</iframe>
<script>
var opacityTimeout;
var contentTimeout;
var transitionDurationMs = 100;
var iframe = document.getElementById('link-preview-iframe')
var tooltipWrapper = document.getElementById('tooltip-wrapper')
var tooltipContent = document.getElementById('tooltip-content')
function hideTooltip() {
opacityTimeout = setTimeout(function() {
tooltipWrapper.style.opacity = 0;
contentTimeout = setTimeout(function() {
tooltipContent.innerHTML = '';
tooltipWrapper.style.display = 'none';
}, transitionDurationMs + 1);
}, transitionDurationMs)
}
function showTooltip(event) {
var elem = event.target;
var elem_props = elem.getClientRects()[elem.getClientRects().length - 1];
var top = window.pageYOffset || document.documentElement.scrollTop
if (event.target.host === window.location.host) {
iframe.src = event.target.href
iframe.onload = function() {
tooltipContentHtml = ''
tooltipContentHtml += '<div style="font-weight: bold;">' + iframe.contentWindow.document.querySelector('h1').innerHTML + '</div>'
tooltipContentHtml += iframe.contentWindow.document.querySelector('content').innerHTML
tooltipContent.innerHTML = tooltipContentHtml
tooltipWrapper.style.display = 'block';
setTimeout(function() {
tooltipWrapper.style.opacity = 1;
}, 1)
}
tooltipWrapper.style.left = elem_props.left - (tooltipWrapper.offsetWidth / 2) + (elem_props.width / 2) + "px";
if ((window.innerHeight - elem_props.top) < (tooltipWrapper.offsetHeight)) {
tooltipWrapper.style.top = elem_props.top + top - tooltipWrapper.offsetHeight - 10 + "px";
} else if ((window.innerHeight - elem_props.top) > (tooltipWrapper.offsetHeight)) {
tooltipWrapper.style.top = elem_props.top + top + 35 + "px";
}
if ((elem_props.left + (elem_props.width / 2)) < (tooltipWrapper.offsetWidth / 2)) {
tooltipWrapper.style.left = "10px";
} else if ((document.body.clientWidth - elem_props.left - (elem_props.width / 2)) < (tooltipWrapper.offsetWidth / 2)) {
tooltipWrapper.style.left = document.body.clientWidth - tooltipWrapper.offsetWidth - 20 + "px";
}
}
}
function setupListeners(linkElement) {
linkElement.addEventListener('mouseleave', function(_event) {
hideTooltip();
});
tooltipWrapper.addEventListener('mouseleave', function(_event) {
hideTooltip();
});
linkElement.addEventListener('mouseenter', function(event) {
clearTimeout(opacityTimeout);
clearTimeout(contentTimeout);
showTooltip(event);
});
tooltipWrapper.addEventListener('mouseenter', function(event) {
clearTimeout(opacityTimeout);
clearTimeout(contentTimeout);
});
}
document.querySelectorAll('content a').forEach(setupListeners);
</script>
<script src="/assets/js/scripts.js" async></script>
</body>
</html>