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 // Skip validation if File API is not available 384 if (typeof File === "undefined") { 385 return []; 386 } 387 388 var fileInput = $(attribute.input, attribute.$form).get(0); 389 390 // Skip validation if file input does not exist 391 // (in case file inputs are added dynamically and no file input has been added to the form) 392 if (typeof fileInput === "undefined") { 393 return []; 394 } 395 396 var files = fileInput.files; 397 if (!files) { 398 messages.push(options.message); 399 return []; 400 } 401 402 if (files.length === 0) { 403 if (!options.skipOnEmpty) { 404 messages.push(options.uploadRequired); 405 } 406 407 return []; 408 } 409 410 if (options.maxFiles && options.maxFiles < files.length) { 411 messages.push(options.tooMany); 412 return []; 413 } 414 415 return files; 416 } 417 418 function validateFile(file, messages, options) { 419 if (options.extensions && options.extensions.length > 0) { 420 var found = false; 421 var filename = file.name.toLowerCase(); 422 423 for (var index=0; index < options.extensions.length; index++) { 424 var ext = options.extensions[index].toLowerCase(); 425 if ((ext === '' && filename.indexOf('.') === -1) || (filename.substr(filename.length - options.extensions[index].length - 1) === ('.' + ext))) { 426 found = true; 427 break; 428 } 429 } 430 431 if (!found) { 432 messages.push(options.wrongExtension.replace(/\{file\}/g, file.name)); 433 } 434 } 435 436 if (options.mimeTypes && options.mimeTypes.length > 0) { 437 if (!validateMimeType(options.mimeTypes, file.type)) { 438 messages.push(options.wrongMimeType.replace(/\{file\}/g, file.name)); 439 } 440 } 441 442 if (options.maxSize && options.maxSize < file.size) { 443 messages.push(options.tooBig.replace(/\{file\}/g, file.name)); 444 } 445 446 if (options.minSize && options.minSize > file.size) { 447 messages.push(options.tooSmall.replace(/\{file\}/g, file.name)); 448 } 449 } 450 451 function validateMimeType(mimeTypes, fileType) { 452 for (var i = 0, len = mimeTypes.length; i < len; i++) { 453 if (new RegExp(mimeTypes[i]).test(fileType)) { 454 return true; 455 } 456 } 457 458 return false;
459 } 460 461 function validateImageSize(file, image, messages, options) { 462 if (options.minWidth && image.width < options.minWidth) { 463 messages.push(options.underWidth.replace(/\{file\}/g, file.name)); 464 } 465 466 if (options.maxWidth && image.width > options.maxWidth) { 467 messages.push(options.overWidth.replace(/\{file\}/g, file.name)); 468 } 469 470 if (options.minHeight && image.height < options.minHeight) { 471 messages.push(options.underHeight.replace(/\{file\}/g, file.name)); 472 } 473 474 if (options.maxHeight && image.height > options.maxHeight) { 475 messages.push(options.overHeight.replace(/\{file\}/g, file.name)); 476 } 477 } 478 479 /** 480 * PHP: `trim($path, ' /')`, JS: `yii.helpers.trim(path, {chars: ' /'})` 481 */ 482 function trimString(value, options = {skipOnEmpty: true, chars: null}) { 483 if (options.skipOnEmpty !== false && pub.isEmpty(value)) { 484 return value; 485 } 486 487 value = new String(value); 488 if (options.chars || !String.prototype.trim) { 489 var chars = !options.chars 490 ? ' \\s\xA0' 491 : options.chars.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\$1'); 492 493 return value.replace(new RegExp('^[' + chars + ']+|[' + chars + ']+$', 'g'), ''); 494 } 495 496 return value.trim(); 497 } 498 499 return pub; 500})(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.