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  * @extends myfaces._impl.xhrCore._FinalizeableObj
 21  * @description
 22  *
 23  * A set of helper routines which are utilized within our Ajax subsystem and nowhere else
 24  */
 25 myfaces._impl.core._Runtime.extendClass("myfaces._impl.xhrCore._AjaxUtils", myfaces._impl.xhrCore._FinalizeableObj,
 26 /** @lends myfaces._impl.xhrCore._AjaxUtils.prototype */
 27 {
 28       _processedExceptions: {},
 29 
 30     /**
 31      * Constructor
 32      * @param {function} onException - exception handler
 33      * @param {function} onWarning - warning handler
 34      */
 35     constructor_ : function(onException, onWarning) {
 36         this._onException = onException;
 37         this._onWarning = onWarning;
 38     },
 39 
 40 
 41     /**
 42      * determines fields to submit
 43      * @param {Object} request the xhr request object
 44      * @param {Object} context (Map)
 45      * @param {Node} item - item that triggered the event
 46      * @param {Node} parentItem - form element item is nested in
 47      * @param {Array} partialIds - ids fo PPS
 48      */
 49     encodeSubmittableFields : function(targetBuf, request, context, item,
 50                                        parentItem, partialIds) {
 51 
 52         try {
 53             if (!parentItem) {
 54                 this._onWarning(request, context, "myfaces._impl.xhrCore._AjaxUtils", "encodeSubmittableFields " + "Html-Component is not nested in a Form-Tag");
 55                 return null;
 56             }
 57 
 58             if (partialIds && partialIds.length > 0) {
 59                 this.encodePartialSubmit(parentItem, item, false, partialIds, targetBuf);
 60             } else {
 61                 // add all nodes
 62                 var eLen = parentItem.elements.length;
 63                 for (var e = 0; e < eLen; e++) {
 64                     this.encodeElement(parentItem.elements[e], targetBuf);
 65                 } // end of for (formElements)
 66             }
 67 
 68             this.appendIssuingItem(item, targetBuf);
 69         } catch (e) {
 70             this._onException(request, context, "myfaces._impl.xhrCore._AjaxUtils", "encodeSubmittableFields", e);
 71         }
 72     },
 73 
 74     /**
 75      * checks recursively if contained in PPS
 76      * the algorithm is as follows we have an issuing item
 77      * the parent form of the issuing item and a set of child ids which do not
 78      * have to be inputs, we scan now for those ids and all inputs which are childs
 79      * of those ids
 80      *
 81      * Now this algorithm is up for discussion because it is relatively complex
 82      * but for now we will leave it as it is.
 83      *
 84      * @param {Node} node - the root node of the partial page submit
 85      * @param {boolean} submitAll - if set to true, all elements within this node will
 86      * be added to the partial page submit
 87      * @param {Array} partialIds - an array of partial ids which should be used for the submit
 88      * @param {Array} targetBuf a target string buffer which receives the encoded elements
 89      */
 90     encodePartialSubmit : function(node, issuingItem, submitAll,
 91                                    partialIds, targetBuf) {
 92         var _Lang = myfaces._impl._util._Lang;
 93         var _Impl = myfaces._impl.core.Impl;
 94         var _Dom = myfaces._impl._util._Dom;
 95 
 96         var partialIdsFilter = function(curNode) {
 97             if (curNode.nodeType != 1) return false;
 98             if (submitAll && node != curNode) return true;
 99 
100             var id = curNode.id || curNode.name;
101 
102             return (id && _Lang.contains(partialIds, id)) || id == _Impl.P_VIEWSTATE;
103         };
104 
105         //shallow scan because we have a second scanning step, to find the encodable childs of
106         //the result nodes, that way we can reduce the number of nodes
107         var nodes = _Dom.findAll(node, partialIdsFilter, false);
108 
109         var allowedTagNames = {"input":true, "select":true, "textarea":true};
110 
111         if (nodes && nodes.length) {
112             for (var cnt = 0; cnt < nodes.length; cnt++) {
113                 //we can shortcut the form any other nodetype
114                 //must get a separate investigation
115                 var subNodes = (nodes[cnt].tagName.toLowerCase() == "form") ?
116                         node.elements :
117                         _Dom.findByTagNames(nodes[cnt], allowedTagNames, true);
118 
119                 if (subNodes && subNodes.length) {
120                     for (var cnt2 = 0; cnt2 < subNodes.length; cnt2++) {
121                         this.encodeElement(subNodes[cnt2], targetBuf);
122                     }
123                 } else {
124                     this.encodeElement(nodes[cnt], targetBuf);
125                 }
126             }
127         }
128 
129         this.appendViewState(node, targetBuf);
130     },
131 
132     /**
133      * appends the viewstate element if not given already
134      *
135      * @param parentNode
136      * @param targetBuf
137      *
138      * TODO dom level2 handling here, for dom level2 we can omit the check and readd the viewstate
139      */
140     appendViewState: function(parentNode, targetBuf) {
141         var _Dom = myfaces._impl._util._Dom;
142         var _Impl = myfaces._impl.core.Impl;
143 
144         //viewstate covered, do a preemptive check
145         if (targetBuf.hasKey(_Impl.P_VIEWSTATE)) return;
146 
147         var viewStates = _Dom.findByName(parentNode, _Impl.P_VIEWSTATE, true);
148         if (viewStates && viewStates.length) {
149             for (var cnt2 = 0; cnt2 < viewStates.length; cnt2++) {
150                 this.encodeElement(viewStates[cnt2], targetBuf);
151             }
152         }
153     },
154 
155     /**
156      * appends the issuing item if not given already
157      * @param item
158      * @param targetBuf
159      */
160     appendIssuingItem: function (item, targetBuf) {
161         // if triggered by a Button send it along
162         if (item && item.type && item.type.toLowerCase() == "submit") {
163             targetBuf.append(item.name, item.value);
164         }
165     },
166 
167 
168     /**
169      * encodes a single input element for submission
170      *
171      * @param {Node} element - to be encoded
172      * @param {} targetBuf - a target array buffer receiving the encoded strings
173      */
174     encodeElement : function(element, targetBuf) {
175 
176         //browser behavior no element name no encoding (normal submit fails in that case)
177         //https://issues.apache.org/jira/browse/MYFACES-2847
178         if (!element.name) {
179             return;
180         }
181 
182         var _RT = myfaces._impl.core._Runtime;
183         var name = element.name;
184         var tagName = element.tagName.toLowerCase();
185         var elemType = element.type;
186         if (elemType != null) {
187             elemType = elemType.toLowerCase();
188         }
189 
190         // routine for all elements
191         // rules:
192         // - process only inputs, textareas and selects
193         // - elements muest have attribute "name"
194         // - elements must not be disabled
195         if (((tagName == "input" || tagName == "textarea" || tagName == "select") &&
196                 (name != null && name != "")) && !element.disabled) {
197 
198             // routine for select elements
199             // rules:
200             // - if select-one and value-Attribute exist => "name=value"
201             // (also if value empty => "name=")
202             // - if select-one and value-Attribute don't exist =>
203             // "name=DisplayValue"
204             // - if select multi and multple selected => "name=value1&name=value2"
205             // - if select and selectedIndex=-1 don't submit
206             if (tagName == "select") {
207                 // selectedIndex must be >= 0 sein to be submittet
208                 if (element.selectedIndex >= 0) {
209                     var uLen = element.options.length;
210                     for (var u = 0; u < uLen; u++) {
211                         // find all selected options
212                         //var subBuf = [];
213                         if (element.options[u].selected) {
214                             var elementOption = element.options[u];
215                             targetBuf.append(name, (elementOption.getAttribute("value") != null) ?
216                                     elementOption.value : elementOption.text);
217                         }
218                     }
219                 }
220             }
221 
222             // routine for remaining elements
223             // rules:
224             // - don't submit no selects (processed above), buttons, reset buttons, submit buttons,
225             // - submit checkboxes and radio inputs only if checked
226             if ((tagName != "select" && elemType != "button"
227                     && elemType != "reset" && elemType != "submit" && elemType != "image")
228                     && ((elemType != "checkbox" && elemType != "radio") || element.checked)) {
229                 if ('undefined' != typeof element.files && element.files != null && _RT.getXHRLvl() >= 2 && element.files.length) {
230                     //xhr level2
231                     targetBuf.append(name, element.files[0]);
232                 } else {
233                     targetBuf.append(name, element.value);
234                 }
235             }
236 
237         }
238     },
239 
240     _finalize: function() {
241         delete this._onException;
242         delete this._onWarning;
243     }
244 
245 });