deploy: b6283352d2
				
					
				
			This commit is contained in:
		
							
								
								
									
										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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user