Browse Source

Merge branch 'develop' of https://github.com/almende/vis into develop

revert-3409-performance
Yotam Berkowitz 7 years ago
parent
commit
91c2c31bbc
36 changed files with 6430 additions and 203 deletions
  1. +10
    -0
      .github/ISSUE_TEMPLATE.md
  2. +11
    -0
      .github/PULL_REQUEST_TEMPLATE.md
  3. +7
    -1
      docs/graph2d/index.html
  4. +4
    -4
      docs/graph3d/index.html
  5. +7
    -0
      docs/timeline/index.html
  6. +51
    -0
      examples/graph2d/21_barsWithEnd.html
  7. +1894
    -0
      examples/network/layout/demo.jsonp
  8. +3762
    -0
      examples/network/layout/demo_big.jsonp
  9. +46
    -0
      examples/network/layout/hierarchicalLayoutBigUserDefined.html
  10. +32
    -6
      examples/timeline/dataHandling/dataSerialization.html
  11. +2
    -2
      examples/timeline/groups/subgroups.html
  12. +16
    -1
      examples/timeline/interaction/eventListeners.html
  13. +3
    -0
      examples/timeline/interaction/navigationMenu.html
  14. +9
    -9
      examples/timeline/items/expectedVsActualTimesItems.html
  15. +4
    -4
      examples/timeline/items/visibleFrameTemplateContent.html
  16. +16
    -16
      examples/timeline/resources/data/wk2014.json
  17. +16
    -16
      examples/timeline/styling/itemTemplates.html
  18. +31
    -6
      lib/graph3d/Graph3d.js
  19. +39
    -21
      lib/network/modules/LayoutEngine.js
  20. +5
    -5
      lib/timeline/Range.js
  21. +16
    -36
      lib/timeline/TimeStep.js
  22. +16
    -0
      lib/timeline/Timeline.js
  23. +7
    -5
      lib/timeline/component/ItemSet.js
  24. +9
    -2
      lib/timeline/component/LineGraph.js
  25. +17
    -1
      lib/timeline/component/graph2d_types/bar.js
  26. +1
    -4
      lib/timeline/component/item/BoxItem.js
  27. +38
    -28
      lib/timeline/component/item/Item.js
  28. +1
    -5
      lib/timeline/component/item/PointItem.js
  29. +1
    -4
      lib/timeline/component/item/RangeItem.js
  30. +16
    -0
      lib/timeline/locales.js
  31. +0
    -17
      lib/util.js
  32. +1
    -0
      misc/we_need_help.md
  33. +1
    -0
      package.json
  34. +212
    -9
      test/PointItem.test.js
  35. +21
    -1
      test/TestSupport.js
  36. +108
    -0
      test/TimelineItemSet.test.js

+ 10
- 0
.github/ISSUE_TEMPLATE.md View File

