Source: components/video-defer.js

; (function () {
    'use strict';

    /**
     * Play videos based on scroll position
     * @namespace
     */
    db.libs.videoDefer = (function ($) {

        var name = 'videoDefer';

        /**
         * The threshold for when videos in view should be played
         * @private
         * @memberof db.libs.videoDefer
         * @constant {number}
         */
        var threshold = 200;

        /**
         * The array for all the videos to be played
         * @private
         * @memberof db.libs.videoDefer
         * @type {array}
         */
        var videos = [];

        /**
         * Check to see if scroll-listener has been added
         * @private
         * @memberof db.libs.videoDefer
         * @type {bool}
         */
        var scrollListenerAdded = false;

        /**
         * Plays a video with <code>data-video-defer="view"</code> attribute if it's within the viewport threshold
         * @private
         * @memberof db.libs.videoDefer
         */
        function playVideosInView() {
            var offset = $(document).scrollTop() + $(window).height() + threshold;
            videos.each(function (i, video) {
                if (video !== null) {
                    if ($(video).offset().top < offset) {
                        video.play(); // Take it away!
                        videos[i] = null;
                        $(video).attr('data-is-played', 'true');
                    }
                }
            });
        }

        /**
        * Find videos in document that haven't been played yet
        * @private
        * @memberof db.libs.videoDefer
        */
        function findVideos() {
            videos = $('[data-video-defer="view"]:not([data-is-played="true"])');

            // If video has no <source> children, we check for the attributes data-sources-small and data-sources-mediumup to create them.
            // This makes it possible to have different sources for different screensizes, for example different cropped videos for small screens.
            videos.each(function(i, video) {
                if(!$(video).children().length) {
                    var sources = [];
                    var types = [];

                    // Get the attributes based on screensize
                    if(window.innerWidth <= 640 && $(video).attr('data-sources-small')) {
                        sources = $(video).attr('data-sources-small').split(",");
                        if($(video).attr('data-types-small')) {
                            types = $(video).attr('data-types-small').split(",");
                        }
                        if($(video).attr('data-poster-small')) {
                            $(video).attr('poster', $(video).attr('data-poster-small'));
                        }
                    } else if (window.innerWidth >= 641 && $(video).attr('data-sources-mediumup')) {
                        sources = $(video).attr('data-sources-mediumup').split(",");
                        if($(video).attr('data-types-mediumup')) {
                            types = $(video).attr('data-types-mediumup').split(",");
                        }
                        if($(video).attr('data-poster-mediumup')) {
                            $(video).attr('poster', $(video).attr('data-poster-mediumup'));
                        }
                    }

                    // Create the <source> elements
                    if(sources.length) {
                        sources.forEach(function(source, i) {
                            var sourceElem = document.createElement('source');
                            sourceElem.src = source.trim();

                            if(types[i]) {
                                sourceElem.type = "video/" + types[i].trim();
                            }

                            $(video).append(sourceElem);
                        });
                    }
                }
            });
        }

        /**
        * Bind scroll-listener
        * @private
        * @memberof db.libs.videoDefer
        */
        function bindScroll() {
            scrollListenerAdded = true;
            $(window).on('scroll', function () {
                window.requestAnimationFrame(playVideosInView);
            });
        }

        /**
         * Initialize the component
         * @public
         * @memberof db.libs.videoDefer
         */
        function init() {
            if (!db.utils.isInitialized($('body'), name)) {
                if (document.readyState === 'complete') {
                    playVideosInView();
                } else {
                    window.addEventListener('load', function () {
                        setTimeout(function () {
                            playVideosInView();
                        }, 0);
                    });
                }

                // Find videos in document
                findVideos();

                // Bind scroll
                if(videos.length && !scrollListenerAdded) {
                    bindScroll();
                }

                // Set component as initialized
                db.utils.initialized($('body'), name);
            }
        }

        /**
         * Reflow the component
         * @public
         * @memberof db.libs.videoDefer
         */
        function reflow() {
            if (!scrollListenerAdded) {
                bindScroll();
            }

            findVideos();
            playVideosInView();
        }

        return {
            init: init,
            reflow: reflow
        };

    })(jQuery);
})();