import { FilterUi } from "./FilterUi.js";
import { SliderHeader } from "../../../ui/slider/SliderHeader.js";
import { TimeSlider } from "../../../ui/slider/TimeSlider.js";
import { FilterSettings } from "../FilterSettings.js";
import "./TimeFilter.css";
export { TimeFilter };
/**
* A class that defines the Ui for a Slider-based TimeFilter
*
* @author rhess <robin.hess@awi.de>
* @memberof vef.map.filters.ui
*/
class TimeFilter extends FilterUi {
/**
* @param {HTMLElement | string} target
* @param {object} options filter specific options
* @param {LayerManager} layers Used for included/excluded layers
*/
constructor(target, options, layers) {
// apply default options
options = Object.assign({
title: "Time Filter",
column: "", // automatically identify time column if not defined
schema: "ymd",
allowMultipleSliderMode: true,
activeSliders: "",
operators: [
"-",
"=",
">",
"≥",
"<",
"≤"
],
initialOperator: "-",
initialBegin: "1980-01-01T00:00:00Z",
initialEnd: "2022-12-31T23:59:59Z",
begin: "1980-01-01T00:00:00Z",
end: "2022-12-31T23:59:59Z"
}, options || {});
super(target, options, layers);
this.schemaRegex = new RegExp("^y(m(d)?)?$");
this.sliderContainer_ = null;
this.sliders_ = [];
this.types_ = {
"y": "YEAR",
"m": "MONTH",
"d": "DAY"
};
this.headerConfig = {
// single types
"YEAR": { pattern: '^([0-9]{4})$', maxLength: 4 },
"MONTH": { pattern: '^(1[0-2]|0[1-9])$', maxLength: 2 },
"DAY": { pattern: '^(3[0-1]|2[0-9]|1[0-9]|0[1-9])$', maxLength: 2 },
// schemas
"ymd": { pattern: '^([0-9]{4})(-((1[0-2]|0[1-9])(-(3[0-1]|2[0-9]|1[0-9]|0[1-9])?)?)?)?$', maxLength: 10 },
"ym": { pattern: '^([0-9]{4})(-(1[0-2]|0[1-9])?)?$', maxLength: 7 },
"y": { pattern: '^([0-9]{4})$', maxLength: 4 }
};
this.settingsClass_ = TimeFilterSettings;
this.currentValue_ = null
this.operator_ = options.initialOperator || options.operators[0];
// validate active sliders and schema
let schema = this.options_.schema ? this.options_.schema.toLowerCase() : 'ymd';
let activeSliders = this.options_.activeSliders ? this.options_.activeSliders.toLowerCase() : '';
if (!this.schemaRegex.test(activeSliders)) activeSliders = "";
for (let i = 0; i < activeSliders.length; ++i) {
if (!schema.includes(activeSliders[i])) {
activeSliders = "";
break;
}
}
this.options_.activeSliders = activeSliders;
this.options_.schema = schema;
// check if time needs to be dynamic
this.options_.begin = this.setTimeDynamically(this.options_.begin);
this.options_.end = this.setTimeDynamically(this.options_.end);
this.options_.initialBegin = this.setTimeDynamically(this.options_.initialBegin);
this.options_.initialEnd = this.setTimeDynamically(this.options_.initialEnd);
// initialize element and slider
this.initFilterElement_();
this.initSliders_();
}
/**
* internal method for initializing the main
* HTML structure of the element
*
* @private
*/
initFilterElement_() {
this.setTitle(this.options_.title);
const content = this.getContentContainer();
content.classList.add("time-filter");
this.sliderContainer_ = document.createElement("div");
this.sliderContainer_.classList.add("slider-container");
content.appendChild(this.sliderContainer_);
content.appendChild(this.initMultipleSliderMode_());
}
/**
* @private
* @param {string} type
* @returns {string} validation regex pattern and max length
*/
getHeaderConfig_(type) {
if (type == "DATE") {
let schema = this.options_.schema;
if (!this.schemaRegex.test(schema)) schema = "ymd";
return this.headerConfig[schema];
} else {
return this.headerConfig[type];
}
}
/**
* @private
* @param {string} type
* @param {object} value
* @param {TimeSlider} slider
*/
parseHeaderValue_(type, value, slider) {
let sliderValue = slider.getValue();
let currentValue = sliderValue;
if (slider.options_.handles == 2) {
if (value.type == "begin") {
currentValue = sliderValue.left;
} else {
currentValue = sliderValue.right;
}
}
switch (type.toLowerCase()) {
case "year":
currentValue.setUTCFullYear(Number.parseInt(value.value));
break;
case "month":
currentValue.setUTCDate(1);
currentValue.setUTCMonth(Number.parseInt(value.value) - 1);
break;
case "day":
currentValue.setUTCDate(Number.parseInt(value.value));
break;
default:
const schema = this.options_.schema.toLowerCase();
// initialize day as 1 to prevent month overflow
currentValue.setUTCDate(1);
// the year is always defined
const year = Number.parseInt(value.value.substr(0, 4));
currentValue.setUTCFullYear(year);
// set default month and overwrite if it is set
let month = (value.type == "begin") ? 0 : 11;
if (schema.includes("m") && (value.value.length >= 7)) {
month = Number.parseInt(value.value.substr(5, 2)) - 1;
}
currentValue.setUTCMonth(month);
// set default month and overwrite if it is set
const daysInMonth = new Date(year, month + 1, 0).getDate();
let day = (value.type == "begin") ? 1 : daysInMonth;
if (schema.includes("d") && (value.value.length >= 10)) {
day = Number.parseInt(value.value.substr(8, 2));
if (day > daysInMonth) day = daysInMonth;
}
currentValue.setUTCDate(day);
break;
}
if (slider.options_.handles == 2) {
if (value.type == "begin") {
sliderValue.left = currentValue;
} else {
sliderValue.right = currentValue;
}
} else {
sliderValue = currentValue;
}
slider.setValue(sliderValue);
slider.stop_();
}
/**
* Init a slider header and its events including
* validation and autocompletion of the input
*
* @param {Slider} slider
* @param {string} type
* @param {string[]} operators
* @param {boolean} isLast
* @returns {SliderHeader} header
*/
initHeader_(slider, type, operators, isLast) {
const config = this.getHeaderConfig_(type);
const header = new SliderHeader(null, {
title: type,
operators: operators,
defaultOperator: ((isLast) ? this.operator_ : null),
useInput: true,
inputPattern: config.pattern,
inputLength: config.maxLength
});
const validCharacters = "0123456789-";
const removeInvalidCharacters = (str) => {
for (let i = str.length - 1; i >= 0; --i) {
if (!validCharacters.includes(str[i])) str = str.replace(str[i], "");
}
return str;
}
// validation and auto-complete minus character or end
header.on("input", (e) => {
let value = removeInvalidCharacters(e.value);
switch (type.toLowerCase()) {
case "year":
if (value.includes("-")) value = str.replace("-", "");
maxLength = 4;
break;
case "month":
if (value.includes("-")) value = str.replace("-", "");
maxLength = 2;
break;
case "day":
if (value.includes("-")) value = str.replace("-", "");
maxLength = 2;
break;
default:
//toDo
const schema = this.options_.schema.toLowerCase();
const parts = value.split("-");
if (schema.includes("m")) {
if ((value.length > 4) && ((parts.length == 1) || (parts[0].length > 4))) {
value = value.substring(0, 4) + "-" + value.substring(4);
}
}
if (schema.includes("d")) {
if ((value.length > 7) && (parts.length <= 2) && (value.charAt(7) != "-")) {
value = value.substring(0, 7) + "-" + value.substring(7);
}
}
break;
}
header.setValue(e.type, value);
});
// operator selection
if (isLast) {
header.on("select", operator => {
this.operator_ = operator;
this.initSliders_();
this.fire("change", this);
});
}
// apply the header value to the slider
header.on("change", value => this.parseHeaderValue_(type, value, slider));
// insert before slider
slider.getElement().insertAdjacentElement("beforebegin", header.getElement())
return header;
}
/**
* method for creating a slider with a header
* @param {string} mode
* @param {boolean} isLast
* @returns {object}
* @private
*/
initSingleSlider_(mode, isLast) {
const options = this.options_;
const operators = (isLast) ? options.operators : ["="];
const type = (this.types_[mode]) ? this.types_[mode] : "DATE"
const sliderOptions = {
min: new Date(options.begin).getTime(),
max: new Date(options.end).getTime()
}
const previousLeft = (this.currentValue_ && (this.currentValue_.left instanceof Date)) ? this.currentValue_.left : this.currentValue_;
const previousRight = (this.currentValue_ && (this.currentValue_.right instanceof Date)) ? this.currentValue_.right : null;
if (isLast && ("-" == this.operator_)) {
sliderOptions.handles = 2;
sliderOptions.value = {
left: new Date(previousLeft || options.initialBegin || options.begin).getTime(),
right: new Date(previousRight || options.initialEnd || options.end).getTime()
}
} else {
sliderOptions.handles = 1;
sliderOptions.value = new Date(previousLeft || options.initialBegin || options.begin).getTime();
}
const container = document.createElement("div");
const slider = new TimeSlider(container, sliderOptions);
const header = this.initHeader_(slider, type, operators, isLast);
const dispose = () => {
header.dispose();
slider.dispose();
container.remove();
};
const item = {
type: type,
container: container,
header: header,
slider: slider,
dispose: dispose
};
slider.on("change", (value) => {
this.updateValues_(item);
});
slider.on("stop", (value) => {
this.updateValues_(item);
this.fire("change", this);
});
this.initArrowControls_(item);
return item;
}
initArrowControls_(item) {
const listener = (type, increment, incValue) => {
const values = this.getValueForSchema_(true, item.slider.getValue());
let value = values[type];
let year = Number.parseInt(value.substring(0, 4));
let month = Number.parseInt(value.substring(5, 7));
let day = Number.parseInt(value.substring(8, 10));
switch (item.type.toLowerCase()) {
case "year":
value = year + ((increment) ? 1 : -1);
break;
case "month":
value = month + ((increment) ? 1 : -1);
break;
case "day":
value = day + ((increment) ? 1 : -1);
break;
default:
const schema = this.options_.schema.toLowerCase();
if (schema.includes("d") && incValue === "d") {
day += (increment) ? 1 : -1;
}
if (schema.includes("m") && incValue === "m") {
month += (increment) ? 1 : -1;
}
if (schema.includes("y") && incValue === "y") {
year += (increment) ? 1 : -1;
}
if (schema.includes("ymd") && incValue === "e") {
day = this.options_.end.substring(8, 10);
month = this.options_.end.substring(5, 7);
year = this.options_.end.substring(0, 4);
}
if (schema.includes("ymd") && incValue === "p") {
day = this.options_.begin.substring(8, 10);
month = this.options_.begin.substring(5, 7);
year = this.options_.begin.substring(0, 4);
}
value = new Date(Date.UTC(year, month - 1, day)).toISOString().substring(0, 10) + value.substring(10);
break;
}
this.parseHeaderValue_(item.type, {
type: type,
value: value
}, item.slider);
}
item.header.on("arrow_down", inputName => listener(inputName, false, "d"));
item.header.on("arrow_up", inputName => listener(inputName, true, "d"));
item.header.on("arrow_down_shift", inputName => listener(inputName, false, "m"));
item.header.on("arrow_up_shift", inputName => listener(inputName, true, "m"));
item.header.on("arrow_down_shift_ctrl", inputName => listener(inputName, false, "y"));
item.header.on("arrow_up_shift_ctrl", inputName => listener(inputName, true, "y"));
item.header.on("pos1", inputName => listener(inputName, true, "p"));
item.header.on("end", inputName => listener(inputName, true, "e"));
item.slider.on("arrow_left", handleName => listener((handleName == "left") ? "begin" : "end", false, "d"));
item.slider.on("arrow_right", handleName => listener((handleName == "left") ? "begin" : "end", true, "d"));
item.slider.on("ArrowRight_Shift", handleName => listener((handleName == "left") ? "begin" : "end", true, "m"));
item.slider.on("ArrowLeft_Shift", handleName => listener((handleName == "left") ? "begin" : "end", false, "m"));
item.slider.on("ArrowRight_Shift_cntrl", handleName => listener((handleName == "left") ? "begin" : "end", true, "y"));
item.slider.on("ArrowLeft_Shift_cntrl", handleName => listener((handleName == "left") ? "begin" : "end", false, "y"));
item.slider.on("home", handleName => listener((handleName == "left") ? "begin" : "end", false, "p"));
item.slider.on("end", handleName => listener((handleName == "left") ? "begin" : "end", false, "e"));
}
/**
* method to update the slider and header values
* @param {object} source slider item
* @param {boolean} init sliders are initialized and not updated
* @private
*/
updateValues_(source, init) {
const options = this.options_;
if ((!this.currentValue_) || init) this.currentValue_ = source.slider.getValue();
const totalMin = new Date(options.begin).getTime();
const totalMax = new Date(options.end).getTime();
const newIso = this.getValueForSchema_(true, source.slider.getValue());
const prevIso = this.getValueForSchema_(true);
// transform currentValue depending on slider
for (let i = 0; i < this.sliders_.length; ++i) {
const item = this.sliders_[i];
switch (item.type) {
case "DATE":
this.currentValue_ = source.slider.getValue();
break;
case "YEAR":
{
let beginYear = newIso.begin.substr(0, 4);
let endYear = newIso.end.substr(0, 4);
if (item.slider.options_.handles == 2) {
this.currentValue_ = {
left: new Date(beginYear + "-01-01T00:00:00Z"),
right: new Date(endYear + "-12-31T23:59:59Z")
};
} else {
this.currentValue_ = new Date(beginYear + "-01-01T00:00:00Z");
}
}
break;
case "MONTH":
{
const year = newIso.begin.substr(0, 4);
let beginMonth = newIso.begin.substr(5, 2);
let endMonth = newIso.end.substr(5, 2);
if (item != source) {
beginMonth = prevIso.begin.substr(5, 2);
endMonth = prevIso.end.substr(5, 2);
}
const daysInEndMonth = new Date(year, endMonth, 0).getDate();
if (item.slider.options_.handles == 2) {
this.currentValue_ = {
left: new Date(year + "-" + beginMonth + "-01T00:00:00Z"),
right: new Date(year + "-" + endMonth + "-" + daysInEndMonth + "T23:59:59Z")
};
} else {
this.currentValue_ = new Date(year + "-" + beginMonth + "-01T00:00:00Z");
}
}
break;
case "DAY":
{
const year = newIso.begin.substr(0, 4);
let month = newIso.begin.substr(5, 2);
let beginDay = newIso.begin.substr(8, 2);
let endDay = newIso.end.substr(8, 2);
if (item != source) {
beginDay = prevIso.begin.substr(8, 2);
endDay = prevIso.end.substr(8, 2);
if (source.type != "MONTH") month = prevIso.begin.substr(5, 2);
}
const daysInMonth = new Date(year, month, 0).getDate();
if (parseInt(beginDay) > daysInMonth) beginDay = daysInMonth;
if (parseInt(endDay) > daysInMonth) endDay = daysInMonth;
if (item.slider.options_.handles == 2) {
this.currentValue_ = {
left: new Date(year + "-" + month + "-" + beginDay + "T00:00:00Z"),
right: new Date(year + "-" + month + "-" + endDay + "T23:59:59Z")
}
} else {
this.currentValue_ = new Date(year + "-" + month + "-" + beginDay + "T00:00:00Z");
}
}
break;
}
}
if (this.currentValue_ instanceof Date) {
if (this.currentValue_ < totalMin) this.currentValue_ = totalMin;
if (this.currentValue_ > totalMax) this.currentValue_ = totalMax;
} else {
if (this.currentValue_.left < totalMin) this.currentValue_.left = totalMin;
if (this.currentValue_.left > totalMax) this.currentValue_.left = totalMax;
if (this.currentValue_.right < totalMin) this.currentValue_.right = totalMin;
if (this.currentValue_.right > totalMax) this.currentValue_.right = totalMax;
}
const isoValue = this.getValueForSchema_(true);
const daysInMonth = new Date(isoValue.begin.substr(0, 4), isoValue.begin.substr(5, 2), 0).getDate();
// update all values in headers and sliders
for (let i = 0; i < this.sliders_.length; ++i) {
const item = this.sliders_[i];
let min = totalMin
let max = totalMax
switch (item.type) {
case "DATE":
item.header.setValues(this.getValueForSchema_());
break;
case "YEAR":
item.header.setValues({
begin: isoValue.begin.substr(0, 4),
end: isoValue.end.substr(0, 4)
});
break;
case "MONTH":
min = new Date(isoValue.begin.substr(0, 5) + "01-01T00:00:00Z").getTime();
max = new Date(isoValue.begin.substr(0, 5) + "12-31T23:59:59Z").getTime();
item.header.setValues({
begin: isoValue.begin.substr(5, 2),
end: isoValue.end.substr(5, 2)
});
break;
case "DAY":
min = new Date(isoValue.begin.substr(0, 8) + "01T00:00:00Z").getTime();
max = new Date(isoValue.begin.substr(0, 8) + daysInMonth + "T23:59:59Z").getTime();
item.header.setValues({
begin: isoValue.begin.substr(8, 2),
end: isoValue.end.substr(8, 2)
});
break;
}
item.slider.options_.min = (min >= totalMin) ? min : totalMin;
item.slider.options_.max = (max <= totalMax) ? max : totalMax;
if (init || (item != source)) {
if (item.slider.options_.handles == 2) {
item.slider.options_.value = {
left: new Date(isoValue.begin).getTime(),
right: new Date(isoValue.end).getTime(),
};
} else {
item.slider.options_.value = new Date(isoValue.begin).getTime();
}
item.slider.validate_();
item.slider.updateHandles_();
}
}
}
/**
* internal method for initializing the date slider.
* the slider gets re-initialized everytime the
* operator changes.
*
* @private
*/
initSliders_() {
// remove all sliders
for (let i in this.sliders_) this.sliders_[i].dispose();
this.sliders_ = [];
// adjust operator
if (!this.options_.operators.includes(this.operator_)) {
if (this.options_.operators.length == 0) this.options_.operators = ["-"];
this.operator_ = this.options_.operators[0];
}
const activeSliders = this.options_.activeSliders;
if (activeSliders.length == 0) {
const item = this.initSingleSlider_(null, true);
this.sliderContainer_.appendChild(item.container);
this.sliders_.push(item);
} else {
for (let i = 0; i < activeSliders.length; ++i) {
const isLast = (activeSliders.length == (i + 1));
const item = this.initSingleSlider_(activeSliders[i], isLast);
this.sliderContainer_.appendChild(item.container);
this.sliders_.push(item);
}
}
this.updateValues_(this.sliders_[this.sliders_.length - 1], true);
}
sortSchema_(input) {
input = input.toLowerCase();
let output = "";
for (let i in this.types_) {
if (input.includes(i)) {
output += i;
}
}
return output;
}
initMultipleSliderMode_() {
const advanced = document.createElement("div");
advanced.classList.add("advanced");
advanced.style.display = "none";
advanced.innerHTML = `
<button class="btn-close"><i class='fas fa-times'></i></button>
<div class="settings"></div>
`;
const settingsContainer = advanced.querySelector(".settings");
// toggle advanced settings
this.settingsOpen_ = false;
const toggleSettings = () => {
const tool = this.getElement().querySelector(".sidebar-element-tool.multiple-slider-mode");
if (this.settingsOpen_) {
advanced.style.display = "none";
tool.classList.remove("enabled");
} else {
advanced.style.display = "flex";
tool.classList.add("enabled")
}
this.settingsOpen_ = !this.settingsOpen_;
}
const closeButton = advanced.querySelector(".btn-close");
closeButton.addEventListener("click", toggleSettings);
this.addTool("vef vef-filter-settings multiple-slider-mode", toggleSettings, "Advanced Settings", true);
const schema = this.sortSchema_(this.options_.schema);
const activeSliders = this.sortSchema_(this.options_.activeSliders);
for (let i = 0; i < schema.length; ++i) {
if (schema[i] in this.types_) {
const settingsItem = document.createElement("div");
settingsItem.classList.add("settings-item");
settingsItem.dataset.type = schema[i];
settingsItem.innerHTML = `
<span class="fa-stack">
<i class="far fa-circle fa-stack-1x"></i>
<i class="fas fa-check fa-stack-1x"></i>
</span>
${this.types_[schema[i]].toLowerCase()}
`;
settingsContainer.appendChild(settingsItem);
}
}
const items = settingsContainer.querySelectorAll(".settings-item");
for (let i = 0; i < items.length; ++i) {
const item = items[i];
const prev = item.previousSibling;
if (activeSliders.includes(schema[i]) && (!prev || prev.classList.contains("checked"))) {
item.classList.add("checked");
}
item.addEventListener("click", () => {
if (item.classList.contains("checked")) {
item.classList.remove("checked");
let next = item.nextSibling;
while (next) {
next.classList.remove("checked");
next = next.nextSibling;
}
} else {
item.classList.add("checked");
let prev = item.previousSibling;
while (prev) {
prev.classList.add("checked");
prev = prev.previousSibling;
}
}
let selectedSliders = "";
for (let j = 0; j < items.length; ++j) {
if (items[j].classList.contains("checked")) selectedSliders += items[j].dataset.type;
}
this.options_.activeSliders = this.sortSchema_(selectedSliders);
this.initSliders_();
this.fire("change", this);
});
}
if (!this.options_.allowMultipleSliderMode) this.toggleToolVisibility("multiple-slider-mode", false);
return advanced;
}
/**
* internal method for getting the selected date values
* adjusted to the time resolution defined in options.schema
*
* @param {boolean} iso returns full iso-datestring if true
* @param {object | number} value (optional)
* @returns {object} {begin, end}
*/
getValueForSchema_(iso, value) {
value = value || this.currentValue_;
let begin = ((isFinite(value.left)) ? value.left : value).toISOString();
let end = ((isFinite(value.right)) ? value.right : value).toISOString();
let schema = this.options_.schema.toLowerCase();
let activeSliders = this.options_.activeSliders.toLowerCase();
if (!this.schemaRegex.test(schema)) schema = "ymd";
if (!this.schemaRegex.test(activeSliders)) activeSliders = "";
// "Y" -> Year is always required
let validBegin = begin.substr(0, 4);
let validEnd = end.substr(0, 4);
if (schema.includes("m") && ((activeSliders.length == 0) || activeSliders.includes("m"))) {
validBegin = begin.substr(0, 7);
validEnd = end.substr(0, 7);
if (schema.includes("d") && ((activeSliders.length == 0) || activeSliders.includes("d"))) {
validBegin = begin.substr(0, 10);
validEnd = end.substr(0, 10);
if (iso) {
validBegin += "T00:00:00";
validEnd += "T23:59:59";
}
} else if (iso) {
const daysInEndMonth = new Date(end.substr(0, 4), end.substr(5, 2), 0).getDate();
validEnd += "-" + daysInEndMonth + "T23:59:59";
validBegin += "-01T00:00:00";
}
} else if (iso) {
validBegin += "-01-01T00:00:00";
validEnd += "-12-31T23:59:59";
}
return {
begin: validBegin,
end: validEnd
}
}
/**
* Get the filter object to pass it on to the Layers.
*
* @override
* @returns {object} filter object
*/
getActiveFilter() {
let val = this.getValueForSchema_(true);
// case "bt" and "eq" and as fallback for invalid operators
let values = [["bt", val.begin, val.end]];
switch (this.operator_) {
case "gt":
case ">":
values = [["gt", val.begin]];
break;
case "gteq":
case "≥":
values = [["gteq", val.begin]];
break;
case "lt":
case "<":
values = [["lt", val.begin]];
break;
case "lteq":
case "≤":
values = [["lteq", val.begin]];
break;
}
const result = {
type: "time",
column: this.options_.column || "",
values: values,
excludedLayers: this.getExcludedLayers()
};
return result;
}
/**
* Get the filter's options-object
*/
getOptions() {
const activeFilterValues = this.getActiveFilter().values[0];
const operator = this.operator_;
const options = Object.assign({}, this.options_);
options.initialOperator = operator;
// assign active values
if (operator == "-") {
options.initialBegin = activeFilterValues[1];
options.initialEnd = activeFilterValues[2];
} else {
options.initialBegin = activeFilterValues[1];
options.initialEnd = "";
}
return options;
}
/**
* method to reload options after applying settings
*/
reloadOptions_() {
this.setTitle(this.options_.title);
this.toggleToolVisibility("btn-power", this.options_.deactivatable);
this.toggleToolVisibility("multiple-slider-mode", this.options_.allowMultipleSliderMode);
this.initSliders_();
this.fire("change", this);
}
setTimeDynamically(time) {
let access = false;
let notTime = ["today"];
notTime.forEach(element => {
if ((typeof time == "string") && time.includes(element)) access = true;
});
// return time if it is not using the today-syntax and alway assume UTC
if (!access) {
if(time && !time.endsWith("Z") && !time.endsWith("z")) time += "Z";
return time
};
if (time.includes("today")) {
let newDate = new Date();
time = time.replace("today", "");
if (time == "") return newDate.toISOString();
let addOrsub = time[0];
time = time.replace(addOrsub, "")
if (time == "") return newDate.toISOString();
let typeOftime = time[0];
time = time.replace(typeOftime, "")
if (time == "") return newDate.toISOString();
let SkipValue = Number(time);
if (addOrsub === "+") {
switch (typeOftime.toLowerCase()) {
case "d":
newDate.setUTCDate(newDate.getUTCDate() + SkipValue);
break;
case "m":
newDate.setUTCMonth(newDate.getUTCMonth() + SkipValue);
break;
case "y":
newDate.setUTCFullYear(newDate.getUTCFullYear() + SkipValue);
break;
}
} else if (addOrsub === "-") {
switch (typeOftime.toLowerCase()) {
case "d":
newDate.setUTCDate(newDate.getUTCDate() - SkipValue);
break;
case "m":
newDate.setUTCMonth(newDate.getUTCMonth() - SkipValue);
break;
case "y":
newDate.setUTCFullYear(newDate.getUTCFullYear() - SkipValue);
break;
}
}
return newDate.toISOString();
}
return time;
}
}
/**
* Class representing the settings for the TimeFilter
* @private
*/
class TimeFilterSettings extends FilterSettings {
htmlExtension = `
<div><label style="width: 100%;"><span class="input-label" style="width: 80px;">Column: </span><input style="width: calc(100% - 80px);" type="text" name="column" placeholder="Let empty to auto-choose a column for each layer"/></label></div>
<div><label style="width: 100%;"><span class="input-label" style="width: 80px;">Schema: </span><select style="width: calc(100% - 80px);" name="schema">
<option value="y">YYYY</option>
<option value="ym">YYYY-MM</option>
<option value="ymd">YYYY-MM-DD</option>
</select></label></div>
<div><label style="width: 100%;"><span class="input-label" style="width: 80px;">Minimum: </span><input style="width: calc(100% - 80px);" type="text" name="minimum"/></label></div>
<div><label style="width: 100%;"><span class="input-label" style="width: 80px;">Maximum: </span><input style="width: calc(100% - 80px);" type="text" name="maximum"/></label></div>
<div>
<label class="input-label" style="width: 80px;">Operators:</label>
<label title="equals" style="margin-right:20px;"><input type="checkbox" name="operator-equals"/> =</label>
<label title="range" style="margin-right:20px;"><input type="checkbox" name="operator-range"/> -</label>
<label title="greater than" style="margin-right:20px;"><input type="checkbox" name="operator-gt"/> ></label>
<label title="greater thanor equal to" style="margin-right:20px;"><input type="checkbox" name="operator-gteq"/> ≥</label>
<label title="less than"><input type="checkbox" name="operator-lt"/> <</label>
<label title="less than or equal to"><input type="checkbox" name="operator-lteq"/> ≤</label>
</div>
<div><label><input type="checkbox" name="allow-multiple-slider-mode"/> Show "Multiple Slider Mode" Button</label></div>
`;
/**
* @param {FilterUi} filter
*/
constructor(filter) {
super(filter);
this.query("form").insertAdjacentHTML("beforeend", this.htmlExtension);
}
/**
* Get the currently configured filter options
*/
getFilterOptions() {
const options = super.getFilterOptions();
const minimum = this.query("form input[name='minimum']");
const maximum = this.query("form input[name='maximum']");
if (!minimum.checkValidity() || !maximum.checkValidity()) return null;
options.begin = minimum.value + "T00:00:00Z";
options.end = maximum.value + "T23:59:59Z"
options.column = this.query("form input[name='column']").value;
options.schema = this.query("form select[name='schema']").value;
options.allowMultipleSliderMode = this.query("form input[name='allow-multiple-slider-mode']").checked;
const operators = [];
if (this.query("form input[name='operator-equals']").checked) operators.push("=");
if (this.query("form input[name='operator-range']").checked) operators.push("-");
if (this.query("form input[name='operator-gt']").checked) operators.push(">");
if (this.query("form input[name='operator-gteq']").checked) operators.push("≥");
if (this.query("form input[name='operator-lt']").checked) operators.push("<");
if (this.query("form input[name='operator-lteq']").checked) operators.push("≤");
options.operators = operators;
return options;
}
/**
* Update Ui based on the filters current options
*/
updateFilterOptions() {
super.updateFilterOptions();
const options = this.filter.options_;
this.query("form input[name='column']").value = options.column;
this.query("form input[name='allow-multiple-slider-mode']").checked = options.allowMultipleSliderMode;
this.query("form select[name='schema']").value = options.schema;
this.query("form input[name='minimum']").value = (options.begin.length > 9) ? options.begin.substring(0, 10) : options.end;
this.query("form input[name='maximum']").value = (options.end.length > 9) ? options.end.substring(0, 10) : options.end;
this.query("form input[name='operator-equals']").checked = options.operators.includes("=");
this.query("form input[name='operator-range']").checked = options.operators.includes("-");
this.query("form input[name='operator-gt']").checked = options.operators.includes(">");
this.query("form input[name='operator-gteq']").checked = options.operators.includes("≥");
this.query("form input[name='operator-lt']").checked = options.operators.includes("<");
this.query("form input[name='operator-lteq']").checked = options.operators.includes("≤");
}
}