/**
* Refer to <span color = "yellow">{@link ve.Component}</span> for methods or fields inherited from this Component's parent such as `.options.attributes` or `.element`.
*
* Date component used for selecting historical dates over the long run. The value stored is an object with the structure { year: {@link number}, month: {@link number}, day: {@link number}, hour: {@link number}, minute: {@link number} }, with negative years representing BC. This data structure is otherwise known as a {@link UF.Date}.
* - Functional binding: <span color=00ffff>veDate</span>().
*
* ##### Constructor:
* - `arg0_value`: {@link UF.Date}
* - `arg1_options`: {@link Object}
* - `.disabled=false`: {@link boolean}
*
* ##### Instance:
* - `.v`: {@link UF.Date}
*
* ##### Methods:
* - <span color=00ffff>{@link ve.Date.handleEvents|handleEvents}</span>() - Used internally to handle events for all date inputs.
*
* @augments ve.Component
* @augments {@link ve.Component}
* @memberof ve.Component
* @type {ve.Date}
*/
ve.Date = class extends ve.Component {
static demo_value = Date.getCurrentDate();
constructor (arg0_value, arg1_options) {
//Convert from parameters
let value = (arg0_value) ? arg0_value : Date.getCurrentDate();
let options = (arg1_options) ? arg1_options : {};
super(options);
//Initialise options
options.attributes = (options.attributes) ? options.attributes : {};
//Declare local instance variables
let attributes = {
readonly: options.disabled,
...options.attributes
};
this.element = document.createElement("div");
this.element.setAttribute("component", "ve-date");
this.element.instance = this;
HTML.applyTelestyle(this.element, options.style);
this.options = options;
this.value = Date.convertTimestampToDate(value);
//Format html_string
let attributes_string = HTML.objectToAttributes(attributes);
let html_string = [];
html_string.push(`<span id = "name"></span> `);
html_string.push(`<input id = "day" class = "day-input" placeholder = "DD" size = "4"${attributes_string}>`);
html_string.push(`<input id = "month" class = "month-input" list = "months" placeholder = "Month"${attributes_string}>`);
html_string.push(`<datalist id = "months">`);
Object.iterate(Date.months, (local_key, local_value) => {
html_string.push(`<option value = "${local_value.name}">${local_value.month + 1}</option>`);
});
html_string.push(`</datalist>`);
html_string.push(`<input id = "year" class = "year-input" placeholder = "YYYY"${attributes_string}>`);
html_string.push(`<span id = "year-type">AD</span>`);
html_string.push(`<input id = "hour" value = "00" placeholder = "HH" size = "2"${attributes_string}>:`);
html_string.push(`<input id = "minute" value = "00" placeholder = "MM" size = "2"${attributes_string}>`);
this.element.innerHTML = html_string.join("");
//Handle inputs
this.handleEvents();
this.name = options.name;
}
/**
* Returns the date contained in the present component.
* - Accessor of: {@link ve.Date}
*
* @returns {{year: number, month: number, day: number, hour: number, minute: number}}
*/
get v () {
//Determine if #year-type needs a flip for this.value
if (this.element.querySelector(`#year-type`).innerHTML === "BC")
if (this.value.year > 0) this.value.year *= -1;
//Return statement
return Date.convertTimestampToDate(this.value);
}
/**
* Sets the date contained in the present component.
* - Accessor of: {@link ve.Date}
*
* @param {{year: number, month: number, day: number, hour: number, minute: number}} arg0_value
*/
set v (arg0_value) {
//Convert from parameters
let value = (arg0_value) ? Date.convertTimestampToDate(arg0_value) : Date.getCurrentDate();
//Declare local instance variables
let year_type_el = this.element.querySelector(`#year-type`);
//Set #year-type first
if (value.year < 0) {
year_type_el.innerHTML = "BC";
value.year = Math.abs(value.year);
} else {
year_type_el.innerHTML = "AD";
}
//Set year, month, day, hour, minute
this.element.querySelector("#year").value = value.year;
this.element.querySelector("#month").value = Date.months[Date.all_months[value.month - 1]].name;
this.element.querySelector("#day").value = value.day;
this.element.querySelector(`#hour`).value = value.hour.toString().padStart(2, "0");
this.element.querySelector(`#minute`).value = value.minute.toString().padStart(2, "0");
//Set value
this.value = value;
this.fireFromBinding();
}
/**
* Handles input events for {@link ve.Date}. Local helper function, since inputs need to be constrained to valid dates to remain fluid.
* - Method of: {@link ve.Date}
*/
handleEvents () {
this.element.querySelector(`#year`).addEventListener("change", (e) => {
let actual_value = parseInt(e.target.value);
let year_type_el = this.element.querySelector(`#year-type`);
if (!isNaN(actual_value)) {
if (actual_value < 0) {
actual_value = Math.abs(actual_value);
year_type_el.innerHTML = "BC";
} else if (actual_value === 0) {
actual_value = 1;
year_type_el.innerHTML = "AD";
} else {
year_type_el.innerHTML = "AD";
}
e.target.value = actual_value;
this.value.year = actual_value;
this.fireToBinding();
}
});
this.element.querySelector(`#month`).addEventListener("change", (e) => {
let actual_month = -1;
if (!isNaN(parseInt(e.target.value))) {
let local_month = Date.months[Date.all_months[parseInt(e.target.value)]];
if (local_month)
actual_month = local_month.month;
} else {
actual_month = Date.getMonth(e.target.value);
}
e.target.value = Date.months[Date.all_months[actual_month - 1]].name;
this.value.month = actual_month;
this.fireToBinding();
});
this.element.querySelector(`#day`).addEventListener("change", (e) => {
let actual_value = parseInt(e.target.value);
let current_month = Date.getMonth(this.element.querySelector(`#month`).value);
let current_year = parseInt(this.element.querySelector(`#year`).value);
if (!isNaN(actual_value) && current_month) {
//Get days_in_month
let days_in_month = current_month.days;
if (Date.isLeapYear(current_year) && current_month.leap_year_days)
days_in_month = current_month.leap_year_days;
if (actual_value < 1) actual_value = 1;
if (actual_value > days_in_month) actual_value = days_in_month;
} else {
actual_value = 1;
}
e.target.value = actual_value;
this.value.day = actual_value;
this.fireToBinding();
});
this.element.querySelector(`#hour`).addEventListener("change", (e) => {
let actual_value = parseInt(e.target.value);
if (!isNaN(actual_value)) {
if (actual_value > 23) actual_value = 23;
if (actual_value < 0) actual_value = 0;
} else {
actual_value = 0;
}
e.target.value = actual_value.toString().padStart(2, "0");
this.value.hour = actual_value;
this.fireToBinding();
});
this.element.querySelector(`#minute`).addEventListener("change", (e) => {
let actual_value = parseInt(e.target.value);
if (!isNaN(actual_value)) {
if (actual_value > 59) actual_value = 59;
if (actual_value < 0) actual_value = 0;
} else {
actual_value = 0;
}
e.target.value = actual_value.toString().padStart(2, "0");
this.value.minute = actual_value;
this.fireToBinding();
});
}
};
//Functional binding
/**
* @returns {ve.Date}
*/
veDate = function () {
//Return statement
return new ve.Date(...arguments);
};