block by tophtucker c7fcb00c54c6ce5100be

Type topology

Full Screen

It is too hard to play with the topology of type in HTML! This effectively spools (or coils) a long column of text onto a cylinder with a circumference equal to the height of the first section, so that when you get to the beginning of the second section, the top of the first section comes around again. (To prove it’s the same DOM element, try highlighting a sentence from Solon and notice that your highlight is still there when it comes around the second time.)

For example text I’ve chosen Plutarch’s Parallel Lives, so that you may compare Solon and Poplicola.

If you view on a sufficiently wide screen, you’ll notice that when you get to the third section (Plutarch’s comparison of the two), it breaks down a bit. What I’d like is for the beginnings of the previous two sections to be aligned, so that you have three columns of text with headers aligned. But, since the sections are different lengths, that would require different columns scrolling at different rates. I haven’t gotten there yet.


Ideas

There are definitely more elegant ways to do this but I’m an idiot. You can imagine other topologies, especially for periodic prose. Like maybe there’s a daily weather report. Maybe it’s more, like, elliptic, i.e. periodic in two dimensions; what if the previous day’s weather is aligned at left, and the preview year’s weather on a row above? Is that a good idea? Probably not!!!

Also, not necessarily topologies per se, but there are lots of ways you could recollect (re-collect) earlier passages in margins when references come up. Hmmm. It’s cute that DOM stays intact here but probably you’d want lots of clones.


Sources:

index.html

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>

<link rel="stylesheet" type="text/css" href="main.css"/>

<body>

  <h1>Parallel Lives</h1>

  <article></article>
    
</body>

<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="main.js" charset="utf-8"></script>

<!-- 

//classics.mit.edu/Plutarch/solon.html
//classics.mit.edu/Plutarch/poplicol.html
//classics.mit.edu/Plutarch/p_s_comp.html

-->

</html>

comparison.html

<h2>The Comparison of Poplicola with Solon</h2>

