Browse Source

Merged develop into hammer2 branch

flowchartTest
jos 9 years ago
parent
commit
7e6261a328
443 changed files with 26063 additions and 157496 deletions
  1. +59
    -1
      HISTORY.md
  2. +1
    -1
      LICENSE-MIT
  3. +1
    -1
      NOTICE
  4. +1
    -1
      README.md
  5. +1
    -1
      bower.json
  6. +1
    -2
      dist/vis.css
  7. +24312
    -23699
      dist/vis.js
  8. +1
    -1
      dist/vis.map
  9. +1
    -1
      dist/vis.min.css
  10. +16
    -16
      dist/vis.min.js
  11. +6
    -5
      docs/css/style.css
  12. +0
    -12
      docs/graph2d.html
  13. +65
    -14
      docs/network.html
  14. +65
    -20
      docs/timeline.html
  15. +1
    -1
      examples/graph2d/08_performance.html
  16. +1
    -1
      examples/graph2d/13_localization.html
  17. +10
    -1
      examples/network/02_random_nodes.html
  18. +13
    -3
      examples/network/06_groups.html
  19. +0
    -2
      examples/network/09_sizing.html
  20. +10
    -1
      examples/network/15_dot_language_playground.html
  21. +9
    -0
      examples/network/18_fully_random_nodes_clustering.html
  22. +9
    -0
      examples/network/19_scale_free_graph_clustering.html
  23. +9
    -0
      examples/network/20_navigation.html
  24. +7
    -1
      examples/network/23_hierarchical_layout.html
  25. +8
    -0
      examples/network/24_hierarchical_layout_userdefined.html
  26. +8
    -0
      examples/network/31_localization.html
  27. +9
    -0
      examples/network/32_hierarchicaLayoutMethods.html
  28. +7
    -0
      examples/network/33_animation.html
  29. +100
    -0
      examples/network/34_circular_images.html
  30. +15
    -16
      examples/network/35_label_stroke.html
  31. +85
    -0
      examples/network/36_HTML_in_Nodes.html
  32. +4
    -4
      examples/network/37_label_alignment.html
  33. BIN
      examples/network/img/indonesia/1.png
  34. BIN
      examples/network/img/indonesia/10.png
  35. BIN
      examples/network/img/indonesia/11.png
  36. BIN
      examples/network/img/indonesia/12.png
  37. BIN
      examples/network/img/indonesia/13.png
  38. BIN
      examples/network/img/indonesia/14.png
  39. BIN
      examples/network/img/indonesia/2.png
  40. BIN
      examples/network/img/indonesia/3.png
  41. BIN
      examples/network/img/indonesia/4.png
  42. BIN
      examples/network/img/indonesia/5.png
  43. BIN
      examples/network/img/indonesia/6.png
  44. BIN
      examples/network/img/indonesia/7.png
  45. BIN
      examples/network/img/indonesia/8.png
  46. BIN
      examples/network/img/indonesia/9.png
  47. +4
    -0
      examples/network/index.html
  48. +1
    -1
      examples/timeline/03_performance.html
  49. +1
    -1
      examples/timeline/05_groups.html
  50. +1
    -1
      examples/timeline/13_past_and_future.html
  51. +53
    -0
      examples/timeline/32_grid_styling.html
  52. +1
    -0
      examples/timeline/index.html
  53. +3
    -2
      lib/graph3d/Graph3d.js
  54. +383
    -242
      lib/network/Edge.js
  55. +0
    -3
      lib/network/Groups.js
  56. +37
    -15
      lib/network/Images.js
  57. +206
    -98
      lib/network/Network.js
  58. +194
    -42
      lib/network/Node.js
  59. +32
    -44
      lib/network/mixins/HierarchicalLayoutMixin.js
  60. +19
    -8
      lib/network/mixins/ManipulationMixin.js
  61. +3
    -0
      lib/network/mixins/MixinLoader.js
  62. +2
    -2
      lib/network/mixins/physics/BarnesHutMixin.js
  63. +13
    -0
      lib/network/mixins/physics/PhysicsMixin.js
  64. +8
    -2
      lib/network/mixins/physics/RepulsionMixin.js
  65. +4
    -2
      lib/timeline/Core.js
  66. +14
    -8
      lib/timeline/Range.js
  67. +73
    -0
      lib/timeline/TimeStep.js
  68. +17
    -14
      lib/timeline/component/DataAxis.js
  69. +54
    -47
      lib/timeline/component/LineGraph.js
  70. +60
    -39
      lib/timeline/component/TimeAxis.js
  71. +1
    -2
      lib/timeline/component/css/timeaxis.css
  72. +28
    -76
      lib/util.js
  73. +2
    -2
      package.json
  74. +14
    -0
      test/timeline.html
  75. +0
    -470
      www2/css/bootstrap-theme.css
  76. +0
    -1
      www2/css/bootstrap-theme.css.map
  77. +0
    -5
      www2/css/bootstrap-theme.min.css
  78. +0
    -6332
      www2/css/bootstrap.css
  79. +0
    -1
      www2/css/bootstrap.css.map
  80. +0
    -5
      www2/css/bootstrap.min.css
  81. +0
    -144
      www2/css/carousel.css
  82. +0
    -87
      www2/css/prettify.css
  83. BIN
      www2/dist/img/network/acceptDeleteIcon.png
  84. BIN
      www2/dist/img/network/addNodeIcon.png
  85. BIN
      www2/dist/img/network/backIcon.png
  86. BIN
      www2/dist/img/network/connectIcon.png
  87. BIN
      www2/dist/img/network/cross.png
  88. BIN
      www2/dist/img/network/cross2.png
  89. BIN
      www2/dist/img/network/deleteIcon.png
  90. BIN
      www2/dist/img/network/downArrow.png
  91. BIN
      www2/dist/img/network/editIcon.png
  92. BIN
      www2/dist/img/network/leftArrow.png
  93. BIN
      www2/dist/img/network/minus.png
  94. BIN
      www2/dist/img/network/plus.png
  95. BIN
      www2/dist/img/network/rightArrow.png
  96. BIN
      www2/dist/img/network/upArrow.png
  97. BIN
      www2/dist/img/network/zoomExtends.png
  98. BIN
      www2/dist/img/timeline/delete.png
  99. +0
    -795
      www2/dist/vis.css
  100. +0
    -34028
      www2/dist/vis.js

+ 59
- 1
HISTORY.md View File

@ -2,22 +2,80 @@
http://visjs.org http://visjs.org
## not yet released, version 3.7.3-SNAPSHOT
## 2015-01-16, version 3.9.0
### Network
- Reverted change in image class, fixed bug #552
- Improved (not neccesarily fixed) the fontFill offset between different browsers. #365
- Fixed dashed lines on firefox on Unix systems
- Altered the Manipulation Mixin to be succesfully destroyed from memory when calling destroy();
- Improved drawing of arrowheads on smooth curves. #349
- Caught case where click originated on external DOM element and drag progressed to vis.
- Added label stroke support to Nodes, Edges & Groups as per-object or global settings. Thank you @klmdb!
- Reverted patch that made nodes return to 'default' setting if no group was assigned to fix issue #561. The correct way to 'remove' a group from a node is to assign it a different one.
- Made the node/edge selected by the popup system the same as selected by the click-to-select system. Thank you @pavlos256!
- Improved edit edge control nodes positions, altered style a little.
- Fixed issue #564 by resetting state to initial when no callback is performed in the return function.
- Added condition to Repulsion similar to BarnesHut to ensure nodes do not overlap.
- Added labelAlignment option to edges. Thanks @T-rav!
- Close active sessions in dataManipulation when calling setData().
- Fixed alignment issue with edgelabels
### Timeline
- Added byUser flag to options of the rangechange and rangechanged event.
## 2015-01-09, version 3.8.0
### General
- Updated to moment.js v2.9.0
### Network ### Network
- Fixed flipping of hierarchical network on update when using RL and DU. - Fixed flipping of hierarchical network on update when using RL and DU.
- Added zoomExtentOnStabilize option to network. - Added zoomExtentOnStabilize option to network.
- Improved destroy function, added them to the examples.
- Nodes now have bounding boxes that are used for zoomExtent.
- Made physics more stable (albeit a little slower).
- Added a check so only one 'activator' overlay is created on clickToUse.
- Made global color options for edges overrule the inheritColors.
- Improved cleaning up of the physics configuration on destroy and in options.
- Made nodes who lost their group revert back to default color.
- Changed group behaviour, groups now extend the options, not replace. This allows partial defines of color.
- Fixed bug where box shaped nodes did not use hover color.
- Fixed Locales docs.
- When hovering over a node that does not have a title, the title of one of the connected edges that HAS a title is no longer shown.
- Fixed error in repulsion physics model.
- Improved physics handling for smoother network simulation.
- Fixed infinite loop when an image can not be found and no brokenImage is provided.
- Added getBoundingBox method.
- Community fix for SVG images in IE11, thanks @dponch!
- Fixed repeating stabilized event when the network is already stabilized.
- Added circularImages, thanks for the contribution @brendon1982!
- Stopped infinite loop when brokenImage is also not available.
- Changed util color functions so they don't need eval. Thanks @naskooskov!
### Graph2d ### Graph2d
- Fixed round-off errors of zero on the y-axis. - Fixed round-off errors of zero on the y-axis.
- added show major/minor lines options to dataAxis. - added show major/minor lines options to dataAxis.
- Fixed adapting to width and height changes.
- Added a check so only one 'activator' overlay is created on clickToUse.
- DataAxis width option now draws correctly.
### Timeline ### Timeline
- Implemented support for styling of the vertical grid.
- Support for custom date formatting of the labels on the time axis. - Support for custom date formatting of the labels on the time axis.
- added show major/minor lines options to timeline. - added show major/minor lines options to timeline.
- Added a check so only one 'activator' overlay is created on clickToUse.
### Graph3d
- Fixed mouse coordinates for tooltips.
## 2014-12-09, version 3.7.2 ## 2014-12-09, version 3.7.2

+ 1
- 1
LICENSE-MIT View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014 Almende B.V.
Copyright (c) 2014-2015 Almende B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

+ 1
- 1
NOTICE View File

@ -1,5 +1,5 @@
Vis.js Vis.js
Copyright 2010-2014 Almende B.V.
Copyright 2010-2015 Almende B.V.
Vis.js is dual licensed under both Vis.js is dual licensed under both

+ 1
- 1
README.md View File

@ -287,7 +287,7 @@ Then run the tests:
## License ## License
Copyright (C) 2010-2014 Almende B.V.
Copyright (C) 2010-2015 Almende B.V.
Vis.js is dual licensed under both Vis.js is dual licensed under both

+ 1
- 1
bower.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "name": "vis",
"version": "3.7.3-SNAPSHOT",
"version": "3.9.0",
"main": ["dist/vis.min.js", "dist/vis.min.css"], "main": ["dist/vis.min.js", "dist/vis.min.css"],
"description": "A dynamic, browser-based visualization library.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",

+ 1
- 2
dist/vis.css View File

