deploy: b6283352d2
This commit is contained in:
parent
cfb0dba7ee
commit
ddeee28362
@ -1,58 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en"><head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Initial Seeds 🌱 Digital Garden</title>
|
|
||||||
<meta name="description" content="<p>Set of areas that guide our reveries?</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>The history of hypertext, rss+adjacent protocols and standards.</li>
|
|
||||||
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
|
||||||
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
">
|
|
||||||
<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">
|
|
||||||
<article itemscope itemtype="http://schema.org/BlogPosting">
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1 itemprop="name headline">Initial Seeds</h1>
|
|
||||||
<p class="post-meta"><time class="dt-published" datetime="2021-03-01T00:00:00+00:00" itemprop="datePublished">
|
|
||||||
Mar 1, 2021
|
|
||||||
</time></p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="e-content" itemprop="articleBody">
|
|
||||||
<p>Set of areas that guide our reveries?</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>The history of hypertext, rss+adjacent protocols and standards.</li>
|
|
||||||
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
|
||||||
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<!--more-->
|
|
||||||
|
|
||||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Publish from RSS to arena?
|
|
||||||
RSS to twitter? The gram?
|
|
||||||
Use DPress to get it on SSB?
|
|
||||||
</code></pre></div></div>
|
|
||||||
|
|
||||||
<p><em>What if… we travel back in time and blow up BGP? Would Xanadu be realized? Would actual plural internetworking have persisted?</em></p>
|
|
||||||
|
|
||||||
<p>Publishing → making things public</p>
|
|
||||||
|
|
||||||
<p><strong>» COMMISSIONING BODIES «</strong> <br />
|
|
||||||
Economy as a form a expression</p>
|
|
||||||
|
|
||||||
<p>Antagonizing the separation of frontend/backend</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a class="u-url" href="/2021/03/01/initial-seeds.html" hidden></a>
|
|
||||||
</article>
|
|
||||||
</main><footer>
|
|
||||||
|
|
||||||
</footer><script src="/assets/js/scripts.js" async></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
146
2021/03/01/initial-seeds/index.html
Normal file
146
2021/03/01/initial-seeds/index.html
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Initial Seeds 🌱 Digital Garden</title>
|
||||||
|
<meta name="description" content="<p>Set of areas that guide our reveries?</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>The history of <a href="/hypertext">hypertext</a>, rss+adjacent protocols and standards.</li>
|
||||||
|
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
||||||
|
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
">
|
||||||
|
<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">
|
||||||
|
<article itemscope itemtype="http://schema.org/BlogPosting">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1 itemprop="name headline">Initial Seeds</h1>
|
||||||
|
<p class="post-meta"><time class="dt-published" datetime="2021-03-01T00:00:00+00:00" itemprop="datePublished">
|
||||||
|
Mar 1, 2021
|
||||||
|
</time></p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="e-content" itemprop="articleBody">
|
||||||
|
<p>Set of areas that guide our reveries?</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>The history of <a href="/hypertext">hypertext</a>, rss+adjacent protocols and standards.</li>
|
||||||
|
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
||||||
|
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Publish from RSS to arena?
|
||||||
|
RSS to twitter? The gram?
|
||||||
|
Use DPress to get it on SSB?
|
||||||
|
</code></pre></div></div>
|
||||||
|
|
||||||
|
<p><em>What if… we travel back in time and blow up BGP? Would Xanadu be realized? Would actual plural internetworking have persisted?</em></p>
|
||||||
|
|
||||||
|
<p>Publishing → making things public</p>
|
||||||
|
|
||||||
|
<p><strong>» COMMISSIONING BODIES «</strong> <br />
|
||||||
|
Economy as a form a expression</p>
|
||||||
|
|
||||||
|
<p>Antagonizing the separation of frontend/backend</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="u-url" href="/2021/03/01/initial-seeds/" hidden></a>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</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>
|
89
404.html
89
404.html
@ -28,6 +28,93 @@
|
|||||||
|
|
||||||
</main><footer>
|
</main><footer>
|
||||||
|
|
||||||
</footer><script src="/assets/js/scripts.js" async></script>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
@ -18,6 +18,93 @@
|
|||||||
</article>
|
</article>
|
||||||
</main><footer>
|
</main><footer>
|
||||||
|
|
||||||
</footer><script src="/assets/js/scripts.js" async></script>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
feed.xml
4
feed.xml
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2021-03-11T03:31:15+00:00</updated><id>/feed.xml</id><title type="html">Digital Garden</title><subtitle>A digital garden or public notebook for The Bentway’s Digital and/as Public Space Micro-Residency.</subtitle><entry><title type="html">Initial Seeds</title><link href="/2021/03/01/initial-seeds.html" rel="alternate" type="text/html" title="Initial Seeds" /><published>2021-03-01T00:00:00+00:00</published><updated>2021-03-01T00:00:00+00:00</updated><id>/2021/03/01/initial-seeds</id><content type="html" xml:base="/2021/03/01/initial-seeds.html"><p>Set of areas that guide our reveries?</p>
|
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2021-03-11T03:38:00+00:00</updated><id>/feed.xml</id><title type="html">Digital Garden</title><subtitle>A digital garden or public notebook for The Bentway’s Digital and/as Public Space Micro-Residency.</subtitle><entry><title type="html">Initial Seeds</title><link href="/2021/03/01/initial-seeds/" rel="alternate" type="text/html" title="Initial Seeds" /><published>2021-03-01T00:00:00+00:00</published><updated>2021-03-11T03:37:19+00:00</updated><id>/2021/03/01/initial-seeds</id><content type="html" xml:base="/2021/03/01/initial-seeds/"><p>Set of areas that guide our reveries?</p>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>The history of hypertext, rss+adjacent protocols and standards.</li>
|
<li>The history of <a href="/hypertext">hypertext</a>, rss+adjacent protocols and standards.</li>
|
||||||
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
<li>The act of publishing as “making something public” → publicness → hybrid public space.</li>
|
||||||
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
<li>The possibilities for <em>the infrastructural</em> (maintenance/repair) to draw from the past to rethink the present through co-operative approaches.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
403
hypertext.html
Normal file
403
hypertext.html
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Hypertext 🌱 Digital Garden</title>
|
||||||
|
<meta name="description" content="<p>Hypertext Markup Language</p>
|
||||||
|
">
|
||||||
|
<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>Hypertext</h1>
|
||||||
|
<time datetime="2021-03-11T03:37:19+00:00">
|
||||||
|
Last updated on March 11, 2021
|
||||||
|
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notes-entry-container">
|
||||||
|
<content>
|
||||||
|
<p>Hypertext Markup Language</p>
|
||||||
|
|
||||||
|
</content>
|
||||||
|
|
||||||
|
<side style="font-size: 0.9em">
|
||||||
|
<h3 style="margin-bottom: 1em">Notes mentioning this note</h3>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-gap: 1em; grid-template-columns: repeat(1fr);">
|
||||||
|
|
||||||
|
<div class="backlink-box">
|
||||||
|
<a class="internal-link" href="/statement-of-intent">Statement of Intent</a><br>
|
||||||
|
<div style="font-size: 0.9em">Hypha’s practice is situated across many topics that are present in the theme of Adaptive Reuse & Creative Misuse. Drawing...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</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>
|
93
index.html
93
index.html
@ -8,7 +8,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/2021/03/01/initial-seeds.html">Initial Seeds</a>
|
<a href="/2021/03/01/initial-seeds/">Initial Seeds</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<div class="post_excerpt">
|
<div class="post_excerpt">
|
||||||
<article>
|
<article>
|
||||||
<h2 itemprop="headline">
|
<h2 itemprop="headline">
|
||||||
<a href="/2021/03/01/initial-seeds.html" rel="permalink">Initial Seeds</a>
|
<a href="/2021/03/01/initial-seeds/" rel="permalink">Initial Seeds</a>
|
||||||
</h2>
|
</h2>
|
||||||
<p class="excerpt" itemprop="description"><em>
|
<p class="excerpt" itemprop="description"><em>
|
||||||
Set of areas that guide our reveries?
|
Set of areas that guide our reveries?
|
||||||
@ -38,6 +38,93 @@
|
|||||||
|
|
||||||
</main><footer>
|
</main><footer>
|
||||||
|
|
||||||
</footer><script src="/assets/js/scripts.js" async></script>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
403
protocols.html
Normal file
403
protocols.html
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Protocols 🌱 Digital Garden</title>
|
||||||
|
<meta name="description" content="
|
||||||
|
">
|
||||||
|
<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>Protocols</h1>
|
||||||
|
<time datetime="2021-03-11T03:37:19+00:00">
|
||||||
|
Last updated on March 11, 2021
|
||||||
|
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notes-entry-container">
|
||||||
|
<content>
|
||||||
|
|
||||||
|
|
||||||
|
</content>
|
||||||
|
|
||||||
|
<side style="font-size: 0.9em">
|
||||||
|
<h3 style="margin-bottom: 1em">Notes mentioning this note</h3>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-gap: 1em; grid-template-columns: repeat(1fr);">
|
||||||
|
|
||||||
|
<div class="backlink-box">
|
||||||
|
<a class="internal-link" href="/statement-of-intent">Statement of Intent</a><br>
|
||||||
|
<div style="font-size: 0.9em">Hypha’s practice is situated across many topics that are present in the theme of Adaptive Reuse & Creative Misuse. Drawing...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</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>
|
403
rss.html
Normal file
403
rss.html
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>RSS 🌱 Digital Garden</title>
|
||||||
|
<meta name="description" content="<p>Really Simple Syndication</p>
|
||||||
|
">
|
||||||
|
<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>RSS</h1>
|
||||||
|
<time datetime="2021-03-11T03:37:19+00:00">
|
||||||
|
Last updated on March 11, 2021
|
||||||
|
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notes-entry-container">
|
||||||
|
<content>
|
||||||
|
<p>Really Simple Syndication</p>
|
||||||
|
|
||||||
|
</content>
|
||||||
|
|
||||||
|
<side style="font-size: 0.9em">
|
||||||
|
<h3 style="margin-bottom: 1em">Notes mentioning this note</h3>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-gap: 1em; grid-template-columns: repeat(1fr);">
|
||||||
|
|
||||||
|
<div class="backlink-box">
|
||||||
|
<a class="internal-link" href="/statement-of-intent">Statement of Intent</a><br>
|
||||||
|
<div style="font-size: 0.9em">Hypha’s practice is situated across many topics that are present in the theme of Adaptive Reuse & Creative Misuse. Drawing...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</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>
|
401
statement-of-intent.html
Normal file
401
statement-of-intent.html
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
<!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="<p>Hypha’s 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 [[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 Hypha’s 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>
|
||||||
|
">
|
||||||
|
<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:37:19+00:00">
|
||||||
|
Last updated on March 11, 2021
|
||||||
|
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notes-entry-container">
|
||||||
|
<content>
|
||||||
|
<p>Hypha’s practice is situated across many topics that are present in the theme of <em>Adaptive Reuse & 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 Hypha’s 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>
|
Loading…
Reference in New Issue
Block a user