<p>
There is something singular in the present parallel which has not occurred 
in any other of the lives; that the one should be the imitator of the other, 
and the other his best evidence. Upon the survey of Solon's sentence to 
Croesus in favour of Tellus's happiness, it seems more applicable to Poplicola; 
for Tellus, whose virtuous life and dying well had gained him the name 
of the happiest man, yet was never celebrated in Solon's poems for a good 
man, nor have his children or any magistracy of his deserved a memorial; 
but Poplicola's life was the most eminent amongst the Romans, as well for 
the greatness of his virtue as his power, and also since his death many 
amongst the distinguished families, even in our days, the Poplicolae, Messalae, 
and Valerii, after a lapse of six hundred years, acknowledge him as the 
fountain of their honour. Besides, Tellus, though keeping his post and 
fighting like a valiant soldier, was yet slain by his enemies; but Poplicola, 
the better fortune, slew his, and saw his country victorious under his 
command. And his honours and triumphs brought him, which was Solon's ambition, 
to a happy end; the ejaculation which, in his verses against Mimnermus 
about the continuance of man's life, he himself made-
</p><p>"Mourned let me die; and may I, when life ends,
<br>Occasion sighs and sorrows to my friends," is evidence to Poplicola's 
happiness; his death did not only draw tears from his friends and acquaintance, 
but was the object of universal regret and sorrow through the whole city, 
the women deplored his loss as that of a son, brother, or common father. 
"Wealth I would have," said Solon, "but wealth by wrong procure would not," 
because punishment would follow. But Poplicola's riches were not only justly 
his, but he spent them nobly in doing good to the distressed. So that if 
Solon was reputed the wisest man, we must allow Poplicola to be the happiest; 
for what Solon wished for as the greatest and most perfect good, this Poplicola 
had, and used and enjoyed to his death.
</p><p>And as Solon may thus be said to have contributed to Poplicola's 
glory, so did also Poplicola to his, by his choice of him as his model 
in the formation of republican institutions; in reducing, for example, 
the excessive powers and assumption of the consulship. Several of his laws, 
indeed, he actually transferred to Rome, as his empowering the people to 
elect their officers, and allowing offenders the liberty of appealing to 
the people, as Solon did to the jurors. He did not, indeed, create a new 
senate, as Solon did, but augmented the old to almost double its number. 
The appointment of treasurers again, the quaestors, has a like origin; 
with the intent that the chief magistrate should not, if of good character, 
be withdrawn from greater matters; or, if bad, have the greater temptation 
to injustice, by holding both the government and treasury in his hands. 
The aversion to tyranny was stronger in Poplicola; any one who attempted 
usurpation could, by Solon's law, only be punished upon conviction; but 
Poplicola made it death before a trial. And though Solon justly gloried, 
that, when arbitrary power was absolutely offered to him by circumstances, 
and when his countrymen would have willingly seen him accept it, he yet 
declined it; still Poplicola merited no less, who, receiving a despotic 
command, converted it to a popular office, and did not employ the whole 
legal power which he held. We must allow, indeed, that Solon was before 
Poplicola in observing that-
</p><p>"A people always minds its rulers best
<br>When it is neither humoured nor oppressed."
</p><p>The remission of debts was peculiar to Solon; it was his great 
means for confirming the citizens' liberty; for a mere law to give all 
men equal rights is but useless, if the poor must sacrifice those rights 
to their debts, and, in the very seats and sanctuaries of equality, the 
courts of justice, the offices of state, and the public discussions, be 
more than anywhere at the beck and bidding of the rich. A yet more extraordinary 
success was, that, although usually civil violence is caused by any remission 
of debts, upon this one occasion this dangerous but powerful remedy actually 
put an end to the civil violence already existing, Solon's own private 
worth and reputation overbalancing all the ordinary ill-repute and discredit 
of the change. The beginning of his government was more glorious, for he 
was entirely original, and followed no man's example, and, without the 
aid of any ally, achieved his most important measures by his own conduct; 
yet the close of Poplicola's life was more happy and desirable, for Solon 
saw the dissolution of his own commonwealth, Poplicola maintained the state 
in good order to the civil wars. Solon, leaving his laws, as soon as he 
had made them, engraved in wood, but destitute of a defender, departed 
from Athens; whilst Poplicola, remaining both in and out of office, laboured 
to establish the government. Solon, though he actually knew of Pisistratus's 
ambition, yet was not able to suppress it, but had to yield to usurpation 
in its infancy; whereas Poplicola utterly subverted and dissolved a potent 
monarchy, strongly settled by long continuance; uniting thus to virtues 
equal to those, and purposes identical with those of Solon, the good fortune 
and the power that alone could make them effective.
</p><p>In military exploits, Daimachus of Plataea will not even allow 
Solon the conduct of the war against the Megarians, as was before intimated; 
but Poplicola was victorious in the most important conflicts, both as a 
private soldier and commander. In domestic politics, also, Solon, in play, 
as it were, and by counterfeiting madness induced the enterprise against 
Salamis; whereas Poplicola, in the very beginning, exposed himself to the 
greatest risk, took arms against Tarquin, detected the conspiracy, and, 
being principally concerned both in preventing the escape of and afterwards 
punishing the traitors, not only expelled the tyrants from the city, but 
extirpated their very hopes. And as, in cases calling for contest and resistance 
and manful opposition, he behaved with courage and resolution, so, in instances 
where peaceable language, persuasion, and concession were requisite, he 
was yet more to be commended; and succeeded in gaining happily to reconciliation 
and friendship, Porsenna, a terrible and invincible enemy. Some may, perhaps, 
object that Solon recovered Salamis, which they had lost, for the Athenians; 
whereas Poplicola receded from part of what the Romans were at that time 
possessed of; but judgment is to be made of actions according to the times 
in which they were performed. The conduct of a wise politician is ever 
suited to the present posture of affairs; often by foregoing a part he 
saves the whole, and by yielding in a small matter secures a greater; and 
so Poplicola, by restoring what the Romans had lately usurped, saved their 
undoubted patrimony, and procured, moreover, the stores of the enemy for 
those who were only too thankful to secure their city. Permitting the decision 
of the controversy to his adversary, he not only got the victory, but likewise 
what he himself would willingly have given to purchase the victory, Porsenna 
putting an end to the war, and leaving them all the provision of his camp, 
from the sense of the virtue and gallant disposition of the Romans which 
their consul had impressed upon him.
</p>

main-old.js

