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  * @class
 19  * @name Impl
 20  * @memberOf myfaces._impl.core
 21  * @description Implementation singleton which implements all interface method
 22  * defined by our faces.js API
 23  * */
 24 _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.prototype */ {
 25 
 26     //third option myfaces._impl.xhrCoreAjax which will be the new core impl for now
 27     _transport:myfaces._impl.core._Runtime.getGlobalConfig("transport", myfaces._impl.xhrCore._Transports),
 28 
 29     /**
 30      * external event listener queue!
 31      */
 32     _evtListeners:new (myfaces._impl.core._Runtime.getGlobalConfig("eventListenerQueue", myfaces._impl._util._ListenerQueue))(),
 33 
 34     /**
 35      * external error listener queue!
 36      */
 37     _errListeners:new (myfaces._impl.core._Runtime.getGlobalConfig("errorListenerQueue", myfaces._impl._util._ListenerQueue))(),
 38 
 39     /*CONSTANTS*/
 40 
 41     /*internal identifiers for options*/
 42     IDENT_ALL:"@all",
 43     IDENT_NONE:"@none",
 44     IDENT_THIS:"@this",
 45     IDENT_FORM:"@form",
 46 
 47     /*
 48      * [STATIC] constants
 49      */
 50 
 51     P_PARTIAL_SOURCE:"jakarta.faces.source",
 52     P_VIEWSTATE:"jakarta.faces.ViewState",
 53     P_CLIENTWINDOW:"jakarta.faces.ClientWindow",
 54     P_AJAX:"jakarta.faces.partial.ajax",
 55     P_EXECUTE:"jakarta.faces.partial.execute",
 56     P_RENDER:"jakarta.faces.partial.render",
 57     P_EVT:"jakarta.faces.partial.event",
 58     P_WINDOW_ID:"jakarta.faces.ClientWindow",
 59     P_RESET_VALUES:"jakarta.faces.partial.resetValues",
 60 
 61     /* message types */
 62     ERROR:"error",
 63     EVENT:"event",
 64 
 65     /* event emitting stages */
 66     BEGIN:"begin",
 67     COMPLETE:"complete",
 68     SUCCESS:"success",
 69 
 70     /*ajax errors spec 14.4.2*/
 71     HTTPERROR:"httpError",
 72     EMPTY_RESPONSE:"emptyResponse",
 73     MALFORMEDXML:"malformedXML",
 74     SERVER_ERROR:"serverError",
 75     CLIENT_ERROR:"clientError",
 76     TIMEOUT_EVENT:"timeout",
 77 
 78     /*error reporting threshold*/
 79     _threshold:"ERROR",
 80 
 81     /*blockfilter for the passthrough filtering, the attributes given here
 82      * will not be transmitted from the options into the passthrough*/
 83     _BLOCKFILTER:{onerror:1, onevent:1, render:1, execute:1, myfaces:1, delay:1, resetValues:1, params: 1},
 84 
 85     /**
 86      * collect and encode data for a given form element (must be of type form)
 87      * find the jakarta.faces.ViewState element and encode its value as well!
 88      * return a concatenated string of the encoded values!
 89      *
 90      * @throws Error in case of the given element not being of type form!
 91      * https://issues.apache.org/jira/browse/MYFACES-2110
 92      */
 93     getViewState:function (form) {
 94         /**
 95          *  typecheck assert!, we opt for strong typing here
 96          *  because it makes it easier to detect bugs
 97          */
 98         if (form) {
 99             form = this._Lang.byId(form);
100         }
101 
102         if (!form
103             || !form.nodeName
104             || form.nodeName.toLowerCase() != "form") {
105             throw new Error(this._Lang.getMessage("ERR_VIEWSTATE"));
106         }
107 
108         var ajaxUtils = myfaces._impl.xhrCore._AjaxUtils;
109 
110         var ret = this._Lang.createFormDataDecorator([]);
111         ajaxUtils.encodeSubmittableFields(ret, form, null);
112 
113         return ret.makeFinal();
114     },
115 
116     /**
117      * this function has to send the ajax requests
118      *
119      * following request conditions must be met:
120      * <ul>
121      *  <li> the request must be sent asynchronously! </li>
122      *  <li> the request must be a POST!!! request </li>
123      *  <li> the request url must be the form action attribute </li>
124      *  <li> all requests must be queued with a client side request queue to ensure the request ordering!</li>
125      * </ul>
126      *
127      * @param {String|Node} elem any dom element no matter being it html or faces, from which the event is emitted
128      * @param {|Event|} event any javascript event supported by that object
129      * @param {|Object|} options  map of options being pushed into the ajax cycle
130      *
131      *
132      * a) transformArguments out of the function
133      * b) passThrough handling with a map copy with a filter map block map
134      */
135     request:function (elem, event, options) {
136         if (this._delayTimeout) {
137             clearTimeout(this._delayTimeout);
138             delete this._delayTimeout;
139         }
140         /*namespace remap for our local function context we mix the entire function namespace into
141          *a local function variable so that we do not have to write the entire namespace
142          *all the time
143          **/
144         var _Lang = this._Lang,
145             _Dom = this._Dom;
146         /*assert if the onerror is set and once if it is set it must be of type function*/
147         _Lang.assertType(options.onerror, "function");
148         /*assert if the onevent is set and once if it is set it must be of type function*/
149         _Lang.assertType(options.onevent, "function");
150 
151         //options not set we define a default one with nothing
152         options = options || {};
153 
154         /**
155          * we cross reference statically hence the mapping here
156          * the entire mapping between the functions is stateless
157          */
158         //null definitely means no event passed down so we skip the ie specific checks
159         if ('undefined' == typeof event) {
160             event = window.event || null;
161         }
162 
163         //improve the error messages if an empty elem is passed
164         if (!elem) {
165             throw _Lang.makeException(new Error(), "ArgNotSet", null, this._nameSpace, "request", _Lang.getMessage("ERR_MUST_BE_PROVIDED1", "{0}: source  must be provided", "faces.ajax.request", "source element id"));
166         }
167         var oldElem = elem;
168         elem = _Dom.byIdOrName(elem);
169         if (!elem) {
170             throw _Lang.makeException(new Error(), "Notfound", null, this._nameSpace, "request", _Lang.getMessage("ERR_PPR_UNKNOWNCID", "{0}: Node with id {1} could not be found from source", this._nameSpace + ".request", oldElem));
171         }
172 
173         var elementId = _Dom.nodeIdOrName(elem);
174 
175         /*
176          * We make a copy of our options because
177          * we should not touch the incoming params!
178          * this copy is also the pass through parameters
179          * which are sent down our request
180          */
181         // this is legacy behavior which is faulty, will be removed if we decide to do it
182         // that way
183         var passThrgh = _Lang.mixMaps({}, options, true, this._BLOCKFILTER);
184         // jsdoc spec everything under params must be passed through
185         if(options.params)  {
186             passThrgh = _Lang.mixMaps(passThrgh, options.params, true, {});
187         }
188 
189         if (event) {
190             passThrgh[this.P_EVT] = event.type;
191         }
192 
193         /**
194          * ajax pass through context with the source
195          * onevent and onerror
196          */
197         var context = {
198             source:elem,
199             onevent:options.onevent,
200             onerror:options.onerror,
201             viewId: "",
202             //TODO move the myfaces part into the _mfInternal part
203             myfaces:options.myfaces,
204             _mfInternal:{}
205         };
206         //additional meta information to speed things up, note internal non faces
207         //pass through options are stored under _mfInternal in the context
208         var mfInternal = context._mfInternal;
209 
210         /**
211          * fetch the parent form
212          *
213          * note we also add an override possibility here
214          * so that people can use dummy forms and work
215          * with detached objects
216          */
217         var form = (options.myfaces && options.myfaces.form) ?
218             _Lang.byId(options.myfaces.form) :
219             this._getForm(elem, event);
220 
221         context.viewId = this.getViewId(form);
222 
223         /**
224          * faces2.2 client window must be part of the issuing form so it is encoded
225          * automatically in the request
226          */
227         //we set the client window before encoding by a call to faces.getClientWindow
228         var clientWindow = faces.getClientWindow(form);
229         //in case someone decorates the getClientWindow we reset the value from
230         //what we are getting
231         if ('undefined' != typeof clientWindow && null != clientWindow) {
232             var formElem = _Dom.getNamedElementFromForm(form, this.P_CLIENTWINDOW);
233             if (formElem) {
234                 //we store the value for later processing during the ajax phase
235                 //job so that we do not get double values
236                 context._mfInternal._clientWindow = faces.getClientWindow(form);
237             } else {
238                 passThrgh[this.P_CLIENTWINDOW] = faces.getClientWindow(form);
239             }
240         } /*  spec proposal
241         else {
242             var formElem = _Dom.getNamedElementFromForm(form, this.P_CLIENTWINDOW);
243             if (formElem) {
244                 context._mfInternal._clientWindow = "undefined";
245             } else {
246                 passThrgh[this.P_CLIENTWINDOW] = "undefined";
247             }
248         }
249         */
250 
251         /**
252          * binding contract the jakarta.faces.source must be set
253          */
254         passThrgh[this.P_PARTIAL_SOURCE] = elementId;
255 
256         /**
257          * jakarta.faces.partial.ajax must be set to true
258          */
259         passThrgh[this.P_AJAX] = true;
260 
261         /**
262          * if resetValues is set to true
263          * then we have to set jakarta.faces.resetValues as well
264          * as pass through parameter
265          * the value has to be explicitly true, according to
266          * the specs jsdoc
267          */
268         if(options.resetValues === true) {
269             passThrgh[this.P_RESET_VALUES] = true;
270         }
271 
272         if (options.execute) {
273             /*the options must be a blank delimited list of strings*/
274             /*compliance with Mojarra which automatically adds @this to an execute
275              * the spec rev 2.0a however states, if none is issued nothing at all should be sent down
276              */
277             options.execute = (options.execute.indexOf("@this") == -1) ? options.execute : options.execute;
278 
279             this._transformList(passThrgh, this.P_EXECUTE, options.execute, form, elementId, context.viewId);
280         } else {
281             passThrgh[this.P_EXECUTE] = elementId;
282         }
283 
284         if (options.render) {
285             this._transformList(passThrgh, this.P_RENDER, options.render, form, elementId, context.viewId);
286         }
287 
288         /**
289          * multiple transports upcoming faces 2.x feature currently allowed
290          * default (no value) xhrQueuedPost
291          *
292          * xhrQueuedPost
293          * xhrPost
294          * xhrGet
295          * xhrQueuedGet
296          * iframePost
297          * iframeQueuedPost
298          *
299          */
300         var transportType = this._getTransportType(context, passThrgh, form);
301 
302         mfInternal["_mfSourceFormId"] = form.id;
303         mfInternal["_mfSourceControlId"] = elementId;
304         mfInternal["_mfTransportType"] = transportType;
305 
306         //mojarra compatibility, mojarra is sending the form id as well
307         //this is not documented behavior but can be determined by running
308         //mojarra under blackbox conditions
309         //i assume it does the same as our formId_submit=1 so leaving it out
310         //won´t hurt but for the sake of compatibility we are going to add it
311         passThrgh[form.id] = form.id;
312 
313         /* faces2.2 only: options.delay || */
314         var delayTimeout = options.delay || this._RT.getLocalOrGlobalConfig(context, "delay", false);
315 
316         if (!!delayTimeout) {
317             if(!(delayTimeout >= 0)) {
318                 // abbreviation which covers all cases of non positive values,
319                 // including NaN and non-numeric strings, no type equality is deliberate here,
320                 throw new Error("Invalid delay value: " + delayTimeout);
321             }
322             if (this._delayTimeout) {
323                 clearTimeout(this._delayTimeout);
324             }
325             this._delayTimeout = setTimeout(_Lang.hitch(this, function () {
326                 this._transport[transportType](elem, form, context, passThrgh);
327                 this._delayTimeout = null;
328             }), parseInt(delayTimeout));
329         } else {
330             this._transport[transportType](elem, form, context, passThrgh);
331         }
332     },
333 
334     /**
335      * fetches the form in an unprecise manner depending
336      * on an element or event target
337      *
338      * @param elem
339      * @param event
340      */
341     _getForm:function (elem, event) {
342         var _Dom = this._Dom;
343         var _Lang = this._Lang;
344         var form = _Dom.fuzzyFormDetection(elem);
345 
346         if (!form && event) {
347             //in case of no form is given we retry over the issuing event
348             form = _Dom.fuzzyFormDetection(_Lang.getEventTarget(event));
349             if (!form) {
350                 throw _Lang.makeException(new Error(), null, null, this._nameSpace, "_getForm", _Lang.getMessage("ERR_FORM"));
351             }
352         } else if (!form) {
353             throw _Lang.makeException(new Error(), null, null, this._nameSpace, "_getForm", _Lang.getMessage("ERR_FORM"));
354 
355         }
356         return form;
357     },
358 
359     /**
360      * determines the transport type to be called
361      * for the ajax call
362      *
363      * @param context the context
364      * @param passThrgh  pass through values
365      * @param form the form which issues the request
366      */
367     _getTransportType:function (context, passThrgh, form) {
368         /**
369          * if execute or render exist
370          * we have to pass them down as a blank delimited string representation
371          * of an array of ids!
372          */
373         //for now we turn off the transport auto selection, to enable 2.0 backwards compatibility
374         //on protocol level, the file upload only can be turned on if the auto selection is set to true
375         var getConfig = this._RT.getLocalOrGlobalConfig,
376             _Lang = this._Lang,
377             _Dom = this._Dom;
378 
379         var transportAutoSelection = getConfig(context, "transportAutoSelection", true);
380         /*var isMultipart = (transportAutoSelection && _Dom.getAttribute(form, "enctype") == "multipart/form-data") ?
381          _Dom.isMultipartCandidate((!getConfig(context, "pps",false))? form : passThrgh[this.P_EXECUTE]) :
382          false;
383          **/
384         if (!transportAutoSelection) {
385             return getConfig(context, "transportType", "xhrQueuedPost");
386         }
387         var multiPartCandidate = _Dom.isMultipartCandidate((!getConfig(context, "pps", false)) ?
388             form : passThrgh[this.P_EXECUTE]);
389         var multipartForm = (_Dom.getAttribute(form, "enctype") || "").toLowerCase() == "multipart/form-data";
390         //spec section jsdoc, if we have a multipart candidate in our execute (aka fileupload)
391         //and the form is not multipart then we have to raise an error
392         if (multiPartCandidate && !multipartForm) {
393             throw _Lang.makeException(new Error(), null, null, this._nameSpace, "_getTransportType", _Lang.getMessage("ERR_NO_MULTIPART_FORM", "No Multipart form", form.id));
394         }
395         var isMultipart = multiPartCandidate && multipartForm;
396         /**
397          * multiple transports upcoming faces 2.2 feature currently allowed
398          * default (no value) xhrQueuedPost
399          *
400          * xhrQueuedPost
401          * xhrPost
402          * xhrGet
403          * xhrQueuedGet
404          * iframePost
405          * iframeQueuedPost
406          *
407          */
408         var transportType = (!isMultipart) ?
409             getConfig(context, "transportType", "xhrQueuedPost") :
410             getConfig(context, "transportType", "multipartQueuedPost");
411         if (!this._transport[transportType]) {
412             //throw new Error("Transport type " + transportType + " does not exist");
413             throw new Error(_Lang.getMessage("ERR_TRANSPORT", null, transportType));
414         }
415         return transportType;
416 
417     },
418 
419     /**
420      * transforms the list to the expected one
421      * with the proper none all form and this handling
422      * (note we also could use a simple string replace but then
423      * we would have had double entries under some circumstances)
424      *
425      * @param passThrgh
426      * @param target
427      * @param srcStr
428      * @param form
429      * @param elementId
430      * @param namingContainerId the naming container namingContainerId
431      */
432     _transformList:function (passThrgh, target, srcStr, form, elementId, namingContainerId) {
433         var _Lang = this._Lang;
434         //this is probably the fastest transformation method
435         //it uses an array and an index to position all elements correctly
436         //the offset variable is there to prevent 0 which results in a javascript
437         //false
438         srcStr = this._Lang.trim(srcStr);
439         var offset = 1,
440             vals = (srcStr) ? srcStr.split(/\s+/) : [],
441             idIdx = (vals.length) ? _Lang.arrToMap(vals, offset) : {},
442 
443             //helpers to improve speed and compression
444             none = idIdx[this.IDENT_NONE],
445             all = idIdx[this.IDENT_ALL],
446             theThis = idIdx[this.IDENT_THIS],
447             theForm = idIdx[this.IDENT_FORM];
448 
449         if (none) {
450             //in case of none nothing is returned
451             if ('undefined' != typeof passThrgh.target) {
452                 delete passThrgh.target;
453             }
454             return passThrgh;
455         }
456         if (all) {
457             //in case of all only one value is returned
458             passThrgh[target] = this.IDENT_ALL;
459             return passThrgh;
460         }
461 
462         if (theForm) {
463             //the form is replaced with the proper id but the other
464             //values are not touched
465             vals[theForm - offset] = form.id;
466         }
467         if (theThis && !idIdx[elementId]) {
468             //in case of this, the element id is set
469             vals[theThis - offset] = elementId;
470         }
471 
472         //the final list must be blank separated
473         passThrgh[target] = this._remapNamingContainer(elementId, form, namingContainerId,vals).join(" ");
474         return passThrgh;
475     },
476 
477     /**
478      * in namespaced situations root naming containers must be resolved
479      * ":" absolute searches must be mapped accordingly, the same
480      * goes for absolut searches containing already the root naming container id
481      *
482      * @param issuingElementId the issuing element id
483      * @param form the hosting form of the issiung element id
484      * @param rootNamingContainerId the root naming container id
485      * @param elements a list of element client ids to be processed
486      * @returns {*} the mapped element client ids, which are resolved correctly to their naming containers
487      * @private
488      */
489     _remapNamingContainer: function(issuingElementId, form, rootNamingContainerId, elements) {
490         var SEP = faces.separatorchar;
491         function remapViewId(toTransform) {
492             var EMPTY_STR = "";
493             var rootNamingContainerPrefix = (rootNamingContainerId.length) ? rootNamingContainerId+SEP : EMPTY_STR;
494             var formClientId = form.id;
495             // nearest parent naming container relative to the form
496             var nearestNamingContainer = formClientId.substring(0, formClientId.lastIndexOf(SEP));
497             var nearestNamingContainerPrefix = (nearestNamingContainer.length) ? nearestNamingContainer + SEP : EMPTY_STR;
498             // absolut search expression, always starts with SEP or the name of the root naming container
499             var hasLeadingSep = toTransform.indexOf(SEP) === 0;
500             var isAbsolutSearchExpr = hasLeadingSep || (rootNamingContainerId.length
501                 && toTransform.indexOf(rootNamingContainerPrefix) == 0);
502             if (isAbsolutSearchExpr) {
503                 //we cut off the leading sep if there is one
504                 toTransform = hasLeadingSep ? toTransform.substring(1) : toTransform;
505                 toTransform = toTransform.indexOf(rootNamingContainerPrefix) == 0 ? toTransform.substring(rootNamingContainerPrefix.length) : toTransform;
506                 //now we prepend either the prefix or "" from the cut-off string to get the final result
507                 return  [rootNamingContainerPrefix, toTransform].join(EMPTY_STR);
508             } else { //relative search according to the javadoc
509                 //we cut off the root naming container id from the form
510                 if (formClientId.indexOf(rootNamingContainerPrefix) == 0) {
511                     formClientId = formClientId.substring(rootNamingContainerPrefix.length);
512                 }
513 
514                 //If prependId = true, the outer form id must be present in the id if same form
515                 var hasPrependId = toTransform.indexOf(formClientId) == 0;
516 
517                 return hasPrependId ?
518                     [rootNamingContainerPrefix, toTransform].join(EMPTY_STR) :
519                     [nearestNamingContainerPrefix, toTransform].join(EMPTY_STR);
520             }
521         }
522 
523         for(var cnt = 0; cnt < elements.length; cnt++) {
524             elements[cnt] = remapViewId(this._Lang.trim(elements[cnt]));
525         }
526 
527         return elements;
528     },
529 
530     addOnError:function (/*function*/errorListener) {
531         /*error handling already done in the assert of the queue*/
532         this._errListeners.enqueue(errorListener);
533     },
534 
535     addOnEvent:function (/*function*/eventListener) {
536         /*error handling already done in the assert of the queue*/
537         this._evtListeners.enqueue(eventListener);
538     },
539 
540     /**
541      * implementation triggering the error chain
542      *
543      * @param {Object} request the request object which comes from the xhr cycle
544      * @param {Object} context (Map) the context object being pushed over the xhr cycle keeping additional metadata
545      * @param {String} name the error name
546      * @param {String} errorName the server error name in case of a server error
547      * @param {String} errorMessage the server error message in case of a server error
548      * @param {String} caller optional caller reference for extended error messages
549      * @param {String} callFunc optional caller Function reference for extended error messages
550      *
551      *  handles the errors, in case of an onError exists within the context the onError is called as local error handler
552      *  the registered error handlers in the queue receiv an error message to be dealt with
553      *  and if the projectStage is at development an alert box is displayed
554      *
555      *  note: we have additional functionality here, via the global config myfaces.config.defaultErrorOutput a function can be provided
556      *  which changes the default output behavior from alert to something else
557      *
558      *
559      */
560     sendError:function sendError(/*Object*/request, /*Object*/ context, /*String*/ name, /*String*/ errorName, /*String*/ errorMessage, caller, callFunc) {
561         var _Lang = myfaces._impl._util._Lang;
562         var UNKNOWN = _Lang.getMessage("UNKNOWN");
563 
564         var eventData = {};
565         //we keep this in a closure because we might reuse it for our errorMessage
566         var malFormedMessage = function () {
567             return (name && name === myfaces._impl.core.Impl.MALFORMEDXML) ? _Lang.getMessage("ERR_MALFORMEDXML") : "";
568         };
569 
570         //by setting unknown values to unknown we can handle cases
571         //better where a simulated context is pushed into the system
572         eventData.type = this.ERROR;
573 
574         eventData.status = name || UNKNOWN;
575         eventData.errorName = errorName || UNKNOWN;
576         eventData.errorMessage = errorMessage || UNKNOWN;
577 
578         try {
579             eventData.source = context.source || UNKNOWN;
580             eventData.responseCode = request.status || UNKNOWN;
581             eventData.responseText = request.responseText || UNKNOWN;
582             eventData.responseXML = request.responseXML || UNKNOWN;
583         } catch (e) {
584             // silently ignore: user can find out by examining the event data
585         }
586         //extended error message only in dev mode
587         if (faces.getProjectStage() === "Development") {
588             eventData.errorMessage = eventData.errorMessage || "";
589             eventData.errorMessage = (caller) ? eventData.errorMessage + "\nCalling class: " + caller : eventData.errorMessage;
590             eventData.errorMessage = (callFunc) ? eventData.errorMessage + "\n Calling function: " + callFunc : eventData.errorMessage;
591         }
592 
593         /**/
594         if (context["onerror"]) {
595             context.onerror(eventData);
596         }
597 
598         /*now we serve the queue as well*/
599         this._errListeners.broadcastEvent(eventData);
600 
601         if (faces.getProjectStage() === "Development" && this._errListeners.length() == 0 && !context["onerror"]) {
602             var DIVIDER = "--------------------------------------------------------",
603                 defaultErrorOutput = myfaces._impl.core._Runtime.getGlobalConfig("defaultErrorOutput", alert),
604                 finalMessage = [],
605                 //we remap the function to achieve a better compressability
606                 pushMsg = _Lang.hitch(finalMessage, finalMessage.push);
607 
608             (errorMessage) ? pushMsg(_Lang.getMessage("MSG_ERROR_MESSAGE") + " " + errorMessage + "\n") : null;
609 
610             pushMsg(DIVIDER);
611 
612             (caller) ? pushMsg("Calling class:" + caller) : null;
613             (callFunc) ? pushMsg("Calling function:" + callFunc) : null;
614             (name) ? pushMsg(_Lang.getMessage("MSG_ERROR_NAME") + " " + name) : null;
615             (errorName && name != errorName) ? pushMsg("Server error name: " + errorName) : null;
616 
617             pushMsg(malFormedMessage());
618             pushMsg(DIVIDER);
619             pushMsg(_Lang.getMessage("MSG_DEV_MODE"));
620             defaultErrorOutput(finalMessage.join("\n"));
621         }
622     },
623 
624     /**
625      * sends an event
626      */
627     sendEvent:function sendEvent(/*Object*/request, /*Object*/ context, /*event name*/ name) {
628         var _Lang = myfaces._impl._util._Lang;
629         var eventData = {};
630         var UNKNOWN = _Lang.getMessage("UNKNOWN");
631 
632         eventData.type = this.EVENT;
633 
634         eventData.status = name;
635         eventData.source = context.source;
636 
637         if (name !== this.BEGIN) {
638 
639             try {
640                 //we bypass a problem with ie here, ie throws an exception if no status is given on the xhr object instead of just passing a value
641                 var getValue = function (value, key) {
642                     try {
643                         return value[key]
644                     } catch (e) {
645                         return UNKNOWN;
646                     }
647                 };
648 
649                 eventData.responseCode = getValue(request, "status");
650                 eventData.responseText = getValue(request, "responseText");
651                 eventData.responseXML = getValue(request, "responseXML");
652 
653             } catch (e) {
654                 var impl = myfaces._impl.core._Runtime.getGlobalConfig("facesAjaxImpl", myfaces._impl.core.Impl);
655                 impl.sendError(request, context, this.CLIENT_ERROR, "ErrorRetrievingResponse",
656                     _Lang.getMessage("ERR_CONSTRUCT", e.toString()));
657 
658                 //client errors are not swallowed
659                 throw e;
660             }
661 
662         }
663 
664         /**/
665         if (context.onevent) {
666             /*calling null to preserve the original scope*/
667             context.onevent.call(null, eventData);
668         }
669 
670         /*now we serve the queue as well*/
671         this._evtListeners.broadcastEvent(eventData);
672     },
673 
674     /**
675      * Spec. 13.3.3
676      * Examining the response markup and updating the DOM tree
677      * @param {XMLHttpRequest} request - the ajax request
678      * @param {Object} context - the ajax context
679      */
680     response:function (request, context) {
681         this._RT.getLocalOrGlobalConfig(context, "responseHandler", myfaces._impl.xhrCore._AjaxResponse).processResponse(request, context);
682     },
683 
684     /**
685      * fetches the separator char from the given script tags
686      *
687      * @return {char} the separator char for the given script tags
688      */
689     getSeparatorChar:function () {
690         if (this._separator) {
691             return this.separatorchar;
692         }
693         var SEPARATOR_CHAR = "separatorchar",
694             found = false,
695             getConfig = myfaces._impl.core._Runtime.getGlobalConfig,
696             scriptTags = document.getElementsByTagName("script");
697         for (var i = 0; i < scriptTags.length && !found; i++) {
698             if (scriptTags[i] && scriptTags[i] && scriptTags[i].src.search(/\/jakarta\.faces\.resource.*\/faces\.js.*separator/) != -1) {
699                 found = true;
700                 var result = scriptTags[i].src.match(/separator=([^&;]*)/);
701                 this._separator = decodeURIComponent(result[1]);
702             }
703         }
704         this._separator = getConfig(SEPARATOR_CHAR, this._separator || ":");
705         return this._separator;
706     },
707 
708     /**
709      * @return the project stage also emitted by the server:
710      * it cannot be cached and must be delivered over the server
711      * The value for it comes from the request parameter of the faces.js script called "stage".
712      */
713     getProjectStage:function () {
714         //since impl is a singleton we only have to do it once at first access
715 
716         if (!this._projectStage) {
717             var PRJ_STAGE = "projectStage",
718                 STG_PROD = "Production",
719 
720                 scriptTags = document.getElementsByTagName("script"),
721                 getConfig = myfaces._impl.core._Runtime.getGlobalConfig,
722                 projectStage = null,
723                 found = false,
724                 allowedProjectStages = {STG_PROD:1, "Development":1, "SystemTest":1, "UnitTest":1};
725 
726             /* run through all script tags and try to find the one that includes faces.js */
727             for (var i = 0; i < scriptTags.length && !found; i++) {
728                 if (scriptTags[i] && scriptTags[i] && scriptTags[i].src.search(/\/jakarta\.faces\.resource\/faces\.js.*ln=jakarta\.faces/) != -1) {
729                     var result = scriptTags[i].src.match(/stage=([^&;]*)/);
730                     found = true;
731                     if (result) {
732                         // we found stage=XXX
733                         // return only valid values of ProjectStage
734                         projectStage = (allowedProjectStages[result[1]]) ? result[1] : null;
735 
736                     }
737                     else {
738                         //we found the script, but there was no stage parameter -- Production
739                         //(we also add an override here for testing purposes, the default, however is Production)
740                         projectStage = getConfig(PRJ_STAGE, STG_PROD);
741                     }
742                 }
743             }
744             /* we could not find anything valid --> return the default value */
745             this._projectStage = getConfig(PRJ_STAGE, projectStage || STG_PROD);
746         }
747         return this._projectStage;
748     },
749 
750     /**
751      * implementation of the external chain function
752      * moved into the impl
753      *
754      *  @param {Object} source the source which also becomes
755      * the scope for the calling function (unspecified side behavior)
756      * the spec states here that the source can be any arbitrary code block.
757      * Which means it either is a javascript function directly passed or a code block
758      * which has to be evaluated separately.
759      *
760      * After revisiting the code additional testing against components showed that
761      * the this parameter is only targeted at the component triggering the eval
762      * (event) if a string code block is passed. This is behavior we have to resemble
763      * in our function here as well, I guess.
764      *
765      * @param {Event} event the event object being passed down into the the chain as event origin
766      *   the spec is contradicting here, it on one hand defines event, and on the other
767      *   it says it is optional, after asking, it meant that event must be passed down
768      *   but can be undefined
769      */
770     chain:function (source, event) {
771         var len = arguments.length;
772         var _Lang = this._Lang;
773         var throwErr = function (msgKey) {
774             throw Error("faces.util.chain: " + _Lang.getMessage(msgKey));
775         };
776         /**
777          * generic error condition checker which raises
778          * an exception if the condition is met
779          * @param assertion
780          * @param message
781          */
782         var errorCondition = function (assertion, message) {
783             if (assertion === true) throwErr(message);
784         };
785         var FUNC = 'function';
786         var ISSTR = _Lang.isString;
787 
788         //the spec is contradicting here, it on one hand defines event, and on the other
789         //it says it is optional, I have cleared this up now
790         //the spec meant the param must be passed down, but can be 'undefined'
791 
792         errorCondition(len < 2, "ERR_EV_OR_UNKNOWN");
793         errorCondition(len < 3 && (FUNC == typeof event || ISSTR(event)), "ERR_EVT_PASS");
794         if (len < 3) {
795             //nothing to be done here, move along
796             return true;
797         }
798         //now we fetch from what is given from the parameter list
799         //we cannot work with splice here in any performant way so we do it the hard way
800         //arguments only are give if not set to undefined even null values!
801 
802         //assertions source either null or set as dom element:
803         errorCondition('undefined' == typeof source, "ERR_SOURCE_DEF_NULL");
804         errorCondition(FUNC == typeof source, "ERR_SOURCE_FUNC");
805         errorCondition(ISSTR(source), "ERR_SOURCE_NOSTR");
806 
807         //assertion if event is a function or a string we already are in our function elements
808         //since event either is undefined, null or a valid event object
809         errorCondition(FUNC == typeof event || ISSTR(event), "ERR_EV_OR_UNKNOWN");
810 
811         for (var cnt = 2; cnt < len; cnt++) {
812             //we do not change the scope of the incoming functions
813             //but we reuse the argument array capabilities of apply
814             var ret;
815 
816             if (FUNC == typeof arguments[cnt]) {
817                 ret = arguments[cnt].call(source, event);
818             } else {
819                 //either a function or a string can be passed in case of a string we have to wrap it into another function
820                 ret = new Function("event", arguments[cnt]).call(source, event);
821             }
822             //now if one function returns false in between we stop the execution of the cycle
823             //here, note we do a strong comparison here to avoid constructs like 'false' or null triggering
824             if (ret === false /*undefined check implicitly done here by using a strong compare*/) {
825                 return false;
826             }
827         }
828         return true;
829     },
830 
831     /**
832      * error handler behavior called internally
833      * and only into the impl it takes care of the
834      * internal message transformation to a myfaces internal error
835      * and then uses the standard send error mechanisms
836      * also a double error logging prevention is done as well
837      *
838      * @param request the request currently being processed
839      * @param context the context affected by this error
840      * @param exception the exception being thrown
841      */
842     stdErrorHandler:function (request, context, exception) {
843         //newer browsers do not allow to hold additional values on native objects like exceptions
844         //we hence capsule it into the request, which is gced automatically
845         //on ie as well, since the stdErrorHandler usually is called between requests
846         //this is a valid approach
847         if (this._threshold == "ERROR") {
848             var mfInternal = exception._mfInternal || {};
849 
850             var finalMsg = [];
851             finalMsg.push(exception.message);
852             this.sendError(request, context,
853                 mfInternal.title || this.CLIENT_ERROR, mfInternal.name || exception.name, finalMsg.join("\n"), mfInternal.caller, mfInternal.callFunc);
854         }
855     },
856 
857     /**
858      * @return the client window id of the current window, if one is given
859      */
860     getClientWindow:function (node) {
861         var fetchWindowIdFromForms = this._Lang.hitch(this, function (forms) {
862             var result_idx = {};
863             var result;
864             var foundCnt = 0;
865             for (var cnt = forms.length - 1; cnt >= 0; cnt--) {
866 
867                 var currentForm = forms[cnt];
868                 var winIdElement = this._Dom.getNamedElementFromForm(currentForm, this.P_WINDOW_ID);
869                 var windowId = (winIdElement) ? winIdElement.value : null;
870 
871                 if (windowId) {
872                     if (foundCnt > 0 && "undefined" == typeof result_idx[windowId]) throw Error("Multiple different windowIds found in document");
873                     result = windowId;
874                     result_idx[windowId] = true;
875                     foundCnt++;
876                 }
877             }
878             return result;
879         });
880 
881         var fetchWindowIdFromURL = function () {
882             var href = window.location.href, windowId = "jfwid";
883             var regex = new RegExp("[\\?&]" + windowId + "=([^&#\\;]*)");
884             var results = regex.exec(href);
885             //initial trial over the url and a regexp
886             if (results != null) return results[1];
887             return null;
888         };
889 
890         //byId ($)
891         var finalNode = (node) ? this._Dom.byId(node) : document.body;
892 
893         var forms = this._Dom.findByTagName(finalNode, "form");
894         var result = fetchWindowIdFromForms(forms);
895         return (null != result) ? result : fetchWindowIdFromURL();
896     },
897 
898     /**
899      * returns the view id from an incoming form
900      * crossport from new codebase
901      * @param form
902      */
903     getViewId: function (form) {
904         var _t = this;
905         var foundViewStates = this._Dom.findAll(form, function(node) {
906             return node.tagName === "INPUT" && node.type === "hidden" && (node.name || "").indexOf(_t.P_VIEWSTATE) !== -1
907         }, true);
908         if(!foundViewStates.length) {
909             return "";
910         }
911         var viewId =  foundViewStates[0].id.split(faces.separatorchar, 2)[0];
912         var viewStateViewId = viewId.indexOf(this.P_VIEWSTATE) === -1 ? viewId : "";
913         // myfaces specific, we in non portlet environments prepend the viewId
914         // even without being in a naming container, the other components ignore that
915         return form.id.indexOf(viewStateViewId) === 0 ? viewStateViewId : "";
916     }
917 });
918 
919 
920