OSDN Git Service

エッジの同期を追加。まだ不完全。 master
authorhikarupsp <hikarupsp@users.sourceforge.jp>
Mon, 5 May 2014 10:18:19 +0000 (19:18 +0900)
committerhikarupsp <hikarupsp@users.sourceforge.jp>
Mon, 5 May 2014 10:18:19 +0000 (19:18 +0900)
memdb/memdb.js
memdb/memdb.php
mgcanvas/index.html
mgcanvas/mgcanvas.js

index 968dc8d..2f17e33 100644 (file)
@@ -7,11 +7,13 @@ function MemoryDB(syncPHPURL){
        this.root = new Array();
        //
        this.nodeList = new Array();
+       this.edgeList = new Array();
        //
        this.syncPHPURL = syncPHPURL;
        this.isEnabledNetDB = true;
        //
-       this.callback_updatedNode = null;       // function(nodeinstance){};
+       this.callback_updatedNode = null;       // function(nodeInstance){};
+       this.callback_updatedEdge = null;       // function(edgeInstance){};
 }
 MemoryDB.prototype = {
        UUID_Null: "00000000-0000-0000-0000-000000000000",
@@ -25,20 +27,29 @@ MemoryDB.prototype = {
                        // XMLHttpRequest オブジェクトを作成
                        rq = new XMLHttpRequest();
                }catch(e){}
+               if(rq){
+                       return rq;
+               }
                // Internet Explorer
                try{
                        rq = new ActiveXObject('MSXML2.XMLHTTP.6.0');
                }catch(e){}
+               if(rq){
+                       return rq;
+               }
                try{
                        rq = new ActiveXObject('MSXML2.XMLHTTP.3.0');
                }catch(e){}
+               if(rq){
+                       return rq;
+               }
                try{
                        rq = new ActiveXObject('MSXML2.XMLHTTP');
                }catch(e){}
-               if(rq == null){
-                       return null;
+               if(rq){
+                       return rq;
                }
-               return rq;
+               return null;
        },
        requestObjectDisableCache: function(rq){
                //call after open request.
@@ -75,14 +86,18 @@ MemoryDB.prototype = {
        syncDB: function(){
                // MySQLと同期
                // 定期的に呼び出されることを想定
+               this.syncDBNode();
+               this.syncDBEdge();
+       },
+       syncDBNode: function(){
                var r, a, d;
-               if(this.root.length == 0){
+               if(this.nodeList.length == 0){
                        // 初回同期時は全て取得
                        r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnode", null);
                } else{
                        // 差分のみを取得
                        d = this.getNodeFromUUID(this.UUID_Node_MemoryDBNetworkTimestamp).identifier;
-                       r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getnodemod&t=" + d, null);
+                       r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnodemod&t=" + d, null);
                }
                a = r.split("\n");
                for(var i = 0, iLen = a.length; i < iLen; i++){
@@ -97,7 +112,40 @@ MemoryDB.prototype = {
                        }
                        this.updateNodeInternal(d[2], d[1], d[0]);
                }
-               //console.log(this.root);
+       },
+       syncDBEdge: function(){
+               var r, a, d;
+               if(this.edgeList.length == 0){
+                       // 初回同期時は全て取得
+                       r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getalledge", null);
+               } else{
+                       // 差分のみを取得
+                       /*
+                       d = this.getNodeFromUUID(this.UUID_Node_MemoryDBNetworkTimestamp).identifier;
+                       r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnodemod&t=" + d, null);
+                       */
+                       return;
+               }
+               a = r.split("\n");
+               for(var i = 0, iLen = a.length; i < iLen; i++){
+                       try{
+                               d = eval(a[i]);
+                       } catch(e){
+                               console.log(i + ": " + e + "\n");
+                               continue;
+                       }
+                       if(d === undefined){
+                               continue;
+                       }
+                       if(     d[0] != this.UUID_Node_MemoryDBNetworkTimestamp &&
+                               d[0] != this.UUID_Node_MemoryDBNetworkResponseCode){
+                               // edge data
+                               this.updateEdgeInternal(d[2], d[3], d[1], d[0]);
+                       } else{
+                               // node data
+                               this.updateNodeInternal(d[2], d[1], d[0]);
+                       }
+               }
        },
        updateNode: function(ident, tid, nid){
                // 該当タグのデータを書き換え、もしくは新規作成する。
@@ -151,6 +199,61 @@ MemoryDB.prototype = {
                        this.callback_updatedNode(t);
                }
        },
+       updateEdge: function(nid0, nid1, tid, eid){
+               // 該当タグのデータを書き換え、もしくは新規作成する。
+               // 可能であればネットワークに反映する
+               // eid(nodeid)は省略可能で、省略時は新たなUUIDが自動的に付与される
+               // tid(typeid)も省略可能で、省略時はNullUUIDが付与される
+               // 戻り値はMemoryDBEdgeTagインスタンス
+               // エラー発生時はundefinedを返す。
+               this.updateEdgeInternal(nid0, nid1, tid, eid, true);
+       },
+       updateEdgeInternal: function(nid0, nid1, tid, eid, enableSync){
+               // 基本的にローカルデータのみ変更
+               // enableSync == trueでネットワーク同期する
+               var t, s, r;
+               if(!tid){
+                       tid = this.UUID_Null;
+               }
+               if(!eid){
+                       eid = this.createUUID();
+               }
+               // 存在確認
+               t = this.getEdgeFromUUID(eid);
+               if(t){
+                       // 変更
+                       /*
+                       t.typeid = tid;
+                       t.identifier = ident;
+                       if(enableSync && this.isEnabledNetDB){
+                               s = this.syncPHPURL + "?action=updatenode";
+                               s += "&nid=" + encodeURIComponent(nid);
+                               s += "&tid=" + encodeURIComponent(tid);
+                               s += "&ident=" + encodeURIComponent(ident);
+                               r = this.sendRequestSync("GET", s, null);
+                               //console.log(r);
+                       }
+                       */
+                       return;
+               } else{
+                       // 新規作成
+                       t = new MemoryDBEdgeTag(eid, tid, nid0, nid1);
+                       this.root.push(t);
+                       this.edgeList.push(t);
+                       if(enableSync && this.isEnabledNetDB){
+                               s = this.syncPHPURL + "?action=addedge";
+                               s += "&eid=" + encodeURIComponent(eid);
+                               s += "&tid=" + encodeURIComponent(tid);
+                               s += "&nid0=" + encodeURIComponent(nid0);
+                               s += "&nid1=" + encodeURIComponent(nid1);
+                               r = this.sendRequestSync("GET", s, null);
+                               console.log(r);
+                       }
+               }
+               if(this.callback_updatedEdge){
+                       this.callback_updatedEdge(t);
+               }
+       },
        createUUID: function(){
                var f = this.createUUIDSub;
                return (f() + f() + "-" + f() + "-" + f() + "-" + f() + "-" + f() + f() + f());
@@ -161,6 +264,9 @@ MemoryDB.prototype = {
        getNodeFromUUID: function(nodeid){
                return this.nodeList.isIncluded(nodeid, function(a, b){ return a.nodeid == b; });
        },
+       getEdgeFromUUID: function(edgeid){
+               return this.edgeList.isIncluded(edgeid, function(a, b){ return a.edgeid == b; });
+       },
 }
 
 function MemoryDBNodeTag(nodeid, typeid, identifier){
@@ -173,8 +279,11 @@ MemoryDBNodeTag.prototype = {
 
 }
 
-function MemoryDBEdgeTag(typeUUIDStr){
-       this.uuid = null;
+function MemoryDBEdgeTag(edgeid, typeid, nodeid0, nodeid1){
+       this.edgeid = edgeid;
+       this.typeid = typeid;
+       this.nodeid0 = nodeid0;
+       this.nodeid1 = nodeid1;
 }
 MemoryDBEdgeTag.prototype = {
 
index c9c9088..e5b6d33 100644 (file)
@@ -47,7 +47,7 @@ insert into Node (
 define("QUERY_ADD_Node_TYPES", "sssi");
 //
 define("QUERY_ADD_Edge", "
-insert into Node (
+insert into Edge (
        edgeid, typeid, nodeid0, nodeid1, modtimestamp
 ) values (
        unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), ?
@@ -63,10 +63,18 @@ WHERE
 ");
 define("QUERY_UPDATE_Node_TYPES", "ssis");
 //
-
+define("QUERY_UPDATE_Edge", "
+UPDATE Edge SET
+       typeid=unhex(replace(?, '-', '')), nodeid0=unhex(replace(?, '-', '')), nodeid1=unhex(replace(?, '-', '')), modtimestamp=?
+WHERE
+       edgeid=unhex(replace(?, '-', ''))
+");
+define("QUERY_UPDATE_Edge_TYPES", "sssis");
+//
 define("QUERY_SELECT_ALL_Node", "select hex(nodeid), hex(typeid), identifier from Node");
 define("QUERY_SELECT_ALL_Node_With_modtimestamp", "select hex(nodeid), hex(typeid), identifier, modtimestamp from Node");
 define("QUERY_SELECT_ALL_Edge", "select hex(edgeid), hex(typeid),  hex(nodeid0), hex(nodeid1) from Edge");
+define("QUERY_SELECT_ALL_Edge_With_modtimestamp", "select hex(edgeid), hex(typeid),  hex(nodeid0), hex(nodeid1), modtimestamp from Edge");
 
 define("QUERY_SELECT_modified_Node", "select hex(nodeid), hex(typeid), identifier from Node WHERE modtimestamp>?");
 define("QUERY_SELECT_modified_Node_TYPES", "i");
@@ -104,7 +112,30 @@ if(isset($_GET['action'])){
                // put timestamp tag
                echoMemoryDBNetworkTimestamp();
                exit();
-       } else if(strcmp($action, 'getnodemod') == 0){
+       } else if(strcmp($action, 'getalledge') == 0){
+               $stmt = $db->prepare(QUERY_SELECT_ALL_Edge);
+               $stmt->execute();
+               //
+               $stmt->store_result();
+               if($stmt->errno != 0){
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B");
+               }
+               
+               $stmt->bind_result($uuid, $typeid, $nid0, $nid1);
+               while($stmt->fetch()){
+                       echoEdge(
+                               getFormedUUIDString($uuid),
+                               getFormedUUIDString($typeid),
+                               getFormedUUIDString($nid0),
+                               getFormedUUIDString($nid1)
+                       );
+                       echo(PHP_EOL);
+               }
+               $stmt->close();
+               // put timestamp tag
+               echoMemoryDBNetworkTimestamp();
+               exit();
+       } else if(strcmp($action, 'getallnodemod') == 0){
                if(isset($_GET['t'])){
                        $ts = $_GET['t'];
                } else{
@@ -154,6 +185,29 @@ if(isset($_GET['action'])){
                echo($stmt->num_rows);
                $stmt->close();
                exit(" OK " . getTimeStampMs());
+       } else if(strcmp($action, 'viewalledge') == 0){
+               $stmt = $db->prepare(QUERY_SELECT_ALL_Edge_With_modtimestamp);
+               $stmt->execute();
+               //
+               $stmt->store_result();
+               if($stmt->errno != 0){
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B");
+               }
+               
+               $stmt->bind_result($uuid, $typeid, $nid0, $nid1, $mts);
+               while($stmt->fetch()){
+                       echoEdge(
+                               getFormedUUIDString($uuid),
+                               getFormedUUIDString($typeid),
+                               getFormedUUIDString($nid0),
+                               getFormedUUIDString($nid1)
+                       );
+                       echo(' @' . $mts);
+                       echo("<br />");
+               }
+               echo($stmt->num_rows);
+               $stmt->close();
+               exit(" OK " . getTimeStampMs());
        } else if(strcmp($action, 'addnode') == 0){
                if(isset($_GET['nid'])){
                        $nodeid = $_GET['nid'];
@@ -210,6 +264,72 @@ if(isset($_GET['action'])){
                }
                $stmt->close();
                exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
+       } else if(strcmp($action, 'addedge') == 0){
+               if(isset($_GET['eid'])){
+                       $edgeid = $_GET['eid'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "edgeid needed.");
+               }
+               if(isset($_GET['tid'])){
+                       $typeid = $_GET['tid'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "typeid needed.");
+               }
+               if(isset($_GET['nid0'])){
+                       $nid0 = $_GET['nid0'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid0 needed.");
+               }
+               if(isset($_GET['nid1'])){
+                       $nid1 = $_GET['nid1'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid1 needed.");
+               }
+
+               $stmt = $db->prepare(QUERY_ADD_Edge);
+               $mts = getTimeStampMs();
+               $stmt->bind_param(QUERY_ADD_Edge_TYPES, $edgeid, $typeid, $nid0, $nid1, $mts);
+               $stmt->execute();
+               //
+               $stmt->store_result();
+               if($stmt->errno != 0){
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", mysqli_error($db));
+               }
+               $stmt->close();
+               exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
+       } else if(strcmp($action, 'updateedge') == 0){
+               if(isset($_GET['eid'])){
+                       $edgeid = $_GET['eid'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "edgeid needed.");
+               }
+               if(isset($_GET['tid'])){
+                       $typeid = $_GET['tid'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "typeid needed.");
+               }
+               if(isset($_GET['nid0'])){
+                       $nid0 = $_GET['nid0'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid0 needed.");
+               }
+               if(isset($_GET['nid1'])){
+                       $nid1 = $_GET['nid1'];
+               } else{
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid1 needed.");
+               }
+
+               $stmt = $db->prepare(QUERY_UPDATE_Edge);
+               $mts = getTimeStampMs();
+               $stmt->bind_param(QUERY_UPDATE_Edge_TYPES, $typeid, $nid0, $nid1, $mts, $edgeid);
+               $stmt->execute();
+               //
+               $stmt->store_result();
+               if($stmt->errno != 0){
+                       exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", mysqli_error($db));
+               }
+               $stmt->close();
+               exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
        }
 }
 
@@ -236,6 +356,11 @@ function echoNode($nid, $tid, $ident)
        echo('["' . $nid .'","' . $tid .'","' . $ident . '"]');
 }
 
+function echoEdge($eid, $tid, $nid0, $nid1)
+{
+       echo('["' . $eid .'","' . $tid .'","' . $nid0 .'","' . $nid1 . '"]');
+}
+
 function connectDB()
 {
        $db = new mysqli('localhost', DATABASE_USER, DATABASE_PWD, DATABASE_NAME);
index 931bd65..8b70865 100755 (executable)
@@ -21,8 +21,52 @@ var mgmain;
 var memdb;
 
 onload = function() {
+       var that = this;
+       var DOM_Node0ID = document.getElementById("nidBox0");
+       var DOM_Node0Ident = document.getElementById("identBox0");
+       var DOM_Node1ID = document.getElementById("nidBox1");
+       var DOM_Node1Ident = document.getElementById("identBox1");
+       var DOM_EdgeID = document.getElementById("eidBox");
+       var DOM_EdgeTypeID = document.getElementById("etidBox");
+
        memdb = new MemoryDB("./../memdb/memdb.php");
        mgmain = new MGCanvas(document.getElementById("mainCanvas"));
+       document.getElementById("updateButton0").onclick = function(){
+               mgmain.setIdentifierForSelectedNode(DOM_Node0Ident.value);
+       };
+       document.getElementById("updateButton1").onclick = function(){
+               mgmain.setIdentifierForSelectedNode(DOM_Node1Ident.value, true);
+       };
+       document.getElementById("connectNodesButton").onclick = function(){
+               memdb.updateEdge(DOM_Node0ID.value, DOM_Node1ID.value);
+       };
+       mgmain.callback_selectedNodeChanged = function(newNode){
+               if(newNode){
+                       DOM_Node0ID.value = newNode.nodeid;
+                       DOM_Node0Ident.value = newNode.identifier;
+               } else{
+                       DOM_Node0ID.value = "not selected";
+                       DOM_Node0Ident.value = "";
+               }
+       };
+       mgmain.callback_selectedNodeDestinationChanged = function(newNode){
+               if(newNode){
+                       DOM_Node1ID.value = newNode.nodeid;
+                       DOM_Node1Ident.value = newNode.identifier;
+               } else{
+                       DOM_Node1ID.value = "not selected";
+                       DOM_Node1Ident.value = "";
+               }
+       };
+       mgmain.callback_selectedEdgeChanged = function(newEdge){
+               if(newEdge){
+                       DOM_EdgeID.value = newEdge.edgeid;
+                       DOM_EdgeTypeID.value = newEdge.typeid;
+               } else{
+                       DOM_EdgeID.value = "not selected";
+                       DOM_EdgeTypeID.value = "";
+               }
+       };
        mgmain.setSourceMemoryDB(memdb);
 }
 </script>
@@ -30,7 +74,7 @@ onload = function() {
 <body>
 <h1>Mind Graph Canvas</h1>
 <canvas id="mainCanvas" width="1024" height="768" style="border:1px solid #000000;"></canvas>
-<br />
+<h3>Control</h3>
 <button onclick="mgmain.bringToCenter();">Center</button>
 <button onclick="mgmain.isPaused = !mgmain.isPaused;">Freeze</button>
 <button onclick="mgmain.zoomIn();">+</button>
@@ -39,11 +83,17 @@ onload = function() {
 <button onclick="mgmain.moveViewRelative(0, 10);">↓</button>
 <button onclick="mgmain.moveViewRelative(-10, 0);">←</button>
 <button onclick="mgmain.moveViewRelative(10, 0);">→</button>
-<br />
-identifier:<input id="identBox" type="text"></input>
-<br />
-<button onclick="mgmain.setIdentifierForSelectedNode(getElementById('identBox').value);">setNodeIdent</button>
-<button onclick="memdb.updateNode(getElementById('identBox').value);">addNode</button>
+<h3>Node0</h3>
+id:<input disabled id="nidBox0" type="text" size="50"></input>
+identifier:<input id="identBox0" type="text" size="25"></input>
+<button id="updateButton0">updateNode</button>
+<h3>Node1</h3>
+id:<input disabled id="nidBox1" type="text" size="50"></input>
+identifier:<input id="identBox1" type="text" size="25"></input>
+<button id="updateButton1">updateNode</button>
+<h3>Edge</h3>
+id :<input disabled id="eidBox" type="text" size="50"></input>
+type :<input disabled id="etidBox" type="text" size="50"></input><button id="connectNodesButton">connectNodes</button>
 
 </body>
 </html>
\ No newline at end of file
index b80639d..9594a4c 100755 (executable)
@@ -1,22 +1,31 @@
 
 function MGCanvas(canvasDOMObj){
        var that = this;
-
+       //
        this.initGraphicContext(canvasDOMObj);
+       //
        this.tickPerSecond = 30;
        this.tickCount = 0;
        this.tickTimer = window.setInterval(function(){ that.tick(); }, 1000 / this.tickPerSecond);
        this.isPaused = false;
        this.isEnabledAutomaticTracking = true;
+       //
        this.nodeList = new Array();
        this.edgeList = new Array();
+       //
        this.selectedNode = null;
+       this.callback_selectedNodeChanged = null;       //function(newNodeInstance){};
+       //
+       this.selectedNodeDestination = null;
+       this.callback_selectedNodeDestinationChanged = null;    //function(newNodeInstance){};
+       //
        this.selectedEdge = null;
+       this.callback_selectedEdgeChanged = null;       //function(newEdgeInstance){};
+       //
        this.srcMemoryDB = null;
        this.srcMemoryDBSyncPerTick = 60;
        this.srcMemoryDBSyncCount = this.srcMemoryDBSyncPerTick;
-       
-       var that = this;
+
        window.addEventListener('keydown', function(event){
                switch(event.keyCode){
                        case 37:        //左カーソル
@@ -56,7 +65,7 @@ function MGCanvas(canvasDOMObj){
                var p = that.convertPointToGraphLayerFromCanvasLayerP(that.lastMousePosition);
                //console.log(p.x + "," + p.y);
                var node = that.getNodeAtPointP(p);
-               that.selectNode(node);
+               that.selectNode(node, e.shiftKey);
                var edge = that.getEdgeAtPointP(p);
                that.selectEdge(edge);
                that.isMouseDown = true;
@@ -92,12 +101,32 @@ MGCanvas.prototype = {
                                // 新規追加
                                n = new MGNode(that, t.identifier);
                                n.nodeid = t.nodeid;
+                               n.typeid = t.typeid;
                                that.nodeList.push(n);
                        } else{
                                // 更新
                                n.identifier = t.identifier;
+                               n.typeid = t.typeid;
                        }
-               }
+               };
+               mdb.callback_updatedEdge = function(t){
+                       var e;
+                       var n = function(nid){ return that.nodeList.isIncluded(nid, function(a, b){ return (a.nodeid == b); }); };
+                       e = that.edgeList.isIncluded(t.edgeid, function(a, b){ return a.edgeid == b; });
+                       if(!e){
+                               // 新規追加
+                               e = new MGEdge(that, "", n(t.nodeid0), n(t.nodeid1));
+                               e.edgeid = t.edgeid;
+                               e.typeid = t.typeid;
+                               that.edgeList.push(e);
+                       } else{
+                               // 更新
+                               // e.identifier
+                               e.node0 = n(t.nodeid0);
+                               e.node1 = n(t.nodeid1);
+                               e.typeid = t.typeid;
+                       }
+               };
        },
        bringToCenter: function(){
                // 重心を求めて、それを表示オフセットに設定する
@@ -382,7 +411,7 @@ MGCanvas.prototype = {
                this.context.translate(w, h);
                this.displayRect = new Rectangle(-w, -h, this.canvas.width, this.canvas.height);
                this.currentScale = 1;
-               this.zoomOut();
+               //this.zoomOut();
                this.positionOffset = new Point2D(0, 0);
        },
        convertPointToGraphLayerFromCanvasLayerP: function(pCanvas){
@@ -422,16 +451,43 @@ MGCanvas.prototype = {
                }
                return null;
        },
-       selectNode: function(node){
-               if(this.selectedNode){
-                       this.selectedNode.isSelected = false;
-               }
-               if(node){
-                       node.isSelected = true;
+       selectNode: function(node, destNode){
+               if(!destNode){
+                       // 通常のノード選択
+                       if(this.selectedNode == node){
+                               return;
+                       }
+                       if(this.selectedNode){
+                               this.selectedNode.isSelected = false;
+                       }
+                       if(node){
+                               node.isSelected = true;
+                       }
+                       this.selectedNode = node;
+                       if(this.callback_selectedNodeChanged){
+                               this.callback_selectedNodeChanged(this.selectedNode);
+                       }
+               } else{
+                       // 接続先のノード選択(Shiftキーを押しながら)
+                       if(this.selectedNodeDestination == node){
+                               return;
+                       }
+                       if(this.selectedNodeDestination){
+                               this.selectedNodeDestination.isSelected = false;
+                       }
+                       if(node){
+                               node.isSelected = true;
+                       }
+                       this.selectedNodeDestination = node;
+                       if(this.callback_selectedNodeDestinationChanged){
+                               this.callback_selectedNodeDestinationChanged(this.selectedNodeDestination);
+                       }
                }
-               this.selectedNode = node;
        },
        selectEdge: function(edge){
+               if(this.selectedEdge == edge){
+                       return;
+               }
                if(this.selectedEdge){
                        this.selectedEdge.isSelected = false;
                }
@@ -439,13 +495,26 @@ MGCanvas.prototype = {
                        edge.isSelected = true;
                }
                this.selectedEdge = edge;
+               if(this.callback_selectedEdgeChanged){
+                       this.callback_selectedEdgeChanged(this.selectedEdge);
+               }
        },
-       setIdentifierForSelectedNode: function(str){
-               if(this.selectedNode){
-                       if(this.srcMemoryDB){
-                               this.srcMemoryDB.updateNode(str, this.selectedNode.typeid, this.selectedNode.nodeid);
-                       } else{
-                               this.selectedNode.identifier = str;
+       setIdentifierForSelectedNode: function(str, destNode){
+               if(!destNode){
+                       if(this.selectedNode){
+                               if(this.srcMemoryDB){
+                                       this.srcMemoryDB.updateNode(str, this.selectedNode.typeid, this.selectedNode.nodeid);
+                               } else{
+                                       this.selectedNode.identifier = str;
+                               }
+                       }
+               } else{
+                       if(this.selectedNodeDestination){
+                               if(this.srcMemoryDB){
+                                       this.srcMemoryDB.updateNode(str, this.selectedNodeDestination.typeid, this.selectedNodeDestination.nodeid);
+                               } else{
+                                       this.selectedNodeDestination.identifier = str;
+                               }
                        }
                }
        },
@@ -545,9 +614,14 @@ MGNode.prototype = {
 
 function MGEdge(env, identifier, node0, node1){
        this.env = env;
+       //
        this.identifier = identifier;
        this.node0 = node0;
        this.node1 = node1;
+       //
+       this.edgeid = null;
+       this.typeid = null;
+       //
        this.freeLength = 250;
        //
        this.strokeStyle = "rgba(0, 0, 0, 0.5)";
@@ -563,8 +637,9 @@ MGEdge.prototype = {
                        this.env.context.strokeStyle = this.strokeStyle;
                }
                if(this.node0 && this.node1){
-                       this.drawCurvedLineP(this.node0.position, this.node1.position);
-                       this.env.strokeRect(this.centerPoint.x - 8, this.centerPoint.y - 8, 16, 16);
+                       //this.drawCurvedLineP(this.node0.position, this.node1.position);
+                       this.env.drawLineP(this.node0.position, this.node1.position)
+                       //this.env.strokeRect(this.centerPoint.x - 8, this.centerPoint.y - 8, 16, 16);
                        if(this.identifier){
                                this.env.drawText(this.identifier.toString(), this.centerPoint.x, this.centerPoint.y);
                        }