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.xhrCore._FinalizeableObj
 22  * @description
 23  * This class is responsible for handling the standardized xml ajax response
 24  */
 25 myfaces._impl.core._Runtime.extendClass("myfaces._impl.xhrCore._AjaxResponse", myfaces._impl.xhrCore._FinalizeableObj,
 26 /** @lends myfaces._impl.xhrCore._AjaxResponse.prototype */
 27 {
 28 
 29     /*partial response types*/
 30     RESP_PARTIAL : "partial-response",
 31     RESP_TYPE_ERROR : "error",
 32     RESP_TYPE_REDIRECT : "redirect",
 33     RESP_TYPE_CHANGES : "changes",
 34 
 35     /*partial commands*/
 36     CMD_CHANGES : "changes",
 37     CMD_UPDATE : "update",
 38     CMD_DELETE : "delete",
 39     CMD_INSERT : "insert",
 40     CMD_EVAL : "eval",
 41     CMD_ERROR : "error",
 42     CMD_ATTRIBUTES : "attributes",
 43     CMD_EXTENSION : "extension",
 44     CMD_REDIRECT : "redirect",
 45 
 46     /*other constants*/
 47     P_VIEWSTATE: "javax.faces.ViewState",
 48     P_VIEWROOT: "javax.faces.ViewRoot",
 49     P_VIEWHEAD: "javax.faces.ViewHead",
 50     P_VIEWBODY: "javax.faces.ViewBody",
 51 
 52 
 53     /**
 54      * Standard constructor
 55      *
 56      * @param {function} base request classed parent object
 57      * @param {function} onException
 58      * @param {function} onWarning
 59      */
 60     constructor_: function(onException, onWarning) {
 61         //List of non form elements to be updated (which can have forms embedded)
 62         this._updateElems = [];
 63         // List of forms to be updated if any inner block is updated
 64         this._updateForms = [];
 65         this._onException = onException;
 66         this._onWarning = onWarning;
 67 
 68         this.appliedViewState = null;
 69 
 70         this._Lang = myfaces._impl._util._Lang;
 71         this._Dom = myfaces._impl._util._Dom;
 72         this._RT = myfaces._impl.core._Runtime;
 73         this._Impl = this._RT.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl);
 74     },
 75     /**
 76      * uses response to start Html element replacement
 77      *
 78      * @param {Object} request (xhrRequest) - xhr request object
 79      * @param {Object} context (Map) - AJAX context
 80      *
 81      * A special handling has to be added to the update cycle
 82      * according to the JSDoc specs if the CDATA block contains html tags the outer rim must be stripped
 83      * if the CDATA block contains a head section the document head must be replaced
 84      * and if the CDATA block contains a body section the document body must be replaced!
 85      *
 86      */
 87     processResponse : function(request, context) {
 88         try {
 89             var _Impl = this._Impl;
 90 
 91             // TODO:
 92             // Solution from
 93             // http://www.codingforums.com/archive/index.php/t-47018.html
 94             // to solve IE error 1072896658 when a Java server sends iso88591
 95             // istead of ISO-8859-1
 96 
 97             if (!request) {
 98                 throw Exception(this._Lang.getMessage("ERR_EMPTY_RESPONSE",null,"jsf.ajaxResponse"));
 99             }
100 
101             if (!this._Lang.exists(request, "responseXML")) {
102                 _Impl.sendError(request, context, myfaces._impl.core.Impl.EMPTY_RESPONSE);
103                 return;
104             }
105             //check for a parseError under certain browsers
106 
107             var xmlContent = request.responseXML;
108             //ie6+ keeps the parsing response under xmlContent.parserError
109             //while the rest of the world keeps it as element under the first node
110 
111             if (this._Lang.isXMLParseError(xmlContent)) {
112                 _Impl.sendError(request, context, myfaces._impl.core.Impl.MALFORMEDXML);
113                 return;
114             }
115             var partials = xmlContent.childNodes[0];
116             if ('undefined' == typeof partials || partials == null) {
117                 _Impl.sendError(request, context, _Impl.MALFORMEDXML);
118                 return;
119             } else {
120                 if (partials.tagName != this.RESP_PARTIAL) {
121                     // IE 8 sees XML Header as first sibling ...
122                     partials = partials.nextSibling;
123                     if (!partials || partials.tagName != this.RESP_PARTIAL) {
124                         _Impl.sendError(request, context, myfaces._impl.core.Impl.MALFORMEDXML);
125                         return;
126                     }
127                 }
128             }
129 
130             var childNodesLength = partials.childNodes.length;
131 
132             for (var loop = 0; loop < childNodesLength; loop++) {
133                 var childNode = partials.childNodes[loop];
134                 var tagName = childNode.tagName;
135                 /**
136                  * <eval>
137                  *      <![CDATA[javascript]]>
138                  * </eval>
139                  */
140 
141                 //this ought to be enough for eval
142                 //however the run scripts still makes sense
143                 //in the update and insert area for components
144                 //which do not use the response writer properly
145                 //we might add this one as custom option in update and
146                 //insert!
147                 if (tagName == this.CMD_ERROR) {
148                     this.processError(request, context, childNode);
149                     return;
150                 } else if (tagName == this.CMD_REDIRECT) {
151                     if (!this.processRedirect(request, context, childNode)) return;
152                 } else if (tagName == this.CMD_CHANGES) {
153                     if (!this.processChanges(request, context, childNode)) return;
154                 }
155             }
156 
157             //fixup missing viewStates due to spec deficiencies
158             this.fixViewStates(context);
159         } catch (e) {
160             this._onException(request, context, "myfaces._impl.xhrCore._AjaxResponse", "processResponse", e);
161         }
162     },
163 
164     /**
165      * fixes the viewstates in the current page
166      *
167      * @param context
168      */
169     fixViewStates : function(context) {
170 
171         if (null == this.appliedViewState) {
172             return;
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._setVSTForm(document.forms[cnt]);
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         this._Lang.arrForEach(this._updateForms, this._setVSTForm, 0, this);
190 
191         //set the viewstate of all forms within our updated elements
192         this._Lang.arrForEach(this._updateElems, this._setVSTInnerForms, 0, this);
193     },
194 
195     /**
196      * sets the viewstate element in a given form
197      *
198      * @param theForm the form to which the element has to be set to
199      * @param doNotChange if set to true no change is performed if the element is found already to be rendered
200      */
201     _setVSTForm: function(theForm) {
202         theForm = this._Lang.byId(theForm);
203         if(!theForm) return;
204 
205         var viewStateField = (theForm.elements) ? theForm.elements[this.P_VIEWSTATE] : null;//this._Dom.findFormElement(elem, this.P_VIEWSTATE);
206 
207         if (viewStateField) {
208             this._Dom.setAttribute(viewStateField, "value", this.appliedViewState);
209         } else if (!viewStateField) {
210             var element = this._Dom.getDummyPlaceHolder();
211             element.innerHTML = ["<input type='hidden'", "id='", this.P_VIEWSTATE ,"' name='", this.P_VIEWSTATE ,"' value='" , this.appliedViewState , "' />"].join("");
212             //now we go to proper dom handling after having to deal with another ie screwup
213             try {
214                 theForm.appendChild(element.childNodes[0]);
215             } finally {
216                 element.innerHTML = "";
217             }
218         }
219     },
220 
221     _setVSTInnerForms: function(elem) {
222         elem = this._Dom.byIdOrName(elem);
223         var replacedForms = this._Dom.findByTagName(elem, "form", false);
224         var applyVST = this._Lang.hitch(this, function(elem) {
225             this._setVSTForm(elem);
226         });
227 
228         try {
229             this._Lang.arrForEach(replacedForms, applyVST, 0, this);
230         } finally {
231             delete applyVST;
232         }
233     },
234 
235 
236     /**
237      * processes an incoming error from the response
238      * which is hosted under the <error> tag
239      * @param request the current request
240      * @param context the contect object
241      * @param node the node in the xml hosting the error message
242      */
243     processError : function(request, context, node) {
244         /**
245          * <error>
246          *      <error-name>String</error-name>
247          *      <error-message><![CDATA[message]]></error-message>
248          * <error>
249          */
250         var errorName = node.firstChild.textContent || "";
251         var errorMessage = node.childNodes[1].firstChild.data || "";
252 
253         var _Impl = this._Impl;
254 
255         _Impl.sendError(request, context, myfaces._impl.core.Impl.SERVER_ERROR, errorName, errorMessage);
256     },
257 
258     /**
259      * processes an incoming xml redirect directive from the ajax response
260      * @param request the request object
261      * @param context the context
262      * @param node the node hosting the redirect data
263      */
264     processRedirect : function(request, context, node) {
265         /**
266          * <redirect url="url to redirect" />
267          */
268         var redirectUrl = node.getAttribute("url");
269         if (!redirectUrl) {
270             var _Impl = this._Impl;
271 
272             _Impl.sendError(request, context, myfaces._impl.core.Impl.MALFORMEDXML, myfaces._impl.core.Impl.MALFORMEDXML,this._Lang.getMessage("ERR_RED_URL", null, "_AjaxResponse.processRedirect"));
273             return false;
274         }
275         redirectUrl = this._Lang.trim(redirectUrl);
276         if (redirectUrl == "") {
277             return false;
278         }
279         window.location = redirectUrl;
280         return true;
281     },
282 
283     /**
284      * main entry point for processing the changes
285      * it deals with the <changes> node of the
286      * response
287      *
288      * @param request the xhr request object
289      * @param context the context map
290      * @param node the changes node to be processed
291      */
292     processChanges : function(request, context, node) {
293         var changes = node.childNodes;
294 
295         //note we need to trace the changes which could affect our insert update or delete
296         //se that we can realign our ViewStates afterwards
297         //the realignment must happen post change processing
298         
299         for (var i = 0; i < changes.length; i++) {
300          
301             switch (changes[i].tagName) {
302 
303                 case this.CMD_UPDATE:
304                     if (!this.processUpdate(request, context, changes[i])) {
305                         return false;
306                     }
307                     break;
308                 case this.CMD_EVAL:
309                     this._Lang.globalEval(changes[i].firstChild.data);
310                     break;
311                 case this.CMD_INSERT:
312                     if (!this.processInsert(request, context, changes[i])) return false;
313                     break;
314                 case this.CMD_DELETE:
315                     if (!this.processDelete(request, context, changes[i])) return false;
316                     break;
317                 case this.CMD_ATTRIBUTES:
318                     if (!this.processAttributes(request, context, changes[i])) return false;
319                     break;
320                 case this.CMD_EXTENSION:
321                     break;
322                 default:
323                     var _Impl = this._Impl;
324                     _Impl.sendError(request, context, myfaces._impl.core.Impl.MALFORMEDXML);
325                     return false;
326             }
327         }
328 
329         return true;
330     },
331 
332     /**
333      * First substep process a pending update tag
334      *
335      * @param request the xhr request object
336      * @param context the context map
337      * @param node the changes node to be processed
338      */
339     processUpdate : function(request, context, node) {
340         if (node.getAttribute('id') == this.P_VIEWSTATE) {
341             //update the submitting forms viewstate to the new value
342             // The source form has to be pulled out of the CURRENT document first because the context object
343             // may refer to an invalid document if an update of the entire body has occurred before this point.
344             var viewStateValue = node.firstChild.nodeValue;
345 
346             var elementId = (context._mfInternal)? context._mfInternal["_mfSourceControlId"] : context.source.id;
347             var sourceForm = (context._mfInternal)? (document.forms[context._mfInternal["_mfSourceFormId"]] || this._Dom.fuzzyFormDetection(elementId)) : this._Dom.fuzzyFormDetection(elementId);
348             this.appliedViewState = viewStateValue;
349             //source form could not be determined either over the form identifer or the element
350             //we now skip this phase and just add everything we need for the fixup code
351 
352             if (!sourceForm) {
353                 //no source form found is not an error because
354                 //we might be able to recover one way or the other
355                 return true;
356             }
357 
358             this._updateForms.push(sourceForm.id)
359             //this._setVSTForm(sourceForm);
360         }
361         else {
362             // response may contain several blocks
363             var cDataBlock = this._Dom.concatCDATABlocks(node);
364 
365             switch (node.getAttribute('id')) {
366                 case this.P_VIEWROOT:
367 
368 
369                     cDataBlock = cDataBlock.substring(cDataBlock.indexOf("<html"));
370 
371                     var parsedData = this._replaceHead(request, context, cDataBlock);
372 
373                     var resultNode = ('undefined' != typeof parsedData &&  null != parsedData) ? this._replaceBody(request, context, cDataBlock, parsedData) : this._replaceBody(request, context, cDataBlock);
374                     if (resultNode) {
375                         this._pushOperationResult(resultNode);
376                     }
377                     break;
378                 case this.P_VIEWHEAD:
379                     //we cannot replace the head, almost no browser allows this, some of them throw errors
380                     //others simply ignore it or replace it and destroy the dom that way!
381                     this._replaceHead(request, context, cDataBlock);
382 
383                     break;
384                 case this.P_VIEWBODY:
385                     //we assume the cdata block is our body including the tag
386                     var resultNode = this._replaceBody(request, context, cDataBlock);
387                     if (resultNode) {
388                         this._pushOperationResult(resultNode);
389                     }
390                     break;
391 
392                 default:
393                     var resultNode = this.replaceHtmlItem(request, context, node.getAttribute('id'), cDataBlock);
394                     if (resultNode) {
395                         this._pushOperationResult(resultNode);
396                     }
397                     break;
398             }
399         }
400         return true;
401     },
402 
403     _pushOperationResult: function(resultNode) {
404         var pushSubnode = this._Lang.hitch(this, function(currNode) {
405             var parentForm = this._Dom.getParent(currNode, "form");
406             //if possible we work over the ids
407             //so that elements later replaced are referenced
408             //at the latest possibility
409             if (null != parentForm) {
410                 this._updateForms.push(parentForm.id || parentForm);
411             }
412             else {
413                 this._updateElems.push(currNode.id || currNode);
414             }
415         });
416         var isArr = 'undefined' != typeof resultNode.length && 'undefined' == typeof resultNode.nodeType;
417         if (isArr && resultNode.length) {
418             for (var cnt = 0; cnt < resultNode.length; cnt++) {
419                 pushSubnode(resultNode[cnt]);
420             }
421         } else if (!isArr) {
422             pushSubnode(resultNode);
423         }
424 
425     },
426 
427     /**
428      * replaces a current head theoretically,
429      * pratically only the scripts are evaled anew since nothing else
430      * can be changed.
431      *
432      * @param request the current request
433      * @param context the ajax context
434      * @param newData the data to be processed
435      *
436      * @return an xml representation of the page for further processing if possible
437      */
438     _replaceHead: function(request, context, newData) {
439 
440         var _Impl = this._Impl;
441 
442         var isWebkit = this._RT.browser.isWebKit;
443 
444         //we have to work around an xml parsing bug in Webkit
445         //see https://issues.apache.org/jira/browse/MYFACES-3061
446         var doc = (!isWebkit)? this._Lang.parseXML(newData) : null;
447 
448         var newHead = null;
449         if (!isWebkit  && this._Lang.isXMLParseError(doc)) {
450             doc = this._Lang.parseXML(newData.replace(/<!\-\-[\s\n]*<!\-\-/g, "<!--").replace(/\/\/-->[\s\n]*\/\/-->/g, "//-->"));
451         }
452 
453         if (isWebkit || this._Lang.isXMLParseError(doc) ) {
454             //the standard xml parser failed we retry with the stripper
455             var parser = new (this._RT.getGlobalConfig("updateParser", myfaces._impl._util._HtmlStripper))();
456             var headData = parser.parse(newData, "head");
457             //We cannot avoid it here, but we have reduced the parsing now down to the bare minimum
458             //for further processing
459             newHead = this._Lang.parseXML("<head>" + headData + "</head>");
460             //last and slowest option create a new head element and let the browser
461             //do its slow job
462             if (this._Lang.isXMLParseError(newHead)) {
463                 try {
464                     newHead = document.createElement("head");
465                     newHead.innerHTML = headData;
466                 } catch (e) {
467                     //we give up no further fallbacks
468                     _Impl.sendError(request, context, _Impl.MALFORMEDXML, _Impl.MALFORMEDXML, "Error head replacement failed reason:"+e.toString());
469                     return null;
470                 }
471             }
472         } else {
473             //parser worked we go on
474             newHead = doc.getElementsByTagName("head")[0];
475         }
476 
477         this._Dom.runScripts(newHead, true);
478 
479         return doc;
480     },
481 
482 
483     /**
484      * special method to handle the body dom manipulation,
485      * replacing the entire body does not work fully by simply adding a second body
486      * and by creating a range instead we have to work around that by dom creating a second
487      * body and then filling it properly!
488      *
489      * @param {Object} request our request object
490      * @param {Object} context (Map) the response context
491      * @param {String} newData the markup which replaces the old dom node!
492      * @param {Node} parsedData (optional) preparsed XML representation data of the current document
493      */
494     _replaceBody : function(request, context, newData /*varargs*/) {
495 
496         var oldBody = document.getElementsByTagName("body")[0];
497         var placeHolder = document.createElement("div");
498         var isWebkit = this._RT.browser.isWebKit;
499 
500         placeHolder.id = "myfaces_bodyplaceholder";
501 
502         var bodyParent = oldBody.parentNode;
503         this._Dom._removeChildNodes(oldBody);
504         oldBody.innerHTML = "";
505         var newBody = oldBody;
506 
507         newBody.appendChild(placeHolder);
508 
509         var bodyData = null;
510 
511         var doc = null;
512 
513         //we have to work around an xml parsing bug in Webkit
514         //see https://issues.apache.org/jira/browse/MYFACES-3061
515         if(!isWebkit) {
516             doc = (arguments.length > 3) ? arguments[3] : this._Lang.parseXML(newData);
517         }
518 
519         if (!isWebkit && this._Lang.isXMLParseError(doc)) {
520             doc = this._Lang.parseXML(newData.replace(/<!\-\-[\s\n]*<!\-\-/g, "<!--").replace(/\/\/-->[\s\n]*\/\/-->/g, "//-->"));
521         }
522 
523         if (isWebkit || this._Lang.isXMLParseError(doc)) {
524             //the standard xml parser failed we retry with the stripper
525 
526             var parser = new (this._RT.getGlobalConfig("updateParser", myfaces._impl._util._HtmlStripper))();
527 
528 
529             bodyData = parser.parse(newData, "body");
530         } else {
531             //parser worked we go on
532             var newBodyData = doc.getElementsByTagName("body")[0];
533 
534             //speedwise we serialize back into the code
535             //for code reduction, speedwise we will take a small hit
536             //there which we will clean up in the future, but for now
537             //this is ok, I guess, since replace body only is a small subcase
538             bodyData = this._Lang.serializeChilds(newBodyData);
539 
540             if (!this._RT.browser.isIEMobile || this._RT.browser.isIEMobile >= 7) {
541                 //TODO check what is failing there
542                 for (var cnt = 0; cnt < newBodyData.attributes.length; cnt++) {
543                     var value = newBodyData.attributes[cnt].value;
544                     if (value)
545                         this._Dom.setAttribute(newBody, newBodyData.attributes[cnt].name, value);
546                 }
547             }
548         }
549 
550         //TODO eliminate the serialisation in case of already having a parsed tree
551         var returnedElement = this.replaceHtmlItem(request, context, placeHolder, bodyData);
552 
553         if (returnedElement) {
554             this._pushOperationResult(returnedElement);
555         }
556         return returnedElement;
557     },
558 
559     /**
560      * Replaces HTML elements through others and handle errors if the occur in the replacement part
561      *
562      * @param {Object} request (xhrRequest)
563      * @param {Object} context (Map)
564      * @param {Object} itemIdToReplace (String|Node) - ID of the element to replace
565      * @param {String} markup - the new tag
566      */
567     replaceHtmlItem : function(request, context, itemIdToReplace, markup) {
568         try {
569             //TODO make a detachement fixup which tries to replace the item
570             //with the correct name upon its parent form if given
571 
572 
573             var origIdentifier = itemIdToReplace;
574             var item = (!this._Lang.isString(itemIdToReplace)) ? itemIdToReplace :
575                     this._Dom.byIdOrName(itemIdToReplace);
576 
577             if (!item) {
578                 throw Error(this._Lang.getMessage("ERR_ITEM_ID_NOTFOUND", null,"_AjaxResponse.replaceHtmlItem",(itemIdToReplace)? itemIdToReplace.toString():"undefined"));
579             }
580             return this._Dom.outerHTML(item, markup);
581 
582         } catch (e) {
583             this._onException(request, context, "myfaces._impl.xhrCore._AjaxResponse", "replaceHTMLItem", e);
584         }
585         return null;
586     }
587     ,
588 
589     /*insert, three attributes can be present
590      * id = insert id
591      * before = before id
592      * after = after  id
593      *
594      * the insert id is the id of the node to be inserted
595      * the before is the id if set which the component has to be inserted before
596      * the after is the id if set which the component has to be inserted after
597      **/
598     processInsert : function(request, context, node) {
599         /*remapping global namespaces for speed and readability reasons*/
600         var _Impl = this._Impl;
601         var _Dom = this._Dom;
602         var _Lang = this._Lang;
603 
604         var insertId = node.getAttribute('id');
605         var beforeId = node.getAttribute('before');
606         var afterId = node.getAttribute('after');
607 
608         var isInsert = insertId && this._Lang.trim(insertId) != "";
609         var isBefore = beforeId && this._Lang.trim(beforeId) != "";
610         var isAfter = afterId && this._Lang.trim(afterId) != "";
611 
612         if (!isInsert) {
613             _Impl.sendError(request, context, _Impl.MALFORMEDXML, _Impl.MALFORMEDXML,this._Lang.getMessage("ERR_PPR_IDREQ"));
614             return false;
615         }
616         if (!(isBefore || isAfter)) {
617             _Impl.sendError(request, context, _Impl.MALFORMEDXML, _Impl.MALFORMEDXML,this._Lang.getMessage("ERR_PPR_INSERTBEFID"));
618             return false;
619         }
620         //either before or after but not two at the same time
621         var nodeHolder = null;
622         var parentNode = null;
623 
624 
625         var cDataBlock = this._Dom.concatCDATABlocks(node);
626 
627         var replacementFragment;
628         if (isBefore) {
629             beforeId = this._Lang.trim(beforeId);
630             var beforeNode = this._Dom.byIdOrName(beforeId);
631             if (!beforeNode) {
632                 _Impl.sendError(request, context, _Impl.MALFORMEDXML, _Impl.MALFORMEDXML,this._Lang.getMessage("ERR_PPR_INSERTBEFID_1", null,"_AjaxResponse.processInsert",beforeId));
633                 return false;
634             }
635             /**
636              *we generate a temp holder
637              *so that we can use innerHTML for
638              *generating the content upfront
639              *before inserting it"
640              **/
641             nodeHolder = document.createElement("div");
642             parentNode = beforeNode.parentNode;
643             parentNode.insertBefore(nodeHolder, beforeNode);
644             replacementFragment = this.replaceHtmlItem(request, context,
645                     nodeHolder, cDataBlock);
646             if (replacementFragment) {
647                 this._pushOperationResult(replacementFragment);
648             }
649 
650         } else {
651             afterId = this._Lang.trim(afterId);
652             var afterNode = this._Dom.byIdOrName(afterId);
653             if (!afterNode) {
654                 _Impl.sendError(request, context, _Impl.MALFORMEDXML, _Impl.MALFORMEDXML, this._Lang.getMessage("ERR_PPR_INSERTBEFID_2", null,"_AjaxResponse.processInsert", afterId));
655                 return false;
656             }
657 
658             nodeHolder = document.createElement("div");
659             parentNode = afterNode.parentNode;
660 
661             //TODO nextsibling not working in ieMobile 6.1 we have to change the method
662             //of accessing it to something else
663             parentNode.insertBefore(nodeHolder, afterNode.nextSibling);
664 
665             replacementFragment = this.replaceHtmlItem(request, context,
666                     nodeHolder, cDataBlock);
667 
668             if (replacementFragment) {
669                 this._pushOperationResult(replacementFragment);
670             }
671 
672         }
673         return true;
674     }
675     ,
676 
677     processDelete : function(request, context, node) {
678         var _Impl = this._Impl;
679 
680         var deleteId = node.getAttribute('id');
681         if (!deleteId) {
682             _Impl.sendError(request, context, _Impl.MALFORMEDXML,
683                     _Impl.MALFORMEDXML,this._Lang.getMessage("ERR_PPR_DELID", null,"_AjaxResponse.processDelete"));
684             return false;
685         }
686 
687         var item = this._Dom.byIdOrName(deleteId);
688         if (!item) {
689             throw Error(this._Lang.getMessage("ERR_PPR_UNKNOWNCID", null,"_AjaxResponse.processDelete",deleteId));
690         }
691 
692         var parentForm = this._Dom.getParent(item, "form");
693         if (null != parentForm) {
694             this._updateForms.push(parentForm);
695         }
696         this._Dom.deleteItem(item);
697 
698         return true;
699     }
700     ,
701 
702     processAttributes : function(request, context, node) {
703         //we now route into our attributes function to bypass
704         //IE quirks mode incompatibilities to the biggest possible extent
705         //most browsers just have to do a setAttributes but IE
706         //behaves as usual not like the official standard
707         //myfaces._impl._util.this._Dom.setAttribute(domNode, attribute, value;
708 
709         var _Impl = this._Impl;
710 
711         //<attributes id="id of element"> <attribute name="attribute name" value="attribute value" />* </attributes>
712         var elemId = node.getAttribute('id');
713         if (!elemId) {
714             _Impl.sendError(request, context, _Impl.MALFORMEDXML
715                     , _Impl.MALFORMEDXML, "Error in attributes, id not in xml markup");
716             return false;
717         }
718         var childNodes = node.childNodes;
719 
720         if (!childNodes) {
721             return false;
722         }
723         for (var loop2 = 0; loop2 < childNodes.length; loop2++) {
724             var attributesNode = childNodes[loop2];
725 
726             var attrName = attributesNode.getAttribute("name");
727             var attrValue = attributesNode.getAttribute("value");
728 
729             if (!attrName) {
730                 continue;
731             }
732 
733             attrName = this._Lang.trim(attrName);
734             /*no value means reset*/
735             //value can be of boolean value hence full check
736             if ('undefined' == typeof attrValue || null == attrValue) {
737                 attrValue = "";
738             }
739 
740             switch (elemId) {
741                 case this.P_VIEWROOT:
742                     throw new Error(this._Lang.getMessage("ERR_NO_VIEWROOTATTR", null,"_AjaxResponse.processAttributes"));
743                     break;
744 
745                 case this.P_VIEWHEAD:
746                     throw new Error(this._Lang.getMessage("ERR_NO_HEADATTR", null,"_AjaxResponse.processAttributes"));
747                     break;
748 
749                 case this.P_VIEWBODY:
750                     var element = document.getElementsByTagName("body")[0];
751                     this._Dom.setAttribute(element, attrName, attrValue);
752                     break;
753 
754                 default:
755                     this._Dom.setAttribute(document.getElementById(elemId), attrName, attrValue);
756                     break;
757             }
758 
759         }
760         return true;
761     },
762     _finalize: function() {
763         delete this._onException;
764         delete this._onWarning;
765         delete this._updateElems;
766         // List of forms to be updated if any inner block is updated
767         delete this._updateForms;
768         delete this.appliedViewState;
769     }
770 
771 });
772