This commit is contained in:
2026-06-04 16:36:02 +03:00
parent 46b029c310
commit 8497fbe2b9
+341 -340
View File
@@ -4,381 +4,382 @@
export class Validator { export class Validator {
constructor(target) { constructor(target) {
this.target = target; this.target = target;
} }
static INPUT_STR = 1; static INPUT_STR = 1;
static INPUT_NUM = 2; static INPUT_NUM = 2;
static INPUT_FILE = 3; static INPUT_FILE = 3;
handlers = { handlers = {
required: (val, present) => present && val !== '' && val !== undefined && val !== null, required: (val, present) => present && val !== '' && val !== undefined && val !== null,
not_empty: (val, present) => present && val !== '' && val !== undefined && val !== null && val !== 0 && val !== '0', not_empty: (val, present) => present && val !== '' && val !== undefined && val !== null && val !== 0 && val !== '0',
sometimes: (_, present) => !present ? null : true, sometimes: (_, present) => !present ? null : true,
filled: (val, present) => !present ? null : val !== '' && val !== undefined && val !== null, filled: (val, present) => !present ? null : val !== '' && val !== undefined && val !== null,
present: (_, present) => present, present: (_, present) => present,
missing: (_, present) => !present, missing: (_, present) => !present,
nullable: (val, present) => (!present || val === '' || val === undefined || val === null) ? null: true, nullable: (val, present) => (!present || val === '' || val === undefined || val === null) ? null: true,
numeric: (val, present, arg, format) => { numeric: (val, present, arg, format) => {
format.value = Validator.INPUT_NUM; format.value = Validator.INPUT_NUM;
return !present ? null : !isNaN(val); return !present ? null : !isNaN(val);
}, },
integer: (val, present, arg, format) => { integer: (val, present, arg, format) => {
format.value = Validator.INPUT_NUM; format.value = Validator.INPUT_NUM;
return !present ? null : Number.isInteger(Number(val)); return !present ? null : Number.isInteger(Number(val));
}, },
boolean: (val, present, arg, format) => { boolean: (val, present, arg, format) => {
format.value = Validator.INPUT_NUM; format.value = Validator.INPUT_NUM;
return !present ? null : [true, false, 0, 1, '0', '1'].includes(val); return !present ? null : [true, false, 0, 1, '0', '1'].includes(val);
}, },
string: (val, present, arg, format) => { string: (val, present, arg, format) => {
format.value = Validator.INPUT_STR; format.value = Validator.INPUT_STR;
return !present ? null : typeof val === 'string'; return !present ? null : typeof val === 'string';
}, },
file: (val, present, arg, format) => { file: (val, present, arg, format) => {
format.value = Validator.INPUT_FILE; format.value = Validator.INPUT_FILE;
return !present ? null : ( return !present ? null : (
typeof val === 'object' && typeof val === 'object' &&
'error' in val && 'error' in val &&
'tmp_name' in val && 'tmp_name' in val &&
val.error === 0 val.error === 0
); );
}, },
image: (val, present, arg, format) => { image: (val, present, arg, format) => {
format.value = Validator.INPUT_FILE; format.value = Validator.INPUT_FILE;
if (!present) return null; if (!present) return null;
if ( if (
typeof val !== 'object' || typeof val !== 'object' ||
!('error' in val) || !('error' in val) ||
!('tmp_name' in val) || !('tmp_name' in val) ||
val.error !== 0 || val.error !== 0 ||
typeof val.name !== 'string' typeof val.name !== 'string'
) { ) {
return false; return false;
} }
const ext = val.name.split('.').pop().toLowerCase(); const ext = val.name.split('.').pop().toLowerCase();
return ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp'].includes(ext); return ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp'].includes(ext);
}, },
mimes: (val, present, arg, format) => { mimes: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
if ( if (
typeof val !== 'object' || typeof val !== 'object' ||
!('error' in val) || !('error' in val) ||
!('tmp_name' in val) || !('tmp_name' in val) ||
val.error !== 0 || val.error !== 0 ||
typeof val.name !== 'string' typeof val.name !== 'string'
) { ) {
return false; return false;
} }
const allowed = arg.split(',').map(ext => ext.trim().toLowerCase()); const allowed = arg.split(',').map(ext => ext.trim().toLowerCase());
const ext = val.name.split('.').pop().toLowerCase(); const ext = val.name.split('.').pop().toLowerCase();
return allowed.includes(ext); return allowed.includes(ext);
}, },
min: (val, present, arg, format) => { min: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length >= arg) || (format.value === Validator.INPUT_STR && val.length >= arg) ||
(format.value === Validator.INPUT_NUM && Number(val) >= arg) (format.value === Validator.INPUT_NUM && Number(val) >= arg)
); );
}, },
max: (val, present, arg, format) => { max: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length <= arg) || (format.value === Validator.INPUT_STR && val.length <= arg) ||
(format.value === Validator.INPUT_NUM && Number(val) <= arg) (format.value === Validator.INPUT_NUM && Number(val) <= arg)
); );
}, },
size: (val, present, arg, format) => { size: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length == arg) || (format.value === Validator.INPUT_STR && val.length == arg) ||
(format.value === Validator.INPUT_NUM && Number(val) == arg) (format.value === Validator.INPUT_NUM && Number(val) == arg)
); );
}, },
gt: (val, present, arg, format) => { gt: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length > arg) || (format.value === Validator.INPUT_STR && val.length > arg) ||
(format.value === Validator.INPUT_NUM && Number(val) > arg) (format.value === Validator.INPUT_NUM && Number(val) > arg)
); );
}, },
gte: (val, present, arg, format) => { gte: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length >= arg) || (format.value === Validator.INPUT_STR && val.length >= arg) ||
(format.value === Validator.INPUT_NUM && Number(val) >= arg) (format.value === Validator.INPUT_NUM && Number(val) >= arg)
); );
}, },
lt: (val, present, arg, format) => { lt: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length < arg) || (format.value === Validator.INPUT_STR && val.length < arg) ||
(format.value === Validator.INPUT_NUM && Number(val) < arg) (format.value === Validator.INPUT_NUM && Number(val) < arg)
); );
}, },
lte: (val, present, arg, format) => { lte: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
return ( return (
(format.value === Validator.INPUT_STR && val.length <= arg) || (format.value === Validator.INPUT_STR && val.length <= arg) ||
(format.value === Validator.INPUT_NUM && Number(val) <= arg) (format.value === Validator.INPUT_NUM && Number(val) <= arg)
); );
}, },
between: (val, present, arg, format) => { between: (val, present, arg, format) => {
if (!present) return null; if (!present) return null;
const [min, max] = arg.split(',').map(Number); const [min, max] = arg.split(',').map(Number);
if (isNaN(min) || isNaN(max)) return false; if (isNaN(min) || isNaN(max)) return false;
if (format.value === Validator.INPUT_STR) { if (format.value === Validator.INPUT_STR) {
const len = val.length; const len = val.length;
return len >= min && len <= max; return len >= min && len <= max;
} else if (format.value === Validator.INPUT_NUM) { } else if (format.value === Validator.INPUT_NUM) {
return Number(val) >= min && Number(val) <= max; return Number(val) >= min && Number(val) <= max;
} }
return false; return false;
}, },
accepted: (val, present) => !present ? null : ['yes', 'on', 1, '1', true, 'true'].includes(val), accepted: (val, present) => !present ? null : ['yes', 'on', 1, '1', true, 'true'].includes(val),
declined: (val, present) => !present ? null : ['no', 'off', 0, '0', false, 'false'].includes(val), declined: (val, present) => !present ? null : ['no', 'off', 0, '0', false, 'false'].includes(val),
starts_with: (val, present, arg) => { starts_with: (val, present, arg) => {
if (!present) return null; if (!present) return null;
return arg.split(',').some(prefix => val.startsWith(prefix)); return arg.split(',').some(prefix => val.startsWith(prefix));
}, },
ends_with: (val, present, arg) => { ends_with: (val, present, arg) => {
if (!present) return null; if (!present) return null;
return arg.split(',').some(suffix => val.endsWith(suffix)); return arg.split(',').some(suffix => val.endsWith(suffix));
}, },
alpha: (val, present) => !present ? null : /^[a-zA-Z]+$/.test(val), alpha: (val, present) => !present ? null : /^[a-zA-Z]+$/.test(val),
alpha_num: (val, present) => !present ? null : /^[a-zA-Z0-9]+$/.test(val), alpha_num: (val, present) => !present ? null : /^[a-zA-Z0-9]+$/.test(val),
uppercase: (val, present) => !present ? null : val === val.toUpperCase(), uppercase: (val, present) => !present ? null : val === val.toUpperCase(),
lowercase: (val, present) => !present ? null : val === val.toLowerCase(), lowercase: (val, present) => !present ? null : val === val.toLowerCase(),
domain: (val, present) => !present ? null : /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(val), domain: (val, present) => !present ? null : /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(val),
email: (val, present) => !present ? null : /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), email: (val, present) => !present ? null : /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),
url: (val, present) => { url: (val, present) => {
if (!present) return null; if (!present) return null;
try { try {
new URL(val); new URL(val);
return true; return true;
} catch { } catch {
return false; return false;
} }
}, },
ip: (val, present) => !present ? null : (this.handlers.ipv4(val, present) || this.handlers.ipv6(val, present) ), ip: (val, present) => !present ? null : (this.handlers.ipv4(val, present) || this.handlers.ipv6(val, present) ),
ipv4: (val, present) => !present ? null : /^(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)$/.test(val), ipv4: (val, present) => !present ? null : /^(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)$/.test(val),
ipv6: (val, present) => !present ? null : /^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4})$/.test(val), ipv6: (val, present) => !present ? null : /^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4})$/.test(val),
hash: (val, present) => !present ? null : /^[a-f0-9]+$/i.test(val), hash: (val, present) => !present ? null : /^[a-f0-9]+$/i.test(val),
in: (val, present, arg) => !present ? null : arg.split(',').includes(val), in: (val, present, arg) => !present ? null : arg.split(',').includes(val),
same: (val, present, arg) => !present ? null : val === this.target.elements[arg].value, same: (val, present, arg) => !present ? null : val === this.target.elements[arg].value,
json: (val, present) => { json: (val, present) => {
if (!present) return null; if (!present) return null;
try { try {
JSON.parse(val); JSON.parse(val);
return true; return true;
} catch { } catch {
return false; return false;
} }
}, },
date: (val, present, arg, format) => !present ? null : format.value === Validator.INPUT_STR && !isNaN(Date.parse(val)), date: (val, present, arg, format) => !present ? null : format.value === Validator.INPUT_STR && !isNaN(Date.parse(val)),
cnp: (val, present, arg, format) => !present ? null : this.validCNP(val) cnp: (val, present, arg, format) => !present ? null : this.validCNP(val)
}; };
getData() { getData() {
let fdata = new FormData(); let fdata = new FormData();
[...this.target.elements].forEach(el => { for (const el of [...this.target.elements]) {
if (!el.name || el.type == 'button' || el.type == 'submit') { if (!el.name || el.type == 'button' || el.type == 'submit') {
return true; continue;
} }
let value; let value;
let displayedVal = null; let displayedVal = null;
const widget = el.dxWidget(); const widget = el.dxWidget();
if (widget) { if (widget) {
value = widget.getValue(); value = widget.getValue();
displayedVal = el.value.trim(); displayedVal = el.value.trim();
} }
else { else {
value = el.value.trim(); value = el.value.trim();
} }
switch (el.type) { switch (el.type) {
case 'file': case 'file':
let uploader = el.dxData('uploader'); let uploader = el.dxData('uploader');
if (uploader) { if (uploader) {
uploader.getFiles().forEach(filename => fdata.append('file__' + el.name + '[]', filename)); uploader.getFiles().forEach(filename => fdata.append('file__' + el.name + '[]', filename));
} }
break; break;
case 'radio': case 'radio':
if (!fdata.has(el.name)) { if (!fdata.has(el.name)) {
fdata.set(el.name, ''); fdata.set(el.name, '');
} }
if (el.checked) { if (el.checked) {
fdata.set(el.name, value); fdata.set(el.name, value);
} }
break; break;
case 'checkbox': case 'checkbox':
if (el.checked) { if (el.checked) {
fdata.append(el.name + '[]', value ); fdata.append(el.name + '[]', value );
} }
break; break;
case 'select-multiple': case 'select-multiple':
for (let j = 0; j < el.options.length; j++) { for (let j = 0; j < el.options.length; j++) {
if (el.options[j].selected && el.options[j].value != 0) { if (el.options[j].selected && el.options[j].value != 0) {
fdata.append(el.name + '[]', /^\d+$/.test(el.options[j].value) fdata.append(el.name + '[]', /^\d+$/.test(el.options[j].value)
? parseInt(el.options[j].value) ? parseInt(el.options[j].value)
: el.options[j].value : el.options[j].value
); );
} }
} }
break; break;
default: default:
if (fdata.has(el.name)) { if (fdata.has(el.name) ) {
fdata.append(el.name + '[]', fdata.get(el.name) ); if (!el.name.endsWith('[]') ) {
fdata.delete(el.name); fdata.delete(el.name);
fdata.append(el.name + '[]', value); fdata.append(el.name + '[]', value);
} continue;
else if (fdata.has(el.name + '[]')) { }
fdata.append(el.name + '[]', value);
}
else {
fdata.set(el.name, value); fdata.append(el.name, value);
continue;
}
if (displayedVal) { fdata.set(el.name, value);
fdata.set(el.name + '_value', displayedVal);
}
}
}
});
return fdata; if (displayedVal) {
} fdata.set(el.name + '_value', displayedVal);
}
}
}
validCNP(value) { return fdata;
}
let i = 0 , year = 0 , hashResult = 0 , cnp = [] , hashTable = [ 2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9 ]; validCNP(value) {
if ( value.length !== 13 ) { return false; } let i = 0 , year = 0 , hashResult = 0 , cnp = [] , hashTable = [ 2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9 ];
for ( i = 0 ; i < 13 ; i++ ) { if ( value.length !== 13 ) { return false; }
cnp[i] = parseInt( value.charAt(i) , 10 ); for ( i = 0 ; i < 13 ; i++ ) {
if( isNaN( cnp[i] ) ) { return false; } cnp[i] = parseInt( value.charAt(i) , 10 );
if( i < 12 ) { hashResult = hashResult + ( cnp[i] * hashTable[i] ); } if( isNaN( cnp[i] ) ) { return false; }
}
hashResult = hashResult % 11; if( i < 12 ) { hashResult = hashResult + ( cnp[i] * hashTable[i] ); }
}
if( hashResult === 10 ) { hashResult = 1; } hashResult = hashResult % 11;
year = (cnp[1]*10)+cnp[2]; if( hashResult === 10 ) { hashResult = 1; }
switch( cnp[0] ) { year = (cnp[1]*10)+cnp[2];
case 1 : case 2 : { year += 1900; } break;
case 3 : case 4 : { year += 1800; } break;
case 5 : case 6 : { year += 2000; } break;
case 7 : case 8 : case 9 : { year += 2000; if( year > ( parseInt( new Date().getFullYear() , 10 ) - 14 ) ) { year -= 100; } } break;
default : { return false; }
}
if( year < 1800 || year > 2099 ) { return false; } switch( cnp[0] ) {
case 1 : case 2 : { year += 1900; } break;
case 3 : case 4 : { year += 1800; } break;
case 5 : case 6 : { year += 2000; } break;
case 7 : case 8 : case 9 : { year += 2000; if( year > ( parseInt( new Date().getFullYear() , 10 ) - 14 ) ) { year -= 100; } } break;
default : { return false; }
}
return ( cnp[12] === hashResult ); if( year < 1800 || year > 2099 ) { return false; }
}
return ( cnp[12] === hashResult );
}
} }