<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ember Virtual List Component</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.13.5/ember-template-compiler.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.13.5/ember.debug.js"></script>
<style id="jsbin-css">
/* Put your CSS here */
html, body {
  margin: 20px;
  box-sizing: border-box;
}

h1, h2 {
  font-weight: normal;
}

.x-virtual-list__container {
  width: 300px;
  height: 500px;
  outline: 1px solid black;
  overflow: scroll;
  position: relative;
}

.x-virtual-list__scroller {
  position: absolute;
  background-color: blue;
  width: 100%;
}

.x-virtual_list__item {
  position: absolute;
  outline: 1px solid white;
  height: 40px;
  width: calc(100% - 10px);
  line-height: 40px;
  padding-left: 10px;
  color: white;
  background-color: green;
}

.x-virtual_list__item2 {
  outline: 1px solid white;
  height: 40px;
  width: calc(100% - 10px);
  line-height: 40px;
  padding-left: 10px;
  color: white;
  background-color: green;
  pointer-events: none;
}


.x-virtual-list__debug {
  list-style-type: none;
  padding-left: 0;
}
</style>
</head>
<body>

  <script type="text/x-handlebars">
    <h1>Scroll Scroll</h1>
    {{outlet}}
  </script>

  <script type="text/x-handlebars" id="components/x-virtual-list-item">
    {{item.name}}  
  </script>  
    
  <script type="text/x-handlebars" id="components/x-virtual-list">
    <div class="x-virtual-list__container">
      <div class="x-virtual-list__scroller" style={{scrollerStyle}}>
      <div class="x-virtual-list__positioner" style={{positionerStyle}}>
          {{#each visibleRows key='rowKey' as |row|}}
            <div class="x-virtual_list__item2" {{action 'log'}}>{{component itemComponent item=row.item}}</div>
          {{/each}}
        </div>
      </div>
    </div>
    <ul class="x-virtual-list__debug">
      <li>scroll offset: {{scrollOffset}}</li>
      <li>visible elements: {{visibleRows.length}}</li>
      <li>start offset: {{startOffset}}</li>
      <li>end offset: {{endOffset}}</li>  
      <li>start index: {{startIndex}}</li>
      <li>end index: {{endIndex}}</li>
    </ul>
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    {{x-virtual-list items=model}}

    <div class="x-virtual-list__container">
      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>


    </div>


  </script>

<script id="jsbin-javascript">
App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexRoute = Ember.Route.extend({
  model: function() {
    var people = [
      'Bill', 
      'Zach', 
      'Seung',
      'Kapil', 
      'Tom', 
      'Ren',
      'George', 
      'Ed', 
      'Ben',
      'Curtis', 
      'Allie', 
      'Tao',
      'David', 
      'Gian', 
      'Clifton',
      'Richard', 
      'Max', 
      'Tony',
      'Kiran', 
      'Mike', 
      'Brian',
      'Joe',
      'Matt',
      'Alex',
      'Jeremy',
      'Travis'
    ];
    var items = [];
    for (var i = 0; i < 1000; i++) {
      var person = people[Math.floor(Math.random() * people.length)];
      items.push({
        name: (i+1) + ". " + person
      });
    }
    return items;
  }
});

App.XVirtualListComponent = Ember.Component.extend({
  rowHeight: 40,
  scrollOffset: 0,
  containerHeight: 0,
  itemComponent: 'x-virtual-list-item',
  
  visibleRows: function() {
    var rowHeight = this.get('rowHeight');
    var startIndex = this.get('startIndex');
    var endIndex = this.get('endIndex');
    return this.get('items').slice(startIndex, endIndex).map(function(item, i) { 
      var index = i + startIndex;
      var top = (rowHeight * index);
      return {
        rowKey: index,
        item: item,
      };
    });
  }.property('items', 'rowHeight', 'startIndex', 'endIndex'),
  
  positionerStyle: function() {
      var top = this.get('rowHeight') * this.get('startIndex');
      return new Ember.Handlebars.SafeString("transform: translateY(" + top + "px);");
  }.property('startIndex', 'rowHeight'),
  
  startOffset: function() {
    return Math.abs(this.get('scrollOffset')) / this.get('rowHeight');
  }.property('scrollOffset', 'containerHeight', 'rowHeight'),

  endOffset: function() {
    return (Math.abs(this.get('scrollOffset')) + this.getWithDefault('containerHeight', 0)) / this.get('rowHeight');
  }.property('scrollOffset', 'containerHeight', 'rowHeight'),

  startIndex: function() {
    return Math.floor(this.get('startOffset'));
  }.property('startOffset'),

  endIndex: function() {
    return Math.ceil(this.get('endOffset'));
  }.property('endOffset'),

  scrollerStyle: function() {
    var height = this.get('items.length') * this.get('rowHeight');
    var style = "height: " + height + "px";
    return new Ember.Handlebars.SafeString(style);
  }.property('items.length', 'rowHeight'),
  
  hookupScroll: function() {
    var component = this;
    this.outer().bind('scroll', function() {
       Ember.run.once(component, function() {
         var containerOffset = this.outer().offset().top;
         var scrollerOffset = this.inner().offset().top;
         var scrollOffset = scrollerOffset - containerOffset;
         this.set('scrollOffset', scrollOffset);
       });
     });
  }.on('didInsertElement'),
  
  readySetGo: function() {
    Ember.run.later(this, function() {
      this.set('containerHeight', this.outer().height());
    });
  }.on('didInsertElement'),
  
  outer: function() {
    return this.$().find('.x-virtual-list__container');
  },
  
  inner: function() {
    return this.$().find('.x-virtual-list__scroller');
  },
  
  actions: {
    log: function() {
      alert("hello");
    }
  },
  
});
</script>

<script id="jsbin-source-html" type="text/html"><!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ember Virtual List Component</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"><\/script>
  <script src="http://builds.emberjs.com/tags/v1.13.5/ember-template-compiler.js"><\/script>
  <script src="http://builds.emberjs.com/tags/v1.13.5/ember.debug.js"><\/script>
</head>
<body>

  <script type="text/x-handlebars">
    <h1>Scroll Scroll</h1>
    {{outlet}}
  <\/script>

  <script type="text/x-handlebars" id="components/x-virtual-list-item">
    {{item.name}}  
  <\/script>  
    
  <script type="text/x-handlebars" id="components/x-virtual-list">
    <div class="x-virtual-list__container">
      <div class="x-virtual-list__scroller" style={{scrollerStyle}}>
      <div class="x-virtual-list__positioner" style={{positionerStyle}}>
          {{#each visibleRows key='rowKey' as |row|}}
            <div class="x-virtual_list__item2" {{action 'log'}}>{{component itemComponent item=row.item}}</div>
          {{/each}}
        </div>
      </div>
    </div>
    <ul class="x-virtual-list__debug">
      <li>scroll offset: {{scrollOffset}}</li>
      <li>visible elements: {{visibleRows.length}}</li>
      <li>start offset: {{startOffset}}</li>
      <li>end offset: {{endOffset}}</li>  
      <li>start index: {{startIndex}}</li>
      <li>end index: {{endIndex}}</li>
    </ul>
  <\/script>

  <script type="text/x-handlebars" data-template-name="index">
    {{x-virtual-list items=model}}

    <div class="x-virtual-list__container">
      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>

      <div class="x-virtual_list__item2">
        Hello
      </div>


    </div>


  <\/script>

</body>
</html>
</script>

<script id="jsbin-source-css" type="text/css">/* Put your CSS here */
html, body {
  margin: 20px;
  box-sizing: border-box;
}

h1, h2 {
  font-weight: normal;
}

.x-virtual-list__container {
  width: 300px;
  height: 500px;
  outline: 1px solid black;
  overflow: scroll;
  position: relative;
}

.x-virtual-list__scroller {
  position: absolute;
  background-color: blue;
  width: 100%;
}

.x-virtual_list__item {
  position: absolute;
  outline: 1px solid white;
  height: 40px;
  width: calc(100% - 10px);
  line-height: 40px;
  padding-left: 10px;
  color: white;
  background-color: green;
}

.x-virtual_list__item2 {
  outline: 1px solid white;
  height: 40px;
  width: calc(100% - 10px);
  line-height: 40px;
  padding-left: 10px;
  color: white;
  background-color: green;
  pointer-events: none;
}


.x-virtual-list__debug {
  list-style-type: none;
  padding-left: 0;
}</script>

<script id="jsbin-source-javascript" type="text/javascript">App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexRoute = Ember.Route.extend({
  model: function() {
    var people = [
      'Bill', 
      'Zach', 
      'Seung',
      'Kapil', 
      'Tom', 
      'Ren',
      'George', 
      'Ed', 
      'Ben',
      'Curtis', 
      'Allie', 
      'Tao',
      'David', 
      'Gian', 
      'Clifton',
      'Richard', 
      'Max', 
      'Tony',
      'Kiran', 
      'Mike', 
      'Brian',
      'Joe',
      'Matt',
      'Alex',
      'Jeremy',
      'Travis'
    ];
    var items = [];
    for (var i = 0; i < 1000; i++) {
      var person = people[Math.floor(Math.random() * people.length)];
      items.push({
        name: (i+1) + ". " + person
      });
    }
    return items;
  }
});

App.XVirtualListComponent = Ember.Component.extend({
  rowHeight: 40,
  scrollOffset: 0,
  containerHeight: 0,
  itemComponent: 'x-virtual-list-item',
  
  visibleRows: function() {
    var rowHeight = this.get('rowHeight');
    var startIndex = this.get('startIndex');
    var endIndex = this.get('endIndex');
    return this.get('items').slice(startIndex, endIndex).map(function(item, i) { 
      var index = i + startIndex;
      var top = (rowHeight * index);
      return {
        rowKey: index,
        item: item,
      };
    });
  }.property('items', 'rowHeight', 'startIndex', 'endIndex'),
  
  positionerStyle: function() {
      var top = this.get('rowHeight') * this.get('startIndex');
      return new Ember.Handlebars.SafeString("transform: translateY(" + top + "px);");
  }.property('startIndex', 'rowHeight'),
  
  startOffset: function() {
    return Math.abs(this.get('scrollOffset')) / this.get('rowHeight');
  }.property('scrollOffset', 'containerHeight', 'rowHeight'),

  endOffset: function() {
    return (Math.abs(this.get('scrollOffset')) + this.getWithDefault('containerHeight', 0)) / this.get('rowHeight');
  }.property('scrollOffset', 'containerHeight', 'rowHeight'),

  startIndex: function() {
    return Math.floor(this.get('startOffset'));
  }.property('startOffset'),

  endIndex: function() {
    return Math.ceil(this.get('endOffset'));
  }.property('endOffset'),

  scrollerStyle: function() {
    var height = this.get('items.length') * this.get('rowHeight');
    var style = "height: " + height + "px";
    return new Ember.Handlebars.SafeString(style);
  }.property('items.length', 'rowHeight'),
  
  hookupScroll: function() {
    var component = this;
    this.outer().bind('scroll', function() {
       Ember.run.once(component, function() {
         var containerOffset = this.outer().offset().top;
         var scrollerOffset = this.inner().offset().top;
         var scrollOffset = scrollerOffset - containerOffset;
         this.set('scrollOffset', scrollOffset);
       });
     });
  }.on('didInsertElement'),
  
  readySetGo: function() {
    Ember.run.later(this, function() {
      this.set('containerHeight', this.outer().height());
    });
  }.on('didInsertElement'),
  
  outer: function() {
    return this.$().find('.x-virtual-list__container');
  },
  
  inner: function() {
    return this.$().find('.x-virtual-list__scroller');
  },
  
  actions: {
    log: function() {
      alert("hello");
    }
  },
  
});</script></body>
</html>