@ -0,0 +1,10 @@
Please make sure to **read the following list** before creating a new issue:
* This issue tracker is not supposed to be used for questions on how to use visjs. It is intended to be used for bug reports and feature requests! In case you face yourself with a usage question, then post your question e.g. on [stackoverflow](https://stackoverflow.com/questions/tagged/vis.js) tagged with "vis.js".
* Have you already used the [github search](https://github.com/almende/vis/issues), read the [documentation](http://visjs.org/) and looked at the [examples](https://github.com/almende/vis/tree/develop/examples)?
* Make sure to mention which vis-component (network, timeline, graph2D, graph3d) you are referring to.
* Make sure to use the [latest version of vis.js](https://cdnjs.com/libraries/vis) for bug reports.
* Make sure to mention which browser and OS you are using when creating a bug report.
* Please provide a minimal code example that demonstrates your issue. We recommend using [jsbin](jsbin.com) for that.
* Delete this list from the actual issue.

+ 11
- 0
.github/PULL_REQUEST_TEMPLATE.md View File

@ -0,0 +1,11 @@
**Thank you for contributing to vis.js!!**
Please make sure to check the following requirements before creating a pull request:
* [ ] All pull requests must be to the [develop branch](https://github.com/almende/vis/tree/develop). Pull requests to the `master` branch will be closed!
* [ ] Make sure your changes are based on the latest version of the [develop branch](https://github.com/almende/vis/tree/develop). (Use e.g. `git fetch && git rebase origin develop` to update you feature branch).
* [ ] Provide an additional or update an example to demonstrate your changes or new features.
* [ ] Update the documentation if you introduced new behavior or changed existing behavior.
* [ ] Reference issue numbers of issues that your pull request addresses. (If you write something like `fixes #1781` in your git commit message this issue gets closed automatically by merging your pull request).
* [ ] Expect review comments and change requests by reviewer.
* [ ] Delete this checklist from your pull request.

+ 7
- 1
docs/graph2d/index.html View File

@ -297,7 +297,13 @@ var items = [
<td>label</td>
<td>Object</td>
<td>no</td>
<td>A label object which will be displayed near to the item. A label object has one requirement - a <b> content </b> property. In addition you can set the <b> xOffset, yOffset and className </b> for further appearance customisations </td>
<td>A label object which will be displayed near to the item. A label object has one requirement - a <b> content </b> property. In addition you can set the <b> xOffset, yOffset and className </b> for further appearance customisations.</td>
</tr>
<tr>
<td>end</td>
<td>Date</td>
<td>no</td>
<td>A location on the x-axis that when supplied will have the bar stretch to the end point and ignore the barChart.width property.</td>
</tr>
</table>

+ 4
- 4
docs/graph3d/index.html View File

@ -532,8 +532,8 @@ var options = {
<td>xBarWidth</td>
<td>number</td>
<td>none</td>
<td>The width of bars in x direction. By default, the width is equal to the distance
between the data points, such that bars adjoin each other.
<td>The width of bars in x direction. By default, the width is equal to the smallest distance
between the data points.
Only applicable for styles <code>'bar'</code> and <code>'bar-color'</code>.</td>
</tr>
@ -575,8 +575,8 @@ var options = {
<td>yBarWidth</td>
<td>number</td>
<td>none</td>
<td>The width of bars in y direction. By default, the width is equal to the distance
between the data points, such that bars adjoin each other.
<td>The width of bars in y direction. By default, the width is equal to the smallest distance
between the data points.
Only applicable for styles <code>'bar'</code> and <code>'bar-color'</code>.</td>
</tr>

+ 7
- 0
docs/timeline/index.html View File

@ -1431,6 +1431,13 @@ document.getElementById('myTimeline').onclick = function (event) {
</td>
</tr>
<tr>
<td>toggleRollingMode()</td>
<td>none</td>
<td>Toggle rollingMode.
</td>
</tr>
<tr>
<td>zoomIn(percentage)</td>
<td>none</td>

+ 51
- 0
examples/graph2d/21_barsWithEnd.html View File

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Bar Graph Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2d | Bar Graph With End Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows how you can plot a bar chart and supply an end value to have it fill.
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-11 00:00:00', end: '2014-06-13 00:00:00', y: 10 },
{x: '2014-06-13 00:00:00', end: '2014-06-14 00:00:00', y: 15 },
{x: '2014-06-15 00:00:00', end: '2014-06-16 00:00:00', y: 14 },
{x: '2014-06-16 00:00:00', end: '2014-06-17 00:00:00', y: 17 },
{x: '2014-06-17 00:00:00', end: '2014-06-18 00:00:00', y: 20 },
{x: '2014-06-18 00:00:00', end: '2014-06-25 00:00:00', y: 25 },
];
var dataset = new vis.DataSet(items);
var options = {
style:'bar',
drawPoints: false,
dataAxis: {
icons:true
},
orientation:'top',
start: '2014-06-10',
end: '2014-06-18'
};
var graph2d = new vis.Graph2d(container, items, options);
</script>
</body>
</html>

+ 1894
- 0
examples/network/layout/demo.jsonp
File diff suppressed because it is too large
View File


+ 3762
- 0
examples/network/layout/demo_big.jsonp
File diff suppressed because it is too large
View File


+ 46
- 0
examples/network/layout/hierarchicalLayoutBigUserDefined.html View File

@ -0,0 +1,46 @@
<!doctype html>
<html>
<head>
<title>Network | Hierarchical layout</title>
<style type="text/css">
body {
font: 10pt sans;
}
#mynetwork {
width: 600px;
height: 600px;
border: 1px solid lightgray;
}
</style>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis-network.min.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript">
var network = null;
function p(data) {
var container = document.getElementById('mynetwork');
var options = {
layout: {
hierarchical: {},
},
};
console.log("starting layout");
network = new vis.Network(container, data, options);
console.log("layout complete");
}
</script>
</head>
<body>
<h2>Hierarchical Layout</h2>
<div id="mynetwork"></div>
<script type="text/javascript" src="./demo.jsonp"></script>
</body>
</html>

+ 32
- 6
examples/timeline/dataHandling/dataSerialization.html View File

@ -35,12 +35,38 @@
<textarea id="data">
[
{"id": 1, "content": "item 1", "start": "2014-01-01 01:00:00"},
{"id": 2, "content": "item 2", "start": "2014-01-01 02:00:00"},
{"id": 3, "content": "item 3", "start": "2014-01-01 03:00:00"},
{"id": 4, "content": "item 4", "start": "2014-01-01 04:00:00", "end": "2014-01-01 04:30:00"},
{"id": 5, "content": "item 5", "start": "2014-01-01 05:00:00", "type": "point"},
{"id": 6, "content": "item 6", "start": "2014-01-01 06:00:00"}
{
"id": 1,
"content": "item 1",
"start": "2014-01-01T01:00:00"
},
{
"id": 2,
"content": "item 2",
"start": "2014-01-01T02:00:00"
},
{
"id": 3,
"content": "item 3",
"start": "2014-01-01T03:00:00"
},
{
"id": 4,
"content": "item 4",
"start": "2014-01-01T04:00:00",
"end": "2014-01-01T04:30:00"
},
{
"id": 5,
"content": "item 5",
"start": "2014-01-01T05:00:00",
"type": "point"
},
{
"id": 6,
"content": "item 6",
"start": "2014-01-01T06:00:00"
}
]
</textarea>

+ 2
- 2
examples/timeline/groups/subgroups.html View File

@ -51,7 +51,7 @@
{id: 'SG_1_1',start: '2014-01-25', end: '2014-01-27', type: 'background', group:'bar', subgroup:'sg_1', subgroupOrder:0},
{id: 'SG_1_2', start: '2014-01-26', end: '2014-01-27', type: 'background', className: 'positive',group:'bar', subgroup:'sg_1', subgroupOrder:0},
{id: 1, content: 'subgroup0', start: '2014-01-23 12:00:00', end: '2014-01-26 12:00:00',group:'bar', subgroup:'sg_1', subgroupOrder:0},
{id: 1, content: 'subgroup0', start: '2014-01-23T12:00:00', end: '2014-01-26T12:00:00',group:'bar', subgroup:'sg_1', subgroupOrder:0},
{id: 'SG_2_1', start: '2014-01-27', end: '2014-01-29', type: 'background', group:'bar', subgroup:'sg_2', subgroupOrder:1},
{id: 'SG_2_2', start: '2014-01-27', end: '2014-01-28', type: 'background', className: 'negative',group:'bar', subgroup:'sg_2', subgroupOrder:1},
{id: 2, content: 'subgroup1', start: '2014-01-27', end: '2014-01-29',group:'bar', subgroup:'sg_2', subgroupOrder:1},
@ -78,4 +78,4 @@
}
</script>
</body>
</html>
</html>

+ 16
- 1
examples/timeline/interaction/eventListeners.html View File

@ -41,9 +41,11 @@
timeline.on('rangechange', function (properties) {
logEvent('rangechange', properties);
});
timeline.on('rangechanged', function (properties) {
logEvent('rangechanged', properties);
});
timeline.on('select', function (properties) {
logEvent('select', properties);
});
@ -52,6 +54,7 @@
logEvent('itemover', properties);
setHoveredItem(properties.item);
});
timeline.on('itemout', function (properties) {
logEvent('itemout', properties);
setHoveredItem('none');
@ -83,11 +86,23 @@
logEvent(event, properties);
});
function stringifyObject (object) {
if (!object) return;
var replacer = function(key, value) {
if (value && value.tagName) {
return "DOM Element";
} else {
return value;
}
}
return JSON.stringify(object, replacer)
}
function logEvent(event, properties) {
var log = document.getElementById('log');
var msg = document.createElement('div');
msg.innerHTML = 'event=' + JSON.stringify(event) + ', ' +
'properties=' + JSON.stringify(properties);
'properties=' + stringifyObject(properties);
log.firstChild ? log.insertBefore(msg, log.firstChild) : log.appendChild(msg);
}

+ 3
- 0
examples/timeline/interaction/navigationMenu.html View File

@ -38,6 +38,7 @@
<input type="button" id="zoomOut" value="Zoom out"/>
<input type="button" id="moveLeft" value="Move left"/>
<input type="button" id="moveRight" value="Move right"/>
<input type="button" id="toggleRollingMode" value="toggleRollingMode"/>
</div>
</div>
@ -74,6 +75,8 @@
document.getElementById('zoomOut').onclick = function () { timeline.zoomOut( 0.2); };
document.getElementById('moveLeft').onclick = function () { move( 0.2); };
document.getElementById('moveRight').onclick = function () { move(-0.2); };
document.getElementById('toggleRollingMode').onclick = function () { timeline.toggleRollingMode() };
</script>
</body>

+ 9
- 9
examples/timeline/items/expectedVsActualTimesItems.html View File

@ -61,16 +61,16 @@
id: 1,
content: 'item 1 (expected time)',
className: 'expected',
start: '2014-01-23 12:00:00',
end: '2014-01-26 12:00:00',
start: '2014-01-23T12:00:00',
end: '2014-01-26T12:00:00',
group:'group1',
subgroup:'sg_1'
},
{
id: 2,
content: 'item 1 (actual time)',
start: '2014-01-24 12:00:00',
end: '2014-01-27 12:00:00',
start: '2014-01-24T12:00:00',
end: '2014-01-27T12:00:00',
group:'group1',
subgroup:'sg_1'
},
@ -80,16 +80,16 @@
id: 3,
content: 'item 2 (expected time)',
className: 'expected',
start: '2014-01-13 12:00:00',
end: '2014-01-16 12:00:00',
start: '2014-01-13T12:00:00',
end: '2014-01-16T12:00:00',
group:'group1',
subgroup:'sg_2'
},
{
id: 4,
content: 'item 2 (actual time)',
start: '2014-01-14 12:00:00',
end: '2014-01-17 12:00:00',
start: '2014-01-14T12:00:00',
end: '2014-01-17T12:00:00',
group:'group1',
subgroup:'sg_2'
}
@ -108,4 +108,4 @@
</script>
</body>
</html>
</html>

+ 4
- 4
examples/timeline/items/visibleFrameTemplateContent.html View File

@ -45,18 +45,18 @@
{id: 1, value: 0.2, content: 'item 1', start: '2014-04-20', end: '2014-04-26'},
{id: 2, value: 0.6, content: 'item 2', start: '2014-05-14', end: '2014-05-18'},
{id: 3, type: 'point', content: 'item 3', start: '2014-04-15', end: '2014-05-18'},
{id: 4, content: 'item 4 with visibleFramTemplate in item', start: '2014-04-16', end: '2014-04-26', visibleFramTemplate: '<div class="progress-wrapper"><div class="progress"></div><label class="progress-label">80%<label></div>'
{id: 4, content: 'item 4 with visibleFrameTemplate in item', start: '2014-04-16', end: '2014-04-26', visibleFrameTemplate: '<div class="progress-wrapper"><div class="progress" style="width:80%"></div><label class="progress-label">80 per cent<label></div>'
}
]);
// Configuration for the Timeline
var options = {
visibleFrameTemplate: function(item) {
if (item.visibleFramTemplate) {
return item.visibleFramTemplate;
if (item.visibleFrameTemplate) {
return item.visibleFrameTemplate;
}
var percentage = item.value * 100 + '%';
return '<div class="progress-wrapper"><div class="progress"></div><label class="progress-label">' + percentage + '<label></div>';
return '<div class="progress-wrapper"><div class="progress" style="width:' + percentage + '"></div><label class="progress-label">' + percentage + '<label></div>';
}
};

+ 16
- 16
examples/timeline/resources/data/wk2014.json View File

@ -7,7 +7,7 @@
"abbr2": "cl",
"score2": "1 (2)",
"description": "round of 16",
"start": "2014-06-28 13:00"
"start": "2014-06-28T13:00"
},
{
"player1": "Colombia",
@ -17,7 +17,7 @@
"abbr2": "uy",
"score2": 0,
"description": "round of 16",
"start": "2014-06-28 17:00"
"start": "2014-06-28T17:00"
},
{
"player1": "Netherlands",
@ -27,7 +27,7 @@
"abbr2": "mx",
"score2": 1,
"description": "round of 16",
"start": "2014-06-29 13:00"
"start": "2014-06-29T13:00"
},
{
"player1": "Costa Rica",
@ -37,7 +37,7 @@
"abbr2": "gr",
"score2": "1 (3)",
"description": "round of 16",
"start": "2014-06-29 17:00"
"start": "2014-06-29T17:00"
},
{
"player1": "France",
@ -47,7 +47,7 @@
"abbr2": "ng",
"score2": 0,
"description": "round of 16",
"start": "2014-06-30 13:00"
"start": "2014-06-30T13:00"
},
{
"player1": "Germany",
@ -57,7 +57,7 @@
"abbr2": "dz",
"score2": 1,
"description": "round of 16",
"start": "2014-06-30 17:00"
"start": "2014-06-30T17:00"
},
{
"player1": "Argentina",
@ -67,7 +67,7 @@
"abbr2": "ch",
"score2": 0,
"description": "round of 16",
"start": "2014-07-01 13:00"
"start": "2014-07-01T13:00"
},
{
"player1": "Belgium",
@ -77,7 +77,7 @@
"abbr2": "us",
"score2": 1,
"description": "round of 16",
"start": "2014-07-01 17:00"
"start": "2014-07-01T17:00"
},
{
"player1": "France",
@ -87,7 +87,7 @@
"abbr2": "de",
"score2": 1,
"description": "quarter-finals",
"start": "2014-07-04 13:00"
"start": "2014-07-04T13:00"
},
{
"player1": "Brazil",
@ -97,7 +97,7 @@
"abbr2": "co",
"score2": 1,
"description": "quarter-finals",
"start": "2014-07-04 17:00"
"start": "2014-07-04T17:00"
},
{
"player1": "Argentina",
@ -107,7 +107,7 @@
"abbr2": "be",
"score2": 0,
"description": "quarter-finals",
"start": "2014-07-05 13:00"
"start": "2014-07-05T13:00"
},
{
"player1": "Netherlands",
@ -117,7 +117,7 @@
"abbr2": "cr",
"score2": "0 (3)",
"description": "quarter-finals",
"start": "2014-07-05 17:00"
"start": "2014-07-05T17:00"
},
{
"player1": "Brazil",
@ -127,7 +127,7 @@
"abbr2": "de",
"score2": 7,
"description": "semi-finals",
"start": "2014-07-08 17:00"
"start": "2014-07-08T17:00"
},
{
"player1": "Netherlands",
@ -137,7 +137,7 @@
"abbr2": "ar",
"score2": "0 (4)",
"description": "semi-finals",
"start": "2014-07-09 17:00"
"start": "2014-07-09T17:00"
},
{
"player1": "Germany",
@ -147,6 +147,6 @@
"abbr2": "ar",
"score2": 0,
"description": "final",
"start": "2014-07-13 16:00"
"start": "2014-07-13T16:00"
}
]
]

