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