Gitter migration: Point people to app.gitter.im (rollout pt. 1)
[gitter.git] / public / js / views / behaviors / smooth-scroll.js
blob07604160f3458ea46a92c06c88a656b2a6d7793f
1 'use strict';
2 var Marionette = require('backbone.marionette');
3 var behaviourLookup = require('./lookup');
4 var _ = require('lodash');
5 var rafUtils = require('../../utils/raf-utils');
6 var passiveEventListener = require('../../utils/passive-event-listener');
8 var Behavior = Marionette.Behavior.extend({
9   defaults: {
10     scrollElementSelector: null,
11     contentWrapper: null
12   },
14   initialize: function() {
15     var selector = this.options.scrollElementSelector;
16     var wrapperSelector = this.options.contentWrapper;
17     this.queue = [];
19     this.scrollElement = selector ? document.querySelector(selector) : this.view.el;
20     this.wrapper = wrapperSelector ? this.scrollElement.querySelector(wrapperSelector) : null;
22     // Make sure every time the collectionView renders it decorates its childs and updates the banners
23     this.listenTo(this.view, 'render', this.decorateIfVisible);
25     // Debounced actions for improved performance
26     this.lazyDecorator = _.debounce(this.decorateIfVisible.bind(this), 500);
27     this.lazyDecoratorQueue = rafUtils.debounce(this.decorateQueue, this);
29     this.lazyTracker = _.debounce(this.trackViewport.bind(this), 500);
30     this.lazyPointerEvents = _.debounce(this.enablePointerEvents.bind(this), 250);
32     this.scrollHandler = this.smoothScroll.bind(this);
33     passiveEventListener.addEventListener(this.scrollElement, 'scroll', this.scrollHandler);
34   },
36   // Trigger an event on the child of it's currently on screen
37   decorateIfVisible: function() {
38     this.view.children.each(
39       function(child) {
40         if (this.isElementVisible(child.el)) child.trigger('messageInViewport');
41       }.bind(this)
42     );
43   },
45   onAddChild: function(child) {
46     this.queue.push(child);
47     this.lazyDecoratorQueue();
48   },
50   decorateQueue: function() {
51     var queue = this.queue;
52     this.queue = [];
54     queue.forEach(function(child) {
55       if (this.isElementVisible(child.el)) child.trigger('messageInViewport');
56     }, this);
57   },
59   // Give an element tells you if it's on screen or above/below the fold
60   isElementVisible: function(el) {
61     var rect = el.getBoundingClientRect();
62     return (
63       rect.bottom >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight)
64     );
65   },
67   // Trigger an event on the view after scrolling to keep track of the most centered element on screen
68   trackViewport: function() {
69     this.view.triggerMethod('trackViewportCenter');
70   },
72   // Disable hover and other pointer events while scrolling
73   disablePointerEvents: function() {
74     if (this.wrapper && !this.wrapper.classList.contains('disable-hover'))
75       this.wrapper.classList.add('disable-hover');
76   },
78   enablePointerEvents: function() {
79     if (this.wrapper) this.wrapper.classList.remove('disable-hover');
80   },
82   smoothScroll: function() {
83     this.disablePointerEvents();
84     this.lazyDecorator();
85     this.lazyTracker();
86     this.lazyPointerEvents();
87   },
89   onDestroy: function() {
90     passiveEventListener.removeEventListener(this.scrollElement, 'scroll', this.scrollHandler);
91   }
92 });
94 behaviourLookup.register('SmoothScroll', Behavior);
95 module.exports = Behavior;