@ -320,8 +320,7 @@
.vis.timeline .timeaxis .grid.vertical { .vis.timeline .timeaxis .grid.vertical {
position: absolute; position: absolute;
width: 0;
border-right: 1px solid;
border-left: 1px solid;
} }
.vis.timeline .timeaxis .grid.minor { .vis.timeline .timeaxis .grid.minor {

+ 24312
- 23699
dist/vis.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.min.css
File diff suppressed because it is too large
View File


+ 16
- 16
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 6
- 5
docs/css/style.css View File

@ -31,8 +31,8 @@ h1 {
h2 { h2 {
padding-top: 20px; padding-top: 20px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid #a0c0f0;
color: #2B7CE9;
border-bottom: 1px solid #e0e0e0;
color: #064880;
} }
h3 { h3 {
@ -44,12 +44,13 @@ a > img {
} }
a { a {
color: #2B7CE9;
color: #064880;
text-decoration: none; text-decoration: none;
} }
a:visited { a:visited {
color: #2E60A4;
color: #064880;
text-decoration: none;
} }
a:hover { a:hover {
@ -78,5 +79,5 @@ td {
p.important_note { p.important_note {
color: #3a6baa; color: #3a6baa;
font-weight:bold;
font-weight:bold;
} }

+ 0
- 12
docs/graph2d.html View File

@ -452,18 +452,6 @@ The options colored in green can also be used as options for the groups. All opt
<td>true</td> <td>true</td>
<td>Toggle the drawing of the major labels on the Y axis.</td> <td>Toggle the drawing of the major labels on the Y axis.</td>
</tr> </tr>
<tr>
<td>dataAxis.showMajorLines</td>
<td>Boolean</td>
<td>true</td>
<td>Toggle the drawing of the major lines on the Y axis.</td>
</tr>
<tr>
<td>dataAxis.showMinorLines</td>
<td>Boolean</td>
<td>true</td>
<td>Toggle the drawing of the major lines on the Y axis.</td>
</tr>
<tr> <tr>
<td>dataAxis.icons</td> <td>dataAxis.icons</td>
<td>Boolean</td> <td>Boolean</td>

+ 65
- 14
docs/network.html View File

@ -932,6 +932,18 @@ All options defined per-node override these global settings.
<td>undefined</td> <td>undefined</td>
<td>If a color is supplied, there will be a background color behind the label. If left undefined, no background color is shown.</td> <td>If a color is supplied, there will be a background color behind the label. If left undefined, no background color is shown.</td>
</tr> </tr>
<tr>
<td class="greenField">fontStrokeWidth</td>
<td>Number</td>
<td>0</td>
<td>The width of the label stroke (border around label's text) in pixels.</td>
</tr>
<tr>
<td class="greenField">fontStrokeColor</td>
<td>String</td>
<td>'white'</td>
<td>The color of the label stroke.</td>
</tr>
<tr> <tr>
<td class="greenField">shape</td> <td class="greenField">shape</td>
@ -940,11 +952,11 @@ All options defined per-node override these global settings.
<td>Define the shape for the node. <td>Define the shape for the node.
Choose from Choose from
<code>ellipse</code> (default), <code>circle</code>, <code>box</code>, <code>ellipse</code> (default), <code>circle</code>, <code>box</code>,
<code>database</code>, <code>image</code>, <code>label</code>, <code>dot</code>,
<code>database</code>, <code>image</code>, <code>circularImage</code>, <code>label</code>, <code>dot</code>,
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>. <code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>.
<br><br> <br><br>
In case of <code>image</code>, a property with name <code>image</code> must
In case of <code>image</code> and <code>circularImage</code>, a property with name <code>image</code> must
be provided, containing image urls. be provided, containing image urls.
<br><br> <br><br>
@ -1156,15 +1168,19 @@ var options = {
<td>Font fill for the background color of the text label of the edge. <td>Font fill for the background color of the text label of the edge.
Only applicable when property <code>label</code> is defined.</td> Only applicable when property <code>label</code> is defined.</td>
</tr> </tr>
<tr> <tr>
<td class="greenField">style</td>
<td>string</td>
<td>line</td>
<td>Define a line style for the edge.
Choose from <code>line</code> (default), <code>arrow</code>,
<code>arrow-center</code>, or <code>dash-line</code>.
</td>
<td class="greenField">fontStrokeWidth</td>
<td>Number</td>
<td>0</td>
<td>The width of the label stroke (border around label's text) in pixels.
Only applicable when property <code>label</code> is defined.</td>
</tr>
<tr>
<td class="greenField">fontStrokeColor</td>
<td>String</td>
<td>'white'</td>
<td>The color of the label stroke.
Only applicable when property <code>label</code> is defined.</td>
</tr> </tr>
<tr> <tr>
<td class="greenField">inheritColor</td> <td class="greenField">inheritColor</td>
@ -1173,6 +1189,23 @@ var options = {
<td>Possible values: <code>"to","from", true, false</code>. If this value is set to false, the edge color information is used. If the value is set to true or "from", <td>Possible values: <code>"to","from", true, false</code>. If this value is set to false, the edge color information is used. If the value is set to true or "from",
the color data from the borders of the "from" node is used. If this value is "to", the color data from the borders of the "to" node is used.</td> the color data from the borders of the "from" node is used. If this value is "to", the color data from the borders of the "to" node is used.</td>
</tr> </tr>
<tr>
<td class="greenField">labelAlignment</td>
<td>String</td>
<td>horizontal</td>
<td>Possible values: <code>"line-above", "line-center", "line-below"</code>. The alignment of the label when drawn on the edge.
If <code>horizontal</code> it will align the label absolute horizontial.</td>
</tr>
<tr>
<td class="greenField">style</td>
<td>string</td>
<td>line</td>
<td>Define a line style for the edge.
Choose from <code>line</code> (default), <code>arrow</code>,
<code>arrow-center</code>, or <code>dash-line</code>.
</td>
</tr>
<tr> <tr>
<td class="greenField">width</td> <td class="greenField">width</td>
<td>number</td> <td>number</td>
@ -1316,6 +1349,18 @@ var nodes = [
<td>14</td> <td>14</td>
<td>Font size for the node in pixels.</td> <td>Font size for the node in pixels.</td>
</tr> </tr>
<tr>
<td>fontStrokeWidth</td>
<td>Number</td>
<td>0</td>
<td>The width of the label stroke (border around label's text) in pixels.</td>
</tr>
<tr>
<td>fontStrokeColor</td>
<td>String</td>
<td>"white"</td>
<td>The color of the label stroke.</td>
</tr>
<tr> <tr>
<td>shape</td> <td>shape</td>
<td>String</td> <td>String</td>
@ -2017,15 +2062,15 @@ To load a locale into the Timeline not supported by default, one can add a new l
locales: { locales: {
// create a new locale (text strings should be replaced with localized strings) // create a new locale (text strings should be replaced with localized strings)
mylocale: { mylocale: {
add: 'Add Node',
edit: 'Edit', edit: 'Edit',
link: 'Add Link',
del: 'Delete selected', del: 'Delete selected',
back: 'Back',
addNode: 'Add Node',
addEdge: 'Add Edge',
editNode: 'Edit Node', editNode: 'Edit Node',
editEdge: 'Edit Edge', editEdge: 'Edit Edge',
back: 'Back',
addDescription: 'Click in an empty space to place a new node.', addDescription: 'Click in an empty space to place a new node.',
linkDescription: 'Click on a node and drag the edge to another node to connect them.',
edgeDescription: 'Click on a node and drag the edge to another node to connect them.',
editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.',
createEdgeError: 'Cannot link edges to a cluster.', createEdgeError: 'Cannot link edges to a cluster.',
deleteClusterError: 'Clusters cannot be deleted.' deleteClusterError: 'Clusters cannot be deleted.'
@ -2155,6 +2200,12 @@ var options = {
<td>Returns the x and y coodinates of the center of the screen (in canvas space). <td>Returns the x and y coodinates of the center of the screen (in canvas space).
</td> </td>
</tr> </tr>
<tr>
<td>getBoundingBox()</td>
<td>Object</td>
<td>Returns a bounding box for the node including label in the format: {top:Number,left:Number,right:Number,bottom:Number}. These values are in canvas space.
</td>
</tr>
<tr> <tr>
<td>getSelection()</td> <td>getSelection()</td>
<td>Array of ids</td> <td>Array of ids</td>

+ 65
- 20
docs/timeline.html View File

@ -742,24 +742,6 @@ var options = {
<code>showMinorLabels</code> are false, no horizontal axis will be <code>showMinorLabels</code> are false, no horizontal axis will be
visible.</td> visible.</td>
</tr> </tr>
<tr>
<td>showMajorLines</td>
<td>boolean</td>
<td>true</td>
<td>By default, the timeline shows both minor and major date lines on the
time axis. You can use this option to hide the lines from the major dates.
</tr>
<tr>
<td>showMinorLines</td>
<td>boolean</td>
<td>true</td>
<td>By default, the timeline shows both minor and major date lines on the
time axis. You can use this option to hide the lines from the minor dates.
</tr>
<tr> <tr>
<td>stack</td> <td>stack</td>
<td>Boolean</td> <td>Boolean</td>
@ -1071,24 +1053,26 @@ timeline.off('select', onSelect);
<tr> <tr>
<td>rangechange</td> <td>rangechange</td>
<td>Fired repeatedly when the user is dragging the timeline window.
<td>Fired repeatedly when the timeline window is being changed.
</td> </td>
<td> <td>
<ul> <ul>
<li><code>start</code> (Number): timestamp of the current start of the window.</li> <li><code>start</code> (Number): timestamp of the current start of the window.</li>
<li><code>end</code> (Number): timestamp of the current end of the window.</li> <li><code>end</code> (Number): timestamp of the current end of the window.</li>
<li><code>byUser</code> (Boolean): change happened because of user drag/zoom.</li>
</ul> </ul>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>rangechanged</td> <td>rangechanged</td>
<td>Fired once after the user has dragged the timeline window.
<td>Fired once after the timeline window has been changed.
</td> </td>
<td> <td>
<ul> <ul>
<li><code>start</code> (Number): timestamp of the current start of the window.</li> <li><code>start</code> (Number): timestamp of the current start of the window.</li>
<li><code>end</code> (Number): timestamp of the current end of the window.</li> <li><code>end</code> (Number): timestamp of the current end of the window.</li>
<li><code>byUser</code> (Boolean): change happened because of user drag/zoom.</li>
</ul> </ul>
</td> </td>
</tr> </tr>
@ -1344,6 +1328,67 @@ To load a locale into the Timeline not supported by default, one can add a new l
&lt;/style&gt; &lt;/style&gt;
</pre> </pre>
<h3 id="Grid_Backgrounds">Grid Backgrounds</h3>
<p>
The background grid of the time axis can be styled, for example to highlight
weekends or to create grids with an alternating white/lightgray background.
</p>
<p>
Depending on the zoom level, the grids get certain css classes attached.
The following classes are available:
</p>
<table>
<tr>
<th>Description</th><th>values</th>
</tr>
<tr>
<td>Alternating columns</td><td><code>even</code>, <code>odd</code></td>
</tr>
<tr>
<td>Current date</td><td><code>today</code>, <code>tomorrow</code>, <code>yesterday</code>, <code>current-week</code>, <code>current-month</code>, <code>current-year</code></td>
</tr>
<tr>
<td>Hours</td><td><code>0h</code>, <code>1h</code>, ..., <code>23h</code></td>
</tr>
<tr>
<td>Grouped hours</td><td><code>0-4h</code> to <code>20-24h</code></td>
</tr>
<tr>
<td>Weekday</td><td><code>monday</code>, <code>tuesday</code>, <code>wednesday</code>, <code>thursday</code>, <code>friday</code>, <code>saturday</code>, <code>sunday</code></td>
</tr>
<tr>
<td>Days</td><td><code>date1</code>, <code>date2</code>, ..., <code>date31</code></td>
</tr>
<tr>
<td>Months</td><td><code>januari</code>, <code>februari</code>, <code>march</code>, <code>april</code>, <code>may</code>, <code>june</code>, <code>july</code>, <code>august</code>, <code>september</code>, <code>october</code>, <code>november</code>, <code>december</code></td>
</tr>
<tr>
<td>Years</td><td><code>year2014</code>, <code>year2015</code>, ...</td>
</tr>
</table>
<p>Examples:</p>
<pre class="prettyprint lang-html">&lt;style&gt;
/* alternating column backgrounds */
.vis.timeline .timeaxis .grid.odd {
background: #f5f5f5;
}
/* gray background in weekends, white text color */
.vis.timeline .timeaxis .grid.saturday,
.vis.timeline .timeaxis .grid.sunday {
background: gray;
}
.vis.timeline .timeaxis .text.saturday,
.vis.timeline .timeaxis .text.sunday {
color: white;
}
&lt;/style&gt;
</pre>
<h2 id="Data_Policy">Data Policy</h2> <h2 id="Data_Policy">Data Policy</h2>
<p> <p>
All code and data is processed and rendered in the browser. All code and data is processed and rendered in the browser.

+ 1
- 1
examples/graph2d/08_performance.html View File

@ -15,7 +15,7 @@
</style> </style>
<!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js --> <!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.3.1/moment.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script src="../../dist/vis.js"></script> <script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />

+ 1
- 1
examples/graph2d/13_localization.html View File

@ -13,7 +13,7 @@
} }
</style> </style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment-with-langs.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment-with-langs.min.js"></script>
<script src="../../dist/vis.js"></script> <script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head> </head>

+ 10
- 1
examples/network/02_random_nodes.html View File

@ -22,7 +22,16 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];
@ -75,7 +84,7 @@
nodes: nodes, nodes: nodes,
edges: edges edges: edges
}; };
var options = {stabilization:false};
var options = {stabilize:false};
network = new vis.Network(container, data, options); network = new vis.Network(container, data, options);
// add event listeners // add event listeners

+ 13
- 3
examples/network/06_groups.html View File

@ -22,14 +22,24 @@
var nodes = null; var nodes = null;
var edges = null; var edges = null;
var network = null; var network = null;
var nodesData = null;
google.load('visualization', '1'); google.load('visualization', '1');
// Set callback to run when API is loaded // Set callback to run when API is loaded
google.setOnLoadCallback(draw); google.setOnLoadCallback(draw);
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
// Called when the Visualization API is loaded. // Called when the Visualization API is loaded.
function draw() { function draw() {
destroy();
var from, to; var from, to;
nodes = []; nodes = [];
@ -121,11 +131,11 @@
nodeOffset += nodeCount; nodeOffset += nodeCount;
group++; group++;
} }
nodesData = new vis.DataSet(nodes);
// create a network // create a network
var container = document.getElementById('mynetwork'); var container = document.getElementById('mynetwork');
var data = { var data = {
nodes: nodes,
nodes: nodesData,
edges: edges edges: edges
}; };
var options = { var options = {
@ -137,6 +147,7 @@
}; };
network = new vis.Network(container, data, options); network = new vis.Network(container, data, options);
} }
</script> </script>
</head> </head>
@ -149,7 +160,6 @@
<input type="submit" value="Go"> <input type="submit" value="Go">
</form> </form>
<br> <br>
<div id="mynetwork"></div> <div id="mynetwork"></div>
</body> </body>

+ 0
- 2
examples/network/09_sizing.html View File

@ -22,8 +22,6 @@
var edges = null; var edges = null;
var network = null; var network = null;
var DIR = 'img/soft-scraps-icons/';
function draw() { function draw() {
// create people. // create people.
// value corresponds with the age of the person // value corresponds with the age of the person

+ 10
- 1
examples/network/15_dot_language_playground.html View File

@ -80,7 +80,8 @@
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
var network, data;
var network = null;
var data = null;
var btnDraw = document.getElementById('draw'); var btnDraw = document.getElementById('draw');
var txtData = document.getElementById('data'); var txtData = document.getElementById('data');
@ -92,8 +93,16 @@
network.redraw() network.redraw()
}; };
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
// parse and draw the data // parse and draw the data
function draw () { function draw () {
destroy();
try { try {
txtError.innerHTML = ''; txtError.innerHTML = '';

+ 9
- 0
examples/network/18_fully_random_nodes_clustering.html View File

@ -22,7 +22,16 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
// randomly create some nodes and edges // randomly create some nodes and edges

+ 9
- 0
examples/network/19_scale_free_graph_clustering.html View File

@ -22,7 +22,16 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];

+ 9
- 0
examples/network/20_navigation.html View File

@ -41,7 +41,16 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];

+ 7
- 1
examples/network/23_hierarchical_layout.html View File

@ -22,9 +22,15 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];

+ 8
- 0
examples/network/24_hierarchical_layout_userdefined.html View File

@ -23,7 +23,15 @@
var network = null; var network = null;
var directionInput = document.getElementById("direction"); var directionInput = document.getElementById("direction");
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];

+ 8
- 0
examples/network/31_localization.html View File

@ -60,7 +60,15 @@
var edges = null; var edges = null;
var network = null; var network = null;
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
nodes = []; nodes = [];
edges = []; edges = [];
var connectionCount = []; var connectionCount = [];

+ 9
- 0
examples/network/32_hierarchicaLayoutMethods.html View File

@ -21,7 +21,16 @@
var network = null; var network = null;
var layoutMethod = "hubsize"; var layoutMethod = "hubsize";
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
var nodes = []; var nodes = [];
var edges = []; var edges = [];
// randomly create some nodes and edges // randomly create some nodes and edges

+ 7
- 0
examples/network/33_animation.html View File

@ -46,8 +46,15 @@
var statusUpdateSpan; var statusUpdateSpan;
var finishMessage = ""; var finishMessage = "";
function destroy() {
if (network !== null) {
network.destroy();
network = null;
}
}
function draw() { function draw() {
destroy();
statusUpdateSpan = document.getElementById("statusUpdate"); statusUpdateSpan = document.getElementById("statusUpdate");
doButton = document.getElementById("btnDo"); doButton = document.getElementById("btnDo");
focusButton = document.getElementById("btnFocus"); focusButton = document.getElementById("btnFocus");

+ 100
- 0
examples/network/34_circular_images.html View File

@ -0,0 +1,100 @@
<!doctype html>
<html>
<head>
<title>Network | Circular images</title>
<style type="text/css">
body {
font: 10pt arial;
}
#mynetwork {
width: 800px;
height: 800px;
border: 1px solid lightgray;
background-color:#333333;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var DIR = 'img/soft-scraps-icons/';
var nodes = null;
var edges = null;
var network = null;
// Called when the Visualization API is loaded.
function draw() {
// create people.
// value corresponds with the age of the person
var DIR = 'img/indonesia/';
nodes = [
{id: 1, shape: 'circularImage', image: DIR + '1.png'},
{id: 2, shape: 'circularImage', image: DIR + '2.png'},
{id: 3, shape: 'circularImage', image: DIR + '3.png'},
{id: 4, label:"pictures by this guy!", shape: 'circularImage', image: DIR + '4.png'},
{id: 5, shape: 'circularImage', image: DIR + '5.png'},
{id: 6, shape: 'circularImage', image: DIR + '6.png'},
{id: 7, shape: 'circularImage', image: DIR + '7.png'},
{id: 8, shape: 'circularImage', image: DIR + '8.png'},
{id: 9, shape: 'circularImage', image: DIR + '9.png'},
{id: 10, shape: 'circularImage', image: DIR + '10.png'},
{id: 11, shape: 'circularImage', image: DIR + '11.png'},
{id: 12, shape: 'circularImage', image: DIR + '12.png'},
{id: 13, shape: 'circularImage', image: DIR + '13.png'},
{id: 14, shape: 'circularImage', image: DIR + '14.png'},
{id: 15, label:"when images\nfail\nto load", shape: 'circularImage', image: DIR + 'missing.png', brokenImage: DIR + 'missingBrokenImage.png'}
];
// create connetions between people
// value corresponds with the amount of contact between two people
edges = [
{from: 1, to: 2},
{from: 2, to: 3},
{from: 2, to: 4},
{from: 4, to: 5},
{from: 4, to: 10},
{from: 4, to: 6},
{from: 6, to: 7},
{from: 7, to: 8},
{from: 8, to: 9},
{from: 8, to: 10},
{from: 10, to: 11},
{from: 11, to: 12},
{from: 12, to: 13},
{from: 13, to: 14},
{from: 14, to: 15}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
nodes: {
borderWidth:4,
color: {
border: '#222222',
background: '#666666'
},
fontColor:'#eeeeee'
},
edges: {
color: 'lightgray'
}
};
network = new vis.Network(container, data, options);
}
</script>
</head>
<body onload="draw()">
<div id="mynetwork"></div>
<div id="info"></div>
</body>
</html>

www2/examples/network/07_selections.html → examples/network/35_label_stroke.html View File

@ -1,7 +1,7 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<title>Network | Selections</title>
<title>Network | Label stroke</title>
<script type="text/javascript" src="../../dist/vis.js"></script> <script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
@ -11,6 +11,7 @@
width: 400px; width: 400px;
height: 400px; height: 400px;
border: 1px solid lightgray; border: 1px solid lightgray;
background: #d1d1d1;
} }
</style> </style>
</head> </head>
@ -18,12 +19,11 @@
<body> <body>
<div id="mynetwork"></div> <div id="mynetwork"></div>
<div id="info"></div>
<script type="text/javascript"> <script type="text/javascript">
// create an array with nodes // create an array with nodes
var nodes = [ var nodes = [
{id: 1, label: 'Node 1'},
{id: 1, label: 'Node 1', fontStrokeWidth : 5, fontStrokeColor: 'white'},
{id: 2, label: 'Node 2'}, {id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'}, {id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'}, {id: 4, label: 'Node 4'},
@ -32,8 +32,8 @@
// create an array with edges // create an array with edges
var edges = [ var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 1, to: 2, label: 'edgeLabel', fontStrokeWidth : 2, fontStrokeColor : '#00ff00'},
{from: 1, to: 3, label: 'edgeLabel'},
{from: 2, to: 4}, {from: 2, to: 4},
{from: 2, to: 5} {from: 2, to: 5}
]; ];
@ -45,19 +45,18 @@
edges: edges edges: edges
}; };
var options = { var options = {
nodes: {
shape: 'box'
nodes : {
shape : 'dot',
fontStrokeWidth : 1,
fontStrokeColor : '#d1d1d1'
},
edges : {
fontStrokeWidth : 1,
fontStrokeColor : '#d1d1d1',
fontFill : 'none'
} }
}; };
network = new vis.Network(container, data, options);
// add event listener
network.on('select', function(properties) {
document.getElementById('info').innerHTML += 'selection: ' + JSON.stringify(properties) + '<br>';
});
// set initial selection (id's of some nodes)
network.selectNodes([3, 4, 5]);
var network = new vis.Network(container, data, options);
</script> </script>
</body> </body>

+ 85
- 0
examples/network/36_HTML_in_Nodes.html View File

@ -0,0 +1,85 @@
<!doctype html>
<html>
<head>
<title>Network | HTML in nodex</title>
<style type="text/css">
body {
font: 10pt arial;
}
#mynetwork {
width: 600px;
height: 600px;
border: 1px solid lightgray;
background-color:#eeeeee;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var nodes = null;
var edges = null;
var network = null;
var DIR = 'img/refresh-cl/';
var LENGTH_MAIN = 150;
var LENGTH_SUB = 50;
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="243" height="65">' +
'<rect x="0" y="0" width="100%" height="100%" fill="#7890A7" stroke-width="20" stroke="#ffffff" ></rect>' +
'<foreignObject x="15" y="10" width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like' +
'<span style="color:white; text-shadow:0 0 20px #000000;">' +
'cheese</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
// Called when the Visualization API is loaded.
function draw() {
// Create a data table with nodes.
nodes = [];
// Create a data table with links.
edges = [];
nodes.push({id: 1, label: 'Get HTML', image: url, shape: 'image'});
nodes.push({id: 2, label: 'Using SVG', image: url, shape: 'image'});
edges.push({from: 1, to: 2, length: 300});
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
stabilize: false,
smoothCurves:false
};
network = new vis.Network(container, data, options);
}
</script>
</head>
<body onload="draw()">
<p>
This example demonstrates showing custom HTML in Nodes, by using an SVG image.
</p>
<p style="color: red;">
WARNING: this is currently not supported by all browsers.
</p>
<div id="mynetwork"></div>
</body>
</html>

www2/examples/network/01_basic_usage.html → examples/network/37_label_alignment.html View File

@ -31,10 +31,10 @@
// create an array with edges // create an array with edges
var edges = [ var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 2, to: 4},
{from: 2, to: 5}
{from: 1, to: 2, label: '1 to 2', labelAlignment:'line-center', fontFill:'orange' },
{from: 1, to: 3, label: '1 to 3', labelAlignment:'line-above', fontFill:'green'},
{from: 2, to: 4, label: '2 to 4', fontFill:'red'},
{from: 2, to: 5, label: '2 to 5', labelAlignment:'line-below', fontFill:'#ccd' }
]; ];
// create a network // create a network

BIN
examples/network/img/indonesia/1.png View File

Before After
Width: 64  |  Height: 64  |  Size: 30 KiB

BIN
examples/network/img/indonesia/10.png View File

Before After
Width: 64  |  Height: 64  |  Size: 28 KiB

BIN
examples/network/img/indonesia/11.png View File

Before After
Width: 64  |  Height: 64  |  Size: 28 KiB

BIN
examples/network/img/indonesia/12.png View File

Before After
Width: 64  |  Height: 64  |  Size: 29 KiB

BIN
examples/network/img/indonesia/13.png View File

Before After
Width: 64  |  Height: 57  |  Size: 30 KiB

BIN
examples/network/img/indonesia/14.png View File

Before After
Width: 64  |  Height: 64  |  Size: 30 KiB

BIN
examples/network/img/indonesia/2.png View File

Before After
Width: 64  |  Height: 64  |  Size: 31 KiB

BIN
examples/network/img/indonesia/3.png View File

Before After
Width: 64  |  Height: 64  |  Size: 31 KiB

BIN
examples/network/img/indonesia/4.png View File

Before After
Width: 64  |  Height: 64  |  Size: 31 KiB

BIN
examples/network/img/indonesia/5.png View File

Before After
Width: 64  |  Height: 64  |  Size: 31 KiB

BIN
examples/network/img/indonesia/6.png View File

Before After
Width: 64  |  Height: 64  |  Size: 30 KiB

BIN
examples/network/img/indonesia/7.png View File

Before After
Width: 64  |  Height: 64  |  Size: 29 KiB

BIN
examples/network/img/indonesia/8.png View File

Before After
Width: 64  |  Height: 64  |  Size: 31 KiB

BIN
examples/network/img/indonesia/9.png View File

Before After
Width: 64  |  Height: 64  |  Size: 30 KiB

+ 4
- 0
examples/network/index.html View File

@ -45,6 +45,10 @@
<p><a href="31_localization.html">31_localization.html</a></p> <p><a href="31_localization.html">31_localization.html</a></p>
<p><a href="32_hierarchicaLayoutMethods.html">32_hierarchicaLayoutMethods.html</a></p> <p><a href="32_hierarchicaLayoutMethods.html">32_hierarchicaLayoutMethods.html</a></p>
<p><a href="33_animation.html">33_animation.html</a></p> <p><a href="33_animation.html">33_animation.html</a></p>
<p><a href="34_circular_images.html">34_circular_images.html</a></p>
<p><a href="35_label_stroke.html">35_label_stroke.html</a></p>
<p><a href="36_HTML_in_Nodes.html">36_HTML_in_Nodes.html</a></p>
<p><a href="37_label_alignment.html">37_label_alignment.html</a></p>
<p><a href="graphviz/graphviz_gallery.html">graphviz_gallery.html</a></p> <p><a href="graphviz/graphviz_gallery.html">graphviz_gallery.html</a></p>
</div> </div>

+ 1
- 1
examples/timeline/03_performance.html View File

@ -11,7 +11,7 @@
</style> </style>
<!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js --> <!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.3.1/moment.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script src="../../dist/vis.js"></script> <script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />

+ 1
- 1
examples/timeline/05_groups.html View File

@ -17,7 +17,7 @@
</style> </style>
<!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js --> <!-- note: moment.js must be loaded before vis.js, else vis.js uses its embedded version of moment.js -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.3.1/moment.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script src="../../dist/vis.js"></script> <script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />

+ 1
- 1
examples/timeline/13_past_and_future.html View File

@ -80,7 +80,7 @@
// set a custom range from -2 minute to +3 minutes current time // set a custom range from -2 minute to +3 minutes current time
var start = new Date((new Date()).getTime() - 2 * 60 * 1000); var start = new Date((new Date()).getTime() - 2 * 60 * 1000);
var end = new Date((new Date()).getTime() + 3 * 60 * 1000); var end = new Date((new Date()).getTime() + 3 * 60 * 1000);
timeline.setWindow(start, end);
timeline.setWindow(start, end, {animate: false});
</script> </script>

+ 53
- 0
examples/timeline/32_grid_styling.html View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Grid styling</title>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body, html {
font-family: sans-serif;
}
/* alternating column backgrounds */
.vis.timeline .timeaxis .grid.odd {
background: #f5f5f5;
}
/* gray background in weekends, white text color */
.vis.timeline .timeaxis .grid.saturday,
.vis.timeline .timeaxis .grid.sunday {
background: gray;
}
.vis.timeline .timeaxis .text.saturday,
.vis.timeline .timeaxis .text.sunday {
color: white;
}
</style>
</head>
<body>
<div id="visualization"></div>
<script type="text/javascript">
// DOM element where the Timeline will be attached
var container = document.getElementById('visualization');
// Create a DataSet (allows two way data-binding)
var items = new vis.DataSet([
{id: 1, content: 'custom', start: '2015-01-01'},
{id: 2, content: 'styling', start: '2016-01-01'},
{id: 3, content: 'of', start: '2017-01-01'},
{id: 4, content: 'background', start: '2018-01-01'},
{id: 5, content: 'grid', start: '2019-01-01'}
]);
// Configuration for the Timeline
var options = {};
// Create a Timeline
var timeline = new vis.Timeline(container, items, options);
</script>
</body>
</html>

+ 1
- 0
examples/timeline/index.html View File

@ -42,6 +42,7 @@
<p><a href="29_hiding_times.html">29_hiding_times.html</a></p> <p><a href="29_hiding_times.html">29_hiding_times.html</a></p>
<p><a href="30_subgroups.html">30_subgroups.html</a></p> <p><a href="30_subgroups.html">30_subgroups.html</a></p>
<p><a href="31_background_areas_with_groups.html">31_background_areas_with_groups.html</a></p> <p><a href="31_background_areas_with_groups.html">31_background_areas_with_groups.html</a></p>
<p><a href="32_grid_styling.html">32_grid_styling.html</a></p>
<p><a href="requirejs/requirejs_example.html">requirejs_example.html</a></p> <p><a href="requirejs/requirejs_example.html">requirejs_example.html</a></p>

+ 3
- 2
lib/graph3d/Graph3d.js View File

@ -1941,8 +1941,9 @@ Graph3d.prototype._onMouseUp = function (event) {
*/ */
Graph3d.prototype._onTooltip = function (event) { Graph3d.prototype._onTooltip = function (event) {
var delay = 300; // ms var delay = 300; // ms
var mouseX = getMouseX(event) - util.getAbsoluteLeft(this.frame);
var mouseY = getMouseY(event) - util.getAbsoluteTop(this.frame);
var boundingRect = this.frame.getBoundingClientRect();
var mouseX = getMouseX(event) - boundingRect.left;
var mouseY = getMouseY(event) - boundingRect.top;
if (!this.showTooltip) { if (!this.showTooltip) {
return; return;

+ 383
- 242
lib/network/Edge.js View File

@ -75,8 +75,8 @@ Edge.prototype.setProperties = function(properties) {
return; return;
} }
var fields = ['style','fontSize','fontFace','fontColor','fontFill','width',
'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor'
var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width',
'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment'
]; ];
util.selectiveDeepExtend(fields, this.options, properties); util.selectiveDeepExtend(fields, this.options, properties);
@ -119,6 +119,7 @@ Edge.prototype.setProperties = function(properties) {
case 'dash-line': this.draw = this._drawDashLine; break; case 'dash-line': this.draw = this._drawDashLine; break;
default: this.draw = this._drawLine; break; default: this.draw = this._drawLine; break;
} }
}; };
/** /**
@ -322,168 +323,176 @@ Edge.prototype._getLineWidth = function() {
}; };
Edge.prototype._getViaCoordinates = function () { Edge.prototype._getViaCoordinates = function () {
var xVia = null;
var yVia = null;
var factor = this.options.smoothCurves.roundness;
var type = this.options.smoothCurves.type;
var dx = Math.abs(this.from.x - this.to.x);
var dy = Math.abs(this.from.y - this.to.y);
if (type == 'discrete' || type == 'diagonalCross') {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) {
return this.via;
}
else if (this.options.smoothCurves.enabled == false) {
return {x:0,y:0};
}
else {
var xVia = null;
var yVia = null;
var factor = this.options.smoothCurves.roundness;
var type = this.options.smoothCurves.type;
var dx = Math.abs(this.from.x - this.to.x);
var dy = Math.abs(this.from.y - this.to.y);
if (type == 'discrete' || type == 'diagonalCross') {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
}
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
}
} }
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
if (type == "discrete") {
xVia = dx < factor * dy ? this.from.x : xVia;
} }
} }
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
}
} }
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
}
}
if (type == "discrete") {
yVia = dy < factor * dx ? this.from.y : yVia;
} }
}
if (type == "discrete") {
xVia = dx < factor * dy ? this.from.x : xVia;
} }
} }
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
else if (type == "straightCross") {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down
xVia = this.from.x;
if (this.from.y < this.to.y) {
yVia = this.to.y - (1 - factor) * dy;
} }
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
else {
yVia = this.to.y + (1 - factor) * dy;
} }
} }
else if (this.from.y < this.to.y) {
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right
if (this.from.x < this.to.x) { if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
xVia = this.to.x - (1 - factor) * dx;
} }
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
else {
xVia = this.to.x + (1 - factor) * dx;
} }
}
if (type == "discrete") {
yVia = dy < factor * dx ? this.from.y : yVia;
yVia = this.from.y;
} }
} }
}
else if (type == "straightCross") {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down
xVia = this.from.x;
if (this.from.y < this.to.y) {
yVia = this.to.y - (1-factor) * dy;
else if (type == 'horizontal') {
if (this.from.x < this.to.x) {
xVia = this.to.x - (1 - factor) * dx;
} }
else { else {
yVia = this.to.y + (1-factor) * dy;
xVia = this.to.x + (1 - factor) * dx;
} }
yVia = this.from.y;
} }
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right
if (this.from.x < this.to.x) {
xVia = this.to.x - (1-factor) * dx;
else if (type == 'vertical') {
xVia = this.from.x;
if (this.from.y < this.to.y) {
yVia = this.to.y - (1 - factor) * dy;
} }
else { else {
xVia = this.to.x + (1-factor) * dx;
yVia = this.to.y + (1 - factor) * dy;
} }
yVia = this.from.y;
} }
}
else if (type == 'horizontal') {
if (this.from.x < this.to.x) {
xVia = this.to.x - (1-factor) * dx;
}
else {
xVia = this.to.x + (1-factor) * dx;
}
yVia = this.from.y;
}
else if (type == 'vertical') {
xVia = this.from.x;
if (this.from.y < this.to.y) {
yVia = this.to.y - (1-factor) * dy;
}
else {
yVia = this.to.y + (1-factor) * dy;
}
}
else { // continuous
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
else { // continuous
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(1) // console.log(1)
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(2) // console.log(2)
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x > xVia ? this.to.x :xVia;
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
}
} }
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(3) // console.log(3)
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(4, this.from.x, this.to.x) // console.log(4, this.from.x, this.to.x)
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
}
} }
} }
}
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(5) // console.log(5)
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(6) // console.log(6)
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
} }
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(7) // console.log(7)
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(8) // console.log(8)
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
} }
} }
} }
}
return {x:xVia, y:yVia};
return {x: xVia, y: yVia};
}
}; };
/** /**
@ -556,7 +565,7 @@ Edge.prototype._label = function (ctx, text, x, y) {
if (this.dirtyLabel == true) { if (this.dirtyLabel == true) {
var lines = String(text).split('\n'); var lines = String(text).split('\n');
var lineCount = lines.length; var lineCount = lines.length;
var fontSize = (Number(this.options.fontSize) + 4);
var fontSize = Number(this.options.fontSize);
yLine = y + (1 - lineCount) / 2 * fontSize; yLine = y + (1 - lineCount) / 2 * fontSize;
var width = ctx.measureText(lines[0]).width; var width = ctx.measureText(lines[0]).width;
@ -572,25 +581,117 @@ Edge.prototype._label = function (ctx, text, x, y) {
this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine};
} }
var yLine = this.labelDimensions.yLine;
ctx.save();
if (this.options.labelAlignment != "horizontal"){
ctx.translate(x, yLine);
this._rotateForLabelAlignment(ctx);
x = 0;
yLine = 0;
}
this._drawLabelRect(ctx);
this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize);
ctx.restore();
}
};
/**
* Rotates the canvas so the text is most readable
* @param {CanvasRenderingContext2D} ctx
* @private
*/
Edge.prototype._rotateForLabelAlignment = function(ctx) {
var dy = this.from.y - this.to.y;
var dx = this.from.x - this.to.x;
var angleInDegrees = Math.atan2(dy, dx);
// rotate so label it is readable
if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){
angleInDegrees = angleInDegrees + Math.PI;
}
ctx.rotate(angleInDegrees);
};
if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") {
ctx.fillStyle = this.options.fontFill;
ctx.fillRect(this.labelDimensions.left,
this.labelDimensions.top,
this.labelDimensions.width,
this.labelDimensions.height);
/**
* Draws the label rectangle
* @param {CanvasRenderingContext2D} ctx
* @param {String} labelAlignment
* @private
*/
Edge.prototype._drawLabelRect = function(ctx) {
if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") {
ctx.fillStyle = this.options.fontFill;
var lineMargin = 2;
if (this.options.labelAlignment == 'line-center') {
ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height);
}
else if (this.options.labelAlignment == 'line-above') {
ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height);
} }
else if (this.options.labelAlignment == 'line-below') {
ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height);
}
else {
ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height);
}
}
};
// draw text
ctx.fillStyle = this.options.fontColor || "black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
yLine = this.labelDimensions.yLine;
for (var i = 0; i < lineCount; i++) {
ctx.fillText(lines[i], x, yLine);
yLine += fontSize;
/**
* Draws the label text
* @param {CanvasRenderingContext2D} ctx
* @param {Number} x
* @param {Number} yLine
* @param {Array} lines
* @param {Number} lineCount
* @param {Number} fontSize
* @private
*/
Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) {
// draw text
ctx.fillStyle = this.options.fontColor || "black";
ctx.textAlign = "center";
// check for label alignment
if (this.options.labelAlignment != 'horizontal') {
var lineMargin = 2;
if (this.options.labelAlignment == 'line-above') {
ctx.textBaseline = "alphabetic";
yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
} }
else if (this.options.labelAlignment == 'line-below') {
ctx.textBaseline = "hanging";
yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers
}
else {
ctx.textBaseline = "middle";
}
}
else {
ctx.textBaseline = "middle";
} }
// check for strokeWidth
if (this.options.fontStrokeWidth > 0){
ctx.lineWidth = this.options.fontStrokeWidth;
ctx.strokeStyle = this.options.fontStrokeColor;
ctx.lineJoin = 'round';
}
for (var i = 0; i < lineCount; i++) {
if(this.options.fontStrokeWidth > 0){
ctx.strokeText(lines[i], x, yLine);
}
ctx.fillText(lines[i], x, yLine);
yLine += fontSize;
}
}; };
/** /**
@ -609,7 +710,8 @@ Edge.prototype._drawDashLine = function(ctx) {
var via = null; var via = null;
// only firefox and chrome support this method, else we use the legacy one. // only firefox and chrome support this method, else we use the legacy one.
if (ctx.mozDash !== undefined || ctx.setLineDash !== undefined) {
if (ctx.setLineDash !== undefined) {
ctx.save();
// configure the dash pattern // configure the dash pattern
var pattern = [0]; var pattern = [0];
if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) {
@ -620,27 +722,16 @@ Edge.prototype._drawDashLine = function(ctx) {
} }
// set dash settings for chrome or firefox // set dash settings for chrome or firefox
if (typeof ctx.setLineDash !== 'undefined') { //Chrome
ctx.setLineDash(pattern);
ctx.lineDashOffset = 0;
} else { //Firefox
ctx.mozDash = pattern;
ctx.mozDashOffset = 0;
}
ctx.setLineDash(pattern);
ctx.lineDashOffset = 0;
// draw the line // draw the line
via = this._line(ctx); via = this._line(ctx);
// restore the dash settings. // restore the dash settings.
if (typeof ctx.setLineDash !== 'undefined') { //Chrome
ctx.setLineDash([0]);
ctx.lineDashOffset = 0;
} else { //Firefox
ctx.mozDash = [0];
ctx.mozDashOffset = 0;
}
ctx.setLineDash([0]);
ctx.lineDashOffset = 0;
ctx.restore();
} }
else { // unsupporting smooth lines else { // unsupporting smooth lines
// draw dashed line // draw dashed line
@ -782,7 +873,69 @@ Edge.prototype._drawArrowCenter = function(ctx) {
} }
}; };
Edge.prototype._pointOnBezier = function(t) {
var via = this._getViaCoordinates();
var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x;
var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y;
return {x:x,y:y};
}
/**
* This function uses binary search to look for the point where the bezier curve crosses the border of the node.
*
* @param from
* @param ctx
* @returns {*}
* @private
*/
Edge.prototype._findBorderPosition = function(from,ctx) {
var maxIterations = 10;
var iteration = 0;
var low = 0;
var high = 1;
var pos,angle,distanceToBorder, distanceToNodes, difference;
var threshold = 0.2;
var node = this.to;
if (from == true) {
node = this.from;
}
while (low <= high && iteration < maxIterations) {
var middle = (low + high) * 0.5;
pos = this._pointOnBezier(middle);
angle = Math.atan2((node.y - pos.y), (node.x - pos.x));
distanceToBorder = node.distanceToBorder(ctx,angle);
distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2));
difference = distanceToBorder - distanceToNodes;
if (Math.abs(difference) < threshold) {
break; // found
}
else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
if (from == false) {
low = middle;
}
else {
high = middle;
}
}
else {
if (from == false) {
high = middle;
}
else {
low = middle;
}
}
iteration++;
}
pos.t = middle;
return pos;
};
/** /**
* Redraw a edge as a line with an arrow * Redraw a edge as a line with an arrow
@ -797,59 +950,37 @@ Edge.prototype._drawArrow = function(ctx) {
ctx.fillStyle = ctx.strokeStyle; ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this._getLineWidth(); ctx.lineWidth = this._getLineWidth();
var angle, length;
//draw a line
if (this.from != this.to) {
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var dx = (this.to.x - this.from.x);
var dy = (this.to.y - this.from.y);
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI);
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength;
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
// set vars
var angle, length, arrowPos;
var via;
if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) {
via = this.via;
}
else if (this.options.smoothCurves.enabled == true) {
via = this._getViaCoordinates();
}
if (this.options.smoothCurves.enabled == true && via.x != null) {
angle = Math.atan2((this.to.y - via.y), (this.to.x - via.x));
dx = (this.to.x - via.x);
dy = (this.to.y - via.y);
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
}
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
var xTo,yTo;
if (this.options.smoothCurves.enabled == true && via.x != null) {
xTo = (1 - toBorderPoint) * via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * via.y + toBorderPoint * this.to.y;
}
else {
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
}
// if not connected to itself
if (this.from != this.to) {
// draw line
this._line(ctx);
ctx.beginPath();
ctx.moveTo(xFrom,yFrom);
if (this.options.smoothCurves.enabled == true && via.x != null) {
ctx.quadraticCurveTo(via.x,via.y,xTo, yTo);
// draw arrow head
if (this.options.smoothCurves.enabled == true) {
var via = this._getViaCoordinates();
arrowPos = this._findBorderPosition(false, ctx);
var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1))
angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x));
} }
else { else {
ctx.lineTo(xTo, yTo);
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var dx = (this.to.x - this.from.x);
var dy = (this.to.y - this.from.y);
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
arrowPos = {};
arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
} }
ctx.stroke();
// draw arrow at the end of the line // draw arrow at the end of the line
length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor;
ctx.arrow(xTo, yTo, angle, length);
ctx.arrow(arrowPos.x,arrowPos.y, angle, length);
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
@ -857,9 +988,7 @@ Edge.prototype._drawArrow = function(ctx) {
if (this.label) { if (this.label) {
var point; var point;
if (this.options.smoothCurves.enabled == true && via != null) { if (this.options.smoothCurves.enabled == true && via != null) {
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x));
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y));
point = {x:midpointX, y:midpointY};
point = this._pointOnBezier(0.5);
} }
else { else {
point = this._pointOnLine(0.5); point = this._pointOnLine(0.5);
@ -912,8 +1041,6 @@ Edge.prototype._drawArrow = function(ctx) {
} }
}; };
/** /**
* Calculate the distance between a point (x3,y3) and a line segment from * Calculate the distance between a point (x3,y3) and a line segment from
* (x1,y1) to (x2,y2). * (x1,y1) to (x2,y2).
@ -1054,26 +1181,30 @@ Edge.prototype._drawControlNodes = function(ctx) {
var nodeIdFrom = "edgeIdFrom:".concat(this.id); var nodeIdFrom = "edgeIdFrom:".concat(this.id);
var nodeIdTo = "edgeIdTo:".concat(this.id); var nodeIdTo = "edgeIdTo:".concat(this.id);
var constants = { var constants = {
nodes:{group:'', radius:8},
nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2},
physics:{damping:0}, physics:{damping:0},
clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}}
}; };
this.controlNodes.from = new Node( this.controlNodes.from = new Node(
{id:nodeIdFrom, {id:nodeIdFrom,
shape:'dot', shape:'dot',
color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}}
},{},{},constants); },{},{},constants);
this.controlNodes.to = new Node( this.controlNodes.to = new Node(
{id:nodeIdTo, {id:nodeIdTo,
shape:'dot', shape:'dot',
color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}}
},{},{},constants); },{},{},constants);
} }
if (this.controlNodes.from.selected == false && this.controlNodes.to.selected == false) {
this.controlNodes.positions = this.getControlNodePositions(ctx);
this.controlNodes.positions = {};
if (this.controlNodes.from.selected == false) {
this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx);
this.controlNodes.from.x = this.controlNodes.positions.from.x; this.controlNodes.from.x = this.controlNodes.positions.from.x;
this.controlNodes.from.y = this.controlNodes.positions.from.y; this.controlNodes.from.y = this.controlNodes.positions.from.y;
}
if (this.controlNodes.to.selected == false) {
this.controlNodes.positions.to = this.getControlNodeToPosition(ctx);
this.controlNodes.to.x = this.controlNodes.positions.to.x; this.controlNodes.to.x = this.controlNodes.positions.to.x;
this.controlNodes.to.y = this.controlNodes.positions.to.y; this.controlNodes.to.y = this.controlNodes.positions.to.y;
} }
@ -1165,46 +1296,56 @@ Edge.prototype._restoreControlNodes = function() {
* this calculates the position of the control nodes on the edges of the parent nodes. * this calculates the position of the control nodes on the edges of the parent nodes.
* *
* @param ctx * @param ctx
* @returns {{from: {x: number, y: number}, to: {x: *, y: *}}}
* @returns {x: *, y: *}
*/ */
Edge.prototype.getControlNodePositions = function(ctx) {
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var dx = (this.to.x - this.from.x);
var dy = (this.to.y - this.from.y);
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI);
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength;
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
var via;
if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true) {
via = this.via;
}
else if (this.options.smoothCurves.enabled == true) {
via = this._getViaCoordinates();
Edge.prototype.getControlNodeFromPosition = function(ctx) {
// draw arrow head
var controlnodeFromPos;
if (this.options.smoothCurves.enabled == true) {
controlnodeFromPos = this._findBorderPosition(true, ctx);
} }
else {
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var dx = (this.to.x - this.from.x);
var dy = (this.to.y - this.from.y);
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
if (this.options.smoothCurves.enabled == true && via.x != null) {
angle = Math.atan2((this.to.y - via.y), (this.to.x - via.x));
dx = (this.to.x - via.x);
dy = (this.to.y - via.y);
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI);
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength;
controlnodeFromPos = {};
controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
} }
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
var xTo,yTo;
if (this.options.smoothCurves.enabled == true && via.x != null) {
xTo = (1 - toBorderPoint) * via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * via.y + toBorderPoint * this.to.y;
return controlnodeFromPos;
};
/**
* this calculates the position of the control nodes on the edges of the parent nodes.
*
* @param ctx
* @returns {{from: {x: number, y: number}, to: {x: *, y: *}}}
*/
Edge.prototype.getControlNodeToPosition = function(ctx) {
// draw arrow head
var controlnodeFromPos,controlnodeToPos;
if (this.options.smoothCurves.enabled == true) {
controlnodeToPos = this._findBorderPosition(false, ctx);
} }
else { else {
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var dx = (this.to.x - this.from.x);
var dy = (this.to.y - this.from.y);
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
controlnodeToPos = {};
controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
} }
return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}};
return controlnodeToPos;
}; };
module.exports = Edge; module.exports = Edge;

