Browse Source

Fix template to accept react elements

codeClimate
Yotam Berkowitz 8 years ago
parent
commit
8cb0e74580
47 changed files with 1686 additions and 757 deletions
  1. +1
    -0
      .gitignore
  2. +12
    -19
      CONTRIBUTING.md
  3. +5
    -3
      docs/data/dataset.html
  4. +2
    -1
      docs/graph3d/index.html
  5. +10
    -4
      docs/network/edges.html
  6. +20
    -10
      docs/network/index.html
  7. +26
    -17
      docs/timeline/index.html
  8. +10
    -4
      examples/graph3d/11_tooltips.html
  9. +55
    -0
      examples/network/edgeStyles/arrowTypes.html
  10. +75
    -0
      examples/network/other/clustersOfclusters.html
  11. +24
    -3
      examples/network/other/manipulation.html
  12. +177
    -0
      examples/network/other/saveAndLoad.html
  13. +4
    -0
      examples/timeline/groups/verticalItemsHide.html
  14. +141
    -0
      examples/timeline/other/functionLabelFormats.html
  15. +1
    -0
      examples/timeline/other/localization.html
  16. +1
    -1
      index-timeline-graph2d.js
  17. +36
    -26
      lib/DataSet.js
  18. +46
    -34
      lib/DataView.js
  19. +2
    -2
      lib/graph3d/Filter.js
  20. +732
    -498
      lib/graph3d/Graph3d.js
  21. +2
    -2
      lib/graph3d/Slider.js
  22. +40
    -3
      lib/graph3d/StepNumber.js
  23. +18
    -0
      lib/network/locales.js
  24. +5
    -2
      lib/network/modules/Clustering.js
  25. +4
    -4
      lib/network/modules/EdgesHandler.js
  26. +5
    -5
      lib/network/modules/PhysicsEngine.js
  27. +3
    -2
      lib/network/modules/SelectionHandler.js
  28. +11
    -3
      lib/network/modules/components/edges/BezierEdgeDynamic.js
  29. +13
    -4
      lib/network/modules/components/edges/util/EdgeBase.js
  30. +6
    -12
      lib/network/options.js
  31. +12
    -2
      lib/network/shapes.js
  32. +3
    -4
      lib/timeline/Core.js
  33. +8
    -0
      lib/timeline/TimeStep.js
  34. +2
    -3
      lib/timeline/Timeline.js
  35. +19
    -23
      lib/timeline/component/Group.js
  36. +1
    -1
      lib/timeline/component/item/BackgroundItem.js
  37. +16
    -3
      lib/timeline/component/item/BoxItem.js
  38. +24
    -19
      lib/timeline/component/item/Item.js
  39. +4
    -3
      lib/timeline/component/item/PointItem.js
  40. +2
    -8
      lib/timeline/component/item/RangeItem.js
  41. +6
    -0
      lib/timeline/locales.js
  42. +0
    -2
      lib/timeline/optionsGraph2d.js
  43. +3
    -5
      lib/timeline/optionsTimeline.js
  44. +9
    -18
      lib/util.js
  45. +67
    -0
      misc/how_to_help.md
  46. +15
    -0
      misc/we_need_help.md
  47. +8
    -7
      package.json

+ 1
- 0
.gitignore View File

@ -5,6 +5,7 @@ node_modules
.project
.settings/
npm-debug.log
.directory
# vim temporary files
.*.sw[op]

+ 12
- 19
CONTRIBUTING.md View File

