Conflicts: Jakefile.js examples/graph/index.html src/graph/Edge.js src/graph/Graph.js src/graph/graphMixins/ClusterMixin.js src/graph/graphMixins/SelectionMixin.js src/timeline/Controller.js src/timeline/Range.js src/timeline/Timeline.js src/timeline/component/ItemSet.js src/timeline/component/RootPanel.js src/timeline/component/item/Item.js src/util.jscss_transitions
| @ -0,0 +1 @@ | |||||
| visjs.org | |||||
| @ -0,0 +1,94 @@ | |||||
| #menu { | |||||
| position: absolute; | |||||
| left: -170px; | |||||
| top: 35px; | |||||
| background-color: #a7c8f9; | |||||
| padding: 15px; | |||||
| border-radius: 3px; | |||||
| } | |||||
| #forkme { | |||||
| position: fixed; | |||||
| top: 0; | |||||
| right: 0; | |||||
| border: 0; | |||||
| } | |||||
| div.nav { | |||||
| text-align: center; | |||||
| } | |||||
| div.nav ul { | |||||
| text-decoration: none; | |||||
| text-transform: uppercase; | |||||
| margin-bottom: 30px; | |||||
| padding-left: 0; | |||||
| } | |||||
| li.nav { | |||||
| } | |||||
| div.nav ul li { | |||||
| text-decoration: none; | |||||
| text-transform: uppercase; | |||||
| font-weight: normal; | |||||
| font-size: 11pt; | |||||
| list-style: none; | |||||
| margin-top: 5px; | |||||
| } | |||||
| div.nav ul li ul li { | |||||
| text-decoration: none; | |||||
| text-transform: none; | |||||
| font-weight: normal; | |||||
| font-size: 11pt; | |||||
| color: #4D4D4D; | |||||
| list-style: none; | |||||
| } | |||||
| div.nav a { | |||||
| color: #2B7CE9; | |||||
| color: white; | |||||
| font-weight: bold; | |||||
| } | |||||
| .subtitle { | |||||
| color: gray; | |||||
| text-transform: uppercase; | |||||
| font-size: 11pt; | |||||
| } | |||||
| .download td { | |||||
| border: none; | |||||
| padding: 5px 20px 5px 0; | |||||
| } | |||||
| .gallery .thumb { | |||||
| display: inline-block; | |||||
| text-align: center; | |||||
| margin-right: 10px; | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| .gallery .thumb img { | |||||
| border: 1px solid white; | |||||
| border-radius: 5px; | |||||
| height: 90px; | |||||
| margin: 0; | |||||
| } | |||||
| .gallery .thumb a:hover img { | |||||
| border-color: lightgray; | |||||
| } | |||||
| .gallery .thumb div { | |||||
| margin: 0; | |||||
| } | |||||
| img { | |||||
| border: 0; | |||||
| } | |||||
| @ -0,0 +1,214 @@ | |||||
| <!doctype html> | |||||
| <html> | |||||
| <head> | |||||
| <title>Graph | Navigation</title> | |||||
| <style type="text/css"> | |||||
| body { | |||||
| font: 10pt sans; | |||||
| } | |||||
| #mygraph { | |||||
| position:relative; | |||||
| width: 600px; | |||||
| height: 600px; | |||||
| border: 1px solid lightgray; | |||||
| } | |||||
| table.legend_table { | |||||
| font-size: 11px; | |||||
| border-width:1px; | |||||
| border-color:#d3d3d3; | |||||
| border-style:solid; | |||||
| } | |||||
| table.legend_table,td { | |||||
| border-width:1px; | |||||
| border-color:#d3d3d3; | |||||
| border-style:solid; | |||||
| padding: 2px; | |||||
| } | |||||
| div.table_content { | |||||
| width:80px; | |||||
| text-align:center; | |||||
| } | |||||
| div.table_description { | |||||
| width:100px; | |||||
| } | |||||
| #operation { | |||||
| font-size:28px; | |||||
| } | |||||
| #graph-popUp { | |||||
| display:none; | |||||
| position:absolute; | |||||
| top:350px; | |||||
| left:170px; | |||||
| z-index:299; | |||||
| width:250px; | |||||
| height:120px; | |||||
| background-color: #f9f9f9; | |||||
| border-style:solid; | |||||
| border-width:3px; | |||||
| border-color: #5394ed; | |||||
| padding:10px; | |||||
| text-align: center; | |||||
| } | |||||
| </style> | |||||
| <link type="text/css" rel="stylesheet" charset="UTF-8" href="../../dist/css/graph-manipulation.css"> | |||||
| <link type="text/css" rel="stylesheet" charset="UTF-8" href="../../dist/css/graph-navigation.css"> | |||||
| <script type="text/javascript" src="../../dist/vis.js"></script> | |||||
| <script type="text/javascript"> | |||||
| var nodes = null; | |||||
| var edges = null; | |||||
| var graph = null; | |||||
| function draw() { | |||||
| nodes = []; | |||||
| edges = []; | |||||
| var connectionCount = []; | |||||
| // randomly create some nodes and edges | |||||
| var nodeCount = 25; | |||||
| for (var i = 0; i < nodeCount; i++) { | |||||
| nodes.push({ | |||||
| id: i, | |||||
| label: String(i) | |||||
| }); | |||||
| connectionCount[i] = 0; | |||||
| // create edges in a scale-free-graph way | |||||
| if (i == 1) { | |||||
| var from = i; | |||||
| var to = 0; | |||||
| edges.push({ | |||||
| from: from, | |||||
| to: to | |||||
| }); | |||||
| connectionCount[from]++; | |||||
| connectionCount[to]++; | |||||
| } | |||||
| else if (i > 1) { | |||||
| var conn = edges.length * 2; | |||||
| var rand = Math.floor(Math.random() * conn); | |||||
| var cum = 0; | |||||
| var j = 0; | |||||
| while (j < connectionCount.length && cum < rand) { | |||||
| cum += connectionCount[j]; | |||||
| j++; | |||||
| } | |||||
| var from = i; | |||||
| var to = j; | |||||
| edges.push({ | |||||
| from: from, | |||||
| to: to | |||||
| }); | |||||
| connectionCount[from]++; | |||||
| connectionCount[to]++; | |||||
| } | |||||
| } | |||||
| // create a graph | |||||
| var container = document.getElementById('mygraph'); | |||||
| var data = { | |||||
| nodes: nodes, | |||||
| edges: edges | |||||
| }; | |||||
| var options = { | |||||
| edges: { | |||||
| length: 50 | |||||
| }, | |||||
| stabilize: false, | |||||
| dataManipulation: true, | |||||
| onAdd: function(data,callback) { | |||||
| var span = document.getElementById('operation'); | |||||
| var idInput = document.getElementById('node-id'); | |||||
| var labelInput = document.getElementById('node-label'); | |||||
| var saveButton = document.getElementById('saveButton'); | |||||
| var cancelButton = document.getElementById('cancelButton'); | |||||
| var div = document.getElementById('graph-popUp'); | |||||
| span.innerHTML = "Add Node"; | |||||
| idInput.value = data.id; | |||||
| labelInput.value = data.label; | |||||
| saveButton.onclick = saveData.bind(this,data,callback); | |||||
| cancelButton.onclick = clearPopUp.bind(); | |||||
| div.style.display = 'block'; | |||||
| }, | |||||
| onEdit: function(data,callback) { | |||||
| var span = document.getElementById('operation'); | |||||
| var idInput = document.getElementById('node-id'); | |||||
| var labelInput = document.getElementById('node-label'); | |||||
| var saveButton = document.getElementById('saveButton'); | |||||
| var cancelButton = document.getElementById('cancelButton'); | |||||
| var div = document.getElementById('graph-popUp'); | |||||
| span.innerHTML = "Edit Node"; | |||||
| idInput.value = data.id; | |||||
| labelInput.value = data.label; | |||||
| saveButton.onclick = saveData.bind(this,data,callback); | |||||
| cancelButton.onclick = clearPopUp.bind(); | |||||
| div.style.display = 'block'; | |||||
| } | |||||
| }; | |||||
| graph = new vis.Graph(container, data, options); | |||||
| // add event listeners | |||||
| graph.on('select', function(params) { | |||||
| document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes; | |||||
| }); | |||||
| graph.on("frameResize", function(params) {console.log(params.width,params.height)}); | |||||
| function clearPopUp() { | |||||
| var saveButton = document.getElementById('saveButton'); | |||||
| var cancelButton = document.getElementById('cancelButton'); | |||||
| saveButton.onclick = null; | |||||
| cancelButton.onclick = null; | |||||
| var div = document.getElementById('graph-popUp'); | |||||
| div.style.display = 'none'; | |||||
| } | |||||
| function saveData(data,callback) { | |||||
| var idInput = document.getElementById('node-id'); | |||||
| var labelInput = document.getElementById('node-label'); | |||||
| var div = document.getElementById('graph-popUp'); | |||||
| data.id = idInput.value; | |||||
| data.label = labelInput.value; | |||||
| clearPopUp(); | |||||
| callback(data); | |||||
| } | |||||
| } | |||||
| </script> | |||||
| </head> | |||||
| <body onload="draw();"> | |||||
| <h2>Editing the dataset</h2> | |||||
| <div style="width: 700px; font-size:14px;"> | |||||
| In this example we have enabled the data manipulation setting. If the dataManipulation option is set to true, the edit button will appear. | |||||
| If you prefer to have the toolbar visible initially, you can set the initiallyVisible option to true. The exact method is described in the docs. | |||||
| <br /><br /> | |||||
| The data manipulation allows the user to add nodes, connect them, edit them and delete any selected items. In this example we have created trigger functions | |||||
| for the add and edit operations. By settings these trigger functions the user can direct the way the data is manipulated. In this example we have created a simple | |||||
| pop-up that allows us to edit some of the properties. | |||||
| </div> | |||||
| <br /> | |||||
| <div id="graph-popUp"> | |||||
| <span id="operation">node</span> <br> | |||||
| <table style="margin:auto;"><tr> | |||||
| <td>id</td><td><input id="node-id" value="new value"></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td>label</td><td><input id="node-label" value="new value"> </td> | |||||
| </tr></table> | |||||
| <input type="button" value="save" id="saveButton"></button> | |||||
| <input type="button" value="cancel" id="cancelButton"></button> | |||||
| </div> | |||||
| <br /> | |||||
| <div id="mygraph"></div> | |||||
| <p id="selection"></p> | |||||
| </body> | |||||
| </html> | |||||
| @ -0,0 +1,373 @@ | |||||
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> | |||||
| <html> | |||||
| <head> | |||||
| <title>Graph | Multiline text</title> | |||||
| <style type="text/css"> | |||||
| #mygraph { | |||||
| width: 900px; | |||||
| height: 900px; | |||||
| border: 1px solid lightgray; | |||||
| } | |||||
| </style> | |||||
| <script type="text/javascript" src="../../dist/vis.js"></script> | |||||
| <script type="text/javascript"> | |||||
| function draw() { | |||||
| // create some nodes | |||||
| var nodes = [ | |||||
| {id:0,"labelIGNORE":"Myriel","group":1}, | |||||
| {id:1,"labelIGNORE":"Napoleon","group":1}, | |||||
| {id:2,"labelIGNORE":"Mlle.Baptistine","group":1}, | |||||
| {id:3,"labelIGNORE":"Mme.Magloire","group":1}, | |||||
| {id:4,"labelIGNORE":"CountessdeLo","group":1}, | |||||
| {id:5,"labelIGNORE":"Geborand","group":1}, | |||||
| {id:6,"labelIGNORE":"Champtercier","group":1}, | |||||
| {id:7,"labelIGNORE":"Cravatte","group":1}, | |||||
| {id:8,"labelIGNORE":"Count","group":1}, | |||||
| {id:9,"labelIGNORE":"OldMan","group":1}, | |||||
| {id:10,"labelIGNORE":"Labarre","group":2}, | |||||
| {id:11,"labelIGNORE":"Valjean","group":2}, | |||||
| {id:12,"labelIGNORE":"Marguerite","group":3}, | |||||
| {id:13,"labelIGNORE":"Mme.deR","group":2}, | |||||
| {id:14,"labelIGNORE":"Isabeau","group":2}, | |||||
| {id:15,"labelIGNORE":"Gervais","group":2}, | |||||
| {id:16,"labelIGNORE":"Tholomyes","group":3}, | |||||
| {id:17,"labelIGNORE":"Listolier","group":3}, | |||||
| {id:18,"labelIGNORE":"Fameuil","group":3}, | |||||
| {id:19,"labelIGNORE":"Blacheville","group":3}, | |||||
| {id:20,"labelIGNORE":"Favourite","group":3}, | |||||
| {id:21,"labelIGNORE":"Dahlia","group":3}, | |||||
| {id:22,"labelIGNORE":"Zephine","group":3}, | |||||
| {id:23,"labelIGNORE":"Fantine","group":3}, | |||||
| {id:24,"labelIGNORE":"Mme.Thenardier","group":4}, | |||||
| {id:25,"labelIGNORE":"Thenardier","group":4}, | |||||
| {id:26,"labelIGNORE":"Cosette","group":5}, | |||||
| {id:27,"labelIGNORE":"Javert","group":4}, | |||||
| {id:28,"labelIGNORE":"Fauchelevent","group":0}, | |||||
| {id:29,"labelIGNORE":"Bamatabois","group":2}, | |||||
| {id:30,"labelIGNORE":"Perpetue","group":3}, | |||||
| {id:31,"labelIGNORE":"Simplice","group":2}, | |||||
| {id:32,"labelIGNORE":"Scaufflaire","group":2}, | |||||
| {id:33,"labelIGNORE":"Woman1","group":2}, | |||||
| {id:34,"labelIGNORE":"Judge","group":2}, | |||||
| {id:35,"labelIGNORE":"Champmathieu","group":2}, | |||||
| {id:36,"labelIGNORE":"Brevet","group":2}, | |||||
| {id:37,"labelIGNORE":"Chenildieu","group":2}, | |||||
| {id:38,"labelIGNORE":"Cochepaille","group":2}, | |||||
| {id:39,"labelIGNORE":"Pontmercy","group":4}, | |||||
| {id:40,"labelIGNORE":"Boulatruelle","group":6}, | |||||
| {id:41,"labelIGNORE":"Eponine","group":4}, | |||||
| {id:42,"labelIGNORE":"Anzelma","group":4}, | |||||
| {id:43,"labelIGNORE":"Woman2","group":5}, | |||||
| {id:44,"labelIGNORE":"MotherInnocent","group":0}, | |||||
| {id:45,"labelIGNORE":"Gribier","group":0}, | |||||
| {id:46,"labelIGNORE":"Jondrette","group":7}, | |||||
| {id:47,"labelIGNORE":"Mme.Burgon","group":7}, | |||||
| {id:48,"labelIGNORE":"Gavroche","group":8}, | |||||
| {id:49,"labelIGNORE":"Gillenormand","group":5}, | |||||
| {id:50,"labelIGNORE":"Magnon","group":5}, | |||||
| {id:51,"labelIGNORE":"Mlle.Gillenormand","group":5}, | |||||
| {id:52,"labelIGNORE":"Mme.Pontmercy","group":5}, | |||||
| {id:53,"labelIGNORE":"Mlle.Vaubois","group":5}, | |||||
| {id:54,"labelIGNORE":"Lt.Gillenormand","group":5}, | |||||
| {id:55,"labelIGNORE":"Marius","group":8}, | |||||
| {id:56,"labelIGNORE":"BaronessT","group":5}, | |||||
| {id:57,"labelIGNORE":"Mabeuf","group":8}, | |||||
| {id:58,"labelIGNORE":"Enjolras","group":8}, | |||||
| {id:59,"labelIGNORE":"Combeferre","group":8}, | |||||
| {id:60,"labelIGNORE":"Prouvaire","group":8}, | |||||
| {id:61,"labelIGNORE":"Feuilly","group":8}, | |||||
| {id:62,"labelIGNORE":"Courfeyrac","group":8}, | |||||
| {id:63,"labelIGNORE":"Bahorel","group":8}, | |||||
| {id:64,"labelIGNORE":"Bossuet","group":8}, | |||||
| {id:65,"labelIGNORE":"Joly","group":8}, | |||||
| {id:66,"labelIGNORE":"Grantaire","group":8}, | |||||
| {id:67,"labelIGNORE":"MotherPlutarch","group":9}, | |||||
| {id:68,"labelIGNORE":"Gueulemer","group":4}, | |||||
| {id:69,"labelIGNORE":"Babet","group":4}, | |||||
| {id:70,"labelIGNORE":"Claquesous","group":4}, | |||||
| {id:71,"labelIGNORE":"Montparnasse","group":4}, | |||||
| {id:72,"labelIGNORE":"Toussaint","group":5}, | |||||
| {id:73,"labelIGNORE":"Child1","group":10}, | |||||
| {id:74,"labelIGNORE":"Child2","group":10}, | |||||
| {id:75,"labelIGNORE":"Brujon","group":4}, | |||||
| {id:76,"labelIGNORE":"Mme.Hucheloup","group":8} | |||||
| ]; | |||||
| // create some edges | |||||
| var edges = [ | |||||
| {"from":1,"to":0,"valueIGNORED":1}, | |||||
| {"from":2,"to":0,"valueIGNORED":8}, | |||||
| {"from":3,"to":0,"valueIGNORED":10}, | |||||
| {"from":3,"to":2,"valueIGNORED":6}, | |||||
| {"from":4,"to":0,"valueIGNORED":1}, | |||||
| {"from":5,"to":0,"valueIGNORED":1}, | |||||
| {"from":6,"to":0,"valueIGNORED":1}, | |||||
| {"from":7,"to":0,"valueIGNORED":1}, | |||||
| {"from":8,"to":0,"valueIGNORED":2}, | |||||
| {"from":9,"to":0,"valueIGNORED":1}, | |||||
| {"from":11,"to":10,"valueIGNORED":1}, | |||||
| {"from":11,"to":3,"valueIGNORED":3}, | |||||
| {"from":11,"to":2,"valueIGNORED":3}, | |||||
| {"from":11,"to":0,"valueIGNORED":5}, | |||||
| {"from":12,"to":11,"valueIGNORED":1}, | |||||
| {"from":13,"to":11,"valueIGNORED":1}, | |||||
| {"from":14,"to":11,"valueIGNORED":1}, | |||||
| {"from":15,"to":11,"valueIGNORED":1}, | |||||
| {"from":17,"to":16,"valueIGNORED":4}, | |||||
| {"from":18,"to":16,"valueIGNORED":4}, | |||||
| {"from":18,"to":17,"valueIGNORED":4}, | |||||
| {"from":19,"to":16,"valueIGNORED":4}, | |||||
| {"from":19,"to":17,"valueIGNORED":4}, | |||||
| {"from":19,"to":18,"valueIGNORED":4}, | |||||
| {"from":20,"to":16,"valueIGNORED":3}, | |||||
| {"from":20,"to":17,"valueIGNORED":3}, | |||||
| {"from":20,"to":18,"valueIGNORED":3}, | |||||
| {"from":20,"to":19,"valueIGNORED":4}, | |||||
| {"from":21,"to":16,"valueIGNORED":3}, | |||||
| {"from":21,"to":17,"valueIGNORED":3}, | |||||
| {"from":21,"to":18,"valueIGNORED":3}, | |||||
| {"from":21,"to":19,"valueIGNORED":3}, | |||||
| {"from":21,"to":20,"valueIGNORED":5}, | |||||
| {"from":22,"to":16,"valueIGNORED":3}, | |||||
| {"from":22,"to":17,"valueIGNORED":3}, | |||||
| {"from":22,"to":18,"valueIGNORED":3}, | |||||
| {"from":22,"to":19,"valueIGNORED":3}, | |||||
| {"from":22,"to":20,"valueIGNORED":4}, | |||||
| {"from":22,"to":21,"valueIGNORED":4}, | |||||
| {"from":23,"to":16,"valueIGNORED":3}, | |||||
| {"from":23,"to":17,"valueIGNORED":3}, | |||||
| {"from":23,"to":18,"valueIGNORED":3}, | |||||
| {"from":23,"to":19,"valueIGNORED":3}, | |||||
| {"from":23,"to":20,"valueIGNORED":4}, | |||||
| {"from":23,"to":21,"valueIGNORED":4}, | |||||
| {"from":23,"to":22,"valueIGNORED":4}, | |||||
| {"from":23,"to":12,"valueIGNORED":2}, | |||||
| {"from":23,"to":11,"valueIGNORED":9}, | |||||
| {"from":24,"to":23,"valueIGNORED":2}, | |||||
| {"from":24,"to":11,"valueIGNORED":7}, | |||||
| {"from":25,"to":24,"valueIGNORED":13}, | |||||
| {"from":25,"to":23,"valueIGNORED":1}, | |||||
| {"from":25,"to":11,"valueIGNORED":12}, | |||||
| {"from":26,"to":24,"valueIGNORED":4}, | |||||
| {"from":26,"to":11,"valueIGNORED":31}, | |||||
| {"from":26,"to":16,"valueIGNORED":1}, | |||||
| {"from":26,"to":25,"valueIGNORED":1}, | |||||
| {"from":27,"to":11,"valueIGNORED":17}, | |||||
| {"from":27,"to":23,"valueIGNORED":5}, | |||||
| {"from":27,"to":25,"valueIGNORED":5}, | |||||
| {"from":27,"to":24,"valueIGNORED":1}, | |||||
| {"from":27,"to":26,"valueIGNORED":1}, | |||||
| {"from":28,"to":11,"valueIGNORED":8}, | |||||
| {"from":28,"to":27,"valueIGNORED":1}, | |||||
| {"from":29,"to":23,"valueIGNORED":1}, | |||||
| {"from":29,"to":27,"valueIGNORED":1}, | |||||
| {"from":29,"to":11,"valueIGNORED":2}, | |||||
| {"from":30,"to":23,"valueIGNORED":1}, | |||||
| {"from":31,"to":30,"valueIGNORED":2}, | |||||
| {"from":31,"to":11,"valueIGNORED":3}, | |||||
| {"from":31,"to":23,"valueIGNORED":2}, | |||||
| {"from":31,"to":27,"valueIGNORED":1}, | |||||
| {"from":32,"to":11,"valueIGNORED":1}, | |||||
| {"from":33,"to":11,"valueIGNORED":2}, | |||||
| {"from":33,"to":27,"valueIGNORED":1}, | |||||
| {"from":34,"to":11,"valueIGNORED":3}, | |||||
| {"from":34,"to":29,"valueIGNORED":2}, | |||||
| {"from":35,"to":11,"valueIGNORED":3}, | |||||
| {"from":35,"to":34,"valueIGNORED":3}, | |||||
| {"from":35,"to":29,"valueIGNORED":2}, | |||||
| {"from":36,"to":34,"valueIGNORED":2}, | |||||
| {"from":36,"to":35,"valueIGNORED":2}, | |||||
| {"from":36,"to":11,"valueIGNORED":2}, | |||||
| {"from":36,"to":29,"valueIGNORED":1}, | |||||
| {"from":37,"to":34,"valueIGNORED":2}, | |||||
| {"from":37,"to":35,"valueIGNORED":2}, | |||||
| {"from":37,"to":36,"valueIGNORED":2}, | |||||
| {"from":37,"to":11,"valueIGNORED":2}, | |||||
| {"from":37,"to":29,"valueIGNORED":1}, | |||||
| {"from":38,"to":34,"valueIGNORED":2}, | |||||
| {"from":38,"to":35,"valueIGNORED":2}, | |||||
| {"from":38,"to":36,"valueIGNORED":2}, | |||||
| {"from":38,"to":37,"valueIGNORED":2}, | |||||
| {"from":38,"to":11,"valueIGNORED":2}, | |||||
| {"from":38,"to":29,"valueIGNORED":1}, | |||||
| {"from":39,"to":25,"valueIGNORED":1}, | |||||
| {"from":40,"to":25,"valueIGNORED":1}, | |||||
| {"from":41,"to":24,"valueIGNORED":2}, | |||||
| {"from":41,"to":25,"valueIGNORED":3}, | |||||
| {"from":42,"to":41,"valueIGNORED":2}, | |||||
| {"from":42,"to":25,"valueIGNORED":2}, | |||||
| {"from":42,"to":24,"valueIGNORED":1}, | |||||
| {"from":43,"to":11,"valueIGNORED":3}, | |||||
| {"from":43,"to":26,"valueIGNORED":1}, | |||||
| {"from":43,"to":27,"valueIGNORED":1}, | |||||
| {"from":44,"to":28,"valueIGNORED":3}, | |||||
| {"from":44,"to":11,"valueIGNORED":1}, | |||||
| {"from":45,"to":28,"valueIGNORED":2}, | |||||
| {"from":47,"to":46,"valueIGNORED":1}, | |||||
| {"from":48,"to":47,"valueIGNORED":2}, | |||||
| {"from":48,"to":25,"valueIGNORED":1}, | |||||
| {"from":48,"to":27,"valueIGNORED":1}, | |||||
| {"from":48,"to":11,"valueIGNORED":1}, | |||||
| {"from":49,"to":26,"valueIGNORED":3}, | |||||
| {"from":49,"to":11,"valueIGNORED":2}, | |||||
| {"from":50,"to":49,"valueIGNORED":1}, | |||||
| {"from":50,"to":24,"valueIGNORED":1}, | |||||
| {"from":51,"to":49,"valueIGNORED":9}, | |||||
| {"from":51,"to":26,"valueIGNORED":2}, | |||||
| {"from":51,"to":11,"valueIGNORED":2}, | |||||
| {"from":52,"to":51,"valueIGNORED":1}, | |||||
| {"from":52,"to":39,"valueIGNORED":1}, | |||||
| {"from":53,"to":51,"valueIGNORED":1}, | |||||
| {"from":54,"to":51,"valueIGNORED":2}, | |||||
| {"from":54,"to":49,"valueIGNORED":1}, | |||||
| {"from":54,"to":26,"valueIGNORED":1}, | |||||
| {"from":55,"to":51,"valueIGNORED":6}, | |||||
| {"from":55,"to":49,"valueIGNORED":12}, | |||||
| {"from":55,"to":39,"valueIGNORED":1}, | |||||
| {"from":55,"to":54,"valueIGNORED":1}, | |||||
| {"from":55,"to":26,"valueIGNORED":21}, | |||||
| {"from":55,"to":11,"valueIGNORED":19}, | |||||
| {"from":55,"to":16,"valueIGNORED":1}, | |||||
| {"from":55,"to":25,"valueIGNORED":2}, | |||||
| {"from":55,"to":41,"valueIGNORED":5}, | |||||
| {"from":55,"to":48,"valueIGNORED":4}, | |||||
| {"from":56,"to":49,"valueIGNORED":1}, | |||||
| {"from":56,"to":55,"valueIGNORED":1}, | |||||
| {"from":57,"to":55,"valueIGNORED":1}, | |||||
| {"from":57,"to":41,"valueIGNORED":1}, | |||||
| {"from":57,"to":48,"valueIGNORED":1}, | |||||
| {"from":58,"to":55,"valueIGNORED":7}, | |||||
| {"from":58,"to":48,"valueIGNORED":7}, | |||||
| {"from":58,"to":27,"valueIGNORED":6}, | |||||
| {"from":58,"to":57,"valueIGNORED":1}, | |||||
| {"from":58,"to":11,"valueIGNORED":4}, | |||||
| {"from":59,"to":58,"valueIGNORED":15}, | |||||
| {"from":59,"to":55,"valueIGNORED":5}, | |||||
| {"from":59,"to":48,"valueIGNORED":6}, | |||||
| {"from":59,"to":57,"valueIGNORED":2}, | |||||
| {"from":60,"to":48,"valueIGNORED":1}, | |||||
| {"from":60,"to":58,"valueIGNORED":4}, | |||||
| {"from":60,"to":59,"valueIGNORED":2}, | |||||
| {"from":61,"to":48,"valueIGNORED":2}, | |||||
| {"from":61,"to":58,"valueIGNORED":6}, | |||||
| {"from":61,"to":60,"valueIGNORED":2}, | |||||
| {"from":61,"to":59,"valueIGNORED":5}, | |||||
| {"from":61,"to":57,"valueIGNORED":1}, | |||||
| {"from":61,"to":55,"valueIGNORED":1}, | |||||
| {"from":62,"to":55,"valueIGNORED":9}, | |||||
| {"from":62,"to":58,"valueIGNORED":17}, | |||||
| {"from":62,"to":59,"valueIGNORED":13}, | |||||
| {"from":62,"to":48,"valueIGNORED":7}, | |||||
| {"from":62,"to":57,"valueIGNORED":2}, | |||||
| {"from":62,"to":41,"valueIGNORED":1}, | |||||
| {"from":62,"to":61,"valueIGNORED":6}, | |||||
| {"from":62,"to":60,"valueIGNORED":3}, | |||||
| {"from":63,"to":59,"valueIGNORED":5}, | |||||
| {"from":63,"to":48,"valueIGNORED":5}, | |||||
| {"from":63,"to":62,"valueIGNORED":6}, | |||||
| {"from":63,"to":57,"valueIGNORED":2}, | |||||
| {"from":63,"to":58,"valueIGNORED":4}, | |||||
| {"from":63,"to":61,"valueIGNORED":3}, | |||||
| {"from":63,"to":60,"valueIGNORED":2}, | |||||
| {"from":63,"to":55,"valueIGNORED":1}, | |||||
| {"from":64,"to":55,"valueIGNORED":5}, | |||||
| {"from":64,"to":62,"valueIGNORED":12}, | |||||
| {"from":64,"to":48,"valueIGNORED":5}, | |||||
| {"from":64,"to":63,"valueIGNORED":4}, | |||||
| {"from":64,"to":58,"valueIGNORED":10}, | |||||
| {"from":64,"to":61,"valueIGNORED":6}, | |||||
| {"from":64,"to":60,"valueIGNORED":2}, | |||||
| {"from":64,"to":59,"valueIGNORED":9}, | |||||
| {"from":64,"to":57,"valueIGNORED":1}, | |||||
| {"from":64,"to":11,"valueIGNORED":1}, | |||||
| {"from":65,"to":63,"valueIGNORED":5}, | |||||
| {"from":65,"to":64,"valueIGNORED":7}, | |||||
| {"from":65,"to":48,"valueIGNORED":3}, | |||||
| {"from":65,"to":62,"valueIGNORED":5}, | |||||
| {"from":65,"to":58,"valueIGNORED":5}, | |||||
| {"from":65,"to":61,"valueIGNORED":5}, | |||||
| {"from":65,"to":60,"valueIGNORED":2}, | |||||
| {"from":65,"to":59,"valueIGNORED":5}, | |||||
| {"from":65,"to":57,"valueIGNORED":1}, | |||||
| {"from":65,"to":55,"valueIGNORED":2}, | |||||
| {"from":66,"to":64,"valueIGNORED":3}, | |||||
| {"from":66,"to":58,"valueIGNORED":3}, | |||||
| {"from":66,"to":59,"valueIGNORED":1}, | |||||
| {"from":66,"to":62,"valueIGNORED":2}, | |||||
| {"from":66,"to":65,"valueIGNORED":2}, | |||||
| {"from":66,"to":48,"valueIGNORED":1}, | |||||
| {"from":66,"to":63,"valueIGNORED":1}, | |||||
| {"from":66,"to":61,"valueIGNORED":1}, | |||||
| {"from":66,"to":60,"valueIGNORED":1}, | |||||
| {"from":67,"to":57,"valueIGNORED":3}, | |||||
| {"from":68,"to":25,"valueIGNORED":5}, | |||||
| {"from":68,"to":11,"valueIGNORED":1}, | |||||
| {"from":68,"to":24,"valueIGNORED":1}, | |||||
| {"from":68,"to":27,"valueIGNORED":1}, | |||||
| {"from":68,"to":48,"valueIGNORED":1}, | |||||
| {"from":68,"to":41,"valueIGNORED":1}, | |||||
| {"from":69,"to":25,"valueIGNORED":6}, | |||||
| {"from":69,"to":68,"valueIGNORED":6}, | |||||
| {"from":69,"to":11,"valueIGNORED":1}, | |||||
| {"from":69,"to":24,"valueIGNORED":1}, | |||||
| {"from":69,"to":27,"valueIGNORED":2}, | |||||
| {"from":69,"to":48,"valueIGNORED":1}, | |||||
| {"from":69,"to":41,"valueIGNORED":1}, | |||||
| {"from":70,"to":25,"valueIGNORED":4}, | |||||
| {"from":70,"to":69,"valueIGNORED":4}, | |||||
| {"from":70,"to":68,"valueIGNORED":4}, | |||||
| {"from":70,"to":11,"valueIGNORED":1}, | |||||
| {"from":70,"to":24,"valueIGNORED":1}, | |||||
| {"from":70,"to":27,"valueIGNORED":1}, | |||||
| {"from":70,"to":41,"valueIGNORED":1}, | |||||
| {"from":70,"to":58,"valueIGNORED":1}, | |||||
| {"from":71,"to":27,"valueIGNORED":1}, | |||||
| {"from":71,"to":69,"valueIGNORED":2}, | |||||
| {"from":71,"to":68,"valueIGNORED":2}, | |||||
| {"from":71,"to":70,"valueIGNORED":2}, | |||||
| {"from":71,"to":11,"valueIGNORED":1}, | |||||
| {"from":71,"to":48,"valueIGNORED":1}, | |||||
| {"from":71,"to":41,"valueIGNORED":1}, | |||||
| {"from":71,"to":25,"valueIGNORED":1}, | |||||
| {"from":72,"to":26,"valueIGNORED":2}, | |||||
| {"from":72,"to":27,"valueIGNORED":1}, | |||||
| {"from":72,"to":11,"valueIGNORED":1}, | |||||
| {"from":73,"to":48,"valueIGNORED":2}, | |||||
| {"from":74,"to":48,"valueIGNORED":2}, | |||||
| {"from":74,"to":73,"valueIGNORED":3}, | |||||
| {"from":75,"to":69,"valueIGNORED":3}, | |||||
| {"from":75,"to":68,"valueIGNORED":3}, | |||||
| {"from":75,"to":25,"valueIGNORED":3}, | |||||
| {"from":75,"to":48,"valueIGNORED":1}, | |||||
| {"from":75,"to":41,"valueIGNORED":1}, | |||||
| {"from":75,"to":70,"valueIGNORED":1}, | |||||
| {"from":75,"to":71,"valueIGNORED":1}, | |||||
| {"from":76,"to":64,"valueIGNORED":1}, | |||||
| {"from":76,"to":65,"valueIGNORED":1}, | |||||
| {"from":76,"to":66,"valueIGNORED":1}, | |||||
| {"from":76,"to":63,"valueIGNORED":1}, | |||||
| {"from":76,"to":62,"valueIGNORED":1}, | |||||
| {"from":76,"to":48,"valueIGNORED":1}, | |||||
| {"from":76,"to":58,"valueIGNORED":1} | |||||
| ]; | |||||
| // create a graph | |||||
| var container = document.getElementById('mygraph'); | |||||
| var data = { | |||||
| nodes: nodes, | |||||
| edges: edges | |||||
| }; | |||||
| var options = {nodes: {shape:'circle'},stabilize: false}; | |||||
| var graph = new vis.Graph(container, data, options); | |||||
| } | |||||
| </script> | |||||
| </head> | |||||
| <body onload="draw()"> | |||||
| <div id="mygraph"></div> | |||||
| </body> | |||||
| </html> | |||||
| @ -0,0 +1,40 @@ | |||||
| /**************************************\ | |||||
| Shapes4FREE License | |||||
| http://www.shapes4free.com/ - visit us to get free photoshop shapes, read our easy-to-understand shapes tutorials and tips, and view beautiful examples of using shapes in all kinds of design | |||||
| More about the license: http://www.shapes4free.com/license/ | |||||
| \**************************************/ | |||||
| This resource was created by Oksana Khristenko | |||||
| This resource has been downloaded from Shapes4FREE.com and is free for personal or commercial projects. You may use it for web and print design. | |||||
| No attribution or backlinks are required, but we would certainly appreciate it if you bookmarked www.shapes4free.com and shared the link to it with your friends: | |||||
| www.shapes4free.com - free photoshop shapes | |||||
| You may not resell or distribute this resource. Uploading it to another website | |||||
| or offering them for download on another website is not allowed. If you would like to feature this resource on | |||||
| your website or share them with friends, do not link directly to the resource files, | |||||
| please link to the appropriate page on Shapes4FREE.com where it is possible to download the freebie. | |||||
| /**************************************\ | |||||
| Shapes4FREE Ëèöåíçèÿ | |||||
| http://www.shapes4free.com/ - áåñïëàòíûå ôèãóðû äëÿ Ôîòîøîïà, óðîêè è ïîäñêàçêè, à òàêæå êðàñèâûå ïðèìåðû èñïîëüçîâàíèÿ ôèãóð âî âñåõ âèäàõ äèçàéíà | |||||
| Ëèöåíçèÿ: http://www.shapes4free.com/license-ru/ | |||||
| \**************************************/ | |||||
| Àâòîð: Îêñàíà Õðèñòåíêî | |||||
| Âñå áåñïëàòíûå ðåñóðñû êîòîðûå ìîæíî ñêà÷àòü íà Shapes4FREE.com, âêëþ÷àÿ ïðîèçâîëüíûå ôèãóðû äëÿ Ôîòîøîïà | |||||
| (photoshop custom shapes) áåñïëàòíû äëÿ èñïîëüçîâàíèÿ â ëè÷íûõ è êîììåð÷åñêèõ ïðîåêòàõ. Ðàçðåøåíî èñïîëüçîâàòü | |||||
| áåñïëàòíûå ðåñóðñû Shapes4FREE â âåá äèçàéíå è ïå÷àòíûõ ìàòåðèàëàõ. | |||||
| Ññûëêà íà ñàéò Shapes4FREE.com íå òðåáóåòñÿ íî ïðèâåòñòâóåòñÿ. Ìû áóäåì ðàäû åñëè âû ðàññêàæåòå î íàñ äðóçüÿì: | |||||
| www.shapes4free.com - áåñïëàòíûå ôèãóðû äëÿ Ôîòîøîïà | |||||
| Çàïðåùåíî ïðîäàâàòü èëè ðàñïðîñòðàíÿòü áåñïëàòíûå ðåñóðñû ñîçäàííûå Shapes4FREE. | |||||
| Çàïðåùåíî çàãðóæàòü èõ íà äðóãèå ñàéòû è ïîçâîëÿòü ïîëüçîâàòåëÿì èõ ñêà÷èâàòü. Åñëè âû õîòèòå ðàññêàçàòü î íàøåì | |||||
| áåñïëàòíîì ðåñóðñå íà ñàéòå èëè ïîäåëèòüñÿ ñ äðóçüÿìè, íå ñîçäàâàéòå ïðÿìûõ ññûëîê íà ôàéë, ñîçäàéòå ññûëêó íà | |||||
| ñîîòâåòñòâóþùóþ ñòðàíèöó ñàéòà Shapes4FREE.com ãäå ìîæíî áóäåò ñêà÷àòü ýòîò ðåñóðñ. | |||||
| @ -0,0 +1,169 @@ | |||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | |||||
| <svg | |||||
| xmlns:dc="http://purl.org/dc/elements/1.1/" | |||||
| xmlns:cc="http://creativecommons.org/ns#" | |||||
| xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||||
| xmlns:svg="http://www.w3.org/2000/svg" | |||||
| xmlns="http://www.w3.org/2000/svg" | |||||
| xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||||
| xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||||
| width="128" | |||||
| height="128" | |||||
| id="svg2" | |||||
| version="1.1" | |||||
| inkscape:version="0.48.3.1 r9886" | |||||
| sodipodi:docname="vis.svg" | |||||
| inkscape:export-filename="/home/jos/projects/vis-pages/img/logo/vis256.png" | |||||
| inkscape:export-xdpi="180" | |||||
| inkscape:export-ydpi="180"> | |||||
| <defs | |||||
| id="defs4"> | |||||
| <filter | |||||
| inkscape:collect="always" | |||||
| id="filter3765"> | |||||
| <feGaussianBlur | |||||
| inkscape:collect="always" | |||||
| stdDeviation="0.098994946" | |||||
| id="feGaussianBlur3767" /> | |||||
| </filter> | |||||
| <filter | |||||
| color-interpolation-filters="sRGB" | |||||
| inkscape:collect="always" | |||||
| id="filter3765-8"> | |||||
| <feGaussianBlur | |||||
| inkscape:collect="always" | |||||
| stdDeviation="0.098994946" | |||||
| id="feGaussianBlur3767-2" /> | |||||
| </filter> | |||||
| <filter | |||||
| color-interpolation-filters="sRGB" | |||||
| inkscape:collect="always" | |||||
| id="filter3765-8-3"> | |||||
| <feGaussianBlur | |||||
| inkscape:collect="always" | |||||
| stdDeviation="0.098994946" | |||||
| id="feGaussianBlur3767-2-9" /> | |||||
| </filter> | |||||
| <filter | |||||
| color-interpolation-filters="sRGB" | |||||
| inkscape:collect="always" | |||||
| id="filter3765-3"> | |||||
| <feGaussianBlur | |||||
| inkscape:collect="always" | |||||
| stdDeviation="0.098994946" | |||||
| id="feGaussianBlur3767-3" /> | |||||
| </filter> | |||||
| </defs> | |||||
| <sodipodi:namedview | |||||
| id="base" | |||||
| pagecolor="#ffffff" | |||||
| bordercolor="#666666" | |||||
| borderopacity="1.0" | |||||
| inkscape:pageopacity="0.0" | |||||
| inkscape:pageshadow="2" | |||||
| inkscape:zoom="2.8284271" | |||||
| inkscape:cx="13.788283" | |||||
| inkscape:cy="60.335199" | |||||
| inkscape:document-units="px" | |||||
| inkscape:current-layer="layer1" | |||||
| showgrid="false" | |||||
| inkscape:snap-global="true" | |||||
| inkscape:window-width="1600" | |||||
| inkscape:window-height="850" | |||||
| inkscape:window-x="0" | |||||
| inkscape:window-y="0" | |||||
| inkscape:window-maximized="1" | |||||
| inkscape:showpageshadow="false" | |||||
| borderlayer="false" | |||||
| showborder="true"> | |||||
| <inkscape:grid | |||||
| type="xygrid" | |||||
| id="grid2987" | |||||
| empspacing="5" | |||||
| visible="true" | |||||
| enabled="true" | |||||
| snapvisiblegridlinesonly="true" /> | |||||
| </sodipodi:namedview> | |||||
| <metadata | |||||
| id="metadata7"> | |||||
| <rdf:RDF> | |||||
| <cc:Work | |||||
| rdf:about=""> | |||||
| <dc:format>image/svg+xml</dc:format> | |||||
| <dc:type | |||||
| rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||||
| <dc:title></dc:title> | |||||
| </cc:Work> | |||||
| </rdf:RDF> | |||||
| </metadata> | |||||
| <g | |||||
| inkscape:label="Layer 1" | |||||
| inkscape:groupmode="layer" | |||||
| id="layer1" | |||||
| transform="translate(0,-924.36217)"> | |||||
| <path | |||||
| sodipodi:type="arc" | |||||
| style="fill:#109618;fill-opacity:1;stroke:none;opacity:0.01587302" | |||||
| id="path4104" | |||||
| sodipodi:cx="64.25" | |||||
| sodipodi:cy="64.5" | |||||
| sodipodi:rx="63.75" | |||||
| sodipodi:ry="64" | |||||
| d="m 128,64.5 a 63.75,64 0 1 1 -127.5,0 63.75,64 0 1 1 127.5,0 z" | |||||
| transform="matrix(0.93139646,0,0,0.83984375,-189.81956,958.94835)" /> | |||||
| <path | |||||
| style="fill:#ffcf00;fill-opacity:1;stroke:#ffcf00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" | |||||
| d="M 3.3795626,993.2579 C 22.563164,969.55671 64.941101,938.83212 101.8906,932.98778 c 15.01437,32.97153 15.80482,65.24066 11.89745,97.27812 -53.04371,-1.7716 -84.489701,-14.912 -110.4084874,-37.008 z" | |||||
| id="path2991" | |||||
| inkscape:connector-curvature="0" | |||||
| sodipodi:nodetypes="cccc" /> | |||||
| <path | |||||
| style="fill:#109618;fill-opacity:1;stroke:#109618;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" | |||||
| d="m 4.586161,1010.1966 c 29.519439,19.0948 61.669847,32.8593 114.396079,29.9232 6.98138,-4.9614 5.91066,-11.2031 3.61936,-17.6388 -37.656342,4.9772 -79.514689,1.6063 -118.015439,-12.2844 z" | |||||
| id="path3761" | |||||
| inkscape:connector-curvature="0" | |||||
| sodipodi:nodetypes="cccc" /> | |||||
| <path | |||||
| sodipodi:type="arc" | |||||
| style="fill:#ff9900;fill-opacity:1;stroke:none;filter:url(#filter3765-8)" | |||||
| id="path3763-6" | |||||
| sodipodi:cx="66.998367" | |||||
| sodipodi:cy="63.476505" | |||||
| sodipodi:rx="10.429825" | |||||
| sodipodi:ry="9.3691645" | |||||
| d="m 77.428192,63.476505 a 10.429825,9.3691645 0 1 1 -20.85965,0 10.429825,9.3691645 0 1 1 20.85965,0 z" | |||||
| transform="matrix(3.7791753,0,0,4.159068,-180.52628,715.8869)" /> | |||||
| <path | |||||
| sodipodi:type="arc" | |||||
| style="fill:#dc3912;fill-opacity:1;stroke:none;filter:url(#filter3765-8-3)" | |||||
| id="path3763-6-3" | |||||
| sodipodi:cx="66.998367" | |||||
| sodipodi:cy="63.476505" | |||||
| sodipodi:rx="10.429825" | |||||
| sodipodi:ry="9.3691645" | |||||
| d="m 77.428192,63.476505 a 10.429825,9.3691645 0 1 1 -20.85965,0 10.429825,9.3691645 0 1 1 20.85965,0 z" | |||||
| transform="matrix(2.1560756,0,0,2.3930971,-65.92105,839.4416)" /> | |||||
| <path | |||||
| sodipodi:type="arc" | |||||
| style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3765)" | |||||
| id="path3763" | |||||
| sodipodi:cx="66.998367" | |||||
| sodipodi:cy="63.476505" | |||||
| sodipodi:rx="10.429825" | |||||
| sodipodi:ry="9.3691645" | |||||
| d="m 77.428192,63.476505 a 10.429825,9.3691645 0 1 1 -20.85965,0 10.429825,9.3691645 0 1 1 20.85965,0 z" | |||||
| transform="matrix(1.369772,0,0,1.5215787,-11.897888,900.22297)" /> | |||||
| <path | |||||
| sodipodi:type="arc" | |||||
| style="fill:#4d4d4d;fill-opacity:1;stroke:none;filter:url(#filter3765-3)" | |||||
| id="path3763-9" | |||||
| sodipodi:cx="66.998367" | |||||
| sodipodi:cy="63.476505" | |||||
| sodipodi:rx="10.429825" | |||||
| sodipodi:ry="9.3691645" | |||||
| d="m 77.428192,63.476505 a 10.429825,9.3691645 0 1 1 -20.85965,0 10.429825,9.3691645 0 1 1 20.85965,0 z" | |||||
| transform="matrix(0.68506744,0,0,0.76063592,38.273417,952.729)" /> | |||||
| </g> | |||||
| </svg> | |||||
| @ -0,0 +1,317 @@ | |||||
| <!doctype html> | |||||
| <html> | |||||
| <head> | |||||
| <title>vis.js | a dynamic, browser-based visualization library</title> | |||||
| <meta charset='utf-8' /> | |||||
| <meta name="title" content="vis.js"> | |||||
| <meta name="description" content="vis.js is a dynamic, browser-based visualization library" /> | |||||
| <meta name="keywords" content="vis, visualization, javascript, browser based, web based, chart, linechart, timeline, graph, network, browser" /> | |||||
| <meta name="author" content="Almende B.V."> | |||||
| <link href="docs/css/prettify.css" type="text/css" rel="stylesheet" /> | |||||
| <link href='docs/css/style.css' type='text/css' rel='stylesheet'> | |||||
| <link href="css/style.css" type="text/css" rel="stylesheet" > | |||||
| <script type="text/javascript" src="docs/lib/prettify/prettify.js"></script> | |||||
| </head> | |||||
| <body onload="prettyPrint();"> | |||||
| <div id="container"> | |||||
| <div id="menu"> | |||||
| <a href="http://visjs.org/"><img src="img/logo/vis128.png" alt="logo"></a> | |||||
| <div class="nav"> | |||||
| <ul> | |||||
| <li><a href="#install">Install</a></li> | |||||
| <li><a href="#example">Example</a></li> | |||||
| <li><a href="#gallery">Gallery</a></li> | |||||
| <li> | |||||
| <a href="docs/index.html" target="_blank"> | |||||
| Docs | |||||
| <img src="img/external-link-icons/external-link-icon.png" style="vertical-align: text-top;" title="Docs will open in a new window"> | |||||
| </a> | |||||
| </li> | |||||
| <li><a href="#license">License</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| </div> | |||||
| <h1> | |||||
| vis.js<br> | |||||
| <span class="subtitle">a visual interaction system</span> | |||||
| </h1> | |||||
| <p> | |||||
| Vis.js is a dynamic, browser based visualization library. | |||||
| The library is designed to be easy to use, to handle large amounts | |||||
| of dynamic data, and to enable manipulation of and interaction with the data. | |||||
| The library consists of the components DataSet, Timeline, and Graph. | |||||
| </p> | |||||
| <p> | |||||
| The vis.js library is developed by <a href="http://almende.com" target="_blank">Almende B.V</a>, | |||||
| as part of the <a href="http://chap.almende.com/" target="_blank">CHAP</a>. | |||||
| </p> | |||||
| <h2 id="install">Install</h2> | |||||
| <h3>npm</h3> | |||||
| <pre class="prettyprint"> | |||||
| npm install vis | |||||
| </pre> | |||||
| <h3>bower</h3> | |||||
| <pre class="prettyprint"> | |||||
| bower install vis | |||||
| </pre> | |||||
| <h3>download</h3> | |||||
| <a href="download/vis.zip">Click here to download vis.js</a> | |||||
| (version <span class="version">0.4.0</span>) | |||||
| <h2 id="example">Example</h2> | |||||
| <p> | |||||
| A basic example demonstrating how to use the vis.js timeline is shown below. | |||||
| See the <a href="#gallery">gallery</a> below for more examples. | |||||
| </p> | |||||
| <pre class="prettyprint lang-html"><!doctype html> | |||||
| <html> | |||||
| <head> | |||||
| <title>Timeline | Basic demo</title> | |||||
| <script src="http://visjs.org/dist/vis.js"></script> | |||||
| <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" /> | |||||
| <style type="text/css"> | |||||
| body, html { | |||||
| font-family: sans-serif; | |||||
| } | |||||
| </style> | |||||
| </head> | |||||
| <body> | |||||
| <div id="mytimeline"></div> | |||||
| <script type="text/javascript"> | |||||
| var container = document.getElementById('mytimeline'); | |||||
| var data = [ | |||||
| {id: 1, content: 'item 1', start: '2013-04-20'}, | |||||
| {id: 2, content: 'item 2', start: '2013-04-14'}, | |||||
| {id: 3, content: 'item 3', start: '2013-04-18'}, | |||||
| {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'}, | |||||
| {id: 5, content: 'item 5', start: '2013-04-25'}, | |||||
| {id: 6, content: 'item 6', start: '2013-04-27'} | |||||
| ]; | |||||
| var options = {}; | |||||
| var timeline = new vis.Timeline(container, data, options); | |||||
| </script> | |||||
| </body> | |||||
| </html> | |||||
| </pre> | |||||
| <h2 id="gallery">Gallery</h2> | |||||
| This gallery gives an idea of the features and possibilities of the library. | |||||
| The source code of the examples can be found in the | |||||
| <a href="https://github.com/almende/vis/tree/master/examples" target="_blank">examples directory</a>. | |||||
| <h3 id="timeline">Timeline</h3> | |||||
| <p> | |||||
| The timeline from vis.js displays different types of data on a timeline. | |||||
| </p> | |||||
| <div class="gallery"> | |||||
| <div class="thumb"> | |||||
| <a href="examples/timeline/01_basic.html"> | |||||
| <img src="img/gallery/timeline/01_basic.png"> | |||||
| <div>basic usage</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/timeline/02_dataset.html"> | |||||
| <img src="img/gallery/timeline/02_dataset.png"> | |||||
| <div>dataset</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/timeline/03_much_data.html"> | |||||
| <img src="img/gallery/timeline/03_much_data.png"> | |||||
| <div>much data</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/timeline/04_html_data.html"> | |||||
| <img src="img/gallery/timeline/04_html_data.png"> | |||||
| <div>html data</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/timeline/05_groups.html"> | |||||
| <img src="img/gallery/timeline/05_groups.png"> | |||||
| <div>groups</div> | |||||
| </a> | |||||
| </div> | |||||
| </div> | |||||
| <h3 id="graph">Graph</h3> | |||||
| <p> | |||||
| The graph from vis.js visualizes graphs and networks with | |||||
| customizable styles. | |||||
| </p> | |||||
| <div class="gallery"> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/01_basic_usage.html"> | |||||
| <img src="img/gallery/graph/01_basic_usage.png"> | |||||
| <div>basic usage</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/02_random_nodes.html"> | |||||
| <img src="img/gallery/graph/02_random_nodes.png"> | |||||
| <div>random nodes</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/03_images.html"> | |||||
| <img src="img/gallery/graph/03_images.png"> | |||||
| <div>images</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/04_shapes.html"> | |||||
| <img src="img/gallery/graph/04_shapes.png"> | |||||
| <div>shapes</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/05_social_network.html"> | |||||
| <img src="img/gallery/graph/05_social_network.png"> | |||||
| <div>social network</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/06_groups.html"> | |||||
| <img src="img/gallery/graph/06_groups.png"> | |||||
| <div>groups</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/07_selections.html"> | |||||
| <img src="img/gallery/graph/07_selections.png"> | |||||
| <div>selections</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/08_mobile_friendly.html"> | |||||
| <img src="img/gallery/graph/08_mobile_friendly.png"> | |||||
| <div>mobile friendly</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/09_sizing.html"> | |||||
| <img src="img/gallery/graph/09_sizing.png"> | |||||
| <div>sizing</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/10_multiline_text.html"> | |||||
| <img src="img/gallery/graph/10_multiline_text.png"> | |||||
| <div>multiline text</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/11_custom_style.html"> | |||||
| <img src="img/gallery/graph/11_custom_style.png"> | |||||
| <div>custom style</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/12_scalable_images.html"> | |||||
| <img src="img/gallery/graph/12_scalable_images.png"> | |||||
| <div>scalable images</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/13_dashed_lines.html"> | |||||
| <img src="img/gallery/graph/13_dashed_lines.png"> | |||||
| <div>dashed lines</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/14_dot_language.html"> | |||||
| <img src="img/gallery/graph/14_dot_language.png"> | |||||
| <div>dot language</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/15_dot_language_playground.html"> | |||||
| <img src="img/gallery/graph/15_dot_language_playground.png"> | |||||
| <div>playground</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/16_dynamic_data.html"> | |||||
| <img src="img/gallery/graph/16_dynamic_data.png"> | |||||
| <div>dynamic data</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/17_network_info.html"> | |||||
| <img src="img/gallery/graph/17_network_info.png"> | |||||
| <div>network info</div> | |||||
| </a> | |||||
| </div> | |||||
| <div class="thumb"> | |||||
| <a href="examples/graph/graphviz/graphviz_gallery.html"> | |||||
| <img src="img/gallery/graph/graphviz_gallery.png"> | |||||
| <div>graphviz gallery</div> | |||||
| </a> | |||||
| </div> | |||||
| </div> | |||||
| <h2 id="docs">Docs</h2> | |||||
| <p> | |||||
| Documentation is available here: | |||||
| <a href="docs/index.html" target="_blank">Documentation</a> | |||||
| </p> | |||||
| <h2 id="license">License</h2> | |||||
| <p> | |||||
| Copyright (C) 2010-2014 Almende B.V. | |||||
| </p> | |||||
| <p> | |||||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| you may not use this file except in compliance with the License. | |||||
| You may obtain a copy of the License at | |||||
| </p> | |||||
| <p> | |||||
| http://www.apache.org/licenses/LICENSE-2.0 | |||||
| </p> | |||||
| <p> | |||||
| Unless required by applicable law or agreed to in writing, software | |||||
| distributed under the License is distributed on an "AS IS" BASIS, | |||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| See the License for the specific language governing permissions and | |||||
| limitations under the License. | |||||
| </p> | |||||
| <a id="forkme" href="https://github.com/almende/vis/" target="_blank"> | |||||
| <img src="img/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" > | |||||
| </a> | |||||
| </div> | |||||
| </body> | |||||
| </html> | |||||
| @ -1,245 +0,0 @@ | |||||
| /** | |||||
| * Created by Alex on 1/22/14. | |||||
| */ | |||||
| var NavigationMixin = { | |||||
| /** | |||||
| * This function moves the navigation controls if the canvas size has been changed. If the arugments | |||||
| * verticaAlignTop and horizontalAlignLeft are false, the correction will be made | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _relocateNavigation : function() { | |||||
| if (this.sectors !== undefined) { | |||||
| var xOffset = this.navigationClientWidth - this.frame.canvas.clientWidth; | |||||
| var yOffset = this.navigationClientHeight - this.frame.canvas.clientHeight; | |||||
| this.navigationClientWidth = this.frame.canvas.clientWidth; | |||||
| this.navigationClientHeight = this.frame.canvas.clientHeight; | |||||
| var node = null; | |||||
| for (var nodeId in this.sectors["navigation"]["nodes"]) { | |||||
| if (this.sectors["navigation"]["nodes"].hasOwnProperty(nodeId)) { | |||||
| node = this.sectors["navigation"]["nodes"][nodeId]; | |||||
| if (!node.horizontalAlignLeft) { | |||||
| node.x -= xOffset; | |||||
| } | |||||
| if (!node.verticalAlignTop) { | |||||
| node.y -= yOffset; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation | |||||
| * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent | |||||
| * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. | |||||
| * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadNavigationElements : function() { | |||||
| var DIR = this.constants.navigation.iconPath; | |||||
| this.navigationClientWidth = this.frame.canvas.clientWidth; | |||||
| this.navigationClientHeight = this.frame.canvas.clientHeight; | |||||
| if (this.navigationClientWidth === undefined) { | |||||
| this.navigationClientWidth = 0; | |||||
| this.navigationClientHeight = 0; | |||||
| } | |||||
| var offset = 15; | |||||
| var intermediateOffset = 7; | |||||
| var navigationNodes = [ | |||||
| {id: 'navigation_up', shape: 'image', image: DIR + '/uparrow.png', triggerFunction: "_moveUp", | |||||
| verticalAlignTop: false, x: 45 + offset + intermediateOffset, y: this.navigationClientHeight - 45 - offset - intermediateOffset}, | |||||
| {id: 'navigation_down', shape: 'image', image: DIR + '/downarrow.png', triggerFunction: "_moveDown", | |||||
| verticalAlignTop: false, x: 45 + offset + intermediateOffset, y: this.navigationClientHeight - 15 - offset}, | |||||
| {id: 'navigation_left', shape: 'image', image: DIR + '/leftarrow.png', triggerFunction: "_moveLeft", | |||||
| verticalAlignTop: false, x: 15 + offset, y: this.navigationClientHeight - 15 - offset}, | |||||
| {id: 'navigation_right', shape: 'image', image: DIR + '/rightarrow.png',triggerFunction: "_moveRight", | |||||
| verticalAlignTop: false, x: 75 + offset + 2 * intermediateOffset, y: this.navigationClientHeight - 15 - offset}, | |||||
| {id: 'navigation_plus', shape: 'image', image: DIR + '/plus.png', triggerFunction: "_zoomIn", | |||||
| verticalAlignTop: false, horizontalAlignLeft: false, | |||||
| x: this.navigationClientWidth - 45 - offset - intermediateOffset, y: this.navigationClientHeight - 15 - offset}, | |||||
| {id: 'navigation_min', shape: 'image', image: DIR + '/minus.png', triggerFunction: "_zoomOut", | |||||
| verticalAlignTop: false, horizontalAlignLeft: false, | |||||
| x: this.navigationClientWidth - 15 - offset, y: this.navigationClientHeight - 15 - offset}, | |||||
| {id: 'navigation_zoomExtends', shape: 'image', image: DIR + '/zoomExtends.png', triggerFunction: "zoomToFit", | |||||
| verticalAlignTop: false, horizontalAlignLeft: false, | |||||
| x: this.navigationClientWidth - 15 - offset, y: this.navigationClientHeight - 45 - offset - intermediateOffset} | |||||
| ]; | |||||
| var nodeObj = null; | |||||
| for (var i = 0; i < navigationNodes.length; i++) { | |||||
| nodeObj = this.sectors["navigation"]['nodes']; | |||||
| nodeObj[navigationNodes[i]['id']] = new Node(navigationNodes[i], this.images, this.groups, this.constants); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * By setting the clustersize to be larger than 1, we use the clustering drawing method | |||||
| * to illustrate the buttons are presed. We call this highlighting. | |||||
| * | |||||
| * @param {String} elementId | |||||
| * @private | |||||
| */ | |||||
| _highlightNavigationElement : function(elementId) { | |||||
| if (this.sectors["navigation"]["nodes"].hasOwnProperty(elementId)) { | |||||
| this.sectors["navigation"]["nodes"][elementId].clusterSize = 2; | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Reverting back to a normal button | |||||
| * | |||||
| * @param {String} elementId | |||||
| * @private | |||||
| */ | |||||
| _unHighlightNavigationElement : function(elementId) { | |||||
| if (this.sectors["navigation"]["nodes"].hasOwnProperty(elementId)) { | |||||
| this.sectors["navigation"]["nodes"][elementId].clusterSize = 1; | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * un-highlight (for lack of a better term) all navigation controls elements | |||||
| * @private | |||||
| */ | |||||
| _unHighlightAll : function() { | |||||
| for (var nodeId in this.sectors['navigation']['nodes']) { | |||||
| if (this.sectors['navigation']['nodes'].hasOwnProperty(nodeId)) { | |||||
| this._unHighlightNavigationElement(nodeId); | |||||
| } | |||||
| } | |||||
| }, | |||||
| _preventDefault : function(event) { | |||||
| if (event !== undefined) { | |||||
| if (event.preventDefault) { | |||||
| event.preventDefault(); | |||||
| } else { | |||||
| event.returnValue = false; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * move the screen up | |||||
| * By using the increments, instead of adding a fixed number to the translation, we keep fluent and | |||||
| * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently | |||||
| * To avoid this behaviour, we do the translation in the start loop. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _moveUp : function(event) { | |||||
| this._highlightNavigationElement("navigation_up"); | |||||
| this.yIncrement = this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen down | |||||
| * @private | |||||
| */ | |||||
| _moveDown : function(event) { | |||||
| this._highlightNavigationElement("navigation_down"); | |||||
| this.yIncrement = -this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen left | |||||
| * @private | |||||
| */ | |||||
| _moveLeft : function(event) { | |||||
| this._highlightNavigationElement("navigation_left"); | |||||
| this.xIncrement = this.constants.keyboard.speed.x; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen right | |||||
| * @private | |||||
| */ | |||||
| _moveRight : function(event) { | |||||
| this._highlightNavigationElement("navigation_right"); | |||||
| this.xIncrement = -this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Zoom in, using the same method as the movement. | |||||
| * @private | |||||
| */ | |||||
| _zoomIn : function(event) { | |||||
| this._highlightNavigationElement("navigation_plus"); | |||||
| this.zoomIncrement = this.constants.keyboard.speed.zoom; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Zoom out | |||||
| * @private | |||||
| */ | |||||
| _zoomOut : function() { | |||||
| this._highlightNavigationElement("navigation_min"); | |||||
| this.zoomIncrement = -this.constants.keyboard.speed.zoom; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Stop zooming and unhighlight the zoom controls | |||||
| * @private | |||||
| */ | |||||
| _stopZoom : function() { | |||||
| this._unHighlightNavigationElement("navigation_plus"); | |||||
| this._unHighlightNavigationElement("navigation_min"); | |||||
| this.zoomIncrement = 0; | |||||
| }, | |||||
| /** | |||||
| * Stop moving in the Y direction and unHighlight the up and down | |||||
| * @private | |||||
| */ | |||||
| _yStopMoving : function() { | |||||
| this._unHighlightNavigationElement("navigation_up"); | |||||
| this._unHighlightNavigationElement("navigation_down"); | |||||
| this.yIncrement = 0; | |||||
| }, | |||||
| /** | |||||
| * Stop moving in the X direction and unHighlight left and right. | |||||
| * @private | |||||
| */ | |||||
| _xStopMoving : function() { | |||||
| this._unHighlightNavigationElement("navigation_left"); | |||||
| this._unHighlightNavigationElement("navigation_right"); | |||||
| this.xIncrement = 0; | |||||
| } | |||||
| }; | |||||
| @ -0,0 +1,128 @@ | |||||
| div.graph-manipulationDiv { | |||||
| border-width:0px; | |||||
| border-bottom: 1px; | |||||
| border-style:solid; | |||||
| border-color: #d6d9d8; | |||||
| background: #ffffff; /* Old browsers */ | |||||
| background: -moz-linear-gradient(top, #ffffff 0%, #fcfcfc 48%, #fafafa 50%, #fcfcfc 100%); /* FF3.6+ */ | |||||
| background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(48%,#fcfcfc), color-stop(50%,#fafafa), color-stop(100%,#fcfcfc)); /* Chrome,Safari4+ */ | |||||
| background: -webkit-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Chrome10+,Safari5.1+ */ | |||||
| background: -o-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Opera 11.10+ */ | |||||
| background: -ms-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* IE10+ */ | |||||
| background: linear-gradient(to bottom, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* W3C */ | |||||
| filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc',GradientType=0 ); /* IE6-9 */ | |||||
| width: 600px; | |||||
| height:30px; | |||||
| z-index:10; | |||||
| position:absolute; | |||||
| } | |||||
| div.graph-manipulation-editMode { | |||||
| height:30px; | |||||
| z-index:10; | |||||
| position:absolute; | |||||
| margin-top:20px; | |||||
| } | |||||
| div.graph-manipulation-closeDiv { | |||||
| height:30px; | |||||
| width:30px; | |||||
| z-index:11; | |||||
| position:absolute; | |||||
| margin-top:3px; | |||||
| margin-left:590px; | |||||
| background-position: 0px 0px; | |||||
| background-repeat:no-repeat; | |||||
| background-image: url("../../dist/img/cross.png"); | |||||
| cursor: pointer; | |||||
| -webkit-touch-callout: none; | |||||
| -webkit-user-select: none; | |||||
| -khtml-user-select: none; | |||||
| -moz-user-select: none; | |||||
| -ms-user-select: none; | |||||
| user-select: none; | |||||
| } | |||||
| span.graph-manipulationUI { | |||||
| font-family: verdana; | |||||
| font-size: 12px; | |||||
| -moz-border-radius: 15px; | |||||
| border-radius: 15px; | |||||
| display:inline-block; | |||||
| background-position: 0px 0px; | |||||
| background-repeat:no-repeat; | |||||
| height:24px; | |||||
| margin: -14px 0px 0px 10px; | |||||
| vertical-align:middle; | |||||
| cursor: pointer; | |||||
| padding: 0px 8px 0px 8px; | |||||
| -webkit-touch-callout: none; | |||||
| -webkit-user-select: none; | |||||
| -khtml-user-select: none; | |||||
| -moz-user-select: none; | |||||
| -ms-user-select: none; | |||||
| user-select: none; | |||||
| } | |||||
| span.graph-manipulationUI:hover { | |||||
| box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.20); | |||||
| } | |||||
| span.graph-manipulationUI:active { | |||||
| box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.50); | |||||
| } | |||||
| span.graph-manipulationUI.back { | |||||
| background-image: url("../../dist/img/backIcon.png"); | |||||
| } | |||||
| span.graph-manipulationUI.none:hover { | |||||
| box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0); | |||||
| cursor: default; | |||||
| } | |||||
| span.graph-manipulationUI.none:active { | |||||
| box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0); | |||||
| } | |||||
| span.graph-manipulationUI.none { | |||||
| padding: 0px 0px 0px 0px; | |||||
| } | |||||
| span.graph-manipulationUI.notification{ | |||||
| margin: 2px; | |||||
| font-weight: bold; | |||||
| } | |||||
| span.graph-manipulationUI.add { | |||||
| background-image: url("../../dist/img/addNodeIcon.png"); | |||||
| } | |||||
| span.graph-manipulationUI.edit { | |||||
| background-image: url("../../dist/img/editIcon.png"); | |||||
| } | |||||
| span.graph-manipulationUI.edit.editmode { | |||||
| background-color: #fcfcfc; | |||||
| border-style:solid; | |||||
| border-width:1px; | |||||
| border-color: #cccccc; | |||||
| } | |||||
| span.graph-manipulationUI.connect { | |||||
| background-image: url("../../dist/img/connectIcon.png"); | |||||
| } | |||||
| span.graph-manipulationUI.delete { | |||||
| background-image: url("../../dist/img/deleteIcon.png"); | |||||
| } | |||||
| /* top right bottom left */ | |||||
| span.graph-manipulationLabel { | |||||
| margin: 0px 0px 0px 23px; | |||||
| line-height: 25px; | |||||
| } | |||||
| div.graph-seperatorLine { | |||||
| display:inline-block; | |||||
| width:1px; | |||||
| height:20px; | |||||
| background-color: #bdbdbd; | |||||
| margin: 5px 7px 0px 15px; | |||||
| } | |||||
| @ -0,0 +1,62 @@ | |||||
| div.graph-navigation { | |||||
| width:34px; | |||||
| height:34px; | |||||
| z-index:10; | |||||
| -moz-border-radius: 17px; | |||||
| border-radius: 17px; | |||||
| position:absolute; | |||||
| display:inline-block; | |||||
| background-position: 2px 2px; | |||||
| background-repeat:no-repeat; | |||||
| cursor: pointer; | |||||
| -webkit-touch-callout: none; | |||||
| -webkit-user-select: none; | |||||
| -khtml-user-select: none; | |||||
| -moz-user-select: none; | |||||
| -ms-user-select: none; | |||||
| user-select: none; | |||||
| } | |||||
| div.graph-navigation:hover { | |||||
| box-shadow: 0px 0px 3px 3px rgba(56, 207, 21, 0.30); | |||||
| } | |||||
| div.graph-navigation:active { | |||||
| box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95); | |||||
| } | |||||
| div.graph-navigation.up { | |||||
| background-image: url("../../dist/img/upArrow.png"); | |||||
| margin-top:520px; | |||||
| margin-left:55px; | |||||
| } | |||||
| div.graph-navigation.down { | |||||
| background-image: url("../../dist/img/downArrow.png"); | |||||
| margin-top:560px; | |||||
| margin-left:55px; | |||||
| } | |||||
| div.graph-navigation.left { | |||||
| background-image: url("../../dist/img/leftArrow.png"); | |||||
| margin-top:560px; | |||||
| margin-left:15px; | |||||
| } | |||||
| div.graph-navigation.right { | |||||
| background-image: url("../../dist/img/rightArrow.png"); | |||||
| margin-top:560px; | |||||
| margin-left:95px; | |||||
| } | |||||
| div.graph-navigation.zoomIn { | |||||
| background-image: url("../../dist/img/plus.png"); | |||||
| margin-top:560px; | |||||
| margin-left:555px; | |||||
| } | |||||
| div.graph-navigation.zoomOut { | |||||
| background-image: url("../../dist/img/minus.png"); | |||||
| margin-top:560px; | |||||
| margin-left:515px; | |||||
| } | |||||
| div.graph-navigation.zoomExtends { | |||||
| background-image: url("../../dist/img/zoomExtends.png"); | |||||
| margin-top:520px; | |||||
| margin-left:555px; | |||||
| } | |||||
| @ -0,0 +1,433 @@ | |||||
| /** | |||||
| * Created by Alex on 2/4/14. | |||||
| */ | |||||
| var manipulationMixin = { | |||||
| /** | |||||
| * clears the toolbar div element of children | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _clearManipulatorBar : function() { | |||||
| while (this.manipulationDiv.hasChildNodes()) { | |||||
| this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore | |||||
| * these functions to their original functionality, we saved them in this.cachedFunctions. | |||||
| * This function restores these functions to their original function. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _restoreOverloadedFunctions : function() { | |||||
| for (var functionName in this.cachedFunctions) { | |||||
| if (this.cachedFunctions.hasOwnProperty(functionName)) { | |||||
| this[functionName] = this.cachedFunctions[functionName]; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Enable or disable edit-mode. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _toggleEditMode : function() { | |||||
| this.editMode = !this.editMode; | |||||
| var toolbar = document.getElementById("graph-manipulationDiv"); | |||||
| var closeDiv = document.getElementById("graph-manipulation-closeDiv"); | |||||
| var editModeDiv = document.getElementById("graph-manipulation-editMode"); | |||||
| if (this.editMode == true) { | |||||
| toolbar.style.display="block"; | |||||
| closeDiv.style.display="block"; | |||||
| editModeDiv.style.display="none"; | |||||
| closeDiv.onclick = this._toggleEditMode.bind(this); | |||||
| } | |||||
| else { | |||||
| toolbar.style.display="none"; | |||||
| closeDiv.style.display="none"; | |||||
| editModeDiv.style.display="block"; | |||||
| closeDiv.onclick = null; | |||||
| } | |||||
| this._createManipulatorBar() | |||||
| }, | |||||
| /** | |||||
| * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _createManipulatorBar : function() { | |||||
| // remove bound functions | |||||
| this.off('select', this.boundFunction); | |||||
| // restore overloaded functions | |||||
| this._restoreOverloadedFunctions(); | |||||
| // resume calculation | |||||
| this.freezeSimulation = false; | |||||
| // reset global variables | |||||
| this.blockConnectingEdgeSelection = false; | |||||
| this.forceAppendSelection = false | |||||
| if (this.editMode == true) { | |||||
| while (this.manipulationDiv.hasChildNodes()) { | |||||
| this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); | |||||
| } | |||||
| // add the icons to the manipulator div | |||||
| this.manipulationDiv.innerHTML = "" + | |||||
| "<span class='graph-manipulationUI add' id='graph-manipulate-addNode'>" + | |||||
| "<span class='graph-manipulationLabel'>Add Node</span></span>" + | |||||
| "<div class='graph-seperatorLine'></div>" + | |||||
| "<span class='graph-manipulationUI connect' id='graph-manipulate-connectNode'>" + | |||||
| "<span class='graph-manipulationLabel'>Add Link</span></span>"; | |||||
| if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { | |||||
| this.manipulationDiv.innerHTML += "" + | |||||
| "<div class='graph-seperatorLine'></div>" + | |||||
| "<span class='graph-manipulationUI edit' id='graph-manipulate-editNode'>" + | |||||
| "<span class='graph-manipulationLabel'>Edit Node</span></span>"; | |||||
| } | |||||
| if (this._selectionIsEmpty() == false) { | |||||
| this.manipulationDiv.innerHTML += "" + | |||||
| "<div class='graph-seperatorLine'></div>" + | |||||
| "<span class='graph-manipulationUI delete' id='graph-manipulate-delete'>" + | |||||
| "<span class='graph-manipulationLabel'>Delete selected</span></span>"; | |||||
| } | |||||
| // bind the icons | |||||
| var addNodeButton = document.getElementById("graph-manipulate-addNode"); | |||||
| addNodeButton.onclick = this._createAddNodeToolbar.bind(this); | |||||
| var addEdgeButton = document.getElementById("graph-manipulate-connectNode"); | |||||
| addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this); | |||||
| if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { | |||||
| var editButton = document.getElementById("graph-manipulate-editNode"); | |||||
| editButton.onclick = this._editNode.bind(this); | |||||
| } | |||||
| if (this._selectionIsEmpty() == false) { | |||||
| var deleteButton = document.getElementById("graph-manipulate-delete"); | |||||
| deleteButton.onclick = this._deleteSelected.bind(this); | |||||
| } | |||||
| var closeDiv = document.getElementById("graph-manipulation-closeDiv"); | |||||
| closeDiv.onclick = this._toggleEditMode.bind(this); | |||||
| this.boundFunction = this._createManipulatorBar.bind(this); | |||||
| this.on('select', this.boundFunction); | |||||
| } | |||||
| else { | |||||
| this.editModeDiv.innerHTML = "" + | |||||
| "<span class='graph-manipulationUI edit editmode' id='graph-manipulate-editModeButton'>" + | |||||
| "<span class='graph-manipulationLabel'>Edit</span></span>" | |||||
| var editModeButton = document.getElementById("graph-manipulate-editModeButton"); | |||||
| editModeButton.onclick = this._toggleEditMode.bind(this); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Create the toolbar for adding Nodes | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _createAddNodeToolbar : function() { | |||||
| // clear the toolbar | |||||
| this._clearManipulatorBar(); | |||||
| this.off('select', this.boundFunction); | |||||
| // create the toolbar contents | |||||
| this.manipulationDiv.innerHTML = "" + | |||||
| "<span class='graph-manipulationUI back' id='graph-manipulate-back'>" + | |||||
| "<span class='graph-manipulationLabel'>Back</span></span>" + | |||||
| "<div class='graph-seperatorLine'></div>" + | |||||
| "<span class='graph-manipulationUI none' id='graph-manipulate-back'>" + | |||||
| "<span class='graph-manipulationLabel'>Click in an empty space to place a new node</span></span>"; | |||||
| // bind the icon | |||||
| var backButton = document.getElementById("graph-manipulate-back"); | |||||
| backButton.onclick = this._createManipulatorBar.bind(this); | |||||
| // we use the boundFunction so we can reference it when we unbind it from the "select" event. | |||||
| this.boundFunction = this._addNode.bind(this); | |||||
| this.on('select', this.boundFunction); | |||||
| }, | |||||
| /** | |||||
| * create the toolbar to connect nodes | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _createAddEdgeToolbar : function() { | |||||
| // clear the toolbar | |||||
| this._clearManipulatorBar(); | |||||
| this._unselectAll(true); | |||||
| this.freezeSimulation = true; | |||||
| this.off('select', this.boundFunction); | |||||
| this._unselectAll(); | |||||
| this.forceAppendSelection = false; | |||||
| this.blockConnectingEdgeSelection = true; | |||||
| this.manipulationDiv.innerHTML = "" + | |||||
| "<span class='graph-manipulationUI back' id='graph-manipulate-back'>" + | |||||
| "<span class='graph-manipulationLabel'>Back</span></span>" + | |||||
| "<div class='graph-seperatorLine'></div>" + | |||||
| "<span class='graph-manipulationUI none' id='graph-manipulate-back'>" + | |||||
| "<span id='graph-manipulatorLabel' class='graph-manipulationLabel'>Click on a node and drag the edge to another node to connect them.</span></span>"; | |||||
| // bind the icon | |||||
| var backButton = document.getElementById("graph-manipulate-back"); | |||||
| backButton.onclick = this._createManipulatorBar.bind(this); | |||||
| // we use the boundFunction so we can reference it when we unbind it from the "select" event. | |||||
| this.boundFunction = this._handleConnect.bind(this); | |||||
| this.on('select', this.boundFunction); | |||||
| // temporarily overload functions | |||||
| this.cachedFunctions["_handleTouch"] = this._handleTouch; | |||||
| this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease; | |||||
| this._handleTouch = this._handleConnect; | |||||
| this._handleOnRelease = this._finishConnect; | |||||
| // redraw to show the unselect | |||||
| this._redraw(); | |||||
| }, | |||||
| /** | |||||
| * the function bound to the selection event. It checks if you want to connect a cluster and changes the description | |||||
| * to walk the user through the process. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _handleConnect : function(pointer) { | |||||
| if (this._getSelectedNodeCount() == 0) { | |||||
| var node = this._getNodeAt(pointer); | |||||
| if (node != null) { | |||||
| if (node.clusterSize > 1) { | |||||
| alert("Cannot create edges to a cluster.") | |||||
| } | |||||
| else { | |||||
| this._selectObject(node,false); | |||||
| // create a node the temporary line can look at | |||||
| this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); | |||||
| this.sectors['support']['nodes']['targetNode'].x = node.x; | |||||
| this.sectors['support']['nodes']['targetNode'].y = node.y; | |||||
| this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants); | |||||
| this.sectors['support']['nodes']['targetViaNode'].x = node.x; | |||||
| this.sectors['support']['nodes']['targetViaNode'].y = node.y; | |||||
| this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge"; | |||||
| // create a temporary edge | |||||
| this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants); | |||||
| this.edges['connectionEdge'].from = node; | |||||
| this.edges['connectionEdge'].connected = true; | |||||
| this.edges['connectionEdge'].smooth = true; | |||||
| this.edges['connectionEdge'].selected = true; | |||||
| this.edges['connectionEdge'].to = this.sectors['support']['nodes']['targetNode']; | |||||
| this.edges['connectionEdge'].via = this.sectors['support']['nodes']['targetViaNode']; | |||||
| this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; | |||||
| this._handleOnDrag = function(event) { | |||||
| var pointer = this._getPointer(event.gesture.touches[0]); | |||||
| this.sectors['support']['nodes']['targetNode'].x = this._canvasToX(pointer.x); | |||||
| this.sectors['support']['nodes']['targetNode'].y = this._canvasToY(pointer.y); | |||||
| this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._canvasToX(pointer.x) + this.edges['connectionEdge'].from.x); | |||||
| this.sectors['support']['nodes']['targetViaNode'].y = this._canvasToY(pointer.y); | |||||
| }; | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| _finishConnect : function(pointer) { | |||||
| if (this._getSelectedNodeCount() == 1) { | |||||
| // restore the drag function | |||||
| this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; | |||||
| delete this.cachedFunctions["_handleOnDrag"]; | |||||
| // remember the edge id | |||||
| var connectFromId = this.edges['connectionEdge'].fromId; | |||||
| // remove the temporary nodes and edge | |||||
| delete this.edges['connectionEdge'] | |||||
| delete this.sectors['support']['nodes']['targetNode']; | |||||
| delete this.sectors['support']['nodes']['targetViaNode']; | |||||
| var node = this._getNodeAt(pointer); | |||||
| if (node != null) { | |||||
| if (node.clusterSize > 1) { | |||||
| alert("Cannot create edges to a cluster.") | |||||
| } | |||||
| else { | |||||
| this._createEdge(connectFromId,node.id); | |||||
| this._createManipulatorBar(); | |||||
| } | |||||
| } | |||||
| this._unselectAll(); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Adds a node on the specified location | |||||
| * | |||||
| * @param {Object} pointer | |||||
| */ | |||||
| _addNode : function() { | |||||
| if (this._selectionIsEmpty() && this.editMode == true) { | |||||
| var positionObject = this._pointerToPositionObject(this.pointerPosition); | |||||
| var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",fixed:false}; | |||||
| if (this.triggerFunctions.add) { | |||||
| if (this.triggerFunctions.add.length == 2) { | |||||
| var me = this; | |||||
| this.triggerFunctions.add(defaultData, function(finalizedData) { | |||||
| me.createNodeOnClick = true; | |||||
| me.nodesData.add(finalizedData); | |||||
| me.createNodeOnClick = false; | |||||
| me._createManipulatorBar(); | |||||
| me.moving = true; | |||||
| me.start(); | |||||
| }); | |||||
| } | |||||
| else { | |||||
| alert("The function for add does not support two arguments (data,callback)."); | |||||
| this._createManipulatorBar(); | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| else { | |||||
| this.createNodeOnClick = true; | |||||
| this.nodesData.add(defaultData); | |||||
| this.createNodeOnClick = false; | |||||
| this._createManipulatorBar(); | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * connect two nodes with a new edge. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _createEdge : function(sourceNodeId,targetNodeId) { | |||||
| if (this.editMode == true) { | |||||
| var defaultData = {from:sourceNodeId, to:targetNodeId}; | |||||
| if (this.triggerFunctions.connect) { | |||||
| if (this.triggerFunctions.connect.length == 2) { | |||||
| var me = this; | |||||
| this.triggerFunctions.connect(defaultData, function(finalizedData) { | |||||
| me.edgesData.add(finalizedData) | |||||
| me.moving = true; | |||||
| me.start(); | |||||
| }); | |||||
| } | |||||
| else { | |||||
| alert("The function for connect does not support two arguments (data,callback)."); | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| else { | |||||
| this.edgesData.add(defaultData) | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _editNode : function() { | |||||
| if (this.triggerFunctions.edit && this.editMode == true) { | |||||
| var node = this._getSelectedNode(); | |||||
| var data = {id:node.id, | |||||
| label: node.label, | |||||
| group: node.group, | |||||
| shape: node.shape, | |||||
| color: { | |||||
| background:node.color.background, | |||||
| border:node.color.border, | |||||
| highlight: { | |||||
| background:node.color.highlight.background, | |||||
| border:node.color.highlight.border | |||||
| } | |||||
| }}; | |||||
| if (this.triggerFunctions.edit.length == 2) { | |||||
| var me = this; | |||||
| this.triggerFunctions.edit(data, function (finalizedData) { | |||||
| me.nodesData.update(finalizedData); | |||||
| me._createManipulatorBar(); | |||||
| me.moving = true; | |||||
| me.start(); | |||||
| }); | |||||
| } | |||||
| else { | |||||
| alert("The function for edit does not support two arguments (data, callback).") | |||||
| } | |||||
| } | |||||
| else { | |||||
| alert("No edit function has been bound to this button.") | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * delete everything in the selection | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _deleteSelected : function() { | |||||
| if (!this._selectionIsEmpty() && this.editMode == true) { | |||||
| if (!this._clusterInSelection()) { | |||||
| var selectedNodes = this.getSelectedNodes(); | |||||
| var selectedEdges = this.getSelectedEdges(); | |||||
| if (this.triggerFunctions.delete) { | |||||
| var me = this; | |||||
| var data = {nodes: selectedNodes, edges: selectedEdges}; | |||||
| if (this.triggerFunctions.delete.length = 2) { | |||||
| this.triggerFunctions.delete(data, function (finalizedData) { | |||||
| me.edgesData.remove(finalizedData.edges); | |||||
| me.nodesData.remove(finalizedData.nodes); | |||||
| this._unselectAll(); | |||||
| me.moving = true; | |||||
| me.start(); | |||||
| }); | |||||
| } | |||||
| else { | |||||
| alert("The function for edit does not support two arguments (data, callback).") | |||||
| } | |||||
| } | |||||
| else { | |||||
| this.edgesData.remove(selectedEdges); | |||||
| this.nodesData.remove(selectedNodes); | |||||
| this._unselectAll(); | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| } | |||||
| } | |||||
| else { | |||||
| alert("Clusters cannot be deleted."); | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| @ -0,0 +1,219 @@ | |||||
| /** | |||||
| * Created by Alex on 2/10/14. | |||||
| */ | |||||
| var graphMixinLoaders = { | |||||
| /** | |||||
| * Load a mixin into the graph object | |||||
| * | |||||
| * @param {Object} sourceVariable | this object has to contain functions. | |||||
| * @private | |||||
| */ | |||||
| _loadMixin : function(sourceVariable) { | |||||
| for (var mixinFunction in sourceVariable) { | |||||
| if (sourceVariable.hasOwnProperty(mixinFunction)) { | |||||
| Graph.prototype[mixinFunction] = sourceVariable[mixinFunction]; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * removes a mixin from the graph object. | |||||
| * | |||||
| * @param {Object} sourceVariable | this object has to contain functions. | |||||
| * @private | |||||
| */ | |||||
| _clearMixin : function(sourceVariable) { | |||||
| for (var mixinFunction in sourceVariable) { | |||||
| if (sourceVariable.hasOwnProperty(mixinFunction)) { | |||||
| Graph.prototype[mixinFunction] = undefined; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Mixin the physics system and initialize the parameters required. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadPhysicsSystem : function() { | |||||
| this._loadMixin(physicsMixin); | |||||
| this._loadSelectedForceSolver(); | |||||
| }, | |||||
| /** | |||||
| * This loads the node force solver based on the barnes hut or repulsion algorithm | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadSelectedForceSolver : function() { | |||||
| // this overloads the this._calculateNodeForces | |||||
| if (this.constants.physics.barnesHut.enabled == true) { | |||||
| this._clearMixin(repulsionMixin); | |||||
| this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; | |||||
| this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; | |||||
| this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; | |||||
| this.constants.physics.damping = this.constants.physics.barnesHut.damping; | |||||
| this.constants.physics.springGrowthPerMass = this.constants.physics.barnesHut.springGrowthPerMass; | |||||
| this._loadMixin(barnesHutMixin); | |||||
| } | |||||
| else { | |||||
| this._clearMixin(barnesHutMixin); | |||||
| this.barnesHutTree = undefined; | |||||
| this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; | |||||
| this.constants.physics.springLength = this.constants.physics.repulsion.springLength; | |||||
| this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; | |||||
| this.constants.physics.damping = this.constants.physics.repulsion.damping; | |||||
| this.constants.physics.springGrowthPerMass = this.constants.physics.repulsion.springGrowthPerMass; | |||||
| this._loadMixin(repulsionMixin); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Mixin the cluster system and initialize the parameters required. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadClusterSystem : function() { | |||||
| this.clusterSession = 0; | |||||
| this.hubThreshold = 5; | |||||
| this._loadMixin(ClusterMixin); | |||||
| }, | |||||
| /** | |||||
| * Mixin the sector system and initialize the parameters required | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadSectorSystem : function() { | |||||
| this.sectors = { }, | |||||
| this.activeSector = ["default"]; | |||||
| this.sectors["active"] = { }, | |||||
| this.sectors["active"]["default"] = {"nodes":{}, | |||||
| "edges":{}, | |||||
| "nodeIndices":[], | |||||
| "formationScale": 1.0, | |||||
| "drawingNode": undefined }; | |||||
| this.sectors["frozen"] = {}, | |||||
| this.sectors["support"] = {"nodes":{}, | |||||
| "edges":{}, | |||||
| "nodeIndices":[], | |||||
| "formationScale": 1.0, | |||||
| "drawingNode": undefined }; | |||||
| this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields | |||||
| this._loadMixin(SectorMixin); | |||||
| }, | |||||
| /** | |||||
| * Mixin the selection system and initialize the parameters required | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadSelectionSystem : function() { | |||||
| this.selectionObj = { }; | |||||
| this._loadMixin(SelectionMixin); | |||||
| }, | |||||
| /** | |||||
| * Mixin the navigationUI (User Interface) system and initialize the parameters required | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadManipulationSystem : function() { | |||||
| // reset global variables -- these are used by the selection of nodes and edges. | |||||
| this.blockConnectingEdgeSelection = false; | |||||
| this.forceAppendSelection = false | |||||
| if (this.constants.dataManipulation.enabled == true) { | |||||
| // load the manipulator HTML elements. All styling done in css. | |||||
| if (this.manipulationDiv === undefined) { | |||||
| this.manipulationDiv = document.createElement('div'); | |||||
| this.manipulationDiv.className = 'graph-manipulationDiv'; | |||||
| this.manipulationDiv.id = 'graph-manipulationDiv'; | |||||
| if (this.editMode == true) { | |||||
| this.manipulationDiv.style.display = "block"; | |||||
| } | |||||
| else { | |||||
| this.manipulationDiv.style.display = "none"; | |||||
| } | |||||
| this.containerElement.insertBefore(this.manipulationDiv, this.frame); | |||||
| } | |||||
| if (this.editModeDiv === undefined) { | |||||
| this.editModeDiv = document.createElement('div'); | |||||
| this.editModeDiv.className = 'graph-manipulation-editMode'; | |||||
| this.editModeDiv.id = 'graph-manipulation-editMode'; | |||||
| if (this.editMode == true) { | |||||
| this.editModeDiv.style.display = "none"; | |||||
| } | |||||
| else { | |||||
| this.editModeDiv.style.display = "block"; | |||||
| } | |||||
| this.containerElement.insertBefore(this.editModeDiv, this.frame); | |||||
| } | |||||
| if (this.closeDiv === undefined) { | |||||
| this.closeDiv = document.createElement('div'); | |||||
| this.closeDiv.className = 'graph-manipulation-closeDiv'; | |||||
| this.closeDiv.id = 'graph-manipulation-closeDiv'; | |||||
| this.closeDiv.style.display = this.manipulationDiv.style.display; | |||||
| this.containerElement.insertBefore(this.closeDiv, this.frame); | |||||
| } | |||||
| // load the manipulation functions | |||||
| this._loadMixin(manipulationMixin); | |||||
| // create the manipulator toolbar | |||||
| this._createManipulatorBar(); | |||||
| } | |||||
| else { | |||||
| if (this.manipulationDiv !== undefined) { | |||||
| // removes all the bindings and overloads | |||||
| this._createManipulatorBar(); | |||||
| // remove the manipulation divs | |||||
| this.containerElement.removeChild(this.manipulationDiv); | |||||
| this.containerElement.removeChild(this.editModeDiv); | |||||
| this.containerElement.removeChild(this.closeDiv); | |||||
| this.manipulationDiv = undefined; | |||||
| this.editModeDiv = undefined; | |||||
| this.closeDiv = undefined; | |||||
| // remove the mixin functions | |||||
| this._clearMixin(manipulationMixin); | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Mixin the navigation (User Interface) system and initialize the parameters required | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadNavigationControls : function() { | |||||
| this._loadMixin(NavigationMixin); | |||||
| // the clean function removes the button divs, this is done to remove the bindings. | |||||
| this._cleanNavigation(); | |||||
| if (this.constants.navigation.enabled == true) { | |||||
| this._loadNavigationElements(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,173 @@ | |||||
| /** | |||||
| * Created by Alex on 1/22/14. | |||||
| */ | |||||
| var NavigationMixin = { | |||||
| _cleanNavigation : function() { | |||||
| // clean up previosu navigation items | |||||
| var wrapper = document.getElementById('graph-navigation_wrapper'); | |||||
| if (wrapper != null) { | |||||
| this.containerElement.removeChild(wrapper); | |||||
| } | |||||
| document.onmouseup = null; | |||||
| }, | |||||
| /** | |||||
| * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation | |||||
| * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent | |||||
| * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. | |||||
| * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _loadNavigationElements : function() { | |||||
| this._cleanNavigation(); | |||||
| this.navigationDivs = {}; | |||||
| var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; | |||||
| var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','zoomToFit']; | |||||
| this.navigationDivs['wrapper'] = document.createElement('div'); | |||||
| this.navigationDivs['wrapper'].id = "graph-navigation_wrapper"; | |||||
| this.containerElement.insertBefore(this.navigationDivs['wrapper'],this.frame); | |||||
| for (var i = 0; i < navigationDivs.length; i++) { | |||||
| this.navigationDivs[navigationDivs[i]] = document.createElement('div'); | |||||
| this.navigationDivs[navigationDivs[i]].id = "graph-navigation_" + navigationDivs[i]; | |||||
| this.navigationDivs[navigationDivs[i]].className = "graph-navigation " + navigationDivs[i]; | |||||
| this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); | |||||
| this.navigationDivs[navigationDivs[i]].onmousedown = this[navigationDivActions[i]].bind(this); | |||||
| } | |||||
| document.onmouseup = this._stopMovement.bind(this); | |||||
| }, | |||||
| /** | |||||
| * this stops all movement induced by the navigation buttons | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _stopMovement : function() { | |||||
| this._xStopMoving(); | |||||
| this._yStopMoving(); | |||||
| this._stopZoom(); | |||||
| }, | |||||
| /** | |||||
| * stops the actions performed by page up and down etc. | |||||
| * | |||||
| * @param event | |||||
| * @private | |||||
| */ | |||||
| _preventDefault : function(event) { | |||||
| if (event !== undefined) { | |||||
| if (event.preventDefault) { | |||||
| event.preventDefault(); | |||||
| } else { | |||||
| event.returnValue = false; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * move the screen up | |||||
| * By using the increments, instead of adding a fixed number to the translation, we keep fluent and | |||||
| * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently | |||||
| * To avoid this behaviour, we do the translation in the start loop. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _moveUp : function(event) { | |||||
| console.log("here") | |||||
| this.yIncrement = this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen down | |||||
| * @private | |||||
| */ | |||||
| _moveDown : function(event) { | |||||
| this.yIncrement = -this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen left | |||||
| * @private | |||||
| */ | |||||
| _moveLeft : function(event) { | |||||
| this.xIncrement = this.constants.keyboard.speed.x; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * move the screen right | |||||
| * @private | |||||
| */ | |||||
| _moveRight : function(event) { | |||||
| this.xIncrement = -this.constants.keyboard.speed.y; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Zoom in, using the same method as the movement. | |||||
| * @private | |||||
| */ | |||||
| _zoomIn : function(event) { | |||||
| this.zoomIncrement = this.constants.keyboard.speed.zoom; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Zoom out | |||||
| * @private | |||||
| */ | |||||
| _zoomOut : function() { | |||||
| this.zoomIncrement = -this.constants.keyboard.speed.zoom; | |||||
| this.start(); // if there is no node movement, the calculation wont be done | |||||
| this._preventDefault(event); | |||||
| }, | |||||
| /** | |||||
| * Stop zooming and unhighlight the zoom controls | |||||
| * @private | |||||
| */ | |||||
| _stopZoom : function() { | |||||
| this.zoomIncrement = 0; | |||||
| }, | |||||
| /** | |||||
| * Stop moving in the Y direction and unHighlight the up and down | |||||
| * @private | |||||
| */ | |||||
| _yStopMoving : function() { | |||||
| this.yIncrement = 0; | |||||
| }, | |||||
| /** | |||||
| * Stop moving in the X direction and unHighlight left and right. | |||||
| * @private | |||||
| */ | |||||
| _xStopMoving : function() { | |||||
| this.xIncrement = 0; | |||||
| } | |||||
| }; | |||||
| @ -0,0 +1,373 @@ | |||||
| /** | |||||
| * Created by Alex on 2/10/14. | |||||
| */ | |||||
| var barnesHutMixin = { | |||||
| /** | |||||
| * This function calculates the forces the nodes apply on eachother based on a gravitational model. | |||||
| * The Barnes Hut method is used to speed up this N-body simulation. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _calculateNodeForces : function() { | |||||
| var node; | |||||
| var nodes = this.calculationNodes; | |||||
| var nodeIndices = this.calculationNodeIndices; | |||||
| var nodeCount = nodeIndices.length; | |||||
| this._formBarnesHutTree(nodes,nodeIndices); | |||||
| var barnesHutTree = this.barnesHutTree; | |||||
| // place the nodes one by one recursively | |||||
| for (var i = 0; i < nodeCount; i++) { | |||||
| node = nodes[nodeIndices[i]]; | |||||
| // starting with root is irrelevant, it never passes the BarnesHut condition | |||||
| this._getForceContribution(barnesHutTree.root.children.NW,node); | |||||
| this._getForceContribution(barnesHutTree.root.children.NE,node); | |||||
| this._getForceContribution(barnesHutTree.root.children.SW,node); | |||||
| this._getForceContribution(barnesHutTree.root.children.SE,node); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. | |||||
| * If a region contains a single node, we check if it is not itself, then we apply the force. | |||||
| * | |||||
| * @param parentBranch | |||||
| * @param node | |||||
| * @private | |||||
| */ | |||||
| _getForceContribution : function(parentBranch,node) { | |||||
| // we get no force contribution from an empty region | |||||
| if (parentBranch.childrenCount > 0) { | |||||
| var dx,dy,distance; | |||||
| // get the distance from the center of mass to the node. | |||||
| dx = parentBranch.centerOfMass.x - node.x; | |||||
| dy = parentBranch.centerOfMass.y - node.y; | |||||
| distance = Math.sqrt(dx * dx + dy * dy); | |||||
| // BarnesHut condition | |||||
| // original condition : s/d < theta = passed === d/s > 1/theta = passed | |||||
| // calcSize = 1/s --> d * 1/s > 1/theta = passed | |||||
| if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) { | |||||
| // duplicate code to reduce function calls to speed up program | |||||
| if (distance == 0) { | |||||
| distance = 0.5*Math.random(); | |||||
| dx = distance; | |||||
| } | |||||
| var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); | |||||
| var fx = dx * gravityForce; | |||||
| var fy = dy * gravityForce; | |||||
| node.fx += fx; | |||||
| node.fy += fy; | |||||
| } | |||||
| else { | |||||
| // Did not pass the condition, go into children if available | |||||
| if (parentBranch.childrenCount == 4) { | |||||
| this._getForceContribution(parentBranch.children.NW,node); | |||||
| this._getForceContribution(parentBranch.children.NE,node); | |||||
| this._getForceContribution(parentBranch.children.SW,node); | |||||
| this._getForceContribution(parentBranch.children.SE,node); | |||||
| } | |||||
| else { // parentBranch must have only one node, if it was empty we wouldnt be here | |||||
| if (parentBranch.children.data.id != node.id) { // if it is not self | |||||
| // duplicate code to reduce function calls to speed up program | |||||
| if (distance == 0) { | |||||
| distance = 0.5*Math.random(); | |||||
| dx = distance; | |||||
| } | |||||
| var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); | |||||
| var fx = dx * gravityForce; | |||||
| var fy = dy * gravityForce; | |||||
| node.fx += fx; | |||||
| node.fy += fy; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. | |||||
| * | |||||
| * @param nodes | |||||
| * @param nodeIndices | |||||
| * @private | |||||
| */ | |||||
| _formBarnesHutTree : function(nodes,nodeIndices) { | |||||
| var node; | |||||
| var nodeCount = nodeIndices.length; | |||||
| var minX = Number.MAX_VALUE, | |||||
| minY = Number.MAX_VALUE, | |||||
| maxX =-Number.MAX_VALUE, | |||||
| maxY =-Number.MAX_VALUE; | |||||
| // get the range of the nodes | |||||
| for (var i = 0; i < nodeCount; i++) { | |||||
| var x = nodes[nodeIndices[i]].x; | |||||
| var y = nodes[nodeIndices[i]].y; | |||||
| if (x < minX) { minX = x; } | |||||
| if (x > maxX) { maxX = x; } | |||||
| if (y < minY) { minY = y; } | |||||
| if (y > maxY) { maxY = y; } | |||||
| } | |||||
| // make the range a square | |||||
| var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y | |||||
| if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize | |||||
| else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize | |||||
| var minimumTreeSize = 1e-5; | |||||
| var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); | |||||
| var halfRootSize = 0.5 * rootSize; | |||||
| var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); | |||||
| // construct the barnesHutTree | |||||
| var barnesHutTree = {root:{ | |||||
| centerOfMass:{x:0,y:0}, // Center of Mass | |||||
| mass:0, | |||||
| range: {minX:centerX-halfRootSize,maxX:centerX+halfRootSize, | |||||
| minY:centerY-halfRootSize,maxY:centerY+halfRootSize}, | |||||
| size: rootSize, | |||||
| calcSize: 1 / rootSize, | |||||
| children: {data:null}, | |||||
| maxWidth: 0, | |||||
| level: 0, | |||||
| childrenCount: 4 | |||||
| }}; | |||||
| this._splitBranch(barnesHutTree.root); | |||||
| // place the nodes one by one recursively | |||||
| for (i = 0; i < nodeCount; i++) { | |||||
| node = nodes[nodeIndices[i]]; | |||||
| this._placeInTree(barnesHutTree.root,node); | |||||
| } | |||||
| // make global | |||||
| this.barnesHutTree = barnesHutTree | |||||
| }, | |||||
| _updateBranchMass : function(parentBranch, node) { | |||||
| var totalMass = parentBranch.mass + node.mass; | |||||
| var totalMassInv = 1/totalMass; | |||||
| parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass; | |||||
| parentBranch.centerOfMass.x *= totalMassInv; | |||||
| parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass; | |||||
| parentBranch.centerOfMass.y *= totalMassInv; | |||||
| parentBranch.mass = totalMass; | |||||
| var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); | |||||
| parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; | |||||
| }, | |||||
| _placeInTree : function(parentBranch,node,skipMassUpdate) { | |||||
| if (skipMassUpdate != true || skipMassUpdate === undefined) { | |||||
| // update the mass of the branch. | |||||
| this._updateBranchMass(parentBranch,node); | |||||
| } | |||||
| if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW | |||||
| if (parentBranch.children.NW.range.maxY > node.y) { // in NW | |||||
| this._placeInRegion(parentBranch,node,"NW"); | |||||
| } | |||||
| else { // in SW | |||||
| this._placeInRegion(parentBranch,node,"SW"); | |||||
| } | |||||
| } | |||||
| else { // in NE or SE | |||||
| if (parentBranch.children.NW.range.maxY > node.y) { // in NE | |||||
| this._placeInRegion(parentBranch,node,"NE"); | |||||
| } | |||||
| else { // in SE | |||||
| this._placeInRegion(parentBranch,node,"SE"); | |||||
| } | |||||
| } | |||||
| }, | |||||
| _placeInRegion : function(parentBranch,node,region) { | |||||
| switch (parentBranch.children[region].childrenCount) { | |||||
| case 0: // place node here | |||||
| parentBranch.children[region].children.data = node; | |||||
| parentBranch.children[region].childrenCount = 1; | |||||
| this._updateBranchMass(parentBranch.children[region],node); | |||||
| break; | |||||
| case 1: // convert into children | |||||
| // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) | |||||
| // we move one node a pixel and we do not put it in the tree. | |||||
| if (parentBranch.children[region].children.data.x == node.x && | |||||
| parentBranch.children[region].children.data.y == node.y) { | |||||
| node.x += Math.random(); | |||||
| node.y += Math.random(); | |||||
| this._placeInTree(parentBranch,node, true); | |||||
| } | |||||
| else { | |||||
| this._splitBranch(parentBranch.children[region]); | |||||
| this._placeInTree(parentBranch.children[region],node); | |||||
| } | |||||
| break; | |||||
| case 4: // place in branch | |||||
| this._placeInTree(parentBranch.children[region],node); | |||||
| break; | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch | |||||
| * after the split is complete. | |||||
| * | |||||
| * @param parentBranch | |||||
| * @private | |||||
| */ | |||||
| _splitBranch : function(parentBranch) { | |||||
| // if the branch is filled with a node, replace the node in the new subset. | |||||
| var containedNode = null; | |||||
| if (parentBranch.childrenCount == 1) { | |||||
| containedNode = parentBranch.children.data; | |||||
| parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; | |||||
| } | |||||
| parentBranch.childrenCount = 4; | |||||
| parentBranch.children.data = null; | |||||
| this._insertRegion(parentBranch,"NW"); | |||||
| this._insertRegion(parentBranch,"NE"); | |||||
| this._insertRegion(parentBranch,"SW"); | |||||
| this._insertRegion(parentBranch,"SE"); | |||||
| if (containedNode != null) { | |||||
| this._placeInTree(parentBranch,containedNode); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This function subdivides the region into four new segments. | |||||
| * Specifically, this inserts a single new segment. | |||||
| * It fills the children section of the parentBranch | |||||
| * | |||||
| * @param parentBranch | |||||
| * @param region | |||||
| * @param parentRange | |||||
| * @private | |||||
| */ | |||||
| _insertRegion : function(parentBranch, region) { | |||||
| var minX,maxX,minY,maxY; | |||||
| var childSize = 0.5 * parentBranch.size; | |||||
| switch (region) { | |||||
| case "NW": | |||||
| minX = parentBranch.range.minX; | |||||
| maxX = parentBranch.range.minX + childSize; | |||||
| minY = parentBranch.range.minY; | |||||
| maxY = parentBranch.range.minY + childSize; | |||||
| break; | |||||
| case "NE": | |||||
| minX = parentBranch.range.minX + childSize; | |||||
| maxX = parentBranch.range.maxX; | |||||
| minY = parentBranch.range.minY; | |||||
| maxY = parentBranch.range.minY + childSize; | |||||
| break; | |||||
| case "SW": | |||||
| minX = parentBranch.range.minX; | |||||
| maxX = parentBranch.range.minX + childSize; | |||||
| minY = parentBranch.range.minY + childSize; | |||||
| maxY = parentBranch.range.maxY; | |||||
| break; | |||||
| case "SE": | |||||
| minX = parentBranch.range.minX + childSize; | |||||
| maxX = parentBranch.range.maxX; | |||||
| minY = parentBranch.range.minY + childSize; | |||||
| maxY = parentBranch.range.maxY; | |||||
| break; | |||||
| } | |||||
| parentBranch.children[region] = { | |||||
| centerOfMass:{x:0,y:0}, | |||||
| mass:0, | |||||
| range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, | |||||
| size: 0.5 * parentBranch.size, | |||||
| calcSize: 2 * parentBranch.calcSize, | |||||
| children: {data:null}, | |||||
| maxWidth: 0, | |||||
| level: parentBranch.level+1, | |||||
| childrenCount: 0 | |||||
| }; | |||||
| }, | |||||
| /** | |||||
| * This function is for debugging purposed, it draws the tree. | |||||
| * | |||||
| * @param ctx | |||||
| * @param color | |||||
| * @private | |||||
| */ | |||||
| _drawTree : function(ctx,color) { | |||||
| if (this.barnesHutTree !== undefined) { | |||||
| ctx.lineWidth = 1; | |||||
| this._drawBranch(this.barnesHutTree.root,ctx,color); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This function is for debugging purposes. It draws the branches recursively. | |||||
| * | |||||
| * @param branch | |||||
| * @param ctx | |||||
| * @param color | |||||
| * @private | |||||
| */ | |||||
| _drawBranch : function(branch,ctx,color) { | |||||
| if (color === undefined) { | |||||
| color = "#FF0000"; | |||||
| } | |||||
| if (branch.childrenCount == 4) { | |||||
| this._drawBranch(branch.children.NW,ctx); | |||||
| this._drawBranch(branch.children.NE,ctx); | |||||
| this._drawBranch(branch.children.SE,ctx); | |||||
| this._drawBranch(branch.children.SW,ctx); | |||||
| } | |||||
| ctx.strokeStyle = color; | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(branch.range.minX,branch.range.minY); | |||||
| ctx.lineTo(branch.range.maxX,branch.range.minY); | |||||
| ctx.stroke(); | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(branch.range.maxX,branch.range.minY); | |||||
| ctx.lineTo(branch.range.maxX,branch.range.maxY); | |||||
| ctx.stroke(); | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(branch.range.maxX,branch.range.maxY); | |||||
| ctx.lineTo(branch.range.minX,branch.range.maxY); | |||||
| ctx.stroke(); | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(branch.range.minX,branch.range.maxY); | |||||
| ctx.lineTo(branch.range.minX,branch.range.minY); | |||||
| ctx.stroke(); | |||||
| /* | |||||
| if (branch.mass > 0) { | |||||
| ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); | |||||
| ctx.stroke(); | |||||
| } | |||||
| */ | |||||
| } | |||||
| }; | |||||
| @ -0,0 +1,245 @@ | |||||
| /** | |||||
| * Created by Alex on 2/6/14. | |||||
| */ | |||||
| var physicsMixin = { | |||||
| /** | |||||
| * Toggling barnes Hut calculation on and off. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _toggleBarnesHut : function() { | |||||
| this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; | |||||
| this._loadSelectedForceSolver(); | |||||
| this.moving = true; | |||||
| this.start(); | |||||
| }, | |||||
| /** | |||||
| * Before calculating the forces, we check if we need to cluster to keep up performance and we check | |||||
| * if there is more than one node. If it is just one node, we dont calculate anything. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _initializeForceCalculation : function() { | |||||
| // stop calculation if there is only one node | |||||
| if (this.nodeIndices.length == 1) { | |||||
| this.nodes[this.nodeIndices[0]]._setForce(0,0); | |||||
| } | |||||
| else { | |||||
| // if there are too many nodes on screen, we cluster without repositioning | |||||
| if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { | |||||
| this.clusterToFit(this.constants.clustering.reduceToNodes, false); | |||||
| } | |||||
| // we now start the force calculation | |||||
| this._calculateForces(); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Calculate the external forces acting on the nodes | |||||
| * Forces are caused by: edges, repulsing forces between nodes, gravity | |||||
| * @private | |||||
| */ | |||||
| _calculateForces : function() { | |||||
| // Gravity is required to keep separated groups from floating off | |||||
| // the forces are reset to zero in this loop by using _setForce instead | |||||
| // of _addForce | |||||
| this._calculateGravitationalForces(); | |||||
| this._calculateNodeForces(); | |||||
| if (this.constants.smoothCurves == true) { | |||||
| this._calculateSpringForcesWithSupport(); | |||||
| } | |||||
| else { | |||||
| this._calculateSpringForces(); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also | |||||
| * handled in the calculateForces function. We then use a quadratic curve with the center node as control. | |||||
| * This function joins the datanodes and invisible (called support) nodes into one object. | |||||
| * We do this so we do not contaminate this.nodes with the support nodes. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _updateCalculationNodes : function() { | |||||
| if (this.constants.smoothCurves == true) { | |||||
| this.calculationNodes = {}; | |||||
| this.calculationNodeIndices = []; | |||||
| for (var nodeId in this.nodes) { | |||||
| if (this.nodes.hasOwnProperty(nodeId)) { | |||||
| this.calculationNodes[nodeId] = this.nodes[nodeId]; | |||||
| } | |||||
| } | |||||
| var supportNodes = this.sectors['support']['nodes']; | |||||
| for (var supportNodeId in supportNodes) { | |||||
| if (supportNodes.hasOwnProperty(supportNodeId)) { | |||||
| if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { | |||||
| this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; | |||||
| } | |||||
| else { | |||||
| supportNodes[supportNodeId]._setForce(0,0); | |||||
| } | |||||
| } | |||||
| } | |||||
| for (var idx in this.calculationNodes) { | |||||
| if (this.calculationNodes.hasOwnProperty(idx)) { | |||||
| this.calculationNodeIndices.push(idx); | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| this.calculationNodes = this.nodes; | |||||
| this.calculationNodeIndices = this.nodeIndices; | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * this function applies the central gravity effect to keep groups from floating off | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _calculateGravitationalForces : function() { | |||||
| var dx, dy, distance, node, i; | |||||
| var nodes = this.calculationNodes; | |||||
| var gravity = this.constants.physics.centralGravity; | |||||
| var gravityForce = 0; | |||||
| for (i = 0; i < this.calculationNodeIndices.length; i++) { | |||||
| node = nodes[this.calculationNodeIndices[i]]; | |||||
| node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. | |||||
| // gravity does not apply when we are in a pocket sector | |||||
| if (this._sector() == "default") { | |||||
| dx = -node.x; | |||||
| dy = -node.y; | |||||
| distance = Math.sqrt(dx*dx + dy*dy); | |||||
| gravityForce = gravity / distance; | |||||
| node.fx = dx * gravityForce; | |||||
| node.fy = dy * gravityForce; | |||||
| } | |||||
| else { | |||||
| node.fx = 0; | |||||
| node.fy = 0; | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * this function calculates the effects of the springs in the case of unsmooth curves. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _calculateSpringForces : function() { | |||||
| var edgeLength, edge, edgeId; | |||||
| var dx, dy, fx, fy, springForce, length; | |||||
| var edges = this.edges; | |||||
| // forces caused by the edges, modelled as springs | |||||
| for (edgeId in edges) { | |||||
| if (edges.hasOwnProperty(edgeId)) { | |||||
| edge = edges[edgeId]; | |||||
| if (edge.connected) { | |||||
| // only calculate forces if nodes are in the same sector | |||||
| if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { | |||||
| edgeLength = edge.length; | |||||
| // this implies that the edges between big clusters are longer | |||||
| edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; | |||||
| dx = (edge.from.x - edge.to.x); | |||||
| dy = (edge.from.y - edge.to.y); | |||||
| length = Math.sqrt(dx * dx + dy * dy); | |||||
| springForce = this.constants.physics.springConstant * (edgeLength - length) / length; | |||||
| fx = dx * springForce; | |||||
| fy = dy * springForce; | |||||
| edge.from.fx += fx; | |||||
| edge.from.fy += fy; | |||||
| edge.to.fx -= fx; | |||||
| edge.to.fy -= fy; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This function calculates the springforces on the nodes, accounting for the support nodes. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _calculateSpringForcesWithSupport : function() { | |||||
| var edgeLength, edge, edgeId, combinedClusterSize; | |||||
| var edges = this.edges; | |||||
| // forces caused by the edges, modelled as springs | |||||
| for (edgeId in edges) { | |||||
| if (edges.hasOwnProperty(edgeId)) { | |||||
| edge = edges[edgeId]; | |||||
| if (edge.connected) { | |||||
| // only calculate forces if nodes are in the same sector | |||||
| if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { | |||||
| if (edge.via != null) { | |||||
| var node1 = edge.to; | |||||
| var node2 = edge.via; | |||||
| var node3 = edge.from; | |||||
| edgeLength = edge.length; | |||||
| combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; | |||||
| // this implies that the edges between big clusters are longer | |||||
| edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; | |||||
| this._calculateSpringForce(node1,node2,0.5*edgeLength); | |||||
| this._calculateSpringForce(node2,node3,0.5*edgeLength); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. | |||||
| * | |||||
| * @param node1 | |||||
| * @param node2 | |||||
| * @param edgeLength | |||||
| * @private | |||||
| */ | |||||
| _calculateSpringForce : function(node1,node2,edgeLength) { | |||||
| var dx, dy, fx, fy, springForce, length; | |||||
| dx = (node1.x - node2.x); | |||||
| dy = (node1.y - node2.y); | |||||
| length = Math.sqrt(dx * dx + dy * dy); | |||||
| springForce = this.constants.physics.springConstant * (edgeLength - length) / length; | |||||
| fx = dx * springForce; | |||||
| fy = dy * springForce; | |||||
| node1.fx += fx; | |||||
| node1.fy += fy; | |||||
| node2.fx -= fx; | |||||
| node2.fy -= fy; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,66 @@ | |||||
| /** | |||||
| * Created by Alex on 2/10/14. | |||||
| */ | |||||
| var repulsionMixin = { | |||||
| /** | |||||
| * Calculate the forces the nodes apply on eachother based on a repulsion field. | |||||
| * This field is linearly approximated. | |||||
| * | |||||
| * @private | |||||
| */ | |||||
| _calculateNodeForces : function() { | |||||
| var dx, dy, angle, distance, fx, fy, combinedClusterSize, | |||||
| repulsingForce, node1, node2, i, j; | |||||
| var nodes = this.calculationNodes; | |||||
| var nodeIndices = this.calculationNodeIndices; | |||||
| // approximation constants | |||||
| var a_base = -2/3; | |||||
| var b = 4/3; | |||||
| // repulsing forces between nodes | |||||
| var nodeDistance = this.constants.physics.repulsion.nodeDistance; | |||||
| var minimumDistance = nodeDistance; | |||||
| // we loop from i over all but the last entree in the array | |||||
| // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j | |||||
| for (i = 0; i < nodeIndices.length-1; i++) { | |||||
| node1 = nodes[nodeIndices[i]]; | |||||
| for (j = i+1; j < nodeIndices.length; j++) { | |||||
| node2 = nodes[nodeIndices[j]]; | |||||
| combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; | |||||
| dx = node2.x - node1.x; | |||||
| dy = node2.y - node1.y; | |||||
| distance = Math.sqrt(dx * dx + dy * dy); | |||||
| minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); | |||||
| var a = a_base / minimumDistance; | |||||
| if (distance < 2*minimumDistance) { | |||||
| if (distance < 0.5*minimumDistance) { | |||||
| repulsingForce = 1.0; | |||||
| } | |||||
| else { | |||||
| repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) | |||||
| } | |||||
| // amplify the repulsion for clusters. | |||||
| repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; | |||||
| repulsingForce = repulsingForce/distance; | |||||
| fx = dx * repulsingForce; | |||||
| fy = dy * repulsingForce; | |||||
| node1.fx -= fx; | |||||
| node1.fy -= fy; | |||||
| node2.fx += fx; | |||||
| node2.fy += fy; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,58 @@ | |||||
| // Update the version numbers and library sizes in index.html | |||||
| var fs = require('fs'), | |||||
| zlib = require('zlib'); | |||||
| var VIS_ZIP = './dist/vis.js', | |||||
| INDEX = 'index.html'; | |||||
| // read version from dist/vis.js | |||||
| function version(callback) { | |||||
| fs.readFile(VIS_ZIP, function (err, data) { | |||||
| if (!err) { | |||||
| var match = /@version\s*([\w\.-]*)/i.exec(data); | |||||
| var version = undefined; | |||||
| if (match) { | |||||
| version = match[1]; | |||||
| } | |||||
| callback(null, version); | |||||
| } | |||||
| else { | |||||
| callback(err); | |||||
| } | |||||
| }); | |||||
| } | |||||
| // update version and library sizes in index.md | |||||
| function updateVersion(version, callback) { | |||||
| fs.readFile(INDEX, function (err, data) { | |||||
| if (!err) { | |||||
| data = String(data); | |||||
| data = data.replace(/<span class="version">([\w\.-]*)<\/span>/g, | |||||
| '<span class="version">' + version + '</span>'); | |||||
| fs.writeFile(INDEX, data, callback); | |||||
| } | |||||
| else { | |||||
| callback(err); | |||||
| } | |||||
| }); | |||||
| } | |||||
| version(function (err, version) { | |||||
| console.log('version: ' + version); | |||||
| if (version) { | |||||
| updateVersion(version, function (err, res) { | |||||
| if (err) { | |||||
| console.log(err); | |||||
| } | |||||
| else { | |||||
| console.log('done'); | |||||
| } | |||||
| }); | |||||
| } | |||||
| else { | |||||
| } | |||||
| }); | |||||