2023-09-30 22:45:55 +00:00
|
|
|
/**
|
|
|
|
* BBTimeline
|
2023-10-18 13:44:49 +00:00
|
|
|
* @version v0.1.1.121 beta (2023/10/18 2058)
|
2023-09-30 22:45:55 +00:00
|
|
|
*/
|
|
|
|
class BBTimeline {
|
|
|
|
constructor(el) {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
a.Container = document.getElementById(el);
|
|
|
|
a.Padding = {
|
|
|
|
Left: 20,
|
|
|
|
Top: 20,
|
|
|
|
Right: 20,
|
2023-10-22 21:14:12 +00:00
|
|
|
Bottom: 20
|
2023-09-30 22:45:55 +00:00
|
|
|
};
|
|
|
|
a.Size = {
|
2023-10-03 07:01:09 +00:00
|
|
|
W: a.Container.innerWidth || a.Container.clientWidth,
|
|
|
|
H: a.Container.innerHeight || a.Container.clientHeight
|
2023-09-30 22:45:55 +00:00
|
|
|
};
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Layer = {
|
|
|
|
Background: null,
|
|
|
|
Flourish: null,
|
|
|
|
Markers: null
|
|
|
|
};
|
2023-10-03 07:01:09 +00:00
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
a.DateParsePattern = "yyyy-MM-dd";
|
2023-10-03 07:01:09 +00:00
|
|
|
|
2023-09-30 22:45:55 +00:00
|
|
|
a.Axis = {
|
2023-10-22 21:14:12 +00:00
|
|
|
LineColour1: "#000000",
|
2023-09-30 22:45:55 +00:00
|
|
|
LineWidth: 1,
|
|
|
|
Font: "8pt Arial",
|
|
|
|
LabelColour: "#000000",
|
2023-10-22 21:14:12 +00:00
|
|
|
LabelSpacing: 6
|
|
|
|
};
|
|
|
|
a.XAxis = {
|
|
|
|
NoPartPerDay: 4,
|
|
|
|
HourLineSpace: 6,
|
|
|
|
HourLineHeight: 10,
|
|
|
|
HourLineColour: "#EAEAEA",
|
|
|
|
DayLineHeight: 20,
|
|
|
|
DayLineColour: "#9E9E9E",
|
|
|
|
Position: 'top'
|
2023-09-30 22:45:55 +00:00
|
|
|
};
|
2023-10-18 13:44:49 +00:00
|
|
|
|
2023-09-30 22:45:55 +00:00
|
|
|
a.Marker = {
|
|
|
|
BorderColour: "#3A5D9C",
|
|
|
|
BorderWidth: 2,
|
|
|
|
BackColour: "#D4DEEF",
|
2023-10-22 21:14:12 +00:00
|
|
|
Width: 10
|
|
|
|
};
|
|
|
|
a.MarkerLabel = {
|
|
|
|
Colour: "#3A5D9C",
|
2023-10-02 22:42:08 +00:00
|
|
|
Font: "9pt Arial",
|
2023-10-22 21:14:12 +00:00
|
|
|
Margin: 8,
|
2023-10-19 23:22:29 +00:00
|
|
|
Line: {
|
|
|
|
Colour: "#A6A6A6",
|
|
|
|
Width: 1,
|
|
|
|
}
|
2023-09-30 22:45:55 +00:00
|
|
|
};
|
2023-10-18 13:44:49 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
a.HotTrack = {
|
2023-10-22 21:14:12 +00:00
|
|
|
Colour: "#F57C00",
|
|
|
|
Width: 3
|
2023-09-30 22:45:55 +00:00
|
|
|
};
|
2023-10-18 13:44:49 +00:00
|
|
|
|
2023-10-01 02:37:24 +00:00
|
|
|
a.Events = [];
|
2023-10-19 23:22:29 +00:00
|
|
|
a.StartDate = a.DateToInternalString(new Date());
|
2023-10-02 22:42:08 +00:00
|
|
|
a.ShowDate = a.StartDate;
|
2023-10-22 21:14:12 +00:00
|
|
|
a.ShowMarkerLabel = true;
|
2023-10-18 13:44:49 +00:00
|
|
|
|
2023-10-01 02:37:24 +00:00
|
|
|
a.Enabled = false;
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Debug = false;
|
2023-10-19 23:22:29 +00:00
|
|
|
a.EnableHotTracking = true;
|
2023-10-01 02:37:24 +00:00
|
|
|
|
|
|
|
a.initialiseComponents();
|
2023-09-30 22:45:55 +00:00
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
|
|
|
|
get CTX() {
|
|
|
|
return a.Layer.Markers.CTX;
|
|
|
|
}
|
|
|
|
|
|
|
|
get NewEvent() {
|
2023-09-30 22:45:55 +00:00
|
|
|
const a = this;
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
return {
|
|
|
|
Date: "",
|
|
|
|
Label: "",
|
|
|
|
Position: { X: 0, Y: 0 },
|
|
|
|
Events: [],
|
|
|
|
HitBox: null,
|
|
|
|
BorderColour: a.Marker.BorderColour,
|
|
|
|
BackColour: a.Marker.BackColour
|
|
|
|
};
|
|
|
|
}
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
get NewEventItem() {
|
|
|
|
return {
|
|
|
|
Title: "",
|
|
|
|
Description: "",
|
|
|
|
Link: "",
|
|
|
|
Tag: null
|
|
|
|
};
|
|
|
|
}
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
get VisibleDays() {
|
|
|
|
const a = this;
|
|
|
|
const clientWidth = (a.Size.W - (a.Padding.Left + a.Padding.Right));
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-22 21:14:12 +00:00
|
|
|
return Math.floor(clientWidth / (a.XAxis.NoPartPerDay * a.XAxis.HourLineSpace));
|
2023-10-19 23:22:29 +00:00
|
|
|
}
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
get VisibleStartDate() {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
return a.ConvertToDate(a.ShowDate);
|
2023-09-30 22:45:55 +00:00
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
get VisibleEndDate() {
|
2023-10-03 07:01:09 +00:00
|
|
|
const a = this;
|
|
|
|
|
|
|
|
let date = a.ConvertToDate(a.ShowDate);
|
2023-10-19 23:22:29 +00:00
|
|
|
date.setDate(date.getDate() + a.VisibleDays);
|
2023-10-03 07:01:09 +00:00
|
|
|
|
|
|
|
// Minus one for lead up
|
|
|
|
date.setDate(date.getDate() - 1);
|
|
|
|
|
|
|
|
return a.DateToString(date, a.DateParsePattern);
|
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
get VisibleEvents() {
|
|
|
|
const a = this;
|
|
|
|
let result = [];
|
|
|
|
|
|
|
|
a.Layer.Background.XAxisPositions.forEach(function (e) {
|
|
|
|
const event = a.FindEvent(e.Date);
|
|
|
|
if (event == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set offsetX on current view
|
|
|
|
event.Position.X = e.X;
|
|
|
|
|
|
|
|
result.push(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AddEvent(date, label, options) {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
const _options = Object.assign(a.NewEventItem, options);
|
|
|
|
|
|
|
|
let event = a.FindEvent(date);
|
|
|
|
if (event == null) {
|
|
|
|
let newEvent = a.NewEvent;
|
|
|
|
newEvent.Date = date;
|
|
|
|
|
|
|
|
a.Events.push(newEvent);
|
|
|
|
|
|
|
|
event = a.FindEvent(date);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (label != null) {
|
|
|
|
event.Label = label;
|
|
|
|
}
|
|
|
|
|
|
|
|
event.Events.push(_options);
|
|
|
|
}
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
Clear() {
|
2023-09-30 22:45:55 +00:00
|
|
|
const a = this;
|
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Layer.Background.Clear();
|
|
|
|
a.Layer.Flourish.Clear();
|
|
|
|
a.Layer.Markers.Clear();
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
a.StartDate = a.DateToInternalString(new Date());
|
2023-10-02 22:42:08 +00:00
|
|
|
a.ShowDate = a.StartDate;
|
|
|
|
a.Enabled = false;
|
|
|
|
a.Events = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
DeleteMarker(date)
|
|
|
|
{
|
|
|
|
const a = this;
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
for (let i=0; i<a.Events.length; i++) {
|
|
|
|
if (a.Events[i].Date != date) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
a.Events.splice(i, 1);
|
2023-09-30 22:45:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
FindDatePosition(date) {
|
2023-09-30 22:45:55 +00:00
|
|
|
const a = this;
|
2023-10-19 23:22:29 +00:00
|
|
|
const points = a.Layer.Background.XAxisPositions;
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-01 02:37:24 +00:00
|
|
|
for (let i=0; i<points.length; i++) {
|
|
|
|
if (points[i].Date == date){
|
|
|
|
return points[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
FindEvent(date) {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
for (let i=0; i<a.Events.length; i++) {
|
|
|
|
if (a.Events[i].Date == date) {
|
|
|
|
return a.Events[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
FindEventsByCoords(x, y) {
|
2023-10-01 02:37:24 +00:00
|
|
|
const a = this;
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-01 02:37:24 +00:00
|
|
|
for (let i=0; i<a.Events.length; i++) {
|
2023-10-02 22:42:08 +00:00
|
|
|
const e = a.Events[i].HitBox;
|
|
|
|
if (a.Events[i].HitBox == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-03 17:17:55 +00:00
|
|
|
const x2 = (e.X + e.W);
|
|
|
|
const y2 = (e.Y + e.H);
|
|
|
|
|
|
|
|
if ((x >= e.X) && (x <= x2) && (y >= e.Y) && (y <= y2)){
|
2023-10-01 02:37:24 +00:00
|
|
|
return a.Events[i];
|
2023-09-30 22:45:55 +00:00
|
|
|
}
|
2023-10-01 02:37:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
Load(startDate) {
|
2023-10-01 02:37:24 +00:00
|
|
|
const a = this;
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
a.StartDate = startDate;
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
a.Show(startDate);
|
2023-10-01 02:37:24 +00:00
|
|
|
}
|
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
Show(date) {
|
|
|
|
const a = this;
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-03 07:01:09 +00:00
|
|
|
if (a.ConvertToDate(date) < a.ConvertToDate(a.StartDate)) {
|
2023-10-02 22:42:08 +00:00
|
|
|
date = a.StartDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
a.ShowDate = date;
|
|
|
|
a.Enabled = true;
|
|
|
|
|
|
|
|
a.Invalidate(true, true);
|
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
ShowNext() {
|
|
|
|
const a = this;
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
let date = a.VisibleStartDate;
|
|
|
|
date.setDate(date.getDate() + (a.VisibleDays - 1));
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
a.Show(a.DateToInternalString(date));
|
|
|
|
}
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
ShowPrevious() {
|
|
|
|
const a = this;
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
let date = a.VisibleStartDate;
|
|
|
|
date.setDate(date.getDate() - (a.VisibleDays - 1));
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
a.Show(a.DateToInternalString(date));
|
|
|
|
}
|
2023-10-02 22:42:08 +00:00
|
|
|
|
|
|
|
UpdateLabel(date, label) {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
let event = a.FindEvent(date);
|
|
|
|
if (event == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
event.Label = label;
|
|
|
|
|
|
|
|
a.Invalidate(false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateMarker(date, borderColour, backColour) {
|
|
|
|
const a = this;
|
|
|
|
|
|
|
|
let event = a.FindEvent(date);
|
|
|
|
if (event == null) {
|
|
|
|
return;
|
|
|
|
}
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
event.BorderColour = borderColour;
|
|
|
|
event.BackColour = backColour;
|
|
|
|
|
|
|
|
a.Invalidate(false, true);
|
|
|
|
}
|
2023-10-01 02:37:24 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
Invalidate(redrawAxis, redrawMarkers) {
|
2023-09-30 22:45:55 +00:00
|
|
|
const a = this;
|
|
|
|
|
2023-10-22 21:14:12 +00:00
|
|
|
if (redrawAxis) a.Layer.Background.Invalidate();
|
2023-10-18 13:44:49 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
a.Layer.Flourish.Clear();
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-22 21:14:12 +00:00
|
|
|
if (redrawMarkers) a.Layer.Markers.Invalidate();
|
2023-10-14 16:26:58 +00:00
|
|
|
|
2023-10-02 22:42:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DateToString(date, pattern) {
|
|
|
|
let result = pattern;
|
|
|
|
|
|
|
|
result = result.replace("fffffff", date.getMilliseconds().toString().padStart(7, '0'));
|
|
|
|
result = result.replace("ffffff", date.getMilliseconds().toString().padStart(6, '0'));
|
|
|
|
result = result.replace("fffff", date.getMilliseconds().toString().padStart(5, '0'));
|
|
|
|
result = result.replace("yyyy", date.getFullYear().toString().padStart(4, '0'));
|
|
|
|
result = result.replace("MMMM", "{1}");
|
|
|
|
result = result.replace("dddd", "{2}");
|
|
|
|
result = result.replace("ffff", date.getMilliseconds().toString().padStart(4, '0'));
|
|
|
|
result = result.replace("yyy", date.getFullYear().toString().padStart(3, '0'));
|
|
|
|
result = result.replace("MMM", "{3}");
|
|
|
|
result = result.replace("ddd", "{4}");
|
|
|
|
result = result.replace("fff", date.getMilliseconds().toString().padStart(3, '0'));
|
|
|
|
result = result.replace("zzz", "");
|
|
|
|
result = result.replace("yy", date.getFullYear().toString().slice(-2));
|
|
|
|
result = result.replace("MM", (date.getMonth() + 1).toString().padStart(2, '0'));
|
|
|
|
result = result.replace("dd", date.getDate().toString().padStart(2, '0'));
|
|
|
|
result = result.replace("HH", date.getHours().toString().padStart(2, '0'));
|
|
|
|
result = result.replace("hh", (date.getHours() > 12 ? (date.getHours() - 12) : date.getHours()).toString().padStart(2, '0'));
|
|
|
|
result = result.replace("mm", date.getMinutes().toString().padStart(2, '0'));
|
|
|
|
result = result.replace("ss", date.getSeconds().toString().padStart(2, '0'));
|
|
|
|
result = result.replace("ff", date.getMilliseconds().toString().padStart(2, '0'));
|
|
|
|
result = result.replace("tt", "{5}");
|
|
|
|
result = result.replace("zz", "");
|
|
|
|
result = result.replace("y", date.getFullYear().toString());
|
|
|
|
result = result.replace("M", (date.getMonth() + 1).toString());
|
|
|
|
result = result.replace("d", date.getDate().toString());
|
|
|
|
result = result.replace("H", date.getHours().toString());
|
|
|
|
result = result.replace("h", (date.getHours() > 12 ? (date.getHours() - 12) : date.getHours()).toString());
|
|
|
|
result = result.replace("m", date.getMinutes().toString());
|
|
|
|
result = result.replace("s", date.getSeconds().toString());
|
|
|
|
result = result.replace("z", "");
|
|
|
|
result = result.replace("t", "{6}");
|
|
|
|
result = result.replace("Z", "");
|
|
|
|
|
|
|
|
result = result.replace("{1}", date.toLocaleString('default', { month: 'long' }));
|
|
|
|
result = result.replace("{2}", date.toLocaleString('default', { weekday: 'long' }));
|
|
|
|
result = result.replace("{3}", date.toLocaleString('default', { month: 'short' }));
|
|
|
|
result = result.replace("{4}", date.toLocaleString('default', { weekday: 'short' }));
|
|
|
|
result = result.replace("{5}", (date.getHours() >= 12 ? "PM" : "AM"));
|
|
|
|
result = result.replace("{6}", (date.getHours() >= 12 ? "P" : "A"));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
DateToInternalString(date) {
|
|
|
|
const a = this;
|
2023-10-08 14:57:22 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
return a.DateToString(date, a.DateParsePattern);
|
2023-10-02 22:42:08 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
ConvertToDate(value) {
|
|
|
|
return new Date(Date.parse(value));
|
2023-09-30 22:45:55 +00:00
|
|
|
}
|
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
|
|
|
|
OnMouseDown(sender, e, event) { /* delegate */ }
|
|
|
|
|
|
|
|
OnMouseMove(sender, e, event) { /* delegate */ }
|
|
|
|
|
|
|
|
OnClick(sender, e, event) { /* delegate */ }
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-19 23:22:29 +00:00
|
|
|
OnDblClick(sender, e, event) { /* delegate */ }
|
2023-09-30 22:45:55 +00:00
|
|
|
|
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
initialiseComponents() {
|
2023-10-02 22:42:08 +00:00
|
|
|
const a = this;
|
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Container.innerHTML = "<canvas></canvas><canvas></canvas><canvas></canvas>";
|
2023-10-02 22:42:08 +00:00
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
const canvasList = a.Container.getElementsByTagName("canvas");
|
2023-09-30 22:45:55 +00:00
|
|
|
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Layer.Background = new BBTimelineBackgroundCanvas(a, canvasList[0]);
|
2023-10-19 23:22:29 +00:00
|
|
|
a.Layer.Flourish = new BBTimelineFlourishCanvas(a, canvasList[1]);
|
2023-10-18 13:44:49 +00:00
|
|
|
a.Layer.Markers = new BBTimelineForegroundCanvas(a, canvasList[2]);
|
2023-09-30 22:45:55 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|