schedule-grid-js/www/utils.js

209 lines
5.5 KiB
JavaScript

export class Time {
constructor(obj) {
this.hour = parseInt(obj.hour ?? 0);
this.minute = parseInt(obj.minute ?? 0);
this.second = parseInt(obj.second ?? 0);
}
static fromInputValue(str) {
const parts = str.split(':').map(part => parseInt(part));
return new Time({hour: parts[0], minute: parts[1], second: parts[2]});
}
asJsonObject() {
let res = {}
if (this.hour !== 0) res.hour = this.hour;
if (this.minute !== 0) res.minute = this.minute;
if (this.second !== 0) res.second = this.second;
return res;
}
cmp(other) {
if (this.hour < other.hour) return -1;
if (this.hour > other.hour) return 1;
if (this.minute < other.minute) return -1;
if (this.minute > other.minute) return 1;
if (this.second < other.second) return -1;
if (this.second > other.second) return 1;
return 0;
}
get durationSinceMidnight() {
return new TimeRange({start_time: new Time({}), end_time: this}).duration;
}
plus(duration) {
const total = this.durationSinceMidnight.total_seconds
+ duration.total_seconds;
const second = total % 60;
const minute = Math.floor(total / 60) % 60;
const hour = Math.floor(total / 60 / 60) % 24;
return new Time({hour, minute, second});
}
// TODO Just ignoring seconds?
to12HourString() {
let hour = this.hour % 12;
if (hour === 0) hour = 12;
return hour + ':' + this.minute.toString().padStart(2, '0');
}
get inputValue() {
return this.hour.toString().padStart(2, '0') + ':'
+ this.minute.toString().padStart(2, '0') + ':'
+ this.second.toString().padStart(2, '0');
}
}
export class Duration {
constructor(obj) {
if (obj.total_seconds) {
this.total_seconds = obj.total_seconds;
this.hour = Math.floor(obj.total_seconds / 60 / 60);
this.minute = Math.floor(obj.total_seconds / 60) % 60;
this.second = obj.total_seconds % 60;
} else {
this.hour = obj.hour ?? 0;
this.minute = obj.minute ?? 0;
this.second = obj.second ?? 0;
this.total_seconds = ((this.hour * 60) + this.minute) * 60 + this.second;
}
}
asJsonObject() {
let res = {}
if (this.hour !== 0) res.hour = this.hour;
if (this.minute !== 0) res.minute = this.minute;
if (this.second !== 0) res.second = this.second;
return res;
}
dividedBy(other) {
return this.total_seconds / other.total_seconds;
}
times(num) {
const total = this.total_seconds * num;
const second = total % 60;
const minute = Math.floor(total / 60) % 60;
const hour = Math.floor(total / 60 / 60);
return new Duration({hour, minute, second});
}
toString() {
let res = "";
if (this.hour) {
res += this.hour + (this.hour === 1 ? " hour" : " hours ");
}
if (this.minute) {
res += this.minute + (this.minute === 1 ? " minute" : " minutes ");
}
if (this.second) {
res += this.second + (this.second === 1 ? " second" : " seconds");
}
return this.total_seconds === 0 ? "0 seconds" : res.trimEnd();
}
get inputValue() {
return new Time({}).plus(this).inputValue;
}
}
export class TimeRange {
constructor(args) {
this.start_time = args.start_time;
this.end_time = args.end_time;
}
get duration() {
return new Duration({
'total_seconds': 60 * 60 * (this.end_time.hour - this.start_time.hour)
+ 60 * (this.end_time.minute - this.start_time.minute)
+ this.end_time.second - this.start_time.second,
});
}
overlaps(other) {
return (other.start_time.cmp(this.start_time) <= 0
&& this.start_time.cmp(other.end_time) < 0)
|| (this.start_time.cmp(other.start_time) <= 0
&& other.start_time.cmp(this.end_time) < 0);
}
to12HourString() {
return this.start_time.to12HourString() + '-'
+ this.end_time.to12HourString();
}
}
const dayDictionary = {
'M': 'Monday',
'T': 'Tuesday',
'W': 'Wednesday',
'R': 'Thursday',
'F': 'Friday',
'S': 'Saturday',
'U': 'Sunday',
};
export function DayString(day) {
return dayDictionary[day];
}
export const allDays = Object.keys(dayDictionary);
export function clearChildren(el) {
while (el.firstChild) el.removeChild(el.lastChild);
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
export function setDifference(setA, setB) {
const _difference = new Set(setA);
for (const elem of setB) {
_difference.delete(elem);
}
return _difference;
}
export function setIntersection(setA, setB) {
if (!(setA instanceof Set)) setA = new Set(setA);
const _intersection = new Set();
for (const elem of setB) {
if (setA.has(elem)) {
_intersection.add(elem);
}
}
return _intersection;
}
export function setEqual(setA, setB) {
if (!(setA instanceof Set)) setA = new Set(setA);
if (!(setB instanceof Set)) setB = new Set(setB);
if (setA.size != setB.size) return false;
return setDifference(setA, setB).size === 0;
}
// From https://stackoverflow.com/a/68227965
export function debounceAsync(task, ms) {
function deferred() {
let cancel, promise = new Promise((resolve, reject) => {
cancel = reject
setTimeout(resolve, ms)
})
return { promise, cancel }
}
let t = { promise: null, cancel: _ => void 0 }
return async (...args) => {
try {
t.cancel()
t = deferred()
await t.promise
await task(...args)
}
catch (_) { /* prevent memory leak */ }
}
}