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