function scroller() {
  var windowHeight;
  var container = d3.select('body');
  // event dispatcher
  var dispatch = d3.dispatch("active", "progress");

  // d3 selection of all the
  // text sections that will
  // be scrolled through
  var sections = null;

  // array that will hold the
  // y coordinate of each section
  // that is scrolled through
  var sectionPositions = [];
  var currentIndex = -1;
  var currentPos = -20;
  // y coordinate of
  var containerStart = 0;

  /**
   * scroll - constructor function.
   * Sets up scroller to monitor
   * scrolling of els selection.
   *
   * @param els - d3 selection of
   *  elements that will be scrolled
   *  through by user.
   */
  function scroll(els) {
    sections = els;

    // when window is scrolled call
    // position. When it is resized
    // call resize.
    d3.select(window)
      .on("scroll.scroller", position)
      .on("resize.scroller", resize);

    // manually call resize
    // initially to setup
    // scroller.
    resize();

    // hack to get position
    // to be called once for
    // the scroll position on
    // load.
    d3.timer(function() {
      position();
      return true;
    });
  }

  /**
   * resize - called initially and
   * also when page is resized.
   * Resets the sectionPositions
   *
   */
  function resize() {
    // sectionPositions will be each sections
    // starting position relative to the top
    // of the first section.
    sectionPositions = [];
    var startPos;
    sections.each(function(d,i) {
      var top = this.getBoundingClientRect().top;
      if(i === 0) {
        startPos = top;
      }
      sectionPositions.push(top - startPos);
    });
    console.log(sectionPositions);
    containerStart = container.node().getBoundingClientRect().top + window.pageYOffset;
  }

  /**
   * position - get current users position.
   * if user has scrolled to new section,
   * dispatch active event with new section
   * index.
   *
   */
  function position() {
    var pos = window.pageYOffset - 10 - containerStart;
    var dir = (pos > currentPos) ? +1 : -1;
    currentPos = pos;
    var sectionIndex = d3.bisect(sectionPositions, pos);
    sectionIndex = Math.min(sections.size() - 1, sectionIndex);


    // get the height of the window
    var height = window.innerHeight;

    // if we are scrolling down
    if((dir > 0)) {

      // is the index we think we are at on the screen?
      var onScreen = (sectionPositions[sectionIndex] < (pos + height));

      // if not, revert
      if(!onScreen) {
        console.log('keep current');
        sectionIndex = currentIndex;
      }
      // scrolling down
    } else {
      // is the index we think we are at below the screen?
      var belowScreen = (sectionPositions[sectionIndex] > (pos + height));


      // if so, bump it back
      if(belowScreen) {
        console.log('use prev');
        sectionIndex = Math.max(sectionIndex - 1, 0);
      }
    }

    if (currentIndex !== sectionIndex) {
      dispatch.active(sectionIndex);
      currentIndex = sectionIndex;
    }

    var prevIndex = Math.max(sectionIndex - 1, 0);
    var prevTop = sectionPositions[prevIndex];
    var progress = (pos - prevTop) / (sectionPositions[sectionIndex] - prevTop);
    dispatch.progress(currentIndex, progress);
  }

  /**
   * container - get/set the parent element
   * of the sections. Useful for if the
   * scrolling doesn't start at the very top
   * of the page.
   *
   * @param value - the new container value
   */
  scroll.container = function(value) {
    if (arguments.length === 0) {
      return container;
    }
    container = value;
    return scroll;
  };

  // allows us to bind to scroller events
  // which will interally be handled by
  // the dispatcher.
  d3.rebind(scroll, dispatch, "on");

  return scroll;
}
/**
 * scroller - handles the details
 * of figuring out which section
 * the user is currently scrolled
 * to.
 *
 */
