1 /* Licensed to the Apache Software Foundation (ASF) under one or more
  2  * contributor license agreements.  See the NOTICE file distributed with
  3  * this work for additional information regarding copyright ownership.
  4  * The ASF licenses this file to you under the Apache License, Version 2.0
  5  * (the "License"); you may not use this file except in compliance with
  6  * the License.  You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 /**
 18  * An implementation of an xhr request object
 19  * with partial page submit functionality, and jsf
 20  * ppr request and timeout handling capabilities
 21  *
 22  * Author: Werner Punz (latest modification by $Author: ganeshpuri $)
 23  * Version: $Revision: 1.4 $ $Date: 2009/05/31 09:16:44 $
 24  */
 25 
 26 /**
 27  * @class
 28  * @name _AjaxRequest
 29  * @memberOf myfaces._impl.xhrCore
 30  * @extends myfaces._impl.core.Object
 31  */
 32 _MF_CLS(_PFX_XHR + "_AjaxRequest", _MF_OBJECT, /** @lends myfaces._impl.xhrCore._AjaxRequest.prototype */ {
 33 
 34     _contentType: "application/x-www-form-urlencoded",
 35     /** source element issuing the request */
 36     _source: null,
 37     /** encoding for the submit */
 38     _encoding:null ,
 39     /** context passed down from the caller */
 40     _context:null,
 41     /** source form issuing the request */
 42     _sourceForm: null,
 43     /** passthrough parameters */
 44     _passThrough: null,
 45 
 46     /** queue control */
 47     _timeout: null,
 48     /** enqueuing delay */
 49     //_delay:null,
 50     /** queue size */
 51     _queueSize:-1,
 52 
 53     /**
 54      back reference to the xhr queue,
 55      only set if the object really is queued
 56      */
 57     _xhrQueue: null,
 58 
 59     /** pps an array of identifiers which should be part of the submit, the form is ignored */
 60     _partialIdsArray : null,
 61 
 62     /** xhr object, internal param */
 63     _xhr: null,
 64 
 65     /** predefined method */
 66     _ajaxType:"POST",
 67 
 68     //CONSTANTS
 69     ENCODED_URL:"javax.faces.encodedURL",
 70     /*
 71      * constants used internally
 72      */
 73     _CONTENT_TYPE:"Content-Type",
 74     _HEAD_FACES_REQ:"Faces-Request",
 75     _VAL_AJAX: "partial/ajax",
 76     _XHR_CONST: myfaces._impl.xhrCore.engine.XhrConst,
 77 
 78     // _exception: null,
 79     // _requestParameters: null,
 80     /**
 81      * Constructor
 82      * <p />
 83      * note there is a load of common properties
 84      * inherited by the base class which define the corner
 85      * parameters and the general internal behavior
 86      * like _onError etc...
 87      * @param {Object} args an arguments map which an override any of the given protected
 88      * instance variables, by a simple name value pair combination
 89      */
 90     constructor_: function(args) {
 91 
 92         try {
 93             this._callSuper("constructor_", args);
 94 
 95             this._onException = this._Lang.hitch(this, this._stdErrorHandler);
 96             this._onWarn = this._Lang.hitch(this, this._stdErrorHandler);
 97             this._initDefaultFinalizableFields();
 98             delete this._resettableContent["_xhrQueue"];
 99 
100             this.applyArgs(args);
101             var mfInternal = this._context._mfInternal;
102             mfInternal._onException = this._onException;
103             mfInternal._onWarning = this._onWarn;
104 
105             /*namespace remapping for readability*/
106             //we fetch in the standard arguments
107             //and apply them to our protected attributes
108             //we do not gc the entry hence it is not defined on top
109             var xhrCore = myfaces._impl.xhrCore;
110             this._AJAXUTIL = xhrCore._AjaxUtils;
111 
112         } catch (e) {
113             //_onError
114             this._onException(this._xhr, this._context, "myfaces._impl.xhrCore._AjaxRequest", "constructor", e);
115         }
116     },
117 
118     /**
119      * Sends an Ajax request
120      */
121     send : function() {
122 
123         var _Lang = this._Lang;
124 
125         try {
126 
127             var scopeThis = _Lang.hitch(this, function(functionName) {
128                 return _Lang.hitch(this, this[functionName]);
129             });
130             this._xhr = _Lang.mixMaps(this._getTransport(), {
131                 onprogress: scopeThis("onprogress"),
132                 ontimeout:  scopeThis("ontimeout"),
133                 onloadend:  scopeThis("ondone"),
134                 onload:     scopeThis("onsuccess"),
135                 onerror:    scopeThis("onerror")
136 
137             }, true);
138             var xhr = this._xhr,
139                     sourceForm = this._sourceForm,
140                     targetURL = (typeof sourceForm.elements[this.ENCODED_URL] == 'undefined') ?
141                             sourceForm.action :
142                             sourceForm.elements[this.ENCODED_URL].value,
143                     formData = this.getFormData();
144 
145             for (var key in this._passThrough) {
146                 formData.append(key, this._passThrough[key]);
147             }
148 
149             xhr.open(this._ajaxType, targetURL +
150                     ((this._ajaxType == "GET") ? "?" + this._formDataToURI(formData) : "")
151                     , true);
152 
153             xhr.timeout = this._timeout || 0;
154 
155             var contentType = this._contentType;
156             if (this._encoding) {
157                 contentType = contentType + "; charset:" + this._encoding;
158             }
159 
160             xhr.setRequestHeader(this._CONTENT_TYPE, contentType);
161             xhr.setRequestHeader(this._HEAD_FACES_REQ, this._VAL_AJAX);
162 
163             this._sendEvent("BEGIN");
164             //Check if it is a custom form data object
165             //if yes we use makefinal for the final handling
166             if (formData && formData.makeFinal) {
167                 formData = formData.makeFinal()
168             }
169             xhr.send((this._ajaxType != "GET") ? formData : null);
170 
171         } catch (e) {
172             //_onError//_onError
173             this._onException(this._xhr, this._context, "myfaces._impl.xhrCore._AjaxRequest", "send", e);
174         }
175     },
176 
177 
178     ondone: function() {
179         this._requestDone();
180     },
181 
182 
183     onsuccess: function(evt) {
184 
185         var context = this._context;
186         var xhr = this._xhr;
187         try {
188             this._sendEvent("COMPLETE");
189             //now we have to reroute into our official api
190             //because users might want to decorate it, we will split it apart afterwards
191 
192             context._mfInternal = context._mfInternal || {};
193             jsf.ajax.response((xhr.getXHRObject) ? xhr.getXHRObject() : xhr, context);
194 
195             //an error in the processing has been raised
196             //TODO move all the error callbacks from response into
197             //a thrown exception best with a message history so
198             //that we have a message trace
199             //target 2.1.5
200             if (!context._mfInternal.internalError) {
201                 this._sendEvent("SUCCESS");
202             }
203         } catch (e) {
204             this._onException(xhr, context, "myfaces._impl.xhrCore._AjaxRequest", "callback", e);
205         }
206     },
207 
208     onerror: function(evt) {
209 
210         var context = this._context;
211         var xhr = this._xhr;
212         var _Lang = this._Lang;
213 
214         var errorText = "";
215         this._sendEvent("COMPLETE");
216         try {
217             var UNKNOWN = _Lang.getMessage("UNKNOWN");
218             errorText = _Lang.getMessage("ERR_REQU_FAILED", null,
219                     (xhr.status || UNKNOWN),
220                     (xhr.statusText || UNKNOWN));
221 
222         } catch (e) {
223             errorText = _Lang.getMessage("ERR_REQ_FAILED_UNKNOWN", null);
224         } finally {
225             var _Impl = this.attr("impl");
226             _Impl.sendError(xhr, context, _Impl.HTTPERROR,
227                     _Impl.HTTPERROR, errorText);
228         }
229         //_onError
230     },
231 
232     onprogress: function(evt) {
233         //do nothing for now
234     },
235 
236     ontimeout: function(evt) {
237         try {
238             //we issue an event not an error here before killing the xhr process
239             this._sendEvent("TIMEOUT_EVENT");
240             //timeout done we process the next in the queue
241         } finally {
242             this._requestDone();
243         }
244     },
245 
246     _formDataToURI: function(formData) {
247         if (formData && formData.makeFinal) {
248             formData = formData.makeFinal()
249         }
250         return formData;
251     },
252 
253     _getTransport: function() {
254 
255         var xhr = this._RT.getXHRObject();
256         //the current xhr level2 timeout w3c spec is not implemented by the browsers yet
257         //we have to do a fallback to our custom routines
258 
259         //Chrome fails in the current builds, on our loadend, we disable the xhr
260         //level2 optimisations for now
261         //if (('undefined' == typeof this._timeout || null == this._timeout) && _Rt.getXHRLvl() >= 2) {
262         //no timeout we can skip the emulation layer
263         //    return xhr;
264         //}
265         return new myfaces._impl.xhrCore.engine.Xhr1({xhrObject: xhr});
266     },
267 
268 
269 
270     //----------------- backported from the base request --------------------------------
271     //non abstract ones
272     /**
273      * Spec. 13.3.1
274      * Collect and encode input elements.
275      * Additionally the hidden element javax.faces.ViewState
276      *
277      *
278      * @return  an element of formDataWrapper
279      * which keeps the final Send Representation of the
280      */
281     getFormData : function() {
282         var _AJAXUTIL = this._AJAXUTIL, myfacesOptions = this._context.myfaces;
283         var ret = this._Lang.createFormDataDecorator(jsf.getViewState(this._sourceForm));
284 
285         return ret;
286     },
287 
288     /**
289      * Client error handlers which also in the long run route into our error queue
290      * but also are able to deliver more meaningful messages
291      * note, in case of an error all subsequent xhr requests are dropped
292      * to get a clean state on things
293      *
294      * @param request the xhr request object
295      * @param context the context holding all values for further processing
296      * @param sourceClass (String) the issuing class for a more meaningful message
297      * @param func the issuing function
298      * @param exception the embedded exception
299      */
300     _stdErrorHandler: function(request, context, sourceClass, func, exception) {
301         context._mfInternal.internalError = true;
302         var xhrQueue = this._xhrQueue;
303         try {
304             this.attr("impl").stdErrorHandler(request, context, sourceClass, func, exception);
305         } finally {
306             if (xhrQueue) {
307                 xhrQueue.cleanup();
308             }
309         }
310     },
311 
312     _sendEvent: function(evtType) {
313         var _Impl = this.attr("impl");
314         _Impl.sendEvent(this._xhr, this._context, _Impl[evtType]);
315     },
316 
317     _requestDone: function() {
318         var queue = this._xhrQueue;
319         if (queue) {
320             queue.processQueue();
321         }
322         //ie6 helper cleanup
323         delete this._context.source;
324         this._finalize();
325     },
326 
327     //cleanup
328     _finalize: function() {
329 
330         //final cleanup to terminate everything
331         this._Lang.clearExceptionProcessed();
332 
333         if (this._xhr.readyState == this._XHR_CONST.READY_STATE_DONE) {
334             this._callSuper("_finalize");
335         }
336     }
337 });
338 
339