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 * @class 18 * @name _AjaxUtils 19 * @memberOf myfaces._impl.xhrCore 20 * @description 21 * 22 * A set of helper routines which are utilized within our Ajax subsystem and nowhere else 23 * 24 * TODO move this into a singleton, the current structure is 25 * still a j4fry legacy we need to get rid of it in the long run 26 */ 27 _MF_SINGLTN(_PFX_XHR+"_AjaxUtils", _MF_OBJECT, 28 /** @lends myfaces._impl.xhrCore._AjaxUtils.prototype */ 29 { 30 31 NAMED_VIEWROOT: "namedViewRoot", 32 NAMING_CONTAINER_ID: "myfaces.partialId", 33 34 35 /** 36 * determines fields to submit 37 * @param {Object} targetBuf - the target form buffer receiving the data 38 * @param {Node} parentItem - form element item is nested in 39 * @param {Array} partialIds - ids fo PPS 40 */ 41 encodeSubmittableFields : function(targetBuf, 42 parentItem, partialIds) { 43 if (!parentItem) throw "NO_PARITEM"; 44 if (partialIds ) { 45 this.encodePartialSubmit(parentItem, false, partialIds, targetBuf); 46 } else { 47 // add all nodes 48 var eLen = parentItem.elements.length; 49 for (var e = 0; e < eLen; e++) { 50 this.encodeElement(parentItem.elements[e], targetBuf); 51 } // end of for (formElements) 52 } 53 54 }, 55 56 /** 57 * appends the issuing item if not given already 58 * @param item 59 * @param targetBuf 60 */ 61 appendIssuingItem: function (item, targetBuf) { 62 // if triggered by a Button send it along 63 var identifier = item.id || item.name; 64 var type = ((item && item.type) || "").toLowerCase(); 65 66 if(targetBuf.hasKey(identifier)) { //already processed within the values 67 return; 68 } 69 70 //MYFACES-4606 we cannot send a value on an unchecked box as issuing element 71 var isCheckboxRadio = "checkbox" == type || "radio" == type; 72 if(isCheckboxRadio && !item.checked) { 73 return; 74 } else if (isCheckboxRadio) { 75 var value = ("undefined" == typeof item.value || null == item.value) ? true : item.value; 76 targetBuf.append(identifier, value); 77 //item must have a valid value to be able to be appended, without it no dice! 78 } else if(!(("undefined" == typeof item.value) || (null == item.value))) { 79 var itemValue = item.value; 80 targetBuf.append(identifier, itemValue); 81 } 82 }, 83 84 85 /** 86 * encodes a single input element for submission 87 * 88 * @param {Node} element - to be encoded 89 * @param {} targetBuf - a target array buffer receiving the encoded strings 90 */ 91 encodeElement : function(element, targetBuf) { 92 93 //browser behavior no element name no encoding (normal submit fails in that case) 94 //https://issues.apache.org/jira/browse/MYFACES-2847 95 if (!element.name) { 96 return; 97 } 98 99 var _RT = this._RT; 100 var name = element.name; 101 var tagName = element.tagName.toLowerCase(); 102 var elemType = element.type; 103 if (elemType != null) { 104 elemType = elemType.toLowerCase(); 105 } 106 107 // routine for all elements 108 // rules: 109 // - process only inputs, textareas and selects 110 // - elements muest have attribute "name" 111 // - elements must not be disabled 112 if (((tagName == "input" || tagName == "textarea" || tagName == "select") && 113 (name != null && name != "")) && !element.disabled) { 114 115 // routine for select elements 116 // rules: 117 // - if select-one and value-Attribute exist => "name=value" 118 // (also if value empty => "name=") 119 // - if select-one and value-Attribute don't exist => 120 // "name=DisplayValue" 121 // - if select multi and multple selected => "name=value1&name=value2" 122 // - if select and selectedIndex=-1 don't submit 123 if (tagName == "select") { 124 // selectedIndex must be >= 0 sein to be submittet 125 if (element.selectedIndex >= 0) { 126 var uLen = element.options.length; 127 for (var u = 0; u < uLen; u++) { 128 // find all selected options 129 //var subBuf = []; 130 if (element.options[u].selected) { 131 var elementOption = element.options[u]; 132 targetBuf.append(name, (elementOption.getAttribute("value") != null) ? 133 elementOption.value : elementOption.text); 134 } 135 } 136 } 137 } 138 139 // routine for remaining elements 140 // rules: 141 // - don't submit no selects (processed above), buttons, reset buttons, submit buttons, 142 // - submit checkboxes and radio inputs only if checked 143 if ((tagName != "select" && elemType != "button" 144 && elemType != "reset" && elemType != "submit" && elemType != "image") 145 && ((elemType != "checkbox" && elemType != "radio") || element.checked)) { 146 if ('undefined' != typeof element.files && element.files != null && _RT.getXHRLvl() >= 2 && element.files.length) { 147 //xhr level2 148 targetBuf.append(name, element.files[0]); 149 } else { 150 targetBuf.append(name, element.value); 151 } 152 } 153 154 } 155 }, 156 157 _$ncRemap: function(internalContext, containerId) { 158 var namedVieRoot = internalContext[this.NAMED_VIEWROOT]; 159 var namingContainerId = internalContext[this.NAMING_CONTAINER_ID]; 160 if(!namedVieRoot || !namingContainerId) { 161 return containerId 162 } 163 if(containerId.indexOf(namingContainerId) == 0) { 164 return containerId; 165 } 166 return [namingContainerId, containerId].join(""); 167 }, 168 169 /** 170 * determines the current naming container 171 * and assigns it internally 172 * 173 * @param internalContext 174 * @param formElement 175 * @private 176 */ 177 _assignNamingContainerData: function(internalContext, formElement, separatorChar) { 178 const viewRootId = this._resolveViewRootId(formElement, separatorChar); 179 180 if(!!viewRootId) { 181 internalContext[this.NAMED_VIEWROOT] = true; 182 internalContext[this.NAMING_CONTAINER_ID] = viewRootId; 183 } 184 }, 185 186 /** 187 * resolve the viewRoot id in a naming container situation 188 * (aka ViewState element name is prefixed) 189 * @param form 190 * @return a string (never null) which is either emtpy or contains the prefix for the ViewState 191 * (including the separator) 192 */ 193 _resolveViewRootId: function(form, separatorChar) /*string*/ { 194 form = this._Dom.byId(form); 195 var _t = this; 196 var foundNames = this._Dom.findAll(form, function(node) { 197 var name = null; 198 if(node.getAttribute && node.getAttribute("name")) { 199 name = node.getAttribute("name"); 200 } 201 if(!name || name.indexOf(_t.P_VIEWSTATE)) { 202 return false; 203 } 204 return node; 205 }, true); 206 if(!foundNames.length) { 207 return ""; 208 } 209 return foundNames[0].name.split(separatorChar, 2)[0]; 210 }, 211 212 /** 213 * as per jsdoc before the request it must be ensured that every post argument 214 * is prefixed with the naming container id (there is an exception in mojarra with 215 * the element=element param, which we have to follow here as well. 216 * (inputs are prefixed by name anyway normally this only affects our standard parameters) 217 * @private 218 */ 219 _resoveConfigNamingContainerMapper: function(myfacesOptions, separatorChar) { 220 var isNamedViewRoot = !!myfacesOptions[this.NAMED_VIEWROOT]; 221 if(!isNamedViewRoot) { 222 return; 223 } 224 225 var partialId = myfacesOptions[this.NAMING_CONTAINER_ID]; 226 var prefix = partialId + this.getSeparatorChar(); 227 return function (data /*assoc array of key value pairs*/) { 228 var ret = {}; 229 for(var key in data) { 230 if(!data.hasOwnProperty(key)) { 231 continue; 232 } 233 ret[prefix + key] = data[key] 234 } 235 return ret; 236 } 237 } 238 });