// http://javascript.about.com/od/problemsolving/a/modulobug.htm
Number.prototype.mod = function(n) { return ((this%n)+n)%n; }

var container = d3.select("article");
var columnWidth = 400;
var gutter = 20;

container.style('width', columnWidth + 'px');

queue()
  .defer(d3.xhr, "solon.html")
  .defer(d3.xhr, "poplicola.html")
  .defer(d3.xhr, "comparison.html")
  .awaitAll(render);

function render(error,data) {
  data = data.map(function(d) { return {html: d.response}; });
  
  var section = container.selectAll("section")
    .data(data)
    .enter()
    .append("section")
    .html(ƒ('html'));

  section.each(function(d) {
    d.height = this.getBoundingClientRect().height;
  }).style('height', function(d) { return d.height + 'px'; });

  var block = section.selectAll('h2,h3,p').datum(function(d) {
    return {
      'modulo': d3.select(this.offsetParent).datum().height,
      'top': this.offsetTop
    }
  })
    .style('position', 'absolute')
    .style('top', function(d) { return d.top + 'px'; })
    .style('width', columnWidth + 'px');

  d3.select(window).on("scroll", function() {
    block.each(function(d) {
      var bb = this.getBoundingClientRect();
      if(bb.top + bb.height < 0) {
        d.hidden = true;
      }
    }).classed('hidden', ƒ('hidden'));

    block.filter(ƒ('hidden'))
      .style('top', function(d) { return d.modulo + d.top + 'px' })
      .style('left', function(d) { return -(columnWidth + gutter) + 'px' });

  })

}

main.css

h1 {
  text-align: center;
}

article {
  position: relative;
  margin: 0 auto;
}

main.js

var container = d3.select("article");

// Set up columns
var columnWidth = 400;
var gutter = 20;

// Defined and used later for offsets when things loop around
var x = d3.scale.threshold();
var y = d3.scale.threshold();
var opacity = d3.scale.threshold();

// Load sections
Promise.all(
  ["solon.html", "poplicola.html", "comparison.html"].map(d =>
    fetch(d).then(resp => resp.text())
  )
).then(render);

function render(data) {
  data = data.map(function(html) { return {html}; });

  container.style('width', columnWidth + 'px');
  
  // Load sections into html
  var section = container.selectAll("section")
    .data(data)
    .enter()
    .append("section")
    .html(d => d.html);

  // Save initial computed layout as bound data
  section
    .each(function(d) {
      d.height = this.getBoundingClientRect().height;
      d.top = this.offsetTop;
    })
    .style('height', function(d) { return d.height + 'px'; });

  // Compound breakpoints (for when things should loop around)
  var thresholds = section.data().map(function(d,i) {
    return [d.top, -i*(columnWidth + gutter), 1/Math.pow(2,i)];
  });
  x.domain(thresholds.map(d => d[0]))
    .range(thresholds.map(d => d[1]));
  y.domain(thresholds.map(d => d[0]))
    .range(thresholds.map(d => d[0]));
  opacity.domain(thresholds.map(d => d[0]))
    .range(thresholds.map(d => d[2]));

  // Position everything absolutely
  var block = section.selectAll('h2,h3,p')
    .datum(function(d) {
      return {
        'top': this.offsetTop,
        'height': this.offsetHeight
      }
    })
    .style('position', 'absolute')
    .style('width', columnWidth + 'px');

  d3.select(window).on("scroll", recompute);
  recompute();

  // Recompute layout on scroll
  function recompute() {
    var containerTop = container.node().getBoundingClientRect().top;
    block
      .style('left', function(d) {
        return x(-(containerTop + d.top + d.height)) + 'px';
      })
      .style('top', function(d) {
        return d.top + y(-(containerTop + d.top + d.height)) + 'px';
      })
      .style('opacity', function(d) {
        return opacity(-(containerTop + d.top + d.height));
      });
  }

}

// This isn't actually used but I'm leaving it here as a reminder 
// that there must be better ways to do this than d3 threshold scales,
// like just floors and modulos or uh whatever I'm an idiot.
// http://javascript.about.com/od/problemsolving/a/modulobug.htm
Number.prototype.mod = function(n) { return ((this%n)+n)%n; }