node-calendar-js
Version:
Simple JavaScript calendar library.
270 lines (267 loc) • 8.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Calendar = exports.CONSTANT_DAYS_ORDER = void 0;
const table_1 = require("table");
exports.CONSTANT_DAYS_ORDER = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
class Calendar {
/**
* Creates new Calendar
* @example ```js
* const calendar = new Calendar({ year: 2021, month: 6 });
* console.log(calendar.create());
* ```
*/
constructor(options) {
/**
* Current year
*/
this.year = this._validate(options && typeof options.year === "number" ? options.year : new Date().getFullYear(), "number", "year");
/**
* Current month
*/
this.month = this._validate(options && typeof options.month === "number" ? options.month : new Date().getMonth(), "number", "month");
/**
* Days format
*/
this.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
/**
* Months format
*/
this.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
/**
* Holiday(s). By default, it is set to sunday
*/
this.holidays = [0];
}
/**
* Sets custom days notation
* @param days Days name
*/
setDays(days) {
const isValid = days.length === 7 && days.some(x => typeof x === "string");
if (!isValid)
throw new Error("Invalid days data");
this.days = days;
return this;
}
/**
* Sets custom months notaiton
* @param months Months name
*/
setMonths(months) {
const isValid = months.length === 12 && months.some(x => typeof x === "string");
if (!isValid)
throw new Error("Invalid months data");
this.months = months;
return this;
}
/**
* Sets holidays
* @param data Holidays day array. Example: `[0]` refers to `sunday` whereas `[0, 6]` refers to `saturday` and `sunday`.
*/
setHolidays(data) {
const isValid = data.some(x => typeof x === "number" && (x >= 0 && x < 7));
if (!isValid)
throw new Error("Invalid holidays data");
this.holidays = data;
return this;
}
/**
* Returns total number of days in this month
*/
get length() {
if (this.month === 1 && ((this.year % 4 == 0 && this.year % 100 !== 0) || this.year % 400 === 0))
return 29;
return exports.CONSTANT_DAYS_ORDER[this.month];
}
/**
* If it is a leap year
*/
get leap() {
return (this.year % 4 == 0 && this.year % 100 !== 0) || this.year % 400 === 0;
}
/**
* Returns current month name
*/
get monthName() {
return this.months[this.month];
}
/**
* Creates calendar
* @example ```js
* const data = new Calendar({ year: 2020, month: 0 });
* console.log(data.create());
* ```
*/
create() {
const firstDay = new Date(this.year, this.month, 1);
const intitalDay = firstDay.getDay();
const length = this.length;
const month = this.monthName;
let days = [];
let day = 1;
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 7; j++) {
if (day <= length && (i > 0 || j >= intitalDay)) {
days.push({
row: i,
day: day,
name: this.days[j],
holiday: this.holidays.some(x => x === j)
});
day++;
}
else {
days.push({
row: i,
day: null,
name: null,
holiday: false
});
}
}
if (day > length)
break;
}
return {
year: this.year,
month: this.month,
totalDays: length,
monthName: month,
days: days,
leapMonth: this.length === 29,
leapYear: this.leap
};
}
/**
* Creates HTML output
* @param options Calendar html output options
*/
toHTML(options) {
if (!options)
options = {};
const data = this.create();
const cap = `<caption>${data.monthName} ${data.year}</caption>`;
let heading = "";
let days = "";
const ROWS = [...new Set(data.days.map(m => m.row))];
for (let i = 0; i < 7; i++) {
heading += `<th class="${this.days[i].toLowerCase()}">${this.days[i]}</th>`;
}
ROWS.forEach(index => {
let tempr = "";
const row = data.days.filter(x => x.row === index);
for (let i = 0; i < row.length; i++) {
tempr += `<td class="day${row[i].holiday ? " holiday" : ""}">${row[i].day || ""}</td>`;
}
days += `<tr>${tempr}</tr>`;
});
const style = `
<style>
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
th,
td {
padding: 5px;
text-align: left;
}
.holiday {
color: #db0000;
}
</style>
`;
return `${!!options.includeStyles ? style : ""}<table width="100%">${cap}<thead><tr>${heading}</tr></thead><tbody>${days}</tbody></table>`;
}
/**
* String representation of the calendar
*/
toString() {
const data = this.create();
const ROWS = [...new Set(data.days.map(m => m.row))];
const heading = [];
const days = [];
for (let i = 0; i < 7; i++) {
heading.push(this.days[i]);
}
ROWS.forEach(index => {
const row = data.days.filter(x => x.row === index).map(m => `${m.day || ""}`);
days.push(row);
});
const header = table_1.table([[data.monthName, data.year]]);
const body = [
heading,
...days
];
const tableData = table_1.table(body, {
border: {
topBody: "─",
topJoin: "┬",
topLeft: "┌",
topRight: "┐",
bottomBody: "─",
bottomJoin: "┴",
bottomLeft: "└",
bottomRight: "┘",
bodyLeft: "│",
bodyRight: "│",
bodyJoin: "│",
joinBody: "─",
joinLeft: "├",
joinRight: "┤",
joinJoin: "┼"
}
});
return `${header}${tableData}`;
}
toJSON() {
return this.create();
}
/**
* Static calendar creator
* @param year Creates calendar of full year
* @example ```js
* const calendar = Calendar.createCalendar(2020);
* console.log(calendar.map(m => m.monthName));
* ```
*/
static createCalendar(year = new Date().getFullYear()) {
const valid = typeof year === "number" && year >= 1000 && year <= 9999;
if (!valid)
throw new TypeError(`Invalid year: ${year}!`);
const cal = [];
for (let i = 0; i < 12; i++) {
const st = new Calendar({
year: year,
month: i
});
cal.push(st.create());
}
return cal;
}
/**
* Internal number validation method
* @param data data to validate
* @param vtype value data type
* @param dataType validation data type
*/
_validate(data, vtype = "number", dataType) {
switch (dataType) {
case "month":
if (typeof data === vtype && !(data < 0 && data > 11))
return data;
throw new TypeError(`Invalid month: ${data}!`);
case "year":
if (typeof data === vtype && !(data < 1000 && data > 9999))
return data;
throw new TypeError(`Invalid year: ${data}!`);
default:
throw new Error("Invalid type");
}
}
}
exports.Calendar = Calendar;
exports.default = Calendar;