Initial commit
This commit is contained in:
		
						commit
						8b0701b2e5
					
				
							
								
								
									
										0
									
								
								bbtimeline.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								bbtimeline.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										378
									
								
								bbtimeline.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								bbtimeline.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,378 @@ | ||||
| /** | ||||
|  * BBTimeline | ||||
|  * @version v0.1.0.076 (2023/09/30 1432) | ||||
|  */ | ||||
| class BBTimeline { | ||||
|   constructor(el) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.Container = document.getElementById(el); | ||||
| 
 | ||||
|     a.Padding = { | ||||
|       Left: 20, | ||||
|       Top: 20, | ||||
|       Right: 20, | ||||
|       Bottom: 40 | ||||
|     }; | ||||
|     a.Size = { | ||||
|       Width: a.Container.innerWidth || a.Container.clientWidth, | ||||
|       Height: a.Container.innerHeight || a.Container.clientHeight | ||||
|     }; | ||||
|     a.Axis = { | ||||
|       LineColour1: "#CFCFCF", | ||||
|       LineWidth: 1, | ||||
|       Font: "8pt Arial", | ||||
|       LabelColour: "#000000", | ||||
|       X: { | ||||
|         NoPartPerDay: 4, | ||||
|         HourLineSpace: 6, | ||||
|         HourLineHeight: 10, | ||||
|         HourLineColour: "#A6A6A6", | ||||
|         DayLineHeight: 20, | ||||
|         DayLineColour: "#282828" | ||||
|       } | ||||
|     }; | ||||
|     a.Marker = { | ||||
|       BorderColour: "#3A5D9C", | ||||
|       BorderWidth: 2, | ||||
|       BackColour: "#D4DEEF", | ||||
|       Width: 10 | ||||
|     }; | ||||
|     a.HighlightLine = { | ||||
|       Colour: "#A6A6A6", | ||||
|       Width: 1, | ||||
|     }; | ||||
|     a.StartDate = new Date(); | ||||
|     a.ClientRectangle = a.getClientRectangle(); | ||||
| 
 | ||||
|     a.ctx = a.Container.getContext("2d"); | ||||
|     a.ctx.canvas.width = a.Size.Width; | ||||
|     a.ctx.canvas.height = a.Size.Height; | ||||
|   } | ||||
| 
 | ||||
|   Load(startDate) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.StartDate = ((typeof(startDate) == "undefined") ? new Date() : startDate); | ||||
| 
 | ||||
|     a.invalidate(); | ||||
|     a.initialiseComponents(); | ||||
|   } | ||||
| 
 | ||||
