Added collision avoidance and marker methods
This commit is contained in:
		
							parent
							
								
									00c38a5e00
								
							
						
					
					
						commit
						f935d337d6
					
				
							
								
								
									
										491
									
								
								bbtimeline.js
									
									
									
									
									
								
							
							
						
						
									
										491
									
								
								bbtimeline.js
									
									
									
									
									
								
							@ -36,16 +36,21 @@ class BBTimeline {
 | 
			
		||||
      BorderColour: "#3A5D9C",
 | 
			
		||||
      BorderWidth: 2,
 | 
			
		||||
      BackColour: "#D4DEEF",
 | 
			
		||||
      Width: 10
 | 
			
		||||
      Width: 10,
 | 
			
		||||
      ForeColour: "#3A5D9C",
 | 
			
		||||
      Font: "9pt Arial",
 | 
			
		||||
      LabelCollisionMargin: 4
 | 
			
		||||
    };
 | 
			
		||||
    a.HighlightLine = {
 | 
			
		||||
      Colour: "#A6A6A6",
 | 
			
		||||
      Width: 1,
 | 
			
		||||
    };
 | 
			
		||||
    a.Events = [];
 | 
			
		||||
    a.StartDate = new Date();
 | 
			
		||||
    a.ClientRectangle = a.getClientRectangle();
 | 
			
		||||
    a.StartDate = a.DateToString(new Date(), "yyyy-MM-dd");
 | 
			
		||||
    a.ShowDate = a.StartDate;
 | 
			
		||||
    a.GraphRectangle = a.calcGraphArea();
 | 
			
		||||
    a.Enabled = false;
 | 
			
		||||
    a.Debug = false;
 | 
			
		||||
 | 
			
		||||
    a.ctx = a.Container.getContext("2d");
 | 
			
		||||
    a.ctx.canvas.width = a.Size.Width;
 | 
			
		||||
