|
// class that works with data (dates) |
|
class CalendarModel { |
|
constructor(calendarSettings) { |
|
if (!(calendarSettings instanceof CalendarSettings)) { |
|
throw new TypeError( |
|
`First argument of ${ |
|
this.constructor.name |
|
} should be instance of CalendarSettings` |
|
); |
|
} |
|
|
|
this.calendarSettings = calendarSettings; |
|
this.params = calendarSettings.params; |
|
|
|
// this is today/current date, and we should not modify it (unless current date change after noon) |
|
this.currentDate = new Date(); |
|
|
|
// this is the date we currently display - it updates once we switch month |
|
this.date = new Date(this.params.initialDate); |
|
|
|
/* |
|
There are some countries where week starts on Sun, Mon, Sat or even Fri |
|
This object describes when week should start based on locale |
|
Attention! This list might be incomplete |
|
*/ |
|
this.weekStarts = { |
|
// weekday 1 |
|
mon: [ |
|
"af", |
|
"ar-tn", |
|
"az", |
|
"be", |
|
"bg", |
|
"bm", |
|
"br", |
|
"bs", |
|
"ca", |
|
"cs", |
|
"cv", |
|
"cy", |
|
"da", |
|
"de-at", |
|
"de-ch", |
|
"de", |
|
"el", |
|
"en", |
|
"en-au", |
|
"en-gb", |
|
"en-ie", |
|
"en-nz", |
|
"eo", |
|
"es-do", |
|
"es", |
|
"et", |
|
"eu", |
|
"fi", |
|
"fo", |
|
"fr-ca", |
|
"fr-ch", |
|
"fr", |
|
"fy", |
|
"gd", |
|
"gl", |
|
"gom-latn", |
|
"he", |
|
"hr", |
|
"hu", |
|
"hy-am", |
|
"id", |
|
"is", |
|
"it", |
|
"ja", |
|
"jv", |
|
"ka", |
|
"kk", |
|
"km", |
|
"ko", |
|
"ky", |
|
"lb", |
|
"lo", |
|
"lt", |
|
"lv", |
|
"me", |
|
"mi", |
|
"mk", |
|
"ml", |
|
"mn", |
|
"ms-my", |
|
"ms", |
|
"mt", |
|
"my", |
|
"nb", |
|
"nl-be", |
|
"nl", |
|
"nn", |
|
"pl", |
|
"pt-br", |
|
"pt", |
|
"ro", |
|
"ru", |
|
"sd", |
|
"se", |
|
"si", |
|
"sk", |
|
"sl", |
|
"sq", |
|
"sr-cyrl", |
|
"sr", |
|
"ss", |
|
"sv", |
|
"sw", |
|
"tet", |
|
"tg", |
|
"th", |
|
"tl-ph", |
|
"tlh", |
|
"tr", |
|
"tzl", |
|
"ug-cn", |
|
"uk", |
|
"ur", |
|
"uz-latn", |
|
"uz", |
|
"vi", |
|
"x-pseudo", |
|
"yo", |
|
"zh-cn", |
|
"zh-hk", |
|
"zh-tw", |
|
"en-ca" |
|
], |
|
|
|
// weekday 0 |
|
sun: [ |
|
"ar-dz", |
|
"ar-kw", |
|
"ar-sa", |
|
"bn", |
|
"bo", |
|
"dv", |
|
"en-us", |
|
"en-il", |
|
"es-us", |
|
"gu", |
|
"hi", |
|
"kn", |
|
"mr", |
|
"ne", |
|
"pa-in", |
|
"ta", |
|
"te" |
|
], |
|
|
|
// weekday 6 |
|
sat: ["ar-ly", "ar-ma", "ar", "fa", "tzm-latn", "tzm"], |
|
|
|
// weekday 5 |
|
fri: ["mv"] |
|
}; |
|
|
|
// init |
|
this.init(); |
|
} |
|
|
|
init() { |
|
this.updateDate(); |
|
this.setWeekStartDay(); |
|
this.setDayNames(); |
|
} |
|
|
|
/** |
|
* Checks if argument 'day' is valid day (0-6) |
|
* @param {number} day |
|
* @returns {boolean} |
|
*/ |
|
isValidDay(day) { |
|
return ( |
|
typeof day === "number" && |
|
!Number.isNaN(day) && |
|
Number.isFinite(day) && |
|
day >= 0 && |
|
day <= 6 |
|
); |
|
} |
|
|
|
/** |
|
* Checks if argument 'month' is unmber in correct range (0-11) |
|
* @param {number} month 0-11 |
|
* @returns {boolean} if month is number in range between 0 and 11 |
|
*/ |
|
isValidMonth(month) { |
|
return ( |
|
typeof month === "number" && |
|
!Number.isNaN(month) && |
|
Number.isFinite(month) && |
|
month >= 0 && |
|
month <= 11 |
|
); |
|
} |
|
|
|
/** |
|
* Checks if argument 'year' is number |
|
* In theory, we might work with past or future years so we don't have any limits here |
|
* @param {number} year |
|
* @returns {boolean} if argument 'year' is number |
|
*/ |
|
isValidYear(year) { |
|
return ( |
|
typeof year === "number" && !Number.isNaN(year) && Number.isFinite(year) |
|
); |
|
} |
|
|
|
/** |
|
* Check if argument 'date' is insatce of Date |
|
* @param {Date} date |
|
*/ |
|
isDate(date) { |
|
return date instanceof Date; |
|
} |
|
|
|
/** |
|
* Updates year and month values (when we switch prev/next month in UI) |
|
*/ |
|
updateDate() { |
|
this.year = this.date.getFullYear(); |
|
this.month = this.date.getMonth(); |
|
} |
|
|
|
updateParams() { |
|
this.params = this.calendarSettings.params; |
|
this.init(); |
|
} |
|
|
|
/** |
|
* Returns next week date after dayOfWeek |
|
* @param {Date} date |
|
* @param {number} dayOfWeek (0-6) |
|
* @returns {Date} |
|
*/ |
|
getNextWeekStart(date = this.date, dayOfWeek = 0) { |
|
if (this.isDate(date) && this.isValidDay(dayOfWeek)) { |
|
const resultDate = new Date(date.getTime()); |
|
|
|
resultDate.setDate(date.getDate() + (7 + dayOfWeek - date.getDay()) % 7); |
|
|
|
return resultDate; |
|
} |
|
} |
|
|
|
/** |
|
* Returns day of the week (0 to 6) |
|
* @param {number} month number in range between 0 and 11 |
|
* @param {number} year any number (is case of past or future years) |
|
* @returns {number} day of the week (0 is Sunday, 1 is Monday, ... 6 is Saturday) |
|
*/ |
|
getFirstDayOfTheMonth(month = this.month, year = this.year) { |
|
if (this.isValidMonth(month) && this.isValidYear(year)) { |
|
return new Date(year, month, 1).getDay(); |
|
} |
|
} |
|
|
|
/** |
|
* Returns month matrix (array of Sundays, Mondays, etc.) |
|
* @param {number} month 0-11 |
|
* @param {number} year any number |
|
* @returns {Array} month matrix |
|
* @example |
|
* [ |
|
* [4, 11, 18, 25, 2, 9], // sun |
|
* [29, 5, 12, 19, 26, 3], // mon |
|
* [30, 6, 13, 20, 27, 4], // tue |
|
* [31, 7, 14, 21, 28, 5], // wed |
|
* [1, 8, 15, 22, 29, 6], // thu |
|
* [2, 9, 16, 23, 30, 7], // fri |
|
* [3, 10, 17, 24, 1, 8] // sat |
|
* ] |
|
*/ |
|
getMonthMatrix(month = this.month, year = this.year) { |
|
if (this.isValidMonth(month) && this.isValidYear(year)) { |
|
const monthStartDay = this.getFirstDayOfTheMonth(); // 0-6 where 0 is Sun, 1 is Mon, etc. |
|
const prevMonthOffset = -monthStartDay + 2; // gets day offset from prev month before the first day of the current month |
|
const prevSixDays = -5; |
|
const offsetDate = new Date( |
|
year, |
|
month, |
|
monthStartDay === 0 ? prevSixDays : prevMonthOffset |
|
); |
|
|
|
const mon = []; |
|
const tue = []; |
|
const wed = []; |
|
const thu = []; |
|
const fri = []; |
|
const sat = []; |
|
const sun = []; |
|
|
|
for (let i = 1, sixWeeks = 42; i <= sixWeeks; i++) { |
|
const date = offsetDate.getDate(); |
|
const day = i % 7; // 0-6 where 0 is Sun, 1 is Mon, etc. |
|
|
|
switch (day) { |
|
case 0: |
|
sun.push(date); |
|
break; |
|
case 1: |
|
mon.push(date); |
|
break; |
|
case 2: |
|
tue.push(date); |
|
break; |
|
case 3: |
|
wed.push(date); |
|
break; |
|
case 4: |
|
thu.push(date); |
|
break; |
|
case 5: |
|
fri.push(date); |
|
break; |
|
case 6: |
|
sat.push(date); |
|
break; |
|
} |
|
|
|
offsetDate.setDate(date + 1); |
|
} |
|
|
|
return [sun, mon, tue, wed, thu, fri, sat]; |
|
} |
|
} |
|
|
|
/** |
|
* Returns a string with localised month name and year |
|
* @returns {sring} |
|
* @example November 2018 |
|
*/ |
|
getCalendarMonthAndYear() { |
|
const monthName = this.date.toLocaleString(this.params.locale, { |
|
month: "long" |
|
}); |
|
return `${monthName} ${this.year}`; |
|
} |
|
|
|
/** |
|
* Sets array of localised short day names |
|
* @example ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'] |
|
*/ |
|
setDayNames() { |
|
const weekStart = this.getNextWeekStart(); |
|
const dayNames = []; |
|
|
|
for (let i = 0; i < 7; i++) { |
|
dayNames.push( |
|
new Date(this.year, this.month, weekStart.getDate() + i).toLocaleString( |
|
this.params.locale, |
|
{ weekday: this.params.weekdayLength } |
|
) |
|
); |
|
} |
|
|
|
this.dayNames = dayNames; |
|
} |
|
|
|
/** |
|
* Sets this.date to prev month (when we switch to prev month in UI) |
|
*/ |
|
setPrevMonth() { |
|
this.date.setMonth(this.month - 1); |
|
this.updateDate(); |
|
} |
|
|
|
/** |
|
* Sets this.date to next month (when we switch to next month in UI) |
|
*/ |
|
setNextMonth() { |
|
this.date.setMonth(this.month + 1); |
|
this.updateDate(); |
|
} |
|
|
|
/** |
|
* Sets week start day code (0 is Sun, 1 is Mon, ... 6 is Sat) |
|
* In some countries week starts on Mon, Sun, Sat or even Fri |
|
*/ |
|
setWeekStartDay() { |
|
if (this.weekStarts.mon.includes(this.locale)) { |
|
this.weekStartDay = 1; |
|
} else if (this.weekStarts.sun.includes(this.locale)) { |
|
this.weekStartDay = 0; |
|
} else if (this.weekStarts.sat.includes(this.locale)) { |
|
this.weekStartDay = 6; |
|
} else if (this.weekStarts.fri.includes(this.locale)) { |
|
this.weekStartDay = 5; |
|
} else { |
|
this.weekStartDay = 1; |
|
} |
|
} |
|
} |
|
|
|
// class that works with UI/DOM |
|
class CalendarView { |
|
constructor(calendarElement, calendarModel) { |
|
if (!(calendarElement instanceof HTMLElement)) { |
|
throw new TypeError( |
|
`First argument of ${ |
|
this.constructor.name |
|
} should be instance of HTMLElement.` |
|
); |
|
} |
|
|
|
if (!(calendarModel instanceof CalendarModel)) { |
|
throw new TypeError( |
|
`Second argument of ${ |
|
this.constructor.name |
|
} should be instance of CalendarModel` |
|
); |
|
} |
|
|
|
this.calendar = calendarElement; |
|
this.model = calendarModel; |
|
|
|
// init |
|
this.updateUI(); |
|
this.updateTodayDayAfterMidnight(); |
|
|
|
this.selectedDate = new Date(); |
|
} |
|
|
|
updateUI() { |
|
const matrix = this.model.getMonthMatrix(); |
|
|
|
const month = this.createElement("div", { class: "month" }); |
|
|
|
// calendar header |
|
const header = this.createElement("header", { class: "month-header" }); |
|
const h3 = this.createElement( |
|
"h3", |
|
{ class: "month-name" }, |
|
{ textContent: this.model.getCalendarMonthAndYear() }, |
|
header |
|
); |
|
const prev = this.createElement( |
|
"button", |
|
{ class: "prev-month" }, |
|
{ textContent: "➜" }, |
|
header |
|
); |
|
const next = this.createElement( |
|
"button", |
|
{ class: "next-month" }, |
|
{ textContent: "➜" }, |
|
header |
|
); |
|
|
|
month.appendChild(header); |
|
|
|
// calendar days |
|
const days = this.createElement("div", { class: "days" }, null, month); |
|
|
|
const dayClassNames = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]; |
|
for (let week = 0; week < 7; week++) { |
|
const dayRow = this.createElement( |
|
"div", |
|
{ class: dayClassNames[week] }, |
|
null, |
|
days |
|
); |
|
|
|
if (this.model.params.highlightSundays && week === 0) { |
|
dayRow.classList.add("weekend"); |
|
} |
|
|
|
const dayName = this.createElement( |
|
"div", |
|
{ class: "day-name" }, |
|
{ textContent: this.model.dayNames[week] }, |
|
dayRow |
|
); |
|
|
|
for (let day = 0; matrix[day + 1]; day++) { |
|
const newDay = this.createElement("div", null, { |
|
textContent: matrix[week][day] |
|
}); |
|
|
|
newDay.classList.add("day"); |
|
|
|
if ( |
|
(day < 3 && matrix[week][day] > 23) || |
|
(day > 3 && matrix[week][day] < 23) |
|
) { |
|
newDay.classList.add("day-dummy"); |
|
newDay.classList.remove("day"); |
|
} else if ( |
|
this.model.year === this.model.currentDate.getFullYear() && |
|
this.model.month === this.model.currentDate.getMonth() && |
|
matrix[week][day] === this.model.date.getDate() |
|
) { |
|
newDay.classList.add("day-today"); |
|
} |
|
|
|
dayRow.appendChild(newDay); |
|
} |
|
} |
|
|
|
this.calendar.innerHTML = ""; |
|
this.calendar.appendChild(month); |
|
|
|
// switch to prev month |
|
prev.onclick = () => { |
|
this.model.setPrevMonth(); |
|
this.updateUI(); |
|
}; |
|
|
|
// switch to next month |
|
next.onclick = () => { |
|
this.model.setNextMonth(); |
|
this.updateUI(); |
|
}; |
|
|
|
// select day on click |
|
month.onclick = event => { |
|
const classList = event.target.classList; |
|
|
|
if (classList.contains("day")) { |
|
if (!this.selectMultipleDays) { |
|
[...month.querySelectorAll(".day-selected")].forEach(el => |
|
el.classList.remove("day-selected") |
|
); |
|
} |
|
|
|
classList.toggle("day-selected"); |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* Creates HTMLElement |
|
* @param {string} tag name |
|
* @param {object} attributes for tag element |
|
* @param {object} properties for tag element |
|
* @param {HTMLElement} append tag element to provided HTMLElement |
|
* @example this.createElement('div', {class: 'myClassName', id: 'myID'}, {innerHTML: '<b>hello</b>'}, document.body) |
|
*/ |
|
createElement(tag = "div", attributes, properties, append) { |
|
const element = document.createElement(tag); |
|
|
|
if ( |
|
attributes && |
|
typeof attributes === "object" && |
|
Object.keys(attributes).length |
|
) { |
|
for (let attribute in attributes) { |
|
if (attributes.hasOwnProperty(attribute)) { |
|
element.setAttribute(attribute, attributes[attribute]); |
|
} |
|
} |
|
} |
|
|
|
if ( |
|
properties && |
|
typeof properties === "object" && |
|
Object.keys(properties).length |
|
) { |
|
for (let property in properties) { |
|
if (properties.hasOwnProperty(property)) { |
|
element[property] = properties[property]; |
|
} |
|
} |
|
} |
|
|
|
if (append && append instanceof HTMLElement) { |
|
append.appendChild(element); |
|
} |
|
|
|
return element; |
|
} |
|
|
|
/** |
|
* Updates today date after midnight (00:00) to highlight next day |
|
* and set's inteval to update on next days |
|
*/ |
|
updateTodayDayAfterMidnight() { |
|
const today = new Date(); |
|
const tomorrow = new Date( |
|
today.getFullYear(), |
|
today.getMonth(), |
|
today.getDate() + 1, |
|
0, |
|
0, |
|
0 |
|
); |
|
|
|
setTimeout(() => { |
|
this.model.date = new Date(); |
|
this.model.updateDate(); |
|
this.updateUI(); |
|
|
|
const nextDay = 86400000; |
|
setInterval(() => { |
|
this.model.date = new Date(); |
|
this.model.updateDate(); |
|
this.updateUI(); |
|
}, nextDay); |
|
}, tomorrow - Date.now()); |
|
} |
|
|
|
/** |
|
* Returns selected day or today date |
|
*/ |
|
getSelectedDate() { |
|
const selectedDay = this.calendar.querySelector(".day-selected"); |
|
|
|
return selectedDay |
|
? new Date(this.model.currentDate.setDate(selectedDay.textContent)) |
|
: new Date(); |
|
} |
|
} |
|
|
|
// advdanced/additinal calendar features |
|
class AdvdancedCalendarModel extends CalendarModel { |
|
constructor(...props) { |
|
super(...props); |
|
} |
|
|
|
/** |
|
* Check if argument 'locale' is a valid (exists) |
|
* @param {string} locale |
|
* @return {boolean} |
|
*/ |
|
isValidLocale(locale) { |
|
const _ = this.weekStarts; |
|
const locales = [].concat(_.mon, _.sun, _.sat, _.fri); |
|
|
|
return typeof locale === "string" && locales.includes(locale); |
|
} |
|
|
|
/** |
|
* Sets locale to display month name, start week from Monday or Sunday, etc. |
|
* @param {string} locale |
|
*/ |
|
setLocale(locale) { |
|
if (this.isValidLocale(locale)) { |
|
this.params.locale = locale.toLowerCase(); |
|
|
|
this.setWeekStartDay(); |
|
this.setDayNames(); |
|
|
|
this.calendarSettings.setLocale(locale); |
|
} |
|
} |
|
|
|
/** |
|
* Sets year |
|
* You can set any year in past or future (at least in range -200000 to + 200000) |
|
* @param {number} year |
|
*/ |
|
setYear(year) { |
|
if (this.isValidYear(year)) { |
|
const BIG_BANG = -13.8e9; |
|
const SUN_TURN_INTO_A_RED_STAR = 5e9; |
|
|
|
if (year <= BIG_BANG) { |
|
return console.info("There was no time and dates before the Big Bang!"); |
|
} |
|
|
|
if (year >= SUN_TURN_INTO_A_RED_STAR) { |
|
return console.info( |
|
"Our sun became a giant red star. There is no life on planet Earth, so forget about Earth dates..." |
|
); |
|
} |
|
|
|
if (year > 2e5 || year < -2e5) { |
|
// JS can't create/calculate dates years before or after 2e5, lol |
|
return console.info( |
|
"JS can calculate dates only in range of [-200000 - +200000]" |
|
); |
|
} |
|
|
|
this.year = year; |
|
this.date = new Date(year, this.month); |
|
|
|
if (new Date().getFullYear() === year) { |
|
this.date = new Date(); |
|
} else { |
|
// JS has a bug (or feature?) - new Date(0) -> 1970 and new Date(0, 0) -> 1900, so we need to setFullYear |
|
this.date = new Date(this.date.setFullYear(year)); |
|
} |
|
|
|
this.updateDate(); |
|
} |
|
} |
|
} |
|
|
|
// class that works with default/user settings |
|
class CalendarSettings { |
|
constructor(customParams = {}) { |
|
if (typeof customParams !== "object") { |
|
throw new TypeError( |
|
`First argument for ${ |
|
this.constructor.name |
|
} should be object with params.` |
|
); |
|
} |
|
|
|
this.defaults = { |
|
locale: navigator.language, |
|
initialDate: new Date(), |
|
weekdayLength: "short", |
|
highlightSundays: true |
|
}; |
|
// the puruse of defaults is that we can roll back to default settings any time, so don't change it... |
|
Object.freeze(this.defaults); |
|
|
|
this.customParams = customParams; // params passed to constructor |
|
Object.freeze(this.customParams); // don't modify customParams |
|
|
|
// stored in localStorage, and yes - we update it when user change settings via console or UI |
|
this.userSettings = {}; |
|
this.getStorageSettings(); // init user settings - load from local storage |
|
|
|
// merge multile sources of params into single object <- this object params will be used elsewhere |
|
this.params = {}; |
|
this.updateParams(); |
|
} |
|
|
|
/** |
|
* Reads user settings for calendar from local storage and set this.userSettigs |
|
*/ |
|
getStorageSettings() { |
|
if (localStorage) { |
|
const storageString = localStorage.getItem("calendar"); |
|
|
|
this.userSettings = |
|
storageString !== null ? JSON.parse(storageString) : {}; |
|
} |
|
} |
|
|
|
/** |
|
* Writes user settings for calendar from this.userSettings to local storage |
|
*/ |
|
updateStorageSettings() { |
|
if (localStorage && Object.keys(this.userSettings).length) { |
|
if (Object.keys(this.userSettings).length) { |
|
localStorage.setItem("calendar", JSON.stringify(this.userSettings)); |
|
} else { |
|
this.clearStorageSettings(); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Clears user settings from local storage and set userSettings to empty object |
|
*/ |
|
clearStorageSettings() { |
|
if (localStorage) { |
|
localStorage.removeItem("calendar"); |
|
this.userSettings = {}; |
|
} |
|
} |
|
|
|
/** |
|
* Resets this.params to defaults |
|
*/ |
|
resetToDefaults() { |
|
this.clearStorageSettings(); |
|
this.updateParams(); |
|
} |
|
|
|
/** |
|
* Resets this.params to mix of defaults and customParams (ommiting user settings), |
|
* if there were any customParams - othervise reset to defaults |
|
*/ |
|
resetToCustromParams() { |
|
this.clearStorageSettings(); |
|
|
|
if (Object.keys(this.customParams).length) { |
|
this.params = Object.assign({}, this.defaults, this.customParams); |
|
} else { |
|
this.resetToDefaults(); |
|
} |
|
|
|
this.updateParams(); |
|
} |
|
|
|
/** |
|
* Updates this.params by re-merging defaults, custromParams (from constructor), and user settings |
|
*/ |
|
updateParams() { |
|
this.params = Object.assign( |
|
{}, |
|
this.defaults, |
|
this.customParams, |
|
this.userSettings |
|
); |
|
} |
|
|
|
/** |
|
* Updates user settings in class and local storage |
|
* @param {object} object |
|
*/ |
|
updateUserSetting(object) { |
|
if (typeof object === "object") { |
|
this.userSettings = Object.assign({}, this.userSettings, object); |
|
this.updateStorageSettings(); |
|
this.updateParams(); |
|
} |
|
} |
|
|
|
/** |
|
* Stores locale into user settings |
|
* @param {string} locale |
|
*/ |
|
setLocale(locale) { |
|
if (typeof locale === "string") { |
|
this.updateUserSetting({ locale }); |
|
} |
|
} |
|
|
|
/** |
|
* Stores weekdayLength in user settings |
|
* @param {string} weekdayLength |
|
*/ |
|
setWeekDayLength(weekdayLength) { |
|
if ( |
|
typeof weekdayLength === "string" && |
|
["narrow", "short", "long"].includes(weekdayLength) |
|
) { |
|
this.updateUserSetting({ weekdayLength }); |
|
} |
|
} |
|
|
|
/** |
|
* Stores highlightSundays in user settings |
|
* @param {boolean} bool |
|
*/ |
|
setHighlightSundays(bool) { |
|
this.updateUserSetting({ highlightSundays: !!bool }); |
|
} |
|
|
|
/** |
|
* Stores initialDate in user settings |
|
* @param {Date} date |
|
*/ |
|
setInitialDate(date) { |
|
if (date instanceof Date) { |
|
this.updateUserSetting({ initialDate: date }); |
|
} |
|
} |
|
} |
|
|
|
// initialization |
|
const calendarTag = document.getElementById("calendar"); |
|
const calendarSettings = new CalendarSettings(); |
|
const calendarModel = new AdvdancedCalendarModel(calendarSettings); |
|
const myCalendar = new CalendarView(calendarTag, calendarModel); |
|
|
|
/* |
|
View has access to Model, and |
|
Model has access to Settngs, |
|
so we have access to Settings from View |
|
*/ |
|
|
|
class CalendarOptions { |
|
constructor(calendarView, form) { |
|
if (!calendarView instanceof CalendarView) { |
|
throw new ReferenceError( |
|
`First argument for ${ |
|
this.constructor.name |
|
} should be instance of CalendarView.` |
|
); |
|
} |
|
|
|
if (!form instanceof HTMLFormElement) { |
|
throw new ReferenceError( |
|
`First argument for ${ |
|
this.constructor.name |
|
} should be instance of HTMLForm.` |
|
); |
|
} |
|
|
|
this.form = form; |
|
|
|
this.view = calendarView; |
|
this.model = calendarView.model; |
|
this.settings = calendarView.model.calendarSettings; |
|
|
|
this.locale = form.querySelector("#locale"); |
|
this.locale.onchange = () => { |
|
this.settings.setLocale(this.locale.value); |
|
this.model.updateParams(); |
|
this.view.updateUI(); |
|
}; |
|
|
|
this.weekdayLength = form.querySelector("#weekdayLength"); |
|
this.weekdayLength.onchange = () => { |
|
this.settings.setWeekDayLength(this.weekdayLength.value); |
|
this.model.updateParams(); |
|
this.model.setDayNames(); |
|
this.view.updateUI(); |
|
}; |
|
|
|
this.year = form.querySelector("#year"); |
|
this.year.oninput = () => { |
|
const year = +this.year.value; |
|
const newYear = new Date(new Date().setFullYear(year)); |
|
|
|
/** |
|
* sometimes year is set the wrong way, when it sets 1920 instead of 20 |
|
*/ |
|
this.model.setYear(year); |
|
this.settings.setInitialDate(newYear); |
|
this.view.updateUI(); |
|
}; |
|
|
|
this.weekend = form.querySelector("#weekend"); |
|
this.weekend.onchange = () => { |
|
this.settings.setHighlightSundays(this.weekend.checked); |
|
this.model.updateParams(); |
|
|
|
this.view.updateUI(); |
|
}; |
|
|
|
this.reset = form.querySelector("#reset-user-settings"); |
|
this.reset.onclick = () => { |
|
this.settings.resetToDefaults(); |
|
this.view.updateUI(); |
|
}; |
|
|
|
window.onload = () => { |
|
document.title = new Date().toLocaleString(navigator.language, { |
|
year: "numeric", |
|
month: "long", |
|
day: "numeric" |
|
}); |
|
|
|
this.locale.value = this.settings.params.locale; |
|
this.weekdayLength.value = this.settings.params.weekdayLength; |
|
this.year.value = this.model.year; |
|
this.weekend.checked = this.settings.params.highlightSundays; |
|
}; |
|
} |
|
} |
|
|
|
const optionsForm = document.getElementById("calendar-options"); |
|
myCalendarOptions = new CalendarOptions(myCalendar, optionsForm); |