+ 16
- 16
examples/timeline/styling/itemTemplates.html View File

@ -88,7 +88,7 @@
abbr2: 'cl',
score2: '1 (2)',
description: 'round of 16',
start: '2014-06-28 13:00'
start: '2014-06-28T13:00:00'
},
{
player1: 'Colombia',
@ -98,7 +98,7 @@
abbr2: 'uy',
score2: 0,
description: 'round of 16',
start: '2014-06-28 17:00'
start: '2014-06-28T17:00:00'
},
{
player1: 'Netherlands',
@ -108,7 +108,7 @@
abbr2: 'mx',
score2: 1,
description: 'round of 16',
start: '2014-06-29 13:00'
start: '2014-06-29T13:00:00'
},
{
player1: 'Costa Rica',
@ -118,7 +118,7 @@
abbr2: 'gr',
score2: '1 (3)',
description: 'round of 16',
start: '2014-06-29 17:00'
start: '2014-06-29T17:00:00'
},
{
player1: 'France',
@ -128,7 +128,7 @@
abbr2: 'ng',
score2: 0,
description: 'round of 16',
start: '2014-06-30 13:00'
start: '2014-06-30T13:00:00'
},
{
player1: 'Germany',
@ -138,7 +138,7 @@
abbr2: 'dz',
score2: 1,
description: 'round of 16',
start: '2014-06-30 17:00'
start: '2014-06-30T17:00:00'
},
{
player1: 'Argentina',
@ -148,7 +148,7 @@
abbr2: 'ch',
score2: 0,
description: 'round of 16',
start: '2014-07-01 13:00'
start: '2014-07-01T13:00:00'
},
{
player1: 'Belgium',
@ -158,7 +158,7 @@
abbr2: 'us',
score2: 1,
description: 'round of 16',
start: '2014-07-01 17:00'
start: '2014-07-01T17:00:00'
},
// quarter-finals
@ -170,7 +170,7 @@
abbr2: 'de',
score2: 1,
description: 'quarter-finals',
start: '2014-07-04 13:00'
start: '2014-07-04T13:00:00'
},
{
player1: 'Brazil',
@ -180,7 +180,7 @@
abbr2: 'co',
score2: 1,
description: 'quarter-finals',
start: '2014-07-04 17:00'
start: '2014-07-04T17:00:00'
},
{
player1: 'Argentina',
@ -190,7 +190,7 @@
abbr2: 'be',
score2: 0,
description: 'quarter-finals',
start: '2014-07-05 13:00'
start: '2014-07-05T13:00:00'
},
{
player1: 'Netherlands',
@ -200,7 +200,7 @@
abbr2: 'cr',
score2: '0 (3)',
description: 'quarter-finals',
start: '2014-07-05 17:00'
start: '2014-07-05T17:00:00'
},
// semi-finals
@ -212,7 +212,7 @@
abbr2: 'de',
score2: 7,
description: 'semi-finals',
start: '2014-07-08 17:00'
start: '2014-07-08T17:00:00'
},
{
player1: 'Netherlands',
@ -222,7 +222,7 @@
abbr2: 'ar',
score2: '0 (4)',
description: 'semi-finals',
start: '2014-07-09 17:00'
start: '2014-07-09T17:00:00'
},
// final
@ -234,7 +234,7 @@
abbr2: 'ar',
score2: 0,
description: 'final',
start: '2014-07-13 16:00'
start: '2014-07-13T16:00:00'
}
]);
@ -248,4 +248,4 @@
var timeline = new vis.Timeline(container, items, options);
</script>
</body>
</html>
</html>

