/** * BBTimeline * @version v0.1.1.121 beta (2023/10/18 2058) */ class BBTimeline { constructor(el) { const a = this; a.Container = document.getElementById(el); a.Padding = { Left: 20, Top: 20, Right: 20, Bottom: 0 }; a.Size = { W: a.Container.innerWidth || a.Container.clientWidth, H: a.Container.innerHeight || a.Container.clientHeight }; a.Layer = { Background: null, Flourish: null, Markers: null }; a.DateParsePattern = "yyyy-MM-dd"; a.Axis = { LineColour1: "#000000", LineWidth: 1, Font: "8pt Arial", LabelColour: "#000000", LabelSpacing: 6 }; a.XAxis = { NoPartPerDay: 4, HourLineSpace: 6, HourLineHeight: 10, HourLineColour: "#EAEAEA", DayLineHeight: 20, DayLineColour: "#9E9E9E", Position: 'bottom' }; a.Marker = { BorderColour: "#3A5D9C", BorderWidth: 2, BackColour: "#D4DEEF", Width: 10 }; a.MarkerLabel = { Colour: "#3A5D9C", Font: "9pt Arial", Margin: 8, Line: { Colour: "#A6A6A6", Width: 1, } }; a.HotTrack = { Colour: "#F57C00", Width: 3 }; a.Events = []; a.StartDate = a.DateToInternalString(new Date()); a.ShowDate = a.StartDate; a.ShowMarkerLabel = true; a.Enabled = false; a.Debug = false; a.EnableHotTracking = true; a.initialiseComponents(); } get CTX() { return a.Layer.Markers.CTX; } get NewEvent() { const a = this; return { Date: "", Label: "", Position: { X: 0, Y: 0 }, Events: [], HitBox: null, BorderColour: a.Marker.BorderColour, BackColour: a.Marker.BackColour }; } get NewEventItem() { return { Title: "", Description: "", Link: "", Tag: null }; } get VisibleDays() { const a = this; const clientWidth = (a.Size.W - (a.Padding.Left + a.Padding.Right)); return Math.floor(clientWidth / (a.XAxis.NoPartPerDay * a.XAxis.HourLineSpace)); } get VisibleStartDate() { const a = this; return a.ConvertToDate(a.ShowDate); } get VisibleEndDate() { const a = this; let date = a.ConvertToDate(a.ShowDate); date.setDate(date.getDate() + a.VisibleDays); // Minus one for lead up date.setDate(date.getDate() - 1); return a.DateToString(date, a.DateParsePattern); } 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); } Clear() { const a = this; a.Layer.Background.Clear(); a.Layer.Flourish.Clear(); a.Layer.Markers.Clear(); a.StartDate = a.DateToInternalString(new Date()); a.ShowDate = a.StartDate; a.Enabled = false; a.Events = []; } DeleteMarker(date) { const a = this; for (let i=0; i= e.X) && (x <= x2) && (y >= e.Y) && (y <= y2)){ return a.Events[i]; } } return null; } Load(startDate) { const a = this; a.StartDate = startDate; a.Show(startDate); } Show(date) { const a = this; if (a.ConvertToDate(date) < a.ConvertToDate(a.StartDate)) { date = a.StartDate; } a.ShowDate = date; a.Enabled = true; a.Invalidate(true, true); } ShowNext() { const a = this; let date = a.VisibleStartDate; date.setDate(date.getDate() + (a.VisibleDays - 1)); a.Show(a.DateToInternalString(date)); } ShowPrevious() { const a = this; let date = a.VisibleStartDate; date.setDate(date.getDate() - (a.VisibleDays - 1)); a.Show(a.DateToInternalString(date)); } 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; } event.BorderColour = borderColour; event.BackColour = backColour; a.Invalidate(false, true); } Invalidate(redrawAxis, redrawMarkers) { const a = this; if (redrawAxis) a.Layer.Background.Invalidate(); a.Layer.Flourish.Clear(); if (redrawMarkers) a.Layer.Markers.Invalidate(); } 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; } DateToInternalString(date) { const a = this; return a.DateToString(date, a.DateParsePattern); } ConvertToDate(value) { return new Date(Date.parse(value)); } OnMouseDown(sender, e, event) { /* delegate */ } OnMouseMove(sender, e, event) { /* delegate */ } OnClick(sender, e, event) { /* delegate */ } OnDblClick(sender, e, event) { /* delegate */ } initialiseComponents() { const a = this; a.Container.innerHTML = ""; const canvasList = a.Container.getElementsByTagName("canvas"); a.Layer.Background = new BBTimelineBackgroundCanvas(a, canvasList[0]); a.Layer.Flourish = new BBTimelineFlourishCanvas(a, canvasList[1]); a.Layer.Markers = new BBTimelineForegroundCanvas(a, canvasList[2]); } }