+ 0
- 3
lib/network/Groups.js View File

@ -74,9 +74,6 @@ Groups.prototype.get = function (groupname) {
*/ */
Groups.prototype.add = function (groupname, style) { Groups.prototype.add = function (groupname, style) {
this.groups[groupname] = style; this.groups[groupname] = style;
if (style.color) {
style.color = util.parseColor(style.color);
}
return style; return style;
}; };

+ 37
- 15
lib/network/Images.js View File

@ -4,7 +4,7 @@
*/ */
function Images() { function Images() {
this.images = {}; this.images = {};
this.imageBroken = {};
this.callback = undefined; this.callback = undefined;
} }
@ -24,25 +24,47 @@ Images.prototype.setOnloadCallback = function(callback) {
* @return {Image} img The image object * @return {Image} img The image object
*/ */
Images.prototype.load = function(url, brokenUrl) { Images.prototype.load = function(url, brokenUrl) {
var img = this.images[url];
if (img == undefined) {
var img = this.images[url]; // make a pointer
if (img === undefined) {
// create the image // create the image
var images = this;
var me = this;
img = new Image(); img = new Image();
this.images[url] = img;
img.onload = function() {
if (images.callback) {
images.callback(this);
img.onload = function () {
// IE11 fix -- thanks dponch!
if (this.width == 0) {
document.body.appendChild(this);
this.width = this.offsetWidth;
this.height = this.offsetHeight;
document.body.removeChild(this);
}
if (me.callback) {
me.images[url] = img;
me.callback(this);
} }
}; };
img.onerror = function () { img.onerror = function () {
this.src = brokenUrl;
if (images.callback) {
images.callback(this);
}
};
if (brokenUrl === undefined) {
console.error("Could not load image:", url);
delete this.src;
if (me.callback) {
me.callback(this);
}
}
else if (me.imageBroken[url] === true) {
console.error("Could not load brokenImage:", brokenUrl);
delete this.src;
if (me.callback) {
me.callback(this);
}
}
else {
this.src = brokenUrl;
me.imageBroken[url] = true;
}
};
img.src = url; img.src = url;
} }

+ 206
- 98
lib/network/Network.js View File

@ -35,6 +35,7 @@ function Network (container, data, options) {
throw new SyntaxError('Constructor must be called with the new operator'); throw new SyntaxError('Constructor must be called with the new operator');
} }
this._determineBrowserMethod();
this._initializeMixinLoaders(); this._initializeMixinLoaders();
// create variables and set default values // create variables and set default values
@ -43,8 +44,9 @@ function Network (container, data, options) {
// render and calculation settings // render and calculation settings
this.renderRefreshRate = 60; // hz (fps) this.renderRefreshRate = 60; // hz (fps)
this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
this.renderTime = 0.5 * this.renderTimestep; // measured time it takes to render a frame
this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
this.renderTime = 0; // measured time it takes to render a frame
this.physicsTime = 0; // measured time it takes to render a frame
this.runDoubleSpeed = false;
this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
this.initializing = true; this.initializing = true;
@ -66,6 +68,8 @@ function Network (container, data, options) {
fontSize: 14, // px fontSize: 14, // px
fontFace: 'verdana', fontFace: 'verdana',
fontFill: undefined, fontFill: undefined,
fontStrokeWidth: 0, // px
fontStrokeColor: 'white',
level: -1, level: -1,
color: { color: {
border: '#2B7CE9', border: '#2B7CE9',
@ -79,9 +83,6 @@ function Network (container, data, options) {
background: '#D2E5FF' background: '#D2E5FF'
} }
}, },
borderColor: '#2B7CE9',
backgroundColor: '#97C2FC',
highlightColor: '#D2E5FF',
group: undefined, group: undefined,
borderWidth: 1, borderWidth: 1,
borderWidthSelected: undefined borderWidthSelected: undefined
@ -102,6 +103,9 @@ function Network (container, data, options) {
fontSize: 14, // px fontSize: 14, // px
fontFace: 'arial', fontFace: 'arial',
fontFill: 'white', fontFill: 'white',
fontStrokeWidth: 0, // px
fontStrokeColor: 'white',
labelAlignment:'horizontal',
arrowScaleFactor: 1, arrowScaleFactor: 1,
dash: { dash: {
length: 10, length: 10,
@ -114,7 +118,7 @@ function Network (container, data, options) {
physics: { physics: {
barnesHut: { barnesHut: {
enabled: true, enabled: true,
theta: 1 / 0.6, // inverted to save time during calculation
thetaInverted: 1 / 0.5, // inverted to save time during calculation
gravitationalConstant: -2000, gravitationalConstant: -2000,
centralGravity: 0.3, centralGravity: 0.3,
springLength: 95, springLength: 95,
@ -238,7 +242,7 @@ function Network (container, data, options) {
var network = this; var network = this;
this.groups = new Groups(); // object with groups this.groups = new Groups(); // object with groups
this.images = new Images(); // object with images this.images = new Images(); // object with images
this.images.setOnloadCallback(function () {
this.images.setOnloadCallback(function (status) {
network._redraw(); network._redraw();
}); });
@ -352,6 +356,25 @@ function Network (container, data, options) {
// Extend Network with an Emitter mixin // Extend Network with an Emitter mixin
Emitter(Network.prototype); Emitter(Network.prototype);
/**
* Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because
* some implementations (safari and IE9) did not support requestAnimationFrame
* @private
*/
Network.prototype._determineBrowserMethod = function() {
var browserType = navigator.userAgent.toLowerCase();
this.requiresTimeout = false;
if (browserType.indexOf('msie 9.0') != -1) { // IE 9
this.requiresTimeout = true;
}
else if (browserType.indexOf('safari') != -1) { // safari
if (browserType.indexOf('chrome') <= -1) {
this.requiresTimeout = true;
}
}
}
/** /**
* Get the script path where the vis.js library is located * Get the script path where the vis.js library is located
* *
@ -385,10 +408,10 @@ Network.prototype._getRange = function() {
for (var nodeId in this.nodes) { for (var nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) { if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId]; node = this.nodes[nodeId];
if (minX > (node.x)) {minX = node.x;}
if (maxX < (node.x)) {maxX = node.x;}
if (minY > (node.y)) {minY = node.y;}
if (maxY < (node.y)) {maxY = node.y;}
if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;}
if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;}
if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.bottom;}
if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.top;}
} }
} }
if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
@ -416,6 +439,8 @@ Network.prototype._findCenter = function(range) {
* @param {Boolean} [disableStart] | If true, start is not called. * @param {Boolean} [disableStart] | If true, start is not called.
*/ */
Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) {
this._redraw(true);
if (initialZoom === undefined) { if (initialZoom === undefined) {
initialZoom = false; initialZoom = false;
} }
@ -524,6 +549,11 @@ Network.prototype.setData = function(data, disableStart) {
' parameter pair "nodes" and "edges", but not both.'); ' parameter pair "nodes" and "edges", but not both.');
} }
// clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button.
if (this.constants.dataManipulation.enabled == true) {
this._createManipulatorBar();
}
// set options // set options
this.setOptions(data && data.options); this.setOptions(data && data.options);
// set all data // set all data
@ -571,7 +601,6 @@ Network.prototype.setData = function(data, disableStart) {
Network.prototype.setOptions = function (options) { Network.prototype.setOptions = function (options) {
if (options) { if (options) {
var prop; var prop;
var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation',
'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse'
]; ];
@ -629,6 +658,7 @@ Network.prototype.setOptions = function (options) {
if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
} }
this.constants.edges.inheritColor = false;
} }
if (!options.edges.fontColor) { if (!options.edges.fontColor) {
@ -672,8 +702,10 @@ Network.prototype.setOptions = function (options) {
if ('clickToUse' in options) { if ('clickToUse' in options) {
if (options.clickToUse) { if (options.clickToUse) {
this.activator = new Activator(this.frame);
this.activator.on('change', this._createKeyBinds.bind(this));
if (!this.activator) {
this.activator = new Activator(this.frame);
this.activator.on('change', this._createKeyBinds.bind(this));
}
} }
else { else {
if (this.activator) { if (this.activator) {
@ -686,24 +718,26 @@ Network.prototype.setOptions = function (options) {
if (options.labels) { if (options.labels) {
throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');
} }
}
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
this._loadPhysicsSystem();
// load the navigation system.
this._loadNavigationControls();
// load the data manipulation system
this._loadManipulationSystem();
// configure the smooth curves
this._configureSmoothCurves();
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
this._loadPhysicsSystem();
// load the navigation system.
this._loadNavigationControls();
// load the data manipulation system
this._loadManipulationSystem();
// configure the smooth curves
this._configureSmoothCurves();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
this.setSize(this.constants.width, this.constants.height);
this.moving = true;
this.start();
// bind keys. If disabled, this will not do anything;
this._createKeyBinds();
this.setSize(this.constants.width, this.constants.height);
this.moving = true;
this.start();
}
}; };
@ -730,11 +764,9 @@ Network.prototype._create = function () {
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
this.frame.canvas = document.createElement("canvas"); this.frame.canvas = document.createElement("canvas");
this.frame.canvas.style.position = 'relative'; this.frame.canvas.style.position = 'relative';
this.frame.appendChild(this.frame.canvas); this.frame.appendChild(this.frame.canvas);
if (!this.frame.canvas.getContext) { if (!this.frame.canvas.getContext) {
var noCanvas = document.createElement( 'DIV' ); var noCanvas = document.createElement( 'DIV' );
noCanvas.style.color = 'red'; noCanvas.style.color = 'red';
@ -744,17 +776,13 @@ Network.prototype._create = function () {
this.frame.canvas.appendChild(noCanvas); this.frame.canvas.appendChild(noCanvas);
} }
else { else {
var ctx = this.frame.canvas.getContext("2d"); var ctx = this.frame.canvas.getContext("2d");
this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio || ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio || ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio || ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1); ctx.backingStorePixelRatio || 1);
this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
} }
@ -831,8 +859,20 @@ Network.prototype._createKeyBinds = function() {
} }
}; };
/**
* Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function.
* var network = new vis.Network(..);
* network.destroy();
* network = null;
*/
Network.prototype.destroy = function() { Network.prototype.destroy = function() {
this.start = function () {};
this.redraw = function () {};
this.timer = false;
// cleanup physicsConfiguration if it exists
this._cleanupPhysicsConfiguration();
// remove keybindings // remove keybindings
this.keycharm.reset(); this.keycharm.reset();
@ -841,8 +881,16 @@ Network.prototype.destroy = function() {
// clear events // clear events
this.off(); this.off();
};
this._recursiveDOMDelete(this.containerElement);
}
Network.prototype._recursiveDOMDelete = function(DOMobject) {
while (DOMobject.hasChildNodes() == true) {
this._recursiveDOMDelete(DOMobject.firstChild);
DOMobject.removeChild(DOMobject.firstChild);
}
}
/** /**
* Get the pointer location from a touch location * Get the pointer location from a touch location
@ -891,19 +939,23 @@ Network.prototype._onDragStart = function (event) {
* @private * @private
*/ */
Network.prototype._handleDragStart = function(event) { Network.prototype._handleDragStart = function(event) {
var drag = this.drag;
var node = this._getNodeAt(drag.pointer);
// in case the touch event was triggered on an external div, do the initial touch now.
if (this.drag.pointer === undefined) {
this._onTouch(event);
}
var node = this._getNodeAt(this.drag.pointer);
// note: drag.pointer is set in _onTouch to get the initial touch location // note: drag.pointer is set in _onTouch to get the initial touch location
drag.dragging = true;
drag.selection = [];
drag.translation = this._getTranslation();
drag.nodeId = null;
this.drag.dragging = true;
this.drag.selection = [];
this.drag.translation = this._getTranslation();
this.drag.nodeId = null;
this.draggingNodes = false; this.draggingNodes = false;
if (node != null && this.constants.dragNodes == true) { if (node != null && this.constants.dragNodes == true) {
this.draggingNodes = true; this.draggingNodes = true;
drag.nodeId = node.id;
this.drag.nodeId = node.id;
// select the clicked node if not yet selected // select the clicked node if not yet selected
if (!node.isSelected()) { if (!node.isSelected()) {
this._selectObject(node,false); this._selectObject(node,false);
@ -929,7 +981,7 @@ Network.prototype._handleDragStart = function(event) {
object.xFixed = true; object.xFixed = true;
object.yFixed = true; object.yFixed = true;
drag.selection.push(s);
this.drag.selection.push(s);
} }
} }
} }
@ -991,8 +1043,13 @@ Network.prototype._handleOnDrag = function(event) {
} }
} }
else { else {
// move the network
if (this.constants.dragNetwork == true) { if (this.constants.dragNetwork == true) {
// move the network
// if the drag was not started properly because the click started outside the network div, start it now.
if (this.drag.pointer === undefined) {
this._handleDragStart(event);
return;
}
var diffX = pointer.x - this.drag.pointer.x; var diffX = pointer.x - this.drag.pointer.x;
var diffY = pointer.y - this.drag.pointer.y; var diffY = pointer.y - this.drag.pointer.y;
@ -1001,8 +1058,6 @@ Network.prototype._handleOnDrag = function(event) {
this.drag.translation.y + diffY this.drag.translation.y + diffY
); );
this._redraw(); this._redraw();
// this.moving = true;
// this.start();
} }
} }
@ -1281,34 +1336,49 @@ Network.prototype._checkShowPopup = function (pointer) {
var id; var id;
var lastPopupNode = this.popupObj; var lastPopupNode = this.popupObj;
var nodeUnderCursor = false;
if (this.popupObj == undefined) { if (this.popupObj == undefined) {
// search the nodes for overlap, select the top one in case of multiple nodes // search the nodes for overlap, select the top one in case of multiple nodes
var nodes = this.nodes; var nodes = this.nodes;
var overlappingNodes = [];
for (id in nodes) { for (id in nodes) {
if (nodes.hasOwnProperty(id)) { if (nodes.hasOwnProperty(id)) {
var node = nodes[id]; var node = nodes[id];
if (node.getTitle() !== undefined && node.isOverlappingWith(obj)) {
this.popupObj = node;
break;
if (node.isOverlappingWith(obj)) {
if (node.getTitle() !== undefined) {
overlappingNodes.push(id);
}
} }
} }
} }
if (overlappingNodes.length > 0) {
// if there are overlapping nodes, select the last one, this is the
// one which is drawn on top of the others
this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]];
// if you hover over a node, the title of the edge is not supposed to be shown.
nodeUnderCursor = true;
}
} }
if (this.popupObj === undefined) {
if (this.popupObj === undefined && nodeUnderCursor == false) {
// search the edges for overlap // search the edges for overlap
var edges = this.edges; var edges = this.edges;
var overlappingEdges = [];
for (id in edges) { for (id in edges) {
if (edges.hasOwnProperty(id)) { if (edges.hasOwnProperty(id)) {
var edge = edges[id]; var edge = edges[id];
if (edge.connected && (edge.getTitle() !== undefined) && if (edge.connected && (edge.getTitle() !== undefined) &&
edge.isOverlappingWith(obj)) { edge.isOverlappingWith(obj)) {
this.popupObj = edge;
break;
overlappingEdges.push(id);
} }
} }
} }
if (overlappingEdges.length > 0) {
this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]];
}
} }
if (this.popupObj) { if (this.popupObj) {
@ -1737,9 +1807,10 @@ Network.prototype.redraw = function() {
/** /**
* Redraw the network with the current data * Redraw the network with the current data
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private * @private
*/ */
Network.prototype._redraw = function() {
Network.prototype._redraw = function(hidden) {
var ctx = this.frame.canvas.getContext('2d'); var ctx = this.frame.canvas.getContext('2d');
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
@ -1763,18 +1834,21 @@ Network.prototype._redraw = function() {
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio)
}; };
this._doInAllSectors("_drawAllSectorNodes",ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges",ctx);
if (!(hidden == true)) {
this._doInAllSectors("_drawAllSectorNodes", ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges", ctx);
}
} }
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) {
this._doInAllSectors("_drawNodes",ctx,false); this._doInAllSectors("_drawNodes",ctx,false);
} }
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes",ctx);
if (!(hidden == true)) {
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes", ctx);
}
} }
// this._doInSupportSector("_drawNodes",ctx,true); // this._doInSupportSector("_drawNodes",ctx,true);
@ -1782,6 +1856,10 @@ Network.prototype._redraw = function() {
// restore original scaling and translation // restore original scaling and translation
ctx.restore(); ctx.restore();
if (hidden == true) {
ctx.clearRect(0, 0, w, h);
}
}; };
/** /**
@ -2096,6 +2174,23 @@ Network.prototype._discreteStepNodes = function() {
return false; return false;
}; };
Network.prototype._revertPhysicsState = function() {
var nodes = this.nodes;
for (var nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
nodes[nodeId].revertPosition();
}
}
}
Network.prototype._revertPhysicsTick = function() {
this._doInAllActiveSectors("_revertPhysicsState");
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
this._doInSupportSector("_revertPhysicsState");
}
}
/** /**
* A single simulation step (or "tick") in the physics simulation * A single simulation step (or "tick") in the physics simulation
* *
@ -2112,12 +2207,24 @@ Network.prototype._physicsTick = function() {
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); supportMovingStatus = this._doInSupportSector("_discreteStepNodes");
} }
// gather movement data from all sectors, if one moves, we are NOT stabilzied // gather movement data from all sectors, if one moves, we are NOT stabilzied
for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;}
// determine if the network has stabilzied // determine if the network has stabilzied
this.moving = mainMovingStatus || supportMovingStatus; this.moving = mainMovingStatus || supportMovingStatus;
if (this.moving == false) {
this._revertPhysicsTick();
}
else {
// this is here to ensure that there is no start event when the network is already stable.
if (this.startedStabilization == false) {
this.emit("startStabilization");
this.startedStabilization = true;
}
}
this.stabilizationIterations++; this.stabilizationIterations++;
} }
} }
@ -2133,26 +2240,30 @@ Network.prototype._physicsTick = function() {
Network.prototype._animationStep = function() { Network.prototype._animationStep = function() {
// reset the timer so a new scheduled animation step can be set // reset the timer so a new scheduled animation step can be set
this.timer = undefined; this.timer = undefined;
// handle the keyboad movement // handle the keyboad movement
this._handleNavigation(); this._handleNavigation();
// this schedules a new animation step
this.start();
// start the physics simulation
var calculationTime = Date.now();
var maxSteps = 1;
var startTime = Date.now();
this._physicsTick(); this._physicsTick();
var timeRequired = Date.now() - calculationTime;
while (timeRequired < 0.9*(this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) {
var physicsTime = Date.now() - startTime;
// run double speed if it is a little graph
if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) {
this._physicsTick(); this._physicsTick();
timeRequired = Date.now() - calculationTime;
maxSteps++;
// this makes sure there is no jitter. The decision is taken once to run it at double speed.
if (this.renderTime != 0) {
this.runDoubleSpeed = true
}
} }
// start the rendering process
var renderTime = Date.now();
var renderStartTime = Date.now();
this._redraw(); this._redraw();
this.renderTime = Date.now() - renderTime;
this.renderTime = Date.now() - renderStartTime;
// this schedules a new animation step
this.start();
}; };
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -2165,35 +2276,19 @@ if (typeof window !== 'undefined') {
*/ */
Network.prototype.start = function() { Network.prototype.start = function() {
if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) { if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) {
if (this.startedStabilization == false) {
this.emit("startStabilization");
this.startedStabilization = true;
}
if (!this.timer) { if (!this.timer) {
var ua = navigator.userAgent.toLowerCase();
var requiresTimeout = false;
if (ua.indexOf('msie 9.0') != -1) { // IE 9
requiresTimeout = true;
}
else if (ua.indexOf('safari') != -1) { // safari
if (ua.indexOf('chrome') <= -1) {
requiresTimeout = true;
}
}
if (requiresTimeout == true) {
if (this.requiresTimeout == true) {
this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
} }
else{
this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
else {
this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function
} }
} }
} }
else { else {
this._redraw(); this._redraw();
if (this.stabilizationIterations > 0) {
// this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start())
if (this.stabilizationIterations > 1) {
// trigger the "stabilized" event. // trigger the "stabilized" event.
// The event is triggered on the next tick, to prevent the case that // The event is triggered on the next tick, to prevent the case that
// it is fired while initializing the Network, in which case you would not // it is fired while initializing the Network, in which case you would not
@ -2202,12 +2297,15 @@ Network.prototype.start = function() {
var params = { var params = {
iterations: me.stabilizationIterations iterations: me.stabilizationIterations
}; };
me.stabilizationIterations = 0;
me.startedStabilization = false;
this.stabilizationIterations = 0;
this.startedStabilization = false;
setTimeout(function () { setTimeout(function () {
me.emit("stabilized", params); me.emit("stabilized", params);
}, 0); }, 0);
} }
else {
this.stabilizationIterations = 0;
}
} }
}; };
@ -2505,7 +2603,10 @@ Network.prototype.animateView = function (options) {
} }
}; };
/**
* used to animate smoothly by hijacking the redraw function.
* @private
*/
Network.prototype._lockedRedraw = function () { Network.prototype._lockedRedraw = function () {
var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y};
var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight});
@ -2603,4 +2704,11 @@ Network.prototype.getCenterCoordinates = function () {
return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight});
}; };
Network.prototype.getBoundingBox = function(nodeId) {
if (this.nodes[nodeId] !== undefined) {
return this.nodes[nodeId].boundingBox;
}
}
module.exports = Network; module.exports = Network;

