derafu-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/package.json +47 -0
- package/src/__.js +203 -0
- package/src/derafu.js +140 -0
- package/src/form/fields.js +320 -0
- package/src/form/tables.js +140 -0
- package/src/form/validation.js +482 -0
- package/src/form.js +45 -0
- package/src/tabs.js +267 -0
- package/src/ui.js +493 -0
- package/src/utils.js +353 -0
- package/src/validation/l10n-cl.js +66 -0
package/src/utils.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/*! Utility functions | (c) 2025 Derafu DEV | MIT */
|
|
2
|
+
|
|
3
|
+
// Object where utility functions will be assigned.
|
|
4
|
+
const Utils = {};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates a secure password using alphanumeric characters and special
|
|
8
|
+
* symbols.
|
|
9
|
+
* If the browser supports crypto.getRandomValues(), it is used to ensure
|
|
10
|
+
* cryptographic randomness. Otherwise, Math.random() is used as a less secure
|
|
11
|
+
* alternative.
|
|
12
|
+
*
|
|
13
|
+
* @param {number} length - Desired password length. Default length is 12
|
|
14
|
+
* characters.
|
|
15
|
+
* @returns {string} Password generated securely.
|
|
16
|
+
*/
|
|
17
|
+
Utils.generatePassword = function (length) {
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
// Default length of 12 characters, considered secure for most purposes.
|
|
21
|
+
length = length || 12;
|
|
22
|
+
|
|
23
|
+
// Warning if the requested length is less than 8 characters.
|
|
24
|
+
if (length < 8) {
|
|
25
|
+
console.warn(
|
|
26
|
+
'Password length is less than 8 characters. A minimum ' +
|
|
27
|
+
'length of 8 characters is recommended for greater security.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const charset =
|
|
32
|
+
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-=';
|
|
33
|
+
let password = '';
|
|
34
|
+
|
|
35
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
36
|
+
const array = new Uint32Array(length);
|
|
37
|
+
window.crypto.getRandomValues(array);
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < length; i++) {
|
|
40
|
+
password += charset.charAt(array[i] % charset.length);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
// If for some reason the browser doesn't support crypto, which is
|
|
44
|
+
// unlikely in modern browsers.
|
|
45
|
+
for (let i = 0; i < length; i++) {
|
|
46
|
+
password += charset.charAt(
|
|
47
|
+
Math.floor(Math.random() * charset.length),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return password;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Gets the value of a cookie by its name.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} name - Cookie name.
|
|
59
|
+
* @returns {string|null} Cookie value or null if not found.
|
|
60
|
+
*/
|
|
61
|
+
Utils.getCookie = function (name) {
|
|
62
|
+
'use strict';
|
|
63
|
+
|
|
64
|
+
// Check if we're in a browser environment.
|
|
65
|
+
if (typeof document === 'undefined') {
|
|
66
|
+
// Node.js environment - cookies not available.
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const cookieArray = document.cookie.split(';');
|
|
71
|
+
for (let i = 0; i < cookieArray.length; i++) {
|
|
72
|
+
const cookiePair = cookieArray[i].split('=');
|
|
73
|
+
if (name === cookiePair[0].trim()) {
|
|
74
|
+
return decodeURIComponent(cookiePair[1]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gets the user's language, either from a cookie or from the browser, and
|
|
82
|
+
* verifies if it's in a list of allowed languages. If not, returns a default
|
|
83
|
+
* language.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} [cookieName] - Cookie name to search for. Default is
|
|
86
|
+
* 'app_user_language'.
|
|
87
|
+
* @returns {string} Language code to use.
|
|
88
|
+
*/
|
|
89
|
+
Utils.getUserLanguage = function (cookieName) {
|
|
90
|
+
'use strict';
|
|
91
|
+
if (Utils._userLanguage) {
|
|
92
|
+
return Utils._userLanguage;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// List of allowed languages (the first language will be the default).
|
|
96
|
+
// Format: Accept-Language HTTP header (generally represented in lowercase).
|
|
97
|
+
const allowedLanguages = ['es-cl', 'es-es', 'en-us'];
|
|
98
|
+
|
|
99
|
+
// Check if we're in a browser environment.
|
|
100
|
+
if (typeof navigator === 'undefined') {
|
|
101
|
+
// Node.js environment - return default language.
|
|
102
|
+
Utils._userLanguage = allowedLanguages[0];
|
|
103
|
+
return allowedLanguages[0];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Try to get language from a cookie.
|
|
107
|
+
if (cookieName === undefined) {
|
|
108
|
+
cookieName = 'app_user_language';
|
|
109
|
+
}
|
|
110
|
+
const cookieLanguage = Utils.getCookie(cookieName);
|
|
111
|
+
if (cookieLanguage) {
|
|
112
|
+
const cookieLanguageLower = cookieLanguage.toLowerCase();
|
|
113
|
+
if (allowedLanguages.includes(cookieLanguageLower)) {
|
|
114
|
+
Utils._userLanguage = cookieLanguageLower;
|
|
115
|
+
return cookieLanguageLower;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Get browser language.
|
|
120
|
+
const browserLanguage = navigator.language.toLowerCase();
|
|
121
|
+
if (allowedLanguages.includes(browserLanguage)) {
|
|
122
|
+
Utils._userLanguage = browserLanguage;
|
|
123
|
+
return browserLanguage;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Return default language if browser language is not in the list.
|
|
127
|
+
Utils._userLanguage = allowedLanguages[0];
|
|
128
|
+
return allowedLanguages[0];
|
|
129
|
+
};
|
|
130
|
+
Utils._userLanguage = null;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Method that formats a number using thousands separator. If 'decimalDigits' is
|
|
134
|
+
* not specified, 0 is assumed. If 'true' is passed, 2 decimals are used.
|
|
135
|
+
*
|
|
136
|
+
* @param {number} n - Number to format.
|
|
137
|
+
* @param {number|boolean} decimalDigits - Number of decimal digits or true to
|
|
138
|
+
* use 2. If not provided, 0 is assumed.
|
|
139
|
+
* @param {string} [language] - Language to use for number formatting. Default
|
|
140
|
+
* is obtained with Utils.getUserLanguage().
|
|
141
|
+
* @returns {string} Number formatted according to locale.
|
|
142
|
+
*/
|
|
143
|
+
Utils.num = function (n, decimalDigits, language) {
|
|
144
|
+
'use strict';
|
|
145
|
+
if (typeof n === 'undefined') {
|
|
146
|
+
return '0';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (typeof n === 'string') {
|
|
150
|
+
n = n.includes('.') ? parseFloat(n) : parseInt(n, 10);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (typeof n !== 'number' || isNaN(n)) {
|
|
154
|
+
console.error('num: The provided argument is not a valid number.');
|
|
155
|
+
return n;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (decimalDigits === undefined) {
|
|
159
|
+
decimalDigits = 0;
|
|
160
|
+
}
|
|
161
|
+
decimalDigits = decimalDigits === true ? 2 : decimalDigits;
|
|
162
|
+
if (
|
|
163
|
+
typeof decimalDigits !== 'number' ||
|
|
164
|
+
isNaN(decimalDigits) ||
|
|
165
|
+
decimalDigits < 0
|
|
166
|
+
) {
|
|
167
|
+
console.error(
|
|
168
|
+
'num: The provided number of decimal digits is not valid.',
|
|
169
|
+
);
|
|
170
|
+
return n;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const options = {
|
|
174
|
+
minimumFractionDigits: decimalDigits,
|
|
175
|
+
maximumFractionDigits: decimalDigits,
|
|
176
|
+
};
|
|
177
|
+
if (language === undefined) {
|
|
178
|
+
language = Utils.getUserLanguage();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Map language codes to proper locales for number formatting.
|
|
182
|
+
let locale = language;
|
|
183
|
+
if (language === 'es-cl') {
|
|
184
|
+
locale = 'es-CL';
|
|
185
|
+
} else if (language === 'es-es') {
|
|
186
|
+
locale = 'es-ES';
|
|
187
|
+
} else if (language === 'en-us') {
|
|
188
|
+
locale = 'en-US';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return new Intl.NumberFormat(locale, options).format(n);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Checks if an object, array, or primitive value is empty. Considers as empty
|
|
196
|
+
* any null, undefined, empty string, NaN number, invalid date, empty array, a
|
|
197
|
+
* literal object without properties, or an HTML element of type input, select,
|
|
198
|
+
* or textarea with empty value.
|
|
199
|
+
*
|
|
200
|
+
* @param {Object|Array|Date|String|Number|HTMLElement} obj - Object, array,
|
|
201
|
+
* primitive value, or HTML element to check.
|
|
202
|
+
* @returns {boolean} Returns true if empty, otherwise false.
|
|
203
|
+
*/
|
|
204
|
+
Utils.empty = function (obj) {
|
|
205
|
+
'use strict';
|
|
206
|
+
if (obj === null || obj === undefined || obj === '') {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
if (typeof obj === 'number' && isNaN(obj)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
if (obj instanceof Date) {
|
|
213
|
+
if (isNaN(obj.getTime())) {
|
|
214
|
+
return true;
|
|
215
|
+
} else {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(obj)) {
|
|
220
|
+
return obj.length === 0;
|
|
221
|
+
}
|
|
222
|
+
if (
|
|
223
|
+
obj instanceof HTMLInputElement ||
|
|
224
|
+
obj instanceof HTMLSelectElement ||
|
|
225
|
+
obj instanceof HTMLTextAreaElement
|
|
226
|
+
) {
|
|
227
|
+
return obj.value === '';
|
|
228
|
+
}
|
|
229
|
+
if (typeof obj === 'object') {
|
|
230
|
+
return Object.keys(obj).length === 0;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Checks if a value is an integer representation.
|
|
237
|
+
* Considers integers both numbers and strings that can be converted to integers.
|
|
238
|
+
*
|
|
239
|
+
* @param {string|number} value - Value to check.
|
|
240
|
+
* @returns {boolean} Returns true if the value is an integer, false otherwise.
|
|
241
|
+
*/
|
|
242
|
+
Utils.isInt = function (value) {
|
|
243
|
+
'use strict';
|
|
244
|
+
return typeof value === 'number'
|
|
245
|
+
? Number.isInteger(value)
|
|
246
|
+
: Number.isInteger(parseFloat(value)) &&
|
|
247
|
+
!isNaN(value) &&
|
|
248
|
+
parseFloat(value).toString() === value.toString();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Checks if a value is a floating-point number representation. Considers
|
|
253
|
+
* floating-point numbers both numbers and strings that can be converted to
|
|
254
|
+
* floating-point numbers, excluding integers.
|
|
255
|
+
*
|
|
256
|
+
* @param {string|number} value - Value to check.
|
|
257
|
+
* @returns {boolean} Returns true if the value is a floating-point number,
|
|
258
|
+
* false otherwise.
|
|
259
|
+
*/
|
|
260
|
+
Utils.isFloat = function (value) {
|
|
261
|
+
'use strict';
|
|
262
|
+
|
|
263
|
+
// If it's a number, but not an integer, then it's a floating-point number.
|
|
264
|
+
if (typeof value === 'number') {
|
|
265
|
+
return !Number.isInteger(value) && !isNaN(value);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// If it's a string, check if it's a floating-point number.
|
|
269
|
+
// First, discard strings that cannot be converted to numbers.
|
|
270
|
+
if (typeof value === 'string' && !isNaN(value) && value.trim() !== '') {
|
|
271
|
+
// Then, discard integers.
|
|
272
|
+
if (Utils.isInt(value)) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
// Finally, check that the string contains a decimal point.
|
|
276
|
+
return value.includes('.');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// For all other cases, it's not a floating-point number.
|
|
280
|
+
return false;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generates an object with the values of the specified keys from a source object.
|
|
285
|
+
*
|
|
286
|
+
* @param {Array} keys - List of keys whose values are desired to be extracted
|
|
287
|
+
* from the source object.
|
|
288
|
+
* @param {Object} source - Source object from which values will be extracted.
|
|
289
|
+
* @returns {Object} - Object with the values of the specified keys, where each
|
|
290
|
+
* key is the original key name and the value is the corresponding value in the
|
|
291
|
+
* source object.
|
|
292
|
+
*/
|
|
293
|
+
Utils.keyValues = function (keys, source) {
|
|
294
|
+
const values = {};
|
|
295
|
+
keys.forEach(function (key) {
|
|
296
|
+
values[key] = source[key];
|
|
297
|
+
});
|
|
298
|
+
return values;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Gets the value of an object based on a selector that allows concatenating
|
|
303
|
+
* literal texts and object property values.
|
|
304
|
+
*
|
|
305
|
+
* The selector can include:
|
|
306
|
+
*
|
|
307
|
+
* - Literal texts between double quotes "".
|
|
308
|
+
* - Object properties between parentheses ().
|
|
309
|
+
*
|
|
310
|
+
* @param {Object} obj - The object from which values will be extracted.
|
|
311
|
+
* @param {string} path - The selector that specifies how to access and
|
|
312
|
+
* concatenate object values.
|
|
313
|
+
* @returns {string} - The result of concatenating the values specified by the
|
|
314
|
+
* selector.
|
|
315
|
+
*/
|
|
316
|
+
Utils.selector = function (obj, path) {
|
|
317
|
+
const regex = /\(([^)]+)\)|"([^"]*)"/g;
|
|
318
|
+
let match;
|
|
319
|
+
let result = '';
|
|
320
|
+
while ((match = regex.exec(path)) !== null) {
|
|
321
|
+
// It's a selector, get value from object.
|
|
322
|
+
if (match[1]) {
|
|
323
|
+
result +=
|
|
324
|
+
match[1]
|
|
325
|
+
.split('.')
|
|
326
|
+
.reduce((acc, part) => acc && acc[part], obj) || '';
|
|
327
|
+
}
|
|
328
|
+
// It's a literal text, add as is.
|
|
329
|
+
else if (match[2]) {
|
|
330
|
+
result += match[2];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Return obtained result.
|
|
334
|
+
return result.trim();
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Rounds a number to a specified number of decimal places with precise decimal
|
|
339
|
+
* arithmetic. This function avoids floating-point precision issues that can
|
|
340
|
+
* occur with standard Math.round().
|
|
341
|
+
*
|
|
342
|
+
* @param {number} n - The number to round.
|
|
343
|
+
* @param {number} decimals - The number of decimal places to round to.
|
|
344
|
+
* @returns {number} The rounded number.
|
|
345
|
+
*/
|
|
346
|
+
Utils.round = function (n, decimals) {
|
|
347
|
+
return Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// Export module for use in Node.js.
|
|
351
|
+
if (typeof module === 'object' && module.exports) {
|
|
352
|
+
module.exports = Utils;
|
|
353
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*! Chilean validation | (c) 2025 Derafu DEV | MIT */
|
|
2
|
+
|
|
3
|
+
// Object where Chilean RUT validation functions will be assigned.
|
|
4
|
+
const ValidationL10nCl = {};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates and optionally formats a Chilean RUT (Rol Único Tributario).
|
|
8
|
+
* Verifies that the check digit is correct and can return the formatted RUT.
|
|
9
|
+
*
|
|
10
|
+
* @param {string|number} value - The RUT to validate, can include dots, commas,
|
|
11
|
+
* and dash.
|
|
12
|
+
* @param {boolean} [format=false] - If true, returns the formatted RUT with
|
|
13
|
+
* thousands separator and dash. If false or not provided, only validates the RUT.
|
|
14
|
+
* @returns {boolean|string} If format is false, returns true if the RUT is
|
|
15
|
+
* valid, otherwise false. If format is true, returns the formatted RUT if
|
|
16
|
+
* valid, otherwise false.
|
|
17
|
+
*/
|
|
18
|
+
ValidationL10nCl.rut = function (value, format) {
|
|
19
|
+
'use strict';
|
|
20
|
+
const dv = value.slice(-1).toUpperCase();
|
|
21
|
+
const rut = value
|
|
22
|
+
.replace(/\./g, '')
|
|
23
|
+
.replace(/,/g, '')
|
|
24
|
+
.replace(/-/, '')
|
|
25
|
+
.slice(0, -1);
|
|
26
|
+
if (dv !== 'K' && isNaN(parseInt(dv, 10))) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (dv !== ValidationL10nCl.rut.dv(rut)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return format === true
|
|
33
|
+
? `${
|
|
34
|
+
typeof Utils !== 'undefined'
|
|
35
|
+
? Utils.num(rut, 0, 'es-CL')
|
|
36
|
+
: rut.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
|
|
37
|
+
}-${dv}`
|
|
38
|
+
: true;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Calculates the check digit for a Chilean RUT (Rol Único Tributario).
|
|
43
|
+
* Uses the standard algorithm to calculate the digit based on the RUT number.
|
|
44
|
+
*
|
|
45
|
+
* @param {string|number} numero - RUT without dots or check digit.
|
|
46
|
+
* @returns {string} RUT check digit.
|
|
47
|
+
*/
|
|
48
|
+
ValidationL10nCl.rut.dv = function (numero) {
|
|
49
|
+
'use strict';
|
|
50
|
+
let suma = 0;
|
|
51
|
+
let factor = 2;
|
|
52
|
+
const rutReverso = numero.toString().split('').reverse().join('');
|
|
53
|
+
|
|
54
|
+
for (const digito of rutReverso) {
|
|
55
|
+
suma += parseInt(digito, 10) * factor;
|
|
56
|
+
factor = factor === 7 ? 2 : factor + 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const dv = 11 - (suma % 11);
|
|
60
|
+
return dv === 11 ? '0' : dv === 10 ? 'K' : dv.toString();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Export module for use in node.js.
|
|
64
|
+
if (typeof module === 'object' && module.exports) {
|
|
65
|
+
module.exports = ValidationL10nCl;
|
|
66
|
+
}
|