+ 31
- 6
lib/graph3d/Graph3d.js View File

@ -327,7 +327,34 @@ Graph3d.prototype.getDistinctValues = function(data, column) {
distinctValues.push(data[i][column]);
}
}
return distinctValues;
return distinctValues.sort(function(a,b) { return a - b; });
}
/**
* Determine the smallest difference between the values for given
* column in the passed data set.
*
* @returns {Number|null} Smallest difference value or
* null, if it can't be determined.
*/
Graph3d.prototype.getSmallestDifference = function(data, column) {
var values = this.getDistinctValues(data, column);
var diffs = [];
// Get all the distinct diffs
// Array values is assumed to be sorted here
var smallest_diff = null;
for (var i = 1; i < values.length; i++) {
var diff = values[i] - values[i - 1];
if (smallest_diff == null || smallest_diff > diff ) {
smallest_diff = diff;
}
}
return smallest_diff;
}
@ -467,16 +494,14 @@ Graph3d.prototype._dataInitialize = function (rawData, style) {
this.xBarWidth = this.defaultXBarWidth;
}
else {
var dataX = this.getDistinctValues(data,this.colX);
this.xBarWidth = (dataX[1] - dataX[0]) || 1;
this.xBarWidth = this.getSmallestDifference(data, this.colX) || 1;
}
if (this.defaultYBarWidth !== undefined) {
this.yBarWidth = this.defaultYBarWidth;
}
else {
var dataY = this.getDistinctValues(data,this.colY);
this.yBarWidth = (dataY[1] - dataY[0]) || 1;
this.yBarWidth = this.getSmallestDifference(data, this.colY) || 1;
}
}
@ -2449,4 +2474,4 @@ Graph3d.prototype.setSize = function(width, height) {
// -----------------------------------------------------------------------------
module.exports = Graph3d;
module.exports = Graph3d;

+ 39
- 21
lib/network/modules/LayoutEngine.js View File

@ -429,6 +429,9 @@ class LayoutEngine {
// get a map of all nodes in this branch
let getBranchNodes = (source, map) => {
if (map[source.id]) {
return;
}
map[source.id] = true;
if (this.hierarchicalChildrenReference[source.id]) {
let children = this.hierarchicalChildrenReference[source.id];
@ -471,16 +474,24 @@ class LayoutEngine {
// get the maximum level of a branch.
let getMaxLevel = (nodeId) => {
let level = this.hierarchicalLevels[nodeId];
if (this.hierarchicalChildrenReference[nodeId]) {
let children = this.hierarchicalChildrenReference[nodeId];
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
level = Math.max(level,getMaxLevel(children[i]));
let accumulator = {};
let _getMaxLevel = (nodeId) => {
if (accumulator[nodeId] !== undefined) {
return accumulator[nodeId];
}
let level = this.hierarchicalLevels[nodeId];
if (this.hierarchicalChildrenReference[nodeId]) {
let children = this.hierarchicalChildrenReference[nodeId];
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
level = Math.max(level,_getMaxLevel(children[i]));
}
}
}
}
return level;
accumulator[nodeId] = level;
return level;
};
return _getMaxLevel(nodeId);
};
// check what the maximum level is these nodes have in common.
@ -532,8 +543,8 @@ class LayoutEngine {
let diffAbs = Math.abs(pos2 - pos1);
//console.log("NOW CHEcKING:", node1.id, node2.id, diffAbs);
if (diffAbs > this.options.hierarchical.nodeSpacing) {
let branchNodes1 = {}; branchNodes1[node1.id] = true;
let branchNodes2 = {}; branchNodes2[node2.id] = true;
let branchNodes1 = {};
let branchNodes2 = {};
getBranchNodes(node1, branchNodes1);
getBranchNodes(node2, branchNodes2);
@ -639,7 +650,6 @@ class LayoutEngine {
// check movable area of the branch
if (branches[node.id] === undefined) {
let branchNodes = {};
branchNodes[node.id] = true;
getBranchNodes(node, branchNodes);
branches[node.id] = branchNodes;
}
@ -1238,17 +1248,25 @@ class LayoutEngine {
* @private
*/
_shiftBlock(parentId, diff) {
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
this.body.nodes[parentId].x += diff;
}
else {
this.body.nodes[parentId].y += diff;
}
if (this.hierarchicalChildrenReference[parentId] !== undefined) {
for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
this._shiftBlock(this.hierarchicalChildrenReference[parentId][i], diff);
let progress = {};
let shifter = (parentId) => {
if (progress[parentId]) {
return;
}
}
progress[parentId] = true;
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
this.body.nodes[parentId].x += diff;
}
else {
this.body.nodes[parentId].y += diff;
}
if (this.hierarchicalChildrenReference[parentId] !== undefined) {
for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
shifter(this.hierarchicalChildrenReference[parentId][i]);
}
}
};
shifter(parentId);
}

+ 5
- 5
lib/timeline/Range.js View File

@ -218,7 +218,7 @@ Range.prototype.setRange = function(start, end, animation, byUser, event) {
start: new Date(me.start),
end: new Date(me.end),
byUser:byUser,
event: util.elementsCensor(event)
event: event
}
if (changed) {
@ -248,7 +248,7 @@ Range.prototype.setRange = function(start, end, animation, byUser, event) {
start: new Date(this.start),
end: new Date(this.end),
byUser:byUser,
event: util.elementsCensor(event)
event: event
};
this.body.emitter.emit('rangechange', params);
this.body.emitter.emit('rangechanged', params);
@ -532,7 +532,7 @@ Range.prototype._onDrag = function (event) {
start: startDate,
end: endDate,
byUser: true,
event: util.elementsCensor(event)
event: event
});
// fire a panmove event
@ -565,7 +565,7 @@ Range.prototype._onDragEnd = function (event) {
start: new Date(this.start),
end: new Date(this.end),
byUser: true,
event: util.elementsCensor(event)
event: event
});
};
@ -843,7 +843,7 @@ Range.prototype.moveTo = function(moveTo) {
var newStart = this.start - diff;
var newEnd = this.end - diff;
this.setRange(newStart, newEnd, false, true, event);
this.setRange(newStart, newEnd, false, true, null);
};
module.exports = Range;

+ 16
- 36
lib/timeline/TimeStep.js View File

@ -193,46 +193,26 @@ TimeStep.prototype.next = function() {
// Two cases, needed to prevent issues with switching daylight savings
// (end of March and end of October)
if (this.current.month() < 6) {
switch (this.scale) {
case 'millisecond': this.current.add(this.step, 'millisecond'); break;
case 'second': this.current.add(this.step, 'second'); break;
case 'minute': this.current.add(this.step, 'minute'); break;
case 'hour':
this.current.add(this.step, 'hour');
// in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
// TODO: is this still needed now we use the function of moment.js?
this.current.subtract(this.current.hours() % this.step, 'hour');
break;
case 'weekday': // intentional fall through
case 'day': this.current.add(this.step, 'day'); break;
case 'month': this.current.add(this.step, 'month'); break;
case 'year': this.current.add(this.step, 'year'); break;
default: break;
}
}
else {
switch (this.scale) {
case 'millisecond': this.current.add(this.step, 'millisecond'); break;
case 'second': this.current.add(this.step, 'second'); break;
case 'minute': this.current.add(this.step, 'minute'); break;
case 'hour':
this.current.add(this.moment.duration(this.step, 'hour'));
switch (this.scale) {
case 'millisecond': this.current.add(this.step, 'millisecond'); break;
case 'second': this.current.add(this.step, 'second'); break;
case 'minute': this.current.add(this.step, 'minute'); break;
case 'hour':
this.current.add(this.step, 'hour');
// correct for daylight saving
// FIXME: use this.current.add(moment.duration(this.step, 'hour'))
// see http://momentjs.com/docs/#special-considerations-for-months-and-years
if (this.current.month() < 6) {
this.current.subtract(this.current.hours() % this.step, 'hour');
} else {
if (this.current.hours() % this.step !== 0) {
this.current.add(this.step - this.current.hours() % this.step, 'hour');
}
break;
case 'weekday': // intentional fall through
case 'day': this.current.add(this.step, 'day'); break;
case 'month': this.current.add(this.step, 'month'); break;
case 'year': this.current.add(this.step, 'year'); break;
default: break;
}
}
break;
case 'weekday': // intentional fall through
case 'day': this.current.add(this.step, 'day'); break;
case 'month': this.current.add(this.step, 'month'); break;
case 'year': this.current.add(this.step, 'year'); break;
default: break;
}
if (this.step != 1) {

+ 16
- 0
lib/timeline/Timeline.js View File

@ -572,4 +572,20 @@ Timeline.prototype.getEventProperties = function (event) {
}
};
/**
* Toggle Timeline rolling mode
*/
Timeline.prototype.toggleRollingMode = function () {
if (this.range.rolling) {
this.range.stopRolling();
} else {
if (this.options.rollingMode == undefined) {
this.setOptions(this.options)
}
this.range.startRolling();
}
}
module.exports = Timeline;

+ 7
- 5
lib/timeline/component/ItemSet.js View File

@ -616,8 +616,10 @@ ItemSet.prototype.redraw = function() {
// TODO: would be nicer to get this as a trigger from Range
var visibleInterval = range.end - range.start;
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth);
if (zoomed) this.stackDirty = true;
var scrolled = range.start != this.lastRangeStart;
if (zoomed || scrolled) this.stackDirty = true;
this.lastVisibleInterval = visibleInterval;
this.lastRangeStart = range.start;
this.props.lastWidth = this.props.width;
var restack = this.stackDirty;
@ -1874,7 +1876,7 @@ ItemSet.prototype._onSelectItem = function (event) {
if (newSelection.length > 0 || oldSelection.length > 0) {
this.body.emitter.emit('select', {
items: newSelection,
event: util.elementsCensor(event)
event: event
});
}
};
@ -1919,7 +1921,7 @@ ItemSet.prototype._onMouseOver = function (event) {
this.body.emitter.emit('itemover', {
item: item.id,
event: util.elementsCensor(event)
event: event
});
};
ItemSet.prototype._onMouseOut = function (event) {
@ -1939,7 +1941,7 @@ ItemSet.prototype._onMouseOut = function (event) {
this.body.emitter.emit('itemout', {
item: item.id,
event: util.elementsCensor(event)
event: event
});
};
ItemSet.prototype._onMouseMove = function (event) {
@ -2143,7 +2145,7 @@ ItemSet.prototype._onMultiSelectItem = function (event) {
this.body.emitter.emit('select', {
items: this.getSelection(),
event: util.elementsCensor(event)
event: event
});
}
};

+ 9
- 2
lib/timeline/component/LineGraph.js View File

@ -487,6 +487,7 @@ LineGraph.prototype._updateAllGroupData = function (ids, groupIds) {
//Copy data (because of unmodifiable DataView input.
var extended = util.bridgeObject(item);
extended.x = util.convert(item.x, 'Date');
extended.end = util.convert(item.end, 'Date');
extended.orginalY = item.y; //real Y
extended.y = Number(item.y);
extended[fieldId] = item[fieldId];
@ -908,10 +909,10 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
if (options.stack === true && options.style === 'bar') {
if (options.yAxisOrientation === 'left') {
combinedDataLeft = combinedDataLeft.concat(group.getItems());
combinedDataLeft = combinedDataLeft.concat(groupData);
}
else {
combinedDataRight = combinedDataRight.concat(group.getItems());
combinedDataRight = combinedDataRight.concat(groupData);
}
}
else {
@ -1064,6 +1065,12 @@ LineGraph.prototype._convertXcoordinates = function (datapoints) {
for (var i = 0; i < datapoints.length; i++) {
datapoints[i].screen_x = toScreen(datapoints[i].x) + this.props.width;
datapoints[i].screen_y = datapoints[i].y; //starting point for range calculations
if (datapoints[i].end != undefined) {
datapoints[i].screen_end = toScreen(datapoints[i].end) + this.props.width;
}
else {
datapoints[i].screen_end = undefined;
}
}
};

+ 17
- 1
lib/timeline/component/graph2d_types/bar.js View File

@ -62,8 +62,10 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
for (j = 0; j < processedGroupData[groupIds[i]].length; j++) {
combinedData.push({
screen_x: processedGroupData[groupIds[i]][j].screen_x,
screen_end: processedGroupData[groupIds[i]][j].screen_end,
screen_y: processedGroupData[groupIds[i]][j].screen_y,
x: processedGroupData[groupIds[i]][j].x,
end: processedGroupData[groupIds[i]][j].end,
y: processedGroupData[groupIds[i]][j].y,
groupId: groupIds[i],
label: processedGroupData[groupIds[i]][j].label
@ -128,7 +130,21 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
drawData.offset += (intersections[key].resolved) * drawData.width - (0.5 * drawData.width * (intersections[key].amount + 1));
}
}
DOMutil.drawBar(combinedData[i].screen_x + drawData.offset, combinedData[i].screen_y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
let dataWidth = drawData.width;
let start = combinedData[i].screen_x;
// are we drawing explicit boxes? (we supplied an end value)
if (combinedData[i].screen_end != undefined){
dataWidth = combinedData[i].screen_end - combinedData[i].screen_x;
start += (dataWidth * 0.5);
}
else {
start += drawData.offset
}
DOMutil.drawBar(start, combinedData[i].screen_y - heightOffset, dataWidth, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
// draw points
if (group.options.drawPoints.enabled === true) {
let pointData = {

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

@ -121,10 +121,7 @@ BoxItem.prototype.redraw = function() {
this._updateDataAttributes(this.dom.box);
this._updateStyle(this.dom.box);
var editable = (this.options.editable.updateTime ||
this.options.editable.updateGroup ||
this.editable === true) &&
this.editable !== false;
var editable = (this.editable.updateTime || this.editable.updateGroup);
// update class
var className = (this.data.className? ' ' + this.data.className : '') +

+ 38
- 28
lib/timeline/component/item/Item.js View File

@ -31,19 +31,7 @@ function Item (data, conversion, options) {
this.height = null;
this.editable = null;
if (this.data && this.data.hasOwnProperty('editable')){
if(typeof this.data.editable === 'boolean') {
this.editable = {
updateTime: this.data.editable,
updateGroup: this.data.editable,
remove: this.data.editable
}
}
else if(typeof options.editable === 'object') {
this.editable = {};
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, data.editable);
};
}
this._updateEditStatus();
}
Item.prototype.stack = true;
@ -77,21 +65,8 @@ Item.prototype.setData = function(data) {
this.parent.itemSet._moveToGroup(this, data.group);
}
if (data.hasOwnProperty('editable')){
if (typeof data.editable === 'boolean') {
this.editable = {
updateTime: this.data.editable,
updateGroup: this.data.editable,
remove: this.data.editable
}
}
else if(typeof this.options.editable === 'object') {
this.editable = {};
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, data.editable);
}
}
this.data = data;
this._updateEditStatus();
this.dirty = true;
if (this.displayed) this.redraw();
};
@ -367,7 +342,7 @@ Item.prototype._updateContents = function (element) {
if (this.options.template) {
templateFunction = this.options.template.bind(this);
content = templateFunction(itemData, element);
content = templateFunction(itemData, element, this.data);
} else {
content = this.data.content;
}
@ -461,6 +436,41 @@ Item.prototype._contentToString = function (content) {
return content;
};
/**
* Update the editability of this item.
*/
Item.prototype._updateEditStatus = function() {
if (this.options) {
if(typeof this.options.editable === 'boolean') {
this.editable = {
updateTime: this.options.editable,
updateGroup: this.options.editable,
remove: this.options.editable
};
} else if(typeof this.options.editable === 'object') {
this.editable = {};
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.options.editable);
};
}
// Item data overrides, except if options.editable.overrideItems is set.
if (!this.options || !(this.options.editable) || (this.options.editable.overrideItems !== true)) {
if (this.data) {
if (typeof this.data.editable === 'boolean') {
this.editable = {
updateTime: this.data.editable,
updateGroup: this.data.editable,
remove: this.data.editable
}
} else if (typeof this.data.editable === 'object') {
// TODO: in vis.js 5.0, we should change this to not reset options from the timeline configuration.
// Basically just remove the next line...
this.editable = {};
util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.data.editable);
}
}
}
};
/**
* Return the width of the item left from its start date
* @return {number}

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

@ -99,11 +99,7 @@ PointItem.prototype.redraw = function() {
this._updateDataAttributes(this.dom.point);
this._updateStyle(this.dom.point);
var editable = (this.options.editable.updateTime ||
this.options.editable.updateGroup ||
this.editable === true) &&
this.editable !== false;
var editable = (this.editable.updateTime || this.editable.updateGroup);
// update class
var className = (this.data.className ? ' ' + this.data.className : '') +
(this.selected ? ' vis-selected' : '') +

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

@ -103,10 +103,7 @@ RangeItem.prototype.redraw = function() {
this._updateDataAttributes(this.dom.box);
this._updateStyle(this.dom.box);
var editable = (this.options.editable.updateTime ||
this.options.editable.updateGroup ||
this.editable === true) &&
this.editable !== false;
var editable = (this.editable.updateTime || this.editable.updateGroup);
// update class
var className = (this.data.className ? (' ' + this.data.className) : '') +

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

@ -28,3 +28,19 @@ exports['de'] = {
time: 'Zeit'
};
exports['de_DE'] = exports['de'];
// French
exports['fr'] = {
current: 'actuel',
time: 'heure'
};
exports['fr_FR'] = exports['fr'];
exports['fr_CA'] = exports['fr'];
exports['fr_BE'] = exports['fr'];
// Espanol
exports['es'] = {
current: 'corriente',
time: 'hora'
};
exports['es_ES'] = exports['es'];

+ 0
- 17
lib/util.js View File

@ -16,23 +16,6 @@ exports.isNumber = function (object) {
return (object instanceof Number || typeof object == 'number');
};
/**
* Censors object elements containing dom elements
* @param {*} object
* @return {Object} object without elements
*/
exports.elementsCensor = function (object) {
if (!object) return;
var replacer = function(key, value) {
if (value instanceof Element) {
return "DOM Element";
} else {
return value;
}
}
return JSON.parse(JSON.stringify(object, replacer))
}
/**
* Remove everything in the DOM object

+ 1
- 0
misc/we_need_help.md View File

@ -15,3 +15,4 @@ If you have shown some commitment to the project you can ask [@ludost](//github.
* [@yotamberk](//github.com/yotamberk)
* [@Tooa](//github.com/Tooa)
* [@eymiha](//github.com/eymiha)
* [@bradh](//github.com/bradh)

+ 1
- 0
package.json View File

@ -56,6 +56,7 @@
"gulp-rename": "^1.2.2",
"gulp-util": "^3.0.8",
"jsdom": "9.9.1",
"jsdom-global": "^2.1.1",
"mocha": "^3.2.0",
"mocha-jsdom": "^1.1.0",
"rimraf": "^2.5.4",

+ 212
- 9
test/PointItem.test.js View File

@ -10,16 +10,15 @@ var TestSupport = require('./TestSupport');
describe('Timeline PointItem', function () {
jsdom();
var now = moment();
it('should initialize with minimal data', function() {
var now = moment().toDate();
var pointItem = new PointItem({start: now}, null, null);
var pointItem = new PointItem({start: now.toDate()}, null, null);
assert.equal(pointItem.props.content.height, 0);
assert.equal(pointItem.data.start, now);
assert.deepEqual(pointItem.data.start, now.toDate());
});
it('should have a default width of 0', function() {
var now = moment().toDate();
var pointItem = new PointItem({start: now}, null, null);
assert.equal(pointItem.getWidthRight(), 0);
assert.equal(pointItem.getWidthLeft(), 0);
@ -30,7 +29,6 @@ describe('Timeline PointItem', function () {
});
it('should be visible if the range is during', function() {
var now = moment();
var range = new Range(TestSupport.buildSimpleTimelineRangeBody());
range.start = now.clone().add(-1, 'second');
range.end = range.start.clone().add(1, 'hour');
@ -39,7 +37,6 @@ describe('Timeline PointItem', function () {
});
it('should not be visible if the range is after', function() {
var now = moment();
var range = new Range(TestSupport.buildSimpleTimelineRangeBody());
range.start = now.clone().add(1, 'second');
range.end = range.start.clone().add(1, 'hour');
@ -58,8 +55,214 @@ describe('Timeline PointItem', function () {
it('should be visible for a "now" point with a default range', function() {
var range = new Range(TestSupport.buildSimpleTimelineRangeBody());
var now = moment().toDate();
var pointItem = new PointItem({start: now}, null, null);
var pointItem = new PointItem({start: now.toDate()}, null, null);
assert(pointItem.isVisible(range));
});
});
it('should redraw() and then not be dirty', function() {
var pointItem = new PointItem({start: now.toDate()}, null, {editable: false});
pointItem.setParent(TestSupport.buildMockItemSet());
assert(pointItem.dirty);
pointItem.redraw();
assert(!pointItem.dirty);
});
it('should redraw() and then have point attached to its parent', function() {
var pointItem = new PointItem({start: now.toDate()}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
assert(!parent.dom.foreground.hasChildNodes());
pointItem.redraw();
assert(parent.dom.foreground.hasChildNodes());
});
it('should redraw() and then have the correct classname for a non-editable item', function() {
var pointItem = new PointItem({start: now.toDate(), editable: false}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.dom.dot.className, "vis-item vis-dot vis-readonly");
assert.equal(pointItem.dom.point.className, "vis-item vis-point vis-readonly");
});
it('should redraw() and then have the correct classname for an editable item (with object option)', function() {
var pointItem = new PointItem({start: now.toDate()}, null, {editable: {updateTime: true, updateGroup: false}});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.dom.dot.className, "vis-item vis-dot vis-editable");
assert.equal(pointItem.dom.point.className, "vis-item vis-point vis-editable");
});
it('should redraw() and then have the correct classname for an editable item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate()}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.dom.dot.className, "vis-item vis-dot vis-editable");
assert.equal(pointItem.dom.point.className, "vis-item vis-point vis-editable");
});
it('should redraw() and then have the correct classname for an editable:false override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: false}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.dom.dot.className, "vis-item vis-dot vis-readonly");
assert.equal(pointItem.dom.point.className, "vis-item vis-point vis-readonly");
});
it('should redraw() and then have the correct classname for an editable:true override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: true}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.dom.dot.className, "vis-item vis-dot vis-editable");
assert.equal(pointItem.dom.point.className, "vis-item vis-point vis-editable");
});
it('should redraw() and then have the correct classname for an editable:false override item (with object option)', function() {