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