define(['visibleinviewport', 'browser', 'dom'], function (visibleinviewport, browser, dom) { 'use strict'; var thresholdX; var thresholdY; var requestIdleCallback = window.requestIdleCallback || function (fn) { fn(); }; function resetThresholds() { var x = screen.availWidth; var y = screen.availHeight; if (browser.touch) { x *= 1.5; y *= 1.5; } thresholdX = x; thresholdY = y; } dom.addEventListener(window, "orientationchange", resetThresholds, { passive: true }); dom.addEventListener(window, 'resize', resetThresholds, { passive: true }); resetThresholds(); function isVisible(elem) { return visibleinviewport(elem, true, thresholdX, thresholdY); } var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); var self = {}; function cancelAll(tokens) { for (var i = 0, length = tokens.length; i < length; i++) { tokens[i] = true; } } function unveilElementsInternal(instance, callback) { var unveiledElements = []; var cancellationTokens = []; var loadedCount = 0; function unveilInternal(tokenIndex) { var anyFound = false; var out = false; var elements = instance.elements; // TODO: This out construct assumes left to right, top to bottom for (var i = 0, length = elements.length; i < length; i++) { if (cancellationTokens[tokenIndex]) { return; } if (unveiledElements[i]) { continue; } var elem = elements[i]; if (!out && isVisible(elem)) { anyFound = true; unveiledElements[i] = true; callback(elem); loadedCount++; } else { if (anyFound) { out = true; } } } if (loadedCount >= elements.length) { dom.removeEventListener(document, 'focus', unveil, { capture: true, passive: true }); dom.removeEventListener(document, 'scroll', unveil, { capture: true, passive: true }); dom.removeEventListener(document, wheelEvent, unveil, { capture: true, passive: true }); dom.removeEventListener(window, 'resize', unveil, { capture: true, passive: true }); } } function unveil() { cancelAll(cancellationTokens); var index = cancellationTokens.length; cancellationTokens.length++; setTimeout(function () { unveilInternal(index); }, 1); } dom.addEventListener(document, 'focus', unveil, { capture: true, passive: true }); dom.addEventListener(document, 'scroll', unveil, { capture: true, passive: true }); dom.addEventListener(document, wheelEvent, unveil, { capture: true, passive: true }); dom.addEventListener(window, 'resize', unveil, { capture: true, passive: true }); unveil(); } function LazyLoader(options) { this.options = options; } LazyLoader.prototype.createObserver = function () { unveilElementsInternal(this, this.options.callback); this.observer = 1; }; LazyLoader.prototype.addElements = function (elements) { this.elements = this.elements || []; for (var i = 0, length = elements.length; i < length; i++) { this.elements.push(elements[i]); } var observer = this.observer; if (!observer) { this.createObserver(); } }; LazyLoader.prototype.destroyObserver = function (elements) { }; LazyLoader.prototype.destroy = function (elements) { this.destroyObserver(); this.options = null; }; function unveilElements(elements, root, callback) { if (!elements.length) { return; } var lazyLoader = new LazyLoader({ callback: callback }); lazyLoader.addElements(elements); } LazyLoader.lazyChildren = function (elem, callback) { unveilElements(elem.getElementsByClassName('lazy'), elem, callback); }; return LazyLoader; });