handles the details
of figuring out which section
the user is currently scrolled
to.
handles the details of figuring out which section the user is currently scrolled to .
function scroller() {
  var windowHeight;
  var container = d3.select('body');
  var dispatch = d3.dispatch("active", "progress");
  var sections = null;
  var sectionPositions = [];
  var currentIndex = -1;
  var currentPos = -20;
  var containerStart = 0;
  function scroll(els) {
    sections = els;
    d3.select(window)
      .on("scroll.scroller", position)
      .on("resize.scroller", resize);
    resize();
    d3.timer(function() {
      position();
      return true;
    });
  }
  function resize() {
    sectionPositions = [];
    var startPos;
    sections.each(function(d,i) {
      var top = this.getBoundingClientRect().top;
      if(i === 0) {
        startPos = top;
      }
      sectionPositions.push(top - startPos);
    });
    console.log(sectionPositions);
    containerStart = container.node().getBoundingClientRect().top + window.pageYOffset;
  }
  function position() {
    var pos = window.pageYOffset - 10 - containerStart;
    var dir = (pos > currentPos) ? +1 : -1;
    currentPos = pos;
    var sectionIndex = d3.bisect(sectionPositions, pos);
    sectionIndex = Math.min(sections.size() - 1, sectionIndex);
    var height = window.innerHeight;
    if((dir > 0)) {
      var onScreen = (sectionPositions[sectionIndex] < (pos + height));
      if(!onScreen) {
        console.log('keep current');
        sectionIndex = currentIndex;
      }
    } else {
      var belowScreen = (sectionPositions[sectionIndex] > (pos + height));
      if(belowScreen) {
        console.log('use prev');
        sectionIndex = Math.max(sectionIndex - 1, 0);
      }
    }
    if (currentIndex !== sectionIndex) {
      dispatch.active(sectionIndex);
      currentIndex = sectionIndex;
    }
    var prevIndex = Math.max(sectionIndex - 1, 0);
    var prevTop = sectionPositions[prevIndex];
    var progress = (pos - prevTop) / (sectionPositions[sectionIndex] - prevTop);
    dispatch.progress(currentIndex, progress);
  }
  scroll.container = function(value) {
    if (arguments.length === 0) {
      return container;
    }
    container = value;
    return scroll;
  };
  d3.rebind(scroll, dispatch, "on");
  return scroll;
}
function scroller ( ) { var windowHeight ; var container = d3 . select ( 'body' ) ; var dispatch = d3 . dispatch ( "active" , "progress" ) ; var sections = null ; var sectionPositions = [ ] ; var currentIndex = - 1 ; var currentPos = - 20 ; var containerStart = 0 ; function scroll ( els ) { sections = els ; d3 . select ( window ) . on ( "scroll.scroller" , position ) . on ( "resize.scroller" , resize ) ; resize ( ) ; d3 . timer ( function ( ) { position ( ) ; return true ; } ) ; } function resize ( ) { sectionPositions = [ ] ; var startPos ; sections . each ( function ( d , i ) { var top = this . getBoundingClientRect ( ) . top ; if ( i === 0 ) { startPos = top ; } sectionPositions . push ( top - startPos ) ; } ) ; console . log ( sectionPositions ) ; containerStart = container . node ( ) . getBoundingClientRect ( ) . top + window . pageYOffset ; } function position ( ) { var pos = window . pageYOffset - 10 - containerStart ; var dir = ( pos > currentPos ) ? + 1 : - 1 ; currentPos = pos ; var sectionIndex = d3 . bisect ( sectionPositions , pos ) ; sectionIndex = Math . min ( sections . size ( ) - 1 , sectionIndex ) ; var height = window . innerHeight ; if ( ( dir > 0 ) ) { var onScreen = ( sectionPositions [ sectionIndex ] < ( pos + height ) ) ; if ( ! onScreen ) { console . log ( 'keep current' ) ; sectionIndex = currentIndex ; } } else { var belowScreen = ( sectionPositions [ sectionIndex ] > ( pos + height ) ) ; if ( belowScreen ) { console . log ( 'use prev' ) ; sectionIndex = Math . max ( sectionIndex - 1 , 0 ) ; } } if ( currentIndex !== sectionIndex ) { dispatch . active ( sectionIndex ) ; currentIndex = sectionIndex ; } var prevIndex = Math . max ( sectionIndex - 1 , 0 ) ; var prevTop = sectionPositions [ prevIndex ] ; var progress = ( pos - prevTop ) / ( sectionPositions [ sectionIndex ] - prevTop ) ; dispatch . progress ( currentIndex , progress ) ; } scroll . container = function ( value ) { if ( arguments . length === 0 ) { return container ; } container = value ; return scroll ; } ; d3 . rebind ( scroll , dispatch , "on" ) ; return scroll ; }
scroller - handles the details
 of figuring out which section
 the user is currently scrolled
 to.
scroller - handles the details of figuring out which section the user is currently scrolled to .
// event dispatcher // d3 selection of all the // text sections that will // be scrolled through // array that will hold the // y coordinate of each section // that is scrolled through // y coordinate of /**
   * scroll - constructor function.
   * Sets up scroller to monitor
   * scrolling of els selection.
   *
   * @param els - d3 selection of
   *  elements that will be scrolled
   *  through by user.
   */ // when window is scrolled call // position. When it is resized // call resize. // manually call resize // initially to setup // scroller. // hack to get position // to be called once for // the scroll position on // load. /**
   * resize - called initially and
   * also when page is resized.
   * Resets the sectionPositions
   *
   */ // sectionPositions will be each sections // starting position relative to the top // of the first section. /**
   * position - get current users position.
   * if user has scrolled to new section,
   * dispatch active event with new section
   * index.
   *
   */ // get the height of the window // if we are scrolling down // is the index we think we are at on the screen? // if not, revert // scrolling down // is the index we think we are at below the screen? // if so, bump it back /**
   * container - get/set the parent element
   * of the sections. Useful for if the
   * scrolling doesn't start at the very top
   * of the page.
   *
   * @param value - the new container value
   */ // allows us to bind to scroller events // which will interally be handled by // the dispatcher.