Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
 
2
Prado.AjaxRequest = Class.create();
3
Prado.AjaxRequest.prototype = Object.clone(Ajax.Request.prototype);
4
 
5
/**
6
 * Override Prototype's response implementation.
7
 */
8
Object.extend(Prado.AjaxRequest.prototype,
9
{
10
	/*initialize: function(request)
11
	{
12
		this.CallbackRequest = request;
13
		this.transport = Ajax.getTransport();
14
		this.setOptions(request.options);
15
		this.request(request.url);
16
	},*/
17
 
18
	/**
19
	 * Customize the response, dispatch onXXX response code events, and
20
	 * tries to execute response actions (javascript statements).
21
	 */
22
	respondToReadyState : function(readyState)
23
	{
24
	    var event = Ajax.Request.Events[readyState];
25
	    var transport = this.transport, json = this.getBodyDataPart(Prado.CallbackRequest.DATA_HEADER);
26
 
27
	    if (event == 'Complete')
28
	    {
29
			var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER);
30
	    	if(redirectUrl)
31
	    		document.location.href = redirectUrl;
32
 
33
	      if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i))
34
	      {
35
	        try
36
			{
37
	           json = eval('(' + transport.responseText + ')');
38
	        }catch (e)
39
			{
40
				if(typeof(json) == "string")
41
					json = Prado.CallbackRequest.decode(result);
42
			}
43
	      }
44
 
45
	      try
46
	      {
47
	      	Prado.CallbackRequest.updatePageState(this,transport);
48
			Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
49
			Prado.CallbackRequest.dispatchActions(transport,this.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER));
50
 
51
	        (this.options['on' + this.transport.status]
52
	         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
53
	         || Prototype.emptyFunction)(this, json);
54
	  	      } catch (e) {
55
	        this.dispatchException(e);
56
	      }
57
	    }
58
 
59
	    try {
60
	      (this.options['on' + event] || Prototype.emptyFunction)(this, json);
61
	      Ajax.Responders.dispatch('on' + event, this, transport, json);
62
	    } catch (e) {
63
	      this.dispatchException(e);
64
	    }
65
 
66
	    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
67
	    if (event == 'Complete')
68
	      this.transport.onreadystatechange = Prototype.emptyFunction;
69
	},
70
 
71
	/**
72
	 * Gets header data assuming JSON encoding.
73
	 * @param string header name
74
	 * @return object header data as javascript structures.
75
	 */
76
	getHeaderData : function(name)
77
	{
78
		return this.getJsonData(this.getHeader(name));
79
	},
80
 
81
	getBodyContentPart : function(name)
82
	{
83
		if(typeof(this.transport.responseText)=="string")
84
			return Prado.Element.extractContent(this.transport.responseText, name);
85
	},
86
 
87
	getJsonData : function(json)
88
	{
89
		try
90
		{
91
			return eval('(' + json + ')');
92
		}
93
		catch (e)
94
		{
95
			if(typeof(json) == "string")
96
				return Prado.CallbackRequest.decode(json);
97
		}
98
	},
99
 
100
	getBodyDataPart : function(name)
101
	{
102
		return this.getJsonData(this.getBodyContentPart(name));
103
	}
104
});
105
 
106
/**
107
 * Prado Callback client-side request handler.
108
 */
109
Prado.CallbackRequest = Class.create();
110
 
111
/**
112
 * Static definitions.
113
 */
