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