Aeolin Ferjünnoz 870630063a - fixed two way binding
- added support for list like attributes to toggle classes for example
2026-04-14 15:00:11 +02:00

204 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ─── parseDate (C# DateTime.ParseExact style) ───
// Parses a date string using an exact format pattern.
// Supported tokens:
// yyyy — 4-digit year
// yy — 2-digit year (assumes 2000+)
// MM — 2-digit month (0112)
// M — 1 or 2-digit month
// dd — 2-digit day (0131)
// d — 1 or 2-digit day
// HH — 2-digit hour 24h (0023)
// H — 1 or 2-digit hour 24h
// hh — 2-digit hour 12h (0112)
// h — 1 or 2-digit hour 12h
// mm — 2-digit minute (0059)
// m — 1 or 2-digit minute
// ss — 2-digit second (0059)
// s — 1 or 2-digit second
// fff — 3-digit milliseconds
// ff — 2-digit (tenths + hundredths)
// f — 1-digit (tenths)
// tt — AM/PM
// t — A/P
// Z — literal 'Z' (UTC marker)
// K — timezone offset (+HH:mm, -HH:mm, or Z)
//
// All other characters are matched literally.
// Returns Date on success, null on failure.
const TOKEN_ORDER = [
"yyyy", "yy",
"MM", "M",
"dd", "d",
"HH", "H", "hh", "h",
"mm", "m",
"ss", "s",
"fff", "ff", "f",
"tt", "t",
"K", "Z",
];
const TOKEN_PATTERNS = {
yyyy: "(\\d{4})",
yy: "(\\d{2})",
MM: "(\\d{2})",
M: "(\\d{1,2})",
dd: "(\\d{2})",
d: "(\\d{1,2})",
HH: "(\\d{2})",
H: "(\\d{1,2})",
hh: "(\\d{2})",
h: "(\\d{1,2})",
mm: "(\\d{2})",
m: "(\\d{1,2})",
ss: "(\\d{2})",
s: "(\\d{1,2})",
fff: "(\\d{3})",
ff: "(\\d{2})",
f: "(\\d{1})",
tt: "(AM|PM|am|pm)",
t: "([AaPp])",
K: "(Z|[+-]\\d{2}:\\d{2})",
Z: "(Z)",
};
function buildFormatRegex(format) {
const groups = [];
let regexStr = "";
let i = 0;
while (i < format.length) {
let matched = false;
for (const token of TOKEN_ORDER) {
if (format.startsWith(token, i)) {
groups.push(token);
regexStr += TOKEN_PATTERNS[token];
i += token.length;
matched = true;
break;
}
}
if (!matched) {
// Escape literal character
regexStr += format[i].replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
i++;
}
}
return { regex: new RegExp("^" + regexStr + "$"), groups };
}
export function parseDate(input, format) {
if (input == null || format == null) return null;
if (input instanceof Date) return input;
const str = String(input);
const { regex, groups } = buildFormatRegex(format);
const match = str.match(regex);
if (!match) return null;
let year = 0, month = 1, day = 1;
let hour = 0, minute = 0, second = 0, ms = 0;
let isPM = false, isAM = false;
let tzOffset = null; // minutes
for (let g = 0; g < groups.length; g++) {
const token = groups[g];
const val = match[g + 1];
switch (token) {
case "yyyy": year = parseInt(val, 10); break;
case "yy": year = 2000 + parseInt(val, 10); break;
case "MM":
case "M": month = parseInt(val, 10); break;
case "dd":
case "d": day = parseInt(val, 10); break;
case "HH":
case "H": hour = parseInt(val, 10); break;
case "hh":
case "h": hour = parseInt(val, 10); break;
case "mm":
case "m": minute = parseInt(val, 10); break;
case "ss":
case "s": second = parseInt(val, 10); break;
case "fff": ms = parseInt(val, 10); break;
case "ff": ms = parseInt(val, 10) * 10; break;
case "f": ms = parseInt(val, 10) * 100; break;
case "tt": isPM = val.toUpperCase() === "PM"; isAM = val.toUpperCase() === "AM"; break;
case "t": isPM = val.toUpperCase() === "P"; isAM = val.toUpperCase() === "A"; break;
case "Z": tzOffset = 0; break;
case "K":
if (val === "Z") {
tzOffset = 0;
} else {
const sign = val[0] === "+" ? 1 : -1;
const [h, m] = val.substring(1).split(":").map(Number);
tzOffset = sign * (h * 60 + m);
}
break;
}
}
// 12h → 24h conversion
if (isPM && hour < 12) hour += 12;
if (isAM && hour === 12) hour = 0;
if (tzOffset !== null) {
// Build UTC date, apply offset
const utcMs = Date.UTC(year, month - 1, day, hour, minute, second, ms);
return new Date(utcMs - tzOffset * 60000);
}
return new Date(year, month - 1, day, hour, minute, second, ms);
}
export function formatDate(date, spec) {
const pad = (n, l = 2) => String(n).padStart(l, "0");
const tokens = {
yyyy: date.getFullYear(),
yy: String(date.getFullYear()).slice(-2),
MM: pad(date.getMonth() + 1),
M: date.getMonth() + 1,
dd: pad(date.getDate()),
d: date.getDate(),
HH: pad(date.getHours()),
H: date.getHours(),
hh: pad(date.getHours() % 12 || 12),
h: date.getHours() % 12 || 12,
mm: pad(date.getMinutes()),
m: date.getMinutes(),
ss: pad(date.getSeconds()),
s: date.getSeconds(),
fff: pad(date.getMilliseconds(), 3),
};
let result = spec;
for (const [tok, val] of Object.entries(tokens).sort(
(a, b) => b[0].length - a[0].length,
)) {
result = result.replaceAll(tok, String(val));
}
return result;
}
// ─── Input transform presets ───
export const IntInput = { transform: parseInt };
export const FloatInput = { transform: parseFloat };
export const BoolInput = {
transform: (v) => {
if (typeof v === "boolean") return v;
if (typeof v === "string") {
if (v.toLowerCase() === "true") return true;
if (v.toLowerCase() === "false") return false;
}
return Boolean(v);
},
};
export function DateInput(format) {
return {
transform: (v) => parseDate(v, format),
};
}
export const IsoDateInput = DateInput("yyyy-MM-ddTHH:mm:ssK");