+ 194
- 42
lib/network/Node.js View File

@ -40,8 +40,6 @@ function Node(properties, imagelist, grouplist, networkConstants) {
// set defaults for the properties // set defaults for the properties
this.id = undefined; this.id = undefined;
this.x = null;
this.y = null;
this.allowedToMoveX = false; this.allowedToMoveX = false;
this.allowedToMoveY = false; this.allowedToMoveY = false;
this.xFixed = false; this.xFixed = false;
@ -53,8 +51,8 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.level = -1; this.level = -1;
this.preassignedLevel = false; this.preassignedLevel = false;
this.hierarchyEnumerated = false; this.hierarchyEnumerated = false;
this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached
this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached
this.boundingBox = {top:0, left:0, right:0, bottom:0};
this.imagelist = imagelist; this.imagelist = imagelist;
this.grouplist = grouplist; this.grouplist = grouplist;
@ -64,6 +62,12 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.fy = 0.0; // external force y this.fy = 0.0; // external force y
this.vx = 0.0; // velocity x this.vx = 0.0; // velocity x
this.vy = 0.0; // velocity y this.vy = 0.0; // velocity y
this.x = null;
this.y = null;
// used for reverting to previous position on stabilization
this.previousState = {vx:0,vy:0,x:0,y:0};
this.damping = networkConstants.physics.damping; // written every time gravity is calculated this.damping = networkConstants.physics.damping; // written every time gravity is calculated
this.fixedData = {x:null,y:null}; this.fixedData = {x:null,y:null};
@ -87,6 +91,18 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.parentEdgeId = null; this.parentEdgeId = null;
} }
/**
* Revert the position and velocity of the previous step.
*/
Node.prototype.revertPosition = function() {
this.x = this.previousState.x;
this.y = this.previousState.y;
this.vx = this.previousState.vx;
this.vy = this.previousState.vy;
}
/** /**
* (re)setting the clustering variables and objects * (re)setting the clustering variables and objects
*/ */
@ -141,7 +157,7 @@ Node.prototype.setProperties = function(properties, constants) {
} }
var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor',
'fontSize','fontFace','fontFill','group','mass'
'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass'
]; ];
util.selectiveDeepExtend(fields, this.options, properties); util.selectiveDeepExtend(fields, this.options, properties);
@ -166,19 +182,15 @@ Node.prototype.setProperties = function(properties, constants) {
// copy group properties // copy group properties
if (typeof this.options.group === 'number' || (typeof this.options.group === 'string' && this.options.group != '')) { if (typeof this.options.group === 'number' || (typeof this.options.group === 'string' && this.options.group != '')) {
var groupObj = this.grouplist.get(this.options.group); var groupObj = this.grouplist.get(this.options.group);
for (var prop in groupObj) {
if (groupObj.hasOwnProperty(prop)) {
this.options[prop] = groupObj[prop];
}
}
util.deepExtend(this.options, groupObj);
// the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case.
this.options.color = util.parseColor(this.options.color);
} }
// individual shape properties // individual shape properties
if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;}
if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);}
if (this.options.image!== undefined && this.options.image!= "") {
if (this.options.image !== undefined && this.options.image!= "") {
if (this.imagelist) { if (this.imagelist) {
this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage);
} }
@ -206,13 +218,11 @@ Node.prototype.setProperties = function(properties, constants) {
this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); this.radiusFixed = this.radiusFixed || (properties.radius !== undefined);
if (this.options.shape == 'image') {
if (this.options.shape === 'image' || this.options.shape === 'circularImage') {
this.options.radiusMin = constants.nodes.widthMin; this.options.radiusMin = constants.nodes.widthMin;
this.options.radiusMax = constants.nodes.widthMax; this.options.radiusMax = constants.nodes.widthMax;
} }
// choose draw method depending on the shape // choose draw method depending on the shape
switch (this.options.shape) { switch (this.options.shape) {
case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break;
@ -221,6 +231,7 @@ Node.prototype.setProperties = function(properties, constants) {
case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
// TODO: add diamond shape // TODO: add diamond shape
case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break;
case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
@ -344,11 +355,22 @@ Node.prototype._addForce = function(fx, fy) {
this.fy += fy; this.fy += fy;
}; };
/**
* Store the state before the next step
*/
Node.prototype.storeState = function() {
this.previousState.x = this.x;
this.previousState.y = this.y;
this.previousState.vx = this.vx;
this.previousState.vy = this.vy;
}
/** /**
* Perform one discrete step for the node * Perform one discrete step for the node
* @param {number} interval Time interval in seconds * @param {number} interval Time interval in seconds
*/ */
Node.prototype.discreteStep = function(interval) { Node.prototype.discreteStep = function(interval) {
this.storeState();
if (!this.xFixed) { if (!this.xFixed) {
var dx = this.damping * this.vx; // damping force var dx = this.damping * this.vx; // damping force
var ax = (this.fx - dx) / this.options.mass; // acceleration var ax = (this.fx - dx) / this.options.mass; // acceleration
@ -380,6 +402,7 @@ Node.prototype.discreteStep = function(interval) {
* @param {number} maxVelocity The speed limit imposed on the velocity * @param {number} maxVelocity The speed limit imposed on the velocity
*/ */
Node.prototype.discreteStepLimited = function(interval, maxVelocity) { Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
this.storeState();
if (!this.xFixed) { if (!this.xFixed) {
var dx = this.damping * this.vx; // damping force var dx = this.damping * this.vx; // damping force
var ax = (this.fx - dx) / this.options.mass; // acceleration var ax = (this.fx - dx) / this.options.mass; // acceleration
@ -534,16 +557,9 @@ Node.prototype._resizeImage = function (ctx) {
this.growthIndicator = this.width - width; this.growthIndicator = this.width - width;
} }
} }
}; };
Node.prototype._drawImage = function (ctx) {
this._resizeImage(ctx);
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
var yLabel;
Node.prototype._drawImageAtPosition = function (ctx) {
if (this.imageObj.width != 0 ) { if (this.imageObj.width != 0 ) {
// draw the shade // draw the shade
if (this.clusterSize > 1) { if (this.clusterSize > 1) {
@ -558,16 +574,104 @@ Node.prototype._drawImage = function (ctx) {
// draw the image // draw the image
ctx.globalAlpha = 1.0; ctx.globalAlpha = 1.0;
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
yLabel = this.y + this.height / 2;
}
};
Node.prototype._drawImageLabel = function (ctx) {
var yLabel;
var offset = 0;
if (this.height){
offset = this.height / 2;
var labelDimensions = this.getTextSize(ctx);
if (labelDimensions.lineCount >= 1){
offset += labelDimensions.height / 2;
offset += 3;
}
}
yLabel = this.y + offset;
this._label(ctx, this.label, this.x, yLabel, undefined);
};
Node.prototype._drawImage = function (ctx) {
this._resizeImage(ctx);
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
this._drawImageAtPosition(ctx);
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._drawImageLabel(ctx);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height);
};
Node.prototype._resizeCircularImage = function (ctx) {
if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){
if (!this.width) {
var diameter = this.options.radius * 2;
this.width = diameter;
this.height = diameter;
// scaling used for clustering
//this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor;
//this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor;
this.growthIndicator = this.options.radius- 0.5*diameter;
this._swapToImageResizeWhenImageLoaded = true;
}
} }
else { else {
// image still loading... just draw the label for now
yLabel = this.y;
if (this._swapToImageResizeWhenImageLoaded) {
this.width = 0;
this.height = 0;
delete this._swapToImageResizeWhenImageLoaded;
}
this._resizeImage(ctx);
} }
this._label(ctx, this.label, this.x, yLabel, undefined, "top");
}; };
Node.prototype._drawCircularImage = function (ctx) {
this._resizeCircularImage(ctx);
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
var centerX = this.left + (this.width / 2);
var centerY = this.top + (this.height / 2);
var radius = Math.abs(this.height / 2);
this._drawRawCircle(ctx, centerX, centerY, radius);
ctx.save();
ctx.circle(this.x, this.y, radius);
ctx.stroke();
ctx.clip();
this._drawImageAtPosition(ctx);
ctx.restore();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
this._drawImageLabel(ctx);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height);
};
Node.prototype._resizeBox = function (ctx) { Node.prototype._resizeBox = function (ctx) {
if (!this.width) { if (!this.width) {
@ -609,12 +713,17 @@ Node.prototype._drawBox = function (ctx) {
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.options.color.background;
ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius);
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y); this._label(ctx, this.label, this.x, this.y);
}; };
@ -664,6 +773,11 @@ Node.prototype._drawDatabase = function (ctx) {
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y); this._label(ctx, this.label, this.x, this.y);
}; };
@ -686,15 +800,11 @@ Node.prototype._resizeCircle = function (ctx) {
} }
}; };
Node.prototype._drawCircle = function (ctx) {
this._resizeCircle(ctx);
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
Node.prototype._drawRawCircle = function (ctx, x, y, radius) {
var clusterLineWidth = 2.5; var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border // draw the outer border
@ -703,7 +813,7 @@ Node.prototype._drawCircle = function (ctx) {
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.circle(this.x, this.y, this.options.radius+2*ctx.lineWidth);
ctx.circle(x, y, radius+2*ctx.lineWidth);
ctx.stroke(); ctx.stroke();
} }
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
@ -711,9 +821,22 @@ Node.prototype._drawCircle = function (ctx) {
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
ctx.circle(this.x, this.y, this.options.radius);
ctx.circle(this.x, this.y, radius);
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
};
Node.prototype._drawCircle = function (ctx) {
this._resizeCircle(ctx);
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
this._drawRawCircle(ctx, this.x, this.y, this.options.radius);
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
this._label(ctx, this.label, this.x, this.y); this._label(ctx, this.label, this.x, this.y);
}; };
@ -766,6 +889,12 @@ Node.prototype._drawEllipse = function (ctx) {
ctx.ellipse(this.left, this.top, this.width, this.height); ctx.ellipse(this.left, this.top, this.width, this.height);
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y); this._label(ctx, this.label, this.x, this.y);
}; };
@ -843,8 +972,16 @@ Node.prototype._drawShape = function (ctx, shape) {
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
if (this.label) { if (this.label) {
this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top',true);
this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height);
} }
}; };
@ -869,6 +1006,11 @@ Node.prototype._drawText = function (ctx) {
this.top = this.y - this.height / 2; this.top = this.y - this.height / 2;
this._label(ctx, this.label, this.x, this.y); this._label(ctx, this.label, this.x, this.y);
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
}; };
@ -878,7 +1020,7 @@ Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNo
var lines = text.split('\n'); var lines = text.split('\n');
var lineCount = lines.length; var lineCount = lines.length;
var fontSize = (Number(this.options.fontSize) + 4); // TODO: why is this +4 ?
var fontSize = Number(this.options.fontSize);
var yLine = y + (1 - lineCount) / 2 * fontSize; var yLine = y + (1 - lineCount) / 2 * fontSize;
if (labelUnderNode == true) { if (labelUnderNode == true) {
yLine = y + (1 - lineCount) / (2 * fontSize); yLine = y + (1 - lineCount) / (2 * fontSize);
@ -893,8 +1035,10 @@ Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNo
var height = this.options.fontSize * lineCount; var height = this.options.fontSize * lineCount;
var left = x - width / 2; var left = x - width / 2;
var top = y - height / 2; var top = y - height / 2;
if (baseline == "top") {
if (baseline == "hanging") {
top += 0.5 * fontSize; top += 0.5 * fontSize;
top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
yLine += 4; // distance from node
} }
this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine};
@ -908,7 +1052,15 @@ Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNo
ctx.fillStyle = this.options.fontColor || "black"; ctx.fillStyle = this.options.fontColor || "black";
ctx.textAlign = align || "center"; ctx.textAlign = align || "center";
ctx.textBaseline = baseline || "middle"; ctx.textBaseline = baseline || "middle";
if (this.options.fontStrokeWidth > 0){
ctx.lineWidth = this.options.fontStrokeWidth;
ctx.strokeStyle = this.options.fontStrokeColor;
ctx.lineJoin = 'round';
}
for (var i = 0; i < lineCount; i++) { for (var i = 0; i < lineCount; i++) {
if(this.options.fontStrokeWidth){
ctx.strokeText(lines[i], x, yLine);
}
ctx.fillText(lines[i], x, yLine); ctx.fillText(lines[i], x, yLine);
yLine += fontSize; yLine += fontSize;
} }
@ -928,10 +1080,10 @@ Node.prototype.getTextSize = function(ctx) {
width = Math.max(width, ctx.measureText(lines[i]).width); width = Math.max(width, ctx.measureText(lines[i]).width);
} }
return {"width": width, "height": height};
return {"width": width, "height": height, lineCount: lines.length};
} }
else { else {
return {"width": 0, "height": 0};
return {"width": 0, "height": 0, lineCount: 0};
} }
}; };

