]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - inc/blazy-1.3.1.js
Merge pull request #1110 from virtualtam/doc/v0.9.6
[github/shaarli/Shaarli.git] / inc / blazy-1.3.1.js
CommitLineData
34047d23
A
1/*!
2 hey, [be]Lazy.js - v1.3.1 - 2015.02.01
3 A lazy loading and multi-serving image script
4 (c) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
5*/
6;(function(root, blazy) {
7 if (typeof define === 'function' && define.amd) {
8 // AMD. Register bLazy as an anonymous module
9 define(blazy);
10 } else if (typeof exports === 'object') {
11 // Node. Does not work with strict CommonJS, but
12 // only CommonJS-like environments that support module.exports,
13 // like Node.
14 module.exports = blazy();
15 } else {
16 // Browser globals. Register bLazy on window
17 root.Blazy = blazy();
18 }
19})(this, function () {
20 'use strict';
21
22 //vars
23 var source, options, viewport, images, count, isRetina, destroyed;
24 //throttle vars
25 var validateT, saveViewportOffsetT;
26
27 // constructor
28 function Blazy(settings) {
29 //IE7- fallback for missing querySelectorAll support
30 if (!document.querySelectorAll) {
31 var s=document.createStyleSheet();
32 document.querySelectorAll = function(r, c, i, j, a) {
33 a=document.all, c=[], r = r.replace(/\[for\b/gi, '[htmlFor').split(',');
34 for (i=r.length; i--;) {
35 s.addRule(r[i], 'k:v');
36 for (j=a.length; j--;) a[j].currentStyle.k && c.push(a[j]);
37 s.removeRule(0);
38 }
39 return c;
40 };
41 }
42 //init vars
43 destroyed = true;
44 images = [];
45 viewport = {};
46 //options
47 options = settings || {};
48 options.error = options.error || false;
49 options.offset = options.offset || 100;
50 options.success = options.success || false;
51 options.selector = options.selector || '.b-lazy';
52 options.separator = options.separator || '|';
53 options.container = options.container ? document.querySelectorAll(options.container) : false;
54 options.errorClass = options.errorClass || 'b-error';
55 options.breakpoints = options.breakpoints || false;
56 options.successClass = options.successClass || 'b-loaded';
57 options.src = source = options.src || 'data-src';
58 isRetina = window.devicePixelRatio > 1;
59 viewport.top = 0 - options.offset;
60 viewport.left = 0 - options.offset;
61 //throttle, ensures that we don't call the functions too often
62 validateT = throttle(validate, 25);
63 saveViewportOffsetT = throttle(saveViewportOffset, 50);
64
65 saveViewportOffset();
66
67 //handle multi-served image src
68 each(options.breakpoints, function(object){
69 if(object.width >= window.screen.width) {
70 source = object.src;
71 return false;
72 }
73 });
74
75 // start lazy load
76 initialize();
77 }
78
79 /* public functions
80 ************************************/
81 Blazy.prototype.revalidate = function() {
82 initialize();
83 };
84 Blazy.prototype.load = function(element, force){
85 if(!isElementLoaded(element)) loadImage(element, force);
86 };
87 Blazy.prototype.destroy = function(){
88 if(options.container){
89 each(options.container, function(object){
90 unbindEvent(object, 'scroll', validateT);
91 });
92 }
93 unbindEvent(window, 'scroll', validateT);
94 unbindEvent(window, 'resize', validateT);
95 unbindEvent(window, 'resize', saveViewportOffsetT);
96 count = 0;
97 images.length = 0;
98 destroyed = true;
99 };
100
101 /* private helper functions
102 ************************************/
103 function initialize(){
104 // First we create an array of images to lazy load
105 createImageArray(options.selector);
106 // Then we bind resize and scroll events if not already binded
107 if(destroyed) {
108 destroyed = false;
109 if(options.container) {
110 each(options.container, function(object){
111 bindEvent(object, 'scroll', validateT);
112 });
113 }
114 bindEvent(window, 'resize', saveViewportOffsetT);
115 bindEvent(window, 'resize', validateT);
116 bindEvent(window, 'scroll', validateT);
117 }
118 // And finally, we start to lazy load. Should bLazy ensure domready?
119 validate();
120 }
121
122 function validate() {
123 for(var i = 0; i<count; i++){
124 var image = images[i];
125 if(elementInView(image) || isElementLoaded(image)) {
126 Blazy.prototype.load(image);
127 images.splice(i, 1);
128 count--;
129 i--;
130 }
131 }
132 if(count === 0) {
133 Blazy.prototype.destroy();
134 }
135 }
136
137 function loadImage(ele, force){
138 // if element is visible
139 if(force || (ele.offsetWidth > 0 && ele.offsetHeight > 0)) {
140 var dataSrc = ele.getAttribute(source) || ele.getAttribute(options.src); // fallback to default data-src
141 if(dataSrc) {
142 var dataSrcSplitted = dataSrc.split(options.separator);
143 var src = dataSrcSplitted[isRetina && dataSrcSplitted.length > 1 ? 1 : 0];
144 var img = new Image();
145 // cleanup markup, remove data source attributes
146 each(options.breakpoints, function(object){
147 ele.removeAttribute(object.src);
148 });
149 ele.removeAttribute(options.src);
150 img.onerror = function() {
151 if(options.error) options.error(ele, "invalid");
152 ele.className = ele.className + ' ' + options.errorClass;
153 };
154 img.onload = function() {
155 // Is element an image or should we add the src as a background image?
156 ele.nodeName.toLowerCase() === 'img' ? ele.src = src : ele.style.backgroundImage = 'url("' + src + '")';
157 ele.className = ele.className + ' ' + options.successClass;
158 if(options.success) options.success(ele);
159 };
160 img.src = src; //preload image
161 } else {
162 if(options.error) options.error(ele, "missing");
163 ele.className = ele.className + ' ' + options.errorClass;
164 }
165 }
166 }
167
168 function elementInView(ele) {
169 var rect = ele.getBoundingClientRect();
170
171 return (
172 // Intersection
173 rect.right >= viewport.left
174 && rect.bottom >= viewport.top
175 && rect.left <= viewport.right
176 && rect.top <= viewport.bottom
177 );
178 }
179
180 function isElementLoaded(ele) {
181 return (' ' + ele.className + ' ').indexOf(' ' + options.successClass + ' ') !== -1;
182 }
183
184 function createImageArray(selector) {
185 var nodelist = document.querySelectorAll(selector);
186 count = nodelist.length;
187 //converting nodelist to array
188 for(var i = count; i--; images.unshift(nodelist[i])){}
189 }
190
191 function saveViewportOffset(){
192 viewport.bottom = (window.innerHeight || document.documentElement.clientHeight) + options.offset;
193 viewport.right = (window.innerWidth || document.documentElement.clientWidth) + options.offset;
194 }
195
196 function bindEvent(ele, type, fn) {
197 if (ele.attachEvent) {
198 ele.attachEvent && ele.attachEvent('on' + type, fn);
199 } else {
200 ele.addEventListener(type, fn, false);
201 }
202 }
203
204 function unbindEvent(ele, type, fn) {
205 if (ele.detachEvent) {
206 ele.detachEvent && ele.detachEvent('on' + type, fn);
207 } else {
208 ele.removeEventListener(type, fn, false);
209 }
210 }
211
212 function each(object, fn){
213 if(object && fn) {
214 var l = object.length;
215 for(var i = 0; i<l && fn(object[i], i) !== false; i++){}
216 }
217 }
218
219 function throttle(fn, minDelay) {
220 var lastCall = 0;
221 return function() {
222 var now = +new Date();
223 if (now - lastCall < minDelay) {
224 return;
225 }
226 lastCall = now;
227 fn.apply(images, arguments);
228 };
229 }
230
231 return Blazy;
232});