1 /*
  2  * JBoss, Home of Professional Open Source
  3  * Copyright 2013, Red Hat, Inc. and individual contributors
  4  * by the @authors tag. See the copyright.txt in the distribution for a
  5  * full listing of individual contributors.
  6  *
  7  * This is free software; you can redistribute it and/or modify it
  8  * under the terms of the GNU Lesser General Public License as
  9  * published by the Free Software Foundation; either version 2.1 of
 10  * the License, or (at your option) any later version.
 11  *
 12  * This software is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15  * Lesser General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU Lesser General Public
 18  * License along with this software; if not, write to the Free
 19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21  */
 22 
 23 window.RichFaces = window.RichFaces || {};
 24 RichFaces.jQuery = RichFaces.jQuery || window.jQuery;
 25 
 26 (function($, rf) {
 27 
 28     rf.csv = rf.csv || {};
 29 
 30     var _messages = {};
 31 
 32     var RE_MESSAGE_PATTERN = /\'?\{(\d+)\}\'?/g;
 33 
 34     var interpolateMessage = function (message, values) {
 35         if (message) {
 36             var msgObject = message.replace(RE_MESSAGE_PATTERN, "\n$1\n").split("\n");
 37             var value;
 38             for (var i = 1; i < msgObject.length; i += 2) {
 39                 value = values[msgObject[i]];
 40                 msgObject[i] = typeof value == "undefined" ? "" : value;
 41             }
 42             return msgObject.join('');
 43         } else {
 44             return "";
 45         }
 46     }
 47 
 48     var _value_query = function(control) {
 49         if (null !== control.value && undefined != control.value) {
 50             return control.value;
 51         } else {
 52             return "";
 53         }
 54     };
 55 
 56     var _check_query = function(control) {
 57         if (control.checked) {
 58             return true;
 59         } else {
 60             return false;
 61         }
 62     };
 63 
 64     var _addOption = function(value, option) {
 65         if (option.selected) {
 66             return value[value.length] = option.value;
 67         }
 68 
 69     };
 70 
 71     var valueExtractors = {
 72         hidden : function(control) {
 73             return _value_query(control);
 74         },
 75 
 76         text : function(control) {
 77             return _value_query(control);
 78         },
 79 
 80         textarea : function(control) {
 81             return _value_query(control);
 82         },
 83 
 84         'select-one' : function(control) {
 85             if (control.selectedIndex != -1) {
 86                 return _value_query(control);
 87             }
 88         },
 89 
 90         password : function(control) {
 91             return _value_query(control);
 92         },
 93 
 94         file : function(control) {
 95             return _value_query(control);
 96         },
 97 
 98         radio : function(control) {
 99             return _check_query(control);
100         },
101 
102         checkbox : function(control) {
103             return _check_query(control);
104         },
105 
106 
107         'select-multiple' : function(control) {
108             var cname = control.name;
109             var childs = control.childNodes;
110             var value = [];
111             for (var i = 0; i < childs.length; i++) {
112                 var child = childs[i];
113                 if (child.tagName === 'OPTGROUP') {
114                     var options = child.childNodes;
115                     for (var j = 0; j < options.length; j++) {
116                         value = _addOption(value, options[j]);
117                     }
118                 } else {
119                     value = _addOption(value, child);
120                 }
121             }
122             return value;
123         },
124 
125         // command inputs
126 
127 
128         // same as link, but have additional field - control, for input
129         // submit.
130         input : function(control) {
131             return _value_query(control);
132         }
133     };
134 
135     var getValue = function(element) {
136         var value = "";
137         if (valueExtractors[element.type]) {
138             value = valueExtractors[element.type](element);
139         } else if (undefined !== element.value) {
140             value = element.value;
141         } else {
142             var component = $(element);
143             // TODO: add getValue to baseComponent and change jsdocs
144             if (component) {
145                 if (typeof rf.component(component)["getValue"] === "function") {
146                     value = rf.component(component).getValue();
147                 } else {
148                     var nestedComponents = $("*", component).filter(":editable");
149                     if (nestedComponents) {
150                         var nestedComponent = nestedComponents[0];
151                         value = valueExtractors[nestedComponent.type](nestedComponent);
152                     }
153                 }
154             }
155         }
156         return value;
157     }
158 
159     var getLabel = function(component, id) {
160         if (component.p) {
161             return component.p.label || id;
162         }
163         return id;
164     }
165 
166     $.extend(rf.csv, {
167             RE_DIGITS: /^-?\d+$/,
168             RE_FLOAT: /^(-?\d+)?(\.(\d+)?(e[+-]?\d+)?)?$/,
169             // Messages API
170             addMessage: function (messagesObject) {
171                 $.extend(_messages, messagesObject);
172             },
173             getMessage: function(customMessage, messageId, values) {
174                 var message = customMessage ? customMessage : _messages[messageId] || {detail:"",summary:"",severity:0};
175                 return {detail:interpolateMessage(message.detail, values),summary:interpolateMessage(message.summary, values),severity:message.severity};
176             },
177             interpolateMessage: function(message, values) {
178                 return {detail:interpolateMessage(message.detail, values),summary:interpolateMessage(message.summary, values),severity:message.severity};
179             },
180             sendMessage: function (componentId, message) {
181                 rf.Event.fire(window.document, rf.Event.MESSAGE_EVENT_TYPE, {'sourceId':componentId, 'message':message});
182             },
183             clearMessage: function(componentId) {
184                 rf.Event.fire(window.document, rf.Event.MESSAGE_EVENT_TYPE, {'sourceId':componentId });
185             },
186             validate: function (event, id, element, params) {
187                 var element = rf.getDomElement(element || id);
188                 var value = getValue(element);
189                 var convertedValue;
190                 var converter = params.c;
191                 rf.csv.clearMessage(id);
192                 if (converter) {
193                     var label = getLabel(converter, id);
194                     try {
195                         if (converter.f)
196                             convertedValue = converter.f(value, id, getLabel(converter, id), converter.m);
197                     } catch (e) {
198                         e.severity = 2;
199                         rf.csv.sendMessage(id, e);
200                         return false;
201                     }
202                 } else {
203                     convertedValue = value;
204                 }
205                 var result = true
206                 var validators = params.v;
207                 var validationErrorMessage;
208                 if (validators) {
209                     var validatorFunction,validator;
210                     for (var i = 0; i < validators.length; i++) {
211                         try {
212                             validator = validators[i];
213                             validatorFunction = validator.f;
214                             if (validatorFunction) {
215                                 validatorFunction(convertedValue, getLabel(validator, id), validator.p, validator.m);
216                             }
217                         } catch (e) {
218                             validationErrorMessage = e;
219                             e.severity = 2;
220                             rf.csv.sendMessage(id, e);
221                             result = false;
222                         }
223                     }
224                 }
225                 if (!result && params.oninvalid instanceof Function) {
226                     params.oninvalid([validationErrorMessage]);
227                 }
228                 if (result) {
229                     if (!params.da && params.a) {
230                         params.a.call(element, event, id);
231                     } else if (params.onvalid instanceof Function) {
232                         params.onvalid();
233                     }
234                 }
235                 return result;
236             }
237         });
238 
239     /*
240      * convert all natural number formats
241      *
242      */
243     var _convertNatural = function(value, label, msg, min, max, sample) {
244         var result = null;
245         if (value) {
246             value = $.trim(value);
247             if (!rf.csv.RE_DIGITS.test(value) || (result = parseInt(value, 10)) < min || result > max) {
248                 throw rf.csv.interpolateMessage(msg, sample ? [value, sample, label] : [value,label]);
249             }
250         }
251         return result;
252     }
253 
254     var _convertReal = function(value, label, msg, sample) {
255         var result = null;
256         if (value) {
257             value = $.trim(value);
258             if (!rf.csv.RE_FLOAT.test(value) || isNaN(result = parseFloat(value))) {
259                 // TODO - check Float limits.
260                 throw rf.csv.interpolateMessage(msg, sample ? [value, sample, label] : [value,label]);
261             }
262         }
263         return result;
264     }
265     /*
266      * Converters implementation
267      */
268     $.extend(rf.csv, {
269             "convertBoolean": function (value, label, params, msg) {
270                 if (typeof value === "string") {
271                     var lcvalue = $.trim(value).toLowerCase();
272                     if (lcvalue === 'on' || lcvalue === 'true' || lcvalue === 'yes') {
273                         return true;
274                     }
275                 } else if (true === value) {
276                     return true;
277                 }
278                 return false;
279             },
280             "convertDate": function (value, label, params, msg) {
281                 var result;
282                 value = $.trim(value);
283                 // TODO - JSF date converter options.
284                 result = Date.parse(value);
285                 return result;
286             },
287             "convertByte": function (value, label, params, msg) {
288                 return _convertNatural(value, label, msg, -128, 127, 254);
289             },
290             "convertNumber": function (value, label, params, msg) {
291                 var result;
292                 value = $.trim(value);
293                 result = parseFloat(value);
294                 if (isNaN(result)) {
295                     throw rf.csv.interpolateMessage(msg, [value, 99, label]);
296                 }
297                 return result;
298             },
299             "convertFloat": function (value, label, params, msg) {
300                 return _convertReal(value, label, msg, 2000000000);
301             },
302             "convertDouble": function (value, label, params, msg) {
303                 return _convertReal(value, label, msg, 1999999);
304             },
305             "convertShort": function (value, label, params, msg) {
306                 return _convertNatural(value, label, msg, -32768, 32767, 32456);
307             },
308             "convertInteger": function (value, label, params, msg) {
309                 return _convertNatural(value, label, msg, -2147483648, 2147483648, 9346);
310             },
311             "convertCharacter": function (value, label, params, msg) {
312                 return _convertNatural(value, label, msg, 0, 65535);
313             },
314             "convertLong": function (value, label, params, msg) {
315                 return _convertNatural(value, label, msg, -9223372036854775808, 9223372036854775807, 98765432);
316             }
317         });
318 
319     var validateRange = function(value, label, params, msg) {
320         var isMinSet = typeof params.min === "number";// && params.min >0;
321         var isMaxSet = typeof params.max === "number";// && params.max >0;
322 
323         if (isMaxSet && value > params.max) {
324             throw rf.csv.interpolateMessage(msg, isMinSet ? [params.min,params.max,label] : [params.max,label]);
325         }
326         if (isMinSet && value < params.min) {
327             throw rf.csv.interpolateMessage(msg, isMaxSet ? [params.min,params.max,label] : [params.min,label]);
328         }
329     };
330 
331     var validateRegex = function(value, label, pattern, msg) {
332         if (typeof pattern != "string" || pattern.length == 0) {
333             throw rf.csv.getMessage(msg, 'REGEX_VALIDATOR_PATTERN_NOT_SET', []);
334         }
335 
336         var matchPattern = makePatternAMatch(pattern);
337         var re;
338         try {
339             re = new RegExp(matchPattern);
340         } catch (e) {
341             throw rf.csv.getMessage(msg, 'REGEX_VALIDATOR_MATCH_EXCEPTION', []);
342         }
343         if (!re.test(value)) {
344             throw rf.csv.interpolateMessage(msg, [pattern,label]);
345         }
346 
347     };
348 
349     var makePatternAMatch = function(pattern) {
350         if (! (pattern.slice(0, 1) === '^') ) {
351             pattern = '^' + pattern;
352         }
353         if (! (pattern.slice(-1) === '$') ) {
354             pattern = pattern + '$';
355         }
356         return pattern;
357     }
358     /*
359      * Validators implementation
360      */
361     $.extend(rf.csv, {
362             "validateLongRange": function (value, label, params, msg) {
363                 var type = typeof value;
364                 if (type !== "number") {
365                     if (type != "string") {
366                         throw rf.csv.getMessage(msg, 'LONG_RANGE_VALIDATOR_TYPE', [componentId, ""]);
367                     } else {
368                         value = $.trim(value);
369                         if (!rf.csv.RE_DIGITS.test(value) || (value = parseInt(value, 10)) == NaN) {
370                             throw rf.csv.getMessage(msg, 'LONG_RANGE_VALIDATOR_TYPE', [componentId, ""]);
371                         }
372                     }
373                 }
374 
375                 validateRange(value, label, params, msg);
376             },
377             "validateDoubleRange": function (value, label, params, msg) {
378                 var type = typeof value;
379                 if (type !== "number") {
380                     if (type !== "string") {
381                         throw rf.csv.getMessage(msg, 'DOUBLE_RANGE_VALIDATOR_TYPE', [componentId, ""]);
382                     } else {
383                         value = $.trim(value);
384                         if (!rf.csv.RE_FLOAT.test(value) || (value = parseFloat(value)) == NaN) {
385                             throw rf.csv.getMessage(msg, 'DOUBLE_RANGE_VALIDATOR_TYPE', [componentId, ""]);
386                         }
387                     }
388                 }
389 
390                 validateRange(value, label, params, msg);
391             },
392             "validateLength": function (value, label, params, msg) {
393                 var length = value ? value.length : 0;
394                 validateRange(length, label, params, msg);
395             },
396             "validateSize": function (value, label, params, msg) {
397                 var length = value ? value.length : 0;
398                 validateRange(length, label, params, msg);
399             },
400             "validateRegex": function (value, label, params, msg) {
401                 validateRegex(value, label, params.pattern, msg);
402             },
403             "validatePattern": function (value, label, params, msg) {
404                 validateRegex(value, label, params.regexp, msg);
405             },
406             "validateRequired": function (value, label, params, msg) {
407                 if (undefined === value || null === value || "" === value) {
408                     throw rf.csv.interpolateMessage(msg, [label]);
409                 }
410             },
411             "validateTrue": function (value, label, params, msg) {
412                 if (value !== true) {
413                     throw msg;
414                 }
415             },
416             "validateFalse": function (value, label, params, msg) {
417                 if (value !== false) {
418                     throw msg;
419                 }
420             },
421             "validateMax": function (value, label, params, msg) {
422                 if (value > params.value) {
423                     throw msg;
424                 }
425             },
426             "validateMin": function (value, label, params, msg) {
427                 if (value < params.value) {
428                     throw msg;
429                 }
430             }
431         });
432 
433 })(RichFaces.jQuery, RichFaces);