      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);