114
Object.extend(Prado.CallbackRequest,
115
{
116
	/**
117
	 * Callback request target POST field name.
118
	 */
119
	FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET',
120
	/**
121
	 * Callback request parameter POST field name.
122
	 */
123
	FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER',
124
	/**
125
	 * Callback request page state field name,
126
	 */
127
	FIELD_CALLBACK_PAGESTATE : 'PRADO_PAGESTATE',
128
 
129
	FIELD_POSTBACK_TARGET : 'PRADO_POSTBACK_TARGET',
130
 
131
	FIELD_POSTBACK_PARAMETER : 'PRADO_POSTBACK_PARAMETER',
132
 
133
	/**
134
	 * List of form fields that will be collected during callback.
135
	 */
136
	PostDataLoaders : [],
137
	/**
138
	 * Response data header name.
139
	 */
140
	DATA_HEADER : 'X-PRADO-DATA',
141
	/**
142
	 * Response javascript execution statement header name.
143
	 */
144
	ACTION_HEADER : 'X-PRADO-ACTIONS',
145
	/**
146
	 * Response errors/exceptions header name.
147
	 */
148
	ERROR_HEADER : 'X-PRADO-ERROR',
149
	/**
150
	 * Page state header name.
151
	 */
152
	PAGESTATE_HEADER : 'X-PRADO-PAGESTATE',
153
 
154
	REDIRECT_HEADER : 'X-PRADO-REDIRECT',
155
 
156
	requestQueue : [],
157
 
158
	//all request objects
159
	requests : {},
160
 
161
	getRequestById : function(id)
162
	{
163
		var requests = Prado.CallbackRequest.requests;
164
		if(typeof(requests[id]) != "undefined")
165
			return requests[id];
166
	},
167
 
168
	dispatch : function(id)
169
	{
170
		var requests = Prado.CallbackRequest.requests;
171
		if(typeof(requests[id]) != "undefined")
172
			requests[id].dispatch();
173
	},
174
 
175
	/**
176
	 * Add ids of inputs element to post in the request.
177
	 */
178
	addPostLoaders : function(ids)
179
	{
180
		var self = Prado.CallbackRequest;
181
		self.PostDataLoaders = self.PostDataLoaders.concat(ids);
182
		var list = [];
183
		self.PostDataLoaders.each(function(id)
184
		{
185
			if(list.indexOf(id) < 0)
186
				list.push(id);
187
		});
188
		self.PostDataLoaders = list;
189
	},
190
 
191
	/**
192
	 * Dispatch callback response actions.
193
	 */
194
	dispatchActions : function(transport,actions)
195
	{
196
		var self = Prado.CallbackRequest;
197
		if(actions && actions.length > 0)
198
			actions.each(self.__run.bind(self,transport));
199
	},
200
 
201
	/**
202
	 * Prase and evaluate a Callback clien-side action
203
	 */
204
	__run : function(transport, command)
205
	{
206
		var self = Prado.CallbackRequest;
207
		self.transport = transport;
208
		for(var method in command)
209
		{
210
			try
211
			{
212
				method.toFunction().apply(self,command[method]);
213
			}
214
			catch(e)
215
			{
216
				if(typeof(Logger) != "undefined")
217
					self.Exception.onException(null,e);
218
			}
219
		}
220
	},
221
 
222
	/**
223
	 * Respond to Prado Callback request exceptions.
224
	 */
225
	Exception :
226
	{
227
		/**
228
		 * Server returns 500 exception. Just log it.
229
		 */
230
		"on500" : function(request, transport, data)
231
		{
232
			var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER);
233
			if (e)
234
				Logger.error("Callback Server Error "+e.code, this.formatException(e));
235
			else
236
				Logger.error("Callback Server Error Unknown",'');
237
		},
238
 
239
		/**
240
		 * Callback OnComplete event,logs reponse and data to console.
241
		 */
242
		'on200' : function(request, transport, data)
243
		{
244
			if(transport.status < 500)
245
			{
246
				var msg = 'HTTP '+transport.status+" with response : \n";
247
				if(transport.responseText.trim().length >0)
248
				{
249
					var f = RegExp('(<!--X-PRADO[^>]+-->)([\\s\\S\\w\\W]*)(<!--//X-PRADO[^>]+-->)',"m");
250
					msg += transport.responseText.replace(f,'') + "\n";
251
				}
252
				if(typeof(data)!="undefined" && data != null)
253
					msg += "Data : \n"+inspect(data)+"\n";
254
				data = request.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER);
255
				if(data && data.length > 0)
256
				{
257
					msg += "Actions : \n";
258
					data.each(function(action)
259
					{
260
						msg += inspect(action)+"\n";
261
					});
262
				}
263
				Logger.info(msg);
264
			}
265
		},
266
 
267
		/**
268
		 * Uncaught exceptions during callback response.
269
		 */
270
		onException : function(request,e)
271
		{
272
			msg = "";
273
			$H(e).each(function(item)
274
			{
275
				msg += item.key+": "+item.value+"\n";
276
			})
277
			Logger.error('Uncaught Callback Client Exception:', msg);
278
		},
279
 
280
		/**
281
		 * Formats the exception message for display in console.
282
		 */
283
		formatException : function(e)
284
		{
285
			var msg = e.type + " with message \""+e.message+"\"";
286
			msg += " in "+e.file+"("+e.line+")\n";
287
			msg += "Stack trace:\n";
288
			var trace = e.trace;
289
			for(var i = 0; i<trace.length; i++)
290
			{
291
				msg += "  #"+i+" "+trace[i].file;
292
				msg += "("+trace[i].line+"): ";
293
				msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n";
294
			}
295
			msg += e.version+" "+e.time+"\n";
296
			return msg;
297
		}
298
	},
