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