steps towards rolling own diagram/uml rendering
This commit is contained in:
parent
b65a30639e
commit
d4be01166c
5 changed files with 377 additions and 27 deletions
61
main.go
61
main.go
|
@ -33,6 +33,10 @@ func (web Web) index(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "index", watches)
|
c.HTML(http.StatusOK, "index", watches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (web Web) canvas(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "canvas", gin.H{})
|
||||||
|
}
|
||||||
|
|
||||||
func (web Web) newWatch(c *gin.Context) {
|
func (web Web) newWatch(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "newWatch", gin.H{})
|
c.HTML(http.StatusOK, "newWatch", gin.H{})
|
||||||
}
|
}
|
||||||
|
@ -219,34 +223,35 @@ func main() {
|
||||||
db, _ := gorm.Open(sqlite.Open(viper.GetString("database.dsn")))
|
db, _ := gorm.Open(sqlite.Open(viper.GetString("database.dsn")))
|
||||||
db.AutoMigrate(&Watch{}, &Filter{})
|
db.AutoMigrate(&Watch{}, &Filter{})
|
||||||
|
|
||||||
filters := []Filter{}
|
/*
|
||||||
watch := Watch{
|
filters := []Filter{}
|
||||||
Name: "LG C2 42",
|
watch := Watch{
|
||||||
Interval: 60,
|
Name: "LG C2 42",
|
||||||
Filters: filters,
|
Interval: 60,
|
||||||
}
|
Filters: filters,
|
||||||
db.Create(&watch)
|
}
|
||||||
|
db.Create(&watch)
|
||||||
|
|
||||||
urlFilter := Filter{
|
urlFilter := Filter{
|
||||||
WatchID: watch.ID,
|
WatchID: watch.ID,
|
||||||
ParentID: nil,
|
ParentID: nil,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
Name: "PriceWatch Fetch",
|
Name: "PriceWatch Fetch",
|
||||||
Type: "url",
|
Type: "url",
|
||||||
Var1: "https://tweakers.net/pricewatch/1799060/lg-c2-42-inch-donkerzilveren-voet-zwart.html",
|
Var1: "https://tweakers.net/pricewatch/1799060/lg-c2-42-inch-donkerzilveren-voet-zwart.html",
|
||||||
}
|
}
|
||||||
db.Create(&urlFilter)
|
db.Create(&urlFilter)
|
||||||
|
|
||||||
xpathFilter := Filter{
|
|
||||||
WatchID: watch.ID,
|
|
||||||
Watch: watch,
|
|
||||||
ParentID: &urlFilter.ID,
|
|
||||||
Name: "price select",
|
|
||||||
Type: "xpath",
|
|
||||||
Var1: "//td[@class='shop-price']",
|
|
||||||
}
|
|
||||||
db.Create(&xpathFilter)
|
|
||||||
|
|
||||||
|
xpathFilter := Filter{
|
||||||
|
WatchID: watch.ID,
|
||||||
|
Watch: watch,
|
||||||
|
ParentID: &urlFilter.ID,
|
||||||
|
Name: "price select",
|
||||||
|
Type: "xpath",
|
||||||
|
Var1: "//td[@class='shop-price']",
|
||||||
|
}
|
||||||
|
db.Create(&xpathFilter)
|
||||||
|
*/
|
||||||
//bot, _ := tgbotapi.NewBotAPI(viper.GetString("telegram.token"))
|
//bot, _ := tgbotapi.NewBotAPI(viper.GetString("telegram.token"))
|
||||||
|
|
||||||
//bot.Debug = true
|
//bot.Debug = true
|
||||||
|
@ -269,6 +274,8 @@ func main() {
|
||||||
templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html")
|
templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html")
|
||||||
templates.AddFromFiles("editGroup", "templates/base.html", "templates/editGroup.html")
|
templates.AddFromFiles("editGroup", "templates/base.html", "templates/editGroup.html")
|
||||||
|
|
||||||
|
templates.AddFromFiles("canvas", "templates/base.html", "templates/diagrams.html")
|
||||||
|
|
||||||
templates.AddFromFiles("500", "templates/base.html", "templates/500.html")
|
templates.AddFromFiles("500", "templates/base.html", "templates/500.html")
|
||||||
router.HTMLRender = templates
|
router.HTMLRender = templates
|
||||||
|
|
||||||
|
@ -281,5 +288,7 @@ func main() {
|
||||||
router.POST("/filter/update/", web.updateFilter)
|
router.POST("/filter/update/", web.updateFilter)
|
||||||
router.POST("/filter/delete/", web.deleteFilter)
|
router.POST("/filter/delete/", web.deleteFilter)
|
||||||
|
|
||||||
|
router.GET("/canvas/", web.canvas)
|
||||||
|
|
||||||
router.Run("0.0.0.0:8080")
|
router.Run("0.0.0.0:8080")
|
||||||
}
|
}
|
||||||
|
|
131
static/diagram.js
Normal file
131
static/diagram.js
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
var DiagramNode = /** @class */ (function () {
|
||||||
|
function DiagramNode(x, y, width, height, label) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
DiagramNode.prototype.pointInDiagram = function (x, y) {
|
||||||
|
if (x < this.x) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (y < this.y) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (x > this.x + this.width) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (y > this.y + this.height) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return DiagramNode;
|
||||||
|
}());
|
||||||
|
var _diagram;
|
||||||
|
function diargramOnResize() {
|
||||||
|
_diagram.fillParent();
|
||||||
|
}
|
||||||
|
function diagramOnMouseDown(ev) {
|
||||||
|
_diagram.onmousedown(ev);
|
||||||
|
}
|
||||||
|
function diagramOnMouseUp(ev) {
|
||||||
|
_diagram.onmouseup(ev);
|
||||||
|
}
|
||||||
|
function diagramOnMouseMove(ev) {
|
||||||
|
_diagram.onmousemove(ev);
|
||||||
|
}
|
||||||
|
var Diagrams = /** @class */ (function () {
|
||||||
|
function Diagrams(canvasId) {
|
||||||
|
this.canvas = document.getElementById(canvasId);
|
||||||
|
if (this.canvas === null) {
|
||||||
|
throw "Could not getElementById " + canvasId;
|
||||||
|
}
|
||||||
|
var ctx = this.canvas.getContext("2d");
|
||||||
|
if (ctx === null) {
|
||||||
|
throw "Could not get 2d rendering context";
|
||||||
|
}
|
||||||
|
_diagram = this;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.ctx.font = "30px Arial";
|
||||||
|
this.canvas.onmousemove = diagramOnMouseMove;
|
||||||
|
this.canvas.onmousedown = diagramOnMouseDown;
|
||||||
|
this.canvas.onmouseup = diagramOnMouseUp;
|
||||||
|
window.onresize = diargramOnResize;
|
||||||
|
this.nodes = new Array();
|
||||||
|
this.connections = new Array();
|
||||||
|
this.cameraX = 0;
|
||||||
|
this.cameraY = 0;
|
||||||
|
}
|
||||||
|
Diagrams.prototype.onmousemove = function (ev) {
|
||||||
|
if (this.panning) {
|
||||||
|
this.cameraX += ev.movementX;
|
||||||
|
this.cameraY += ev.movementY;
|
||||||
|
}
|
||||||
|
if (this.nodeDrag) {
|
||||||
|
if (this.nodeDragged === null) {
|
||||||
|
console.error("nodeDrag==true but nodeDragged==null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.nodeDragged.x += ev.movementX;
|
||||||
|
this.nodeDragged.y += ev.movementY;
|
||||||
|
}
|
||||||
|
this.draw();
|
||||||
|
};
|
||||||
|
Diagrams.prototype.onmousedown = function (ev) {
|
||||||
|
if (ev.button != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var canvasRect = this.canvas.getBoundingClientRect();
|
||||||
|
var mouseX = ev.x - canvasRect.left;
|
||||||
|
var mouseY = ev.y - canvasRect.top;
|
||||||
|
for (var _i = 0, _a = this.nodes; _i < _a.length; _i++) {
|
||||||
|
var node = _a[_i];
|
||||||
|
if (node.pointInDiagram(mouseX, mouseY)) {
|
||||||
|
this.nodeDrag = true;
|
||||||
|
this.nodeDragged = node;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.panning = true;
|
||||||
|
};
|
||||||
|
Diagrams.prototype.onmouseup = function (ev) {
|
||||||
|
this.panning = false;
|
||||||
|
this.nodeDrag = false;
|
||||||
|
this.nodeDragged = null;
|
||||||
|
};
|
||||||
|
Diagrams.prototype.drawBackground = function () {
|
||||||
|
this.ctx.fillStyle = "#D8D8D8";
|
||||||
|
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this.ctx.strokeStyle = "#888";
|
||||||
|
this.ctx.lineWidth = 5;
|
||||||
|
this.ctx.strokeRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
};
|
||||||
|
Diagrams.prototype.draw = function () {
|
||||||
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this.drawBackground();
|
||||||
|
for (var _i = 0, _a = this.nodes; _i < _a.length; _i++) {
|
||||||
|
var node = _a[_i];
|
||||||
|
this.ctx.fillStyle = "gray";
|
||||||
|
this.ctx.fillRect(node.x + this.cameraX, node.y + this.cameraY, node.width, node.height);
|
||||||
|
this.ctx.fillStyle = "black";
|
||||||
|
this.ctx.font = "30px Arial";
|
||||||
|
this.ctx.fillText(node.label, node.x + this.cameraX + node.height / 2, node.y + this.cameraY + node.height / 1.5);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Diagrams.prototype.addNode = function (x, y, label) {
|
||||||
|
var textSize = this.ctx.measureText(label);
|
||||||
|
var textHeight = 2 * (textSize.actualBoundingBoxAscent + textSize.actualBoundingBoxDescent);
|
||||||
|
this.nodes.push(new DiagramNode(x, y, textSize.width + textHeight, textHeight, label));
|
||||||
|
};
|
||||||
|
Diagrams.prototype.addConnection = function (A, B) {
|
||||||
|
this.connections.push([A, B]);
|
||||||
|
};
|
||||||
|
Diagrams.prototype.fillParent = function () {
|
||||||
|
this.canvas.width = this.canvas.clientWidth;
|
||||||
|
this.canvas.height = this.canvas.clientHeight;
|
||||||
|
this.draw();
|
||||||
|
};
|
||||||
|
return Diagrams;
|
||||||
|
}());
|
175
static/diagram.ts
Normal file
175
static/diagram.ts
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
class DiagramNode {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
label: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
|
||||||
|
parents: Array<DiagramNode>;
|
||||||
|
children: Array<DiagramNode>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
label: string,
|
||||||
|
){
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointInDiagram(x: number, y: number){
|
||||||
|
if (x < this.x){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (y < this.y){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (x > this.x + this.width) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (y > this.y + this.height) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _diagram: Diagrams;
|
||||||
|
function diargramOnResize(){
|
||||||
|
_diagram.fillParent();
|
||||||
|
}
|
||||||
|
function diagramOnMouseDown(ev: MouseEvent){
|
||||||
|
_diagram.onmousedown(ev);
|
||||||
|
}
|
||||||
|
function diagramOnMouseUp(ev: MouseEvent){
|
||||||
|
_diagram.onmouseup(ev);
|
||||||
|
}
|
||||||
|
function diagramOnMouseMove(ev: MouseEvent){
|
||||||
|
_diagram.onmousemove(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Diagrams {
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
nodes: Array<DiagramNode>;
|
||||||
|
|
||||||
|
connections: Array<[DiagramNode, DiagramNode]>;
|
||||||
|
|
||||||
|
cameraX: number;
|
||||||
|
cameraY: number;
|
||||||
|
|
||||||
|
panning: boolean;
|
||||||
|
|
||||||
|
nodeDrag: boolean;
|
||||||
|
nodeDragged: DiagramNode | null;
|
||||||
|
|
||||||
|
constructor(canvasId: string){
|
||||||
|
this.canvas = document.getElementById(canvasId) as HTMLCanvasElement;
|
||||||
|
if (this.canvas === null){
|
||||||
|
throw `Could not getElementById ${canvasId}`;
|
||||||
|
}
|
||||||
|
let ctx = this.canvas.getContext("2d");
|
||||||
|
if (ctx === null){
|
||||||
|
throw `Could not get 2d rendering context`
|
||||||
|
}
|
||||||
|
_diagram = this;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.ctx.font = "30px Arial";
|
||||||
|
this.canvas.onmousemove = diagramOnMouseMove;
|
||||||
|
this.canvas.onmousedown = diagramOnMouseDown;
|
||||||
|
this.canvas.onmouseup = diagramOnMouseUp;
|
||||||
|
window.onresize = diargramOnResize;
|
||||||
|
|
||||||
|
this.nodes = new Array();
|
||||||
|
this.connections = new Array();
|
||||||
|
|
||||||
|
this.cameraX = 0;
|
||||||
|
this.cameraY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onmousemove(ev: MouseEvent){
|
||||||
|
if (this.panning){
|
||||||
|
this.cameraX += ev.movementX;
|
||||||
|
this.cameraY += ev.movementY;
|
||||||
|
}
|
||||||
|
if (this.nodeDrag){
|
||||||
|
if (this.nodeDragged === null){
|
||||||
|
console.error("nodeDrag==true but nodeDragged==null");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.nodeDragged.x += ev.movementX;
|
||||||
|
this.nodeDragged.y += ev.movementY;
|
||||||
|
}
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
onmousedown(ev: MouseEvent){
|
||||||
|
if (ev.button != 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let canvasRect = this.canvas.getBoundingClientRect();
|
||||||
|
let mouseX = ev.x - canvasRect.left;
|
||||||
|
let mouseY = ev.y - canvasRect.top;
|
||||||
|
for (let node of this.nodes){
|
||||||
|
if (node.pointInDiagram(mouseX, mouseY)) {
|
||||||
|
this.nodeDrag = true;
|
||||||
|
this.nodeDragged = node;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.panning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onmouseup(ev: MouseEvent){
|
||||||
|
this.panning = false;
|
||||||
|
this.nodeDrag = false;
|
||||||
|
this.nodeDragged = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBackground(){
|
||||||
|
this.ctx.fillStyle = "#D8D8D8";
|
||||||
|
this.ctx.fillRect(0,0,this.canvas.width, this.canvas.height);
|
||||||
|
this.ctx.strokeStyle = "#888";
|
||||||
|
this.ctx.lineWidth = 5;
|
||||||
|
this.ctx.strokeRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(){
|
||||||
|
this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height);
|
||||||
|
this.drawBackground();
|
||||||
|
for (let node of this.nodes){
|
||||||
|
this.ctx.fillStyle = "gray";
|
||||||
|
this.ctx.fillRect(node.x + this.cameraX, node.y + this.cameraY, node.width, node.height);
|
||||||
|
this.ctx.fillStyle = "black";
|
||||||
|
this.ctx.font = "30px Arial";
|
||||||
|
this.ctx.fillText(
|
||||||
|
node.label,
|
||||||
|
node.x + this.cameraX + node.height / 2,
|
||||||
|
node.y + this.cameraY + node.height / 1.5
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addNode(x: number, y: number, label: string){
|
||||||
|
let textSize = this.ctx.measureText(label);
|
||||||
|
let textHeight = 2 * (textSize.actualBoundingBoxAscent + textSize.actualBoundingBoxDescent);
|
||||||
|
this.nodes.push(new DiagramNode(x, y, textSize.width + textHeight, textHeight, label));
|
||||||
|
}
|
||||||
|
|
||||||
|
addConnection(A: DiagramNode, B: DiagramNode){
|
||||||
|
this.connections.push([A, B]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fillParent(){
|
||||||
|
this.canvas.width = this.canvas.clientWidth;
|
||||||
|
this.canvas.height = this.canvas.clientHeight;
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,3 +20,13 @@
|
||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canvas_parent {
|
||||||
|
width: 100%;
|
||||||
|
height: 95vh;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
25
templates/diagrams.html
Normal file
25
templates/diagrams.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{{define "head"}}
|
||||||
|
<script src="/static/diagram.js"></script>
|
||||||
|
{{ end }}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="canvas_parent">
|
||||||
|
<canvas id="canvas">
|
||||||
|
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{define "scripts"}}
|
||||||
|
<script>
|
||||||
|
var diagrams;
|
||||||
|
function canvasInit() {
|
||||||
|
diagrams = new Diagrams("canvas");
|
||||||
|
diagrams.addNode(100, 100, "Test");
|
||||||
|
diagrams.addNode(300, 300, "Test2");
|
||||||
|
diagrams.addNode(500, 500, "A much longer name");
|
||||||
|
diagrams.fillParent();
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', canvasInit, false);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
Loading…
Add table
Reference in a new issue