diff --git a/frontend/drupal/sites/all/libraries/blazy/blazy.js b/frontend/drupal/sites/all/libraries/blazy/blazy.js new file mode 100644 index 000000000..00af78dcc --- /dev/null +++ b/frontend/drupal/sites/all/libraries/blazy/blazy.js @@ -0,0 +1,370 @@ +/*! + 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 +*/ +; +(function(root, blazy) { + if (typeof define === 'function' && define.amd) { + // AMD. Register bLazy as an anonymous module + define(blazy); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = blazy(); + } else { + // Browser globals. Register bLazy on window + root.Blazy = blazy(); + } +})(this, function() { + 'use strict'; + + //private vars + var _source, _viewport, _isRetina, _supportClosest, _attrSrc = 'src', _attrSrcset = 'srcset'; + + // constructor + return function Blazy(options) { + //IE7- fallback for missing querySelectorAll support + if (!document.querySelectorAll) { + var s = document.createStyleSheet(); + document.querySelectorAll = function(r, c, i, j, a) { + a = document.all, c = [], r = r.replace(/\[for\b/gi, '[htmlFor').split(','); + for (i = r.length; i--;) { + s.addRule(r[i], 'k:v'); + for (j = a.length; j--;) a[j].currentStyle.k && c.push(a[j]); + s.removeRule(0); + } + return c; + }; + } + + //options and helper vars + var scope = this; + var util = scope._util = {}; + util.elements = []; + 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 = _source = scope.options.src || 'data-src'; + _supportClosest = Element.prototype.closest; + _isRetina = window.devicePixelRatio > 1; + _viewport = {}; + _viewport.top = 0 - scope.options.offset; + _viewport.left = 0 - scope.options.offset; + + + /* public functions + ************************************/ + scope.revalidate = function() { + initialize(scope); + }; + scope.load = function(elements, force) { + var opt = this.options; + if (elements && elements.length === undefined) { + loadElement(elements, force, opt); + } else { + each(elements, function(element) { + loadElement(element, force, opt); + }); + } + }; + scope.destroy = function() { + var util = scope._util; + if (scope.options.container) { + each(scope.options.container, function(object) { + unbindEvent(object, 'scroll', util.validateT); + }); + } + unbindEvent(window, 'scroll', util.validateT); + unbindEvent(window, 'resize', util.validateT); + unbindEvent(window, 'resize', util.saveViewportOffsetT); + util.count = 0; + util.elements.length = 0; + util.destroyed = true; + }; + + //throttle, ensures that we don't call the functions too often + 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); + + //handle multi-served image src (obsolete) + each(scope.options.breakpoints, function(object) { + if (object.width >= window.screen.width) { + _source = object.src; + return false; + } + }); + + // start lazy load + setTimeout(function() { + initialize(scope); + }); // "dom ready" fix + + }; + + + /* Private helper functions + ************************************/ + function initialize(self) { + var util = self._util; + // First we create an array of elements to lazy load + util.elements = toArray(self.options); + util.count = util.elements.length; + // Then we bind resize and scroll events if not already binded + if (util.destroyed) { + util.destroyed = false; + if (self.options.container) { + each(self.options.container, function(object) { + bindEvent(object, 'scroll', util.validateT); + }); + } + bindEvent(window, 'resize', util.saveViewportOffsetT); + bindEvent(window, 'resize', util.validateT); + bindEvent(window, 'scroll', util.validateT); + } + // And finally, we start to lazy load. + validate(self); + } + + function validate(self) { + var util = self._util; + for (var i = 0; i < util.count; i++) { + var element = util.elements[i]; + if (elementInView(element, self.options) || hasClass(element, self.options.successClass)) { + self.load(element); + util.elements.splice(i, 1); + util.count--; + i--; + } + } + if (util.count === 0) { + self.destroy(); + } + } + + function elementInView(ele, options) { + var rect = ele.getBoundingClientRect(); + + if(options.container && _supportClosest){ + // Is element inside a container? + var elementContainer = ele.closest(options.containerClass); + if(elementContainer){ + var containerRect = elementContainer.getBoundingClientRect(); + // Is container in view? + if(inView(containerRect, _viewport)){ + var top = containerRect.top - options.offset; + var right = containerRect.right + options.offset; + var bottom = containerRect.bottom + options.offset; + var left = containerRect.left - options.offset; + var containerRectWithOffset = { + top: top > _viewport.top ? top : _viewport.top, + right: right < _viewport.right ? right : _viewport.right, + bottom: bottom < _viewport.bottom ? bottom : _viewport.bottom, + left: left > _viewport.left ? left : _viewport.left + }; + // Is element in view of container? + return inView(rect, containerRectWithOffset); + } else { + return false; + } + } + } + return inView(rect, _viewport); + } + + function inView(rect, viewport){ + // Intersection + return rect.right >= viewport.left && + rect.bottom >= viewport.top && + rect.left <= viewport.right && + rect.top <= viewport.bottom; + } + + function loadElement(ele, force, options) { + // if element is visible, not loaded or forced + if (!hasClass(ele, options.successClass) && (force || options.loadInvisible || (ele.offsetWidth > 0 && ele.offsetHeight > 0))) { + var dataSrc = getAttr(ele, _source) || getAttr(ele, options.src); // fallback to default 'data-src' + if (dataSrc) { + var dataSrcSplitted = dataSrc.split(options.separator); + var src = dataSrcSplitted[_isRetina && dataSrcSplitted.length > 1 ? 1 : 0]; + var srcset = getAttr(ele, options.srcset); + var isImage = equal(ele, 'img'); + var parent = ele.parentNode; + var isPicture = parent && equal(parent, 'picture'); + // Image or background image + if (isImage || ele.src === undefined) { + var img = new Image(); + // using EventListener instead of onerror and onload + // due to bug introduced in chrome v50 + // (https://productforums.google.com/forum/#!topic/chrome/p51Lk7vnP2o) + var onErrorHandler = function() { + if (options.error) options.error(ele, "invalid"); + addClass(ele, options.errorClass); + unbindEvent(img, 'error', onErrorHandler); + unbindEvent(img, 'load', onLoadHandler); + }; + var onLoadHandler = function() { + // Is element an image + if (isImage) { + if(!isPicture) { + handleSources(ele, src, srcset); + } + // or background-image + } else { + ele.style.backgroundImage = 'url("' + src + '")'; + } + itemLoaded(ele, options); + unbindEvent(img, 'load', onLoadHandler); + unbindEvent(img, 'error', onErrorHandler); + }; + + // Picture element + if (isPicture) { + img = ele; // Image tag inside picture element wont get preloaded + each(parent.getElementsByTagName('source'), function(source) { + handleSource(source, _attrSrcset, options.srcset); + }); + } + bindEvent(img, 'error', onErrorHandler); + bindEvent(img, 'load', onLoadHandler); + handleSources(img, src, srcset); // Preload + + } else { // An item with src like iframe, unity games, simpel video etc + ele.src = src; + itemLoaded(ele, options); + } + } else { + // video with child source + if (equal(ele, 'video')) { + each(ele.getElementsByTagName('source'), function(source) { + handleSource(source, _attrSrc, options.src); + }); + ele.load(); + itemLoaded(ele, options); + } else { + if (options.error) options.error(ele, "missing"); + addClass(ele, options.errorClass); + } + } + } + } + + function itemLoaded(ele, options) { + addClass(ele, options.successClass); + if (options.success) options.success(ele); + // cleanup markup, remove data source attributes + removeAttr(ele, options.src); + removeAttr(ele, options.srcset); + each(options.breakpoints, function(object) { + removeAttr(ele, object.src); + }); + } + + function handleSource(ele, attr, dataAttr) { + var dataSrc = getAttr(ele, dataAttr); + if (dataSrc) { + setAttr(ele, attr, dataSrc); + removeAttr(ele, dataAttr); + } + } + + function handleSources(ele, src, srcset){ + if(srcset) { + setAttr(ele, _attrSrcset, srcset); //srcset + } + ele.src = src; //src + } + + function setAttr(ele, attr, value){ + ele.setAttribute(attr, value); + } + + function getAttr(ele, attr) { + return ele.getAttribute(attr); + } + + function removeAttr(ele, attr){ + ele.removeAttribute(attr); + } + + function equal(ele, str) { + return ele.nodeName.toLowerCase() === str; + } + + function hasClass(ele, className) { + return (' ' + ele.className + ' ').indexOf(' ' + className + ' ') !== -1; + } + + function addClass(ele, className) { + if (!hasClass(ele, className)) { + ele.className += ' ' + className; + } + } + + function toArray(options) { + var array = []; + var nodelist = (options.root).querySelectorAll(options.selector); + for (var i = nodelist.length; i--; array.unshift(nodelist[i])) {} + return array; + } + + function saveViewportOffset(offset) { + _viewport.bottom = (window.innerHeight || document.documentElement.clientHeight) + offset; + _viewport.right = (window.innerWidth || document.documentElement.clientWidth) + offset; + } + + function bindEvent(ele, type, fn) { + if (ele.attachEvent) { + ele.attachEvent && ele.attachEvent('on' + type, fn); + } else { + ele.addEventListener(type, fn, { capture: false, passive: true }); + } + } + + function unbindEvent(ele, type, fn) { + if (ele.detachEvent) { + ele.detachEvent && ele.detachEvent('on' + type, fn); + } else { + ele.removeEventListener(type, fn, { capture: false, passive: true }); + } + } + + function each(object, fn) { + if (object && fn) { + var l = object.length; + for (var i = 0; i < l && fn(object[i], i) !== false; i++) {} + } + } + + function throttle(fn, minDelay, scope) { + var lastCall = 0; + return function() { + var now = +new Date(); + if (now - lastCall < minDelay) { + return; + } + lastCall = now; + fn.apply(scope, arguments); + }; + } +}); diff --git a/frontend/drupal/sites/all/libraries/blazy/blazy.min.js b/frontend/drupal/sites/all/libraries/blazy/blazy.min.js index 3957cac02..649abfd84 100644 --- a/frontend/drupal/sites/all/libraries/blazy/blazy.min.js +++ b/frontend/drupal/sites/all/libraries/blazy/blazy.min.js @@ -3,4 +3,4 @@ A fast, small and dependency free lazy load script (https://github.com/dinbror/blazy) (c) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy */ - (function(q,m){"function"===typeof define&&define.amd?define(m):"object"===typeof exports?module.exports=m():q.Blazy=m()})(this,function(){function q(b){var c=b._util;c.elements=E(b.options);c.count=c.elements.length;c.destroyed&&(c.destroyed=!1,b.options.container&&l(b.options.container,function(a){n(a,"scroll",c.validateT)}),n(window,"resize",c.saveViewportOffsetT),n(window,"resize",c.validateT),n(window,"scroll",c.validateT));m(b)}function m(b){for(var c=b._util,a=0;a=c.left&&b.bottom>=c.top&&b.left<=c.right&&b.top<=c.bottom}function z(b,c,a){if(!t(b,a.successClass)&&(c||a.loadInvisible||0=window.screen.width)return u=a.src,!1});setTimeout(function(){q(a)})}}); + (function(q,m){"function"===typeof define&&define.amd?define(m):"object"===typeof exports?module.exports=m():q.Blazy=m()})(this,function(){function q(b){var c=b._util;c.elements=E(b.options);c.count=c.elements.length;c.destroyed&&(c.destroyed=!1,b.options.container&&l(b.options.container,function(a){n(a,"scroll",c.validateT)}),n(window,"resize",c.saveViewportOffsetT),n(window,"resize",c.validateT),n(window,"scroll",c.validateT));m(b)}function m(b){for(var c=b._util,a=0;a=c.left&&b.bottom>=c.top&&b.left<=c.right&&b.top<=c.bottom}function z(b,c,a){if(!t(b,a.successClass)&&(c||a.loadInvisible||0=window.screen.width)return u=a.src,!1});setTimeout(function(){q(a)})}}); \ No newline at end of file diff --git a/frontend/drupal/sites/all/libraries/blazy/polyfills/closest.js b/frontend/drupal/sites/all/libraries/blazy/polyfills/closest.js new file mode 100644 index 000000000..83c8f5612 --- /dev/null +++ b/frontend/drupal/sites/all/libraries/blazy/polyfills/closest.js @@ -0,0 +1,29 @@ +// Polyfill for Element.closest that falls back to Element.matches that falls back to querySelectorAll +// Created for blazy.js 1.8.1 - https://github.com/dinbror/blazy to ensure IE7+ support + + +(function () { + if (!Element.prototype.matches) { + Element.prototype.matches = + Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector || + function(s) { + var matches = (this.document || this.ownerDocument).querySelectorAll(s), + i = matches.length; + while (--i >= 0 && matches.item(i) !== this) {} + return i > -1; + }; + } + + if (!Element.prototype.closest) { + Element.prototype.closest = Element.prototype.closest || + function(selector) { + var element = this; + while (element.matches && !element.matches(selector)) element = element.parentNode; + return element.matches ? element : null; + }; + } +})(); \ No newline at end of file