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