the-riddle
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

book2v3.js
application/javascript

Download raw (14.3 KB)

// Wrap image s
// Make page 1 cover
// Add pages untill all flows fit
// Go page by page through them
//  If image compare page nums, if placed before text, insert break 
//  requestAnimationFrame
//  Get classes on page elements
//  If class in list to take, add class to page
//  If image region empty reveal secondary text frame increase pagenumber

;(function () {
  'use strict';

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

  var master, container,
      textFlow = document.getNamedFlow('main'),
      imageFlow = document.getNamedFlow('images'),
      captionFlow = document.getNamedFlow('figcaptions');

  function getStartPage () {
    var selector = "body#" + document.body.id + " #pages",
        stylesheets = document.styleSheets;

    for (var s=0; s < stylesheets.length; s++) {
      var rules = stylesheets[s].cssRules;

      for (var r=0; r < rules.length;r++) {
        if (rules[r].selectorText == selector) {
          return parseInt(/\d+/.exec(rules[r].style.counterReset)[0]);
        }
      }
    }
  }

  function insertBreakBefore (refNode, className) {
      var brkNode = document.createElement('div');
      brkNode.classList.add('break-after');
      brkNode.classList.add(className);
      refNode.parentElement.insertBefore(brkNode, refNode);
  }

  /**
   * 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);
    }
  }

  /**
   * Returns nodes within given region
   */
  function regionContent (flow, region) {
    var ranges = region.webkitGetRegionFlowRanges(),
        flowContent = flow.getContent(),
        regionContent = [];

    for (var r=0; r < ranges.length; r++) {
      for (var i=0; i < flowContent.length; i++) {
        if (ranges[r].isPointInRange(flowContent[i])) {
          regionContent.push(flowContent[i]);
        }
      }
    }

    return regionContent;
  }

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

  /**
   * Retreive page by number
   */
  function getPageByNum (num) {
    return document.querySelector('#page-' + num);
  }

  /**
   * 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) {
    return getPageByNode(getRegionByNode(flow, node));
  }

  /**
   * Return the region on the page with given number
   */
  function getRegionByPageNum (flow, num) {
    var regionPageNum, regions = flow.getRegions();
    for (var i = 0; i < regions.length; i++) {
      regionPageNum = getPageNum(getPageByNode(regions[i])); 
      if (regionPageNum == num) {
        return regions[i];
      } else if (regionPageNum > num) {
        return null;
      }
    }
  }

  function getNodeIndex(nodes, refnode) {
    for (var i=0; i < nodes.length; i++) {
      if (nodes[i] == refnode) {
        return i;
      }
    }
  } 

  function wrapImages () {
    var im, wrapper, clsName, imgs = document.querySelectorAll("#main img");

    for (var i=0;i<imgs.length;i++) {
      im = imgs[i];
      wrapper = document.createElement("div");
      wrapper.classList.add("img");
      
      while (im.classList.length > 0) {
        clsName = im.classList[0];

        if (clsName != 'None') {
          wrapper.classList.add(clsName);
          im.classList.remove(clsName);
        }
      }
      
      wrapper.appendChild(im.cloneNode());
      im.parentNode.replaceChild(wrapper, im);

      if (wrapper.classList.contains('insert-break-before')) {
        wrapper.classList.remove('insert-break-before');
        var breakDiv = document.createElement("div");
        breakDiv.classList.add('break-before');
        breakDiv.classList.add('img_break');
        wrapper.parentElement.insertBefore(breakDiv, wrapper);
      } 
    }

    var imgs = document.querySelectorAll("#main div.img");

    for (var i=0; i<imgs.length; i++) {
      if (imgs[i].classList.contains('wrap-next')) {
        var wrapper = document.createElement('div');
        wrapper.classList.add('wrapped_images');

        for (var c=0; c<imgs[i].classList.length; c++) {
          wrapper.classList.add(imgs[i].classList[c]);
        }

        imgs[i].parentNode.insertBefore(wrapper, imgs[i]);
        var caption = imgs[i].nextElementSibling;
        wrapper.appendChild(imgs[i]);
        wrapper.appendChild(caption);
        imgs[i].classList.remove('img');
        do {
          i++;
          var caption = imgs[i].nextElementSibling;
          imgs[i].classList.remove('img');
          wrapper.appendChild(imgs[i]);
          wrapper.appendChild(caption);
        } while (i < imgs.length && imgs[i].classList.contains('wrap-next'));
      }
    }
  }

  function addPages(amount) {
    var page, pagenum, offset = getStartPage();

    for (var i=0;i<amount;i++) {
      page = master.cloneNode(true);
      container.appendChild(page);
      pagenum = offset + document.querySelectorAll('.paper').length;
      page.setAttribute('id', 'page-'+pagenum);
      if (pagenum == 1) {
        page.classList.add('cover');
      }
    }
  }

  function fitContent (callback) {
    if (textFlow.overset || imageFlow.overset || captionFlow.overset) {
      addPages(25);
      window.requestAnimationFrame(function () { fitContent(callback) });
    } else {
      callback();
    }
  }

  /**
   * Return content in flow on given page
   */
  function getPageFlowContent (num, flow) {
    var region = getRegionByPageNum(flow, num);
    if (region) {
      return regionContent(flow, region);
    } else {
      return [];
    }
  }


  function getNextNodeInFlow (flow, refnode) {
    var flowContent = flow.getContent(),
        refIndex = getNodeIndex(flowContent, refnode);

    if (refIndex + 1 < flowContent.length) {
      return flowContent[refIndex + 1];
    } else {
      return null; 
    }
  }

  function findFirstEmptyPage (flow) {
    var regions = flow.getRegions();
    for (var i=0; i<regions.length;i++) {
      if (regions[i].webkitRegionOverset == 'empty' && !regions[i].classList.contains('secondary')) {
        return getPageNum(getPageByNode(regions[i]));
      }
    }
  }

  function getImageRef (image) {
    while(!image.classList.contains('img')) {
      image = image.parentNode;
    }

    var ref = image.previousElementSibling;

    while(!(ref.classList.contains('img_ref'))) {
      ref = ref.previousElementSibling;
    }

    return ref;
  }

  var pageNum;

  document.addEventListener("storiesloaded", function () {
    container = document.querySelector('#pages');
    master = document.querySelector('#master-page');
    master.removeAttribute('id');
    master.remove();

    wrapImages();

    fitContent (function() {
      function alignCaptions (callback) {
        var i = 0, captions = captionFlow.getContent();
        
        function validRegion(captionRegion, imageRegion) {
          var imgPage = getPageNum(getPageByNode(imageRegion)),
              captionPage = getPageNum(getPageByNode(captionRegion));

          if (captionPage < imgPage) {
            return false;
          } else if (imageRegion.classList.contains('secondary') && getPageByNode(imageRegion).classList.contains('no-reveal')) {
            if (captionPage > imgPage) {
              return true;
            } else {
              return captionRegion.classList.contains('secondary');
            }
          } else {
            return true;
          }
        }

        function exec () {
          if (i < captions.length) {
            var caption, captionPage, captionIndex,
                captionRegion, imageRegion,
                img, imgPage;

            caption = captions[i],
            captionRegion = getRegionByNode(captionFlow, caption)
            img = caption.previousElementSibling;

            while (!img.classList.contains('img')) {
              img = img.parentElement;
            }

            imageRegion = getRegionByNode(imageFlow, img);

            while (!validRegion(captionRegion, imageRegion)) {
              insertBreakBefore(caption.parentElement, 'figcaption_break');
              captionRegion = getRegionByNode(captionFlow, caption);
            }

            i++;
            window.requestAnimationFrame(exec);
          } else {
            callback();
          }
        }

        exec();
      }

      function allowedAsMainImage(image, pageNum) {
        var buffer = 0, imageRef = getImageRef(image),
            imageRefPageNum = getPageNum(getPageByRegionNode(textFlow, imageRef));
            if (image.classList.contains('push-more')) {
              buffer = -3;
            } else if (image.classList.contains('push')) {
              buffer = -2;
            } else if (image.classList.contains('cheat')) {
              buffer = 1;
            } else if (image.classList.contains('cheat-a-lot')) {
              buffer = 2;
            }

        if (pageNum+buffer >= imageRefPageNum) {
            return true;
          } else  {
            return false;
          }
      }

      function allowedAsNextImage (image, pageNum) {
        if (image.classList.contains('full') || image.classList.contains('not-secondary')) {
          return false;
        } else {
          var imageRef = getImageRef(image),
              imageRefPageNum = getPageNum(getPageByRegionNode(textFlow, imageRef)),
              buffer = (image.classList.contains('cheat')) ?  1 : 0;
              buffer = (image.classList.contains('cheat-a-lot')) ?  2 : buffer;

          if (pageNum+buffer >= imageRefPageNum) {
            return true;
          } 
          
          return false
        }
      }

      function layoutPage () {
        if (pageNum < parseInt(/\d+/.exec(document.querySelector('.paper:last-of-type').id)[0])) {
          var page, image, imagesOnPage = getPageFlowContent(pageNum, imageFlow);

          page = getPageByNum(pageNum);
          
          if (imagesOnPage.length > 0) {
            for (var i = 0; i < imagesOnPage.length; i++) {
              image = imagesOnPage[i];

              if (i == 0) {
                if (! allowedAsMainImage(image, pageNum)) {
                  page.classList.add('reveal_secondary_main');
                  pageNum++;
                  return layoutPage();
                }  if (image.classList.contains('half')) {
                  page.classList.add('half');
                  if (image.classList.contains('no-reveal')) {
                    page.classList.add('no-reveal');
                  }
                  
                  var nextImage = getNextNodeInFlow(imageFlow, image);
                  
                  if (nextImage) {
                    while (!nextImage.classList.contains('img')) {
                      nextImage = getNextNodeInFlow(imageFlow, nextImage);
                    }

                    while(getPageNum(getPageByRegionNode(imageFlow, nextImage)) == pageNum) {
                      nextImage = getNextNodeInFlow(imageFlow, nextImage);
                    }

                    if (allowedAsNextImage(nextImage, pageNum)) {
                      if (nextImage.classList.contains('half')) {
                        page.classList.add('reveal_secondary_images_half');
                      } else {
                        page.classList.add('reveal_secondary_images');
                      }
                    } else {
                      page.classList.add('reveal_secondary_main');
                      pageNum++;
                      return layoutPage();
                    }
                  } else {
                    page.classList.add('reveal_secondary_main');
                    pageNum++;
                    return layoutPage();
                  }
                }
              } else {
                if (! allowedAsNextImage(image, pageNum)) {
                  insertBreakBefore(image.parentNode, 'img_break');
                  pageNum++;
                  return layoutPage();
                }
              }

              if (image.classList.contains('full')) {
                page.classList.add('full');
                pageNum++;
                return layoutPage();
              }
            }
          } else {
            page.classList.add('reveal_secondary_main');
          }

          pageNum++;
          return layoutPage();
        }
      }
    
      var firstPage = document.querySelector('.paper:first-of-type');
      firstPage.classList.add('cover');
      pageNum = parseInt(/\d+/.exec(firstPage.id)[0]) + 1;

      console.log('Layouting pages');
      layoutPage();
      console.log('Positioning captions');
      alignCaptions(function () {
        console.log('Fixing typography');
        window.typographicFixes();

        var firstEmptyPage = Math.max.apply(Math, [
              findFirstEmptyPage(textFlow), 
              findFirstEmptyPage(captionFlow)]),
            lastPageWithContent = document.querySelector('#page-' + (firstEmptyPage - 1));

        console.log(lastPageWithContent);

        while (lastPageWithContent.nextSibling) {
            lastPageWithContent.parentNode.removeChild(lastPageWithContent.nextSibling);
        }

        //indexKeywords();
        indexNames();
        window.setTimeout(function () {
        
          document.layoutcomplete = true;
      
        }, 1000);
        console.log('layout done');
      });
    });
  });
})();