// BASED on this article - http://www.bennadel.com/blog/2597-preloading-images-in-angularjs-with-promises.htm // Preloading Images In AngularJS With Promises. // I provide a utility class for preloading image objects. TRMFullScreenApp.factory( "preloader", function( $q, $rootScope, $timeout ) { // I manage the preloading of image objects. Accepts an array of image URLs. function Preloader( imageLocations ) { // I am the image SRC values to preload. this.imageLocations = imageLocations; this.failedImages = []; //Doesn't get used, but nice to be able to track failed images. this.cleanedImages = []; // As the images load, we'll need to keep track of the load/error // counts when announing the progress on the loading. this.imageCount = this.imageLocations.length; this.imageHandleCount = 0; // I am the possible states that the preloader can be in. this.states = { PENDING: 1, LOADING: 2, COMPLETED: 3 }; // I keep track of the current state of the preloader. this.state = this.states.PENDING; // When loading the images, a promise will be returned to indicate // when the loading has completed (and / or progressed). this.deferred = $q.defer(); this.promise = this.deferred.promise; } // --- // STATIC METHODS. // --- // I reload the given images [Array] and return a promise. The promise // will be resolved with the array of image locations. Preloader.preloadImages = function( imageLocations ) { var preloader = new Preloader( imageLocations ); return( preloader.load() ); }; // --- // INSTANCE METHODS. // --- Preloader.prototype = { // Best practice for "instnceof" operator. constructor: Preloader, // --- // PUBLIC METHODS. // --- // I determine if the preloader has started loading images yet. isInitiated: function isInitiated() { return( this.state !== this.states.PENDING ); }, // I determine if the preloader has successfully loaded all of the images. isCompleted: function isComplete() { return( this.state === this.states.COMPLETED ); }, // I initiate the preload of the images. Returns a promise. load: function load() { // If the images are already loading, return the existing promise. if ( this.isInitiated() ) { return( this.promise ); } this.state = this.states.LOADING; for ( var i = 0 ; i < this.imageCount ; i++ ) { this.loadImageLocation( this.imageLocations[ i ] ); } // Return the deferred promise for the load event. return( this.promise ); }, // --- // PRIVATE METHODS. // --- // This is the only image handler. Each image coming in is either flagged with a true or false parameter. handleImage: function handleImage( imageLocation, exists ) { this.imageHandleCount++; //Track failed image locations. if(!exists){ this.failedImages.push(imageLocation); //console.log(this.failedImages); } // Notify the progress of the overall deferred. This is different // than Resolving the deferred - you can call notify many times // before the ultimate resolution (or rejection) of the deferred. this.deferred.notify({ percent: Math.ceil( this.imageHandleCount / this.imageCount * 100 ), imageLocation: imageLocation }); //If image exists, push src into cleanedImages[], if(exists){ this.cleanedImages.push(imageLocation); console.log("success",imageLocation); }else{ console.log("failed",imageLocation); } //if a full cycle on this.imageLocations has been achieved if ( this.imageHandleCount === this.imageCount ) { //console.log(this.cleanedImages); if ( this.failedImages.length === this.imageHandleCount ) { //console.log(this.cleanedImages); this.state = this.states.REJECTED; //return all the successful images this.deferred.reject( imageLocation ); }else{ this.state = this.states.COMPLETED; //return all the successful images this.deferred.resolve( [this.cleanedImages, this.slideTitles] ); } } }, // I load the given image location and then wire the load / error // events back into the preloader instance. // -- // NOTE: The load/error events trigger a $digest. loadImageLocation: function loadImageLocation( imageLocation ) { var preloader = this; // When it comes to creating the image object, it is critical that // we bind the event handlers BEFORE we actually set the image // source. Failure to do so will prevent the events from proper // triggering in some browsers. var image = $( new Image() ) .load( function( event ) { // Since the load event is asynchronous, we have to // tell AngularJS that something changed. $rootScope.$apply( function() { preloader.handleImage( event.target.src, true ); // Clean up object reference to help with the // garbage collection in the closure. preloader = image = event = null; } ); } ) .error( function( event ) { // Since the load event is asynchronous, we have to // tell AngularJS that something changed. $rootScope.$apply( function() { preloader.handleImage( event.target.src, false ); // Clean up object reference to help with the // garbage collection in the closure. preloader = image = event = null; } ); } ) .prop( "src", imageLocation ); } }; // Return the factory instance. return( Preloader ); } );