aegis-framework 0.2.1 → 0.3.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/README.md +552 -158
- package/aegis/runtime/aegis-api.js +643 -1
- package/package.json +1 -1
|
@@ -989,6 +989,649 @@
|
|
|
989
989
|
*/
|
|
990
990
|
uid: function (prefix = 'aegis') {
|
|
991
991
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
992
|
+
},
|
|
993
|
+
|
|
994
|
+
// ==================== Form Utilities ====================
|
|
995
|
+
|
|
996
|
+
form: {
|
|
997
|
+
/**
|
|
998
|
+
* Serialize form to object
|
|
999
|
+
*/
|
|
1000
|
+
serialize: function (selector) {
|
|
1001
|
+
const form = Aegis.get(selector);
|
|
1002
|
+
if (!form) return {};
|
|
1003
|
+
const data = {};
|
|
1004
|
+
const formData = new FormData(form);
|
|
1005
|
+
formData.forEach((value, key) => {
|
|
1006
|
+
if (data[key]) {
|
|
1007
|
+
if (!Array.isArray(data[key])) data[key] = [data[key]];
|
|
1008
|
+
data[key].push(value);
|
|
1009
|
+
} else {
|
|
1010
|
+
data[key] = value;
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
return data;
|
|
1014
|
+
},
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Reset form
|
|
1018
|
+
*/
|
|
1019
|
+
reset: function (selector) {
|
|
1020
|
+
const form = Aegis.get(selector);
|
|
1021
|
+
if (form) form.reset();
|
|
1022
|
+
return form;
|
|
1023
|
+
},
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Fill form with data
|
|
1027
|
+
*/
|
|
1028
|
+
fill: function (selector, data) {
|
|
1029
|
+
const form = Aegis.get(selector);
|
|
1030
|
+
if (!form) return;
|
|
1031
|
+
Object.entries(data).forEach(([name, value]) => {
|
|
1032
|
+
const field = form.querySelector(`[name="${name}"]`);
|
|
1033
|
+
if (field) {
|
|
1034
|
+
if (field.type === 'checkbox') {
|
|
1035
|
+
field.checked = !!value;
|
|
1036
|
+
} else if (field.type === 'radio') {
|
|
1037
|
+
const radio = form.querySelector(`[name="${name}"][value="${value}"]`);
|
|
1038
|
+
if (radio) radio.checked = true;
|
|
1039
|
+
} else {
|
|
1040
|
+
field.value = value;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
return form;
|
|
1045
|
+
},
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Simple validation
|
|
1049
|
+
*/
|
|
1050
|
+
validate: function (selector, rules = {}) {
|
|
1051
|
+
const form = Aegis.get(selector);
|
|
1052
|
+
if (!form) return { valid: false, errors: ['Form not found'] };
|
|
1053
|
+
const errors = [];
|
|
1054
|
+
Object.entries(rules).forEach(([name, rule]) => {
|
|
1055
|
+
const field = form.querySelector(`[name="${name}"]`);
|
|
1056
|
+
const value = field?.value || '';
|
|
1057
|
+
if (rule.required && !value.trim()) {
|
|
1058
|
+
errors.push(`${name} is required`);
|
|
1059
|
+
}
|
|
1060
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
1061
|
+
errors.push(`${name} must be at least ${rule.minLength} characters`);
|
|
1062
|
+
}
|
|
1063
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
1064
|
+
errors.push(`${name} is invalid`);
|
|
1065
|
+
}
|
|
1066
|
+
if (rule.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
1067
|
+
errors.push(`${name} must be a valid email`);
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
return { valid: errors.length === 0, errors };
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
|
|
1074
|
+
// ==================== HTTP/Fetch Utilities ====================
|
|
1075
|
+
|
|
1076
|
+
http: {
|
|
1077
|
+
/**
|
|
1078
|
+
* GET request
|
|
1079
|
+
*/
|
|
1080
|
+
get: async function (url, options = {}) {
|
|
1081
|
+
const response = await fetch(url, { method: 'GET', ...options });
|
|
1082
|
+
return options.raw ? response : response.json();
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* POST request
|
|
1087
|
+
*/
|
|
1088
|
+
post: async function (url, data, options = {}) {
|
|
1089
|
+
const response = await fetch(url, {
|
|
1090
|
+
method: 'POST',
|
|
1091
|
+
headers: { 'Content-Type': 'application/json', ...options.headers },
|
|
1092
|
+
body: JSON.stringify(data),
|
|
1093
|
+
...options
|
|
1094
|
+
});
|
|
1095
|
+
return options.raw ? response : response.json();
|
|
1096
|
+
},
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* PUT request
|
|
1100
|
+
*/
|
|
1101
|
+
put: async function (url, data, options = {}) {
|
|
1102
|
+
const response = await fetch(url, {
|
|
1103
|
+
method: 'PUT',
|
|
1104
|
+
headers: { 'Content-Type': 'application/json', ...options.headers },
|
|
1105
|
+
body: JSON.stringify(data),
|
|
1106
|
+
...options
|
|
1107
|
+
});
|
|
1108
|
+
return options.raw ? response : response.json();
|
|
1109
|
+
},
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* DELETE request
|
|
1113
|
+
*/
|
|
1114
|
+
delete: async function (url, options = {}) {
|
|
1115
|
+
const response = await fetch(url, { method: 'DELETE', ...options });
|
|
1116
|
+
return options.raw ? response : response.json();
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
// ==================== Cookie Utilities ====================
|
|
1121
|
+
|
|
1122
|
+
cookie: {
|
|
1123
|
+
get: function (name) {
|
|
1124
|
+
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
|
1125
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
1126
|
+
},
|
|
1127
|
+
set: function (name, value, days = 365, path = '/') {
|
|
1128
|
+
const expires = new Date(Date.now() + days * 864e5).toUTCString();
|
|
1129
|
+
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=${path}`;
|
|
1130
|
+
},
|
|
1131
|
+
remove: function (name, path = '/') {
|
|
1132
|
+
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path}`;
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
|
|
1136
|
+
// ==================== URL Utilities ====================
|
|
1137
|
+
|
|
1138
|
+
url: {
|
|
1139
|
+
/**
|
|
1140
|
+
* Get query parameters as object
|
|
1141
|
+
*/
|
|
1142
|
+
params: function (url = window.location.href) {
|
|
1143
|
+
const params = {};
|
|
1144
|
+
new URL(url).searchParams.forEach((v, k) => params[k] = v);
|
|
1145
|
+
return params;
|
|
1146
|
+
},
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Get single parameter
|
|
1150
|
+
*/
|
|
1151
|
+
param: function (name, url = window.location.href) {
|
|
1152
|
+
return new URL(url).searchParams.get(name);
|
|
1153
|
+
},
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Build URL with params
|
|
1157
|
+
*/
|
|
1158
|
+
build: function (base, params = {}) {
|
|
1159
|
+
const url = new URL(base, window.location.origin);
|
|
1160
|
+
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
1161
|
+
return url.toString();
|
|
1162
|
+
},
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Get current path
|
|
1166
|
+
*/
|
|
1167
|
+
path: function () {
|
|
1168
|
+
return window.location.pathname;
|
|
1169
|
+
},
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Get hash without #
|
|
1173
|
+
*/
|
|
1174
|
+
hash: function () {
|
|
1175
|
+
return window.location.hash.slice(1);
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
|
|
1179
|
+
// ==================== String Utilities ====================
|
|
1180
|
+
|
|
1181
|
+
string: {
|
|
1182
|
+
capitalize: function (str) {
|
|
1183
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1184
|
+
},
|
|
1185
|
+
|
|
1186
|
+
titleCase: function (str) {
|
|
1187
|
+
return str.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
|
|
1188
|
+
},
|
|
1189
|
+
|
|
1190
|
+
camelCase: function (str) {
|
|
1191
|
+
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');
|
|
1192
|
+
},
|
|
1193
|
+
|
|
1194
|
+
kebabCase: function (str) {
|
|
1195
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
|
|
1196
|
+
},
|
|
1197
|
+
|
|
1198
|
+
snakeCase: function (str) {
|
|
1199
|
+
return str.replace(/([a-z])([A-Z])/g, '$1_$2').replace(/[\s-]+/g, '_').toLowerCase();
|
|
1200
|
+
},
|
|
1201
|
+
|
|
1202
|
+
slugify: function (str) {
|
|
1203
|
+
return str.toLowerCase().trim()
|
|
1204
|
+
.replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
1205
|
+
},
|
|
1206
|
+
|
|
1207
|
+
truncate: function (str, length, suffix = '...') {
|
|
1208
|
+
return str.length > length ? str.slice(0, length) + suffix : str;
|
|
1209
|
+
},
|
|
1210
|
+
|
|
1211
|
+
pad: function (str, length, char = ' ', end = false) {
|
|
1212
|
+
str = String(str);
|
|
1213
|
+
return end ? str.padEnd(length, char) : str.padStart(length, char);
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
reverse: function (str) {
|
|
1217
|
+
return str.split('').reverse().join('');
|
|
1218
|
+
},
|
|
1219
|
+
|
|
1220
|
+
count: function (str, search) {
|
|
1221
|
+
return (str.match(new RegExp(search, 'g')) || []).length;
|
|
1222
|
+
},
|
|
1223
|
+
|
|
1224
|
+
between: function (str, start, end) {
|
|
1225
|
+
const s = str.indexOf(start) + start.length;
|
|
1226
|
+
const e = str.indexOf(end, s);
|
|
1227
|
+
return s > start.length - 1 && e > -1 ? str.slice(s, e) : '';
|
|
1228
|
+
},
|
|
1229
|
+
|
|
1230
|
+
template: function (str, data) {
|
|
1231
|
+
return str.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] ?? '');
|
|
1232
|
+
}
|
|
1233
|
+
},
|
|
1234
|
+
|
|
1235
|
+
// ==================== Array Utilities ====================
|
|
1236
|
+
|
|
1237
|
+
array: {
|
|
1238
|
+
unique: function (arr) {
|
|
1239
|
+
return [...new Set(arr)];
|
|
1240
|
+
},
|
|
1241
|
+
|
|
1242
|
+
shuffle: function (arr) {
|
|
1243
|
+
const a = [...arr];
|
|
1244
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
1245
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1246
|
+
[a[i], a[j]] = [a[j], a[i]];
|
|
1247
|
+
}
|
|
1248
|
+
return a;
|
|
1249
|
+
},
|
|
1250
|
+
|
|
1251
|
+
chunk: function (arr, size) {
|
|
1252
|
+
const chunks = [];
|
|
1253
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
1254
|
+
chunks.push(arr.slice(i, i + size));
|
|
1255
|
+
}
|
|
1256
|
+
return chunks;
|
|
1257
|
+
},
|
|
1258
|
+
|
|
1259
|
+
flatten: function (arr, depth = Infinity) {
|
|
1260
|
+
return arr.flat(depth);
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
compact: function (arr) {
|
|
1264
|
+
return arr.filter(Boolean);
|
|
1265
|
+
},
|
|
1266
|
+
|
|
1267
|
+
first: function (arr, n = 1) {
|
|
1268
|
+
return n === 1 ? arr[0] : arr.slice(0, n);
|
|
1269
|
+
},
|
|
1270
|
+
|
|
1271
|
+
last: function (arr, n = 1) {
|
|
1272
|
+
return n === 1 ? arr[arr.length - 1] : arr.slice(-n);
|
|
1273
|
+
},
|
|
1274
|
+
|
|
1275
|
+
sample: function (arr) {
|
|
1276
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1277
|
+
},
|
|
1278
|
+
|
|
1279
|
+
groupBy: function (arr, key) {
|
|
1280
|
+
return arr.reduce((groups, item) => {
|
|
1281
|
+
const k = typeof key === 'function' ? key(item) : item[key];
|
|
1282
|
+
(groups[k] = groups[k] || []).push(item);
|
|
1283
|
+
return groups;
|
|
1284
|
+
}, {});
|
|
1285
|
+
},
|
|
1286
|
+
|
|
1287
|
+
sortBy: function (arr, key, desc = false) {
|
|
1288
|
+
return [...arr].sort((a, b) => {
|
|
1289
|
+
const va = typeof key === 'function' ? key(a) : a[key];
|
|
1290
|
+
const vb = typeof key === 'function' ? key(b) : b[key];
|
|
1291
|
+
return desc ? (vb > va ? 1 : -1) : (va > vb ? 1 : -1);
|
|
1292
|
+
});
|
|
1293
|
+
},
|
|
1294
|
+
|
|
1295
|
+
diff: function (arr1, arr2) {
|
|
1296
|
+
return arr1.filter(x => !arr2.includes(x));
|
|
1297
|
+
},
|
|
1298
|
+
|
|
1299
|
+
intersect: function (arr1, arr2) {
|
|
1300
|
+
return arr1.filter(x => arr2.includes(x));
|
|
1301
|
+
},
|
|
1302
|
+
|
|
1303
|
+
range: function (start, end, step = 1) {
|
|
1304
|
+
const arr = [];
|
|
1305
|
+
for (let i = start; i <= end; i += step) arr.push(i);
|
|
1306
|
+
return arr;
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1309
|
+
|
|
1310
|
+
// ==================== Object Utilities ====================
|
|
1311
|
+
|
|
1312
|
+
object: {
|
|
1313
|
+
clone: function (obj) {
|
|
1314
|
+
return JSON.parse(JSON.stringify(obj));
|
|
1315
|
+
},
|
|
1316
|
+
|
|
1317
|
+
merge: function (...objects) {
|
|
1318
|
+
return objects.reduce((acc, obj) => ({ ...acc, ...obj }), {});
|
|
1319
|
+
},
|
|
1320
|
+
|
|
1321
|
+
isEmpty: function (obj) {
|
|
1322
|
+
return Object.keys(obj).length === 0;
|
|
1323
|
+
},
|
|
1324
|
+
|
|
1325
|
+
pick: function (obj, keys) {
|
|
1326
|
+
return keys.reduce((acc, key) => {
|
|
1327
|
+
if (key in obj) acc[key] = obj[key];
|
|
1328
|
+
return acc;
|
|
1329
|
+
}, {});
|
|
1330
|
+
},
|
|
1331
|
+
|
|
1332
|
+
omit: function (obj, keys) {
|
|
1333
|
+
return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
|
|
1334
|
+
},
|
|
1335
|
+
|
|
1336
|
+
keys: function (obj) {
|
|
1337
|
+
return Object.keys(obj);
|
|
1338
|
+
},
|
|
1339
|
+
|
|
1340
|
+
values: function (obj) {
|
|
1341
|
+
return Object.values(obj);
|
|
1342
|
+
},
|
|
1343
|
+
|
|
1344
|
+
entries: function (obj) {
|
|
1345
|
+
return Object.entries(obj);
|
|
1346
|
+
},
|
|
1347
|
+
|
|
1348
|
+
fromEntries: function (entries) {
|
|
1349
|
+
return Object.fromEntries(entries);
|
|
1350
|
+
},
|
|
1351
|
+
|
|
1352
|
+
get: function (obj, path, fallback = undefined) {
|
|
1353
|
+
return path.split('.').reduce((o, k) => (o || {})[k], obj) ?? fallback;
|
|
1354
|
+
},
|
|
1355
|
+
|
|
1356
|
+
set: function (obj, path, value) {
|
|
1357
|
+
const keys = path.split('.');
|
|
1358
|
+
const last = keys.pop();
|
|
1359
|
+
const target = keys.reduce((o, k) => (o[k] = o[k] || {}), obj);
|
|
1360
|
+
target[last] = value;
|
|
1361
|
+
return obj;
|
|
1362
|
+
}
|
|
1363
|
+
},
|
|
1364
|
+
|
|
1365
|
+
// ==================== Date Utilities ====================
|
|
1366
|
+
|
|
1367
|
+
date: {
|
|
1368
|
+
now: function () {
|
|
1369
|
+
return new Date();
|
|
1370
|
+
},
|
|
1371
|
+
|
|
1372
|
+
format: function (date, format = 'YYYY-MM-DD') {
|
|
1373
|
+
const d = new Date(date);
|
|
1374
|
+
const pad = n => String(n).padStart(2, '0');
|
|
1375
|
+
return format
|
|
1376
|
+
.replace('YYYY', d.getFullYear())
|
|
1377
|
+
.replace('MM', pad(d.getMonth() + 1))
|
|
1378
|
+
.replace('DD', pad(d.getDate()))
|
|
1379
|
+
.replace('HH', pad(d.getHours()))
|
|
1380
|
+
.replace('mm', pad(d.getMinutes()))
|
|
1381
|
+
.replace('ss', pad(d.getSeconds()));
|
|
1382
|
+
},
|
|
1383
|
+
|
|
1384
|
+
ago: function (date) {
|
|
1385
|
+
const seconds = Math.floor((Date.now() - new Date(date)) / 1000);
|
|
1386
|
+
const intervals = [
|
|
1387
|
+
[31536000, 'year'], [2592000, 'month'], [86400, 'day'],
|
|
1388
|
+
[3600, 'hour'], [60, 'minute'], [1, 'second']
|
|
1389
|
+
];
|
|
1390
|
+
for (const [secs, unit] of intervals) {
|
|
1391
|
+
const n = Math.floor(seconds / secs);
|
|
1392
|
+
if (n >= 1) return `${n} ${unit}${n > 1 ? 's' : ''} ago`;
|
|
1393
|
+
}
|
|
1394
|
+
return 'just now';
|
|
1395
|
+
},
|
|
1396
|
+
|
|
1397
|
+
isToday: function (date) {
|
|
1398
|
+
const d = new Date(date);
|
|
1399
|
+
const today = new Date();
|
|
1400
|
+
return d.toDateString() === today.toDateString();
|
|
1401
|
+
},
|
|
1402
|
+
|
|
1403
|
+
isYesterday: function (date) {
|
|
1404
|
+
const d = new Date(date);
|
|
1405
|
+
const yesterday = new Date(Date.now() - 864e5);
|
|
1406
|
+
return d.toDateString() === yesterday.toDateString();
|
|
1407
|
+
},
|
|
1408
|
+
|
|
1409
|
+
add: function (date, amount, unit = 'day') {
|
|
1410
|
+
const d = new Date(date);
|
|
1411
|
+
const ms = { second: 1000, minute: 60000, hour: 3600000, day: 864e5 };
|
|
1412
|
+
return new Date(d.getTime() + amount * (ms[unit] || ms.day));
|
|
1413
|
+
},
|
|
1414
|
+
|
|
1415
|
+
diff: function (date1, date2, unit = 'day') {
|
|
1416
|
+
const ms = Math.abs(new Date(date1) - new Date(date2));
|
|
1417
|
+
const divisors = { second: 1000, minute: 60000, hour: 3600000, day: 864e5 };
|
|
1418
|
+
return Math.floor(ms / (divisors[unit] || divisors.day));
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
|
|
1422
|
+
// ==================== Number Utilities ====================
|
|
1423
|
+
|
|
1424
|
+
number: {
|
|
1425
|
+
format: function (num, decimals = 0, locale = 'pt-BR') {
|
|
1426
|
+
return new Intl.NumberFormat(locale, {
|
|
1427
|
+
minimumFractionDigits: decimals,
|
|
1428
|
+
maximumFractionDigits: decimals
|
|
1429
|
+
}).format(num);
|
|
1430
|
+
},
|
|
1431
|
+
|
|
1432
|
+
currency: function (num, currency = 'BRL', locale = 'pt-BR') {
|
|
1433
|
+
return new Intl.NumberFormat(locale, {
|
|
1434
|
+
style: 'currency',
|
|
1435
|
+
currency
|
|
1436
|
+
}).format(num);
|
|
1437
|
+
},
|
|
1438
|
+
|
|
1439
|
+
clamp: function (num, min, max) {
|
|
1440
|
+
return Math.min(Math.max(num, min), max);
|
|
1441
|
+
},
|
|
1442
|
+
|
|
1443
|
+
random: function (min = 0, max = 100) {
|
|
1444
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1445
|
+
},
|
|
1446
|
+
|
|
1447
|
+
percent: function (value, total) {
|
|
1448
|
+
return total ? Math.round((value / total) * 100) : 0;
|
|
1449
|
+
},
|
|
1450
|
+
|
|
1451
|
+
bytes: function (bytes, decimals = 2) {
|
|
1452
|
+
if (bytes === 0) return '0 B';
|
|
1453
|
+
const k = 1024;
|
|
1454
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
1455
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1456
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
|
1457
|
+
},
|
|
1458
|
+
|
|
1459
|
+
ordinal: function (n) {
|
|
1460
|
+
const s = ['th', 'st', 'nd', 'rd'];
|
|
1461
|
+
const v = n % 100;
|
|
1462
|
+
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
|
1463
|
+
}
|
|
1464
|
+
},
|
|
1465
|
+
|
|
1466
|
+
// ==================== Clipboard Utilities ====================
|
|
1467
|
+
|
|
1468
|
+
clipboard: {
|
|
1469
|
+
copy: async function (text) {
|
|
1470
|
+
try {
|
|
1471
|
+
await navigator.clipboard.writeText(text);
|
|
1472
|
+
return true;
|
|
1473
|
+
} catch (e) {
|
|
1474
|
+
// Fallback
|
|
1475
|
+
const ta = document.createElement('textarea');
|
|
1476
|
+
ta.value = text;
|
|
1477
|
+
ta.style.position = 'fixed';
|
|
1478
|
+
ta.style.opacity = '0';
|
|
1479
|
+
document.body.appendChild(ta);
|
|
1480
|
+
ta.select();
|
|
1481
|
+
document.execCommand('copy');
|
|
1482
|
+
document.body.removeChild(ta);
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
|
|
1487
|
+
paste: async function () {
|
|
1488
|
+
try {
|
|
1489
|
+
return await navigator.clipboard.readText();
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
|
|
1496
|
+
// ==================== Toast/Notification ====================
|
|
1497
|
+
|
|
1498
|
+
toast: function (message, options = {}) {
|
|
1499
|
+
const { duration = 3000, type = 'info', position = 'bottom-right' } = options;
|
|
1500
|
+
|
|
1501
|
+
// Create container if not exists
|
|
1502
|
+
let container = document.getElementById('aegis-toast-container');
|
|
1503
|
+
if (!container) {
|
|
1504
|
+
container = document.createElement('div');
|
|
1505
|
+
container.id = 'aegis-toast-container';
|
|
1506
|
+
container.style.cssText = `
|
|
1507
|
+
position: fixed; z-index: 99999; display: flex; flex-direction: column; gap: 10px;
|
|
1508
|
+
${position.includes('top') ? 'top: 20px' : 'bottom: 20px'};
|
|
1509
|
+
${position.includes('left') ? 'left: 20px' : 'right: 20px'};
|
|
1510
|
+
`;
|
|
1511
|
+
document.body.appendChild(container);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
const colors = { info: '#3498db', success: '#2ecc71', warning: '#f39c12', error: '#e74c3c' };
|
|
1515
|
+
const icons = { info: 'ℹ️', success: '✅', warning: '⚠️', error: '❌' };
|
|
1516
|
+
|
|
1517
|
+
const toast = document.createElement('div');
|
|
1518
|
+
toast.style.cssText = `
|
|
1519
|
+
background: ${colors[type] || colors.info}; color: white; padding: 12px 20px;
|
|
1520
|
+
border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
|
1521
|
+
font-family: -apple-system, sans-serif; font-size: 14px;
|
|
1522
|
+
display: flex; align-items: center; gap: 10px;
|
|
1523
|
+
animation: aegisToastIn 0.3s ease;
|
|
1524
|
+
max-width: 350px;
|
|
1525
|
+
`;
|
|
1526
|
+
toast.innerHTML = `<span>${icons[type] || ''}</span><span>${message}</span>`;
|
|
1527
|
+
container.appendChild(toast);
|
|
1528
|
+
|
|
1529
|
+
// Add animation styles if not exist
|
|
1530
|
+
if (!document.getElementById('aegis-toast-styles')) {
|
|
1531
|
+
const style = document.createElement('style');
|
|
1532
|
+
style.id = 'aegis-toast-styles';
|
|
1533
|
+
style.textContent = `
|
|
1534
|
+
@keyframes aegisToastIn { from { opacity: 0; transform: translateX(100px); } }
|
|
1535
|
+
@keyframes aegisToastOut { to { opacity: 0; transform: translateX(100px); } }
|
|
1536
|
+
`;
|
|
1537
|
+
document.head.appendChild(style);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
setTimeout(() => {
|
|
1541
|
+
toast.style.animation = 'aegisToastOut 0.3s ease forwards';
|
|
1542
|
+
setTimeout(() => toast.remove(), 300);
|
|
1543
|
+
}, duration);
|
|
1544
|
+
|
|
1545
|
+
return toast;
|
|
1546
|
+
},
|
|
1547
|
+
|
|
1548
|
+
// ==================== Random Utilities ====================
|
|
1549
|
+
|
|
1550
|
+
random: {
|
|
1551
|
+
int: function (min = 0, max = 100) {
|
|
1552
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1553
|
+
},
|
|
1554
|
+
|
|
1555
|
+
float: function (min = 0, max = 1, decimals = 2) {
|
|
1556
|
+
return parseFloat((Math.random() * (max - min) + min).toFixed(decimals));
|
|
1557
|
+
},
|
|
1558
|
+
|
|
1559
|
+
bool: function (probability = 0.5) {
|
|
1560
|
+
return Math.random() < probability;
|
|
1561
|
+
},
|
|
1562
|
+
|
|
1563
|
+
pick: function (arr) {
|
|
1564
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1565
|
+
},
|
|
1566
|
+
|
|
1567
|
+
color: function () {
|
|
1568
|
+
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
|
|
1569
|
+
},
|
|
1570
|
+
|
|
1571
|
+
uuid: function () {
|
|
1572
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
1573
|
+
const r = Math.random() * 16 | 0;
|
|
1574
|
+
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
|
|
1579
|
+
// ==================== Validation Utilities ====================
|
|
1580
|
+
|
|
1581
|
+
is: {
|
|
1582
|
+
email: function (str) {
|
|
1583
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
|
|
1584
|
+
},
|
|
1585
|
+
url: function (str) {
|
|
1586
|
+
try { new URL(str); return true; } catch { return false; }
|
|
1587
|
+
},
|
|
1588
|
+
number: function (val) {
|
|
1589
|
+
return typeof val === 'number' && !isNaN(val);
|
|
1590
|
+
},
|
|
1591
|
+
string: function (val) {
|
|
1592
|
+
return typeof val === 'string';
|
|
1593
|
+
},
|
|
1594
|
+
array: function (val) {
|
|
1595
|
+
return Array.isArray(val);
|
|
1596
|
+
},
|
|
1597
|
+
object: function (val) {
|
|
1598
|
+
return val !== null && typeof val === 'object' && !Array.isArray(val);
|
|
1599
|
+
},
|
|
1600
|
+
function: function (val) {
|
|
1601
|
+
return typeof val === 'function';
|
|
1602
|
+
},
|
|
1603
|
+
empty: function (val) {
|
|
1604
|
+
if (val == null) return true;
|
|
1605
|
+
if (Array.isArray(val) || typeof val === 'string') return val.length === 0;
|
|
1606
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
1607
|
+
return false;
|
|
1608
|
+
},
|
|
1609
|
+
mobile: function () {
|
|
1610
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
1611
|
+
}
|
|
1612
|
+
},
|
|
1613
|
+
|
|
1614
|
+
// ==================== Log Utilities ====================
|
|
1615
|
+
|
|
1616
|
+
log: {
|
|
1617
|
+
info: function (...args) {
|
|
1618
|
+
console.log('%c[INFO]', 'color: #3498db; font-weight: bold;', ...args);
|
|
1619
|
+
},
|
|
1620
|
+
success: function (...args) {
|
|
1621
|
+
console.log('%c[SUCCESS]', 'color: #2ecc71; font-weight: bold;', ...args);
|
|
1622
|
+
},
|
|
1623
|
+
warn: function (...args) {
|
|
1624
|
+
console.log('%c[WARN]', 'color: #f39c12; font-weight: bold;', ...args);
|
|
1625
|
+
},
|
|
1626
|
+
error: function (...args) {
|
|
1627
|
+
console.log('%c[ERROR]', 'color: #e74c3c; font-weight: bold;', ...args);
|
|
1628
|
+
},
|
|
1629
|
+
debug: function (...args) {
|
|
1630
|
+
console.log('%c[DEBUG]', 'color: #9b59b6; font-weight: bold;', ...args);
|
|
1631
|
+
},
|
|
1632
|
+
table: function (data) {
|
|
1633
|
+
console.table(data);
|
|
1634
|
+
}
|
|
992
1635
|
}
|
|
993
1636
|
};
|
|
994
1637
|
|
|
@@ -1005,4 +1648,3 @@
|
|
|
1005
1648
|
console.log('%c⚡ Aegis v' + Aegis.version + ' loaded', 'color: #00ff88; font-weight: bold;');
|
|
1006
1649
|
|
|
1007
1650
|
})();
|
|
1008
|
-
|
package/package.json
CHANGED