+ 32
- 44
lib/network/mixins/HierarchicalLayoutMixin.js View File

@ -18,23 +18,6 @@ exports._resetLevels = function() {
*/ */
exports._setupHierarchicalLayout = function() { exports._setupHierarchicalLayout = function() {
if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) {
if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") {
this.constants.hierarchicalLayout.levelSeparation = this.constants.hierarchicalLayout.levelSeparation < 0 ? this.constants.hierarchicalLayout.levelSeparation : this.constants.hierarchicalLayout.levelSeparation * -1;
}
else {
this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation);
}
if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "LR") {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "vertical";
}
}
else {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "horizontal";
}
}
// get the size of the largest hubs and check if the user has defined a level for a node. // get the size of the largest hubs and check if the user has defined a level for a node.
var hubsize = 0; var hubsize = 0;
var node, nodeId; var node, nodeId;
@ -74,7 +57,7 @@ exports._setupHierarchicalLayout = function() {
this._determineLevels(hubsize); this._determineLevels(hubsize);
} }
else { else {
this._determineLevelsDirected();
this._determineLevelsDirected(false);
} }
} }
@ -218,36 +201,24 @@ exports._determineLevels = function(hubsize) {
} }
}; };
/** /**
* this function allocates nodes in levels based on the recursive branching from the largest hubs.
* this function allocates nodes in levels based on the direction of the edges
* *
* @param hubsize * @param hubsize
* @private * @private
*/ */
exports._determineLevelsDirected = function() { exports._determineLevelsDirected = function() {
var nodeId, node;
var nodeId, node, firstNode;
var minLevel = 10000;
// set first node to source // set first node to source
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
this.nodes[nodeId].level = 10000;
break;
}
}
firstNode = this.nodes[this.nodeIndices[0]];
firstNode.level = minLevel;
this._setLevelDirected(minLevel,firstNode.edges,firstNode.id);
// branch from hubs
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
if (node.level == 10000) {
this._setLevelDirected(10000,node.edges,node.id);
}
}
}
// branch from hubs
var minLevel = 10000;
// get the minimum level
for (nodeId in this.nodes) { for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) { if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId]; node = this.nodes[nodeId];
@ -255,7 +226,7 @@ exports._determineLevelsDirected = function() {
} }
} }
// branch from hubs
// subtract the minimum from the set so we have a range starting from 0
for (nodeId in this.nodes) { for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) { if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId]; node = this.nodes[nodeId];
@ -283,6 +254,23 @@ exports._changeConstants = function() {
this.constants.smoothCurves.dynamic = false; this.constants.smoothCurves.dynamic = false;
} }
this._configureSmoothCurves(); this._configureSmoothCurves();
var config = this.constants.hierarchicalLayout;
config.levelSeparation = Math.abs(config.levelSeparation);
if (config.direction == "RL" || config.direction == "DU") {
config.levelSeparation *= -1;
}
if (config.direction == "RL" || config.direction == "LR") {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "vertical";
}
}
else {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "horizontal";
}
}
}; };
@ -361,7 +349,7 @@ exports._setLevel = function(level, edges, parentId) {
/** /**
* this function is called recursively to enumerate the barnches of the largest hubs and give each node a level.
* this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction
* *
* @param level * @param level
* @param edges * @param edges
@ -370,9 +358,9 @@ exports._setLevel = function(level, edges, parentId) {
*/ */
exports._setLevelDirected = function(level, edges, parentId) { exports._setLevelDirected = function(level, edges, parentId) {
this.nodes[parentId].hierarchyEnumerated = true; this.nodes[parentId].hierarchyEnumerated = true;
var childNode, direction;
for (var i = 0; i < edges.length; i++) { for (var i = 0; i < edges.length; i++) {
var childNode = null;
var direction = 1;
direction = 1;
if (edges[i].toId == parentId) { if (edges[i].toId == parentId) {
childNode = edges[i].from; childNode = edges[i].from;
direction = -1; direction = -1;
@ -386,9 +374,9 @@ exports._setLevelDirected = function(level, edges, parentId) {
} }
for (var i = 0; i < edges.length; i++) { for (var i = 0; i < edges.length; i++) {
var childNode = null;
if (edges[i].toId == parentId) {childNode = edges[i].from;} if (edges[i].toId == parentId) {childNode = edges[i].from;}
else {childNode = edges[i].to;} else {childNode = edges[i].to;}
if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) {
this._setLevelDirected(childNode.level, childNode.edges, childNode.id); this._setLevelDirected(childNode.level, childNode.edges, childNode.id);
} }

+ 19
- 8
lib/network/mixins/ManipulationMixin.js View File

@ -8,15 +8,14 @@ var Edge = require('../Edge');
* @private * @private
*/ */
exports._clearManipulatorBar = function() { exports._clearManipulatorBar = function() {
while (this.manipulationDiv.hasChildNodes()) {
this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);
}
this._recursiveDOMDelete(this.manipulationDiv);
this.manipulationDOM = {}; this.manipulationDOM = {};
this._manipulationReleaseOverload = function () {}; this._manipulationReleaseOverload = function () {};
delete this.sectors['support']['nodes']['targetNode']; delete this.sectors['support']['nodes']['targetNode'];
delete this.sectors['support']['nodes']['targetViaNode']; delete this.sectors['support']['nodes']['targetViaNode'];
this.controlNodesActive = false; this.controlNodesActive = false;
this.freezeSimulation = false;
}; };
/** /**
@ -30,6 +29,7 @@ exports._restoreOverloadedFunctions = function() {
for (var functionName in this.cachedFunctions) { for (var functionName in this.cachedFunctions) {
if (this.cachedFunctions.hasOwnProperty(functionName)) { if (this.cachedFunctions.hasOwnProperty(functionName)) {
this[functionName] = this.cachedFunctions[functionName]; this[functionName] = this.cachedFunctions[functionName];
delete this.cachedFunctions[functionName];
} }
} }
}; };
@ -175,7 +175,8 @@ exports._createManipulatorBar = function() {
} }
this.closeDiv.onclick = this._toggleEditMode.bind(this); this.closeDiv.onclick = this._toggleEditMode.bind(this);
this.boundFunction = this._createManipulatorBar.bind(this);
var me = this;
this.boundFunction = me._createManipulatorBar;
this.on('select', this.boundFunction); this.on('select', this.boundFunction);
} }
else { else {
@ -238,7 +239,8 @@ exports._createAddNodeToolbar = function() {
this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this);
// we use the boundFunction so we can reference it when we unbind it from the "select" event. // we use the boundFunction so we can reference it when we unbind it from the "select" event.
this.boundFunction = this._addNode.bind(this);
var me = this;
this.boundFunction = me._addNode;
this.on('select', this.boundFunction); this.on('select', this.boundFunction);
}; };
@ -254,12 +256,12 @@ exports._createAddEdgeToolbar = function() {
this._unselectAll(true); this._unselectAll(true);
this.freezeSimulation = true; this.freezeSimulation = true;
var locale = this.constants.locales[this.constants.locale];
if (this.boundFunction) { if (this.boundFunction) {
this.off('select', this.boundFunction); this.off('select', this.boundFunction);
} }
var locale = this.constants.locales[this.constants.locale];
this._unselectAll(); this._unselectAll();
this.forceAppendSelection = false; this.forceAppendSelection = false;
this.blockConnectingEdgeSelection = true; this.blockConnectingEdgeSelection = true;
@ -290,7 +292,8 @@ exports._createAddEdgeToolbar = function() {
this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this);
// we use the boundFunction so we can reference it when we unbind it from the "select" event. // we use the boundFunction so we can reference it when we unbind it from the "select" event.
this.boundFunction = this._handleConnect.bind(this);
var me = this;
this.boundFunction = me._handleConnect;
this.on('select', this.boundFunction); this.on('select', this.boundFunction);
// temporarily overload functions // temporarily overload functions
@ -401,14 +404,22 @@ exports._controlNodeDrag = function(event) {
this._redraw(); this._redraw();
}; };
/**
*
* @param pointer
* @private
*/
exports._releaseControlNode = function(pointer) { exports._releaseControlNode = function(pointer) {
var newNode = this._getNodeAt(pointer); var newNode = this._getNodeAt(pointer);
if (newNode !== null) { if (newNode !== null) {
if (this.edgeBeingEdited.controlNodes.from.selected == true) { if (this.edgeBeingEdited.controlNodes.from.selected == true) {
this.edgeBeingEdited._restoreControlNodes();
this._editEdge(newNode.id, this.edgeBeingEdited.to.id); this._editEdge(newNode.id, this.edgeBeingEdited.to.id);
this.edgeBeingEdited.controlNodes.from.unselect(); this.edgeBeingEdited.controlNodes.from.unselect();
} }
if (this.edgeBeingEdited.controlNodes.to.selected == true) { if (this.edgeBeingEdited.controlNodes.to.selected == true) {
this.edgeBeingEdited._restoreControlNodes();
this._editEdge(this.edgeBeingEdited.from.id, newNode.id); this._editEdge(this.edgeBeingEdited.from.id, newNode.id);
this.edgeBeingEdited.controlNodes.to.unselect(); this.edgeBeingEdited.controlNodes.to.unselect();
} }

+ 3
- 0
lib/network/mixins/MixinLoader.js View File

@ -47,6 +47,9 @@ exports._loadPhysicsSystem = function () {
if (this.constants.configurePhysics == true) { if (this.constants.configurePhysics == true) {
this._loadPhysicsConfiguration(); this._loadPhysicsConfiguration();
} }
else {
this._cleanupPhysicsConfiguration();
}
}; };

+ 2
- 2
lib/network/mixins/physics/BarnesHutMixin.js View File

@ -49,9 +49,9 @@ exports._getForceContribution = function(parentBranch,node) {
distance = Math.sqrt(dx * dx + dy * dy); distance = Math.sqrt(dx * dx + dy * dy);
// BarnesHut condition // BarnesHut condition
// original condition : s/d < theta = passed === d/s > 1/theta = passed
// original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed
// calcSize = 1/s --> d * 1/s > 1/theta = passed // calcSize = 1/s --> d * 1/s > 1/theta = passed
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) {
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) {
// duplicate code to reduce function calls to speed up program // duplicate code to reduce function calls to speed up program
if (distance == 0) { if (distance == 0) {
distance = 0.1*Math.random(); distance = 0.1*Math.random();

+ 13
- 0
lib/network/mixins/physics/PhysicsMixin.js View File

@ -305,6 +305,17 @@ exports._calculateSpringForce = function (node1, node2, edgeLength) {
}; };
exports._cleanupPhysicsConfiguration = function() {
if (this.physicsConfiguration !== undefined) {
while (this.physicsConfiguration.hasChildNodes()) {
this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild);
}
this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration);
this.physicsConfiguration = undefined;
}
}
/** /**
* Load the HTML for the physics config and bind it * Load the HTML for the physics config and bind it
* @private * @private
@ -706,3 +717,5 @@ function showValueOfRange (id,map,constantsVariableName) {
this.moving = true; this.moving = true;
this.start(); this.start();
} }

+ 8
- 2
lib/network/mixins/physics/RepulsionMixin.js View File

@ -31,6 +31,12 @@ exports._calculateNodeForces = function () {
dy = node2.y - node1.y; dy = node2.y - node1.y;
distance = Math.sqrt(dx * dx + dy * dy); distance = Math.sqrt(dx * dx + dy * dy);
// same condition as BarnesHut, making sure nodes are never 100% overlapping.
if (distance == 0) {
distance = 0.1*Math.random();
dx = distance;
}
minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification));
var a = a_base / minimumDistance; var a = a_base / minimumDistance;
if (distance < 2 * minimumDistance) { if (distance < 2 * minimumDistance) {
@ -43,15 +49,15 @@ exports._calculateNodeForces = function () {
// amplify the repulsion for clusters. // amplify the repulsion for clusters.
repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification;
repulsingForce = repulsingForce / distance;
repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance);
fx = dx * repulsingForce; fx = dx * repulsingForce;
fy = dy * repulsingForce; fy = dy * repulsingForce;
node1.fx -= fx; node1.fx -= fx;
node1.fy -= fy; node1.fy -= fy;
node2.fx += fx; node2.fx += fx;
node2.fy += fy; node2.fy += fy;
} }
} }
} }

+ 4
- 2
lib/timeline/Core.js View File

@ -211,7 +211,9 @@ Core.prototype.setOptions = function (options) {
if ('clickToUse' in options) { if ('clickToUse' in options) {
if (options.clickToUse) { if (options.clickToUse) {
this.activator = new Activator(this.dom.root);
if (!this.activator) {
this.activator = new Activator(this.dom.root);
}
} }
else { else {
if (this.activator) { if (this.activator) {
@ -626,7 +628,7 @@ Core.prototype.redraw = function() {
this.redraw(); this.redraw();
} }
else { else {
console.log('WARNING: infinite loop in redraw?')
console.log('WARNING: infinite loop in redraw?');
} }
this.redrawCount = 0; this.redrawCount = 0;
} }

+ 14
- 8
lib/timeline/Range.js View File

@ -111,9 +111,13 @@ function validateDirection (direction) {
* If animate is a number, the * If animate is a number, the
* number is taken as duration * number is taken as duration
* Default duration is 500 ms. * Default duration is 500 ms.
* @param {Boolean} [byUser=false]
* *
*/ */
Range.prototype.setRange = function(start, end, animate) {
Range.prototype.setRange = function(start, end, animate, byUser) {
if (byUser !== true) {
byUser = false;
}
var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null;
var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null;
this._cancelAnimation(); this._cancelAnimation();
@ -138,12 +142,12 @@ Range.prototype.setRange = function(start, end, animate) {
DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); DateUtil.updateHiddenDates(me.body, me.options.hiddenDates);
anyChanged = anyChanged || changed; anyChanged = anyChanged || changed;
if (changed) { if (changed) {
me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)});
me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser});
} }
if (done) { if (done) {
if (anyChanged) { if (anyChanged) {
me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end)});
me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser});
} }
} }
else { else {
@ -160,7 +164,7 @@ Range.prototype.setRange = function(start, end, animate) {
var changed = this._applyRange(_start, _end); var changed = this._applyRange(_start, _end);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
if (changed) { if (changed) {
var params = {start: new Date(this.start), end: new Date(this.end)};
var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser};
this.body.emitter.emit('rangechange', params); this.body.emitter.emit('rangechange', params);
this.body.emitter.emit('rangechanged', params); this.body.emitter.emit('rangechanged', params);
} }
@ -413,7 +417,8 @@ Range.prototype._onDrag = function (event) {
// fire a rangechange event // fire a rangechange event
this.body.emitter.emit('rangechange', { this.body.emitter.emit('rangechange', {
start: new Date(this.start), start: new Date(this.start),
end: new Date(this.end)
end: new Date(this.end),
byUser: true
}); });
event.preventDefault(); event.preventDefault();
@ -441,7 +446,8 @@ Range.prototype._onDragEnd = function (event) {
// fire a rangechanged event // fire a rangechanged event
this.body.emitter.emit('rangechanged', { this.body.emitter.emit('rangechanged', {
start: new Date(this.start), start: new Date(this.start),
end: new Date(this.end)
end: new Date(this.end),
byUser: true
}); });
}; };
@ -554,7 +560,7 @@ Range.prototype._onPinch = function (event) {
newEnd = safeEnd; newEnd = safeEnd;
} }
this.setRange(newStart, newEnd);
this.setRange(newStart, newEnd, false, true);
this.startToFront = false; // revert to default this.startToFront = false; // revert to default
this.endToFront = true; // revert to default this.endToFront = true; // revert to default
@ -632,7 +638,7 @@ Range.prototype.zoom = function(scale, center, delta) {
newEnd = safeEnd; newEnd = safeEnd;
} }
this.setRange(newStart, newEnd);
this.setRange(newStart, newEnd, false, true);
this.startToFront = false; // revert to default this.startToFront = false; // revert to default
this.endToFront = true; // revert to default this.endToFront = true; // revert to default

