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 _AjaxResponse
 20  * @memberOf myfaces._impl.xhrCore
 21  * @extends myfaces._impl.core.Object
 22  * @description
 23  * This singleton is responsible for handling the standardized xml ajax response
 24  * Note: since the semantic processing can be handled about 90% in a functional
 25  * style we make this class stateless. Every state information is stored
 26  * temporarily in the context.
 27  *
 28  * The singleton approach also improves performance
 29  * due to less object gc compared to the old instance approach.
 30  *
 31  */
 32 _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhrCore._AjaxResponse.prototype */ {
 33 
 34     /*partial response types*/
 35     RESP_PARTIAL: "partial-response",
 36     RESP_TYPE_ERROR: "error",
 37     RESP_TYPE_REDIRECT: "redirect",
 38     RESP_TYPE_CHANGES: "changes",
 39 
 40     /*partial commands*/
 41     CMD_CHANGES: "changes",
 42     CMD_UPDATE: "update",
 43     CMD_DELETE: "delete",
 44     CMD_INSERT: "insert",
 45     CMD_EVAL: "eval",
 46     CMD_ERROR: "error",
 47     CMD_ATTRIBUTES: "attributes",
 48     CMD_EXTENSION: "extension",
 49     CMD_REDIRECT: "redirect",
 50 
 51     /*other constants*/
 52     P_VIEWSTATE: "javax.faces.ViewState",
 53     P_CLIENTWINDOW: "javax.faces.ClientWindow",
 54     P_VIEWROOT: "javax.faces.ViewRoot",
 55     P_VIEWHEAD: "javax.faces.ViewHead",
 56     P_VIEWBODY: "javax.faces.ViewBody",
 57     P_RESOURCE: "javax.faces.Resource",
 58 
 59     /**
 60      * uses response to start Html element replacement
 61      *
 62      * @param {Object} request (xhrRequest) - xhr request object
 63      * @param {Object} context (Map) - AJAX context
 64      *
 65      * A special handling has to be added to the update cycle
 66      * according to the JSDoc specs if the CDATA block contains html tags the outer rim must be stripped
 67      * if the CDATA block contains a head section the document head must be replaced
 68      * and if the CDATA block contains a body section the document body must be replaced!
 69      *
 70      */
 71     processResponse: function (request, context) {
 72         //mfinternal handling, note, the mfinternal is only optional
 73         //according to the spec
 74         context._mfInternal = context._mfInternal || {};
 75         var mfInternal = context._mfInternal;
 76 
 77         //the temporary data is hosted here
 78         mfInternal._updateElems = [];
 79         mfInternal._updateForms = [];
 80         mfInternal.appliedViewState = null;
 81         mfInternal.appliedClientWindow = null;
 82         mfInternal.namingModeId = null;
 83 
 84 
 85         try {
 86             var _Impl = this.attr("impl"), _Lang = this._Lang;
 87             // TODO:
 88             // Solution from
 89             // http://www.codingforums.com/archive/index.php/t-47018.html
 90             // to solve IE error 1072896658 when a Java server sends iso88591
 91             // istead of ISO-8859-1
 92 
 93             if (!request || !_Lang.exists(request, "responseXML")) {
 94                 throw this.makeException(new Error(), _Impl.EMPTY_RESPONSE, _Impl.EMPTY_RESPONSE, this._nameSpace, "processResponse", "");
 95             }
 96             //check for a parseError under certain browsers
 97 
 98             var xmlContent = request.responseXML;
 99             //ie6+ keeps the parsing response under xmlContent.parserError
100             //while the rest of the world keeps it as element under the first node
101             var xmlErr = _Lang.fetchXMLErrorMessage(request.responseText || request.response, xmlContent)
102             if (xmlErr) {
103                 throw this._raiseError(new Error(), xmlErr.errorMessage + "\n" + xmlErr.sourceText + "\n" + xmlErr.visualError + "\n", "processResponse");
104             }
105             var partials = xmlContent.childNodes[0];
106             if ('undefined' == typeof partials || partials == null) {
107                 throw this._raiseError(new Error(), "No child nodes for response", "processResponse");
108 
109             } else {
110                 if (partials.tagName != this.RESP_PARTIAL) {
111                     // IE 8 sees XML Header as first sibling ...
112                     partials = partials.nextSibling;
113                     if (!partials || partials.tagName != this.RESP_PARTIAL) {
114                         throw this._raiseError(new Error(), "Partial response not set", "processResponse");
115                     }
116                 }
117             }
118 
119 
120             /**
121              * jsf 2.3 naming mode partial response,
122              * we either viewstate all forms (non id mode)
123              * or the forms under the viewroot defined by id
124              *
125              * @type {string} ... the naming mode id is set or an empty string
126              * definitely not a null value to avoid type confusions later on
127              */
128             mfInternal.namingModeId = (partials.id || "");
129 
130 
131             var childNodesLength = partials.childNodes.length;
132 
133             for (var loop = 0; loop < childNodesLength; loop++) {
134                 var childNode = partials.childNodes[loop];
135                 var tagName = childNode.tagName;
136                 /**
137                  * <eval>
138                  *      <![CDATA[javascript]]>
139                  * </eval>
140                  */
141 
142                 //this ought to be enough for eval
143                 //however the run scripts still makes sense
144                 //in the update and insert area for components
145                 //which do not use the response writer properly
146                 //we might add this one as custom option in update and
147                 //insert!
148                 if (tagName == this.CMD_ERROR) {
149                     this.processError(request, context, childNode);
150                 } else if (tagName == this.CMD_REDIRECT) {
151                     this.processRedirect(request, context, childNode);
152                 } else if (tagName == this.CMD_CHANGES) {
153                     this.processChanges(request, context, childNode);
154                 }
155             }
156 
157             //fixup missing viewStates due to spec deficiencies
158             if (mfInternal.appliedViewState) {
159                 this.fixViewStates(context);
160             }
161             if (mfInternal.appliedClientWindow) {
162                 this.fixClientWindows(context);
163             }
164 
165             //spec jsdoc, the success event must be sent from response
166             _Impl.sendEvent(request, context, _Impl["SUCCESS"]);
167         } catch (e) {
168 
169             if (window.console && window.console.error) {
170                 //any error should be logged
171                 console.error(e);
172             }
173             throw e;
174         } finally {
175             delete mfInternal._updateElems;
176             delete mfInternal._updateForms;
177             delete mfInternal.appliedViewState;
178             delete mfInternal.appliedClientWindow;
179             delete mfInternal.namingModeId;
180         }
181     },
182 
183     /**
184      * fixes the viewstates in the current page
185      *
186      * @param context
187      */
188     fixViewStates: function (context) {
189         var _Lang = this._Lang;
190         var mfInternal = context._mfInternal;
191 
192         if (null == mfInternal.appliedViewState) {
193             return;
194         }
195 
196         /**
197          * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
198          * if none is given
199          */
200         this._updateJSFClientArtifacts(context,  mfInternal.appliedViewState, this.P_VIEWSTATE);
201     },
202 
203 
204     fixClientWindows: function (context, theForm) {
205         var _Lang = this._Lang;
206         var mfInternal = context._mfInternal;
207 
208         if (null == mfInternal.appliedClientWindow) {
209             return;
210         }
211 
212         /**
213          * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
214          * if none is given
215          */
216 
217         this._updateJSFClientArtifacts(context, mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
218 
219     },
220 
221 
222     /**
223      * sets the a jsf artifact element with a given identifier to a new value or adds this element
224      *
225      * @param theForm {Node} the form to which the element has to be set to
226      * @param context the current request context
227      */
228     _applyJSFArtifactValueToForm: function (context, theForm, value, identifier) {
229 
230         if (!theForm) return;
231         var _Lang = this._Lang;
232         var _Dom = this._Dom;
233         var prefix = this._getPrefix(context);
234 
235         //in IE7 looking up form elements with complex names (such as 'javax.faces.ViewState') fails in certain cases
236         //iterate through the form elements to find the element, instead
237         var fieldsFound = [];
238 
239         var elements = theForm.elements;
240         for (var i = 0, l = elements.length; i < l; i++) {
241             var e = elements[i];
242             //https://issues.apache.org/jira/browse/MYFACES-4230
243             //ie11 has a deviation from the standard behavior, we have to remap the null/undefined name
244             //to an empty string
245             var eName = e.name || "";
246 
247             if (eName.indexOf(identifier) != -1) {
248                 fieldsFound.push(e);
249             }
250         }
251 
252         if (fieldsFound.length) {
253             _Lang.arrForEach(fieldsFound, function (fieldFound) {
254                 _Dom.setAttribute(fieldFound, "value", value);
255             });
256         } else {
257             var element = this._Dom.getDummyPlaceHolder();
258 
259             //per JSF 2.3 spec the identifier of the element must be unique in the dom tree
260             //otherwise we will break the html spec here
261             element.innerHTML = ["<input type='hidden'", "id='", this._fetchUniqueId(prefix, identifier), "' name='", identifier, "' value='", value, "' />"].join("");
262             //now we go to proper dom handling after having to deal with another ie screw-up
263             try {
264                 theForm.appendChild(element.childNodes[0]);
265             } finally {
266                 element.innerHTML = "";
267             }
268         }
269     },
270 
271     _fetchUniqueId: function(prefix, identifier) {
272         var cnt = 0;
273         var retVal = prefix + identifier + jsf.separatorchar + cnt;
274         while(this._Dom.byId(retVal) != null) {
275             cnt++;
276             retVal = prefix + identifier + jsf.separatorchar + cnt;
277         }
278         return retVal;
279     },
280 
281     /**
282      * updates/inserts the jsf client artifacts under a given viewroot element
283      *
284      * @param context the client context holding all request context data and some internal data
285      * @param elem the root to start with, must be a dom node not an identifier
286      * @param value the new value
287      * @param identifier the identifier for the client artifact aka javax.faces.ViewState, ClientWindowId etc...
288      *
289      * @private
290      */
291     _updateJSFClientArtifacts: function (context, value, identifier) {
292 
293         //elem not found for whatever reason
294         //https://issues.apache.org/jira/browse/MYFACES-3544
295 
296         var prefix = this._getPrefix(context);
297 
298         //do we still need the issuing form update? I guess it is needed.
299         //jsf spec 2.3 and earlier all issuing forms must update
300         var sourceForm = (context._mfInternal._mfSourceFormId) ? this._Dom.byId(context._mfInternal._mfSourceFormId) : null;
301         if (sourceForm) {
302             sourceForm = this._Dom.byId(sourceForm);
303             if (sourceForm) {
304                 //some cases where the source form cannot be updated
305                 //because it is gone
306                 this._applyJSFArtifactValueToForm(context, sourceForm, value, identifier);
307             }
308         }
309 
310 
311 
312         var viewRoot = this._getViewRoot(context);
313         var forms = this._Dom.findByTagNames(viewRoot, {"form": 1}) || [];
314 
315         //since the spec thanks to the over intrusive portlet api still is broken
316         //we need our old fallback hack for proper handling without having
317         //to deal with multiple render targets.
318 
319 
320         if(this._RT.getLocalOrGlobalConfig(context, "no_portlet_env", false)) {
321 
322             //We update all elements under viewroot
323             //this clearly violates the jsf 2.3 jsdocs
324             //however I think that the jsdocs were sloppily updated
325             //because just updating the render targets under one viewroot and the issuing form
326             //again would leave broken viewstates, in the end the portlet spec is at fault here
327             //which came late to the game and expected all frameworks to adapt to their needs.
328             //instead of properly adapting to the frameworks
329             //now the viewroot mechanism per se would work, but people are dropping
330             //jsf 2.3 into old portlet containers which then expose the legacy behavior
331             //of having just one view root.
332             this._Lang.arrForEach(forms, this._Lang.hitch(this, function (elem) {
333                 //update all forms which start with prefix (all render and execute targets
334                 this._applyJSFArtifactValueToForm(context, elem, value, identifier);
335             }));
336         } else {
337 
338 
339             //check for a portlet condition a present viewroot
340 
341             var viewRootId = viewRoot.id || "";
342 
343             for(var cnt = 0; cnt < context._mfInternal._updateForms.length; cnt++) {
344                 var updateForm = context._mfInternal._updateForms[cnt];
345 
346                 //follow the spec 2.3 path 1:1 we update the forms hosting the render targets which start
347                 //with the viewroot
348                 //if there is a viewroot present, however we seem to have a bug in myfaces
349                 //even if we have a naming container response we
350                 //cannot rely on the naming container being prefixed
351 
352                 //This atm is not bad, because we safely can assume
353                 //that if no viewroot can be found we are under
354                 //one single viewroot and can omit the prefix check
355                 //(aka fallback into the old behavior)
356 
357 
358                 if(updateForm.indexOf(viewRootId) != 0) {
359                     continue;
360                 } else { //either an empty viewroot, or a namespace match
361                     this._applyJSFArtifactValueToForm(context, this._Dom.byId(updateForm), value, identifier);
362                 }
363             }
364 
365         }
366 
367     },
368 
369     _getViewRoot: function (context) {
370         var prefix = this._getPrefix(context);
371         if (prefix == "") {
372             return document.getElementsByTagName("body")[0];
373         }
374         prefix = prefix.substr(0, prefix.length - 1);
375         var viewRoot = document.getElementById(prefix);
376         if (viewRoot) {
377             return viewRoot;
378         }
379         return document.getElementsByTagName("body")[0];
380     },
381 
382 
383     _getPrefix: function (context) {
384         var mfInternal = context._mfInternal;
385         var prefix = mfInternal.namingModeId;
386         if (prefix != "") {
387             prefix = prefix + jsf.separatorchar;
388         }
389         return prefix;
390     },
391 
392     /**
393      * processes an incoming error from the response
394      * which is hosted under the <error> tag
395      * @param request the current request
396      * @param context the contect object
397      * @param node the node in the xml hosting the error message
398      */
399     processError: function (request, context, node) {
400         /**
401          * <error>
402          *      <error-name>String</error-name>
403          *      <error-message><![CDATA[message]]></error-message>
404          * <error>
405          */
406         var errorName = node.firstChild.textContent || node.firstChild.text || "",
407             errorMessage = node.childNodes[1].firstChild.data || "";
408 
409         this.attr("impl").sendError(request, context, this.attr("impl").SERVER_ERROR, errorName, errorMessage, "myfaces._impl.xhrCore._AjaxResponse", "processError");
410     },
411 
412     /**
413      * processes an incoming xml redirect directive from the ajax response
414      * @param request the request object
415      * @param context the context
416      * @param node the node hosting the redirect data
417      */
418     processRedirect: function (request, context, node) {
419         /**
420          * <redirect url="url to redirect" />
421          */
422         var _Lang = this._Lang;
423         var redirectUrl = node.getAttribute("url");
424         if (!redirectUrl) {
425             throw this._raiseError(new Error(), _Lang.getMessage("ERR_RED_URL", null, "_AjaxResponse.processRedirect"), "processRedirect");
426         }
427         redirectUrl = _Lang.trim(redirectUrl);
428         if (redirectUrl == "") {
429             return false;
430         }
431         window.location = redirectUrl;
432         return true;
433     }
434     ,
435 
436     /**
437      * main entry point for processing the changes
438      * it deals with the <changes> node of the
439      * response
440      *
441      * @param request the xhr request object
442      * @param context the context map
443      * @param node the changes node to be processed
444      */
445     processChanges: function (request, context, node) {
446         var changes = node.childNodes;
447         var _Lang = this._Lang;
448         //note we need to trace the changes which could affect our insert update or delete
449         //se that we can realign our ViewStates afterwards
450         //the realignment must happen post change processing
451 
452         for (var i = 0; i < changes.length; i++) {
453 
454             switch (changes[i].tagName) {
455 
456                 case this.CMD_UPDATE:
457                     this.processUpdate(request, context, changes[i]);
458                     break;
459                 //this one needs a csp spec extension for the global eval
460                 //for now we recycle the csp for this case from the jsf.js file
461                 case this.CMD_EVAL:
462                     _Lang.globalEval(changes[i].firstChild.data);
463                     break;
464                 case this.CMD_INSERT:
465                     this.processInsert(request, context, changes[i]);
466                     break;
467                 case this.CMD_DELETE:
468                     this.processDelete(request, context, changes[i]);
469                     break;
470                 case this.CMD_ATTRIBUTES:
471                     this.processAttributes(request, context, changes[i]);
472                     break;
473                 case this.CMD_EXTENSION:
474                     break;
475                 case undefined:
476                     // ignoring white spaces
477                     break;
478                 default:
479                     throw this._raiseError(new Error(), "_AjaxResponse.processChanges: Illegal Command Issued", "processChanges");
480             }
481         }
482 
483         return true;
484     },
485 
486     /**
487      * First sub-step process a pending update tag
488      *
489      * @param request the xhr request object
490      * @param context the context map
491      * @param node the changes node to be processed
492      */
493     processUpdate: function (request, context, node) {
494         var mfInternal = context._mfInternal;
495         if ((node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) || (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1)) {
496             if (node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) {
497                 mfInternal.appliedViewState = this._Dom.concatCDATABlocks(node);//node.firstChild.nodeValue;
498             } else if (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1) {
499                 mfInternal.appliedClientWindow = node.firstChild.nodeValue;
500             }
501         }
502         else {
503             // response may contain several blocks
504             var cDataBlock = this._Dom.concatCDATABlocks(node),
505                 resultNode = null,
506                 pushOpRes = this._Lang.hitch(this, this._pushOperationResult);
507 
508             switch (node.getAttribute('id')) {
509                 case this.P_VIEWROOT:
510 
511                     cDataBlock = cDataBlock.substring(cDataBlock.indexOf("<html"));
512 
513                     var parsedData = this._replaceHead(request, context, cDataBlock);
514 
515                     ('undefined' != typeof parsedData && null != parsedData) ? this._replaceBody(request, context, cDataBlock, parsedData) : this._replaceBody(request, context, cDataBlock);
516 
517                     break;
518                 case this.P_VIEWHEAD:
519                     //we cannot replace the head, almost no browser allows this, some of them throw errors
520                     //others simply ignore it or replace it and destroy the dom that way!
521                     this._replaceHead(request, context, cDataBlock);
522 
523                     break;
524                 case this.P_VIEWBODY:
525                     //we assume the cdata block is our body including the tag
526                     resultNode = this._replaceBody(request, context, cDataBlock);
527                     if (resultNode) {
528                         pushOpRes(context, resultNode);
529                     }
530                     break;
531                 case this.P_RESOURCE:
532 
533                     this._addResourceToHead(request, context, cDataBlock);
534                     break;
535                 default:
536 
537                     resultNode = this.replaceHtmlItem(request, context, node.getAttribute('id'), cDataBlock);
538                     if (resultNode) {
539                         pushOpRes(context, resultNode);
540                     }
541                     break;
542             }
543         }
544 
545         return true;
546     },
547 
548     _pushOperationResult: function(context, resultNode) {
549         var mfInternal = context._mfInternal;
550         var pushSubnode = this._Lang.hitch(this, function(currNode) {
551             var parentForm = this._Dom.getParent(currNode, "form");
552             //if possible we work over the ids
553             //so that elements later replaced are referenced
554             //at the latest possibility
555             if (null != parentForm) {
556                 mfInternal._updateForms.push(parentForm.id || parentForm);
557             }
558             else {
559                 mfInternal._updateElems.push(currNode.id || currNode);
560             }
561         });
562 
563         var pushEmbedded = this._Lang.hitch(this, function(currNode) {
564             if(currNode.tagName && this._Lang.equalsIgnoreCase(currNode.tagName, "form")) {
565                 if(currNode.id)  { //should not happen but just in case someone manipulates the html
566                     mfInternal._updateForms.push(currNode.id);
567                 }
568             } else {
569                 var childForms = this._Dom.findByTagName(currNode, "form");
570                 if(childForms && childForms.length) {
571                     for(var cnt = 0; cnt < childForms.length; cnt++) {
572                         if(childForms[cnt].id) {
573                             mfInternal._updateForms.push(childForms[cnt].id);
574                         }
575                     }
576                 }
577             }
578 
579         });
580 
581 
582         var isArr = 'undefined' != typeof resultNode.length && 'undefined' == typeof resultNode.nodeType;
583         if (isArr && resultNode.length) {
584             for (var cnt = 0; cnt < resultNode.length; cnt++) {
585                 pushSubnode(resultNode[cnt]);
586                 pushEmbedded(resultNode[cnt]);
587             }
588         } else if (!isArr) {
589             pushSubnode(resultNode);
590             pushEmbedded(resultNode);
591         }
592 
593     },
594 
595 
596     /**
597      * replaces a current head theoretically,
598      * pratically only the scripts are evaled anew since nothing else
599      * can be changed.
600      *
601      * @param request the current request
602      * @param context the ajax context
603      * @param newData the data to be processed
604      *
605      * @return an xml representation of the page for further processing if possible
606      */
607     _replaceHead: function (request, context, newData) {
608 
609         var _Lang = this._Lang,
610             _Dom = this._Dom;
611 
612         var newDom = _Dom.fromMarkup(newData);
613         var newHead = newDom.getElementsByTagName("head")[0];
614         var oldTags = document.head.childNodes;
615 
616         _Dom.deleteItems(_Lang.objToArray(oldTags));
617         _Dom.appendToHead(newHead);
618 
619 
620         return document.head;
621     },
622 
623     _addResourceToHead: function (request, context, newData) {
624         this._Dom.appendToHead(newData);
625     },
626 
627     /**
628      * special method to handle the body dom manipulation,
629      * replacing the entire body does not work fully by simply adding a second body
630      * and by creating a range instead we have to work around that by dom creating a second
631      * body and then filling it properly!
632      *
633      * @param {Object} request our request object
634      * @param {Object} context (Map) the response context
635      * @param {String} newData the markup which replaces the old dom node!
636      * @param {Node} parsedData (optional) preparsed XML representation data of the current document
637      */
638     _replaceBody: function (request, context, newData /*varargs*/) {
639         var _RT = this._RT,
640             _Dom = this._Dom,
641 
642             oldBody = document.getElementsByTagName("body")[0],
643             placeHolder = document.createElement("div");
644 
645         placeHolder.id = "myfaces_bodyplaceholder";
646 
647         var newDom = _Dom.fromMarkup(newData);
648         var newBodyData = newDom.getElementsByTagName("body")[0];
649 
650         _Dom._removeChildNodes(oldBody);
651         oldBody.innerHTML = "";
652         oldBody.appendChild(placeHolder);
653 
654 
655         //speedwise we serialize back into the code
656         //for code reduction, speedwise we will take a small hit
657         //there which we will clean up in the future, but for now
658         //this is ok, I guess, since replace body only is a small subcase
659         //bodyData = _Lang.serializeChilds(newBodyData);
660         var browser = _RT.browser;
661         if (!browser.isIEMobile || browser.isIEMobile >= 7) {
662             //TODO check what is failing there
663             for (var cnt = 0; cnt < newBodyData.attributes.length; cnt++) {
664                 var value = newBodyData.attributes[cnt].value;
665                 if (value)
666                     _Dom.setAttribute(oldBody, newBodyData.attributes[cnt].name, value);
667             }
668         }
669 
670         var returnedElement = this.replaceHtmlItem(request, context, placeHolder, newBodyData.innerHTML);
671 
672         if (returnedElement) {
673             this._pushOperationResult(context, returnedElement);
674         }
675         return returnedElement;
676     },
677 
678     /**
679      * Replaces HTML elements through others and handle errors if the occur in the replacement part
680      *
681      * @param {Object} request (xhrRequest)
682      * @param {Object} context (Map)
683      * @param {Object} itemIdToReplace (String|Node) - ID of the element to replace
684      * @param {String} markup - the new tag
685      */
686     replaceHtmlItem: function (request, context, itemIdToReplace, markup) {
687         var _Lang = this._Lang, _Dom = this._Dom;
688 
689         var item = (!_Lang.isString(itemIdToReplace)) ? itemIdToReplace :
690             _Dom.byIdOrName(itemIdToReplace);
691 
692         if (!item) {
693             throw this._raiseError(new Error(), _Lang.getMessage("ERR_ITEM_ID_NOTFOUND", null, "_AjaxResponse.replaceHtmlItem", (itemIdToReplace) ? itemIdToReplace.toString() : "undefined"), "replaceHtmlItem");
694         }
695         return _Dom.outerHTML(item, markup, this._RT.getLocalOrGlobalConfig(context, "preserveFocus", false));
696     },
697 
698     /**
699      * xml insert command handler
700      *
701      * @param request the ajax request element
702      * @param context the context element holding the data
703      * @param node the xml node holding the insert data
704      * @return true upon successful completion, false otherwise
705      *
706      **/
707     processInsert: function (request, context, node) {
708         /*remapping global namespaces for speed and readability reasons*/
709         var _Dom = this._Dom,
710             _Lang = this._Lang,
711             //determine which path to go:
712             insertData = this._parseInsertData(request, context, node);
713 
714         if (!insertData) return false;
715 
716         var opNode = _Dom.byIdOrName(insertData.opId);
717         if (!opNode) {
718             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_INSERTBEFID_1", null, "_AjaxResponse.processInsert", insertData.opId), "processInsert");
719         }
720 
721         //call insertBefore or insertAfter in our dom routines
722         var replacementFragment = _Dom[insertData.insertType](opNode, insertData.cDataBlock);
723         if (replacementFragment) {
724             this._pushOperationResult(context, replacementFragment);
725         }
726         return true;
727     },
728 
729     /**
730      * determines the corner data from the insert tag parsing process
731      *
732      *
733      * @param request request
734      * @param context context
735      * @param node the current node pointing to the insert tag
736      * @return false if the parsing failed, otherwise a map with follwing attributes
737      * <ul>
738      *     <li>inserType - a ponter to a constant which maps the direct function name for the insert operation </li>
739      *     <li>opId - the before or after id </li>
740      *     <li>cDataBlock - the html cdata block which needs replacement </li>
741      * </ul>
742      *
743      * TODO we have to find a mechanism to replace the direct sendError calls with a javascript exception
744      * which we then can use for cleaner error code handling
745      */
746     _parseInsertData: function (request, context, node) {
747         var _Lang = this._Lang,
748             _Dom = this._Dom,
749             concatCDATA = _Dom.concatCDATABlocks,
750 
751             INSERT_TYPE_BEFORE = "insertBefore",
752             INSERT_TYPE_AFTER = "insertAfter",
753 
754             id = node.getAttribute("id"),
755             beforeId = node.getAttribute("before"),
756             afterId = node.getAttribute("after"),
757             ret = {};
758 
759         //now we have to make a distinction between two different parsing paths
760         //due to a spec malalignment
761         //a <insert id="... beforeId|AfterId ="...
762         //b <insert><before id="..., <insert> <after id="....
763         //see https://issues.apache.org/jira/browse/MYFACES-3318
764         //simple id, case1
765         if (id && beforeId && !afterId) {
766             ret.insertType = INSERT_TYPE_BEFORE;
767             ret.opId = beforeId;
768             ret.cDataBlock = concatCDATA(node);
769 
770             //<insert id=".. afterId="..
771         } else if (id && !beforeId && afterId) {
772             ret.insertType = INSERT_TYPE_AFTER;
773             ret.opId = afterId;
774             ret.cDataBlock = concatCDATA(node);
775 
776             //<insert><before id="... <insert><after id="...
777         } else if (!id) {
778             var opType = node.childNodes[0].tagName;
779 
780             if (opType != "before" && opType != "after") {
781                 throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_INSERTBEFID"), "_parseInsertData");
782             }
783             opType = opType.toLowerCase();
784             var beforeAfterId = node.childNodes[0].getAttribute("id");
785             ret.insertType = (opType == "before") ? INSERT_TYPE_BEFORE : INSERT_TYPE_AFTER;
786             ret.opId = beforeAfterId;
787             ret.cDataBlock = concatCDATA(node.childNodes[0]);
788         } else {
789             throw this._raiseError(new Error(), [_Lang.getMessage("ERR_PPR_IDREQ"),
790                 "\n ",
791                 _Lang.getMessage("ERR_PPR_INSERTBEFID")].join(""), "_parseInsertData");
792         }
793         ret.opId = _Lang.trim(ret.opId);
794         return ret;
795     },
796 
797     processDelete: function (request, context, node) {
798 
799         var _Lang = this._Lang,
800             _Dom = this._Dom,
801             deleteId = node.getAttribute('id');
802 
803         if (!deleteId) {
804             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_UNKNOWNCID", null, "_AjaxResponse.processDelete", ""), "processDelete");
805         }
806 
807         var item = _Dom.byIdOrName(deleteId);
808         if (!item) {
809             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_UNKNOWNCID", null, "_AjaxResponse.processDelete", deleteId), "processDelete");
810         }
811 
812         var parentForm = this._Dom.getParent(item, "form");
813         if (null != parentForm) {
814             context._mfInternal._updateForms.push(parentForm);
815         }
816         _Dom.deleteItem(item);
817 
818         return true;
819     },
820 
821     processAttributes: function (request, context, node) {
822         //we now route into our attributes function to bypass
823         //IE quirks mode incompatibilities to the biggest possible extent
824         //most browsers just have to do a setAttributes but IE
825         //behaves as usual not like the official standard
826         //myfaces._impl._util.this._Dom.setAttribute(domNode, attribute, value;
827 
828         var _Lang = this._Lang,
829             //<attributes id="id of element"> <attribute name="attribute name" value="attribute value" />* </attributes>
830             elemId = node.getAttribute('id');
831 
832         if (!elemId) {
833             throw this._raiseError(new Error(), "Error in attributes, id not in xml markup", "processAttributes");
834         }
835         var childNodes = node.childNodes;
836 
837         if (!childNodes) {
838             return false;
839         }
840         for (var loop2 = 0; loop2 < childNodes.length; loop2++) {
841             var attributesNode = childNodes[loop2],
842                 attrName = attributesNode.getAttribute("name"),
843                 attrValue = attributesNode.getAttribute("value");
844 
845             if (!attrName) {
846                 continue;
847             }
848 
849             attrName = _Lang.trim(attrName);
850             /*no value means reset*/
851             //value can be of boolean value hence full check
852             if ('undefined' == typeof attrValue || null == attrValue) {
853                 attrValue = "";
854             }
855 
856             switch (elemId) {
857                 case this.P_VIEWROOT:
858                     throw  this._raiseError(new Error(), _Lang.getMessage("ERR_NO_VIEWROOTATTR", null, "_AjaxResponse.processAttributes"), "processAttributes");
859 
860                 case this.P_VIEWHEAD:
861                     throw  this._raiseError(new Error(), _Lang.getMessage("ERR_NO_HEADATTR", null, "_AjaxResponse.processAttributes"), "processAttributes");
862 
863                 case this.P_VIEWBODY:
864                     var element = document.getElementsByTagName("body")[0];
865                     this._Dom.setAttribute(element, attrName, attrValue);
866                     break;
867 
868                 default:
869                     this._Dom.setAttribute(document.getElementById(elemId), attrName, attrValue);
870                     break;
871             }
872         }
873         return true;
874     },
875 
876     /**
877      * internal helper which raises an error in the
878      * format we need for further processing
879      *
880      * @param message the message
881      * @param title the title of the error (optional)
882      * @param name the name of the error (optional)
883      */
884     _raiseError: function (error, message, caller, title, name) {
885         var _Impl = this.attr("impl");
886         var finalTitle = title || _Impl.MALFORMEDXML;
887         var finalName = name || _Impl.MALFORMEDXML;
888         var finalMessage = message || "";
889 
890         return this._Lang.makeException(error, finalTitle, finalName, this._nameSpace, caller || ( (arguments.caller) ? arguments.caller.toString() : "_raiseError"), finalMessage);
891     }
892 });
893