|   Clear(all) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (all) { | ||||
|       a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height); | ||||
|     } else { | ||||
|       const rect = a.getChartCoords(); | ||||
| 
 | ||||
|       a.ctx.clearRect((rect.X1 + a.Axis.LineWidth), rect.Y1, (rect.X2 - rect.X1 - a.Axis.LineWidth), (rect.Y2 - rect.Y1 - a.Axis.LineWidth)); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   initialiseComponents() { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
| 
 | ||||
|     // Vertical highlight line
 | ||||
|     a.ctx.canvas.addEventListener('mousemove', function (e) { | ||||
|       a.Clear(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) { | ||||
|       console.log('mousedown'); | ||||
|       console.log(e); | ||||
|    }); | ||||
|   } | ||||
| 
 | ||||
|   invalidate() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.Clear(true); | ||||
| 
 | ||||
|     a.drawAxis(); | ||||
|     a.drawXAxis(); | ||||
|     a.drawXAxisLabels(); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   drawAxis() { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     a.ctx.beginPath(); | ||||
|     a.ctx.moveTo(coords.X1, coords.Y1); | ||||
|     a.ctx.lineTo(coords.X1, coords.Y2); | ||||
|     a.ctx.lineTo(coords.X2, coords.Y2); | ||||
|     a.ctx.lineWidth = a.Axis.LineWidth; | ||||
|     a.ctx.strokeStyle = a.Axis.LineColour1; | ||||
|     a.ctx.stroke(); | ||||
|   } | ||||
| 
 | ||||
|   drawXAxis() { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     let x = coords.X1; | ||||
|     let y = coords.Y2 + a.Axis.LineWidth; | ||||
| 
 | ||||
|     let i = 0; | ||||
| 
 | ||||
|     while (true) { | ||||
|       if (x >= coords.X2) { | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|       a.ctx.beginPath(); | ||||
|       a.ctx.moveTo(x, y); | ||||
| 
 | ||||
|       if ((i % a.Axis.X.NoPartPerDay) == 0) { | ||||
|         a.ctx.lineTo(x, (y + a.Axis.X.DayLineHeight)); | ||||
|         a.ctx.strokeStyle = a.Axis.X.DayLineColour; | ||||
|       } else { | ||||
|         a.ctx.lineTo(x, (y + a.Axis.X.HourLineHeight)); | ||||
|         a.ctx.strokeStyle = a.Axis.X.HourLineColour; | ||||
|       } | ||||
| 
 | ||||
|       a.ctx.lineWidth = a.Axis.LineWidth; | ||||
|       a.ctx.stroke(); | ||||
| 
 | ||||
|       x += a.Axis.X.HourLineSpace; | ||||
| 
 | ||||
|       i++; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   drawXAxisLabels() { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const result = a.getXAxis(); | ||||
| 
 | ||||
|     const y = coords.Y2 + a.Axis.LineWidth; | ||||
| 
 | ||||
|     result.forEach(function(e, i) { | ||||
|       const date = a.stringToDate(e.Date); | ||||
| 
 | ||||
|       let writeLabel = false; | ||||
|       if ((i == 0)) { | ||||
|         // Don't label first entry if too close to the next month
 | ||||
|         if (date.getDate() < 25) { | ||||
|           writeLabel = true; | ||||
|         } | ||||
|       } else if (date.getDate() == 1) { | ||||
|         writeLabel = true; | ||||
|       } | ||||
| 
 | ||||
|       // if (i == 0) {
 | ||||
|       //   return;
 | ||||
|       // }
 | ||||
| 
 | ||||
|       const labelSize = a.drawText(e.X, (y + a.Axis.X.DayLineHeight), a.dateToString(date, "dd"), "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"); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   drawMarker(x, y) { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const width = a.Marker.Width - (a.Marker.BorderWidth * 2); | ||||
| 
 | ||||
|     a.ctx.beginPath(); | ||||
|     a.ctx.arc(x, y, width, 0, 2 * Math.PI, false); | ||||
|     a.ctx.fillStyle = a.Marker.BackColour; | ||||
|     a.ctx.fill(); | ||||
|     a.ctx.lineWidth = a.Marker.BorderWidth; | ||||
|     a.ctx.strokeStyle = a.Marker.BorderColour; | ||||
|     a.ctx.stroke(); | ||||
|   } | ||||
| 
 | ||||
|   drawText(x, y, label, align) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     a.ctx.font = a.Axis.Font; | ||||
|     a.ctx.fillStyle = a.Axis.LabelColour; | ||||
| 
 | ||||
|     const size = a.measureText(label); | ||||
| 
 | ||||
|     switch (align) { | ||||
|       case "center": | ||||
|         x = (x - size.OffsetLeft); | ||||
|         break; | ||||
|       case "right": | ||||
|         x = (x - size.Width); | ||||
|         break; | ||||
|       case "left": | ||||
|       default: | ||||
|         // do nothing
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     a.ctx.fillText(label, x, (y + size.Height)); | ||||
| 
 | ||||
|     return size; | ||||
|   } | ||||
| 
 | ||||
|   drawVerticalLine(x) { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     a.ctx.beginPath(); | ||||
|     a.ctx.moveTo(x, (coords.Y1 + a.HighlightLine.Width)); | ||||
|     a.ctx.lineTo(x, (coords.Y2 - a.HighlightLine.Width)); | ||||
|     a.ctx.lineWidth = a.HighlightLine.Width; | ||||
|     a.ctx.strokeStyle = a.HighlightLine.Colour; | ||||
|     a.ctx.stroke(); | ||||
|   } | ||||
| 
 | ||||
|   getChartCoords() { | ||||
|     const a = this; | ||||
| 
 | ||||
|     if (a.ClientRectangle == 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) | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   getXAxis() { | ||||
|     const a = this; | ||||
|     const coords = a.getChartCoords(); | ||||
|     if (coords == null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     let result = []; | ||||
|     let x = coords.X1; | ||||
|     let date = a.stringToDate(a.dateToString(a.StartDate, "yyyy-MM-dd")); | ||||
| 
 | ||||
|     // Rollback one day
 | ||||
|     date.setDate(date.getDate() - 1); | ||||
| 
 | ||||
|     while (true) { | ||||
|       if (x >= coords.X2) { | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|       result.push({ | ||||
|         Date: a.dateToString(date, "yyyy-MM-dd"), | ||||
|         X: x | ||||
|       }); | ||||
| 
 | ||||
|       x += (a.Axis.X.HourLineSpace * a.Axis.X.NoPartPerDay); | ||||
|       date.setDate(date.getDate() + 1); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   half(value) { | ||||
|     return (value / 2); | ||||
|   } | ||||
| 
 | ||||
|   measureText(value) { | ||||
|     const a = this; | ||||
| 
 | ||||
|     const size = a.ctx.measureText(value); | ||||
| 
 | ||||
|     return { | ||||
|       Width: size.width, | ||||
|       Height: size.fontBoundingBoxAscent, | ||||
|       OffsetLeft: a.half(size.width), | ||||
|       OffsetTop: a.half(size.fontBoundingBoxAscent) | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   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)); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										66
									
								
								demo-test.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								demo-test.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| <!doctype html> | ||||
| <html lang="en-GB"> | ||||
| <head> | ||||
|   <meta charset="UTF-8" /> | ||||
|   <meta http-equiv="content-type" content="text/html" charset="UTF-8" /> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|   <meta name="description" content="" /> | ||||
|   <meta name="keyword" content="" /> | ||||
| 
 | ||||
|   <!-- <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> | ||||
| </head> | ||||
| <body> | ||||
| 
 | ||||
|   <canvas id="myCanvas"></canvas> | ||||
| 
 | ||||
| <p> | ||||
|   <button onclick="Clear()">Clear</button> | ||||
| 
 | ||||
| </p> | ||||
| 
 | ||||
|   <style> | ||||
| 
 | ||||
| body { | ||||
|   padding: 20px; | ||||
| } | ||||
| 
 | ||||
| canvas { | ||||
|   border-style: solid; | ||||
|   border-width: 1px; | ||||
|   border-color: #000000; | ||||
|   width: 100%; | ||||
|   height: 400px; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
| } | ||||
| 
 | ||||
|   </style> | ||||
| 
 | ||||
|   <script> | ||||
| 
 | ||||
| var timeline1 = new BBTimeline("myCanvas"); | ||||
| 
 | ||||
| timeline1.Load(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| function Clear() | ||||
| { | ||||
|   timeline1.Clear(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| </body> | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Ray
						Ray