/** * @license hey, [be]Lazy.js - v1.8.2 - 2016.10.25 A fast, small and dependency free lazy load script (https://github.com/dinbror/blazy) (c) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy */ 'use strict'; (function(root, factory) { if ("function" === typeof define && define.amd) { define(factory); } else { if ("object" === typeof exports) { module.exports = factory(); } else { root.Blazy = factory(); } } })(this, function() { /** * @param {!Object} self * @return {undefined} */ function initialize(self) { var util = self._util; util.elements = toArray(self.options); util.count = util.elements.length; if (util.destroyed) { /** @type {boolean} */ util.destroyed = false; if (self.options.container) { each(self.options.container, function(table) { bindEvent(table, "scroll", util.validateT); }); } bindEvent(window, "resize", util.saveViewportOffsetT); bindEvent(window, "resize", util.validateT); bindEvent(window, "scroll", util.validateT); } validate(self); } /** * @param {!Object} self * @return {undefined} */ function validate(self) { var util = self._util; /** @type {number} */ var i = 0; for (; i < util.count; i++) { var el = util.elements[i]; var options; a: { var item = el; options = self.options; var value = item.getBoundingClientRect(); if (options.container && _supportClosest && (item = item.closest(options.containerClass))) { item = item.getBoundingClientRect(); options = extend(item, opts) ? extend(value, { top : item.top - options.offset, right : item.right + options.offset, bottom : item.bottom + options.offset, left : item.left - options.offset }) : false; break a; } options = extend(value, opts); } if (options || hasClass(el, self.options.successClass)) { self.load(el); util.elements.splice(i, 1); util.count--; i--; } } if (0 === util.count) { self.destroy(); } } /** * @param {!ClientRect} a * @param {!ClientRect} b * @return {?} */ function extend(a, b) { return a.right >= b.left && a.bottom >= b.top && a.left <= b.right && a.top <= b.bottom; } /** * @param {!Object} target * @param {!Object} data * @param {!Object} options * @return {undefined} */ function init(target, data, options) { if (!hasClass(target, options.successClass) && (data || options.loadInvisible || 0 < target.offsetWidth && 0 < target.offsetHeight)) { if (data = target.getAttribute(src) || target.getAttribute(options.src)) { data = data.split(options.separator); var src = data[A && 1 < data.length ? 1 : 0]; var srcset = target.getAttribute(options.srcset); /** @type {boolean} */ var g = "img" === target.nodeName.toLowerCase(); var p = (data = target.parentNode) && "picture" === data.nodeName.toLowerCase(); if (g || void 0 === target.src) { /** @type {!Image} */ var img = new Image; /** * @return {undefined} */ var onErrorHandler = function() { if (options.error) { options.error(target, "invalid"); } addClass(target, options.errorClass); unbindEvent(img, "error", onErrorHandler); unbindEvent(img, "load", onLoadHandler); }; /** * @return {undefined} */ var onLoadHandler = function() { if (g) { if (!p) { handleSources(target, src, srcset); } } else { /** @type {string} */ target.style.backgroundImage = 'url("' + src + '")'; } load(target, options); unbindEvent(img, "load", onLoadHandler); unbindEvent(img, "error", onErrorHandler); }; if (p) { /** @type {!Object} */ img = target; each(data.getElementsByTagName("source"), function(el) { var attName = options.srcset; var value = el.getAttribute(attName); if (value) { el.setAttribute("srcset", value); el.removeAttribute(attName); } }); } bindEvent(img, "error", onErrorHandler); bindEvent(img, "load", onLoadHandler); handleSources(img, src, srcset); } else { target.src = src; load(target, options); } } else { if ("video" === target.nodeName.toLowerCase()) { each(target.getElementsByTagName("source"), function(b) { var script = options.src; var e = b.getAttribute(script); if (e) { b.setAttribute("src", e); b.removeAttribute(script); } }); target.load(); load(target, options); } else { if (options.error) { options.error(target, "missing"); } addClass(target, options.errorClass); } } } } /** * @param {!Object} item * @param {!Object} options * @return {undefined} */ function load(item, options) { addClass(item, options.successClass); if (options.success) { options.success(item); } item.removeAttribute(options.src); item.removeAttribute(options.srcset); each(options.breakpoints, function(options) { item.removeAttribute(options.src); }); } /** * @param {!Object} elem * @param {!Object} src * @param {?} srcset * @return {undefined} */ function handleSources(elem, src, srcset) { if (srcset) { elem.setAttribute("srcset", srcset); } /** @type {!Object} */ elem.src = src; } /** * @param {!Object} el * @param {string} cn * @return {?} */ function hasClass(el, cn) { return -1 !== (" " + el.className + " ").indexOf(" " + cn + " "); } /** * @param {!Object} el * @param {string} c * @return {undefined} */ function addClass(el, c) { if (!hasClass(el, c)) { el.className += " " + c; } } /** * @param {!Object} s * @return {?} */ function toArray(s) { /** @type {!Array} */ var result = []; s = s.root.querySelectorAll(s.selector); var i = s.length; for (; i--; result.unshift(s[i])) { } return result; } /** * @param {number} offset * @return {undefined} */ function saveViewportOffset(offset) { opts.bottom = (window.innerHeight || document.documentElement.clientHeight) + offset; opts.right = (window.innerWidth || document.documentElement.clientWidth) + offset; } /** * @param {!Object} element * @param {string} type * @param {!Function} fn * @return {undefined} */ function bindEvent(element, type, fn) { if (element.attachEvent) { if (element.attachEvent) { element.attachEvent("on" + type, fn); } } else { element.addEventListener(type, fn, { capture : false, passive : true }); } } /** * @param {!Object} element * @param {string} event * @param {!Function} fn * @return {undefined} */ function unbindEvent(element, event, fn) { if (element.detachEvent) { if (element.detachEvent) { element.detachEvent("on" + event, fn); } } else { element.removeEventListener(event, fn, { capture : false, passive : true }); } } /** * @param {string} i * @param {!Function} a * @return {undefined} */ function each(i, a) { if (i && a) { var o = i.length; /** @type {number} */ var s = 0; for (; s < o && false !== a(i[s], s); s++) { } } } /** * @param {!Function} source * @param {number} k * @param {?} fn * @return {?} */ function throttle(source, k, fn) { /** @type {number} */ var d = 0; return function() { /** @type {number} */ var n = +new Date; if (!(n - d < k)) { /** @type {number} */ d = n; source.apply(fn, arguments); } }; } var src; var opts; var A; var _supportClosest; return function(options) { if (!document.querySelectorAll) { var self = document.createStyleSheet(); /** * @param {string} val * @param {!Array} r * @param {number} n * @param {number} i * @param {!Array} a * @return {!NodeList} */ document.querySelectorAll = function(val, r, n, i, a) { a = document.all; /** @type {!Array} */ r = []; /** @type {!Array} */ val = val.replace(/\[for\b/gi, "[htmlFor").split(","); /** @type {number} */ n = val.length; for (; n--;) { self.addRule(val[n], "k:v"); i = a.length; for (; i--;) { if (a[i].currentStyle.k) { r.push(a[i]); } } self.removeRule(0); } return r; }; } var scope = this; var util = scope._util = {}; /** @type {!Array} */ util.elements = []; /** @type {boolean} */ util.destroyed = true; scope.options = options || {}; scope.options.error = scope.options.error || false; scope.options.offset = scope.options.offset || 100; scope.options.root = scope.options.root || document; scope.options.success = scope.options.success || false; scope.options.selector = scope.options.selector || ".b-lazy"; scope.options.separator = scope.options.separator || "|"; scope.options.containerClass = scope.options.container; scope.options.container = scope.options.containerClass ? document.querySelectorAll(scope.options.containerClass) : false; scope.options.errorClass = scope.options.errorClass || "b-error"; scope.options.breakpoints = scope.options.breakpoints || false; scope.options.loadInvisible = scope.options.loadInvisible || false; scope.options.successClass = scope.options.successClass || "b-loaded"; scope.options.validateDelay = scope.options.validateDelay || 25; scope.options.saveViewportOffsetDelay = scope.options.saveViewportOffsetDelay || 50; scope.options.srcset = scope.options.srcset || "data-srcset"; scope.options.src = src = scope.options.src || "data-src"; /** @type {function(this:Element, string): (Element|null)} */ _supportClosest = Element.prototype.closest; /** @type {boolean} */ A = 1 < window.devicePixelRatio; opts = {}; /** @type {number} */ opts.top = 0 - scope.options.offset; /** @type {number} */ opts.left = 0 - scope.options.offset; /** * @return {undefined} */ scope.revalidate = function() { initialize(scope); }; /** * @param {string} root * @param {!Object} appName * @return {undefined} */ scope.load = function(root, appName) { var options = this.options; if (void 0 === root.length) { init(root, appName, options); } else { each(root, function(Nonstruct) { init(Nonstruct, appName, options); }); } }; /** * @return {undefined} */ scope.destroy = function() { var util = this._util; if (this.options.container) { each(this.options.container, function(img) { unbindEvent(img, "scroll", util.validateT); }); } unbindEvent(window, "scroll", util.validateT); unbindEvent(window, "resize", util.validateT); unbindEvent(window, "resize", util.saveViewportOffsetT); /** @type {number} */ util.count = 0; /** @type {number} */ util.elements.length = 0; /** @type {boolean} */ util.destroyed = true; }; util.validateT = throttle(function() { validate(scope); }, scope.options.validateDelay, scope); util.saveViewportOffsetT = throttle(function() { saveViewportOffset(scope.options.offset); }, scope.options.saveViewportOffsetDelay, scope); saveViewportOffset(scope.options.offset); each(scope.options.breakpoints, function(img) { if (img.width >= window.screen.width) { return src = img.src, false; } }); setTimeout(function() { initialize(scope); }); }; });