1/** 2 * Yii validation module. 3 * 4 * This JavaScript module provides the validation methods for the built-in validators. 5 * 6 * @link https://www.yiiframework.com/ 7 * @copyright Copyright (c) 2008 Yii Software LLC 8 * @license https://www.yiiframework.com/license/ 9 * @author Qiang Xue <[email protected]> 10 * @since 2.0 11 */ 12 13yii.validation = (function ($) { 14 var pub = { 15 isEmpty: function (value) { 16 return value === null || value === undefined || ($.isArray(value) && value.length === 0) || value === ''; 17 }, 18 19 addMessage: function (messages, message, value) { 20 messages.push(message.replace(/\{value\}/g, value)); 21 }, 22 23 required: function (value, messages, options) { 24 var valid = false; 25 if (options.requiredValue === undefined) { 26 var isString = typeof value == 'string' || value instanceof String; 27 if (options.strict && value !== undefined || !options.strict && !pub.isEmpty(isString ? trimString(value) : value)) { 28 valid = true; 29 } 30 } else if (!options.strict && value == options.requiredValue || options.strict && value === options.requiredValue) { 31 valid = true; 32 } 33 34 if (!valid) { 35 pub.addMessage(messages, options.message, value); 36 } 37 }, 38 39 // "boolean" is a reserved keyword in older versions of ES so it's quoted for IE < 9 support 40 'boolean': function (value, messages, options) { 41 if (options.skipOnEmpty && pub.isEmpty(value)) { 42 return; 43 } 44 var valid = !options.strict && (value == options.trueValue || value == options.falseValue) 45 || options.strict && (value === options.trueValue || value === options.falseValue); 46 47 if (!valid) { 48 pub.addMessage(messages, options.message, value); 49 } 50 }, 51 52 string: function (value, messages, options) { 53 if (options.skipOnEmpty && pub.isEmpty(value)) { 54 return; 55 } 56 57 if (typeof value !== 'string') { 58 pub.addMessage(messages, options.message, value); 59 return; 60 } 61 62 if (options.is !== undefined && value.length != options.is) { 63 pub.addMessage(messages, options.notEqual, value); 64 return; 65 } 66 if (options.min !== undefined && value.length < options.min) { 67 pub.addMessage(messages, options.tooShort, value); 68 } 69 if (options.max !== undefined && value.length > options.max) { 70 pub.addMessage(messages, options.tooLong, value); 71 } 72 }, 73 74 file: function (attribute, messages, options) { 75 var files = getUploadedFiles(attribute, messages, options); 76 $.each(files, function (i, file) { 77 validateFile(file, messages, options); 78 }); 79 }, 80 81 image: function (attribute, messages, options, deferredList) { 82 var files = getUploadedFiles(attribute, messages, options); 83 $.each(files, function (i, file) { 84 validateFile(file, messages, options); 85 86 // Skip image validation if FileReader API is not available 87 if (typeof FileReader === "undefined") { 88 return; 89 } 90 91 var deferred = $.Deferred(); 92 pub.validateImage(file, messages, options, deferred, new FileReader(), new Image()); 93 deferredList.push(deferred); 94 }); 95 }, 96 97 validateImage: function (file, messages, options, deferred, fileReader, image) { 98 image.onload = function () { 99 validateImageSize(file, image, messages, options); 100 deferred.resolve(); 101 }; 102 103 image.onerror = function () { 104 messages.push(options.notImage.replace(/\{file\}/g, file.name)); 105 deferred.resolve(); 106 }; 107 108 fileReader.onload = function () { 109 image.src = this.result; 110 }; 111 112 // Resolve deferred if there was error while reading data 113 fileReader.onerror = function () { 114 deferred.resolve(); 115 }; 116
117 fileReader.readAsDataURL(file); 118 }, 119 120 number: function (value, messages, options) { 121 if (options.skipOnEmpty && pub.isEmpty(value)) { 122 return; 123 } 124 125 if (typeof value === 'string' && !options.pattern.test(value)) { 126 pub.addMessage(messages, options.message, value); 127 return; 128 } 129 130 if (options.min !== undefined && value < options.min) { 131 pub.addMessage(messages, options.tooSmall, value); 132 } 133 if (options.max !== undefined && value > options.max) { 134 pub.addMessage(messages, options.tooBig, value); 135 } 136 }, 137 138 range: function (value, messages, options) { 139 if (options.skipOnEmpty && pub.isEmpty(value)) { 140 return; 141 } 142 143 if (!options.allowArray && $.isArray(value)) { 144 pub.addMessage(messages, options.message, value); 145 return; 146 } 147 148 var inArray = true; 149 150 $.each($.isArray(value) ? value : [value], function (i, v) { 151 if ($.inArray(v, options.range) == -1) { 152 inArray = false; 153 return false; 154 } else { 155 return true; 156 } 157 }); 158 159 if (options.not === undefined) { 160 options.not = false; 161 } 162 163 if (options.not === inArray) { 164 pub.addMessage(messages, options.message, value); 165 } 166 }, 167 168 regularExpression: function (value, messages, options) { 169 if (options.skipOnEmpty && pub.isEmpty(value)) { 170 return; 171 } 172 173 if (!options.not && !options.pattern.test(value) || options.not && options.pattern.test(value)) { 174 pub.addMessage(messages, options.message, value); 175 } 176 }, 177 178 email: function (value, messages, options) { 179 if (options.skipOnEmpty && pub.isEmpty(value)) { 180 return; 181 } 182 183 var valid = true, 184 regexp = /^((?:"?([^"]*)"?\s)?)(?:\s+)?(?:(<?)((.+)@([^>]+))(>?))$/, 185 matches = regexp.exec(value); 186 187 if (matches === null) { 188 valid = false; 189 } else { 190 var localPart = matches[5], 191 domain = matches[6]; 192 193 if (options.enableIDN) { 194 localPart = punycode.toASCII(localPart); 195 domain = punycode.toASCII(domain); 196 197 value = matches[1] + matches[3] + localPart + '@' + domain + matches[7]; 198 } 199 200 if (localPart.length > 64) { 201 valid = false; 202 } else if ((localPart + '@' + domain).length > 254) { 203 valid = false; 204 } else { 205 valid = options.pattern.test(value) || (options.allowName && options.fullPattern.test(value)); 206 } 207 } 208 209 if (!valid) { 210 pub.addMessage(messages, options.message, value); 211 } 212 }, 213 214 url: function (value, messages, options) { 215 if (options.skipOnEmpty && pub.isEmpty(value)) { 216 return; 217 } 218 219 if (options.defaultScheme && !/:\/\//.test(value)) { 220 value = options.defaultScheme + '://' + value; 221 } 222 223 var valid = true; 224 225 if (options.enableIDN) { 226 var matches = /^([^:]+):\/\/([^\/]+)(.*)$/.exec(value); 227 if (matches === null) { 228 valid = false; 229 } else { 230 value = matches[1] + '://' + punycode.toASCII(matches[2]) + matches[3]; 231 } 232 } 233 234 if (!valid || !options.pattern.test(value)) { 235 pub.addMessage(messages, options.message, value); 236 } 237 }, 238 239 trim: function ($form, attribute, options, value) { 240 var $input = $form.find(attribute.input); 241 if ($input.is(':checkbox, :radio')) { 242 return value; 243 } 244 245 value = $input.val(); 246 if ( 247 (!options.skipOnEmpty || !pub.isEmpty(value)) 248 && (!options.skipOnArray || !Array.isArray(value)) 249 ) { 250 if (Array.isArray(value)) { 251 for (var i = 0; i < value.length; i++) { 252 value[i] = trimString(value[i], options); 253 } 254 } else { 255 value = trimString(value, options); 256 } 257 $input.val(value); 258 } 259 260 return value; 261 }, 262 263 captcha: function (value, messages, options) { 264 if (options.skipOnEmpty && pub.isEmpty(value)) { 265 return; 266 } 267 268 // CAPTCHA may be updated via AJAX and the updated hash is stored in body data 269 var hash = $('body').data(options.hashKey); 270 hash = hash == null ? options.hash : hash[options.caseSensitive ? 0 : 1]; 271 var v = options.caseSensitive ? value : value.toLowerCase(); 272 for (var i = v.length - 1, h = 0; i >= 0; --i) { 273 h += v.charCodeAt(i) << i; 274 } 275 if (h != hash) { 276 pub.addMessage(messages, options.message, value); 277 } 278 }, 279 280 compare: function (value, messages, options, $form) { 281 if (options.skipOnEmpty && pub.isEmpty(value)) { 282 return; 283 } 284 285 var compareValue, 286 valid = true; 287 if (options.compareAttribute === undefined) { 288 compareValue = options.compareValue; 289 } else { 290 var $target = $('#' + options.compareAttribute); 291 if (!$target.length) { 292 $target = $form.find('[name="' + options.compareAttributeName + '"]'); 293 } 294 compareValue = $target.val(); 295 } 296 297 if (options.type === 'number') { 298 value = value ? parseFloat(value) : 0; 299 compareValue = compareValue ? parseFloat(compareValue) : 0; 300 } 301 switch (options.operator) { 302 case '==': 303 valid = value == compareValue; 304 break; 305 case '===': 306 valid = value === compareValue; 307 break; 308 case '!=': 309 valid = value != compareValue; 310 break; 311 case '!==': 312 valid = value !== compareValue; 313 break; 314 case '>': 315 valid = value > compareValue; 316 break; 317 case '>=': 318 valid = value >= compareValue; 319 break; 320 case '<': 321 valid = value < compareValue; 322 break; 323 case '<=': 324 valid = value <= compareValue; 325 break; 326 default: 327 valid = false;
328 break; 329 } 330 331 if (!valid) { 332 pub.addMessage(messages, options.message, value); 333 } 334 }, 335 336 ip: function (value, messages, options) { 337 if (options.skipOnEmpty && pub.isEmpty(value)) { 338 return; 339 } 340 341 var negation = null, 342 cidr = null, 343 matches = new RegExp(options.ipParsePattern).exec(value); 344 if (matches) { 345 negation = matches[1] || null; 346 value = matches[2]; 347 cidr = matches[4] || null; 348 } 349 350 if (options.subnet === true && cidr === null) { 351 pub.addMessage(messages, options.messages.noSubnet, value); 352 return; 353 } 354 if (options.subnet === false && cidr !== null) { 355 pub.addMessage(messages, options.messages.hasSubnet, value); 356 return; 357 } 358 if (options.negation === false && negation !== null) { 359 pub.addMessage(messages, options.messages.message, value); 360 return; 361 } 362 363 var ipVersion = value.indexOf(':') === -1 ? 4 : 6; 364 if (ipVersion == 6) { 365 if (!(new RegExp(options.ipv6Pattern)).test(value)) { 366 pub.addMessage(messages, options.messages.message, value); 367 } 368 if (!options.ipv6) { 369 pub.addMessage(messages, options.messages.ipv6NotAllowed, value); 370 } 371 } else { 372 if (!(new RegExp(options.ipv4Pattern)).test(value)) { 373 pub.addMessage(messages, options.messages.message, value); 374 } 375 if (!options.ipv4) { 376 pub.addMessage(messages, options.messages.ipv4NotAllowed, value); 377 } 378 } 379 } 380 }; 381 382 function getUploadedFiles(attribute, messages, options) 383 { 384 // Skip validation if File API is not available 385 if (typeof File === "undefined") { 386 return []; 387 } 388 389 var fileInput = $(attribute.input, attribute.$form).get(0); 390 391 // Skip validation if file input does not exist 392 // (in case file inputs are added dynamically and no file input has been added to the form) 393 if (typeof fileInput === "undefined") { 394 return []; 395 } 396 397 var files = fileInput.files; 398 if (!files) { 399 messages.push(options.message); 400 return []; 401 } 402 403 if (files.length === 0) { 404 if (!options.skipOnEmpty) { 405 messages.push(options.uploadRequired); 406 } 407 408 return []; 409 } 410 411 if (options.maxFiles && options.maxFiles < files.length) { 412 messages.push(options.tooMany); 413 return []; 414 } 415 416 return files; 417 } 418 419 function validateFile(file, messages, options) 420 { 421 if (options.extensions && options.extensions.length > 0) { 422 var found = false; 423 var filename = file.name.toLowerCase(); 424 425 for (var index = 0; index < options.extensions.length; index++) { 426 var ext = options.extensions[index].toLowerCase(); 427 if ((ext === '' && filename.indexOf('.') === -1) || (filename.substr(filename.length - options.extensions[index].length - 1) === ('.' + ext))) { 428 found = true; 429 break; 430 } 431 } 432 433 if (!found) { 434 messages.push(options.wrongExtension.replace(/\{file\}/g, file.name)); 435 } 436 } 437 438 if (options.mimeTypes && options.mimeTypes.length > 0) { 439 if (!validateMimeType(options.mimeTypes, file.type)) { 440 messages.push(options.wrongMimeType.replace(/\{file\}/g, file.name)); 441 } 442 } 443 444 if (options.maxSize && options.maxSize < file.size) { 445 messages.push(options.tooBig.replace(/\{file\}/g, file.name)); 446 } 447 448 if (options.minSize && options.minSize > file.size) { 449 messages.push(options.tooSmall.replace(/\{file\}/g, file.name)); 450 } 451 } 452 453 function validateMimeType(mimeTypes, fileType) 454 { 455 for (var i = 0, len = mimeTypes.length; i < len; i++) { 456 if (new RegExp(mimeTypes[i]).test(fileType)) { 457 return true; 458 } 459 } 460 461 return false;
462 } 463 464 function validateImageSize(file, image, messages, options) 465 { 466 if (options.minWidth && image.width < options.minWidth) { 467 messages.push(options.underWidth.replace(/\{file\}/g, file.name)); 468 } 469 470 if (options.maxWidth && image.width > options.maxWidth) { 471 messages.push(options.overWidth.replace(/\{file\}/g, file.name)); 472 } 473 474 if (options.minHeight && image.height < options.minHeight) { 475 messages.push(options.underHeight.replace(/\{file\}/g, file.name)); 476 } 477 478 if (options.maxHeight && image.height > options.maxHeight) { 479 messages.push(options.overHeight.replace(/\{file\}/g, file.name)); 480 } 481 } 482 483 /** 484 * PHP: `trim($path, ' /')`, JS: `yii.helpers.trim(path, {chars: ' /'})` 485 */ 486 function trimString(value, options = {skipOnEmpty: true, chars: null}) 487 { 488 if (options.skipOnEmpty !== false && pub.isEmpty(value)) { 489 return value; 490 } 491 492 value = new String(value); 493 if (options.chars || !String.prototype.trim) { 494 var chars = !options.chars 495 ? ' \\s\xA0' 496 : options.chars.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\$1'); 497 498 return value.replace(new RegExp('^[' + chars + ']+|[' + chars + ']+$', 'g'), ''); 499 } 500 501 return value.trim(); 502 } 503 504 return pub; 505})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.