I started implementing a graph drawing application in javascript and < canvas > element, and i just wanted to hear your thought on the progress so far. I'm very open to suggestions and i'm very interested to hear what you have to say.So you can see the progress so far in the present code, if you have any questions about it feel free to ask.var graph = {init: function(edges) {    graph.vertices = {};    graph.edges = {};    graph.canvas = document.getElementById('platno');    graph.width = graph.canvas.width;    graph.height = graph.canvas.height;    graph.hookes_test = true;    graph.ctx = graph.canvas.getContext('2d');    document.addEventListener(mousedown, graph.klik, false);    document.addEventListener(mouseup, graph.drop, false);    document.addEventListener(dblclick, graph.dblclick, false);    graph.addNodesFromEdgesList(edges);    graph.addEdgesFromEdgesList(edges);    graph.mapEdges(graph.oDuljina);    setInterval(graph.draw, 1024 / 24);},addNodesFromEdgesList: function(EdgesList) {    for (var r1 = 0; r1 < EdgesList.length; r1++) {        for (var r2 = 0; r2 < EdgesList[r1].length - 1; r2++) {            if ((typeof graph.vertices[EdgesList[r1][r2]]) === undefined) {                graph.addNode({                    id: EdgesList[r1][r2],                    x: Math.floor(graph.width / 2 + 100 * Math.cos(Math.PI * (EdgesList[r1][r2] * 2) / 11)),                    y: Math.floor(graph.height / 2 + 100 * Math.sin(Math.PI * (EdgesList[r1][r2] * 2 / 11))),                    size: 6,                    ostalo: 100                });            }        }    }},addEdgesFromEdgesList: function(EdgesList) {    for (var a = 0; a < EdgesList.length; a++) {        graph.addEdge({            from: EdgesList[a][0],            to: EdgesList[a][1],            id: a        });    }},node: function(node) {    this.id = node.id;    this.pos = new vektor(node.x, node.y);    this.size = node.size;    this._size = node.size;    this.expanded = false;},addNode: function(node) {    (typeof graph.vertices[node.id]) === undefined ? graph.vertices[node.id] = new graph.node(node) : console.log(Duplikat cvora! Id: + node.id);},removeNode: function(id) {    if (typeof graph.vertices[id] !== undefined) {        graph.removeEdgeByNodeId(id);        if (id == graph.info_node.id) graph.info_node = false;        delete graph.vertices[id];    } else {        console.log(Ne postoji node! Id: + id);    }},edge: function(edge) {    this.id = edge.id;    this.from = graph.vertices[edge.from];    this.to = graph.vertices[edge.to];},addEdge: function(edge) {    (typeof graph.edges[edge.id]) === undefined ? graph.edges[edge.id] = new graph.edge(edge) : console.log(Duplikat brida! Id: + edge.id);},removeEdgeByEdgeId: function(id) {    (typeof graph.edges[id]) !== undefined ? delete graph.edges[id] : console.log(Ne postoji brid! Id: + id);},removeEdgeByNodeId: function(id) {    if (typeof graph.vertices[id] !== undefined) {        for (var edge in graph.edges) {            if (graph.edges.hasOwnProperty(edge) && (graph.edges[edge].from.id == id || graph.edges[edge].to.id == id)) {                delete graph.edges[edge];            }        }    } else {        console.log(Ne postoji cvor! Id: + id);    }},clearGraph: function() {    for (var id in graph.vertices) {        if (graph.vertices.hasOwnProperty(id)) {            graph.removeNode(id)        }    }},mapNodes: function(funkcija, obj) {    var res = [],        tmp, id;    for (id in graph.vertices) {        if (graph.vertices.hasOwnProperty(id)) {            tmp = funkcija.apply(graph, [graph.vertices[id], obj || {}]);            if (tmp) res.push(tmp);        }    }    return res;},mapEdges: function(funkcija) {    for (var id in graph.edges) {        if (graph.edges.hasOwnProperty(id)) {            funkcija.apply(graph, [graph.edges[id].from, graph.edges[id].to, graph.edges[id].id]);        }    }},vuci: function(e) {    if (graph.drag) {        graph.drag.pos.x = (e.pageX - graph.canvas.offsetLeft);        graph.drag.pos.y = (e.pageY - graph.canvas.offsetTop);        if (graph.drag.pos.x > graph.width - 6) graph.drag.pos.x = graph.width - 6;        else if (graph.drag.pos.x < 6) graph.drag.pos.x = 6;        else if (graph.drag.pos.y > graph.height - 6) graph.drag.pos.y = graph.height - 6;        else if (graph.drag.pos.y < 6) graph.drag.pos.y = 6;    }},klik: function(e) {    graph.drag = graph.getNodeFromXY(e.pageX - graph.canvas.offsetLeft, e.pageY - graph.canvas.offsetTop)[0];    document.addEventListener(mousemove, graph.vuci, false);},drop: function() {    graph.drag = false;    document.removeEventListener(mousemove, graph.vuci, false);},getNodeFromXY: function(_x, _y) {    return graph.mapNodes(function(node, obj) {        if ((obj.x > node.pos.x - node.size) && (obj.x < node.pos.x + node.size) && (obj.y > node.pos.y - node.size) && (obj.y < node.pos.y + node.size)) {            return node;        } else {            return false        };    }, {        x: _x,        y: _y    });},draw: function() {    graph.ctx.clearRect(0, 0, graph.width, graph.height);    background();    graph.mapEdges(crtaj_v);    graph.mapNodes(crtaj_n);    graph.info();    if (!graph.hookes_test) graph.mapEdges(graph.hookes);    function background() {        var grd = graph.ctx.createRadialGradient(graph.width / 2, graph.height / 2, 30, graph.width / 2, graph.height / 2, graph.height);        grd.addColorStop(0, #42586d);        grd.addColorStop(0.5, #36495a);        grd.addColorStop(1, #26323e);        graph.ctx.fillStyle = grd;        graph.ctx.fillRect(0, 0, graph.width, graph.height);    }    function crtaj_n(v) {        graph.ctx.fillStyle = 'rgba(0,0,0,0.4)';        graph.ctx.beginPath();        graph.ctx.arc(v.pos.x, v.pos.y, v.size, 0, Math.PI * 2, true);        graph.ctx.fill();        graph.ctx.strokeStyle = '#818f9a'        graph.ctx.arc(v.pos.x, v.pos.y, v.size, 0, Math.PI * 2, true);        graph.ctx.stroke();        return false;    }    function crtaj_v(v1, v2) {        graph.ctx.beginPath();        graph.ctx.strokeStyle = 'rga(129,143,154,0.1)';        var duljina = [v1.pos.udaljenost(v2.pos) - v1.size, v1.pos.udaljenost(v2.pos) - v2.size];        var kut = Math.atan2(v2.pos.y - v1.pos.y, v2.pos.x - v1.pos.x);        graph.ctx.moveTo(v2.pos.x - (duljina[0] * Math.cos(kut)), v2.pos.y - (duljina[0] * Math.sin(kut)));        graph.ctx.lineTo(v1.pos.x + (duljina[1] * Math.cos(kut)), v1.pos.y + (duljina[1] * Math.sin(kut)));        graph.ctx.stroke();    }},dblclick: function(e) {    var dbl = graph.getNodeFromXY(e.pageX - platno.offsetLeft, e.pageY - platno.offsetTop)[0] || false;    if (dbl.expanded) {        dbl.size = dbl._size;        dbl.expanded = false;        graph.info_node = false;    } else if (dbl) {        graph.mapNodes(function(v1) {            if (v1.expanded) {                v1.size = v1._size;                v1.expanded = false;                graph.info_node = false;            }        })        dbl.size = 30;        dbl.expanded = true;        graph.info_node = dbl;    }},info: function() {    if (graph.info_node) {        graph.ctx.font = 10px Verdana;        graph.ctx.textAlign = center;        graph.ctx.fillStyle = '#ffffff';        graph.ctx.fillText(Node:  + graph.info_node.id, graph.info_node.pos.x, graph.info_node.pos.y + 3, 30);    }},hookes: function(v1, v2, id) {    var duljina = v1.pos.oduzmi(v2.pos),        udaljenost = duljina.duljina() - (graph.edges[id].duljina),        HL = 20 * (udaljenost / duljina.duljina()),        kut = Math.atan2(v2.pos.y - v1.pos.y, v2.pos.x - v1.pos.x);    (graph.drag && (graph.drag.id != v1.id)) || !graph.drag ? graph.zbrojiLokacija(v1, kut, HL) : false;    (graph.drag && (graph.drag.id != v2.id)) || !graph.drag ? graph.oduzmiLokacija(v2, kut, HL) : false;},oDuljina: function(v1, v2, id) {    graph.hookes_test = false;    graph.edges[id].duljina = v1.pos.oduzmi(v2.pos).duljina();},zbrojiLokacija: function(v1, kut, HL) {    var dis = new vektor(HL * Math.cos(kut), HL * Math.sin(kut))    if (v1.pos.x + dis.x > graph.width - v1.size || v1.pos.x + dis.x < 0 + v1.size) {        v1.pos.x += dis.x * (-1);        v1.pos.y += dis.y;    } else if (v1.pos.y + dis.y > graph.height - v1.size || v1.pos.y + dis.y < 0 + v1.size) {        v1.pos.x += dis.x;        v1.pos.y += dis.y * (-1);    } else {        v1.pos = v1.pos.zbroji(dis)    }},oduzmiLokacija: function(v1, kut, HL) {    var dis = new vektor(HL * Math.cos(kut), HL * Math.sin(kut))    if (v1.pos.x + dis.x > graph.width - v1.size || v1.pos.x + dis.x < 0 + v1.size) {        v1.pos.x -= dis.x * (-1);        v1.pos.y -= dis.y;    } else if (v1.pos.y + dis.y > graph.height - v1.size || v1.pos.y + dis.y < 0 + v1.size) {        v1.pos.x -= dis.x;        v1.pos.y -= dis.y * (-1);    } else {        v1.pos = v1.pos.oduzmi(dis)    }}}function vektor(x, y) {    this.x = x;    this.y = y;}vektor.prototype.zbroji = function(v1) {    return new vektor(this.x + v1.x, this.y + v1.y);}vektor.prototype.oduzmi = function(v1) {    return new vektor(this.x - v1.x, this.y - v1.y);}vektor.prototype.division = function(x) {    return new vektor(this.x / x, this.y / x);}vektor.prototype.multiply = function(x) {    return new vektor(this.x * x, this.y * x);}vektor.prototype.udaljenost = function(v1) {    return Math.sqrt(Math.pow(v1.x - this.x, 2) + Math.pow(v1.y - this.y, 2));}vektor.prototype.duljina = function() {    return Math.max(20, Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)));}////////////////////////////////////////  Test data                       ///////////////////////////////////////var edges = [    [1, 2, 1],    [1, 3, 1],    [2, 3, 1],    [3, 4, 1],    [3, 5, 1],    [3, 6, 1],    [4, 1, 1],    [4, 2, 1],    [5, 6, 1]    ];graph.init(edges);UPDATE: added new code, this is just a preview i didn't have time to optimize the code, and also some of function names and varibles are written in my native language.Also I've added a jsfiddle link so you can see the work so far in action.http://jsfiddle.net/nNcHJ/1/ 
Javascript graph skeleton implementation
javascript;graph