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