299
 
300
	/**
301
	 * @return string JSON encoded data.
302
	 */
303
	encode : function(data)
304
	{
305
		return Prado.JSON.stringify(data);
306
	},
307
 
308
	/**
309
	 * @return mixed javascript data decoded from string using JSON decoding.
310
	 */
311
	decode : function(data)
312
	{
313
		if(typeof(data) == "string" && data.trim().length > 0)
314
			return Prado.JSON.parse(data);
315
		else
316
			return null;
317
	},
318
 
319
	/**
320
	 * Dispatch a normal request, no timeouts or aborting of requests.
321
	 */
322
	dispatchNormalRequest : function(callback)
323
	{
324
		callback.options.postBody = callback._getPostData(),
325
		callback.request(callback.url);
326
		return true;
327
	},
328
 
329
	/**
330
	 * Abort the current priority request in progress.
331
	 */
332
	tryNextRequest : function()
333
	{
334
		var self = Prado.CallbackRequest;
335
		//Logger.debug('trying next request');
336
		if(typeof(self.currentRequest) == 'undefined' || self.currentRequest==null)
337
		{
338
			if(self.requestQueue.length > 0)
339
				return self.dispatchQueue();
340
			//else
341
				//Logger.warn('empty queque');
342
		}
343
		//else
344
			//Logger.warn('current request ' + self.currentRequest.id);
345
	},
346
 
347
	/**
348
	 * Updates the page state. It will update only if EnablePageStateUpdate and
349
	 * HasPriority options are both true.
350
	 */
351
	updatePageState : function(request, transport)
352
	{
353
		var self = Prado.CallbackRequest;
354
		var pagestate = $(self.FIELD_CALLBACK_PAGESTATE);
355
		var enabled = request.ActiveControl.EnablePageStateUpdate && request.ActiveControl.HasPriority;
356
		var aborted = typeof(self.currentRequest) == 'undefined' || self.currentRequest == null;
357
		if(enabled && !aborted && pagestate)
358
		{
359
			var data = request.getBodyContentPart(self.PAGESTATE_HEADER);
360
			if(typeof(data) == "string" && data.length > 0)
361
				pagestate.value = data;
362
			else
363
			{
364
				if(typeof(Logger) != "undefined")
365
					Logger.warn("Missing page state:"+data);
366
				//Logger.warn('## bad state: setting current request to null');
367
				self.endCurrentRequest();
368
				//self.tryNextRequest();
369
				return false;
370
			}
371
		}
372
		self.endCurrentRequest();
373
		//Logger.warn('## state updated: setting current request to null');
374
		//self.tryNextRequest();
375
		return true;
376
	},
377
 
378
	enqueue : function(callback)
379
	{
380
		var self = Prado.CallbackRequest;
381
		self.requestQueue.push(callback);
382
		//Logger.warn("equeued "+callback.id+", current queque length="+self.requestQueue.length);
383
		self.tryNextRequest();
384
	},
385
 
386
	dispatchQueue : function()
387
	{
388
		var self = Prado.CallbackRequest;
389
		//Logger.warn("dispatching queque, length="+self.requestQueue.length+" request="+self.currentRequest);
390
		var callback = self.requestQueue.shift();
391
		self.currentRequest = callback;
392
 
393
		//get data
394
		callback.options.postBody = callback._getPostData(),
395
 
396
		//callback.request = new Prado.AjaxRequest(callback);
397
		callback.timeout = setTimeout(function()
398
		{
399
			//Logger.warn("priority timeout");
400
			self.abortRequest(callback.id);
401
		},callback.ActiveControl.RequestTimeOut);
402
		callback.request(callback.url);
403
		//Logger.debug("dispatched "+self.currentRequest.id + " ...")
404
	},
405
 
406
	endCurrentRequest : function()
407
	{
408
		var self = Prado.CallbackRequest;
409
		if(typeof(self.currentRequest) != 'undefined' && self.currentRequest != null)
410
			clearTimeout(self.currentRequest.timeout);
411
		self.currentRequest=null;
412
	},
413
 
414
	abortRequest : function(id)
415
	{
416
		//Logger.warn("abort id="+id);
417
		var self = Prado.CallbackRequest;
418
		if(typeof(self.currentRequest) != 'undefined'
419
			&& self.currentRequest != null && self.currentRequest.id == id)
420
		{
421
			var request = self.currentRequest;
422
			if(request.transport.readyState < 4)
423
				request.transport.abort();
424
			//Logger.warn('## aborted: setting current request to null');
425
			self.endCurrentRequest();
426
		}
427
		self.tryNextRequest();
428
	}
