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