@ -54,32 +59,50 @@ class BBTimeline {
 | 
			
		||||
    a.initialiseComponents();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Load(startDate) {
 | 
			
		||||
  AddEvent(date, label, options) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.StartDate = ((typeof(startDate) == "undefined") ? new Date() : startDate);
 | 
			
		||||
    const _options = Object.assign(a.GetEventItem(), options);
 | 
			
		||||
 | 
			
		||||
    a.invalidate(true);
 | 
			
		||||
    let event = a.FindEvent(date);
 | 
			
		||||
    if (event == null) {
 | 
			
		||||
      a.Events.push(a.GetEvent(date));
 | 
			
		||||
 | 
			
		||||
    a.Enabled = true;
 | 
			
		||||
      event = a.FindEvent(date);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (label != null) {
 | 
			
		||||
      event.Label = label;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event.Events.push(_options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Clear(all) {
 | 
			
		||||
  Clear() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    if (all) {
 | 
			
		||||
      a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height);
 | 
			
		||||
    a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height);
 | 
			
		||||
 | 
			
		||||
      a.Events = [];
 | 
			
		||||
      a.Enabled = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      const rect = a.getClientCoords();
 | 
			
		||||
    a.StartDate = a.DateToString(new Date(), "yyyy-MM-dd");
 | 
			
		||||
    a.ShowDate = a.StartDate;
 | 
			
		||||
    a.Enabled = false;
 | 
			
		||||
    a.Events = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
      a.ctx.clearRect((rect.X1 + a.Axis.LineWidth), rect.Y1, (rect.X2 - rect.X1 - a.Axis.LineWidth), (rect.Y2 - rect.Y1 - a.Axis.LineWidth));
 | 
			
		||||
  DeleteMarker(date)
 | 
			
		||||
  {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    for (let i=0; i<a.Events.length; i++) {
 | 
			
		||||
      if (a.Events[i].Date != date) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      a.Events.splice(i, 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FindDate(date) {
 | 
			
		||||
  FindDatePosition(date) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    const points = a.getXAxis();
 | 
			
		||||
@ -92,11 +115,46 @@ class BBTimeline {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FindEventByCoords(x, y) {
 | 
			
		||||
  FindVisibleEvents() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    let result = [];
 | 
			
		||||
 | 
			
		||||
    const availableX = a.getXAxis();
 | 
			
		||||
    availableX.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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FindEvent(date) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    for (let i=0; i<a.Events.length; i++) {
 | 
			
		||||
      const e = a.Events[i].Rectangle;
 | 
			
		||||
      if (a.Events[i].Date == date) {
 | 
			
		||||
        return a.Events[i];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FindEventsByCoords(x, y) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    for (let i=0; i<a.Events.length; i++) {
 | 
			
		||||
      const e = a.Events[i].HitBox;
 | 
			
		||||
      if (a.Events[i].HitBox == null) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ((x >= e.X1) && (x <= e.X2) && (y >= e.Y1) && (y <= e.Y2)){
 | 
			
		||||
        return a.Events[i];
 | 
			
		||||
@ -106,77 +164,265 @@ class BBTimeline {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  AddEvent(date, title, description, link) {
 | 
			
		||||
  Load(startDate) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.Events.push({
 | 
			
		||||
      Date: date,
 | 
			
		||||
      Title: title,
 | 
			
		||||
      Description: description,
 | 
			
		||||
      Link: link
 | 
			
		||||
    });
 | 
			
		||||
    a.StartDate = startDate;
 | 
			
		||||
 | 
			
		||||
    a.invalidate(false);
 | 
			
		||||
    a.Show(startDate);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Show(date) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    if (a.stringToDate(date) < a.stringToDate(a.StartDate)) {
 | 
			
		||||
      date = a.StartDate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    a.ShowDate = date;
 | 
			
		||||
    a.Enabled = true;
 | 
			
		||||
 | 
			
		||||
    a.Invalidate(true, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ShowNext() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    let date = a.stringToDate(a.ShowDate);
 | 
			
		||||
    date.setMonth(date.getMonth() + 1);
 | 
			
		||||
 | 
			
		||||
    a.Show(a.DateToString(date, "yyyy-MM-dd"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ShowPrevious() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    let date = a.stringToDate(a.ShowDate);
 | 
			
		||||
    date.setMonth(date.getMonth() - 1);
 | 
			
		||||
 | 
			
		||||
    a.Show(a.DateToString(date, "yyyy-MM-dd"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initialiseComponents() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const coords = a.getClientCoords();
 | 
			
		||||
 | 
			
		||||
    // Vertical highlight line
 | 
			
		||||
    // a.ctx.canvas.addEventListener('mousemove', function (e) {
 | 
			
		||||
    //   if (!a.Enabled) {
 | 
			
		||||
    //     return;
 | 
			
		||||
    //   }
 | 
			
		||||
 | 
			
		||||
    //   a.invalidate(false);
 | 
			
		||||
 | 
			
		||||
    //   if ((e.offsetX > (coords.X1 + a.Axis.LineWidth)) && (e.offsetX < coords.X2) && (e.offsetY >= coords.Y1) && (e.offsetY < (coords.Y2 - a.Axis.LineWidth))){
 | 
			
		||||
    //     a.drawVerticalLine(e.offsetX);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    a.ctx.canvas.addEventListener('mousedown', function (e) {
 | 
			
		||||
      if (!a.Enabled) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var event = a.FindEventByCoords(e.offsetX, e.offsetY);
 | 
			
		||||
      var event = a.FindEventsByCoords(e.offsetX, e.offsetY);
 | 
			
		||||
      if (event == null) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.log(event);
 | 
			
		||||
      if (a.Debug) console.log(event);
 | 
			
		||||
 | 
			
		||||
      // console.log(e);
 | 
			
		||||
      a.OnEventClick(event);
 | 
			
		||||
   });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  invalidate(all) {
 | 
			
		||||
  Invalidate(redrawAxis, redrawMarkers) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.Clear(all);
 | 
			
		||||
    if (redrawAxis) {
 | 
			
		||||
      a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height);
 | 
			
		||||
 | 
			
		||||
    if (all) {
 | 
			
		||||
      a.drawAxis();
 | 
			
		||||
      a.drawXAxis();
 | 
			
		||||
      a.drawXAxisLabels();
 | 
			
		||||
    } else {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (redrawMarkers) {
 | 
			
		||||
      a.clearChart();
 | 
			
		||||
 | 
			
		||||
      const visibleEvents = a.FindVisibleEvents();
 | 
			
		||||
      const coords = a.getClientCoords();
 | 
			
		||||
      coords.Y1 += a.Marker.Width;
 | 
			
		||||
 | 
			
		||||
      for (let i=0; i<a.Events.length; i++) {
 | 
			
		||||
        const event = a.FindDate(a.Events[i].Date);
 | 
			
		||||
      if (a.Debug) console.log(visibleEvents);
 | 
			
		||||
 | 
			
		||||
        a.drawVerticalLine(event.X, coords.Y1);
 | 
			
		||||
      visibleEvents.forEach(function (e, i) {
 | 
			
		||||
        // Calculate Y position
 | 
			
		||||
        let posY = a.calcMarkerPosition(e.Position.X, coords.Y1);
 | 
			
		||||
 | 
			
		||||
        const rect = a.drawMarker(event.X, coords.Y1);
 | 
			
		||||
        a.Events[i].Rectangle = rect;
 | 
			
		||||
      }
 | 
			
		||||
        a.drawVerticalLine(e.Position.X, posY);
 | 
			
		||||
        const markerCoords = a.drawMarker(e.Position.X, posY, e.BorderColour, e.BackColour);
 | 
			
		||||
        const labelSize = a.drawText((markerCoords.X2 + a.GraphRectangle.Margin), markerCoords.Y1, e.Label, a.Marker.Font, a.Marker.ForeColour, "left");
 | 
			
		||||
 | 
			
		||||
      console.log(a.Events);
 | 
			
		||||
        e.Position = { X: e.Position.X, Y: posY };
 | 
			
		||||
 | 
			
		||||
        e.HitBox = {
 | 
			
		||||
          X1: markerCoords.X1,
 | 
			
		||||
          Y1: markerCoords.Y1,
 | 
			
		||||
          X2: (markerCoords.X2 + a.GraphRectangle.Margin + labelSize.Width),
 | 
			
		||||
          Y2: markerCoords.Y2
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (a.Debug) a.drawRectangle(e.HitBox);
 | 
			
		||||
        if (a.Debug) console.log(e);
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  GetEvent(date) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      Date: date,
 | 
			
		||||
      Label: "",
 | 
			
		||||
      Position: { X: 0, Y: 0 },
 | 
			
		||||
      Events: [],
 | 
			
		||||
      HitBox: null,
 | 
			
		||||
      BorderColour: a.Marker.BorderColour,
 | 
			
		||||
      BackColour: a.Marker.BackColour
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetEventItem() {
 | 
			
		||||
    return {
 | 
			
		||||
      Title: "",
 | 
			
		||||
      Description: "",
 | 
			
		||||
      Link: "",
 | 
			
		||||
      Tag: null
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  OnEventClick(event) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  calcGraphArea() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    let result = {
 | 
			
		||||
      X: a.Padding.Left,
 | 
			
		||||
      Y: a.Padding.Top,
 | 
			
		||||
      Width: (a.Size.Width - a.Padding.Right),
 | 
			
		||||
      Height: (a.Size.Height - a.Padding.Bottom),
 | 
			
		||||
      Margin: (a.Marker.BorderWidth * 2)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    result.StepHeight = a.Marker.Width + result.Margin;
 | 
			
		||||
    result.NoStep = Math.floor(result.Height / result.StepHeight);
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  calcMarkerPosition(x, y) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    // Calculate Y position
 | 
			
		||||
    let hasMoved = false;
 | 
			
		||||
    let posY = y;
 | 
			
		||||
    for (let i=0; i<a.GraphRectangle.NoStep; i++)
 | 
			
		||||
    {
 | 
			
		||||
      posY = y + (a.GraphRectangle.StepHeight * i);
 | 
			
		||||
 | 
			
		||||
      var clippedEvent = a.FindEventsByCoords(x, posY);
 | 
			
		||||
      if (clippedEvent == null) {
 | 
			
		||||
        hasMoved = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!hasMoved) {
 | 
			
		||||
      posY = y;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return posY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearChart() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const rect = a.getClientCoords();
 | 
			
		||||
 | 
			
		||||
    a.ctx.clearRect((rect.X1 + a.Axis.LineWidth), rect.Y1, (rect.X2 - rect.X1 - a.Axis.LineWidth), (rect.Y2 - rect.Y1 - a.Axis.LineWidth));
 | 
			
		||||
 | 
			
		||||
    // Clear marker positions
 | 
			
		||||
    const visibleEvents = a.FindVisibleEvents();
 | 
			
		||||
    visibleEvents.forEach(function (e, i) {
 | 
			
		||||
      e.Position = { X: 0, Y: 0 };
 | 
			
		||||
      e.HitBox = null;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawAxis() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const coords = a.getClientCoords();
 | 
			
		||||
@ -191,6 +437,8 @@ class BBTimeline {
 | 
			
		||||
    a.ctx.lineWidth = a.Axis.LineWidth;
 | 
			
		||||
    a.ctx.strokeStyle = a.Axis.LineColour1;
 | 
			
		||||
    a.ctx.stroke();
 | 
			
		||||
 | 
			
		||||
    if (a.Debug) a.drawRectangle(coords);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawXAxis() {
 | 
			
		||||
@ -258,17 +506,17 @@ class BBTimeline {
 | 
			
		||||
      //   return;
 | 
			
		||||
      // }
 | 
			
		||||
 | 
			
		||||
      const labelSize = a.drawText(e.X, (y + a.Axis.X.DayLineHeight), a.dateToString(date, "dd"), "center");
 | 
			
		||||
      const labelSize = a.drawText(e.X, (y + a.Axis.X.DayLineHeight), a.DateToString(date, "dd"), a.Axis.Font, a.Axis.LabelColour, "center");
 | 
			
		||||
      const label2Spacing = 6;
 | 
			
		||||
 | 
			
		||||
      // Write month on first of the month
 | 
			
		||||
      if (writeLabel) {
 | 
			
		||||
        a.drawText(e.X, (y + a.Axis.X.DayLineHeight + labelSize.Height + label2Spacing), a.dateToString(date, "MMMM yyyy"), "left");
 | 
			
		||||
        a.drawText(e.X, (y + a.Axis.X.DayLineHeight + labelSize.Height + label2Spacing), a.DateToString(date, "MMMM yyyy"), a.Axis.Font, a.Axis.LabelColour, "left");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawMarker(x, y) {
 | 
			
		||||
  drawMarker(x, y, borderColour, backColour) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const coords = a.getClientCoords();
 | 
			
		||||
    if (coords == null) {
 | 
			
		||||
@ -279,39 +527,22 @@ class BBTimeline {
 | 
			
		||||
 | 
			
		||||
    a.ctx.beginPath();
 | 
			
		||||
    a.ctx.arc(x, y, width, 0, 2 * Math.PI, false);
 | 
			
		||||
    a.ctx.fillStyle = a.Marker.BackColour;
 | 
			
		||||
    a.ctx.fillStyle = backColour;
 | 
			
		||||
    a.ctx.fill();
 | 
			
		||||
    a.ctx.lineWidth = a.Marker.BorderWidth;
 | 
			
		||||
    a.ctx.strokeStyle = a.Marker.BorderColour;
 | 
			
		||||
    a.ctx.strokeStyle = borderColour;
 | 
			
		||||
    a.ctx.stroke();
 | 
			
		||||
 | 
			
		||||
    const offset = a.half(a.Marker.Width);
 | 
			
		||||
 | 
			
		||||
    const result = {
 | 
			
		||||
      X1: x - (offset + a.Marker.BorderWidth),
 | 
			
		||||
      Y1: y - (offset + a.Marker.BorderWidth),
 | 
			
		||||
      X2: x + (offset + a.Marker.BorderWidth),
 | 
			
		||||
      Y2: y + (offset + a.Marker.BorderWidth)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // a.ctx.beginPath();
 | 
			
		||||
    // a.ctx.rect(result.X1, result.Y1, (result.X2 - result.X1), (result.Y2 - result.Y1));
 | 
			
		||||
    // a.ctx.fillStyle = 'yellow';
 | 
			
		||||
    // a.ctx.fill();
 | 
			
		||||
    // a.ctx.lineWidth = 1;
 | 
			
		||||
    // a.ctx.strokeStyle = 'black';
 | 
			
		||||
    // a.ctx.stroke();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
    return a.measureMarker(x, y);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawText(x, y, label, align) {
 | 
			
		||||
  drawText(x, y, label, font, foreColour, align) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.ctx.font = a.Axis.Font;
 | 
			
		||||
    a.ctx.fillStyle = a.Axis.LabelColour;
 | 
			
		||||
    a.ctx.font = font;
 | 
			
		||||
    a.ctx.fillStyle = foreColour;
 | 
			
		||||
 | 
			
		||||
    const size = a.measureText(label);
 | 
			
		||||
    const size = a.measureText(font, label);
 | 
			
		||||
 | 
			
		||||
    switch (align) {
 | 
			
		||||
      case "center":
 | 
			
		||||
@ -331,6 +562,18 @@ class BBTimeline {
 | 
			
		||||
    return size;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawRectangle(coords) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.ctx.beginPath();
 | 
			
		||||
    a.ctx.rect(coords.X1, coords.Y1, (coords.X2 - coords.X1), (coords.Y2 - coords.Y1));
 | 
			
		||||
    //a.ctx.fillStyle = 'yellow';
 | 
			
		||||
    //a.ctx.fill();
 | 
			
		||||
    a.ctx.lineWidth = 1;
 | 
			
		||||
    a.ctx.strokeStyle = 'red';
 | 
			
		||||
    a.ctx.stroke();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawVerticalLine(x, y) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const coords = a.getClientCoords();
 | 
			
		||||
@ -353,26 +596,15 @@ class BBTimeline {
 | 
			
		||||
  getClientCoords() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    if (a.ClientRectangle == null) {
 | 
			
		||||
    if (a.GraphRectangle == null) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      X1: a.ClientRectangle.X,
 | 
			
		||||
      Y1: a.ClientRectangle.Y,
 | 
			
		||||
      X2: (a.ClientRectangle.Width - a.ClientRectangle.X),
 | 
			
		||||
      Y2: (a.ClientRectangle.Height - a.ClientRectangle.Y)
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getClientRectangle() {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      X: a.Padding.Left,
 | 
			
		||||
      Y: a.Padding.Top,
 | 
			
		||||
      Width: (a.Size.Width - a.Padding.Right),
 | 
			
		||||
      Height: (a.Size.Height - a.Padding.Bottom)
 | 
			
		||||
      X1: a.GraphRectangle.X,
 | 
			
		||||
      Y1: a.GraphRectangle.Y,
 | 
			
		||||
      X2: (a.GraphRectangle.Width - a.GraphRectangle.X),
 | 
			
		||||
      Y2: (a.GraphRectangle.Height - a.GraphRectangle.Y)
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -385,7 +617,7 @@ class BBTimeline {
 | 
			
		||||
 | 
			
		||||
    let result = [];
 | 
			
		||||
    let x = coords.X1;
 | 
			
		||||
    let date = a.stringToDate(a.dateToString(a.StartDate, "yyyy-MM-dd"));
 | 
			
		||||
    let date = a.stringToDate(a.ShowDate);
 | 
			
		||||
 | 
			
		||||
    // Rollback one day
 | 
			
		||||
    date.setDate(date.getDate() - 1);
 | 
			
		||||
@ -396,7 +628,7 @@ class BBTimeline {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      result.push({
 | 
			
		||||
        Date: a.dateToString(date, "yyyy-MM-dd"),
 | 
			
		||||
        Date: a.DateToString(date, "yyyy-MM-dd"),
 | 
			
		||||
        X: x
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -411,9 +643,24 @@ class BBTimeline {
 | 
			
		||||
    return (value / 2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  measureText(value) {
 | 
			
		||||
  measureMarker(x, y) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
    const offset = a.half(a.Marker.Width);
 | 
			
		||||
 | 
			
		||||
    const result = {
 | 
			
		||||
      X1: x - (offset + a.Marker.BorderWidth),
 | 
			
		||||
      Y1: y - (offset + a.Marker.BorderWidth),
 | 
			
		||||
      X2: x + (offset + a.Marker.BorderWidth),
 | 
			
		||||
      Y2: y + (offset + a.Marker.BorderWidth)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  measureText(font, value) {
 | 
			
		||||
    const a = this;
 | 
			
		||||
 | 
			
		||||
    a.ctx.font = font;
 | 
			
		||||
    const size = a.ctx.measureText(value);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
@ -424,52 +671,6 @@ class BBTimeline {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stringToDate(value) {
 | 
			
		||||
    return new Date(Date.parse(value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,7 @@
 | 
			
		||||
  <!-- <script src="http://cdn.hiimray.co.uk/8206c600-707c-469e-8d49-a76ae35782af/bootstrap/5.3.0/dist/js/bootstrap.bundle.min.js"></script> -->
 | 
			
		||||
  <!-- <link href="http://cdn.hiimray.co.uk/8206c600-707c-469e-8d49-a76ae35782af/bootstrap/5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
 | 
			
		||||
 | 
			
		||||
  <link href="bbtimeline.css" rel="stylesheet" />
 | 
			
		||||
  <script src="bbtimeline.js"></script>
 | 
			
		||||
  <!-- <link href="bbtimeline.min.css" rel="stylesheet" /> -->
 | 
			
		||||
  <!-- <script src="bbtimeline.min.js"></script> -->
 | 
			
		||||
 | 
			
		||||
  <title></title>
 | 
			
		||||
@ -23,9 +21,18 @@
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
  <button onclick="Clear()">Clear</button>
 | 
			
		||||
  <button onclick="LoadToday()">Load Today</button>
 | 
			
		||||
  <button onclick="LoadNext()">Load Next Month</button>
 | 
			
		||||
 | 
			
		||||
  <button onclick="LoadDemo()">Load Demo Events</button>
 | 
			
		||||
  <button onclick="ToggleDebug()">Toggle Debug</button>
 | 
			
		||||
</p>
 | 
			
		||||
<p>
 | 
			
		||||
  <button onclick="LoadToday()">Show Today</button>
 | 
			
		||||
  <button onclick="LoadPrevious()">Show Previous Month</button>
 | 
			
		||||
  <button onclick="LoadNext()">Show Next Month</button>
 | 
			
		||||
</p>
 | 
			
		||||
<p>
 | 
			
		||||
  <button onclick="UpdateLabel()">Update Label</button>
 | 
			
		||||
  <button onclick="UpdateMarker()">Update Marker</button>
 | 
			
		||||
  <button onclick="DeleteMarker()">Delete Marker</button>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
  <style>
 | 
			
		||||
@ -49,11 +56,13 @@ canvas {
 | 
			
		||||
  <script>
 | 
			
		||||
 | 
			
		||||
var timeline1 = new BBTimeline("myCanvas");
 | 
			
		||||
timeline1.OnEventClick = function(e) {
 | 
			
		||||
  // console.log(e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LoadDemo();
 | 
			
		||||
LoadToday();
 | 
			
		||||
 | 
			
		||||
timeline1.Load();
 | 
			
		||||
timeline1.AddEvent("2023-10-05", "title", "description", "link");
 | 
			
		||||
timeline1.AddEvent("2023-10-06", "title", "description", "link");
 | 
			
		||||
timeline1.AddEvent("2023-10-16", "title", "description", "link");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -63,17 +72,61 @@ function Clear()
 | 
			
		||||
  timeline1.Clear(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function LoadDemo()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.AddEvent("2023-10-05", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-06", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-06", null, { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-16", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-06", "hello 2", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-06", null, { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
 | 
			
		||||
  timeline1.AddEvent("2023-10-20", "hello hello hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-21", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-23", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
  timeline1.AddEvent("2023-10-26", "hello", { Title: Math.random(), Description: Math.random() });
 | 
			
		||||
 | 
			
		||||
  LoadToday();
 | 
			
		||||
  timeline1.Invalidate(false, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ToggleDebug()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.Debug = !timeline1.Debug;
 | 
			
		||||
  timeline1.Invalidate(true, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function LoadToday()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.Load(new Date());
 | 
			
		||||
  const date = timeline1.DateToString(new Date(), "yyyy-MM-dd");
 | 
			
		||||
 | 
			
		||||
  timeline1.Load(date);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function LoadPrevious()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.ShowPrevious();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function LoadNext()
 | 
			
		||||
{
 | 
			
		||||
  let date = timeline1.StartDate;
 | 
			
		||||
  date.setMonth(date.getMonth() + 1);
 | 
			
		||||
  timeline1.ShowNext();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  timeline1.Load(date);
 | 
			
		||||
function UpdateLabel()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.UpdateLabel("2023-10-21", "hello world");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function UpdateMarker()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.UpdateMarker("2023-10-21", "#E68422", "#FAE7D3");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function DeleteMarker()
 | 
			
		||||
{
 | 
			
		||||
  timeline1.DeleteMarker("2023-10-06");
 | 
			
		||||
  timeline1.Invalidate(false, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user