+ 73
- 0
lib/timeline/TimeStep.js View File

@ -526,4 +526,77 @@ TimeStep.prototype.getLabelMajor = function(date) {
return (format && format.length > 0) ? moment(date).format(format) : ''; return (format && format.length > 0) ? moment(date).format(format) : '';
}; };
TimeStep.prototype.getClassName = function() {
var m = moment(this.current);
var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
var step = this.step;
function even(value) {
return (value / step % 2 == 0) ? ' even' : ' odd';
}
function today(date) {
if (date.isSame(new Date(), 'day')) {
return ' today';
}
if (date.isSame(moment().add(1, 'day'), 'day')) {
return ' tomorrow';
}
if (date.isSame(moment().add(-1, 'day'), 'day')) {
return ' yesterday';
}
return '';
}
function currentWeek(date) {
return date.isSame(new Date(), 'week') ? ' current-week' : '';
}
function currentMonth(date) {
return date.isSame(new Date(), 'month') ? ' current-month' : '';
}
function currentYear(date) {
return date.isSame(new Date(), 'year') ? ' current-year' : '';
}
switch (this.scale) {
case 'millisecond':
return even(date.milliseconds()).trim();
case 'second':
return even(date.seconds()).trim();
case 'minute':
return even(date.minutes()).trim();
case 'hour':
var hours = date.hours();
if (this.step == 4) {
hours = hours + '-' + (hours + 4);
}
return hours + 'h' + today(date) + even(date.hours());
case 'weekday':
return date.format('dddd').toLowerCase() +
today(date) + currentWeek(date) + even(date.date());
case 'day':
var day = date.date();
var month = date.format('MMMM').toLowerCase();
return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1);
case 'month':
return date.format('MMMM').toLowerCase() +
currentMonth(date) + even(date.month());
case 'year':
var year = date.year();
return 'year' + year + currentYear(date)+ even(year);
default:
return '';
}
};
module.exports = TimeStep; module.exports = TimeStep;

+ 17
- 14
lib/timeline/component/DataAxis.js View File

