1 /*
  2  * Naos Framework
  3  *
  4  * LICENSE
  5  * http://www.naos-framework.com/website.naos.framework/en/license
  6  *
  7  * Copyright (c) 2008 Jupiter GmbH
  8  */
  9  
 10 /**
 11  * Creates a new NaosAjax request object. This object allows
 12  * you to communicate with the PHP part of the Naos Framework.
 13  *  
 14  * @constructor
 15  * 
 16  * @param requests Defines the actual request. Format {view1: {node: ..., module: ..., controller: ..., action: ..., parameter: {p1: ..., ...}}, ...}
 17  * @param domForms An array of HTML form objects, which should be send with this request.
 18  * @param callbackCreateData When defined this function will be called with the dom tree which will be send as request. This allows you to modify the dom tree before it gets send if necessary. 	
 19  */
 20 function NaosAjax(/**Object|Array*/ requests, /**Array*/ domForms, /**function*/ callbackCreateData)
 21 {
 22 	NaosDebug.debug("[NaosAjax]: Preparing request.");
 23 		
 24 	// transform to vanilla object
 25 	requests = NaosTools.toVanillaObject(requests);
 26 	
 27 	// generate url from requests and status...
 28 	var url = naosConfig.application.base_url+'.ajax/'+naosStatus.application.locale+'/'
 29 		+naosStatus.application.menuItem;
 30 		
 31 	// ... and generate xml request
 32 	var xml = zXmlDom.createDocument();
 33 	
 34 	var xml_request_ajax = xml.createElement('ajax');
 35 	
 36 	var xml_request = xml.createElement('request');
 37 	xml_request.appendChild(xml_request_ajax);
 38 	
 39 	xml.appendChild(xml_request);
 40 	
 41 	// update url and xml request with current status
 42 	var views = naosConfig.layout.views.split(',');
 43 	for(var i=0; i<views.length; ++i)
 44 	{
 45 		var viewStatus = null;
 46 		
 47 		// get current status and update with request
 48 		if(naosStatus.layout[views[i]] != undefined)
 49 			viewStatus = naosStatus.layout[views[i]];
 50 			
 51 		if(requests[views[i]] != undefined)
 52 			viewStatus = requests[views[i]];
 53 			
 54 		if(viewStatus == null)
 55 			continue;
 56 		
 57 		// update url
 58 		url += '//'+views[i]+'/'+viewStatus.node+'/'+viewStatus.module+'/'+viewStatus.controller+'/'+
 59 			viewStatus.action;
 60 		
 61 		for(var j in viewStatus.parameter) {
 62 			url += '/'+j+'/'+viewStatus.parameter[j];
 63 		}
 64 	}
 65 	
 66 	for(var i in requests)
 67 	{
 68 		// update xml request
 69 		var xml_request_ajax_view = xml.createElement('view');
 70 		xml_request_ajax_view.setAttribute('id', i);
 71 		xml_request_ajax.appendChild(xml_request_ajax_view);
 72 	}
 73 	
 74 	// append form data to xml request
 75 	for(var i=0; i < domForms.length; ++i)
 76 	{
 77 		var xml_request_ajax_form = xml.createElement('form');
 78 		xml_request_ajax_form.setAttribute('id', domForms[i].name);
 79 		xml_request_ajax_form.setAttribute('items', domForms[i].elements[domForms[i].name+':items'].value);
 80 		xml_request_ajax_form.setAttribute('titles', domForms[i].elements[domForms[i].name+':titles'].value);
 81 		
 82 		for(var j=0; j < domForms[i].elements.length; ++j)
 83 		{
 84 			if(domForms[i].elements[j].name == domForms[i].name+':items' || domForms[i].elements[j].name == domForms[i].name+':titles')
 85 				continue;
 86 				
 87 			if((domForms[i].elements[j].type == 'checkbox' || domForms[i].elements[j].type == 'radio') && domForms[i].elements[j].checked == false)
 88 				continue;
 89 				
 90 			var xml_request_ajax_form_item = xml.createElement('item');
 91 			xml_request_ajax_form_item.setAttribute('id', domForms[i].elements[j].name);
 92 			xml_request_ajax_form_item.appendChild(xml.createTextNode(domForms[i].elements[j].value));
 93 			
 94 			xml_request_ajax_form.appendChild(xml_request_ajax_form_item);
 95 		}
 96 		
 97 		xml_request_ajax.appendChild(xml_request_ajax_form);
 98 	}
 99 	
100 	// append additional data to xml request
101 	if(callbackCreateData != null)
102 		callbackCreateData(xml, xml_request);
103 	
104 	// set member variables
105 	this.requests = requests;
106 	this.url = url;
107 	this.xmlrequest = xml;
108 		
109 	// create ajax callback functions
110 	/**
111 	 * This function processes a successful AJAX request. It gets called by
112 	 * the Prototype Ajax.Request object.
113 	 * @param ajax Contains the response data.
114 	 * @private
115 	 */
116 	this.callbackOk = function(/**Ajax.Response*/ ajax) {
117 		
118 		// display debug message
119 		NaosDebug.debug("[NaosAjax]: Response received.\nXML: "+
120 			new zXMLSerializer().serializeToString(ajax.responseXML));
121 	
122 		// update application state
123 		var xml_state = zXPath.selectSingleNode(ajax.responseXML, '/response/ajax/state');
124 		naosStatus = NaosTools.fromJSON(xml_state.textContent?xml_state.textContent:xml_state.text);
125 		
126 		// update view output
127 		var xml_views = zXPath.selectNodes(ajax.responseXML, '/response/ajax/view');
128 		for(var k=0; k < xml_views.length; ++k)
129 		{
130 			Naos.updateViewContent(xml_views[k].getAttribute('id'), xml_views[k].textContent?xml_views[k].textContent:xml_views[k].text);
131 			NaosHandler.viewUpdateNotifier(xml_views[k].getAttribute('id'), xml_views[k].textContent?xml_views[k].textContent:xml_views[k].text);
132 		}
133 		
134 		// perform queued operations
135 		var xml_queuedOperations = zXPath.selectSingleNode(ajax.responseXML, '/response/ajax/queuedOperations');
136 		var queuedOperations = NaosTools.fromJSON(xml_queuedOperations.textContent?xml_queuedOperations.textContent:xml_queuedOperations.text);
137 		
138 		// dialog operations
139 		for(qopsModule in queuedOperations['dialog'])
140 		{
141 			for(qopsDialog in queuedOperations['dialog'][qopsModule])
142 			{
143 				if(queuedOperations['dialog'][qopsModule][qopsDialog]['operation'] == 'open')
144 				{
145 					NaosDialog.openDialog(qopsModule, qopsDialog,
146 						queuedOperations['dialog'][qopsModule][qopsDialog]['data']['type'],
147 						queuedOperations['dialog'][qopsModule][qopsDialog]['data']['position'],
148 						queuedOperations['dialog'][qopsModule][qopsDialog]['data']['output']);
149 				}
150 				else if(queuedOperations['dialog'][qopsModule][qopsDialog]['operation'] == 'close')
151 				{
152 					NaosDialog.closeDialog(qopsModule, qopsDialog);
153 				}
154 			}
155 		}
156 		
157 		// custom operations
158 		for(qopsCustomKey in queuedOperations['custom'])
159 		{
160 			
161 			NaosHandler.customOperation(queuedOperations['custom'][qopsCustomKey]['module'], queuedOperations['custom'][qopsCustomKey]['type'], queuedOperations['custom'][qopsCustomKey]['data']);
162 			
163 		}
164 		
165 		NaosAjax.finishRequest(this);
166 		
167 	}.bind(this);
168 	
169 	/**
170 	 * This function processes an unsuccessful AJAX request. It gets called by
171 	 * the Prototype Ajax.Request object.
172 	 * @param ajax Contains the response data.
173 	 * @private
174 	 */
175 	this.callbackError = function(/**Ajax.Response*/ ajax) {
176 		
177 		// display debug message
178 		NaosDebug.debug("[NaosAjax]: Request failed.\nText: "+
179 			ajax.responseText);
180 		
181 		// remove request from queue
182 		NaosAjax.finishRequest(this);
183 		
184 	}.bind(this);
185 	
186 	/**
187 	 * This function gets called by the Prototype Ajax.Request object for debugging.
188 	 * @param ajax Contains the response data.
189 	 * @private
190 	 */
191 	this.callbackDebug = function(/**Ajax.Response*/ ajax) {
192 		
193 		// display debug message
194 		NaosDebug.debug("[NaosAjax]: Request completed.\nText: "+
195 			ajax.responseText);
196 			
197 	}.bind(this);
198 	
199 	/**
200 	 * Schedules the created NaosAjax object for sending.
201 	 */
202 	this.send = function () {
203 		
204 		NaosDebug.debug("[NaosAjax]: Queuing request.");
205 		
206 		NaosAjax.activateRequest(this);
207 	}.bind(this);
208 	
209 	/**
210 	 * An internal function which actually sends the NaosAjax request. It
211 	 * utilizes the Prototype Ajax.Request class. 
212 	 * @private
213 	 */
214 	this._send = function () {
215 		// display hour glass
216 		// '<img src="'+naosConfig.application.base_url+'.resources/naos/images/spinner.gif"/>'
217 		
218 		// display debug message
219 		NaosDebug.debug("[NaosAjax]: Send request.\nURL: "+this.url+"\nXML: "+
220 			new zXMLSerializer().serializeToString(this.xmlrequest));
221 		
222 		// send request
223 		new Ajax.Request(this.url, {method: 'post', onSuccess: this.callbackOk,
224 			onFailure: this.callbackError, postBody: this.xmlrequest, contentType: 'text/xml',
225 			encoding: 'UTF-8', evalJSON: false, evalJS: false, asynchronous: true,
226 			parameters: '',	onComplete: this.callbackDebug
227 			});
228 	}.bind(this);
229 }
230 
231 /**
232  * The request queue with schedules NaosAjax request objects.
233  * @type Array
234  * @private
235  */
236 NaosAjax.requestQueue = new Array();
237 
238 /**
239  * Pointer to the last executed UI effect.
240  * @type null|Effect
241  * @private
242  */
243 NaosAjax.lastGuiEffect = null;
244 
245 /**
246  * Cancels the currently activated UI effect.
247  * @private
248  */
249 NaosAjax.cancelLastGuiEffect = function()
250 {
251 	if(NaosAjax.lastGuiEffect)
252 	{
253 		if(NaosAjax.lastGuiEffect.state == 'running')
254 			NaosAjax.lastGuiEffect.cancel();
255 		NaosAjax.lastGuiEffect = null;
256 	}
257 }
258 
259 /**
260  * Schedules a NaosAjax object by pushing it to the request queue. Activates
261  * the request at once when no pending request is available. Takes care of the
262  * UI notification stuff.
263  * @param naosajax The NaosAjax object which should be scheduled.
264  * @private
265  */
266 NaosAjax.activateRequest = function (/**NaosAjax*/ naosajax) {
267 	NaosAjax.requestQueue.push(naosajax);
268 	
269 	NaosDebug.debug("[NaosAjax]: Request queue count is: "+NaosAjax.requestQueue.length);
270 	
271 	if(NaosAjax.requestQueue.length == 1)
272 	{
273 		// display hour glass
274 		if(!document.getElementById('naos_hourglass'))
275 		{
276 			var blockingElement = document.createElement('div');
277 			blockingElement.setAttribute('id', 'naos_hourglass');
278 			blockingElement.style.display = 'none';
279 			blockingElement.style.position = 'absolute';
280 			blockingElement.style.left = 0;
281 			blockingElement.style.top = 0;
282 			blockingElement.style.width = ''+document.viewport.getDimensions().width+'px';
283 			blockingElement.style.height = ''+document.viewport.getDimensions().height+'px';
284 			//blockingElement.style.background = 'white';
285 			
286 			blockingElement.innerHTML = '<table style="width: '+blockingElement.style.width+'; height: '+blockingElement.style.height+';"><tr><td style="vertical-align: middle; text-align: center;"><img src="'+naosConfig.application.base_url+'.resources/naos/images/spinner.gif"/></td></tr></table>';
287 			
288 			document.getElementsByTagName("body")[0].appendChild(blockingElement);
289 		}
290 		
291 		NaosAjax.cancelLastGuiEffect();
292 		NaosAjax.lastGuiEffect = $('naos_hourglass').appear({ duration: 0.5 });
293 		
294 		NaosAjax.requestQueue[0]._send();
295 	}
296 }
297 
298 /**
299  * Takes care of processed NaosAjax requests. Activates the next scheduled request
300  * if available. Takes care of the UI notification stuff.
301  * @param naosajax The NaosAjax object which is processed already.
302  * @private
303  */
304 NaosAjax.finishRequest = function (/**NaosAjax*/ naosajax) {
305 	for(var i=0; i<NaosAjax.requestQueue.length; ++i)
306 	{
307 		if(NaosAjax.requestQueue[i] == naosajax)
308 		{
309 			NaosAjax.requestQueue.splice(i, 1);
310 			break;
311 		}
312 	}
313 	
314 	if(NaosAjax.requestQueue.length >= 1)
315 	{
316 		NaosAjax.requestQueue[0]._send();
317 	}
318 	else
319 	{
320 		// hide hour glass
321 		NaosAjax.cancelLastGuiEffect();
322 		NaosAjax.lastGuiEffect = $('naos_hourglass').fade({ duration: 0.1 });
323 	}
324 }
325