rpg-calendar
Version:
Low level library for working with RPG/Fantasy dates
230 lines • 20.4 kB
JavaScript
import { isLeapYearBuilder, getDaysInYearBuilder, getDaysInMonthBuilder, getDaysInWeekBuilder, getYearNameBuilder, getPrevMonthYearBuilder, getNextMonthYearBuilder, getExtraDayBuilder, getNextYearBuilder, getPrevYearBuilder } from './lib';
import { getTimeString } from './lib/time';
export class RPGCalendar {
config;
// Utility functions
isLeapYear;
getDaysInYear;
getDaysInMonth;
getDaysInWeek;
getYearName;
getDayName;
getMonthName;
getConfigMonth;
getWeekDays;
getNextMonthYear;
getPrevMonthYear;
getNextYear;
getPrevYear;
getExtraDay;
constructor(config) {
this.config = config;
const { yearNameMap = {} } = config;
this.isLeapYear = isLeapYearBuilder(config?.leapYearInterval || 0, config?.hasYear0);
this.getDaysInYear = getDaysInYearBuilder(config.months, this.isLeapYear);
this.getDaysInMonth = getDaysInMonthBuilder(config.months, this.isLeapYear);
this.getDaysInWeek = getDaysInWeekBuilder(config.weekdays);
this.getYearName = getYearNameBuilder(yearNameMap);
this.getWeekDays = () => this.config.weekdays;
this.getDayName = (dayOfWeek) => this.config.weekdays?.[dayOfWeek - 1]?.name || 'Unknown';
this.getMonthName = (month) => this.config.months?.[month - 1]?.name || 'Unknown';
this.getConfigMonth = (month) => this.config.months[month - 1];
this.getPrevMonthYear = getPrevMonthYearBuilder(config?.hasYear0, this.config.months);
this.getNextMonthYear = getNextMonthYearBuilder(config?.hasYear0, this.config.months);
this.getPrevYear = getPrevYearBuilder(config?.hasYear0);
this.getNextYear = getNextYearBuilder(config?.hasYear0);
this.getExtraDay = getExtraDayBuilder(this.config.months, this.isLeapYear);
}
// dateStringToRPGDate accepts the date in a more readable format, parses it and returns an RPGCalendarDate object.
// dateString resembles the ISO 8601 format but without the fractional seconds: YYYY-MM-DD hh:mm:ss
dateStringToRPGDate(dateString) {
const dateAndTime = dateString.split(' ');
if (dateAndTime.length !== 2) {
throw new Error(`${dateString} is not a valid date time string format`);
}
const dateParts = dateAndTime[0].split('-');
if (dateParts.length !== 3) {
throw new Error(`${dateString} does not contain a valid date part`);
}
const timeParts = dateAndTime[1].split(':');
if (timeParts.length !== 3) {
throw new Error(`${dateString} does not contain a valid time part`);
}
// Assign known values
const time = {
hour: parseInt(timeParts[0]),
minute: parseInt(timeParts[1]),
second: parseInt(timeParts[2])
};
const year = parseInt(dateParts[0]);
const monthOfYear = parseInt(dateParts[1]);
const dayOfMonth = parseInt(dateParts[2]);
// Calculate day of week
const daysInWeek = this.getDaysInWeek();
const dayMod = dayOfMonth % daysInWeek;
const dayOfWeek = dayMod === 0 ? dayOfMonth / daysInWeek : dayMod;
return {
time,
year,
dayOfMonth,
monthOfYear,
dayOfWeek,
dayName: this.getDayName(dayOfWeek),
inLeapYear: this.isLeapYear(year),
monthName: this.getMonthName(monthOfYear),
yearName: this.getYearName(year)
};
}
// epochToDate accepts both a simple day epoch (number of days) or a complex epoch (number of days with the time)
epochToDate(epoch) {
let epochDay;
let time = {
hour: 0,
minute: 0,
second: 0
};
if (typeof epoch === 'string') {
const dateParts = epoch.split('-');
if (dateParts.length !== 2) {
throw new Error(`${epoch} is not a valid date time epoch format`);
}
const timeParts = dateParts[1].split(':');
if (timeParts.length !== 3) {
throw new Error(`${epoch} is not a valid time format`);
}
time = {
hour: parseInt(timeParts[0]),
minute: parseInt(timeParts[1]),
second: parseInt(timeParts[2])
};
epochDay = parseInt(dateParts[0]);
}
else {
epochDay = epoch;
}
// Calculate the year
let year = 0;
let dayOfYear = epochDay;
while (dayOfYear >= this.getDaysInYear(year)) {
dayOfYear = dayOfYear - this.getDaysInYear(year);
year++;
}
// Calculate the month of the year
let monthOfYear = 1;
let dayOfMonth = dayOfYear;
while (dayOfMonth > this.getDaysInMonth(monthOfYear, year)) {
dayOfMonth = dayOfMonth - this.getDaysInMonth(monthOfYear, year);
monthOfYear++;
}
// Calculate day of week
const daysInWeek = this.getDaysInWeek();
const dayMod = dayOfMonth % daysInWeek;
const dayOfWeek = dayMod === 0 ? dayOfMonth / daysInWeek : dayMod;
const epochDayTime = `${epochDay}-${getTimeString(time)}`;
return {
year,
epochDay,
epochDayTime,
monthOfYear,
dayOfMonth,
dayOfYear,
dayOfWeek,
dayName: this.getDayName(dayOfWeek),
inLeapYear: this.isLeapYear(year),
monthName: this.getMonthName(monthOfYear),
yearName: this.getYearName(year),
time
};
}
// createDate is a simple interface that lets you construct a date object. Only the year is required, everything else
// defaults (first month, first day, and 00:00:00)
createDate(year, month = 1, day = 1, hour = 0, minute = 0, second = 0) {
const startYear = this.config?.hasYear0 ? 0 : 1;
// Count the days up to the year
let epochDay = 0;
for (let y = startYear; y < year; y++) {
epochDay += this.getDaysInYear(y);
}
// Count the days up to the month
let dayOfYear = 0;
for (let m = 1; m < month; m++) {
epochDay += this.getDaysInMonth(m, year);
}
// Add in the days
epochDay += day;
dayOfYear += day;
// Calculate day of week
const daysInWeek = this.getDaysInWeek();
const dayMod = day % daysInWeek;
const dayOfWeek = dayMod === 0 ? day / daysInWeek : dayMod;
// Setup time
const time = {
hour,
minute,
second
};
const epochDayTime = `${epochDay}-${getTimeString(time)}`;
return {
year,
epochDay,
epochDayTime,
monthOfYear: month,
dayOfMonth: day,
dayOfYear,
dayOfWeek,
dayName: this.getDayName(dayOfWeek),
inLeapYear: this.isLeapYear(year),
monthName: this.getMonthName(month),
yearName: this.getYearName(year),
time,
extraDay: this.getExtraDay(year, month, day)
};
}
// getDisplayMonth returns a month object based on the month and year
getDisplayMonth(mq) {
const { year, month } = mq;
// Get the month as it's defined in the configuration
const configMonth = this.getConfigMonth(month);
// Get the first day of the month so that we can pull off all the information that pertains to the entire month.
const firstDayOfMonth = this.createDate(year, month, 1);
const { yearName } = firstDayOfMonth;
const daysInMonth = this.getDaysInMonth(month, year);
const daysInWeek = this.getDaysInWeek();
const weeks = [];
for (let d = 1; d <= daysInMonth; d++) {
const w = Math.floor((d - 1) / daysInWeek);
if (!weeks?.[w]) {
weeks.push([]);
}
weeks[w].push(this.createDate(year, month, d));
}
return {
...configMonth,
weeks,
year,
yearName,
monthOfYear: month,
weekdays: this.getWeekDays(),
nextMonthQuery: this.getNextMonthYear({ year, month }),
prevMonthQuery: this.getPrevMonthYear({ year, month }),
nextYearQuery: this.getNextYear({ year, month }),
prevYearQuery: this.getPrevYear({ year, month })
};
}
// getMonths returns all the months in a year. Useful when displaying a dropdown.
getMonths() {
return this.config.months;
}
// getDaySpanFromDate returns a RPGDateSpan with the min and max (start/end) times for the given day. This is useful
// when creating queries or defining an event that lasted for an entire day.
getDaySpanFromDate(date) {
const eh = this.config.hoursInDay - 1;
const em = this.config.minutesInHour - 1;
const es = this.config.secondsInMinutes - 1;
return {
start: this.createDate(date.year, date.monthOfYear, date.dayOfMonth),
end: this.createDate(date.year, date.monthOfYear, date.dayOfMonth, eh, em, es)
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUlBHQ2FsZW5kYXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvUlBHQ2FsZW5kYXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBV0EsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixvQkFBb0IsRUFDcEIscUJBQXFCLEVBQ3JCLG9CQUFvQixFQUNwQixrQkFBa0IsRUFDbEIsdUJBQXVCLEVBQ3ZCLHVCQUF1QixFQUN2QixrQkFBa0IsRUFDbEIsa0JBQWtCLEVBQ2xCLGtCQUFrQixFQUNuQixNQUFNLE9BQU8sQ0FBQztBQUNmLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFM0MsTUFBTSxPQUFPLFdBQVc7SUFpQkY7SUFoQnBCLG9CQUFvQjtJQUNaLFVBQVUsQ0FBNEI7SUFDdEMsYUFBYSxDQUEyQjtJQUN4QyxjQUFjLENBQTBDO0lBQ3hELGFBQWEsQ0FBZTtJQUM1QixXQUFXLENBQTJCO0lBQ3RDLFVBQVUsQ0FBZ0M7SUFDMUMsWUFBWSxDQUE0QjtJQUN4QyxjQUFjLENBQXNDO0lBQ3BELFdBQVcsQ0FBNkI7SUFDeEMsZ0JBQWdCLENBQStEO0lBQy9FLGdCQUFnQixDQUErRDtJQUMvRSxXQUFXLENBQStEO0lBQzFFLFdBQVcsQ0FBK0Q7SUFDMUUsV0FBVyxDQUFnRjtJQUVuRyxZQUFvQixNQUF5QjtRQUF6QixXQUFNLEdBQU4sTUFBTSxDQUFtQjtRQUMzQyxNQUFNLEVBQUUsV0FBVyxHQUFHLEVBQUUsRUFBRSxHQUFHLE1BQU0sQ0FBQztRQUVwQyxJQUFJLENBQUMsVUFBVSxHQUFHLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3JGLElBQUksQ0FBQyxhQUFhLEdBQUcsb0JBQW9CLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDMUUsSUFBSSxDQUFDLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM1RSxJQUFJLENBQUMsYUFBYSxHQUFHLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMzRCxJQUFJLENBQUMsV0FBVyxHQUFHLGtCQUFrQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxXQUFXLEdBQUcsR0FBeUIsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDO1FBQ3BFLElBQUksQ0FBQyxVQUFVLEdBQUcsQ0FBQyxTQUFpQixFQUFVLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLElBQUksU0FBUyxDQUFDO1FBQzFHLElBQUksQ0FBQyxZQUFZLEdBQUcsQ0FBQyxLQUFhLEVBQVUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksSUFBSSxTQUFTLENBQUM7UUFDbEcsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLEtBQWEsRUFBb0IsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN6RixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsdUJBQXVCLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3RGLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyx1QkFBdUIsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdEYsSUFBSSxDQUFDLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUVELG1IQUFtSDtJQUNuSCxtR0FBbUc7SUFDbkcsbUJBQW1CLENBQUMsVUFBa0I7UUFDcEMsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxQyxJQUFJLFdBQVcsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1lBQzVCLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxVQUFVLHlDQUF5QyxDQUFDLENBQUM7U0FDekU7UUFDRCxNQUFNLFNBQVMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVDLElBQUksU0FBUyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7WUFDMUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLFVBQVUscUNBQXFDLENBQUMsQ0FBQztTQUNyRTtRQUVELE1BQU0sU0FBUyxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtZQUMxQixNQUFNLElBQUksS0FBSyxDQUFDLEdBQUcsVUFBVSxxQ0FBcUMsQ0FBQyxDQUFDO1NBQ3JFO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sSUFBSSxHQUFvQjtZQUM1QixJQUFJLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM1QixNQUFNLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM5QixNQUFNLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUMvQixDQUFDO1FBQ0YsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BDLE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFMUMsd0JBQXdCO1FBQ3hCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN4QyxNQUFNLE1BQU0sR0FBRyxVQUFVLEdBQUcsVUFBVSxDQUFDO1FBQ3ZDLE1BQU0sU0FBUyxHQUFHLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUVsRSxPQUFPO1lBQ0wsSUFBSTtZQUNKLElBQUk7WUFDSixVQUFVO1lBQ1YsV0FBVztZQUNYLFNBQVM7WUFDVCxPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUM7WUFDbkMsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDO1lBQ2pDLFNBQVMsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBQztZQUN6QyxRQUFRLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUM7U0FDakMsQ0FBQztJQUNKLENBQUM7SUFFRCxpSEFBaUg7SUFDakgsV0FBVyxDQUFDLEtBQXNCO1FBQ2hDLElBQUksUUFBZ0IsQ0FBQztRQUNyQixJQUFJLElBQUksR0FBb0I7WUFDMUIsSUFBSSxFQUFFLENBQUM7WUFDUCxNQUFNLEVBQUUsQ0FBQztZQUNULE1BQU0sRUFBRSxDQUFDO1NBQ1YsQ0FBQztRQUVGLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFO1lBQzdCLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbkMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtnQkFDMUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssd0NBQXdDLENBQUMsQ0FBQzthQUNuRTtZQUNELE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDMUMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtnQkFDMUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssNkJBQTZCLENBQUMsQ0FBQzthQUN4RDtZQUVELElBQUksR0FBRztnQkFDTCxJQUFJLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUIsTUFBTSxFQUFFLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQzlCLE1BQU0sRUFBRSxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQy9CLENBQUM7WUFFRixRQUFRLEdBQUcsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ25DO2FBQU07WUFDTCxRQUFRLEdBQUcsS0FBSyxDQUFDO1NBQ2xCO1FBRUQscUJBQXFCO1FBQ3JCLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNiLElBQUksU0FBUyxHQUFHLFFBQVEsQ0FBQztRQUN6QixPQUFPLFNBQVMsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQzVDLFNBQVMsR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNqRCxJQUFJLEVBQUUsQ0FBQztTQUNSO1FBRUQsa0NBQWtDO1FBQ2xDLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQztRQUNwQixJQUFJLFVBQVUsR0FBRyxTQUFTLENBQUM7UUFDM0IsT0FBTyxVQUFVLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDMUQsVUFBVSxHQUFHLFVBQVUsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUNqRSxXQUFXLEVBQUUsQ0FBQztTQUNmO1FBRUQsd0JBQXdCO1FBQ3hCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN4QyxNQUFNLE1BQU0sR0FBRyxVQUFVLEdBQUcsVUFBVSxDQUFDO1FBQ3ZDLE1BQU0sU0FBUyxHQUFHLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUVsRSxNQUFNLFlBQVksR0FBRyxHQUFHLFFBQVEsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUUxRCxPQUFPO1lBQ0wsSUFBSTtZQUNKLFFBQVE7WUFDUixZQUFZO1lBQ1osV0FBVztZQUNYLFVBQVU7WUFDVixTQUFTO1lBQ1QsU0FBUztZQUNULE9BQU8sRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQztZQUNuQyxVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUM7WUFDakMsU0FBUyxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsV0FBVyxDQUFDO1lBQ3pDLFFBQVEsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQztZQUNoQyxJQUFJO1NBQ0wsQ0FBQztJQUNKLENBQUM7SUFFRCxzSEFBc0g7SUFDdEgsa0RBQWtEO0lBQ2xELFVBQVUsQ0FBQyxJQUFZLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQyxFQUFFLElBQUksR0FBRyxDQUFDLEVBQUUsTUFBTSxHQUFHLENBQUMsRUFBRSxNQUFNLEdBQUcsQ0FBQztRQUMzRSxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFaEQsZ0NBQWdDO1FBQ2hDLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztRQUNqQixLQUFLLElBQUksQ0FBQyxHQUFHLFNBQVMsRUFBRSxDQUFDLEdBQUcsSUFBSSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3JDLFFBQVEsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ25DO1FBRUQsaUNBQWlDO1FBQ2pDLElBQUksU0FBUyxHQUFHLENBQUMsQ0FBQztRQUNsQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQzlCLFFBQVEsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUMxQztRQUVELGtCQUFrQjtRQUNsQixRQUFRLElBQUksR0FBRyxDQUFDO1FBQ2hCLFNBQVMsSUFBSSxHQUFHLENBQUM7UUFFakIsd0JBQXdCO1FBQ3hCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN4QyxNQUFNLE1BQU0sR0FBRyxHQUFHLEdBQUcsVUFBVSxDQUFDO1FBQ2hDLE1BQU0sU0FBUyxHQUFHLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUUzRCxhQUFhO1FBQ2IsTUFBTSxJQUFJLEdBQUc7WUFDWCxJQUFJO1lBQ0osTUFBTTtZQUNOLE1BQU07U0FDUCxDQUFDO1FBQ0YsTUFBTSxZQUFZLEdBQUcsR0FBRyxRQUFRLElBQUksYUFBYSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFFMUQsT0FBTztZQUNMLElBQUk7WUFDSixRQUFRO1lBQ1IsWUFBWTtZQUNaLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLFVBQVUsRUFBRSxHQUFHO1lBQ2YsU0FBUztZQUNULFNBQVM7WUFDVCxPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUM7WUFDbkMsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDO1lBQ2pDLFNBQVMsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQztZQUNuQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUM7WUFDaEMsSUFBSTtZQUNKLFFBQVEsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDO1NBQzdDLENBQUM7SUFDSixDQUFDO0lBRUQscUVBQXFFO0lBQ3JFLGVBQWUsQ0FBQyxFQUF5QjtRQUN2QyxNQUFNLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsQ0FBQztRQUMzQixxREFBcUQ7UUFDckQsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUUvQyxnSEFBZ0g7UUFDaEgsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3hELE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxlQUFlLENBQUM7UUFDckMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDckQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3hDLE1BQU0sS0FBSyxHQUF3QixFQUFFLENBQUM7UUFFdEMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLFdBQVcsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNyQyxNQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDO1lBQzNDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDZixLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQ2hCO1lBRUQsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNoRDtRQUVELE9BQU87WUFDTCxHQUFHLFdBQVc7WUFDZCxLQUFLO1lBQ0wsSUFBSTtZQUNKLFFBQVE7WUFDUixXQUFXLEVBQUUsS0FBSztZQUNsQixRQUFRLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRTtZQUM1QixjQUFjLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQ3RELGNBQWMsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7WUFDdEQsYUFBYSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7WUFDaEQsYUFBYSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7U0FDakQsQ0FBQztJQUNKLENBQUM7SUFFRCxrRkFBa0Y7SUFDbEYsU0FBUztRQUNQLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDNUIsQ0FBQztJQUVELHFIQUFxSDtJQUNySCw0RUFBNEU7SUFDNUUsa0JBQWtCLENBQUMsSUFBcUI7UUFDdEMsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQ3RDLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQztRQUN6QyxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGdCQUFnQixHQUFHLENBQUMsQ0FBQztRQUU1QyxPQUFPO1lBQ0wsS0FBSyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUM7WUFDcEUsR0FBRyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7U0FDL0UsQ0FBQztJQUNKLENBQUM7Q0FDRiJ9