1 /*
  2 SpiderGL Computer Graphics Library
  3 Copyright (c) 2010, Marco Di Benedetto - Visual Computing Lab, ISTI - CNR
  4 All rights reserved.
  5 
  6 Redistribution and use in source and binary forms, with or without
  7 modification, are permitted provided that the following conditions are met:
  8     * Redistributions of source code must retain the above copyright
  9       notice, this list of conditions and the following disclaimer.
 10     * Redistributions in binary form must reproduce the above copyright
 11       notice, this list of conditions and the following disclaimer in the
 12       documentation and/or other materials provided with the distribution.
 13     * Neither the name of SpiderGL nor the
 14       names of its contributors may be used to endorse or promote products
 15       derived from this software without specific prior written permission.
 16 
 17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 20 DISCLAIMED. IN NO EVENT SHALL PAUL BRUNT BE LIABLE FOR ANY
 21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27 */
 28 
 29 /**
 30  * @fileOverview IO
 31  */
 32 
 33 /**
 34  * The SpiderGL.IO namespace.
 35  *
 36  * @namespace The SpiderGL.IO namespace.
 37  */
 38 SpiderGL.IO = { };
 39 
 40 /**
 41  * Creates a SpiderGL.IO.Request.
 42  *
 43  * SpiderGL.IO.Request is the base class for I/O requests.
 44  *
 45  * @class The SpiderGL.IO.Request is the base class for I/O requests.
 46  *
 47  * @augments SpiderGL.Core.ObjectBase
 48  */
 49 SpiderGL.IO.Request = function (url, options) {
 50 	SpiderGL.Core.ObjectBase.call(this);
 51 
 52 	options = SpiderGL.Utility.getDefaultObject({
 53 		async      : SpiderGL.IO.Request.DEFAULT_ASYNC,
 54 		send       : SpiderGL.IO.Request.DEFAULT_SEND,
 55 		onProgress : null,
 56 		onCancel   : null,
 57 		onError    : null,
 58 		onSuccess  : null,
 59 		onFinish   : null
 60 	}, options);
 61 
 62 	this._url     = url;
 63 	this._async   = options.async;
 64 	this._status  = SpiderGL.IO.Request.NONE;
 65 	this._sent    = false;
 66 	this._aborted = false;
 67 	this._data    = null;
 68 	this._loaded  = 0;
 69 	this._total   = 0;
 70 
 71 	this._events = {
 72 		progress : { main : null, listeners : [ ] },
 73 		cancel   : { main : null, listeners : [ ] },
 74 		error    : { main : null, listeners : [ ] },
 75 		success  : { main : null, listeners : [ ] },
 76 		finish   : { main : null, listeners : [ ] }
 77 	};
 78 
 79 	this.onProgress = options.onProgress;
 80 	this.onCancel   = options.onCancel;
 81 	this.onError    = options.onError;
 82 	this.onSuccess  = options.onSuccess;
 83 	this.onFinish   = options.onFinish;
 84 };
 85 
 86 SpiderGL.IO.Request.NONE      = 0;
 87 SpiderGL.IO.Request.ONGOING   = 1;
 88 SpiderGL.IO.Request.CANCELLED = 2;
 89 SpiderGL.IO.Request.FAILED    = 3;
 90 SpiderGL.IO.Request.SUCCEEDED = 4;
 91 
 92 SpiderGL.IO.Request.DEFAULT_ASYNC = true;
 93 SpiderGL.IO.Request.DEFAULT_SEND  = true;
 94 
 95 SpiderGL.IO.Request.prototype = {
 96 	_indexOf : function (handlers, h) {
 97 		for (var i=0, n=handlers.length; i<n; ++i) {
 98 			if (handlers[i] == h) {
 99 				return i;
100 			}
101 		}
102 		return -1;
103 	},
104 
105 	_setMainListener : function (eventName, eventHandler) {
106 		var evt = this._events[eventName];
107 		if (!evt) return;
108 		if (evt.main == eventHandler) return;
109 		if (eventHandler) { this.addEventListener(eventName, eventHandler); }
110 		else { this.removeEventListener(eventName, eventHandler); }
111 		evt.main = eventHandler;
112 	},
113 
114 	_dispatch : function () {
115 		var name = arguments[0];
116 		var evt  = this._events[name];
117 		if (!evt) return;
118 		var args = Array.prototype.slice.call(arguments, 1);
119 		args.push(this);
120 		var lst  = evt.listeners;
121 		for (var i=0, n=lst.length; i<n; ++i) {
122 			lst[i].apply(null, args);
123 		}
124 	},
125 
126 	_doPostProgress : function () {
127 	},
128 
129 	_doPostCancel : function () {
130 	},
131 
132 	_doPostError : function () {
133 	},
134 
135 	_doPostSuccess : function () {
136 	},
137 
138 	_doPostFinish : function () {
139 	},
140 
141 	_doOnProgress : function (loaded, total) {
142 		if (this._aborted) return;
143 		this._loaded = loaded;
144 		this._total  = total;
145 		this._doPostProgress();
146 		this._dispatch("progress", this._loaded, this._total);
147 	},
148 
149 	_doOnCancel : function () {
150 		if (this._aborted) return;
151 		this._status = SpiderGL.IO.Request.CANCELLED;
152 		this._finishTime = SpiderGL.Utility.getTime();
153 		this._doPostCancel();
154 		this._dispatch("cancel");
155 	},
156 
157 	_doOnError : function () {
158 		if (this._aborted) return;
159 		this._status = SpiderGL.IO.Request.FAILED;
160 		this._finishTime = SpiderGL.Utility.getTime();
161 		this._doPostError();
162 		this._dispatch("error");
163 	},
164 
165 	_doOnSuccess : function () {
166 		if (this._aborted) return;
167 		this._status = SpiderGL.IO.Request.SUCCEEDED;
168 		this._finishTime = SpiderGL.Utility.getTime();
169 		this._doPostSuccess();
170 		this._dispatch("success");
171 	},
172 
173 	_doOnFinish : function () {
174 		this._doPostFinish();
175 		this._dispatch("finish");
176 	},
177 
178 	_doSend : function () {
179 		return false;
180 	},
181 
182 	_doCancel : function () {
183 		return false;
184 	},
185 
186 	get canSend() {
187 		return (this._url && !this._sent);
188 	},
189 
190 	get url() {
191 		return this._url;
192 	},
193 
194 	set url(s) {
195 		this.cancel();
196 		this._url = s;
197 	},
198 
199 	get status() {
200 		return this._status;
201 	},
202 
203 	get data() {
204 		return this._data;
205 	},
206 
207 	get bytesLoaded() {
208 		return this._loaded;
209 	},
210 
211 	get bytesTotal() {
212 		return this._total;
213 	},
214 
215 	get sent() {
216 		return this._sent;
217 	},
218 
219 	get ongoing() {
220 		return (this._status == SpiderGL.IO.Request.ONGOING);
221 	},
222 
223 	get cancelled() {
224 		return (this._status == SpiderGL.IO.Request.CANCELLED);
225 	},
226 
227 	get failed() {
228 		return (this._status == SpiderGL.IO.Request.FAILED);
229 	},
230 
231 	get succeeded() {
232 		return (this._status == SpiderGL.IO.Request.SUCCEEDED);
233 	},
234 
235 	get finished() {
236 		return (this.succeeded || this.failed || this.cancelled);
237 	},
238 
239 	get startTime() {
240 		return this._startTime;
241 	},
242 
243 	get finishTime() {
244 		return this._finishTime;
245 	},
246 
247 	get elapsedTime() {
248 		if (this._startTime < 0) return 0;
249 		if (this._finishTime < 0) return (SpiderGL.Utility.getTime() - this._startTime);
250 		return (this._finishTime - this._startTime);
251 	},
252 
253 	addEventListener : function (eventName, eventHandler) {
254 		if (!eventHandler) return;
255 		var evt = this._events[eventName];
256 		if (!evt) return;
257 		var idx = this._indexOf(evt.listeners, eventHandler);
258 		if (idx >= 0) return;
259 		evt.listeners.push(eventHandler);
260 	},
261 
262 	removeEventListener : function (eventName, eventHandler) {
263 		var evt = this._events[eventName];
264 		if (!evt) return;
265 		var idx = this._indexOf(evt.listeners, eventHandler);
266 		if (idx < 0) return;
267 		evt.listeners.splice(idx, 1);
268 	},
269 
270 	get onProgress() {
271 		return this._events.progress.main;
272 	},
273 
274 	set onProgress(f) {
275 		this._setMainListener("progress", f);
276 	},
277 
278 	get onCancel() {
279 		return this._events.cancel.main;
280 	},
281 
282 	set onCancel(f) {
283 		this._setMainListener("cancel", f);
284 	},
285 
286 	get onError() {
287 		return this._events.error.main;
288 	},
289 
290 	set onError(f) {
291 		this._setMainListener("error", f);
292 	},
293 
294 	get onSuccess() {
295 		return this._events.success.main;
296 	},
297 
298 	set onSuccess(f) {
299 		this._setMainListener("success", f);
300 	},
301 
302 	get onFinish() {
303 		return this._events.finish.main;
304 	},
305 
306 	set onFinish(f) {
307 		this._setMainListener("finish", f);
308 	},
309 
310 	cancel : function () {
311 		if (!this.ongoing) { return false; }
312 		this._status  = SpiderGL.IO.Request.CANCELLED;
313 		this._aborted = true;
314 		var r = this._doCancel();
315 		this._finishTime = SpiderGL.Utility.getTime();
316 		return r;
317 	},
318 
319 	send : function () {
320 		if (!this.canSend) { return false; }
321 		this._data       = null;
322 		this._status     = SpiderGL.IO.Request.ONGOING;
323 		this._aborted    = false;
324 		this._sent       = true;
325 		this._finishTime = -1;
326 		this._startTime  = SpiderGL.Utility.getTime();
327 		var r = this._doSend();
328 		if (!r) {
329 			this._startTime = -1;
330 			this._status = SpiderGL.IO.Request.NONE;
331 			this._sent = false;
332 		};
333 		return r;
334 	}
335 };
336 
337 SpiderGL.Type.extend(SpiderGL.IO.Request, SpiderGL.Core.ObjectBase);
338 
339 /**
340  * Creates a SpiderGL.IO.XHRRequestBase.
341  *
342  * SpiderGL.IO.XHRRequestBase is the base class for I/O requests.
343  *
344  * @class The SpiderGL.IO.XHRRequestBase is the base class for I/O requests.
345  *
346  * @augments SpiderGL.IO.Request
347  */
348 SpiderGL.IO.XHRRequestBase = function (url, options) {
349 	options = options || { };
350 	SpiderGL.IO.Request.call(this, url, options);
351 
352 	var that = this;
353 
354 	var xhr = new XMLHttpRequest();
355 	this._xhr = xhr;
356 
357 	xhr.onprogress = function (evt) { that._xhrOnProgress(evt); };
358 	xhr.onabort    = function ()    { that._doOnCancel(); that._doOnFinish(); };
359 	xhr.onerror    = function ()    { that._doOnError();  that._doOnFinish(); };
360 	xhr.onload     = function ()    {
361 		var status = xhr.status;
362 		if ((status === 0) || (status === 200) || (!!that._range && (status == 206))) {
363 			that._doOnSuccess();
364 		}
365 		else {
366 			that._doOnError();
367 		}
368 		that._doOnFinish();
369 	};
370 
371 	this._range = null;
372 
373 	this._xhr.open("GET", this._url, this._async);
374 
375 	if ("range" in options) {
376 		this._range = [ options.range[0], options.range[1] ];
377 		var rangeStr = "bytes=" + options.range[0] + "-" + options.range[1];
378 		xhr.setRequestHeader("Range", rangeStr);
379 	}
380 
381 	this._prepareXHR();
382 
383 	var send = SpiderGL.Utility.getDefaultValue(options.send, SpiderGL.IO.Request.DEFAULT_SEND);
384 	if (send) {
385 		this.send();
386 	}
387 };
388 
389 SpiderGL.IO.XHRRequestBase.prototype = {
390 	_prepareXHR : function () {
391 	},
392 
393 	_doCancel : function () {
394 		this._xhr.abort();
395 		this._xhr = new XMLHttpRequest();
396 		this._xhr.open("GET", this._url, this._async);
397 		this._prepareXHR();
398 		return true;
399 	},
400 
401 	_doSend : function () {
402 		this._xhr.send();
403 		return true;
404 	},
405 
406 	_xhrOnProgress : function (evt) {
407 		var loaded = 0;
408 		var total  = 0;
409 		if (evt && evt.lengthComputable) {
410 			loaded = evt.loaded;
411 			total  = evt.total;
412 		}
413 		this._doOnProgress(loaded, total);
414 	}
415 };
416 
417 SpiderGL.Type.extend(SpiderGL.IO.XHRRequestBase, SpiderGL.IO.Request);
418 
419 /**
420  * Creates a SpiderGL.IO.XHRRequest.
421  *
422  * SpiderGL.IO.XHRRequest is the base class for I/O requests.
423  *
424  * @class The SpiderGL.IO.XHRRequest is the base class for I/O requests.
425  *
426  * @augments SpiderGL.IO.XHRRequestBase
427  */
428 SpiderGL.IO.XHRRequest = function (url, options) {
429 	SpiderGL.IO.XHRRequestBase.call(this, url, options);
430 };
431 
432 SpiderGL.IO.XHRRequest.prototype = {
433 	_doPostSuccess : function () {
434 		this._data = this._xhr.responseText;
435 	},
436 
437 	get xhr() {
438 		return this._xhr;
439 	},
440 
441 	get response() {
442 		return this.data;
443 	}
444 };
445 
446 SpiderGL.Type.extend(SpiderGL.IO.XHRRequest, SpiderGL.IO.XHRRequestBase);
447 
448 /**
449  * Creates a SpiderGL.IO.TextRequest.
450  *
451  * SpiderGL.IO.TextRequest is the base class for I/O requests.
452  *
453  * @class The SpiderGL.IO.TextRequest is the base class for I/O requests.
454  *
455  * @augments SpiderGL.IO.XHRRequestBase
456  */
457 SpiderGL.IO.TextRequest = function (url, options) {
458 	SpiderGL.IO.XHRRequestBase.call(this, url, options);
459 };
460 
461 SpiderGL.IO.TextRequest.prototype = {
462 	_doPostSuccess : function () {
463 		this._data = this._xhr.responseText;
464 	},
465 
466 	get text() {
467 		return this.data;
468 	}
469 };
470 
471 SpiderGL.Type.extend(SpiderGL.IO.TextRequest, SpiderGL.IO.XHRRequestBase);
472 
473 /**
474  * Synchronous text read.
475  * This function is equivalent to issuing a SpiderGL.IO.TextRequest with the async flag set to false and no callbacks, and then reading its text property.
476  *
477  * @param {string} url The URL of the content
478  *
479  * @returns {string} The text content, or null on failure.
480  */
481 SpiderGL.IO.readText = function (url) {
482 	var r = new SpiderGL.IO.TextRequest(url, {async:false});
483 	return r.text;
484 };
485 
486 /**
487  * Asynchronous text read.
488  * This function creates a SpiderGL.IO.TextRequest with the async and seng flags set to true, overriding their values in the options parameter.
489  *
490  * @param {string} url The URL of the content
491  * @param {object} options The request options.
492  *
493  * @returns {SpiderGL.IO.TextRequest} The internally generated SpiderGL.IO.TextRequest.
494  */
495 SpiderGL.IO.requestText = function (url, options) {
496 	options = SpiderGL.Utility.getDefaultObject({ }, options);
497 	options.async = true;
498 	options.send  = true;
499 	var r = new SpiderGL.IO.TextRequest(url, options);
500 	return r;
501 };
502 
503 /**
504  * Creates a SpiderGL.IO.JSONRequest.
505  *
506  * SpiderGL.IO.JSONRequest is the base class for I/O requests.
507  *
508  * @class The SpiderGL.IO.JSONRequest is the base class for I/O requests.
509  *
510  * @augments SpiderGL.IO.XHRRequestBase
511  */
512 SpiderGL.IO.JSONRequest = function (url, options) {
513 	SpiderGL.IO.XHRRequestBase.call(this, url, options);
514 };
515 
516 SpiderGL.IO.JSONRequest.prototype = {
517 	_doPostSuccess : function () {
518 		this._data = JSON.parse(this._xhr.responseText);
519 	},
520 
521 	get text() {
522 		return this._xhr.responseText;
523 	},
524 
525 	get json() {
526 		return this.data;
527 	}
528 };
529 
530 SpiderGL.Type.extend(SpiderGL.IO.JSONRequest, SpiderGL.IO.XHRRequestBase);
531 
532 /**
533  * Synchronous JSON object read.
534  * This function is equivalent to issuing a SpiderGL.IO.JSONRequest with the async flag set to false and no callbacks, and then reading its json property.
535  *
536  * @param {string} url The URL of the content
537  *
538  * @returns {object} The JSON-parsed object, or null on failure.
539  */
540 SpiderGL.IO.readJSON = function (url) {
541 	var r = new SpiderGL.IO.JSONRequest(url, {async:false});
542 	return r.json;
543 };
544 
545 /**
546  * Asynchronous JSON read.
547  * This function creates a SpiderGL.IO.JSONRequest with the async and seng flags set to true, overriding their values in the options parameter.
548  *
549  * @param {string} url The URL of the content
550  * @param {object} options The request options.
551  *
552  * @returns {SpiderGL.IO.JSONRequest} The internally generated SpiderGL.IO.JSONRequest.
553  */
554 SpiderGL.IO.requestJSON = function (url, options) {
555 	options = SpiderGL.Utility.getDefaultObject({ }, options);
556 	options.async = true;
557 	options.send  = true;
558 	var r = new SpiderGL.IO.JSONRequest(url, options);
559 	return r;
560 };
561 
562 /**
563  * Creates a SpiderGL.IO.BinaryRequest.
564  *
565  * SpiderGL.IO.BinaryRequest is the base class for I/O requests.
566  *
567  * @class The SpiderGL.IO.BinaryRequest is the base class for I/O requests.
568  *
569  * @augments SpiderGL.IO.XHRRequestBase
570  */
571 SpiderGL.IO.BinaryRequest = function (url, options) {
572 	SpiderGL.IO.XHRRequestBase.call(this, url, options);
573 };
574 
575 SpiderGL.IO.BinaryRequest.prototype = {
576 	_prepareXHR : function () {
577 		var xhr = this._xhr;
578 		var overrideMime = false;
579 
580 		/*
581 		if (xhr.hasOwnProperty("responseType")) {
582 			try {
583 				xhr.responseType = "arraybuffer";
584 			}
585 			catch (e) {
586 				overrideMime = true;
587 			}
588 		}
589 		else {
590 				overrideMime = true;
591 		}
592 		*/
593 
594 		if (overrideMime) {
595 			xhr.overrideMimeType("text/plain; charset=x-user-defined");
596 		}
597 
598 		xhr.responseType = "arraybuffer";
599 	},
600 
601 	_setArrayBuffer : function () {
602 		var xhr = this._xhr;
603 
604 		if (xhr.responseType == "arraybuffer") {
605 			this._data = xhr.response;
606 		}
607 		else if (xhr.mozResponseArrayBuffer != null) {
608 			this._data = xhr.mozResponseArrayBuffer;
609 		}
610 		else if (xhr.responseText != null) {
611 			var data = new String(xhr.responseText);
612 			var arr  = new Array(data.length);
613 			for (var i=0, n=data.length; i<n; ++i) {
614 				arr[i] = data.charCodeAt(i) & 0xff;
615 			}
616 			this._data = (new Uint8Array(arr)).buffer;
617 		}
618 		else {
619 			this._data = null;
620 		}
621 	},
622 
623 	_doPostSuccess : function () {
624 		this._setArrayBuffer();
625 	},
626 
627 	get data() {
628 		if (this.ongoing) {
629 			this._setArrayBuffer();
630 		}
631 		return this._data;
632 	},
633 
634 	get buffer() {
635 		return this.data;
636 	}
637 };
638 
639 SpiderGL.Type.extend(SpiderGL.IO.BinaryRequest, SpiderGL.IO.XHRRequestBase);
640 
641 /**
642  * Synchronous binary data read.
643  * This function is equivalent to issuing a SpiderGL.IO.BinaryRequest with the async flag set to false and no callbacks, and then reading its buffer property.
644  *
645  * @param {string} url The URL of the content
646  *
647  * @returns {ArrayBuffer} The content binary data, or null on failure.
648  */
649 SpiderGL.IO.readBinary = function (url) {
650 	var r = new SpiderGL.IO.BinaryRequest(url, {async:false});
651 	return r.buffer;
652 };
653 
654 /**
655  * Asynchronous binary read.
656  * This function creates a SpiderGL.IO.BinaryRequest with the async and seng flags set to true, overriding their values in the options parameter.
657  *
658  * @param {string} url The URL of the content
659  * @param {object} options The request options.
660  *
661  * @returns {SpiderGL.IO.BinaryRequest} The internally generated SpiderGL.IO.BinaryRequest.
662  */
663 SpiderGL.IO.requestBinary = function (url, options) {
664 	options = SpiderGL.Utility.getDefaultObject({ }, options);
665 	options.async = true;
666 	options.send  = true;
667 	var r = new SpiderGL.IO.BinaryRequest(url, options);
668 	return r;
669 };
670 
671 /**
672  * Creates a SpiderGL.IO.ImageRequest.
673  *
674  * SpiderGL.IO.ImageRequest is the base class for I/O requests.
675  * The request is always asynchronous, meaning that the async flag is ignored.
676  *
677  * @class The SpiderGL.IO.ImageRequest is the base class for I/O requests.
678  *
679  * @augments SpiderGL.IO.Request
680  */
681 SpiderGL.IO.ImageRequest = function (url, options) {
682 	options = options || { };
683 	SpiderGL.IO.Request.call(this, url, options);
684 
685 	var that = this;
686 
687 	var img = new Image();
688 	this._img  = img;
689 	this._data = img;
690 
691 	img.onabort = function () { that._doOnCancel();  that._doOnFinish(); };
692 	img.onerror = function () { that._doOnError();   that._doOnFinish(); };
693 	img.onload  = function () { that._doOnSuccess(); that._doOnFinish(); };
694 
695 	if (typeof img.onprogress != "undefined") {
696 		img.onprogress = function (evt) { that._imgOnProgress(evt); };
697 	}
698 
699 	var send = SpiderGL.Utility.getDefaultValue(options.send, SpiderGL.IO.Request.DEFAULT_SEND);
700 	if (send) {
701 		this.send();
702 	}
703 };
704 
705 SpiderGL.IO.ImageRequest.prototype = {
706 	_doPostSuccess : function () {
707 		this._data = this._img;
708 	},
709 
710 	_doCancel : function () {
711 		this._img.src = null;
712 		this._img = new Image();
713 		return true;
714 	},
715 
716 	_doSend : function () {
717 		this._img.src = this._url;
718 		return true;
719 	},
720 
721 	_imgOnProgress : function (evt) {
722 		var loaded = 0;
723 		var total  = 0;
724 		if (evt && evt.lengthComputable) {
725 			loaded = evt.loaded;
726 			total  = evt.total;
727 		}
728 		this._doOnProgress(loaded, total);
729 	},
730 
731 	get image() {
732 		return this.data;
733 	}
734 };
735 
736 SpiderGL.Type.extend(SpiderGL.IO.ImageRequest, SpiderGL.IO.Request);
737 
738 /**
739  * Asynchronous image read.
740  * This function creates a SpiderGL.IO.ImageRequest with the async and seng flags set to true, overriding their values in the options parameter.
741  *
742  * @param {string} url The URL of the content
743  * @param {object} options The request options.
744  *
745  * @returns {SpiderGL.IO.ImageRequest} The internally generated SpiderGL.IO.ImageRequest.
746  */
747 SpiderGL.IO.requestImage = function (url, options) {
748 	options = SpiderGL.Utility.getDefaultObject({ }, options);
749 	options.async = true;
750 	options.send  = true;
751 	var r = new SpiderGL.IO.ImageRequest(url, options);
752 	return r;
753 };
754 
755 /**
756  * Creates a SpiderGL.IO.AggregateRequest.
757  *
758  * SpiderGL.IO.AggregateRequest is the base class for I/O requests.
759  *
760  * @class The SpiderGL.IO.AggregateRequest is the base class for I/O requests.
761  *
762  * @augments SpiderGL.IO.Request
763  */
764 SpiderGL.IO.AggregateRequest = function (options) {
765 	options = options || { };
766 	SpiderGL.IO.Request.call(this, "*", options);
767 
768 	var that = this;
769 
770 	this._proxyOnProgress = function (loaded, total, req) {
771 		that._reqOnProgress(loaded, total, req);
772 	};
773 
774 	this._proxyOnCancel = function (req) {
775 		that._reqOnCancel(req);
776 	};
777 
778 	this._proxyOnError = function (req) {
779 		that._reqOnError(req);
780 	};
781 
782 	this._proxyOnSuccess = function (req) {
783 		that._reqOnSuccess(req);
784 	};
785 
786 	this._proxyOnFinish = function (req) {
787 		that._reqOnFinish(req);
788 	};
789 
790 	this._aggrStartTime  = -1;
791 	this._aggrFinishTime = -1;
792 
793 	this._eventReq = null;
794 	this._cancelledReqs = 0;
795 	this._failedReqs    = 0;
796 	this._succeededReqs = 0;
797 	this._requests = [ ];
798 	var requests = options.requests;
799 	if (requests) {
800 		for (var i=0, n=requests.length; i<n; ++i) {
801 			var r = requests[i];
802 			if (r && !r.sent) {
803 				this._installProxies(r);
804 				this.addRequest(r);
805 			}
806 		}
807 	}
808 
809 	var send = SpiderGL.Utility.getDefaultValue(options.send, SpiderGL.IO.Request.DEFAULT_SEND);
810 	if (send) {
811 		this.send();
812 	}
813 };
814 
815 SpiderGL.IO.AggregateRequest.prototype = {
816 	_doPostCancel : function () {
817 		if (!this._requestsFinished) {
818 			this._status = SpiderGL.IO.Request.ONGOING;
819 		}
820 	},
821 
822 	_doPostError : function () {
823 		if (!this._requestsFinished) {
824 			this._status = SpiderGL.IO.Request.ONGOING;
825 		}
826 	},
827 
828 	_doPostSuccess : function () {
829 		if (!this._requestsFinished) {
830 			this._status = SpiderGL.IO.Request.ONGOING;
831 		}
832 	},
833 
834 	_doCancel : function () {
835 		var requests = this._requests;
836 		for (var i=0, n=requests.length; i<n; ++i) {
837 			requests[i].cancel();
838 		}
839 		this._aggrFinishTime = SpiderGL.Utility.getTime();
840 	},
841 
842 	_doSend : function () {
843 		this._aggrStartTime = SpiderGL.Utility.getTime();
844 		var requests = this._requests;
845 		for (var i=0, n=requests.length; i<n; ++i) {
846 			requests[i].send();
847 		}
848 	},
849 
850 	get _requestsFinished() {
851 		return ((this._cancelledReqs + this._failedReqs + this._succeededReqs) == this._requests.length);
852 	},
853 
854 	_installProxies : function (req) {
855 		req.addEventListener("progress", this._proxyOnProgress);
856 		req.addEventListener("cancel",   this._proxyOnCancel);
857 		req.addEventListener("error",    this._proxyOnError);
858 		req.addEventListener("success",  this._proxyOnSuccess);
859 		req.addEventListener("finish",   this._proxyOnFinish);
860 	},
861 
862 	_uninstallProxies : function (req) {
863 		req.removeEventListener("progress", this._proxyOnProgress);
864 		req.removeEventListener("cancel",   this._proxyOnCancel);
865 		req.removeEventListener("error",    this._proxyOnError);
866 		req.removeEventListener("success",  this._proxyOnSuccess);
867 		req.removeEventListener("finish",   this._proxyOnFinish);
868 	},
869 
870 	_reqOnProgress : function (loaded, total, req) {
871 		var idx = this._indexOf(this._requests, req);
872 		if (idx < 0) return;
873 		this._eventReq = req;
874 		this._doOnProgress(loaded, total);
875 		this._eventReq = null;
876 	},
877 
878 	_reqOnCancel : function (req) {
879 		var idx = this._indexOf(this._requests, req);
880 		if (idx < 0) return;
881 		this._eventReq = req;
882 		//this._doOnCancel();
883 		this._cancelledReqs++;
884 		if (this._requestsFinished) {
885 			this._aggrFinishTime = SpiderGL.Utility.getTime();
886 			if (this._cancelledReqs == this._requests.length) {
887 				this._eventReq = this;
888 				this._doOnCancel();
889 			}
890 		}
891 		else {
892 		}
893 		this._eventReq = null;
894 	},
895 
896 	_reqOnError : function (req) {
897 		var idx = this._indexOf(this._requests, req);
898 		if (idx < 0) return;
899 		this._eventReq = req;
900 		//this._doOnError();
901 		this._failedReqs++;
902 		if (this._requestsFinished) {
903 			this._aggrFinishTime = SpiderGL.Utility.getTime();
904 			this._eventReq = this;
905 			this._doOnError();
906 		}
907 		this._eventReq = null;
908 	},
909 
910 	_reqOnSuccess : function (req) {
911 		var idx = this._indexOf(this._requests, req);
912 		if (idx < 0) return;
913 		this._eventReq = req;
914 		//this._doOnSuccess();
915 		this._succeededReqs++;
916 		if (this._requestsFinished) {
917 			this._aggrFinishTime = SpiderGL.Utility.getTime();
918 			this._eventReq = this;
919 			if (this._failedReqs > 0) {
920 				this._doOnError();
921 			}
922 			else {
923 				this._doOnSuccess();
924 			}
925 		}
926 		this._eventReq = null;
927 	},
928 
929 	_reqOnFinish : function (req) {
930 		var idx = this._indexOf(this._requests, req);
931 		if (idx < 0) return;
932 		this._uninstallProxies(req);
933 		this._eventReq = req;
934 		//this._doOnFinish();
935 		if (this._requestsFinished) {
936 			this._eventReq = this;
937 			this._doOnFinish();
938 		}
939 		this._eventReq = null;
940 	},
941 
942 	get eventSenderRequest() {
943 		return this._eventReq;
944 	},
945 
946 	get requests() {
947 		return this._requests.slice();
948 	},
949 
950 	get requests$() {
951 		return this._requests;
952 	},
953 
954 	get startTime() {
955 		return this._aggrStartTime;
956 	},
957 
958 	get finishTime() {
959 		return this._aggrFinishTime;
960 	},
961 
962 	get elapsedTime() {
963 		if (this._aggrStartTime < 0) return 0;
964 		if (this._aggrFinishTime < 0) return (SpiderGL.Utility.getTime() - this._aggrStartTime);
965 		return (this._aggrFinishTime - this._aggrStartTime);
966 	},
967 
968 	addRequest : function (r) {
969 		if (!r || this._sent) return;
970 		var idx = this._indexOf(this._requests, r);
971 		if (idx >= 0) return;
972 		this._requests.push(r);
973 	},
974 
975 	removeRequest : function (r) {
976 		if (!r || this._sent) return;
977 		var idx = this._indexOf(this._requests, r);
978 		if (idx < 0) return;
979 		this._requests.splice(idx, 1);
980 	}
981 };
982 
983 SpiderGL.Type.extend(SpiderGL.IO.AggregateRequest, SpiderGL.IO.Request);
984 
985