@ -1,21 +1,14 @@
## Contributing
Contributions to the vis.js library are very welcome! We can't do this alone.
You can contribute in different ways: spread the word, report bugs, come up with
ideas and suggestions, and contribute to the code.
If you have any **general question** on how to use the vis.js library in your
project please check out
[stackoverflow](http://stackoverflow.com/questions/tagged/vis.js) for that.
There are a few preferences regarding **code contributions**:
- vis.js follows the node.js code style as described
[here](http://nodeguide.com/style.html).
- When implementing new features, please update the documentation accordingly.
- Send pull requests to the `develop` branch, not the `master` branch.
- Only commit changes done in the source files under `lib`, not to the builds
which are located in the folder `dist`.
Thanks!
[Contributions](//github.com/almende/vis/blob/master/misc/how_to_help.md) to the vis.js library are very welcome! [We can't do this alone](//github.com/almende/vis/blob/master/misc/we_need_help.md).
### Questions
If you have any *general question* on how to use the vis.js library in your own project please check out [stackoverflow](http://stackoverflow.com/questions/tagged/vis.js) for thinks like that. **This is NOT a JavaScript help forum!**
### Bugs, Problems and Feature-Requests
If you really want to open a new issue:
* Please use the [search functionality](//github.com/almende/vis/issues) to make sure that there is not already an issue concerning the same topic.
* Please make sure to **mention which module** of vis.js (network, timeline, graph3d, ...) your are referring to.
* If you think you found a bug please **provide a simple example** (e.g. on [jsbin](jsbin.com)) that demonstrates the problem.
* If you want to propose a feature-request please **describe what you are looking for in detail**, ideally providing a screenshot, drawing or something similar.
* **Close the issue later**, when the issue is no longer needed.

+ 5
- 3
docs/data/dataset.html View File

@ -598,9 +598,11 @@ function (event, properties, senderId) {
<code>update</code>, and <code>remove</code>,
<code>properties</code> is always an object containing a property
<code>items</code>, which contains an array with the ids of the affected
items. The <code>update</code> event has an extra field <code>oldData</code>
containing the original data of the updated items, and a field <code>data</code>
containing the changes: the properties of the items that are being updated.
items. The <code>update</code> and <code>remove</code> events have an extra
field <code>oldData</code> containing the original data of the items in the
dataset before the items were updated or removed. The <code>update</code>
event also contains a field <code>data</code> containing the changes:
the properties of the items that are being updated.
</td>
</tr>
<tr>

+ 2
- 1
docs/graph3d/index.html View File

@ -474,7 +474,8 @@ var options = {
The contents of the tooltip can be customized by providing a callback
function as <code>tooltip</code>. In this case the function is called
with an object containing parameters <code>x</code>,
<code>y</code>, and <code>z</code> argument,
<code>y</code>, <code>z</code>, and <code>data</code>
(the source JS object for the point) as an argument,
and must return a string which may contain HTML.
</td>
</tr>

+ 10
- 4
docs/network/edges.html View File

@ -115,9 +115,9 @@
var options = {
edges:{
arrows: {
to: {enabled: false, scaleFactor:1},
middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1}
to: {enabled: false, scaleFactor:1, type:'arrow'},
middle: {enabled: false, scaleFactor:1, type:'arrow'},
from: {enabled: false, scaleFactor:1, type:'arrow'}
},
arrowStrikethrough: true,
color: {
@ -243,6 +243,12 @@ network.setOptions(options);
<td><code>1</code></td>
<td>The scale factor allows you to change the size of the arrowhead.</td>
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.type</td>
<td>String</td>
<td><code>arrow</code></td>
<td>The type of endpoint. Default is <code>arrow</code>. Also possible is <code>circle</code>.</td>
</tr>
<tr parent="arrows" class="hidden">
<td class="indent">arrows.middle</td>
<td>Object or Boolean</td>
@ -703,4 +709,4 @@ var options: {
<script src="../js/tipuesearch.config.js"></script>
<script src="../js/tipuesearch.js"></script>
<!-- controller -->
<script src="../js/main.js"></script>
<script src="../js/main.js"></script>

+ 20
- 10
docs/network/index.html View File

@ -328,19 +328,23 @@ network.setOptions(options);
<tr><td id="event_locale">locale</td>
<td>String</td>
<td><code>'en'</code></td>
<td>Select the locale. By default, the language is English. If you want to use another language, you
will
need to define your own locale and refer to it here.
<td>Select the locale. By default, the language is English.
</td>
</tr>
<tr><td id="event_locales">locales</td>
<td>Object</td>
<td>defaultLocales</td>
<td>Locales object. By default only <code>'en'</code>, <code>'de'</code>, <code>'es'</code> and <code>'nl'</code> are supported. Take a look
at
the <a href="#locales" data-scroll=""
data-options="{ &quot;easing&quot;: &quot;easeInCubic&quot; }">locales
section below</a> for more explaination on how to customize this.
<td>Locales object. By default
<code>'en'</code>,
<code>'de'</code>,
<code>'es'</code>,
<code>'it'</code>,
<code>'nl'</code>
<code>'pt-br'</code>,
<code>'ru'</code>
are supported. Take a look at the
<a href="#locales" data-scroll="" data-options="{ &quot;easing&quot;: &quot;easeInCubic&quot; }">locales section below</a>
for more explaination on how to customize this.
</td>
</tr>
<tr><td id="event_clickToUse">clickToUse</td>
@ -998,7 +1002,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','setSelection', this);">
<td colspan="2"><span parent="setSelection" class="right-caret" id="method_setSelection"></span> setSelection(
<code>Object selection</code>,
<code>Object selection</code>,
<code>[Object options]</code>)</td>
</tr>
<tr class="hidden" parent="setSelection">
@ -1237,7 +1241,13 @@ var options = {
node
any way you want. This is also the style object that is provided in the processProperties function
for
fine tuning. If undefined, default node options will be used.
fine tuning. If undefined, default node options will be used.<br/><br/>
Default functionality only allows clustering if the cluster will contain 2 or more nodes. To allow clustering of single nodes you can use the <code>allowSingleNodeCluster : true</code> property.
<pre class="prettyprint lang-js">
clusterNodeProperties: {
allowSingleNodeCluster: true
}
</pre>
</td>
</tr>
<tr><td id="event_clusterEdgeProperties">clusterEdgeProperties</td>

+ 26
- 17
docs/timeline/index.html View File

@ -31,13 +31,13 @@
<script language="JavaScript">
smoothScroll.init();
</script>
<!-- Tipue vendor css -->
<link href="../css/tipuesearch.css" rel="stylesheet">
<!-- Tipue vendor css -->
<link href="../css/tipuesearch.css" rel="stylesheet">
<script type="text/javascript" src="../js/toggleTable.js"></script>
</head>
<body onload="prettyPrint();">
@ -213,7 +213,7 @@
or a DataView (offering 1 way data binding).
Items are regular objects and can contain the properties <code>start</code>,
<code>end</code> (optional), <code>content</code>,
<code>group</code> (optional), <code>className</code> (optional),
<code>group</code> (optional), <code>className</code> (optional),
<code>editable</code> (optional), and <code>style</code> (optional).
</p>
@ -557,7 +557,7 @@ function (option, path) {
<tr>
<td>format</td>
<td>Object</td>
<td>Object or Function</td>
<td>none</td>
<td>
Apply custom date formatting of the labels on the time axis. The default value of <code>format</code> is:
@ -583,8 +583,16 @@ function (option, path) {
year: ''
}
}</pre>
For values which not provided in the customized <code>options.format</code>, the default values will be used.
All available formatting syntax is described in the <a href="http://momentjs.com/docs/#/displaying/format/">docs of moment.js</a>.
<br>
You can also use a function format for each label. The function accepts as arguments the date, scale and step in that order, and expects to return a string for the label.
<pre class="prettyprint lang-js">function format({
minorLabels: Function(date: Date, scale: Number, step: Number),
majorLabels: Function(date: Date, scale: Number, step: Number)
}</pre>
</td>
</tr>
@ -624,7 +632,7 @@ function (option, path) {
If no <code>order</code> properties are provided, the order will be undetermined.
</td>
</tr>
<tr>
<td>groupOrderSwap</td>
<td>Function</td>
@ -787,7 +795,7 @@ function (option, path) {
Only applicable when option <code>selectable</code> is <code>true</code>.
</td>
</tr>
<tr>
<td style="font-size: 0.9em">multiselectPerGroup</td>
<td>boolean</td>
@ -805,7 +813,7 @@ function (option, path) {
<td>Callback function triggered when an item is about to be added: when the user double taps an empty space in the Timeline. See section <a href="#Editing_Items">Editing Items</a> for more information. Only applicable when both options <code>selectable</code> and <code>editable.add</code> are set <code><code>true</code></code>.
</td>
</tr>
<tr>
<td>onAddGroup</td>
<td>function</td>
@ -829,7 +837,7 @@ function (option, path) {
<td>Callback function triggered when an item has been moved: after the user has dragged the item to an other position. See section <a href="#Editing_Items">Editing Items</a> for more information. Only applicable when both options <code>selectable</code> and <code>editable.updateTime</code> or <code>editable.updateGroup</code> are set <code><code>true</code></code>.
</td>
</tr>
<tr>
<td>onMoveGroup</td>
<td>function</td>
@ -853,7 +861,7 @@ function (option, path) {
<td>Callback function triggered when an item is about to be removed: when the user tapped the delete button on the top right of a selected item. See section <a href="#Editing_Items">Editing Items</a> for more information. Only applicable when both options <code>selectable</code> and <code>editable.remove</code> are set <code><code>true</code></code>.
</td>
</tr>
<tr>
<td>onRemoveGroup</td>
<td>function</td>
@ -973,13 +981,6 @@ function (option, path) {
<td>A template function used to generate the contents of the items. The function is called by the Timeline with an items data as the first argument and the item element as the second, and must return HTML code as result. When the option template is specified, the items do not need to have a field <code>content</code>. See section <a href="#Templates">Templates</a> for a detailed explanation.</td>
</tr>
<tr>
<td>throttleRedraw</td>
<td>number</td>
<td><code>0</code></td>
<td>Limit the maximum number of redraws to once every x milliseconds. For example setting throttleRedraw to `100` milliseconds will limit the number of redraws to 10 times per second.</td>
</tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','timeAxis', this);">
<td><span parent="timeAxis" class="right-caret"></span> timeAxis</td>
<td>Object</td>
@ -1714,6 +1715,14 @@ var options = {
<code>en_US</code>
</td>
</tr>
<tr>
<td>Italian</td>
<td>
<code>it</code><br>
<code>it_IT</code><br>
<code>it_CH</code>
</td>
</tr>
<tr>
<td>Dutch</td>
<td>

+ 10
- 4
examples/graph3d/11_tooltips.html View File

@ -24,6 +24,11 @@
// Create and populate a data table.
data = new vis.DataSet();
var extra_content = [
'Arbitrary information',
'You can access data from the point source object',
'Tooltip example content',
];
// create some nice looking data with sin/cos
var steps = 5; // number of datapoints will be steps*steps
@ -34,10 +39,10 @@
var z = custom(x,y);
if (withValue) {
var value = (y - x);
data.add({x:x, y:y, z: z, style:value});
data.add({x:x, y:y, z: z, style:value, extra: extra_content[(x*y) % extra_content.length]});
}
else {
data.add({x:x, y:y, z: z});
data.add({x:x, y:y, z: z, extra: extra_content[(x*y) % extra_content.length]});
}
}
}
@ -55,8 +60,9 @@
// Option tooltip can be true, false, or a function returning a string with HTML contents
//tooltip: true,
tooltip: function (point) {
// parameter point contains properties x, y, z
return 'value: <b>' + point.z + '</b>';
// parameter point contains properties x, y, z, and data
// data is the original object passed to the point constructor
return 'value: <b>' + point.z + '</b><br>' + point.data.extra;
},
keepAspectRatio: true,

+ 55
- 0
examples/network/edgeStyles/arrowTypes.html View File

@ -0,0 +1,55 @@
<!doctype html>
<html>
<head>
<title>Network | Basic usage</title>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
#mynetwork {
width: 600px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<p>
There two type of liner endings. The classical "arrow" (default) and "circle".
</p>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'X'},
{id: 2, label: 'Y'},
{id: 3, label: 'Z'}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 2, arrows:'to'},
{from: 2, to: 3, arrows:{
to: {
type: 'circle'
}
}},
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);
</script>
<script src="../../googleAnalytics.js"></script>
</body>
</html>

+ 75
- 0
examples/network/other/clustersOfclusters.html View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cluster Test</title>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css"/>
<style type="text/css">
#network_graph {
width: 1000px;
height: 800px;
border: 1px solid lightgray;
}
</style>
</head>
<body onload="draw()">
<p>
Clusters can contain other clusters, but clusters of a single node is only possible by adding
<pre>allowSingleNodeCluster: true</pre>
to clusterNodeProperties<br/>
In this example repeatedly clicking on the node with open the Clusters.
</p>
<div id="network_graph"></div>
<div id="info"></div>
<script type="text/javascript">
var network;
var node_color = ['orange', 'green', 'red', 'yellow', 'cyan'];
var node_shape = ['star', 'database', 'diamond', 'square', 'triangle'];
var nodes = new vis.DataSet([
{id: 'x', label: 'Node X'},
{id: 'y', label: 'Node Y'},
]);
var network_options = {};
var edges = new vis.DataSet([
{from: 'x', to: 'y'}
]);
var cluster_id = 1;
function draw() {
network = new vis.Network(
document.getElementById('network_graph'),
{
nodes: nodes,
edges: edges
},
network_options
);
network.on('click', function (params) {
if (params.nodes.length == 1) {
if (network.isCluster(params.nodes[0]) == true) {
network.openCluster(params.nodes[0]);
}
}
});
cluster();
cluster();
cluster();
}
function cluster() {
var clusterOptions = {
joinCondition: function (childOptions) {
console.log(childOptions);
return true;
},
clusterNodeProperties: {id: cluster_id, label: "Cluster " + cluster_id, color: node_color[cluster_id - 1], shape: node_shape[cluster_id - 1], allowSingleNodeCluster: true}
};
cluster_id++;
network.cluster(clusterOptions);
}
</script>
</body>
</html>

+ 24
- 3
examples/network/other/manipulation.html View File

@ -1,6 +1,7 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network | Manipulation</title>
<style type="text/css">
@ -64,6 +65,17 @@
var data = getScaleFreeNetwork(25);
var seed = 2;
function setDefaultLocale() {
var defaultLocal = navigator.language;
var select = document.getElementById('locale');
select.selectedIndex = 0; // set fallback value
for (var i = 0, j = select.options.length; i < j; ++i) {
if (select.options[i].getAttribute('value') === defaultLocal) {
select.selectedIndex = i;
break;
}
}
}
function destroy() {
if (network !== null) {
@ -135,11 +147,16 @@
callback(data);
}
function init() {
setDefaultLocale();
draw();
}
</script>
<script src="../../googleAnalytics.js"></script>
</head>
<body onload="draw();">
<body onload="init();">
<h2>Editing the nodes and edges (localized)</h2>
<p style="width: 700px; font-size:14px; text-align: justify;">
The localization is only relevant to the manipulation buttons.
@ -148,8 +165,13 @@
<p>
<label for="locale">Select a locale:</label>
<select id="locale" onchange="draw();">
<option value="en" selected>en</option>
<option value="en">en</option>
<option value="de">de</option>
<option value="es">es</option>
<option value="it">it</option>
<option value="nl">nl</option>
<option value="pt-br">pt</option>
<option value="ru">ru</option>
</select>
</p>
@ -169,4 +191,3 @@
</body>
</html>

+ 177
- 0
examples/network/other/saveAndLoad.html View File

@ -0,0 +1,177 @@
<!doctype html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Network | Saving and loading networks</title>
<style type="text/css">
body {
font: 10pt sans;
}
#network {
float:left;
width: 600px;
height: 600px;
margin:5px;
border: 1px solid lightgray;
}
#config {
float:left;
width: 400px;
height: 600px;
}
#input_output {
height: 10%;
width: 15%;
}
p {
font-size:16px;
max-width:700px;
}
</style>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<p>
In this example, the network data can be exported to JSON and imported back into the network.
Try this out by exporting the network to JSON, clearing the network and then importing it again. The nodes will all appear in the same position as they were before the network was destroyed.
</p>
<div id="network"></div>
<div>
<textarea id=input_output></textarea>
<input type="button" id="import_button" onclick="importNetwork()" value="import"></input>
<input type="button" id="export_button" onclick="exportNetwork()" value="export"></input>
<input type="button" id="destroy_button" onclick="destroyNetwork()" value="destroy"></input>
</div>
<script type="text/javascript">
var network;
var container;
var exportArea;
var importButton;
var exportButton;
function init() {
container = document.getElementById('network');
exportArea = document.getElementById('input_output');
importButton = document.getElementById('import_button');
exportButton = document.getElementById('export_button');
draw();
}
function addContextualInformation(elem, index, array) {
addId(elem, index);
addConnections(elem, index);
}
function addId(elem, index) {
elem.id = index;
}
function addConnections(elem, index) {
// need to replace this with a tree of the network, then get child direct children of the element
elem.connections = network.getConnectedNodes(index);
}
function destroyNetwork() {
network.destroy();
}
function clearOutputArea() {
exportArea.value = "";
}
function draw() {
// create a network of nodes
var data = getScaleFreeNetwork(5);
network = new vis.Network(container, data, {manipulation:{enabled:true}});
clearOutputArea();
}
function exportNetwork() {
clearOutputArea();
var nodes = objectToArray(network.getPositions());
nodes.forEach(addContextualInformation);
// pretty print node data
var exportValue = JSON.stringify(nodes, undefined, 2);
exportArea.value = exportValue;
resizeExportArea();
}
function importNetwork() {
var inputValue = exportArea.value;
var inputData = JSON.parse(inputValue);
var data = {
nodes: getNodeData(inputData),
edges: getEdgeData(inputData)
}
network = new vis.Network(container, data, {});
resizeExportArea();
}
function getNodeData(data) {
var networkNodes = [];
data.forEach(function(elem, index, array) {
networkNodes.push({id: elem.id, label: elem.id, x: elem.x, y: elem.y});
});
return new vis.DataSet(networkNodes);
}
function getEdgeData(data) {
var networkEdges = [];
data.forEach(function(node, index, array) {
// add the connection
node.connections.forEach(function(connId, cIndex, conns) {
networkEdges.push({from: node.id, to: connId});
var elementConnections = array[connId].connections;
// remove the connection from the other node to prevent duplicate connections
var duplicateIndex = elementConnections.findIndex(function(connection) {
connection === node.id;
});
elementConnections = elementConnections.splice(0, duplicateIndex - 1).concat(elementConnections.splice(duplicateIndex + 1, elementConnections.length))
});
});
return new vis.DataSet(networkEdges);
}
function objectToArray(obj) {
return Object.keys(obj).map(function (key) { return obj[key]; });
}
function resizeExportArea() {
exportArea.style.height = (1 + exportArea.scrollHeight) + "px";
}
init();
</script>
</body>
</html>

+ 4
- 0
examples/timeline/groups/verticalItemsHide.html View File

@ -72,6 +72,7 @@
// create items
var items = new vis.DataSet();
var types = [ 'box', 'point', 'range', 'background']
var order = 1;
var truck = 1;
for (var j = 0; j < 25; j++) {
@ -83,11 +84,14 @@
date.setHours(date.getHours() + 2 + Math.floor(Math.random()*4));
var end = new Date(date);
var type = types[Math.floor(4 * Math.random())]
items.add({
id: order,
group: truck,
start: start,
end: end,
type: type,
content: 'Order ' + order
});

+ 141
- 0
examples/timeline/other/functionLabelFormats.html View File

@ -0,0 +1,141 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Custom function label format example</title>
<style>
body, html {
font-family: arial, sans-serif;
font-size: 11pt;
}
#visualization {
box-sizing: border-box;
width: 100%;
height: 300px;
}
</style>
<!-- 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.8.4/moment.min.js"></script>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<p>
This example demonstrate using custom function label formats.
</p>
<div id="visualization"></div>
<script>
var now = moment().minutes(0).seconds(0).milliseconds(0);
var groupCount = 3;
var itemCount = 20;
// create a data set with groups
var names = ['John', 'Alston', 'Lee', 'Grant'];
var groups = new vis.DataSet();
for (var g = 0; g < groupCount; g++) {
groups.add({id: g, content: names[g]});
}
// create a dataset with items
var items = new vis.DataSet();
for (var i = 0; i < itemCount; i++) {
var start = now.clone().add(Math.random() * 200, 'hours');
var group = Math.floor(Math.random() * groupCount);
items.add({
id: i,
group: group,
content: 'item ' + i +
' <span style="color:#97B0F8;">(' + names[group] + ')</span>',
start: start,
type: 'box'
});
}
// create visualization
var container = document.getElementById('visualization');
var options = {
format: {
minorLabels: function(date, scale, step) {
var now = new Date();
var ago = now - date;
var divider;
switch (scale) {
case 'millisecond':
divider = 1;
break;
case 'second':
divider = 1000;
break;
case 'minute':
divider = 1000 * 60;
break;
case 'hour':
divider = 1000 * 60 * 60;
break;
case 'day':
divider = 1000 * 60 * 60 * 24;
break;
case 'weekday':
divider = 1000 * 60 * 60 * 24 * 7;
break;
case 'month':
divider = 1000 * 60 * 60 * 24 * 30;
break;
case 'year':
divider = 1000 * 60 * 60 * 24 * 365;
break;
default:
return new Date(date);
}
return (Math.round(ago * step / divider)) + " " + scale + "s ago"
},
majorLabels: function(date, scale, step) {
var now = new Date();
var ago = now - date;
var divider;
switch (scale) {
case 'millisecond':
divider = 1;
break;
case 'second':
divider = 1000;
break;
case 'minute':
divider = 1000 * 60;
break;
case 'hour':
divider = 1000 * 60 * 60;
break;
case 'day':
divider = 1000 * 60 * 60 * 24;
break;
case 'weekday':
divider = 1000 * 60 * 60 * 24 * 7;
break;
case 'month':
divider = 1000 * 60 * 60 * 24 * 30;
break;
case 'year':
divider = 1000 * 60 * 60 * 24 * 365;
break;
default:
return new Date(date);
}
return (Math.round(ago * step / divider)) + " " + scale + "s ago"
}
}
};
var timeline = new vis.Timeline(container);
timeline.setOptions(options);
timeline.setGroups(groups);
timeline.setItems(items);
</script>
</body>
</html>

+ 1
- 0
examples/timeline/other/localization.html View File

@ -24,6 +24,7 @@
<label for="locale">Select a locale:</label>
<select id="locale">
<option value="en" selected>en</option>
<option value="it">it</option>
<option value="nl">nl</option>
</select>
</p>

+ 1
- 1
index-timeline-graph2d.js View File

@ -44,4 +44,4 @@ exports.timeline = {
// bundled external libraries
exports.moment = require('./lib/module/moment');
exports.Hammer = require('./lib/module/hammer');
exports.keycharm = require('keycharm');
exports.keycharm = require('keycharm');

+ 36
- 26
lib/DataSet.js View File

@ -668,25 +668,26 @@ DataSet.prototype._sort = function (items, order) {
*/
DataSet.prototype.remove = function (id, senderId) {
var removedIds = [],
i, len, removedId;
if (Array.isArray(id)) {
for (i = 0, len = id.length; i < len; i++) {
removedId = this._remove(id[i]);
if (removedId != null) {
removedIds.push(removedId);
removedItems = [],
ids = [],
i, len, itemId, item;
// force everything to be an array for simplicity
ids = Array.isArray(id) ? id : [id];
for (i = 0, len = ids.length; i < len; i++) {
item = this._remove(ids[i]);
if (item) {
itemId = item[this._fieldId];
if (itemId) {
removedIds.push(itemId);
removedItems.push(item);
}
}
}
else {
removedId = this._remove(id);
if (removedId != null) {
removedIds.push(removedId);
}
}
if (removedIds.length) {
this._trigger('remove', {items: removedIds}, senderId);
this._trigger('remove', {items: removedIds, oldData: removedItems}, senderId);
}
return removedIds;
@ -699,20 +700,23 @@ DataSet.prototype.remove = function (id, senderId) {
* @private
*/
DataSet.prototype._remove = function (id) {
var item,
ident;
// confirm the id to use based on the args type
if (util.isNumber(id) || util.isString(id)) {
if (this._data[id]) {
delete this._data[id];
this.length--;
return id;
}
ident = id;
}
else if (id instanceof Object) {
var itemId = id[this._fieldId];
if (itemId !== undefined && this._data[itemId]) {
delete this._data[itemId];
this.length--;
return itemId;
}
ident = id[this._fieldId]; // look for the identifier field using _fieldId
}
// do the remove if the item is found
if (ident !== undefined && this._data[ident]) {
item = this._data[ident];
delete this._data[ident];
this.length--;
return item;
}
return null;
};
@ -723,12 +727,18 @@ DataSet.prototype._remove = function (id) {
* @return {Array} removedIds The ids of all removed items
*/
DataSet.prototype.clear = function (senderId) {
var i, len;
var ids = Object.keys(this._data);
var items = [];
for (i = 0, len = ids.length; i < len; i++) {
items.push(this._data[ids[i]]);
}
this._data = {};
this.length = 0;
this._trigger('remove', {items: ids}, senderId);
this._trigger('remove', {items: ids, oldData: items}, senderId);
return ids;
};

+ 46
- 34
lib/DataView.js View File

@ -35,7 +35,7 @@ function DataView (data, options) {
* @param {DataSet | DataView} data
*/
DataView.prototype.setData = function (data) {
var ids, id, i, len;
var ids, id, i, len, items;
if (this._data) {
// unsubscribe from current dataset
@ -44,10 +44,16 @@ DataView.prototype.setData = function (data) {
}
// trigger a remove of all items in memory
ids = Object.keys(this._ids);
ids = this._data.getIds({filter: this._options && this._options.filter});
items = [];
for (i = 0, len = ids.length; i < len; i++) {
items.push(this._data._data[ids[i]]);
}
this._ids = {};
this.length = 0;
this._trigger('remove', {items: ids});
this._trigger('remove', {items: ids, oldData: items});
}
this._data = data;
@ -80,18 +86,19 @@ DataView.prototype.setData = function (data) {
*/
DataView.prototype.refresh = function () {
var id, i, len;
var ids = this._data.getIds({filter: this._options && this._options.filter});
var oldIds = Object.keys(this._ids);
var newIds = {};
var added = [];
var removed = [];
var ids = this._data.getIds({filter: this._options && this._options.filter}),
oldIds = Object.keys(this._ids),
newIds = {},
addedIds = [],
removedIds = [],
removedItems = [];
// check for additions
for (i = 0, len = ids.length; i < len; i++) {
id = ids[i];
newIds[id] = true;
if (!this._ids[id]) {
added.push(id);
addedIds.push(id);
this._ids[id] = true;
}
}
@ -100,19 +107,20 @@ DataView.prototype.refresh = function () {
for (i = 0, len = oldIds.length; i < len; i++) {
id = oldIds[i];
if (!newIds[id]) {
removed.push(id);
removedIds.push(id);
removedItems.push(this._data[id]);
delete this._ids[id];
}
}
this.length += added.length - removed.length;
this.length += addedIds.length - removedIds.length;
// trigger events
if (added.length) {
if (addedIds.length) {
this._trigger('add', {items: added});
}
if (removed.length) {
this._trigger('remove', {items: removed});
if (removedIds.length) {
this._trigger('remove', {items: removedIds, oldData: removedItems});
}
};
@ -298,13 +306,14 @@ DataView.prototype.getDataSet = function () {
DataView.prototype._onEvent = function (event, params, senderId) {
var i, len, id, item;
var ids = params && params.items;
var data = this._data;
var updatedData = [];
var added = [];
var updated = [];
var removed = [];
if (ids && data) {
var addedIds = [],
updatedIds = [],
removedIds = [],
oldItems = [],
updatedItems = [],
removedItems = [];
if (ids && this._data) {
switch (event) {
case 'add':
// filter the ids of the added items
@ -313,7 +322,7 @@ DataView.prototype._onEvent = function (event, params, senderId) {
item = this.get(id);
if (item) {
this._ids[id] = true;
added.push(id);
addedIds.push(id);
}
}
@ -328,18 +337,20 @@ DataView.prototype._onEvent = function (event, params, senderId) {
if (item) {
if (this._ids[id]) {
updated.push(id);
updatedData.push(params.data[i]);
updatedIds.push(id);
updatedItems.push(params.data[i]);
oldItems.push(params.oldData[i]);
}
else {
this._ids[id] = true;
added.push(id);
addedIds.push(id);
}
}
else {
if (this._ids[id]) {
delete this._ids[id];
removed.push(id);
removedIds.push(id);
removedItems.push(params.oldData[i]);
}
else {
// nothing interesting for me :-(
@ -355,23 +366,24 @@ DataView.prototype._onEvent = function (event, params, senderId) {
id = ids[i];
if (this._ids[id]) {
delete this._ids[id];
removed.push(id);
removedIds.push(id);
removedItems.push(params.oldData[i]);
}
}
break;
}
this.length += added.length - removed.length;
this.length += addedIds.length - removedIds.length;
if (added.length) {
this._trigger('add', {items: added}, senderId);
if (addedIds.length) {
this._trigger('add', {items: addedIds}, senderId);
}
if (updated.length) {
this._trigger('update', {items: updated, data: updatedData}, senderId);
if (updatedIds.length) {
this._trigger('update', {items: updatedIds, oldData: oldItems, data: updatedItems}, senderId);
}
if (removed.length) {
this._trigger('remove', {items: removed}, senderId);
if (removedIds.length) {
this._trigger('remove', {items: removedIds, oldData: removedItems}, senderId);
}
}
};

+ 2
- 2
lib/graph3d/Filter.js View File

@ -111,7 +111,7 @@ Filter.prototype.getValues = function() {
*/
Filter.prototype.getValue = function(index) {
if (index >= this.values.length)
throw 'Error: index out of range';
throw new Error('Index out of range');
return this.values[index];
};
@ -164,7 +164,7 @@ Filter.prototype.setOnLoadCallback = function(callback) {
*/
Filter.prototype.selectValue = function(index) {
if (index >= this.values.length)
throw 'Error: index out of range';
throw new Error('Index out of range');
this.index = index;
this.value = this.values[index];

+ 732
- 498
lib/graph3d/Graph3d.js
File diff suppressed because it is too large
View File


+ 2
- 2
lib/graph3d/Slider.js View File

@ -11,7 +11,7 @@ var util = require('../util');
*/
function Slider(container, options) {
if (container === undefined) {
throw 'Error: No container element defined';
throw new Error('No container element defined');
}
this.container = container;
this.visible = (options && options.visible != undefined) ? options.visible : true;
@ -253,7 +253,7 @@ Slider.prototype.setIndex = function(index) {
this.onChange();
}
else {
throw 'Error: index out of range';
throw new Error('Index out of range');
}
};

+ 40
- 3
lib/graph3d/StepNumber.js View File

@ -35,6 +35,17 @@ function StepNumber(start, end, step, prettyStep) {
this.setRange(start, end, step, prettyStep);
};
/**
* Check for input values, to prevent disasters from happening
*
* Source: http://stackoverflow.com/a/1830844
*/
StepNumber.prototype.isNumeric = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
/**
* Set a new range: start, end and step.
*
@ -45,6 +56,16 @@ function StepNumber(start, end, step, prettyStep) {
* To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
*/
StepNumber.prototype.setRange = function(start, end, step, prettyStep) {
if (!this.isNumeric(start)) {
throw new Error('Parameter \'start\' is not numeric; value: ' + start);
}
if (!this.isNumeric(end)) {
throw new Error('Parameter \'end\' is not numeric; value: ' + start);
}
if (!this.isNumeric(step)) {
throw new Error('Parameter \'step\' is not numeric; value: ' + start);
}
this._start = start ? start : 0;
this._end = end ? end : 0;
@ -115,13 +136,29 @@ StepNumber.prototype.getStep = function () {
};
/**
* Set the current value to the largest value smaller than start, which
* is a multiple of the step size
* Set the current to its starting value.
*
* By default, this will be the largest value smaller than start, which
* is a multiple of the step size.
*
* Parameters checkFirst is optional, default false.
* If set to true, move the current value one step if smaller than start.
*/
StepNumber.prototype.start = function() {
StepNumber.prototype.start = function(checkFirst) {
if (checkFirst === undefined) {
checkFirst = false;
}
this._current = this._start - this._start % this._step;
if (checkFirst) {
if (this.getCurrent() < this._start) {
this.next();
}
}
};
/**
* Do a step, add the step size to the current value
*/

+ 18
- 0
lib/network/locales.js View File

@ -53,6 +53,24 @@ exports['es'] = {
};
exports['es_ES'] = exports['es'];
//Italiano
exports['it'] = {
edit: 'Modifica',
del: 'Cancella la selezione',
back: 'Indietro',
addNode: 'Aggiungi un nodo',
addEdge: 'Aggiungi un vertice',
editNode: 'Modifica il nodo',
editEdge: 'Modifica il vertice',
addDescription: 'Clicca per aggiungere un nuovo nodo',
edgeDescription: 'Clicca su un nodo e trascinalo ad un altro nodo per connetterli.',
editEdgeDescription: 'Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.',
createEdgeError: 'Non si possono collegare vertici ad un cluster',
deleteClusterError: 'I cluster non possono essere cancellati',
editClusterError: 'I clusters non possono essere modificati.'
};
exports['it_IT'] = exports['it'];
// Dutch
exports['nl'] = {
edit: 'Wijzigen',

+ 5
- 2
lib/network/modules/Clustering.js View File

@ -348,8 +348,11 @@ class ClusterEngine {
* @private
*/
_cluster(childNodesObj, childEdgesObj, options, refreshData = true) {
// kill condition: no children so can't cluster or only one node in the cluster, don't bother
if (Object.keys(childNodesObj).length < 2) {return;}
// kill condition: no nodes don't bother
if (Object.keys(childNodesObj).length == 0) {return;}
// allow clusters of 1 if options allow
if (Object.keys(childNodesObj).length == 1 && options.clusterNodeProperties.allowSingleNodeCluster != true) {return;}
// check if this cluster call is not trying to cluster anything that is in another cluster.
for (let nodeId in childNodesObj) {

+ 4
- 4
lib/network/modules/EdgesHandler.js View File

@ -23,9 +23,9 @@ class EdgesHandler {
this.options = {};
this.defaultOptions = {
arrows: {
to: {enabled: false, scaleFactor:1}, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1}
to: {enabled: false, scaleFactor:1, type: 'arrow'}, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
middle: {enabled: false, scaleFactor:1, type: 'arrow'},
from: {enabled: false, scaleFactor:1, type: 'arrow'}
},
arrowStrikethrough: true,
color: {
@ -388,4 +388,4 @@ class EdgesHandler {
}
export default EdgesHandler;
export default EdgesHandler;

+ 5
- 5
lib/network/modules/PhysicsEngine.js View File

@ -628,7 +628,7 @@ class PhysicsEngine {
this._freezeNodes();
}
this.stabilizationIterations = 0;
setTimeout(() => this._stabilizationBatch(),0);
}
@ -649,7 +649,7 @@ class PhysicsEngine {
this.physicsTick();
count++;
}
if (this.stabilized === false && this.stabilizationIterations < this.targetIterations) {
this.body.emitter.emit('stabilizationProgress', {iterations: this.stabilizationIterations, total: this.targetIterations});
setTimeout(this._stabilizationBatch.bind(this),0);
@ -710,11 +710,11 @@ class PhysicsEngine {
let angle = Math.atan2(force.y, force.x);
ctx.fillStyle = color;
ctx.arrow(node.x + factor*force.x + Math.cos(angle)*arrowSize, node.y + factor*force.y+Math.sin(angle)*arrowSize, angle, arrowSize);
ctx.arrowEndpoint(node.x + factor*force.x + Math.cos(angle)*arrowSize, node.y + factor*force.y+Math.sin(angle)*arrowSize, angle, arrowSize);
ctx.fill();
}
}
}
export default PhysicsEngine;
export default PhysicsEngine;

+ 3
- 2
lib/network/modules/SelectionHandler.js View File

@ -534,8 +534,9 @@ class SelectionHandler {
this.hoverObj.edges[edgeId].hover = false;
delete this.hoverObj.edges[edgeId];
}
// if the blur remains the same and the object is undefined (mouse off), we blur the edge
else if (object === undefined) {
// if the blur remains the same and the object is undefined (mouse off) or another
// edge has been hovered, we blur the edge
else if (object === undefined || object instanceof Edge) {
this.blurObject(this.hoverObj.edges[edgeId]);
delete this.hoverObj.edges[edgeId];
hoverChanged = true;

+ 11
- 3
lib/network/modules/components/edges/BezierEdgeDynamic.js View File

@ -133,8 +133,16 @@ class BezierEdgeDynamic extends BezierEdgeBase {
*/
getPoint(percentage, viaNode = this.via) {
let t = percentage;
let x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
let y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
let x, y;
if (this.from === this.to){
let [cx,cy,cr] = this._getCircleData(this.from)
let a = 2 * Math.PI * (1 - t);
x = cx + cr * Math.sin(a);
y = cy + cr - cr * (1 - Math.cos(a));
} else {
x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
}
return {x: x, y: y};
}
@ -151,4 +159,4 @@ class BezierEdgeDynamic extends BezierEdgeBase {
}
export default BezierEdgeDynamic;
export default BezierEdgeDynamic;

+ 13
- 4
lib/network/modules/components/edges/util/EdgeBase.js View File

@ -412,6 +412,7 @@ class EdgeBase {
let node2;
let guideOffset;
let scaleFactor;
let type;
let lineWidth = this.getLineWidth(selected, hover);
if (position === 'from') {
@ -419,17 +420,20 @@ class EdgeBase {
node2 = this.to;
guideOffset = 0.1;
scaleFactor = this.options.arrows.from.scaleFactor;
type = this.options.arrows.from.type;
}
else if (position === 'to') {
node1 = this.to;
node2 = this.from;
guideOffset = -0.1;
scaleFactor = this.options.arrows.to.scaleFactor;
type = this.options.arrows.to.type;
}
else {
node1 = this.to;
node2 = this.from;
scaleFactor = this.options.arrows.middle.scaleFactor;
type = this.options.arrows.middle.type;
}
// if not connected to itself
@ -475,7 +479,7 @@ class EdgeBase {
var yi = arrowPoint.y - length * 0.9 * Math.sin(angle);
let arrowCore = {x: xi, y: yi};
return {point: arrowPoint, core: arrowCore, angle: angle, length: length};
return {point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type};
}
/**
@ -491,8 +495,13 @@ class EdgeBase {
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this.getLineWidth(selected, hover);
// draw arrow at the end of the line
ctx.arrow(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
if (arrowData.type && arrowData.type.toLowerCase() === 'circle') {
// draw circle at the end of the line
ctx.circleEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
} else {
// draw arrow at the end of the line
ctx.arrowEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
}
// draw shadow if enabled
this.enableShadow(ctx);
@ -521,4 +530,4 @@ class EdgeBase {
}
}
export default EdgeBase;
export default EdgeBase;

+ 6
- 12
lib/network/options.js View File

@ -24,9 +24,9 @@ let allOptions = {
},
edges: {
arrows: {
to: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } },
middle: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } },
from: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } },
to: { enabled: { boolean }, scaleFactor: { number }, type: { string: ['arrow', 'circle'] }, __type__: { object, boolean } },
middle: { enabled: { boolean }, scaleFactor: { number }, type: { string: ['arrow', 'circle'] }, __type__: { object, boolean } },
from: { enabled: { boolean }, scaleFactor: { number }, type: { string: ['arrow', 'circle'] }, __type__: { object, boolean } },
__type__: { string: ['from', 'to', 'middle'], object }
},
arrowStrikethrough: { boolean },
@ -371,9 +371,9 @@ let configureOptions = {
},
edges: {
arrows: {
to: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
to: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' },
middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' }
},
arrowStrikethrough: true,
color: {
@ -502,13 +502,7 @@ let configureOptions = {
solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'],
timestep: [0.5, 0.01, 1, 0.01],
//adaptiveTimestep: true
},
global: {
locale: ['en', 'nl']
}
};
export {allOptions, configureOptions};

+ 12
- 2
lib/network/shapes.js View File

@ -206,9 +206,9 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
/**
* Draw an arrow point (no line)
* Draw an arrow at the end of a line with the given angle.
*/
CanvasRenderingContext2D.prototype.arrow = function (x, y, angle, length) {
CanvasRenderingContext2D.prototype.arrowEndpoint = function (x, y, angle, length) {
// tail
var xt = x - length * Math.cos(angle);
var yt = y - length * Math.sin(angle);
@ -233,6 +233,16 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
this.closePath();
};
/**
* Draw an circle an the end of an line with the given angle.
*/
CanvasRenderingContext2D.prototype.circleEndpoint = function (x, y, angle, length) {
var radius = length * 0.4;
var xc = x - radius * Math.cos(angle);
var yc = y - radius * Math.sin(angle);
this.circle(xc, yc, radius);
};
/**
* Sets up the dashedLine functionality for drawing
* Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas

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

@ -222,8 +222,7 @@ Core.prototype.setOptions = function (options) {
var fields = [
'width', 'height', 'minHeight', 'maxHeight', 'autoResize',
'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates',
'locale', 'locales', 'moment', 'rtl',
'throttleRedraw'
'locale', 'locales', 'moment', 'rtl'
];
util.selectiveExtend(fields, this.options, options);
@ -233,7 +232,7 @@ Core.prototype.setOptions = function (options) {
this.dom.leftContainer = this.dom.rightContainer;
this.dom.rightContainer = contentContainer;
this.dom.container.style.direction = "rtl";
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; }
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; }
this.options.orientation = {item:undefined,axis:undefined};
if ('orientation' in options) {
@ -330,7 +329,7 @@ Core.prototype.setOptions = function (options) {
// override redraw with a throttled version
if (!this._origRedraw) {
this._origRedraw = this._redraw.bind(this);
this._redraw = util.throttle(this._origRedraw, this.options.throttleRedraw);
this._redraw = util.throttle(this._origRedraw);
} else {
// Not the initial run: redraw everything
this._redraw();

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

@ -532,6 +532,10 @@ TimeStep.prototype.getLabelMinor = function(date) {
date = this.current;
}
if (typeof(this.format.minorLabels) === "function") {
return this.format.minorLabels(date, this.scale, this.step);
}
var format = this.format.minorLabels[this.scale];
return (format && format.length > 0) ? this.moment(date).format(format) : '';
};
@ -546,6 +550,10 @@ TimeStep.prototype.getLabelMajor = function(date) {
if (date == undefined) {
date = this.current;
}
if (typeof(this.format.majorLabels) === "function") {
return this.format.majorLabels(date, this.scale, this.step);
}
var format = this.format.majorLabels[this.scale];
return (format && format.length > 0) ? this.moment(date).format(format) : '';

+ 2
- 3
lib/timeline/Timeline.js View File

@ -47,7 +47,6 @@ function Timeline (container, items, groups, options) {
end: null,
autoResize: true,
throttleRedraw: 0, // ms
orientation: {
axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
@ -426,7 +425,7 @@ Timeline.prototype.getItemRange = function () {
var start = getStart(item);
var end = getEnd(item);
if (this.options.rtl) {
var startSide = start - (item.getWidthRight() + 10) * factor;
var endSide = end + (item.getWidthLeft() + 10) * factor;
@ -434,7 +433,7 @@ Timeline.prototype.getItemRange = function () {
var startSide = start - (item.getWidthLeft() + 10) * factor;
var endSide = end + (item.getWidthRight() + 10) * factor;
}
if (startSide < min) {
min = startSide;

+ 19
- 23
lib/timeline/component/Group.js View File

@ -87,10 +87,12 @@ Group.prototype._create = function() {
Group.prototype.setData = function(data) {
// update contents
var content;
var templateFunction;
if (this.itemSet.options && this.itemSet.options.groupTemplate) {
content = this.itemSet.options.groupTemplate(data, this.dom.inner);
}
else {
templateFunction = this.itemSet.options.groupTemplate.bind(this);
content = templateFunction(data, this.dom.inner);
} else {
content = data && data.content;
}
@ -100,11 +102,11 @@ Group.prototype.setData = function(data) {
this.dom.inner.removeChild(this.dom.inner.firstChild);
}
this.dom.inner.appendChild(content);
}
else if (content !== undefined && content !== null) {
} else if (content instanceof Object) {
templateFunction(data, this.dom.inner);
} else if (content !== undefined && content !== null) {
this.dom.inner.innerHTML = content;
}
else {
} else {
this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null
}
@ -180,8 +182,16 @@ Group.prototype.redraw = function(range, margin, restack) {
// recalculate the height of the subgroups
this._calculateSubGroupHeights();
this.isVisible = this._isGroupVisible(range, margin);
// calculate actual size and position
var foreground = this.dom.foreground;
this.top = foreground.offsetTop;
this.right = foreground.offsetLeft;
this.width = foreground.offsetWidth;
this.isVisible = this._isGroupVisible(range, margin);
// reposition visible items vertically
if (typeof this.itemSet.options.order === 'function') {
// a custom order function
@ -220,7 +230,7 @@ Group.prototype.redraw = function(range, margin, restack) {
stack.nostack(this.visibleItems, margin, this.subgroups);
}
}
if (!this.isVisible && this.height) {
return resized = false;
}
@ -275,7 +285,6 @@ Group.prototype._calculateSubGroupHeights = function () {
* check if group is visible
* @private
*/
Group.prototype._isGroupVisible = function (range, margin) {
var isVisible =
(this.top <= range.body.domProps.centerContainer.height - range.body.domProps.scrollTop + margin.axis)
@ -556,20 +565,7 @@ Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, ra
// reposition item horizontally
item.repositionX();
}
// debug
//console.log("new line")
//if (this.groupId == null) {
// for (i = 0; i < orderedItems.byStart.length; i++) {
// item = orderedItems.byStart[i].data;
// console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "")
// }
// for (i = 0; i < orderedItems.byEnd.length; i++) {
// item = orderedItems.byEnd[i].data;
// console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "")
// }
//}
return visibleItems;
};

+ 1
- 1
lib/timeline/component/item/BackgroundItem.js View File

@ -47,7 +47,7 @@ BackgroundItem.prototype.stack = false;
*/
BackgroundItem.prototype.isVisible = function(range) {
// determine visibility
return (this.data.start < range.end) && (this.data.end > range.start);
return (this.data.start < range.end) && (this.data.end > range.start);
};
/**

+ 16
- 3
lib/timeline/component/item/BoxItem.js View File

@ -42,9 +42,22 @@ BoxItem.prototype = new Item (null, null, null);
*/
BoxItem.prototype.isVisible = function(range) {
// determine visibility
// TODO: account for the real width of the item. Right now we just add 1/4 to the window
var interval = (range.end - range.start) / 4;
return (this.data.start > range.start - interval) && (this.data.start < range.end + interval);
var isVisible;
var align = this.options.align;
var msPerPixel = (range.end - range.start) / range.body.dom.center.clientWidth;
var widthInMs = this.width * msPerPixel;
if (align == 'right') {
isVisible = (this.data.start.getTime() > range.start ) && (this.data.start.getTime() - widthInMs < range.end);
}
else if (align == 'left') {
isVisible = (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start.getTime() < range.end);
}
else {
// default or 'center'
isVisible = (this.data.start.getTime() + widthInMs/2 > range.start ) && (this.data.start.getTime() - widthInMs/2 < range.end);
}
return isVisible;
};
/**

+ 24
- 19
lib/timeline/component/item/Item.js View File

@ -99,7 +99,6 @@ Item.prototype.setParent = function(parent) {
* @returns {boolean} True if visible
*/
Item.prototype.isVisible = function(range) {
// Should be implemented by Item implementations
return false;
};
@ -188,31 +187,37 @@ Item.prototype._repaintDeleteButton = function (anchor) {
*/
Item.prototype._updateContents = function (element) {
var content;
var templateFunction;
if (this.options.template) {
var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset
content = this.options.template(itemData, element);
}
else {
templateFunction = this.options.template.bind(this);
content = templateFunction(itemData, element);
} else {
content = this.data.content;
}
var changed = this._contentToString(this.content) !== this._contentToString(content);
if (changed) {
// only replace the content when changed
if (content instanceof Element) {
element.innerHTML = '';
element.appendChild(content);
}
else if (content != undefined) {
element.innerHTML = content;
}
else {
if (!(this.data.type == 'background' && this.data.content === undefined)) {
throw new Error('Property "content" missing in item ' + this.id);
if (content instanceof Object) {
templateFunction(itemData, element)
} else {
var changed = this._contentToString(this.content) !== this._contentToString(content);
if (changed) {
// only replace the content when changed
if (content instanceof Element) {
element.innerHTML = '';
element.appendChild(content);
}
else if (content != undefined) {
element.innerHTML = content;
}
else {
if (!(this.data.type == 'background' && this.data.content === undefined)) {
throw new Error('Property "content" missing in item ' + this.id);
}
}
}
this.content = content;
this.content = content;
}
}
};

+ 4
- 3
lib/timeline/component/item/PointItem.js View File

@ -43,9 +43,10 @@ PointItem.prototype = new Item (null, null, null);
*/
PointItem.prototype.isVisible = function(range) {
// determine visibility
// TODO: account for the real width of the item. Right now we just add 1/4 to the window
var interval = (range.end - range.start) / 4;
return (this.data.start > range.start - interval) && (this.data.start < range.end + interval);
var msPerPixel = (range.end - range.start) / range.body.dom.center.clientWidth;
var widthInMs = this.width * msPerPixel;
return (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start < range.end);
};
/**

+ 2
- 8
lib/timeline/component/item/RangeItem.js View File

@ -43,14 +43,8 @@ RangeItem.prototype.baseClassName = 'vis-item vis-range';
*/
RangeItem.prototype.isVisible = function(range) {
// determine visibility
var isVisible =
// determine horizontal visibillity
(this.data.start < range.end) &&
(this.data.end > range.start) &&
// determine vertical visibillity
(this.parent.top < range.body.domProps.centerContainer.height - range.body.domProps.scrollTop) &&
(this.parent.top + this.parent.height > - range.body.domProps.scrollTop)
return isVisible;};
return (this.data.start < range.end) && (this.data.end > range.start);
};
/**
* Repaint the item

+ 6
- 0
lib/timeline/locales.js View File

@ -6,6 +6,12 @@ exports['en'] = {
exports['en_EN'] = exports['en'];
exports['en_US'] = exports['en'];
// Italiano
exports['it'] = {
current: 'attuale',
time: 'tempo'
};
// Dutch
exports['nl'] = {
current: 'huidige',

+ 0
- 2
lib/timeline/optionsGraph2d.js View File

@ -100,7 +100,6 @@ let allOptions = {
},
autoResize: {boolean},
throttleRedraw: {number},
clickToUse: {boolean},
end: {number, date, string, moment},
format: {
@ -225,7 +224,6 @@ let configureOptions = {
},
autoResize: true,
throttleRedraw: [10, 0, 1000, 10],
clickToUse: false,
end: '',
format: {

+ 3
- 5
lib/timeline/optionsTimeline.js View File

@ -27,7 +27,6 @@ let allOptions = {
align: {string},
rtl: {boolean, 'undefined': 'undefined'},
autoResize: {boolean},
throttleRedraw: {number},
clickToUse: {boolean},
dataAttributes: {string, array},
editable: {
@ -48,7 +47,7 @@ let allOptions = {
day: {string,'undefined': 'undefined'},
month: {string,'undefined': 'undefined'},
year: {string,'undefined': 'undefined'},
__type__: {object}
__type__: {object, 'function': 'function'}
},
majorLabels: {
millisecond: {string,'undefined': 'undefined'},
@ -59,7 +58,7 @@ let allOptions = {
day: {string,'undefined': 'undefined'},
month: {string,'undefined': 'undefined'},
year: {string,'undefined': 'undefined'},
__type__: {object}
__type__: {object, 'function': 'function'}
},
__type__: {object}
},
@ -145,7 +144,6 @@ let configureOptions = {
align: ['center', 'left', 'right'],
direction: false,
autoResize: true,
throttleRedraw: [10, 0, 1000, 10],
clickToUse: false,
// dataAttributes: ['all'], // FIXME: can be 'all' or string[]
editable: {
@ -229,4 +227,4 @@ let configureOptions = {
}
};
export {allOptions, configureOptions};
export {allOptions, configureOptions};

+ 9
- 18
lib/util.js View File

@ -702,29 +702,20 @@ exports.updateProperty = function (object, key, value) {
};
/**
* Throttle the given function to be only executed once every `wait` milliseconds
* Throttle the given function to be only executed once per animation frame
* @param {function} fn
* @param {number} wait Time in milliseconds
* @returns {function} Returns the throttled function
*/
exports.throttle = function (fn, wait) {
var timeout = null;
var needExecution = false;
exports.throttle = function (fn) {
var scheduled = false;
return function throttled () {
if (!timeout) {
needExecution = false;
fn();
timeout = setTimeout(function() {
timeout = null;
if (needExecution) {
throttled();
}
}, wait)
}
else {
needExecution = true;
if (!scheduled) {
scheduled = true;
requestAnimationFrame(function () {
scheduled = false;
fn();
});
}
}
};

+ 67
- 0
misc/how_to_help.md View File

@ -0,0 +1,67 @@
# HowTo Help
The company that developed vis.js for the main part, *almende* is [not able to maintain the project at the moment](./we_need_help.md). So help from the community is very needed and welcome!
## There are many ways to help:
### Answering questions
There are new [issues with questions](//github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+label%3Aquestion+sort%3Acreated-desc) how to use vis.js opened almost every day. Be part of the community and help answer them!
A better way to ask questions on how to use vis.js is [stackoverflow](https://stackoverflow.com/tags/vis.js). Questions are posed here also and need to be answered by the community. [Please help answering questions](https://stackoverflow.com/tags/vis.js) here also.
### Closing old issues
A new issue is often opened fast and then forgotten. Please help go trough [the old issues](//github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc) (especially the [questions](//github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc+label%3Aquestion)) and ask the creator of the issues if the problem still exists before closing the issue. The support team uses the **issue inactive** label to mark these issues.
### Improve the webpage
The visjs.org webpage is hosted on the [gh-pages branch](//github.com/almende/vis/tree/gh-pages). If you find a typo or anything else that should be improved feel free to create a pull-request to *gh-pages*. Please make changes in your own fork of gh-pages so the support team can view the changes in your hosted fork.
### Create new examples
We have [a collection of examples](//github.com/almende/vis/tree/develop/examples). Please help by creating interesting new ones that show a specific problem or layout. Keep the examples easy to understand for beginners and remove unnecessary clutter.
### Provide interesting showcases
If you use vis.js to develop something beautiful feel free to create a pull-request to our show cases page in the gh-pages branch](//github.com/almende/vis/tree/gh-pages/showcase). [These showcases are displayed on our webpage](http://visjs.org/showcase/index.html) and we are always looking for new examples.
### Confirming and fixing bugs
Every software has bugs. We also have [quite a nice collection](https://github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc) ;-)
Feel free to fix as many bugs as you want!
You can not only help by fixing bugs, but also by confirming the bug or even creating a minimal code example to prove this bug exists.
### Implementing Feature-Requests
A lot of people have a lot of ideas for improving vis.js. [We label these issues as **enhancement**](https://github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement). Feel free to implement a new feature by creating a new Pull-Request.
[Some issues are labeled **For everybody!**](//github.com/almende/vis/issues?q=is%3Aissue+is%3Aopen+label%3A%22For+everyone%21%22+sort%3Areactions-%2B1-desc). These are a good starting point.
### Reviewing Pull-Requests
We use [GitHub's two-step review](//help.github.com/articles/about-pull-request-reviews/) to make sure pull-requests are clean. You can help by checking out pull-request branches and testing them. You also can comment on lines of code and make sure the pull-request introduces no new bugs or typos.
## Creating Pull Requests
There are some rules for pull-request:
* All pull-request must be to the [develop-branch](//github.com/almende/vis/tree/develop). Pull-request against the [master-branch](//github.com/almende/vis/tree/master) must be closed. (Changes to [gh-pages](//github.com/almende/vis/tree/gh-pages) are also ok.)
* Only commit changes done in the source files in the folder `lib`, not to the builds
which are located in the folder `dist`.
* Keep your changes small and clear. Only work on one topic at one time and only change lines of code that you have to change to reach your goal.
* Test your changes before creating a pull-request. The easiest way is to open the existing examples and playing with them.
* If you are fixing or implementing an existing issue, please refer to it in the description and in the commit message.
* If you are introducing a new feature, add some documentation and a new example to make it easy to adapt.
* If you introduce breaking changes, like changing the signature of a public function, point that out in your description. Breaking changes result in a new major release.
* Always adapt to the code style of the existing source. Never adapt existing code to your personal taste. :trollface:
**Happy Helping!!**

+ 15
- 0
misc/we_need_help.md View File

@ -0,0 +1,15 @@
# We need help!
## The current status
Vis.js is looking for people who can help maintain and improve the library. We've put a lot of effort in building these visualizations, fixing bugs, and supporting users as much as we can. For some time now, we’ve been lacking the manpower to maintain the library the way we have in recent years. [@josdejong](//github.com/josdejong) has left the company for a new opportunity, and [@AlexDM0](//github.com/AlexDM0) has moved internally to a daughter company, with severe impact on his time and availability for Vis.js. At the moment [@ludost](//github.com/ludost) is the official maintainer from Almende, but does not have much time to help out.
Although Almende is looking to replace the expertise required for Vis.js, we don't expect to be able to do comprehensive project management any time soon. At the same time we’d like to spare Vis.js from becoming abandonware, especially given the relative healthy user base. For the longer term future we would be happy if vis.js could stand on its own feet, community supported.
**If you want to support the project please just start by [helping out](./how_to_help.md).**
If you have shown some commitment to the project you can ask [@ludost](//github.com/ludost) to become a member of the community support team. This team has write permissions to the repository and is helping maintaining it. Currently this team consists of:
* [@ludost](//github.com/ludost) (almende maintainer)
* [@mojoaxel](//github.com/mojoaxel)
* [@yotamberk](//github.com/yotamberk)

+ 8
- 7
package.json View File

@ -28,7 +28,13 @@
"watch": "gulp watch",
"watch-dev": "gulp watch --bundle"
},
"dependencies": {},
"dependencies": {
"emitter-component": "^1.1.1",
"moment": "^2.12.0",
"propagating-hammerjs": "^1.4.6",
"hammerjs": "^2.0.6",
"keycharm": "^0.2.0"
},
"devDependencies": {
"async": "^2.0.0-rc.2",
"babel-core": "^6.6.5",
@ -36,18 +42,13 @@
"babel-preset-es2015": "^6.6.0",
"babelify": "^7.2.0",
"clean-css": "^3.4.10",
"emitter-component": "^1.1.1",
"gulp": "^3.9.1",
"gulp-clean-css": "^2.0.11",
"gulp-concat": "^2.6.0",
"gulp-rename": "^1.2.2",
"gulp-util": "^3.0.7",
"hammerjs": "^2.0.6",
"keycharm": "^0.2.0",
"merge-stream": "^1.0.0",
"mocha": "^2.5.3",
"moment": "^2.12.0",
"propagating-hammerjs": "^1.4.6",
"mocha": "^2.4.5",
"rimraf": "^2.5.2",
"uglify-js": "^2.6.2",
"uuid": "^2.0.1",

Loading…
Cancel
Save