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