the-riddle
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

typography.js
application/javascript

Download raw (3.6 KB)

;(function () {

  if ((!document.getNamedFlows) && document.webkitGetNamedFlows) {
    document.getNamedFlows = document.webkitGetNamedFlows;
  }
  
  if (!document.getNamedFlow) {
    document.getNamedFlow = function (name) {
      return document.getNamedFlows(name).namedItem(name);
    }
  }

  var textFlow = document.getNamedFlow('main');

  /**
   * Returns first region the given node is shown within
   * 
   * workaround for getRegionsForContent() as that function only seems to work 
   * on block-elements
   */
  function getRegionByNode(flow, node) {
    var ranges, regions = flow.getRegionsByContent(node);

    if (regions.length > 0) {
      return regions[0];
    } else {
      regions = flow.getRegions();

      for (var i=0; i < regions.length; i++) {
        ranges = regions[i].webkitGetRegionFlowRanges();

        for (var r=0; r < ranges.length; r++) {
          if (ranges[r].isPointInRange(node, 0)) {
            return regions[i];
          }
        }
      }
      console.log('Could not find region for node', node);
    }
  }

  /**
   * Find page for given node, travels up the DOM tree to find
   * first wrapping paper element. Does not work for Nodes in a flow.
   */
  function getPageByNode (node) {
    if (node) {
      if (node.classList.contains('paper')) { 
        return node;
      } else {
        if (node.parentElement) {
          return getPageByNode(node.parentElement);
        } else {
          return false;
        }
      }
      return false;
    }
  }

  /**
   * Get page by node in region
   */
  function getPageByRegionNode (flow, node) {
    var region = getRegionByNode(flow, node);
    if (region) {
      return getPageByNode(getRegionByNode(flow, node));
    } else {
      return null;
    }
  }

  /**
   * Return page number for given page
   */
  function getPageNum (page) {
    return parseInt(page.id.replace(/\D/g, ''));
  }

  window.fixOrphans = function () {
    var threshold = 80,
        step = -.025,
        max = -0.125;
        spans = document.querySelectorAll('.last-line');

    for (var i=0; i < spans.length; i++) {
      var rects = spans[i].getClientRects(),
          lineCount = rects.length; // Remember how many lines we had at the start

      if (rects.length > 0 && rects[rects.length-1].width <= threshold) {
        var spacing = 0;

        while(rects.length >= lineCount && spacing >= max) {
          spacing += step;
          spans[i].style.letterSpacing = spacing + 'mm';
          rects = spans[i].getClientRects()
        }

        if (rects.length == lineCount) {
          spans[i].style.letterSpacing = 0;
        }
      }
    }
  }

  window.fixWidows = function () {
    var threshold = 40,
        p = document.querySelectorAll('p');

    for (var i=0; i < p.length; i++) {
      var rects = p[i].getClientRects();

      if (rects.length > 1 && rects[0].height < threshold) {
        p[i].classList.add('break-before');
      }
    }
  }

  window.reattachHeaders = function () {
    var textFlow = document.getNamedFlow('main'),
        headers = document.querySelectorAll('h4');

    for (var i=0; i< headers.length; i++) {
      var h = headers[i],
          p = h.nextElementSibling;

      if (getPageNum(getPageByRegionNode(textFlow, h)) != getPageNum(getPageByRegionNode(textFlow, p))) {
        h.classList.add('break-before');
      } else {
        var threshold = 75,
            rects = p.getClientRects();

        if (rects.length > 1 && rects[0].height <= threshold) {
          h.classList.add('break-before');
        }
      }
    }
  }

  window.typographicFixes = function () {
    fixOrphans();
    reattachHeaders();
    fixWidows();
  }
})();