@ -19,8 +19,6 @@ function DataAxis (body, options, svg, linegraphOptions) {
orientation: 'left', // supported: 'left', 'right' orientation: 'left', // supported: 'left', 'right'
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
showMinorLines: true,
showMajorLines: true,
icons: true, icons: true,
majorLinesOffset: 7, majorLinesOffset: 7,
minorLinesOffset: 4, minorLinesOffset: 4,
@ -120,8 +118,6 @@ DataAxis.prototype.setOptions = function (options) {
'orientation', 'orientation',
'showMinorLabels', 'showMinorLabels',
'showMajorLabels', 'showMajorLabels',
'showMajorLines',
'showMinorLines',
'icons', 'icons',
'majorLinesOffset', 'majorLinesOffset',
'minorLinesOffset', 'minorLinesOffset',
@ -261,7 +257,7 @@ DataAxis.prototype.setRange = function (start, end) {
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
*/ */
DataAxis.prototype.redraw = function () { DataAxis.prototype.redraw = function () {
var changeCalled = false;
var resized = false;
var activeGroups = 0; var activeGroups = 0;
// Make sure the line container adheres to the vertical scrolling. // Make sure the line container adheres to the vertical scrolling.
@ -314,6 +310,8 @@ DataAxis.prototype.redraw = function () {
frame.style.bottom = ''; frame.style.bottom = '';
frame.style.width = this.width + 'px'; frame.style.width = this.width + 'px';
frame.style.height = this.height + "px"; frame.style.height = this.height + "px";
this.props.width = this.body.domProps.left.width;
this.props.height = this.body.domProps.left.height;
} }
else { // right else { // right
frame.style.top = ''; frame.style.top = '';
@ -321,8 +319,12 @@ DataAxis.prototype.redraw = function () {
frame.style.left = '0'; frame.style.left = '0';
frame.style.width = this.width + 'px'; frame.style.width = this.width + 'px';
frame.style.height = this.height + "px"; frame.style.height = this.height + "px";
this.props.width = this.body.domProps.right.width;
this.props.height = this.body.domProps.right.height;
} }
changeCalled = this._redrawLabels();
resized = this._redrawLabels();
resized = this._isResized() || resized;
if (this.options.icons == true) { if (this.options.icons == true) {
this._redrawGroupIcons(); this._redrawGroupIcons();
@ -333,7 +335,7 @@ DataAxis.prototype.redraw = function () {
this._redrawTitle(orientation); this._redrawTitle(orientation);
} }
return changeCalled;
return resized;
}; };
/** /**
@ -341,6 +343,7 @@ DataAxis.prototype.redraw = function () {
* @private * @private
*/ */
DataAxis.prototype._redrawLabels = function () { DataAxis.prototype._redrawLabels = function () {
var resized = false;
DOMutil.prepareElements(this.DOMelements.lines); DOMutil.prepareElements(this.DOMelements.lines);
DOMutil.prepareElements(this.DOMelements.labels); DOMutil.prepareElements(this.DOMelements.labels);
@ -421,11 +424,9 @@ DataAxis.prototype._redrawLabels = function () {
if (y >= 0) { if (y >= 0) {
this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight);
} }
if (this.options.showMajorLines == true) {
this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth);
}
this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth);
} }
else if (this.options.showMinorLines == true) {
else {
this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth);
} }
@ -457,7 +458,7 @@ DataAxis.prototype._redrawLabels = function () {
DOMutil.cleanupElements(this.DOMelements.lines); DOMutil.cleanupElements(this.DOMelements.lines);
DOMutil.cleanupElements(this.DOMelements.labels); DOMutil.cleanupElements(this.DOMelements.labels);
this.redraw(); this.redraw();
return true;
resized = true;
} }
// this will resize the yAxis if it is too big for the labels. // this will resize the yAxis if it is too big for the labels.
else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) {
@ -466,13 +467,15 @@ DataAxis.prototype._redrawLabels = function () {
DOMutil.cleanupElements(this.DOMelements.lines); DOMutil.cleanupElements(this.DOMelements.lines);
DOMutil.cleanupElements(this.DOMelements.labels); DOMutil.cleanupElements(this.DOMelements.labels);
this.redraw(); this.redraw();
return true;
resized = true;
} }
else { else {
DOMutil.cleanupElements(this.DOMelements.lines); DOMutil.cleanupElements(this.DOMelements.lines);
DOMutil.cleanupElements(this.DOMelements.labels); DOMutil.cleanupElements(this.DOMelements.labels);
return false;
resized = false;
} }
return resized;
}; };
DataAxis.prototype.convertValue = function (value) { DataAxis.prototype.convertValue = function (value) {

+ 54
- 47
lib/timeline/component/LineGraph.js View File

@ -50,8 +50,6 @@ function LineGraph(body, options) {
dataAxis: { dataAxis: {
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
showMinorLines: true,
showMajorLines: true,
icons: false, icons: false,
width: '40px', width: '40px',
visible: true, visible: true,
@ -100,7 +98,8 @@ function LineGraph(body, options) {
this.hammer = null; this.hammer = null;
this.groups = {}; this.groups = {};
this.abortedGraphUpdate = false; this.abortedGraphUpdate = false;
this.autoSizeSVG = false;
this.updateSVGheight = false;
this.updateSVGheightOnResize = false;
var me = this; var me = this;
this.itemsData = null; // DataSet this.itemsData = null; // DataSet
@ -143,7 +142,7 @@ function LineGraph(body, options) {
this.COUNTER = 0; this.COUNTER = 0;
this.body.emitter.on('rangechanged', function() { this.body.emitter.on('rangechanged', function() {
me.lastStart = me.body.range.start; me.lastStart = me.body.range.start;
me.svg.style.left = util.option.asSize(-me.width);
me.svg.style.left = util.option.asSize(-me.props.width);
me.redraw.call(me,true); me.redraw.call(me,true);
}); });
@ -192,13 +191,14 @@ LineGraph.prototype._create = function(){
*/ */
LineGraph.prototype.setOptions = function(options) { LineGraph.prototype.setOptions = function(options) {
if (options) { if (options) {
var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups'];
var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups'];
if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) {
this.autoSizeSVG = true;
this.updateSVGheight = true;
this.updateSVGheightOnResize = true;
} }
else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) {
if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) {
this.autoSizeSVG = true;
this.updateSVGheight = true;
} }
} }
util.selectiveDeepExtend(fields, this.options, options); util.selectiveDeepExtend(fields, this.options, options);
@ -543,49 +543,67 @@ LineGraph.prototype._updateUngrouped = function() {
LineGraph.prototype.redraw = function(forceGraphUpdate) { LineGraph.prototype.redraw = function(forceGraphUpdate) {
var resized = false; var resized = false;
this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px';
if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) {
resized = true;
// calculate actual size and position
this.props.width = this.dom.frame.offsetWidth;
this.props.height = this.body.domProps.centerContainer.height;
// update the graph if there is no lastWidth or with, used for the initial draw
if (this.lastWidth === undefined && this.props.width) {
forceGraphUpdate = true;
} }
// check if this component is resized // check if this component is resized
resized = this._isResized() || resized; resized = this._isResized() || resized;
// check whether zoomed (in that case we need to re-stack everything) // check whether zoomed (in that case we need to re-stack everything)
var visibleInterval = this.body.range.end - this.body.range.start; var visibleInterval = this.body.range.end - this.body.range.start;
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); // we get this from the range changed event
var zoomed = (visibleInterval != this.lastVisibleInterval);
this.lastVisibleInterval = visibleInterval; this.lastVisibleInterval = visibleInterval;
this.lastWidth = this.width;
// calculate actual size and position
this.width = this.dom.frame.offsetWidth;
// the svg element is three times as big as the width, this allows for fully dragging left and right // the svg element is three times as big as the width, this allows for fully dragging left and right
// without reloading the graph. the controls for this are bound to events in the constructor // without reloading the graph. the controls for this are bound to events in the constructor
if (resized == true) { if (resized == true) {
this.svg.style.width = util.option.asSize(3*this.width);
this.svg.style.left = util.option.asSize(-this.width);
this.svg.style.width = util.option.asSize(3*this.props.width);
this.svg.style.left = util.option.asSize(-this.props.width);
// if the height of the graph is set as proportional, change the height of the svg
if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) {
this.updateSVGheight = true;
}
}
// update the height of the graph on each redraw of the graph.
if (this.updateSVGheight == true) {
if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') {
this.options.graphHeight = this.body.domProps.centerContainer.height + 'px';
this.svg.style.height = this.body.domProps.centerContainer.height + 'px';
}
this.updateSVGheight = false;
}
else {
this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px';
} }
// zoomed is here to ensure that animations are shown correctly. // zoomed is here to ensure that animations are shown correctly.
if (zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) {
resized = resized || this._updateGraph();
if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) {
resized = this._updateGraph() || resized;
} }
else { else {
// move the whole svg while dragging // move the whole svg while dragging
if (this.lastStart != 0) { if (this.lastStart != 0) {
var offset = this.body.range.start - this.lastStart; var offset = this.body.range.start - this.lastStart;
var range = this.body.range.end - this.body.range.start; var range = this.body.range.end - this.body.range.start;
if (this.width != 0) {
var rangePerPixelInv = this.width/range;
if (this.props.width != 0) {
var rangePerPixelInv = this.props.width/range;
var xOffset = offset * rangePerPixelInv; var xOffset = offset * rangePerPixelInv;
this.svg.style.left = (-this.width - xOffset) + 'px';
this.svg.style.left = (-this.props.width - xOffset) + 'px';
} }
} }
} }
this.legendLeft.redraw(); this.legendLeft.redraw();
this.legendRight.redraw(); this.legendRight.redraw();
return resized; return resized;
}; };
@ -597,22 +615,13 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) {
LineGraph.prototype._updateGraph = function () { LineGraph.prototype._updateGraph = function () {
// reset the svg elements // reset the svg elements
DOMutil.prepareElements(this.svgElements); DOMutil.prepareElements(this.svgElements);
if (this.width != 0 && this.itemsData != null) {
if (this.props.width != 0 && this.itemsData != null) {
var group, i; var group, i;
var preprocessedGroupData = {}; var preprocessedGroupData = {};
var processedGroupData = {}; var processedGroupData = {};
var groupRanges = {}; var groupRanges = {};
var changeCalled = false; var changeCalled = false;
// update the height of the graph on each redraw of the graph.
if (this.autoSizeSVG == true) {
if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') {
this.options.graphHeight = this.body.domProps.centerContainer.height + 'px';
this.svg.style.height = this.body.domProps.centerContainer.height + 'px';
}
this.autoSizeSVG = false;
}
// getting group Ids // getting group Ids
var groupIds = []; var groupIds = [];
for (var groupId in this.groups) { for (var groupId in this.groups) {
@ -815,7 +824,7 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
* @private * @private
*/ */
LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
var changeCalled = false;
var resized = false;
var yAxisLeftUsed = false; var yAxisLeftUsed = false;
var yAxisRightUsed = false; var yAxisRightUsed = false;
var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal;
@ -824,12 +833,12 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
// this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop.
for (var i = 0; i < groupIds.length; i++) { for (var i = 0; i < groupIds.length; i++) {
var group = this.groups[groupIds[i]]; var group = this.groups[groupIds[i]];
if (group && group.options.yAxisOrientation == 'left') {
if (group && group.options.yAxisOrientation != 'right') {
yAxisLeftUsed = true; yAxisLeftUsed = true;
minLeft = 0; minLeft = 0;
maxLeft = 0; maxLeft = 0;
} }
else {
else if (group && group.options.yAxisOrientation) {
yAxisRightUsed = true; yAxisRightUsed = true;
minRight = 0; minRight = 0;
maxRight = 0; maxRight = 0;
@ -843,7 +852,7 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
minVal = groupRanges[groupIds[i]].min; minVal = groupRanges[groupIds[i]].min;
maxVal = groupRanges[groupIds[i]].max; maxVal = groupRanges[groupIds[i]].max;
if (groupRanges[groupIds[i]].yAxisOrientation == 'left') {
if (groupRanges[groupIds[i]].yAxisOrientation != 'right') {
yAxisLeftUsed = true; yAxisLeftUsed = true;
minLeft = minLeft > minVal ? minVal : minLeft; minLeft = minLeft > minVal ? minVal : minLeft;
maxLeft = maxLeft < maxVal ? maxVal : maxLeft; maxLeft = maxLeft < maxVal ? maxVal : maxLeft;
@ -864,8 +873,8 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
this.yAxisRight.setRange(minRight, maxRight); this.yAxisRight.setRange(minRight, maxRight);
} }
} }
changeCalled = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || changeCalled;
changeCalled = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changeCalled;
resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized;
resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized;
if (yAxisRightUsed == true && yAxisLeftUsed == true) { if (yAxisRightUsed == true && yAxisLeftUsed == true) {
this.yAxisLeft.drawIcons = true; this.yAxisLeft.drawIcons = true;
@ -875,20 +884,18 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
this.yAxisLeft.drawIcons = false; this.yAxisLeft.drawIcons = false;
this.yAxisRight.drawIcons = false; this.yAxisRight.drawIcons = false;
} }
this.yAxisRight.master = !yAxisLeftUsed; this.yAxisRight.master = !yAxisLeftUsed;
if (this.yAxisRight.master == false) { if (this.yAxisRight.master == false) {
if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;}
else {this.yAxisLeft.lineOffset = 0;} else {this.yAxisLeft.lineOffset = 0;}
changeCalled = this.yAxisLeft.redraw() || changeCalled;
resized = this.yAxisLeft.redraw() || resized;
this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels;
this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing;
changeCalled = this.yAxisRight.redraw() || changeCalled;
resized = this.yAxisRight.redraw() || resized;
} }
else { else {
changeCalled = this.yAxisRight.redraw() || changeCalled;
resized = this.yAxisRight.redraw() || resized;
} }
// clean the accumulated lists // clean the accumulated lists
@ -899,7 +906,7 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
groupIds.splice(groupIds.indexOf('__barchartRight'),1); groupIds.splice(groupIds.indexOf('__barchartRight'),1);
} }
return changeCalled;
return resized;
}; };
@ -944,7 +951,7 @@ LineGraph.prototype._convertXcoordinates = function (datapoints) {
var toScreen = this.body.util.toScreen; var toScreen = this.body.util.toScreen;
for (var i = 0; i < datapoints.length; i++) { for (var i = 0; i < datapoints.length; i++) {
xValue = toScreen(datapoints[i].x) + this.width;
xValue = toScreen(datapoints[i].x) + this.props.width;
yValue = datapoints[i].y; yValue = datapoints[i].y;
extractedData.push({x: xValue, y: yValue}); extractedData.push({x: xValue, y: yValue});
} }
@ -974,7 +981,7 @@ LineGraph.prototype._convertYcoordinates = function (datapoints, group) {
} }
for (var i = 0; i < datapoints.length; i++) { for (var i = 0; i < datapoints.length; i++) {
xValue = toScreen(datapoints[i].x) + this.width;
xValue = toScreen(datapoints[i].x) + this.props.width;
yValue = Math.round(axis.convertValue(datapoints[i].y)); yValue = Math.round(axis.convertValue(datapoints[i].y));
extractedData.push({x: xValue, y: yValue}); extractedData.push({x: xValue, y: yValue});
} }

+ 60
- 39
lib/timeline/component/TimeAxis.js View File

@ -15,14 +15,12 @@ var moment = require('../../module/moment');
function TimeAxis (body, options) { function TimeAxis (body, options) {
this.dom = { this.dom = {
foreground: null, foreground: null,
majorLines: [],
lines: [],
majorTexts: [], majorTexts: [],
minorLines: [],
minorTexts: [], minorTexts: [],
redundant: { redundant: {
majorLines: [],
lines: [],
majorTexts: [], majorTexts: [],
minorLines: [],
minorTexts: [] minorTexts: []
} }
}; };
@ -40,8 +38,6 @@ function TimeAxis (body, options) {
// TODO: implement timeaxis orientations 'left' and 'right' // TODO: implement timeaxis orientations 'left' and 'right'
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
showMajorLines: true,
showMinorLines: true,
format: null format: null
}; };
this.options = util.extend({}, this.defaultOptions); this.options = util.extend({}, this.defaultOptions);
@ -67,7 +63,13 @@ TimeAxis.prototype = new Component();
TimeAxis.prototype.setOptions = function(options) { TimeAxis.prototype.setOptions = function(options) {
if (options) { if (options) {
// copy all options that we know // copy all options that we know
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels', 'showMinorLines', 'showMajorLines','hiddenDates', 'format'], this.options, options);
util.selectiveExtend([
'orientation',
'showMinorLabels',
'showMajorLabels',
'hiddenDates',
'format'
], this.options, options);
// apply locale to moment.js // apply locale to moment.js
// TODO: not so nice, this is applied globally to moment.js // TODO: not so nice, this is applied globally to moment.js
@ -194,29 +196,40 @@ TimeAxis.prototype._repaintLabels = function () {
// can be picked for re-use, and clear the lists with lines and texts. // can be picked for re-use, and clear the lists with lines and texts.
// At the end of the function _repaintLabels, left over elements will be cleaned up // At the end of the function _repaintLabels, left over elements will be cleaned up
var dom = this.dom; var dom = this.dom;
dom.redundant.majorLines = dom.majorLines;
dom.redundant.lines = dom.lines;
dom.redundant.majorTexts = dom.majorTexts; dom.redundant.majorTexts = dom.majorTexts;
dom.redundant.minorLines = dom.minorLines;
dom.redundant.minorTexts = dom.minorTexts; dom.redundant.minorTexts = dom.minorTexts;
dom.majorLines = [];
dom.lines = [];
dom.majorTexts = []; dom.majorTexts = [];
dom.minorLines = [];
dom.minorTexts = []; dom.minorTexts = [];
step.first();
var cur;
var x = 0;
var isMajor;
var xPrev = 0;
var width = 0;
var prevLine;
var xFirstMajorLabel = undefined; var xFirstMajorLabel = undefined;
var max = 0; var max = 0;
var className;
step.first();
while (step.hasNext() && max < 1000) { while (step.hasNext() && max < 1000) {
max++; max++;
var cur = step.getCurrent();
var x = this.body.util.toScreen(cur);
var isMajor = step.isMajor();
cur = step.getCurrent();
isMajor = step.isMajor();
className = step.getClassName();
// TODO: lines must have a width, such that we can create css backgrounds
xPrev = x;
x = this.body.util.toScreen(cur);
width = x - xPrev;
if (prevLine) {
prevLine.style.width = width + 'px';
}
if (this.options.showMinorLabels) { if (this.options.showMinorLabels) {
this._repaintMinorText(x, step.getLabelMinor(), orientation);
this._repaintMinorText(x, step.getLabelMinor(), orientation, className);
} }
if (isMajor && this.options.showMajorLabels) { if (isMajor && this.options.showMajorLabels) {
@ -224,14 +237,12 @@ TimeAxis.prototype._repaintLabels = function () {
if (xFirstMajorLabel == undefined) { if (xFirstMajorLabel == undefined) {
xFirstMajorLabel = x; xFirstMajorLabel = x;
} }
this._repaintMajorText(x, step.getLabelMajor(), orientation);
}
if (this.options.showMajorLines == true) {
this._repaintMajorLine(x, orientation);
this._repaintMajorText(x, step.getLabelMajor(), orientation, className);
} }
prevLine = this._repaintMajorLine(x, orientation, className);
} }
else if (this.options.showMinorLines == true) {
this._repaintMinorLine(x, orientation);
else {
prevLine = this._repaintMinorLine(x, orientation, className);
} }
step.next(); step.next();
@ -244,7 +255,7 @@ TimeAxis.prototype._repaintLabels = function () {
widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation
if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
this._repaintMajorText(0, leftText, orientation);
this._repaintMajorText(0, leftText, orientation, className);
} }
} }
@ -264,9 +275,10 @@ TimeAxis.prototype._repaintLabels = function () {
* @param {Number} x * @param {Number} x
* @param {String} text * @param {String} text
* @param {String} orientation "top" or "bottom" (default) * @param {String} orientation "top" or "bottom" (default)
* @param {String} className
* @private * @private
*/ */
TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) {
// reuse redundant label // reuse redundant label
var label = this.dom.redundant.minorTexts.shift(); var label = this.dom.redundant.minorTexts.shift();
@ -275,7 +287,6 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
var content = document.createTextNode(''); var content = document.createTextNode('');
label = document.createElement('div'); label = document.createElement('div');
label.appendChild(content); label.appendChild(content);
label.className = 'text minor';
this.dom.foreground.appendChild(label); this.dom.foreground.appendChild(label);
} }
this.dom.minorTexts.push(label); this.dom.minorTexts.push(label);
@ -284,6 +295,7 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0';
label.style.left = x + 'px'; label.style.left = x + 'px';
label.className = 'text minor ' + className;
//label.title = title; // TODO: this is a heavy operation //label.title = title; // TODO: this is a heavy operation
}; };
@ -292,9 +304,10 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
* @param {Number} x * @param {Number} x
* @param {String} text * @param {String} text
* @param {String} orientation "top" or "bottom" (default) * @param {String} orientation "top" or "bottom" (default)
* @param {String} className
* @private * @private
*/ */
TimeAxis.prototype._repaintMajorText = function (x, text, orientation) {
TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) {
// reuse redundant label // reuse redundant label
var label = this.dom.redundant.majorTexts.shift(); var label = this.dom.redundant.majorTexts.shift();
@ -302,13 +315,13 @@ TimeAxis.prototype._repaintMajorText = function (x, text, orientation) {
// create label // create label
var content = document.createTextNode(text); var content = document.createTextNode(text);
label = document.createElement('div'); label = document.createElement('div');
label.className = 'text major';
label.appendChild(content); label.appendChild(content);
this.dom.foreground.appendChild(label); this.dom.foreground.appendChild(label);
} }
this.dom.majorTexts.push(label); this.dom.majorTexts.push(label);
label.childNodes[0].nodeValue = text; label.childNodes[0].nodeValue = text;
label.className = 'text major ' + className;
//label.title = title; // TODO: this is a heavy operation //label.title = title; // TODO: this is a heavy operation
label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px');
@ -319,19 +332,19 @@ TimeAxis.prototype._repaintMajorText = function (x, text, orientation) {
* Create a minor line for the axis at position x * Create a minor line for the axis at position x
* @param {Number} x * @param {Number} x
* @param {String} orientation "top" or "bottom" (default) * @param {String} orientation "top" or "bottom" (default)
* @param {String} className
* @return {Element} Returns the created line
* @private * @private
*/ */
TimeAxis.prototype._repaintMinorLine = function (x, orientation) {
TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) {
// reuse redundant line // reuse redundant line
var line = this.dom.redundant.minorLines.shift();
var line = this.dom.redundant.lines.shift();
if (!line) { if (!line) {
// create vertical line // create vertical line
line = document.createElement('div'); line = document.createElement('div');
line.className = 'grid vertical minor';
this.dom.background.appendChild(line); this.dom.background.appendChild(line);
} }
this.dom.minorLines.push(line);
this.dom.lines.push(line);
var props = this.props; var props = this.props;
if (orientation == 'top') { if (orientation == 'top') {
@ -342,25 +355,29 @@ TimeAxis.prototype._repaintMinorLine = function (x, orientation) {
} }
line.style.height = props.minorLineHeight + 'px'; line.style.height = props.minorLineHeight + 'px';
line.style.left = (x - props.minorLineWidth / 2) + 'px'; line.style.left = (x - props.minorLineWidth / 2) + 'px';
line.className = 'grid vertical minor ' + className;
return line;
}; };
/** /**
* Create a Major line for the axis at position x * Create a Major line for the axis at position x
* @param {Number} x * @param {Number} x
* @param {String} orientation "top" or "bottom" (default) * @param {String} orientation "top" or "bottom" (default)
* @param {String} className
* @return {Element} Returns the created line
* @private * @private
*/ */
TimeAxis.prototype._repaintMajorLine = function (x, orientation) {
TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) {
// reuse redundant line // reuse redundant line
var line = this.dom.redundant.majorLines.shift();
var line = this.dom.redundant.lines.shift();
if (!line) { if (!line) {
// create vertical line // create vertical line
line = document.createElement('DIV');
line.className = 'grid vertical major';
line = document.createElement('div');
this.dom.background.appendChild(line); this.dom.background.appendChild(line);
} }
this.dom.majorLines.push(line);
this.dom.lines.push(line);
var props = this.props; var props = this.props;
if (orientation == 'top') { if (orientation == 'top') {
@ -371,6 +388,10 @@ TimeAxis.prototype._repaintMajorLine = function (x, orientation) {
} }
line.style.left = (x - props.majorLineWidth / 2) + 'px'; line.style.left = (x - props.majorLineWidth / 2) + 'px';
line.style.height = props.majorLineHeight + 'px'; line.style.height = props.majorLineHeight + 'px';
line.className = 'grid vertical major ' + className;
return line;
}; };
/** /**

+ 1
- 2
lib/timeline/component/css/timeaxis.css View File

@ -35,8 +35,7 @@
.vis.timeline .timeaxis .grid.vertical { .vis.timeline .timeaxis .grid.vertical {
position: absolute; position: absolute;
width: 0;
border-right: 1px solid;
border-left: 1px solid;
} }
.vis.timeline .timeaxis .grid.minor { .vis.timeline .timeaxis .grid.minor {

+ 28
- 76
lib/util.js View File

@ -737,48 +737,36 @@ exports.option.asElement = function (value, defaultValue) {
return value || defaultValue || null; return value || defaultValue || null;
}; };
exports.GiveDec = function(Hex) {
var Value;
if (Hex == "A")
Value = 10;
else if (Hex == "B")
Value = 11;
else if (Hex == "C")
Value = 12;
else if (Hex == "D")
Value = 13;
else if (Hex == "E")
Value = 14;
else if (Hex == "F")
Value = 15;
else
Value = eval(Hex);
return Value;
/**
* http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
*
* @param {String} hex
* @returns {{r: *, g: *, b: *}} | 255 range
*/
exports.hexToRGB = function(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}; };
exports.GiveHex = function(Dec) {
var Value;
if(Dec == 10)
Value = "A";
else if (Dec == 11)
Value = "B";
else if (Dec == 12)
Value = "C";
else if (Dec == 13)
Value = "D";
else if (Dec == 14)
Value = "E";
else if (Dec == 15)
Value = "F";
else
Value = "" + Dec;
return Value;
/**
*
* @param red 0 -- 255
* @param green 0 -- 255
* @param blue 0 -- 255
* @returns {string}
* @constructor
*/
exports.RGBToHex = function(red,green,blue) {
return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
}; };
/** /**
@ -862,42 +850,6 @@ exports.parseColor = function(color) {
return c; return c;
}; };
/**
* http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
*
* @param {String} hex
* @returns {{r: *, g: *, b: *}}
*/
exports.hexToRGB = function(hex) {
hex = hex.replace("#","").toUpperCase();
var a = exports.GiveDec(hex.substring(0, 1));
var b = exports.GiveDec(hex.substring(1, 2));
var c = exports.GiveDec(hex.substring(2, 3));
var d = exports.GiveDec(hex.substring(3, 4));
var e = exports.GiveDec(hex.substring(4, 5));
var f = exports.GiveDec(hex.substring(5, 6));
var r = (a * 16) + b;
var g = (c * 16) + d;
var b = (e * 16) + f;
return {r:r,g:g,b:b};
};
exports.RGBToHex = function(red,green,blue) {
var a = exports.GiveHex(Math.floor(red / 16));
var b = exports.GiveHex(red % 16);
var c = exports.GiveHex(Math.floor(green / 16));
var d = exports.GiveHex(green % 16);
var e = exports.GiveHex(Math.floor(blue / 16));
var f = exports.GiveHex(blue % 16);
var hex = a + b + c + d + e + f;
return "#" + hex;
};
/** /**
* http://www.javascripter.net/faq/rgb2hsv.htm * http://www.javascripter.net/faq/rgb2hsv.htm
* *

+ 2
- 2
package.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "name": "vis",
"version": "3.7.2-SNAPSHOT",
"version": "3.9.0",
"description": "A dynamic, browser-based visualization library.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",
"repository": { "repository": {
@ -31,7 +31,7 @@
"emitter-component": "^1.1.1", "emitter-component": "^1.1.1",
"hammerjs": "^2.0.4", "hammerjs": "^2.0.4",
"keycharm": "^0.1.6", "keycharm": "^0.1.6",
"moment": "^2.7.0",
"moment": "^2.9.0",
"keycharm": "^0.2.0" "keycharm": "^0.2.0"
}, },
"devDependencies": { "devDependencies": {

+ 14
- 0
test/timeline.html View File

@ -25,6 +25,20 @@
width: 40px; width: 40px;
background: rgba(255,255,255,0.5); background: rgba(255,255,255,0.5);
} }
#visualization .grid.vertical.odd {
background: #f5f5f5;
}
#visualization .grid.vertical.saturday,
#visualization .grid.vertical.sunday {
background: gray;
}
#visualization .text.saturday,
#visualization .text.sunday {
color: white;
}
</style> </style>
</head> </head>

+ 0
- 470
www2/css/bootstrap-theme.css View File

@ -1,470 +0,0 @@
/*!
* Bootstrap v3.3.1 (http://getbootstrap.com)
* Copyright 2011-2014 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default:disabled,
.btn-default[disabled] {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary:disabled,
.btn-primary[disabled] {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success:disabled,
.btn-success[disabled] {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info:disabled,
.btn-info[disabled] {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning:disabled,
.btn-warning[disabled] {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger:disabled,
.btn-danger[disabled] {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */

+ 0
- 1
www2/css/bootstrap-theme.css.map
File diff suppressed because it is too large
View File


+ 0
- 5
www2/css/bootstrap-theme.min.css
File diff suppressed because it is too large
View File


+ 0
- 6332
www2/css/bootstrap.css
File diff suppressed because it is too large
View File


+ 0
- 1
www2/css/bootstrap.css.map
File diff suppressed because it is too large
View File


+ 0
- 5
www2/css/bootstrap.min.css
File diff suppressed because it is too large
View File


+ 0
- 144
www2/css/carousel.css View File

@ -1,144 +0,0 @@
/* GLOBAL STYLES
-------------------------------------------------- */
/* Padding below the footer and lighter body text */
body {
padding-bottom: 40px;
color: #5a5a5a;
}
/* CUSTOMIZE THE NAVBAR
-------------------------------------------------- */
/* Special class on .container surrounding .navbar, used for positioning it into place. */
.navbar-wrapper {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 20;
}
/* Flip around the padding for proper display in narrow viewports */
.navbar-wrapper > .container {
padding-right: 0;
padding-left: 0;
}
.navbar-wrapper .navbar {
padding-right: 15px;
padding-left: 15px;
}
.navbar-wrapper .navbar .container {
width: auto;
}
/* CUSTOMIZE THE CAROUSEL
-------------------------------------------------- */
/* Carousel base class */
.carousel {
height: 500px;
margin-bottom: 60px;
}
/* Since positioning the image, we need to help out the caption */
.carousel-caption {
z-index: 10;
text-shadow:
-0px -0px 3px #000,
-2px -2px 0 #000,
2px -2px 0 #000,
-2px 2px 0 #000,
2px 2px 0 #000;
}
/* Declare heights because of positioning of img element */
.carousel .item {
height: 500px;
background-color: #064880;
}
.carousel .item.dark {
background-color: #06132c;
}
.carousel-inner > .item > img {
position: absolute;
top: 0;
left: 0;
min-width: 100%;
height: 500px;
}
/* MARKETING CONTENT
-------------------------------------------------- */
/* Center align the text within the three columns below the carousel */
.marketing .col-lg-3 {
margin-bottom: 20px;
text-align: center;
}
.marketing h2 {
font-weight: normal;
}
.marketing .col-lg-3 p {
margin-right: 10px;
margin-left: 10px;
}
/* Featurettes
------------------------- */
.featurette-divider {
margin: 80px 0; /* Space out the Bootstrap <hr> more */
border-top: 1px solid #d2d2d2;
}
/* Thin out the marketing headings */
.featurette-heading {
font-weight: 300;
line-height: 1;
letter-spacing: -1px;
}
/* RESPONSIVE CSS
-------------------------------------------------- */
@media (min-width: 768px) {
/* Navbar positioning foo */
.navbar-wrapper {
margin-top: 20px;
}
.navbar-wrapper .container {
padding-right: 15px;
padding-left: 15px;
}
.navbar-wrapper .navbar {
padding-right: 0;
padding-left: 0;
}
/* The navbar becomes detached from the top, so we round the corners */
.navbar-wrapper .navbar {
border-radius: 4px;
}
/* Bump up size of carousel content */
.carousel-caption p {
margin-bottom: 20px;
font-size: 21px;
line-height: 1.4;
}
.featurette-heading {
font-size: 50px;
}
}
@media (min-width: 992px) {
.featurette-heading {
margin-top: 120px;
}
}

+ 0
- 87
www2/css/prettify.css View File

@ -1,87 +0,0 @@
.com {
color: gray;
}
.lit {
color: red;
}
.pun {
color: gray;
}
.pln {
color: #333333;
}
pre.prettyprint {
border: 1px solid lightgray;
background-color: #fcfcfc;
padding: 5px;
font-size: 10pt;
line-height: 1.5em;
font-family: monospace;
}
ol.linenums {
margin-top:0;
margin-bottom:0;
}
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 {
list-style:none;
}
li.L1,li.L3,li.L5,li.L7,li.L9 {
background:#eee;
}
.str,.atv {
color: green;
}
.kwd,.tag {
color:#2B7CE9;
}
.typ,.atn,.dec {
color: darkorange;
}
@media print {
.com {
color:#600;
font-style:italic;
}
.typ {
color:#404;
font-weight:700;
}
.lit {
color:#044;
}
.pun {
color:#440;
}
.pln {
color:#000;
}
.atn {
color:#404;
}
.str,.atv {
color:#060;
}
.kwd,.tag {
color:#006;
font-weight:700;
}
}

BIN
www2/dist/img/network/acceptDeleteIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/addNodeIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/backIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/connectIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/cross.png View File

Before After
Width: 7  |  Height: 7  |  Size: 18 KiB

BIN
www2/dist/img/network/cross2.png View File

Before After
Width: 5  |  Height: 5  |  Size: 17 KiB

BIN
www2/dist/img/network/deleteIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/downArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
www2/dist/img/network/editIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
www2/dist/img/network/leftArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
www2/dist/img/network/minus.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.0 KiB

BIN
www2/dist/img/network/plus.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.2 KiB

BIN
www2/dist/img/network/rightArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
www2/dist/img/network/upArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
www2/dist/img/network/zoomExtends.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
www2/dist/img/timeline/delete.png View File

Before After
Width: 16  |  Height: 16  |  Size: 665 B

+ 0
- 795
www2/dist/vis.css View File

@ -1,795 +0,0 @@
.vis .overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* Must be displayed above for example selected Timeline items */
z-index: 10;
}
.vis-active {
box-shadow: 0 0 10px #86d5f8;
}
/* override some bootstrap styles screwing up the timelines css */
.vis [class*="span"] {
min-height: 0;
width: auto;
}
.vis.timeline {
}
.vis.timeline.root {
position: relative;
border: 1px solid #bfbfbf;
overflow: hidden;
padding: 0;
margin: 0;
box-sizing: border-box;
}
.vis.timeline .vispanel {
position: absolute;
padding: 0;
margin: 0;
box-sizing: border-box;
}
.vis.timeline .vispanel.center,
.vis.timeline .vispanel.left,
.vis.timeline .vispanel.right,
.vis.timeline .vispanel.top,
.vis.timeline .vispanel.bottom {
border: 1px #bfbfbf;
}
.vis.timeline .vispanel.center,
.vis.timeline .vispanel.left,
.vis.timeline .vispanel.right {
border-top-style: solid;
border-bottom-style: solid;
overflow: hidden;
}
.vis.timeline .vispanel.center,
.vis.timeline .vispanel.top,
.vis.timeline .vispanel.bottom {
border-left-style: solid;
border-right-style: solid;
}
.vis.timeline .background {
overflow: hidden;
}
.vis.timeline .vispanel > .content {
position: relative;
}
.vis.timeline .vispanel .shadow {
position: absolute;
width: 100%;
height: 1px;
box-shadow: 0 0 10px rgba(0,0,0,0.8);
/* TODO: find a nice way to ensure shadows are drawn on top of items
z-index: 1;
*/
}
.vis.timeline .vispanel .shadow.top {
top: -1px;
left: 0;
}
.vis.timeline .vispanel .shadow.bottom {
bottom: -1px;
left: 0;
}
.vis.timeline .labelset {
position: relative;
overflow: hidden;
box-sizing: border-box;
}
.vis.timeline .labelset .vlabel {
position: relative;
left: 0;
top: 0;
width: 100%;
color: #4d4d4d;
box-sizing: border-box;
}
.vis.timeline .labelset .vlabel {
border-bottom: 1px solid #bfbfbf;
}
.vis.timeline .labelset .vlabel:last-child {
border-bottom: none;
}
.vis.timeline .labelset .vlabel .inner {
display: inline-block;
padding: 5px;
}
.vis.timeline .labelset .vlabel .inner.hidden {
padding: 0;
}
.vis.timeline .itemset {
position: relative;
padding: 0;
margin: 0;
box-sizing: border-box;
}
.vis.timeline .itemset .background,
.vis.timeline .itemset .foreground {
position: absolute;
width: 100%;
height: 100%;
overflow: visible;
}
.vis.timeline .axis {
position: absolute;
width: 100%;
height: 0;
left: 0;
z-index: 1;
}
.vis.timeline .foreground .group {
position: relative;
box-sizing: border-box;
border-bottom: 1px solid #bfbfbf;
}
.vis.timeline .foreground .group:last-child {
border-bottom: none;
}
.vis.timeline .item {
position: absolute;
color: #1A1A1A;
border-color: #97B0F8;
border-width: 1px;
background-color: #D5DDF6;
display: inline-block;
padding: 5px;
}
.vis.timeline .item.selected {
border-color: #FFC200;
background-color: #FFF785;
/* z-index must be higher than the z-index of custom time bar and current time bar */
z-index: 2;
}
.vis.timeline .editable .item.selected {
cursor: move;
}
.vis.timeline .item.point.selected {
background-color: #FFF785;
}
.vis.timeline .item.box {
text-align: center;
border-style: solid;
border-radius: 2px;
}
.vis.timeline .item.point {
background: none;
}
.vis.timeline .item.dot {
position: absolute;
padding: 0;
border-width: 4px;
border-style: solid;
border-radius: 4px;
}
.vis.timeline .item.range {
border-style: solid;
border-radius: 2px;
box-sizing: border-box;
}
.vis.timeline .item.background {
overflow: hidden;
border: none;
background-color: rgba(213, 221, 246, 0.4);
box-sizing: border-box;
padding: 0;
margin: 0;
}
.vis.timeline .item.range .content {
position: relative;
display: inline-block;
max-width: 100%;
overflow: hidden;
}
.vis.timeline .item.background .content {
position: absolute;
display: inline-block;
overflow: hidden;
max-width: 100%;
margin: 5px;
}
.vis.timeline .item.line {
padding: 0;
position: absolute;
width: 0;
border-left-width: 1px;
border-left-style: solid;
}
.vis.timeline .item .content {
white-space: nowrap;
overflow: hidden;
}
.vis.timeline .item .delete {
background: url('img/timeline/delete.png') no-repeat top center;
position: absolute;
width: 24px;
height: 24px;
top: 0;
right: -24px;
cursor: pointer;
}
.vis.timeline .item.range .drag-left {
position: absolute;
width: 24px;
height: 100%;
top: 0;
left: -4px;
cursor: w-resize;
}
.vis.timeline .item.range .drag-right {
position: absolute;
width: 24px;
height: 100%;
top: 0;
right: -4px;
cursor: e-resize;
}
.vis.timeline .timeaxis {
position: relative;
overflow: hidden;
}
.vis.timeline .timeaxis.foreground {
top: 0;
left: 0;
width: 100%;
}
.vis.timeline .timeaxis.background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.vis.timeline .timeaxis .text {
position: absolute;
color: #4d4d4d;
padding: 3px;
white-space: nowrap;
}
.vis.timeline .timeaxis .text.measure {
position: absolute;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
visibility: hidden;
}
.vis.timeline .timeaxis .grid.vertical {
position: absolute;
width: 0;
border-right: 1px solid;
}
.vis.timeline .timeaxis .grid.minor {
border-color: #e5e5e5;
}
.vis.timeline .timeaxis .grid.major {
border-color: #bfbfbf;
}
.vis.timeline .currenttime {
background-color: #FF7F6E;
width: 2px;
z-index: 1;
}
.vis.timeline .customtime {
background-color: #6E94FF;
width: 2px;
cursor: move;
z-index: 1;
}
.vis.timeline.root {
/*
-webkit-transition: height .4s ease-in-out;
transition: height .4s ease-in-out;
*/
}
.vis.timeline .vispanel {
/*
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
transition: height .4s ease-in-out, top .4s ease-in-out;
*/
}
.vis.timeline .axis {
/*
-webkit-transition: top .4s ease-in-out;
transition: top .4s ease-in-out;
*/
}
/* TODO: get animation working nicely
.vis.timeline .item {
-webkit-transition: top .4s ease-in-out;
transition: top .4s ease-in-out;
}
.vis.timeline .item.line {
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
transition: height .4s ease-in-out, top .4s ease-in-out;
}
/**/
.vis.timeline .vispanel.background.horizontal .grid.horizontal {
position: absolute;
width: 100%;
height: 0;
border-bottom: 1px solid;
}
.vis.timeline .vispanel.background.horizontal .grid.minor {
border-color: #e5e5e5;
}
.vis.timeline .vispanel.background.horizontal .grid.major {
border-color: #bfbfbf;
}
.vis.timeline .dataaxis .yAxis.major {
width: 100%;
position: absolute;
color: #4d4d4d;
white-space: nowrap;
}
.vis.timeline .dataaxis .yAxis.major.measure{
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
border: 0px;
visibility: hidden;
width: auto;
}
.vis.timeline .dataaxis .yAxis.minor{
position: absolute;
width: 100%;
color: #bebebe;
white-space: nowrap;
}
.vis.timeline .dataaxis .yAxis.minor.measure{
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
border: 0px;
visibility: hidden;
width: auto;
}
.vis.timeline .dataaxis .yAxis.title{
position: absolute;
color: #4d4d4d;
white-space: nowrap;
bottom: 20px;
text-align: center;
}
.vis.timeline .dataaxis .yAxis.title.measure{
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
visibility: hidden;
width: auto;
}
.vis.timeline .dataaxis .yAxis.title.left {
bottom: 0px;
-webkit-transform-origin: left top;
-moz-transform-origin: left top;
-ms-transform-origin: left top;
-o-transform-origin: left top;
transform-origin: left bottom;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.vis.timeline .dataaxis .yAxis.title.right {
bottom: 0px;
-webkit-transform-origin: right bottom;
-moz-transform-origin: right bottom;
-ms-transform-origin: right bottom;
-o-transform-origin: right bottom;
transform-origin: right bottom;
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
-o-transform: rotate(90deg);
transform: rotate(90deg);
}
.vis.timeline .legend {
background-color: rgba(247, 252, 255, 0.65);
padding: 5px;
border-color: #b3b3b3;
border-style:solid;
border-width: 1px;
box-shadow: 2px 2px 10px rgba(154, 154, 154, 0.55);
}
.vis.timeline .legendText {
/*font-size: 10px;*/
white-space: nowrap;
display: inline-block
}
.vis.timeline .graphGroup0 {
fill:#4f81bd;
fill-opacity:0;
stroke-width:2px;
stroke: #4f81bd;
}
.vis.timeline .graphGroup1 {
fill:#f79646;
fill-opacity:0;
stroke-width:2px;
stroke: #f79646;
}
.vis.timeline .graphGroup2 {
fill: #8c51cf;
fill-opacity:0;
stroke-width:2px;
stroke: #8c51cf;
}
.vis.timeline .graphGroup3 {
fill: #75c841;
fill-opacity:0;
stroke-width:2px;
stroke: #75c841;
}
.vis.timeline .graphGroup4 {
fill: #ff0100;
fill-opacity:0;
stroke-width:2px;
stroke: #ff0100;
}
.vis.timeline .graphGroup5 {
fill: #37d8e6;
fill-opacity:0;
stroke-width:2px;
stroke: #37d8e6;
}
.vis.timeline .graphGroup6 {
fill: #042662;
fill-opacity:0;
stroke-width:2px;
stroke: #042662;
}
.vis.timeline .graphGroup7 {
fill:#00ff26;
fill-opacity:0;
stroke-width:2px;
stroke: #00ff26;
}
.vis.timeline .graphGroup8 {
fill:#ff00ff;
fill-opacity:0;
stroke-width:2px;
stroke: #ff00ff;
}
.vis.timeline .graphGroup9 {
fill: #8f3938;
fill-opacity:0;
stroke-width:2px;
stroke: #8f3938;
}
.vis.timeline .fill {
fill-opacity:0.1;
stroke: none;
}
.vis.timeline .bar {
fill-opacity:0.5;
stroke-width:1px;
}
.vis.timeline .point {
stroke-width:2px;
fill-opacity:1.0;
}
.vis.timeline .legendBackground {
stroke-width:1px;
fill-opacity:0.9;
fill: #ffffff;
stroke: #c2c2c2;
}
.vis.timeline .outline {
stroke-width:1px;
fill-opacity:1;
fill: #ffffff;
stroke: #e5e5e5;
}
.vis.timeline .iconFill {
fill-opacity:0.3;
stroke: none;
}
div.network-manipulationDiv {
border-width: 0;
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 */
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 30px;
}
div.network-manipulation-editMode {
position:absolute;
left: 0;
top: 0;
height: 30px;
margin-top:20px;
}
div.network-manipulation-closeDiv {
position:absolute;
right: 0;
top: 0;
width: 30px;
height: 30px;
background-position: 20px 3px;
background-repeat: no-repeat;
background-image: url("img/network/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;
}
div.network-manipulation-closeDiv:hover {
opacity: 0.6;
}
span.network-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.network-manipulationUI:hover {
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.20);
}
span.network-manipulationUI:active {
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.50);
}
span.network-manipulationUI.back {
background-image: url("img/network/backIcon.png");
}
span.network-manipulationUI.none:hover {
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0);
cursor: default;
}
span.network-manipulationUI.none:active {
box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0);
}
span.network-manipulationUI.none {
padding: 0;
}
span.network-manipulationUI.notification{
margin: 2px;
font-weight: bold;
}
span.network-manipulationUI.add {
background-image: url("img/network/addNodeIcon.png");
}
span.network-manipulationUI.edit {
background-image: url("img/network/editIcon.png");
}
span.network-manipulationUI.edit.editmode {
background-color: #fcfcfc;
border-style:solid;
border-width:1px;
border-color: #cccccc;
}
span.network-manipulationUI.connect {
background-image: url("img/network/connectIcon.png");
}
span.network-manipulationUI.delete {
background-image: url("img/network/deleteIcon.png");
}
/* top right bottom left */
span.network-manipulationLabel {
margin: 0px 0px 0px 23px;
line-height: 25px;
}
div.network-seperatorLine {
display:inline-block;
width:1px;
height:20px;
background-color: #bdbdbd;
margin: 5px 7px 0px 15px;
}
div.network-navigation_wrapper {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
div.network-navigation {
width:34px;
height:34px;
-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.network-navigation:hover {
box-shadow: 0px 0px 3px 3px rgba(56, 207, 21, 0.30);
}
div.network-navigation:active {
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
}
div.network-navigation.up {
background-image: url("img/network/upArrow.png");
bottom:50px;
left:55px;
}
div.network-navigation.down {
background-image: url("img/network/downArrow.png");
bottom:10px;
left:55px;
}
div.network-navigation.left {
background-image: url("img/network/leftArrow.png");
bottom:10px;
left:15px;
}
div.network-navigation.right {
background-image: url("img/network/rightArrow.png");
bottom:10px;
left:95px;
}
div.network-navigation.zoomIn {
background-image: url("img/network/plus.png");
bottom:10px;
right:15px;
}
div.network-navigation.zoomOut {
background-image: url("img/network/minus.png");
bottom:10px;
right:55px;
}
div.network-navigation.zoomExtends {
background-image: url("img/network/zoomExtends.png");
bottom:50px;
right:15px;
}

+ 0
- 34028
www2/dist/vis.js
File diff suppressed because it is too large
View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save