sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
232 lines (198 loc) • 7.53 kB
JavaScript
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Image loader utility class. Useful when an application needs
* to preload multiple images, for example so they can be sized.
*
*
*
*/
goog.provide('goog.net.ImageLoader');
goog.require('goog.dom');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.net.EventType');
goog.require('goog.object');
goog.require('goog.userAgent');
/**
* Image loader utility class. Raises a {@link goog.events.EventType.LOAD}
* event for each image loaded, with an {@link Image} object as the target of
* the event, normalized to have {@code naturalHeight} and {@code naturalWidth}
* attributes.
* @param {Element=} opt_parent An optional parent element whose document object
* should be used to load images.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.net.ImageLoader = function(opt_parent) {
goog.events.EventTarget.call(this);
this.images_ = {};
this.handler_ = new goog.events.EventHandler(this);
this.parent_ = opt_parent;
};
goog.inherits(goog.net.ImageLoader, goog.events.EventTarget);
/**
* Map of image IDs to images src, used to keep track of the images to load.
* @private
* @type {Object.<string, string>}
*/
goog.net.ImageLoader.prototype.images_;
/**
* Event handler object, used to keep track of onload and onreadystatechange
* listeners.
* @private
* @type {goog.events.EventHandler}
*/
goog.net.ImageLoader.prototype.handler_;
/**
* The parent element whose document object will be used to load images.
* Useful if you want to load the images from a window other than the current
* window in order to control the Referer header sent when the image is loaded.
* @type {(Element|undefined)}
* @private
*/
goog.net.ImageLoader.prototype.parent_;
/**
* Adds an image to the image loader, and associates it with the given ID
* string. If an image with that ID already exists, it is silently replaced.
* When the image in question is loaded, the target of the LOAD event will be
* an {@code Image} object with {@code id} and {@code src} attributes based on
* these arguments.
* @param {string} id The ID of the image to load.
* @param {string|Image} image Either the source URL of the image or the HTML
* image element itself (or any object with a {@code src} property, really).
*/
goog.net.ImageLoader.prototype.addImage = function(id, image) {
var src = goog.isString(image) ? image : image.src;
if (src) {
// For now, we just store the source URL for the image.
this.images_[id] = src;
}
};
/**
* Removes the image associated with the given ID string from the image loader.
* @param {string} id The ID of the image to remove.
*/
goog.net.ImageLoader.prototype.removeImage = function(id) {
goog.object.remove(this.images_, id);
};
/**
* Starts loading all images in the image loader in parallel. Raises a LOAD
* event each time an image finishes loading, and a COMPLETE event after all
* images have finished loading.
*/
goog.net.ImageLoader.prototype.start = function() {
goog.object.forEach(this.images_, this.loadImage_, this);
};
/**
* Creates an {@code Image} object with the specified ID and source URL, and
* listens for network events raised as the image is loaded.
* @private
* @param {string} src The image source URL.
* @param {string} id The unique ID of the image to load.
*/
goog.net.ImageLoader.prototype.loadImage_ = function(src, id) {
var image;
if (this.parent_) {
var dom = goog.dom.getDomHelper(this.parent_);
image = dom.createDom('img');
} else {
image = new Image();
}
// Internet Explorer doesn't reliably raise LOAD events on images, so we must
// use READY_STATE_CHANGE (thanks, Jeff!).
// If the image is cached locally, IE won't fire the LOAD event while the
// onreadystate event is fired always. On the other hand, the ERROR event
// is always fired whenever the image is not loaded successfully no matter
// whether it's cached or not.
var loadEvent = goog.userAgent.IE ? goog.net.EventType.READY_STATE_CHANGE :
goog.events.EventType.LOAD;
this.handler_.listen(image, [
loadEvent, goog.net.EventType.ABORT, goog.net.EventType.ERROR
], this.onNetworkEvent_);
image.id = id;
image.src = src;
};
/**
* Handles net events (READY_STATE_CHANGE, LOAD, ABORT, and ERROR).
* @private
* @param {goog.events.Event} evt The network event to handle.
*/
goog.net.ImageLoader.prototype.onNetworkEvent_ = function(evt) {
var image = evt.currentTarget;
if (!image) {
return;
}
if (evt.type == goog.net.EventType.READY_STATE_CHANGE) {
// This implies that the user agent is IE; see loadImage()_.
// Noe that this block is used to check whether the image is ready to
// dispatch the COMPLETE event.
if (image.readyState == goog.net.EventType.COMPLETE) {
// This is the IE equivalent of a LOAD event.
evt.type = goog.events.EventType.LOAD;
} else {
// This may imply that the load failed.
// Note that the image has only the following states:
// * uninitialized
// * loading
// * complete
// When the ERROR or the ABORT event is fired, the readyState
// will be either uninitialized or loading and we'd ignore those states
// since they will be handled separately (eg: evt.type = 'ERROR').
// Notes from MSDN : The states through which an object passes are
// determined by that object. An object can skip certain states
// (for example, interactive) if the state does not apply to that object.
// see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx
// The image is not loaded, ignore.
return;
}
}
// Add natural width/height properties for non-Gecko browsers.
if (typeof image.naturalWidth == 'undefined') {
if (evt.type == goog.events.EventType.LOAD) {
image.naturalWidth = image.width
image.naturalHeight = image.height;
} else {
// This implies that the image fails to be loaded.
image.naturalWidth = 0;
image.naturalHeight = 0;
}
}
// Redispatch the event on behalf of the image.
this.dispatchEvent({type: evt.type, target: image});
// Remove the image from the map.
goog.object.remove(this.images_, image.id);
// If this was the last image, raise a COMPLETE event.
if (goog.object.isEmpty(this.images_)) {
this.dispatchEvent(goog.net.EventType.COMPLETE);
// Unlisten for all network events.
if (this.handler_) {
this.handler_.removeAll();
}
}
};
/**
* Disposes of the image loader.
*/
goog.net.ImageLoader.prototype.disposeInternal = function() {
if (this.images_) {
delete this.images_;
}
if (this.handler_) {
this.handler_.dispose();
this.handler_ = null;
}
goog.net.ImageLoader.superClass_.disposeInternal.call(this);
};