towry
5/24/2016 - 2:01 AM

lazy load images.

lazy load images.


/**
 * Include this script in the page where you want to use lazy load.
 * to make a div or image lazy load of image, this is an example: 
 * 	
 * HTML:
 *	<span class='lazy' data-image="http://placehold.it/300x300"></span>
 *
 * OR:
 *	<img class='lazy' data-image="http://placehold.it/300x300" />
 *
 * To use lazy load in loading, trigger the `lazyload:restart` event
 * on document.
 *
 * To uninstall the lazy load, trigger the `lazyload:teardown` event
 * on document.
 */

var LazyLoad = require('./lib/lazy-load');

(function ($) {

	var instance = new LazyLoad();	

	// setup after dom is ready.
	$(document).ready(function () {
		window.setTimeout(function () {
			instance.setup();
		}, 100);
	});

	$(document).on('lazyload:teardown', function () {
		instance.teardown();
	});

	$(document).on('lazyload:restart', function () {
		instance.restart();
	});

	window.lazy = instance;

}.call(this, window.$ || window.jQuery));
/*!
 * Copyright 2016 MoSeeker, Inc.
 * @author Towry Wang 
 */

var verge = require('verge');
var classes = require('dom-classes');
var objectAssign = require('object-assign');

function LazyLoad(options) {
	var _options = options || {};

	if (Object.prototype.toString.call(_options) !== '[object Object]') {
		throw new TypeError("LazyLoad requires an object as options.");
	}

	this.options = objectAssign({
		lazyClass: 'lazy',
		ignoreFail: true,
		taste: true,
		within: 50
	}, _options);

	this._setup = false;
	this._timer = null;
	this._images = null;
	this._inProcess = false;
}
module.exports = LazyLoad;

LazyLoad.prototype.setup = function () {
	if (this._setup) return;

	this._setup = true;

	this._onResizeHandler = this._onResizeHandler.bind(this);
	this._onScrollHandler = this._onScrollHandler.bind(this);
	this._onTouchEndHandler = this._onTouchEndHandler.bind(this);
	this._queryLazies();

	this.bindEvent();

	if (this.options.taste) {
		this.taste();
	}
}

LazyLoad.prototype.taste = function () {
	if (this._timer) return;

	var self = this;

	this._inProcess = true;
	this._timer = window.setTimeout(function () {
		self.consumeImages();
	}, 0);
}

LazyLoad.prototype.restart = function () {
	this.teardown();
	this.setup();
}

LazyLoad.prototype.teardown = function () {
	if (!this._setup) return;

	this.unbindEvent();
	if (this._timer) {
		window.clearTimeout(this._timer);
		this._timer = null;
	}

	this._images = null;
	this._setup = false;
	this._inProcess = false;
}

LazyLoad.prototype.bindEvent = function () {
	if (!this._setup) return;

	addEvent(window, this._onScrollHandler);
	addEvent(window, 'resize', this._onResizeHandler);
	addEvent(window, 'touchend', this._onTouchEndHandler);
}

LazyLoad.prototype.unbindEvent = function () {
	if (!this._setup) return;
	removeEvent(window, this._onScrollHandler);
	removeEvent(window, 'resize', this._onResizeHandler);
	removeEvent(window, 'touchend', this._onTouchEndHandler);
}

LazyLoad.prototype._queryLazies = function () {
	this._images = [];
	if (document.querySelectorAll) {
		var images = document.querySelectorAll('.' + this.options.lazyClass);
	} else {
		images = document.getElementsByClassName(this.options.lazyClass);
	}

	if (!images) {
		this._images = null;
		return;
	}

	this._images = Array.prototype.slice.call(images);
}

LazyLoad.prototype._onTouchEndHandler = function () {
	window.setTimeout(function () {
		this.consumeImages();
	}.bind(this), 400);
}

LazyLoad.prototype._onResizeHandler = function () {
	this.restart();
}

LazyLoad.prototype._onScrollHandler = function () {
	if (this._timer) {
		window.clearTimeout(this._timer);
		this._timer = null;
		this._inProcess = false;
	}

	var self = this;

	this._inProcess = true;

	this._timer = window.setTimeout(function () {
		self.consumeImages();
	}, 10);
}

LazyLoad.prototype.consumeImages = function () {
	if (!this._images || !this._images.length) {
		this.teardown();
		return;
	}

	if (!this._inProcess) {
		return;
	}

	var left = [];
	var i = 0;
	var image;

	for (i; i < this._images.length; i++) {
		if (!this._inProcess) {
			this._images = i === 0 ? left.concat(this._images) : left.concat(this._images.slice(i-1));
			return;
		}

		image = this._images[i];
		
		if (!image) continue;
		if (!verge.inViewport(image, this.options.within) || isNaN(verge.aspect(image))) {
			left.push(image);
			continue;
		} else {
			console.log(image);
		}

		loadImage(image, function (image) {
			return function () {
				classes.remove(image, this.options.lazyClass);
			}.bind(this);
		}.call(this, image), this.options.ignoreFail ? null : function () {
			this._images.push(image);
		}.bind(this));
	} 

	if (i === this._images.length) {
		this._images = left;
		return;
	}

	this._images = i === 0 ? left.concat(this._images) : left.concat(this._images.slice(i-1));
}

function loadImage(ele, sucessFn, failFn) {
	var img = new Image();
	var src = ele.getAttribute('data-image');
	if (!src) return;

	img.onload = function () {
		if (!ele.tagName) return;

		if (ele.tagName.toLowerCase() === 'img') {
			ele.src = src;
		} else {
			ele.style.backgroundImage = 'url(' + src + ')';
		}

		classes.remove(ele, 'lazy-loading');
		classes.add(ele, 'lazy-loaded');

		sucessFn ? sucessFn() : null;
	}

	img.onerror = function () {
		classes.remove(ele, 'lazy-loading');
		classes.add(ele, 'lazy-error');
		failFn ? failFn() : null;
	}

	classes.remove(ele, 'lazy-loaded');
	classes.add(ele, 'lazy-loading');

	img.src = src;
}


function addEvent(el, name, listener) {
	if (!listener) {
		listener = name;
		name = 'scroll';
	}

	// if (name === 'scroll' && 'ontouchmove' in window) {
	// 	name = 'touchmove';
	// }

	if (window.addEventListener) {
		window.addEventListener(name, listener);
	} else {
		window.attachEvent('on' + name, listener);
	}
}

function removeEvent(el, listener) {
	if (!listener) {
		listener = name;
		name = 'scroll';
	}

	// if (name === 'scroll' && 'ontouchmove' in window) {
	// 	name = 'touchmove';
	// }

	if (window.removeEventListener) {
		window.removeEventListener(name, listener);
	} else {
		window.detachEvent(name, listener);
	}
}