jquery-prefetch
Version:
Prefetch is a lightweight image, list and background preloader for jQuery. Just give them a list of images or elements and the plugin will load every image for you.
358 lines (311 loc) • 10.6 kB
JavaScript
/*!
* jQuery Prefetch - v0.1.3
* http://jquery.eisbehr.de/prefetch/
* http://eisbehr.de
*
* Copyright 2015, Daniel 'Eisbehr' Kern
*
* Dual licensed under the MIT and GPL-2.0 licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
*
* jQuery.prefetch(["image1.jpg", "image2.jpg", ...], {delay: 1000});
* jQuery("img").prefetch();
*/
;(function($, undefined)
{
"use strict";
/**
* makes the plugin available direct trough jquery
* @param {Array|object|string} [imageList]
* @param {object} [settings]
* @returns {Plugin}
*/
$.Prefetch = $.prefetch = function(imageList, settings)
{
return new Plugin(imageList, settings);
};
/**
* makes the plugin available trough jquery selector
* @param {object} [settings]
* @returns {object|Plugin}
*/
$.fn.Prefetch = $.fn.prefetch = function(settings)
{
var instance = $.prefetch(this, settings);
return instance.configuration.chainable ? this : instance;
};
/**
* plugin class constructor
* @constructor
* @access private
* @param {Array|object} imageList
* @param {object} settings
* @returns {Plugin}
*/
function Plugin(imageList, settings)
{
if( settings ) $.extend(this.configuration, settings);
this.addImage(imageList);
return this._initialize();
}
$.extend(Plugin.prototype,
{
/**
* all given items by jquery selector
* @access private
* @type {Array}
*/
_images: [],
/**
* determine if preload is actually running
* @access private
* @type {boolean}
*/
_running: false,
/**
* amount of currently running loadings
* @access private
* @type {Number}
*/
_runningAmount: 0,
/**
* settings and configuration data
* @access public
* @type {object}
*/
configuration:
{
chainable: true,
// loading
startAutomatically: true,
delay: 0,
simultaneous: 3,
objectProperty: "image",
imagesBasePath: null,
// callbacks
onStartLoading: null,
getImageSource: function(source, prefetch)
{
if( prefetch.configuration.imagesBasePath )
return prefetch.configuration.imagesBasePath + source;
return source;
},
onImageLoaded: null,
onImageError: null,
onAbortLoading: null,
onAllLoaded: null
},
/**
* initialize function
* @access private
* @type {function}
* @returns {Plugin}
*/
_initialize: function()
{
var plugin = this;
if( this._images.length > 0 && this.configuration.startAutomatically )
setTimeout(function()
{
plugin.start();
}, this.configuration.delay);
return this;
},
/**
* check for next image to load in list and start
* @access private
* @type {function}
* @return void
*/
_checkForNextImage: function()
{
// stop if not running or limit reached
if( !this._running || (this.configuration.simultaneous > 0 && this._runningAmount >= this.configuration.simultaneous) ) return;
// load next free and waiting image
var foundFreeImage = false;
for( var i = 0; i < this._images.length; ++i )
if( !this._images[i].running && !this._images[i].loaded )
{
foundFreeImage = true;
this._preloadImage(this._images[i]);
break;
}
// finished everything
if( !foundFreeImage && this.getLoadedAmount() == this.getImagesAmount() )
{
this._running = false;
this._triggerCallback(this.configuration.onAllLoaded);
}
},
/**
* preload a single image object
* @access private
* @type {function}
* @param {object} imageObject
* @return void
*/
_preloadImage: function(imageObject)
{
// start loading
imageObject.running = true;
++this._runningAmount;
var plugin = this,
imagePreload = $(new Image());
// bind error callback
imagePreload.error(function()
{
plugin._finishImage(imageObject, false);
});
// bind loaded event
imagePreload.one("load", function()
{
// unbind event and remove image object
imagePreload.unbind("load").remove();
plugin._finishImage(imageObject, true);
});
// set source and start preload
imagePreload.attr("src", this._triggerCallback(this.configuration.getImageSource, imageObject.file));
// call after load even on cached image
if( imagePreload.complete ) imagePreload.load();
},
/**
* finish image loading callback
* @access private
* @type {function}
* @param {object} imageObject
* @param {boolean} loaded
* @return void
*/
_finishImage: function(imageObject, loaded)
{
imageObject.loaded = true;
imageObject.running = false;
--this._runningAmount;
if( loaded ) this._triggerCallback(this.configuration.onImageLoaded, imageObject.file);
else this._triggerCallback(this.configuration.onImageError, imageObject.file);
this._checkForNextImage();
},
/**
* trigger a callback and pass optional image parameters
* @access private
* @type {function}
* @param {function} callback
* @param {string} [image]
* @return {*}
*/
_triggerCallback: function(callback, image)
{
if( callback )
{
if( image )
return callback(image, this);
else
return callback(this);
}
return null;
},
/**
* start preloading images
* @access public
* @type {function}
* @return {Plugin}
*/
start: function()
{
if( !this._running && this.getLoadedAmount() < this.getImagesAmount() )
{
this._running = true;
this._triggerCallback(this.configuration.onStartLoading);
for( var i = this._runningAmount; (this.configuration.simultaneous <= 0 || i < this.configuration.simultaneous) && i < this._images.length; i++ )
this._checkForNextImage();
}
return this;
},
/**
* stop current preloading
* @access public
* @type {function}
* @return {Plugin}
*/
stop: function()
{
if( this._running )
{
this._running = false;
this._triggerCallback(this.configuration.onAbortLoading);
}
return this;
},
/**
* get the amount of images overall, loaded or waiting
* @access public
* @type {function}
* @returns {Number}
*/
getImagesAmount: function()
{
return this._images.length;
},
/**
* get the amount of loaded images
* @access public
* @type {function}
* @returns {Number}
*/
getLoadedAmount: function()
{
var loaded = 0;
for( var i = 0; i < this._images.length; ++i )
if( i in this._images && this._images[i]["loaded"] )
++loaded;
return loaded;
},
/**
* add a list of elements, images or a single item to preloader
* @access public
* @type {function}
* @param {Array|object|string} image
* @return {Plugin}
*/
addImage: function(image)
{
var imageObject = {file: "", running: false, loaded: false},
inputType = Object.prototype.toString.call(image);
// get images from array
if( inputType == "[object Array]" )
{
for( var i = 0; i < image.length; ++i )
if( i in image )
this._images.push(jQuery.extend({}, imageObject, {file: image[i][this.configuration.objectProperty] || image[i]}));
}
// get images from object or jquery selector
else if( inputType == "[object Object]" )
{
var plugin = this;
$.each(image, function()
{
var element = $(this);
if( this.hasOwnProperty(plugin.configuration.objectProperty) || element.attr("src") || element.css("background-image") )
plugin._images.push(jQuery.extend({}, imageObject, {file: this[plugin.configuration.objectProperty] || element.attr("src") || element.css("background-image").replace(/^url\(["']?/, '').replace(/["']?\)$/, '')}));
});
}
// get single image from string
else if( inputType == "[object String]" )
this._images.push(jQuery.extend({}, imageObject, {file: image}));
return this;
},
/**
* destroy this plugin instance
* @access public
* @type {function}
* @returns {Plugin}
*/
destroy: function ()
{
this.stop();
this._images = [];
return this;
}
});
})(jQuery);