429
});
430
 
431
/**
432
 * Automatically aborts the current request when a priority request has returned.
433
 */
434
Ajax.Responders.register({onComplete : function(request)
435
{
436
	if(request && request instanceof Prado.AjaxRequest)
437
	{
438
		if(request.ActiveControl.HasPriority)
439
			Prado.CallbackRequest.tryNextRequest();
440
	}
441
}});
442
 
443
//Add HTTP exception respones when logger is enabled.
444
Event.OnLoad(function()
445
{
446
	if(typeof Logger != "undefined")
447
		Ajax.Responders.register(Prado.CallbackRequest.Exception);
448
});
449
 
450
/**
451
 * Create and prepare a new callback request.
452
 * Call the dispatch() method to start the callback request.
453
 * <code>
454
 * request = new Prado.CallbackRequest(UniqueID, callback);
455
 * request.dispatch();
456
 * </code>
457
 */
458
Prado.CallbackRequest.prototype = Object.extend(Prado.AjaxRequest.prototype,
459
{
460
 
461
	/**
462
	 * Prepare and inititate a callback request.
463
	 */
464
	initialize : function(id, options)
465
	{
466
		/**
467
		 * Callback URL, same url as the current page.
468
		 */
469
		this.url = this.getCallbackUrl();
470
 
471
		this.transport = Ajax.getTransport();
472
		this.Enabled = true;
473
		this.id = id;
474
 
475
		if(typeof(id)=="string"){
476
			Prado.CallbackRequest.requests[id] = this;
477
		}
478
 
479
		this.setOptions(Object.extend(
480
		{
481
			RequestTimeOut : 30000, // 30 second timeout.
482
			EnablePageStateUpdate : true,
483
			HasPriority : true,
484
			CausesValidation : true,
485
			ValidationGroup : null,
486
			PostInputs : true
487
		}, options || {}));
488
 
489
		this.ActiveControl = this.options;
490
	},
491
 
492
	/**
493
	 * Sets the request options
494
	 * @return {Array} request options.
495
	 */
496
	setOptions: function(options){
497
 
498
		this.options = {
499
			method:       'post',
500
			asynchronous: true,
501
			contentType:  'application/x-www-form-urlencoded',
502
			encoding:     'UTF-8',
503
			parameters:   '',
504
			evalJSON:     true,
505
			evalJS:       true
506
		};
507
 
508
		Object.extend(this.options, options || { });
509
 
510
		this.options.method = this.options.method.toLowerCase();
511
		if(Object.isString(this.options.parameters)){
512
			this.options.parameters = this.options.parameters.toQueryParams();
513
		}
514
	},
515
 
516
	/**
517
	 * Gets the url from the forms that contains the PRADO_PAGESTATE
518
	 * @return {String} callback url.
519
	 */
520
	getCallbackUrl : function()
521
	{
522
		return $('PRADO_PAGESTATE').form.action;
523
	},
524
 
525
	/**
526
	 * Sets the request parameter
527
	 * @param {Object} parameter value
528
	 */
529
	setCallbackParameter : function(value)
530
	{
531
		this.ActiveControl['CallbackParameter'] = value;
532
	},
533
 
534
	/**
535
	 * @return {Object} request paramater value.
536
	 */
537
	getCallbackParameter : function()
538
	{
539
		return this.ActiveControl['CallbackParameter'];
540
	},
541
 
542
	/**
543
	 * Sets the callback request timeout.
544
	 * @param {integer} timeout in  milliseconds
545
	 */
546
	setRequestTimeOut : function(timeout)
547
	{
548
		this.ActiveControl['RequestTimeOut'] = timeout;
549
	},
550
 
551
	/**
552
	 * @return {integer} request timeout in milliseconds
553
	 */
554
	getRequestTimeOut : function()
555
	{
556
		return this.ActiveControl['RequestTimeOut'];
557
	},
558
 
559
	/**
560
	 * Set true to enable validation on callback dispatch.
561
	 * @param {boolean} true to validate
562
	 */
563
	setCausesValidation : function(validate)
564
	{
565
		this.ActiveControl['CausesValidation'] = validate;
566
	},
567
 
568
	/**
569
	 * @return {boolean} validate on request dispatch
570
	 */
571
	getCausesValidation : function()
572
	{
573
		return this.ActiveControl['CausesValidation'];
574
	},
575
 
576
	/**
577
	 * Sets the validation group to validate during request dispatch.
578
	 * @param {string} validation group name
579
	 */
580
	setValidationGroup : function(group)
581
	{
582
		this.ActiveControl['ValidationGroup'] = group;
583
	},
584
 
585
	/**
586
	 * @return {string} validation group name.
587
	 */
588
	getValidationGroup : function()
589
	{
590
		return this.ActiveControl['ValidationGroup'];
591
	},
592
 
593
	/**
594
	 * Dispatch the callback request.
595
	 */
596
	dispatch : function()
597
	{
598
		//Logger.info("dispatching request");
599
		//trigger tinyMCE to save data.
600
		if(typeof tinyMCE != "undefined")
601
			tinyMCE.triggerSave();
602
 
603
		if(this.ActiveControl.CausesValidation && typeof(Prado.Validation) != "undefined")
604
		{
605
			var form =  this.ActiveControl.Form || Prado.Validation.getForm();
606
			if(Prado.Validation.validate(form,this.ActiveControl.ValidationGroup,this) == false)
607
				return false;
608
		}
609
 
610
		if(this.ActiveControl.onPreDispatch)
611
			this.ActiveControl.onPreDispatch(this,null);
612
 
613
		if(!this.Enabled)
614
			return;
615
 
616
		// Opera don't have onLoading/onLoaded state, so, simulate them just
617
		// before sending the request.
618
		if (Prototype.Browser.Opera)
619
		{
620
			if (this.ActiveControl.onLoading)
621
			{
622
				this.ActiveControl.onLoading(this,null);
623
				Ajax.Responders.dispatch('onLoading',this, this.transport,null);
624
			}
625
			if (this.ActiveControl.onLoaded)
626
			{
627
				this.ActiveControl.onLoaded(this,null);
628
				Ajax.Responders.dispatch('onLoaded',this, this.transport,null);
629
			}
630
		}
631
 
632
		var result;
633
		if(this.ActiveControl.HasPriority)
634
		{
635
			return Prado.CallbackRequest.enqueue(this);
636
			//return Prado.CallbackRequest.dispatchPriorityRequest(this);
637
		}
638
		else
639
			return Prado.CallbackRequest.dispatchNormalRequest(this);
640
	},
641
 
642
	abort : function()
643
	{
644
		return Prado.CallbackRequest.abortRequest(this.id);
645
	},
646
 
647
	/**
648
	 * Collects the form inputs, encode the parameters, and sets the callback
649
	 * target id. The resulting string is the request content body.
650
	 * @return string request body content containing post data.
651
	 */
652
	_getPostData : function()
653
	{
654
		var data = {};
655
		var callback = Prado.CallbackRequest;
656
		if(this.ActiveControl.PostInputs != false)
657
		{
658
			callback.PostDataLoaders.each(function(name)
659
			{
660
				$A(document.getElementsByName(name)).each(function(element)
661
				{
662
					//IE will try to get elements with ID == name as well.
663
					if(element.type && element.name == name)
664
					{
665
						value = $F(element);
666
						if(typeof(value) != "undefined" && value != null)
667
							data[name] = value;
668
					}
669
				})
670
			})
671
		}
672
		if(typeof(this.ActiveControl.CallbackParameter) != "undefined")
673
			data[callback.FIELD_CALLBACK_PARAMETER] = callback.encode(this.ActiveControl.CallbackParameter);
674
		var pageState = $F(callback.FIELD_CALLBACK_PAGESTATE);
675
		if(typeof(pageState) != "undefined")
676
			data[callback.FIELD_CALLBACK_PAGESTATE] = pageState;
677
		data[callback.FIELD_CALLBACK_TARGET] = this.id;
678
		if(this.ActiveControl.EventTarget)
679
			data[callback.FIELD_POSTBACK_TARGET] = this.ActiveControl.EventTarget;
680
		if(this.ActiveControl.EventParameter)
681
			data[callback.FIELD_POSTBACK_PARAMETER] = this.ActiveControl.EventParameter;
682
		return $H(data).toQueryString();
683
	}
684
});
685
 
686
/**
687
 * Create a new callback request using default settings.
688
 * @param string callback handler unique ID.
689
 * @param mixed parameter to pass to callback handler on the server side.
690
 * @param function client side onSuccess event handler.
691
 * @param object additional request options.
692
 * @return boolean always false.
693
 */
694
Prado.Callback = function(UniqueID, parameter, onSuccess, options)
695
{
696
	var callback =
697
	{
698
		'CallbackParameter' : parameter || '',
699
		'onSuccess' : onSuccess || Prototype.emptyFunction
700
	};
701
 
702
	Object.extend(callback, options || {});
703
 
704
	request = new Prado.CallbackRequest(UniqueID, callback);
705
	request.dispatch();
706
	return false;
707
};