Browse Source

Merge branch 'develop' into network_performance_pullrequest

network_performance_pullrequest
Ludo Stellingwerff 7 years ago
committed by GitHub
parent
commit
b7e0b50253
43 changed files with 6725 additions and 299 deletions
  1. +10
    -0
      .github/ISSUE_TEMPLATE.md
  2. +11
    -0
      .github/PULL_REQUEST_TEMPLATE.md
  3. +71
    -0
      HISTORY.md
  4. +7
    -1
      docs/graph2d/index.html
  5. +62
    -18
      docs/graph3d/index.html
  6. +31
    -14
      docs/timeline/index.html
  7. +51
    -0
      examples/graph2d/21_barsWithEnd.html
  8. +15
    -1
      examples/graph3d/11_tooltips.html
  9. +1894
    -0
      examples/network/layout/demo.jsonp
  10. +3762
    -0
      examples/network/layout/demo_big.jsonp
  11. +46
    -0
      examples/network/layout/hierarchicalLayoutBigUserDefined.html
  12. +32
    -6
      examples/timeline/dataHandling/dataSerialization.html
  13. +5
    -5
      examples/timeline/editing/individualEditableItems.html
  14. +7
    -6
      examples/timeline/editing/tooltipOnItemChange.html
  15. +2
    -2
      examples/timeline/groups/subgroups.html
  16. +16
    -1
      examples/timeline/interaction/eventListeners.html
  17. +9
    -9
      examples/timeline/items/expectedVsActualTimesItems.html
  18. +4
    -4
      examples/timeline/items/visibleFrameTemplateContent.html
  19. +3
    -2
      examples/timeline/other/rtl.html
  20. +3
    -2
      examples/timeline/other/verticalScroll.html
  21. +16
    -16
      examples/timeline/resources/data/wk2014.json
  22. +16
    -16
      examples/timeline/styling/itemTemplates.html
  23. +58
    -20
      lib/graph3d/Graph3d.js
  24. +6
    -4
      lib/graph3d/Settings.js
  25. +39
    -21
      lib/network/modules/LayoutEngine.js
  26. +21
    -10
      lib/timeline/Core.js
  27. +4
    -4
      lib/timeline/Range.js
  28. +7
    -1
      lib/timeline/TimeStep.js
  29. +71
    -39
      lib/timeline/component/Group.js
  30. +24
    -14
      lib/timeline/component/ItemSet.js
  31. +9
    -2
      lib/timeline/component/LineGraph.js
  32. +17
    -1
      lib/timeline/component/graph2d_types/bar.js
  33. +2
    -2
      lib/timeline/component/item/BackgroundItem.js
  34. +1
    -4
      lib/timeline/component/item/BoxItem.js
  35. +38
    -28
      lib/timeline/component/item/Item.js
  36. +1
    -5
      lib/timeline/component/item/PointItem.js
  37. +1
    -4
      lib/timeline/component/item/RangeItem.js
  38. +0
    -17
      lib/util.js
  39. +1
    -0
      misc/we_need_help.md
  40. +11
    -10
      package.json
  41. +212
    -9
      test/PointItem.test.js
  42. +21
    -1
      test/TestSupport.js
  43. +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.

+ 71
- 0
HISTORY.md View File

@ -1,6 +1,77 @@
# vis.js history
http://visjs.org
## 2017-03-19, version 4.19.1
### General
* FIX: #2685 Fixed babel dependencies (#2875)
### Timeline / Graph2D
* FIX #2809: Fix docs typo in "showNested" (#2879)
* FIX #2594: Fixes for removing and adding items to subgroups (#2821)
* FIX: Allow nested groups to be removed (#2852)
## 2017-03-18, version 4.19.0
### General
- FIX: Fix eslint problem on Travis. (#2744)
- added support for eslint (#2695)
- Trivial typo fix in how_to_help doc. (#2714)
- add link to a mentioned example (#2709)
- FEAT: use babel preset2015 for custom builds (#2678)
- FIX: use babel version compatible with webpack@1.14 (#2693)
- FEAT: run mocha tests in travis ci (#2687)
- Add note that PRs should be submitted against the `develop` branch (#2623)
- FIX: Fixes instanceof Object statements for objects from other windows and iFrames. (#2631)
- removed google-analytics from all examples (#2670)
- do not ignore test folder (#2648)
- updated dependencies and devDependencies (#2649)
- general improvements (#2652)
### Network
- FEAT: Improve the performance of the network layout engine (#2729)
- FEAT: Allow for image nodes to have a selected or broken image (#2601)
### Timeline / Graph2D
- FIX #2842: Prevent redirect to blank after drag and drop in FF (#2871)
- FIX #2810: Nested groups do not use "groupOrder" (#2817)
- FIX #2795: fix date for custom format function (#2826)
- FIX #2689: Add animation options for zoomIn/zoomOut funtions (#2830)
- FIX #2800: Removed all "Object.assign" from examples (#2829)
- FIX #2725: Background items positioning when orientation: top (#2831)
- FEAT: Added data as argument to the template function (#2802)
- FIX #2827: Update "progress bar" example to reflect values (#2828)
- FIX #2672: Item events original event (#2704)
- FIX #2696: Update serialization example to use ISOString dates (#2789)
- FIX #2790: Update examples to use ISOString format (#2791)
- FEAT: Added support to supply an end-time to bar charts to have them scale (#2760)
- FIX #1982, #1417: Modify redraw logic to treat scroll as needing restack (#2774)
- FEAT: Initial tests for timeline ItemSet (#2750)
- FIX #2720: Problems with option editable (#2743, #2796, #2806)
- FIX: Range.js "event" is undeclared (#2749)
- FEAT: added new locales for french and espanol (#2723)
- FIX: fixes timestep next issue (#2732)
- FEAT: #2647 Dynamic rolling mode option (#2705)
- FIX #2679: TypeError: Cannot read property 'hasOwnProperty' of null (#2735)
- Add initial tests for Timeline PointItem (#2716)
- FIX #778: Tooltip does not work with background items in timeline (#2703)
- FIX #2598: Flickering onUpdateTimeTooltip (#2702)
- FEAT: refactor tooltip to only use one dom-element (#2662)
- FEAT: Change setCustomTimeTitle title parameter to be a string or a function (#2611)
### Graph3D
- FEAT #2769: Graph3d tooltip styling (#2780)
- FEAT #2540: Adjusted graph3d doc for autoscaling (#2812)
- FIX #2536: 3d bar graph data array unsorted (#2803)
- FEAT: Added showX(YZ)Axis options to Graph3d (#2686)
## 2017-01-29, version 4.18.1

+ 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>

+ 62
- 18
docs/graph3d/index.html View File

@ -498,6 +498,38 @@ var options = {
</td>
</tr>
<tr>
<td>tooltipStyle</td>
<td>Object</td>
<td>
<pre class="prettyprint lang-js">
{
content: {
padding: '10px',
border: '1px solid #4d4d4d',
color: '#1a1a1a',
background: 'rgba(255,255,255,0.7)',
borderRadius: '2px',
boxShadow: '5px 5px 10px rgba(128,128,128,0.5)'
},
line: {
height: '40px',
width: '0',
borderLeft: '1px solid #4d4d4d'
},
dot: {
height: '0',
width: '0',
border: '5px solid #4d4d4d',
borderRadius: '5px'
}
}</pre>
</td>
<td>Tooltip style properties.
Provided properties will be merged with the default object.
</td>
</tr>
<tr>
<td>valueMax</td>
<td>number</td>
@ -532,8 +564,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>
@ -547,14 +579,18 @@ var options = {
<tr>
<td>xMax</td>
<td>number</td>
<td>none</td>
<td>The maximum value for the x-axis.</td>
<td>from data</td>
<td>The maximum value for the x-axis.
If not set, the largest value for x in the data set is used.
</td>
</tr>
<tr>
<td>xMin</td>
<td>number</td>
<td>none</td>
<td>The minimum value for the x-axis.</td>
<td>from data</td>
<td>The minimum value for the x-axis.
If not set, the smallest value for x in the data set is used.
</td>
</tr>
<tr>
<td>xStep</td>
@ -575,8 +611,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>
@ -590,14 +626,18 @@ var options = {
<tr>
<td>yMax</td>
<td>number</td>
<td>none</td>
<td>The maximum value for the y-axis.</td>
<td>from data</td>
<td>The maximum value for the y-axis.
If not set, the largest value for y in the data set is used.
</td>
</tr>
<tr>
<td>yMin</td>
<td>number</td>
<td>none</td>
<td>The minimum value for the y-axis.</td>
<td>from data</td>
<td>The minimum value for the y-axis.
If not set, the smallest value for y in the data set is used.
</td>
</tr>
<tr>
<td>yStep</td>
@ -615,16 +655,20 @@ var options = {
</tr>
<tr>
<td>zMin</td>
<td>zMax</td>
<td>number</td>
<td>none</td>
<td>The minimum value for the z-axis.</td>
<td>from data</td>
<td>The maximum value for the z-axis.
If not set, the largest value for z in the data set is used.
</td>
</tr>
<tr>
<td>zMax</td>
<td>zMin</td>
<td>number</td>
<td>none</td>
<td>The maximum value for the z-axis.</td>
<td>from data</td>
<td>The minimum value for the z-axis.
If not set, the smallest value for z in the data set is used.
</td>
</tr>
<tr>
<td>zStep</td>

+ 31
- 14
docs/timeline/index.html View File

@ -357,7 +357,7 @@ var items = new vis.DataSet([
<td class="indent">editable.updateTime</td>
<td>boolean</td>
<td>no</td>
<td>If true, items can be dragged to another moment int time. See section <a href="#Editing_Items">Editing Items</a> for a detailed explanation.</td>
<td>If true, items can be dragged to another moment in time. See section <a href="#Editing_Items">Editing Items</a> for a detailed explanation.</td>
</tr>
</table>
@ -468,10 +468,10 @@ var groups = [
<td>Array of group ids nested in the group. Nested groups will appear under this nesting group.</td>
</tr>
<tr>
<td>showNestedGroups</td>
<td>showNested</td>
<td>Boolean</td>
<td>no</td>
<td>Assuming the group has nested groups, this will set the initial state of the group - shown or collapsed. The <code>showNestedGroups</code> is defaulted to <code>true</code>.</td>
<td>Assuming the group has nested groups, this will set the initial state of the group - shown or collapsed. The <code>showNested</code> is defaulted to <code>true</code>.</td>
</tr>
</table>
@ -1044,7 +1044,7 @@ function (option, path) {
<td>template</td>
<td>function</td>
<td>none</td>
<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, a string or a template 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>
<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, the item element as the second argument and the edited data as the third argument, and must return HTML code, a string or a template 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>
@ -1439,15 +1439,21 @@ document.getElementById('myTimeline').onclick = function (event) {
</tr>
<tr>
<td>zoomIn(percentage)</td>
<td>zoomIn(percentage [, options])</td>
<td>none</td>
<td>Zoom in the current visible window. The parameter <code>percentage</code> can be a <code>Number</code> and must be between 0 and 1. If the parameter value of <code>percentage</code> is null, the window will be left unchanged.
<td>Zoom in the current visible window. The parameter <code>percentage</code> can be a <code>Number</code> and must be between 0 and 1. If the parameter value of <code>percentage</code> is null, the window will be left unchanged. Available options:
<ul>
<li><code>animation: boolean or {duration: number, easingFunction: string}</code><br>If true (default) or an Object, the range is animated smoothly to the new window. An object can be provided to specify duration and easing function. Default duration is 500 ms, and default easing function is <code>'easeInOutQuad'</code>. Available easing functions: <code>"linear"</code>, <code>"easeInQuad"</code>, <code>"easeOutQuad"</code>, <code>"easeInOutQuad"</code>, <code>"easeInCubic"</code>, <code>"easeOutCubic"</code>, <code>"easeInOutCubic"</code>, <code>"easeInQuart"</code>, <code>"easeOutQuart"</code>, <code>"easeInOutQuart"</code>, <code>"easeInQuint"</code>, <code>"easeOutQuint"</code>, <code>"easeInOutQuint"</code>.</li>
</ul>
</td>
</tr>
<tr>
<td>zoomOut(percentage)</td>
<td>zoomOut(percentage [, options])</td>
<td>none</td>
<td>Zoom out the current visible window. The parameter <code>percentage</code> can be a <code>Number</code> and must be between 0 and 1. If the parameter value of <code>percentage</code> is null, the window will be left unchanged.
<td>Zoom out the current visible window. The parameter <code>percentage</code> can be a <code>Number</code> and must be between 0 and 1. If the parameter value of <code>percentage</code> is null, the window will be left unchanged. Available options:
<ul>
<li><code>animation: boolean or {duration: number, easingFunction: string}</code><br>If true (default) or an Object, the range is animated smoothly to the new window. An object can be provided to specify duration and easing function. Default duration is 500 ms, and default easing function is <code>'easeInOutQuad'</code>. Available easing functions: <code>"linear"</code>, <code>"easeInQuad"</code>, <code>"easeOutQuad"</code>, <code>"easeInOutQuad"</code>, <code>"easeInCubic"</code>, <code>"easeOutCubic"</code>, <code>"easeInOutCubic"</code>, <code>"easeInQuart"</code>, <code>"easeOutQuart"</code>, <code>"easeInOutQuart"</code>, <code>"easeInQuint"</code>, <code>"easeOutQuint"</code>, <code>"easeInOutQuint"</code>.</li>
</ul>
</td>
</tr>
@ -1698,6 +1704,14 @@ var items = new vis.DataSet([
]);
</pre>
<p>
Individual manipulation actions (<code>updateTime</code>, <code>updateGroup</code> and <code>remove</code>) can also be set on individual items. If any of the item-level
actions are specified (and <code>overrideItems</code> is not <code>false</code>) then that takes precedence over the settings at the timeline level. Current behavior is
that if any of the item-level actions are not specified, those items get <code>undefined</code> value (rather than inheriting from the timeline level). This may change
in future major releases, and code that specifies all item level values will handle major release changes better. That is, instead of using
<code>editable: {updateTime : true}</code>, use <code>editable: {updateTime : true, updateGroup: false, remove: false}</code>.
</p>
<p>
One can specify callback functions to validate changes made by the user. There are a number of callback functions for this purpose:
</p>
@ -1743,11 +1757,11 @@ var items = new vis.DataSet([
<h2 id="Templates">Templates</h2>
<p>
Timeline supports templates to format item contents. Any template engine (such as <a href="http://handlebarsjs.com/">handlebars</a> or <a href="http://mustache.github.io/">mustache</a>) can be used, and one can also manually build HTML. In the options, one can provide a template handler. This handler is a function accepting an item's data as argument, and outputs formatted HTML:
Timeline supports templates to format item contents. Any template engine (such as <a href="http://handlebarsjs.com/">handlebars</a> or <a href="http://mustache.github.io/">mustache</a>) can be used, and one can also manually build HTML. In the options, one can provide a template handler. This handler is a function accepting an item's data as the first argument, the item element as the second argument and the edited data as the third argument, and outputs formatted HTML:
</p>
<pre class="prettyprint lang-js">var options = {
template: function (item) {
template: function (item, element, data) {
var html = ... // generate HTML markup for this item
return html;
}
@ -1759,8 +1773,11 @@ var items = new vis.DataSet([
The HTML for an item can be created manually:
<pre class="prettyprint lang-js">var options = {
template: function (item) {
return '&lt;h1&gt;' + item.header + '&lt;/h1&gt;&lt;p&gt;' + item.description + '&lt;/p&gt;';
template: function (item, element, data) {
return '&lt;h1&gt;' + item.header + data.moving?' '+ data.start:'' + '&lt;/h1&gt;&lt;p&gt;' + item.description + '&lt;/p&gt;';
},
onMoving: function (item, callback) {
item.moving = true;
}
};
</pre>
@ -1795,7 +1812,7 @@ var template = Handlebars.compile(source);
You can use a React component for the templates by rendering them to the templates' element directly:
<pre class="prettyprint lang-js">
template: function (item, element) {
template: function (item, element, data) {
return ReactDOM.render(&lt;b&gt;{item.content}&lt;/b&gt;, element);
},
</pre>
@ -1814,7 +1831,7 @@ var templates = {
};
var options = {
template: function (item) {
template: function (item, element, data) {
var template = templates[item.template]; // choose the right template
return template(item); // execute the template
}

+ 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>

+ 15
- 1
examples/graph3d/11_tooltips.html View File

@ -64,12 +64,26 @@
showShadow: false,
// 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, and data
// data is the original object passed to the point constructor
return 'value: <b>' + point.z + '</b><br>' + point.data.extra;
},
// Tooltip default styling can be overridden
tooltipStyle: {
content: {
background : 'rgba(255, 255, 255, 0.7)',
padding : '10px',
borderRadius : '10px'
},
line: {
borderLeft : '1px dotted rgba(0, 0, 0, 0.5)'
},
dot: {
border : '5px solid rgba(0, 0, 0, 0.5)'
}
},
keepAspectRatio: true,
verticalRatio: 0.5

+ 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>

+ 5
- 5
examples/timeline/editing/individualEditableItems.html View File

@ -34,7 +34,7 @@
<div id="visualization"></div>
<script>
// create groups to highligh groupUpdate
// create groups to highlight groupUpdate
var groups = new vis.DataSet([
{id: 1, content: 'Group 1'},
{id: 2, content: 'Group 2'}
@ -45,9 +45,9 @@
{id: 2, content: 'Editable', editable: true, start: '2010-08-23T23:00:00', group: 2},
{id: 3, content: 'Read-only', editable: false, start: '2010-08-24T16:00:00', group: 1},
{id: 4, content: 'Read-only', editable: false, start: '2010-08-26', end: '2010-09-02', group: 2},
{id: 5, content: 'Edit Time Only', editable: { updateTime: true }, start: '2010-08-28', group: 1},
{id: 6, content: 'Edit Group Only', editable: { updateGroup: true }, start: '2010-08-29', group: 2},
{id: 7, content: 'Remove Only', editable: { remove: true }, start: '2010-08-31', end: '2010-09-03', group: 1},
{id: 5, content: 'Edit Time Only', editable: { updateTime: true, updateGroup: false, remove: false }, start: '2010-08-28', group: 1},
{id: 6, content: 'Edit Group Only', editable: { updateTime: false, updateGroup: true, remove: false }, start: '2010-08-29', group: 2},
{id: 7, content: 'Remove Only', editable: { updateTime: false, updateGroup: false, remove: true }, start: '2010-08-31', end: '2010-09-03', group: 1},
{id: 8, content: 'Default', start: '2010-09-04T12:00:00', group: 2}
]);
@ -60,4 +60,4 @@
</script>
</body>
</html>
</html>

+ 7
- 6
examples/timeline/editing/tooltipOnItemChange.html View File

@ -2,6 +2,7 @@
<head>
<title>Timeline | Tooltip on item onUpdateTime Option</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
@ -63,20 +64,20 @@
editable: true
};
var options1 = Object.assign({
var options1 = jQuery.extend(options, {
tooltipOnItemUpdateTime: true
}, options)
})
var container1 = document.getElementById('mytimeline1');
timeline1 = new vis.Timeline(container1, items, null, options1);
var options2 = Object.assign({
var options2 = jQuery.extend(options, {
orientation: 'top',
tooltipOnItemUpdateTime: {
template: function(item) {
return 'html template for tooltip with <b>item.start</b>: ' + item.start;
}
}
}, options)
})
var container2 = document.getElementById('mytimeline2');
timeline2 = new vis.Timeline(container2, items, null, options2);
@ -117,10 +118,10 @@
}
var options3 = Object.assign({
var options3 = jQuery.extend(options, {
orientation: 'top',
tooltipOnItemUpdateTime: true
}, options)
})
var container3 = document.getElementById('mytimeline3');
timeline3 = new vis.Timeline(container3, itemsWithGroups, groups, options3);

+ 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);
}

+ 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>';
}
};

+ 3
- 2
examples/timeline/other/rtl.html View File

@ -3,6 +3,7 @@
<head>
<title>Timeline | RTL example</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
@ -39,10 +40,10 @@
height: '300px',
};
var options1 = Object.assign({}, options)
var options1 = jQuery.extend(options, {})
var timeline1 = new vis.Timeline(container1, items, options1);
var options2 = Object.assign({rtl: true}, options)
var options2 = jQuery.extend(options, {rtl: true})
var timeline2 = new vis.Timeline(container2, items, options2);
</script>

+ 3
- 2
examples/timeline/other/verticalScroll.html View File

@ -2,6 +2,7 @@
<head>
<title>Timeline | Vertical Scroll Option</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
@ -75,11 +76,11 @@ zoomKey: 'ctrlKey'
// create a Timeline
options1 = Object.assign({}, options)
options1 = jQuery.extend(options, {});
var container1 = document.getElementById('mytimeline1');
timeline1 = new vis.Timeline(container1, items, groups, options1);
options2 = Object.assign({horizontalScroll: true}, options)
options2 = jQuery.extend(options, {horizontalScroll: true});
var container2 = document.getElementById('mytimeline2');
timeline2 = new vis.Timeline(container2, items, groups, options2);

+ 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>

+ 58
- 20
lib/graph3d/Graph3d.js View File

@ -67,6 +67,29 @@ var DEFAULTS = {
style : Graph3d.STYLE.DOT,
tooltip : false,
tooltipStyle : {
content : {
padding : '10px',
border : '1px solid #4d4d4d',
color : '#1a1a1a',
background : 'rgba(255,255,255,0.7)',
borderRadius : '2px',
boxShadow : '5px 5px 10px rgba(128,128,128,0.5)'
},
line : {
height : '40px',
width : '0',
borderLeft : '1px solid #4d4d4d'
},
dot : {
height : '0',
width : '0',
border : '5px solid #4d4d4d',
borderRadius : '5px'
}
},
showLegend : autoByDefault, // determined by graph style
backgroundColor : autoByDefault,
@ -327,7 +350,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 +517,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;
}
}
@ -2289,26 +2337,16 @@ Graph3d.prototype._showTooltip = function (dataPoint) {
if (!this.tooltip) {
content = document.createElement('div');
Object.assign(content.style, {}, this.tooltipStyle.content);
content.style.position = 'absolute';
content.style.padding = '10px';
content.style.border = '1px solid #4d4d4d';
content.style.color = '#1a1a1a';
content.style.background = 'rgba(255,255,255,0.7)';
content.style.borderRadius = '2px';
content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)';
line = document.createElement('div');
Object.assign(line.style, {}, this.tooltipStyle.line);
line.style.position = 'absolute';
line.style.height = '40px';
line.style.width = '0';
line.style.borderLeft = '1px solid #4d4d4d';
dot = document.createElement('div');
Object.assign(dot.style, {}, this.tooltipStyle.dot);
dot.style.position = 'absolute';
dot.style.height = '0';
dot.style.width = '0';
dot.style.border = '5px solid #4d4d4d';
dot.style.borderRadius = '5px';
this.tooltip = {
dataPoint: null,
@ -2449,4 +2487,4 @@ Graph3d.prototype.setSize = function(width, height) {
// -----------------------------------------------------------------------------
module.exports = Graph3d;
module.exports = Graph3d;

+ 6
- 4
lib/graph3d/Settings.js View File

@ -2,6 +2,7 @@
// This modules handles the options for Graph3d.
//
////////////////////////////////////////////////////////////////////////////////
var util = require('../util');
var Camera = require('./Camera');
var Point3d = require('./Point3d');
@ -69,7 +70,7 @@ var OPTIONKEYS = [
'axisColor',
'gridColor',
'xCenter',
'yCenter'
'yCenter',
];
@ -115,7 +116,6 @@ function isEmpty(obj) {
}
/**
* Make first letter of parameter upper case.
*
@ -241,7 +241,6 @@ function setOptions(options, dst) {
throw new Error('DEFAULTS not set for module Settings');
}
// Handle the parameters which can be simply copied over
safeCopy(options, dst, OPTIONKEYS);
safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default');
@ -250,7 +249,6 @@ function setOptions(options, dst) {
setSpecialSettings(options, dst);
}
/**
* Special handling for certain parameters
*
@ -274,6 +272,10 @@ function setSpecialSettings(src, dst) {
if (src.onclick != undefined) {
dst.onclick_callback = src.onclick;
}
if (src.tooltipStyle !== undefined) {
util.selectiveDeepExtend(['tooltipStyle'], dst, src);
}
}

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

@ -441,6 +441,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];
@ -483,16 +486,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.
@ -544,8 +555,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);
@ -651,7 +662,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;
}
@ -1250,17 +1260,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);
}

+ 21
- 10
lib/timeline/Core.js View File

@ -245,6 +245,9 @@ Core.prototype._create = function (container) {
}
function handleDrop(event) {
// prevent redirect to blank page - Firefox
if(event.preventDefault) { event.preventDefault(); }
if(event.stopPropagation) { event.stopPropagation(); }
// return when dropping non-vis items
try {
var itemData = JSON.parse(event.dataTransfer.getData("text"))
@ -708,8 +711,15 @@ Core.prototype.getWindow = function() {
/**
* Zoom in the window such that given time is centered on screen.
* @param {Number} percentage - must be between [0..1]
* @param {Object} [options] Available options:
* `animation: boolean | {duration: number, easingFunction: string}`
* If true (default), the range is animated
* smoothly to the new window. An object can be
* provided to specify duration and easing function.
* Default duration is 500 ms, and default easing
* function is 'easeInOutQuad'.
*/
Core.prototype.zoomIn = function(percentage) {
Core.prototype.zoomIn = function(percentage, options) {
if (!percentage || percentage < 0 || percentage > 1) return
var range = this.getWindow();
var start = range.start.valueOf();
@ -720,17 +730,21 @@ Core.prototype.zoomIn = function(percentage) {
var newStart = start + distance;
var newEnd = end - distance;
this.setWindow({
start : newStart,
end : newEnd
});
this.setWindow(newStart, newEnd, options);
};
/**
* Zoom out the window such that given time is centered on screen.
* @param {Number} percentage - must be between [0..1]
* @param {Object} [options] Available options:
* `animation: boolean | {duration: number, easingFunction: string}`
* If true (default), the range is animated
* smoothly to the new window. An object can be
* provided to specify duration and easing function.
* Default duration is 500 ms, and default easing
* function is 'easeInOutQuad'.
*/
Core.prototype.zoomOut = function(percentage) {
Core.prototype.zoomOut = function(percentage, options) {
if (!percentage || percentage < 0 || percentage > 1) return
var range = this.getWindow();
var start = range.start.valueOf();
@ -739,10 +753,7 @@ Core.prototype.zoomOut = function(percentage) {
var newStart = start - interval * percentage / 2;
var newEnd = end + interval * percentage / 2;
this.setWindow({
start : newStart,
end : newEnd
});
this.setWindow(newStart, newEnd, options);
};
/**

+ 4
- 4
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
});
};

+ 7
- 1
lib/timeline/TimeStep.js View File

@ -521,6 +521,9 @@ TimeStep.prototype.getLabelMinor = function(date) {
if (date == undefined) {
date = this.current;
}
if (date instanceof Date) {
date = this.moment(date)
}
if (typeof(this.format.minorLabels) === "function") {
return this.format.minorLabels(date, this.scale, this.step);
@ -540,7 +543,10 @@ TimeStep.prototype.getLabelMajor = function(date) {
if (date == undefined) {
date = this.current;
}
if (date instanceof Date) {
date = this.moment(date)
}
if (typeof(this.format.majorLabels) === "function") {
return this.format.majorLabels(date, this.scale, this.step);
}

+ 71
- 39
lib/timeline/component/Group.js View File

@ -133,21 +133,34 @@ Group.prototype.setData = function(data) {
}
if (data && data.nestedGroups) {
if (data.showNested == false) {
this.showNested = false;
} else {
this.showNested = true;
if (!this.nestedGroups || this.nestedGroups != data.nestedGroups) {
this.nestedGroups = data.nestedGroups;
}
if (data.showNested !== undefined || this.showNested === undefined) {
if (data.showNested == false) {
this.showNested = false;
} else {
this.showNested = true;
}
}
util.addClassName(this.dom.label, 'vis-nesting-group');
var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed'
if (this.showNested) {
util.removeClassName(this.dom.label, 'collapsed');
util.removeClassName(this.dom.label, collapsedDirClassName);
util.addClassName(this.dom.label, 'expanded');
} else {
util.removeClassName(this.dom.label, 'expanded');
var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed'
util.addClassName(this.dom.label, collapsedDirClassName);
}
} else if (this.nestedGroups) {
this.nestedGroups = null;
var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed'
util.removeClassName(this.dom.label, collapsedDirClassName);
util.removeClassName(this.dom.label, 'expanded');
util.removeClassName(this.dom.label, 'vis-nesting-group');
}
if (data && data.nestedInGroup) {
@ -425,30 +438,9 @@ Group.prototype.add = function(item) {
// add to
if (item.data.subgroup !== undefined) {
if (this.subgroups[item.data.subgroup] === undefined) {
this.subgroups[item.data.subgroup] = {
height:0,
top: 0,
start: item.data.start,
end: item.data.end,
visible: false,
index:this.subgroupIndex,
items: []
};
this.subgroupIndex++;
}
if (new Date(item.data.start) < new Date(this.subgroups[item.data.subgroup].start)) {
this.subgroups[item.data.subgroup].start = item.data.start;
}
if (new Date(item.data.end) > new Date(this.subgroups[item.data.subgroup].end)) {
this.subgroups[item.data.subgroup].end = item.data.end;
}
this.subgroups[item.data.subgroup].items.push(item);
this._addToSubgroup(item);
this.orderSubgroups();
}
this.orderSubgroups();
if (this.visibleItems.indexOf(item) == -1) {
var range = this.itemSet.body.range; // TODO: not nice accessing the range like this
@ -456,6 +448,34 @@ Group.prototype.add = function(item) {
}
};
Group.prototype._addToSubgroup = function(item, subgroupId) {
subgroupId = subgroupId || item.data.subgroup;
if (subgroupId != undefined && this.subgroups[subgroupId] === undefined) {
this.subgroups[subgroupId] = {
height:0,
top: 0,
start: item.data.start,
end: item.data.end,
visible: false,
index:this.subgroupIndex,
items: []
};
this.subgroupIndex++;
}
if (new Date(item.data.start) < new Date(this.subgroups[subgroupId].start)) {
this.subgroups[subgroupId].start = item.data.start;
}
if (new Date(item.data.end) > new Date(this.subgroups[subgroupId].end)) {
this.subgroups[subgroupId].end = item.data.end;
}
this.subgroups[subgroupId].items.push(item);
};
Group.prototype._updateSubgroupsSizes = function () {
var me = this;
if (me.subgroups) {
@ -526,22 +546,30 @@ Group.prototype.remove = function(item) {
if (index != -1) this.visibleItems.splice(index, 1);
if(item.data.subgroup !== undefined){
var subgroup = this.subgroups[item.data.subgroup];
this._removeFromSubgroup(item);
this.orderSubgroups();
}
};
Group.prototype._removeFromSubgroup = function(item, subgroupId) {
subgroupId = subgroupId || item.data.subgroup;
if (subgroupId != undefined) {
var subgroup = this.subgroups[subgroupId];
if (subgroup){
var itemIndex = subgroup.items.indexOf(item);
subgroup.items.splice(itemIndex,1);
if (!subgroup.items.length){
delete this.subgroups[item.data.subgroup];
this.subgroupIndex--;
} else {
this._updateSubgroupsSizes();
// Check the item is actually in this subgroup. How should items not in the group be handled?
if (itemIndex >= 0) {
subgroup.items.splice(itemIndex,1);
if (!subgroup.items.length){
delete this.subgroups[subgroupId];
} else {
this._updateSubgroupsSizes();
}
}
this.orderSubgroups();
}
}
};
/**
* Remove an item from the corresponding DataSet
* @param {Item} item
@ -722,6 +750,10 @@ Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visi
}
};
Group.prototype.changeSubgroup = function(item, oldSubgroup, newSubgroup) {
this._removeFromSubgroup(item, oldSubgroup);
this._addToSubgroup(item, newSubgroup);
this.orderSubgroups();
};
module.exports = Group;

+ 24
- 14
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;
@ -1155,7 +1157,8 @@ ItemSet.prototype._orderNestedGroups = function(groupIds) {
var nestedGroups = this.groupsData.get({
filter: function(nestedGroup) {
return nestedGroup.nestedInGroup == groupId;
}
},
order: this.options.groupOrder
});
var nestedGroupIds = nestedGroups.map(function(nestedGroup) { return nestedGroup.id })
newGroupIdsOrder = newGroupIdsOrder.concat(nestedGroupIds);
@ -1196,21 +1199,28 @@ ItemSet.prototype._updateItem = function(item, itemData) {
var oldGroupId = item.data.group;
var oldSubGroupId = item.data.subgroup;
if (oldGroupId != itemData.group) {
var oldGroup = this.groups[oldGroupId];
if (oldGroup) oldGroup.remove(item);
}
// update the items data (will redraw the item when displayed)
item.setData(itemData);
var groupId = this._getGroupId(item.data);
var group = this.groups[groupId];
var group = this.groups[groupId];
if (!group) {
item.groupShowing = false;
item.groupShowing = false;
} else if (group && group.data && group.data.showNested) {
item.groupShowing = true;
item.groupShowing = true;
}
// update group
if (oldGroupId != item.data.group || oldSubGroupId != item.data.subgroup) {
var oldGroup = this.groups[oldGroupId];
if (oldGroup) oldGroup.remove(item);
if (group) group.add(item);
if (group) {
if (oldGroupId != item.data.group) {
group.add(item);
} else if (oldSubGroupId != item.data.subgroup) {
group.changeSubgroup(item, oldSubGroupId);
}
}
};
@ -1641,7 +1651,7 @@ ItemSet.prototype._onDragEnd = function (event) {
ItemSet.prototype._onGroupClick = function (event) {
var group = this.groupFromTarget(event);
if (!group.nestedGroups) return;
if (!group || !group.nestedGroups) return;
var groupsData = this.groupsData;
if (this.groupsData instanceof DataView) {
@ -1874,7 +1884,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 +1929,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 +1949,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 +2153,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 = {

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

@ -143,6 +143,7 @@ BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX;
*/
BackgroundItem.prototype.repositionY = function(margin) {
var height;
var orientation = this.options.orientation.item;
// special positioning for subgroups
if (this.data.subgroup !== undefined) {
@ -154,7 +155,6 @@ BackgroundItem.prototype.repositionY = function(margin) {
this.dom.box.style.height = this.parent.subgroups[itemSubgroup].height + 'px';
var orientation = this.options.orientation.item;
if (orientation == 'top') {
this.dom.box.style.top = this.parent.top + this.parent.subgroups[itemSubgroup].top + 'px';
} else {
@ -170,8 +170,8 @@ BackgroundItem.prototype.repositionY = function(margin) {
height = Math.max(this.parent.height,
this.parent.itemSet.body.domProps.center.height,
this.parent.itemSet.body.domProps.centerContainer.height);
this.dom.box.style.bottom = orientation == 'bottom' ? '0' : '';
this.dom.box.style.top = orientation == 'top' ? '0' : '';
this.dom.box.style.bottom = orientation == 'top' ? '' : '0';
}
else {
height = this.parent.height;

+ 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, options.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) : '') +

+ 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)

+ 11
- 10
package.json View File

@ -1,6 +1,6 @@
{
"name": "vis",
"version": "4.18.1-SNAPSHOT",
"version": "4.19.1-SNAPSHOT",
"description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/",
"license": "(Apache-2.0 OR MIT)",
@ -28,16 +28,8 @@
"lint": "eslint lib",
"watch": "gulp watch",
"watch-dev": "gulp watch --bundle"
},
},
"dependencies": {
"babel-core": "^6.6.5",
"babel-loader": "^6.2.4",
"babel-polyfill": "^6.22.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.8.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-es2015": "^6.6.0",
"babel-runtime": "^6.22.0",
"emitter-component": "^1.1.1",
"moment": "^2.17.1",
"propagating-hammerjs": "^1.4.6",
@ -46,7 +38,15 @@
},
"devDependencies": {
"async": "^2.1.4",
"babel-core": "^6.6.5",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.4",
"babel-polyfill": "^6.22.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.8.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-es2015": "^6.6.0",
"babel-runtime": "^6.22.0",
"babelify": "^7.3.0",
"clean-css": "^4.0.2",
"eslint": "^3.15.0",
@ -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() {
var pointItem = new PointItem({start: now.toDate(), editable: false}, 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-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:false override item (with object option for group change)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: false}, null, {editable: {updateTime: false, updateGroup: 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 object option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: true}, null, {editable: {updateTime: false, 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:true non-override item (with object option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: true}, null, {editable: {updateTime: false, updateGroup: false, overrideItems: 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:false non-override item (with object option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: false}, null, {editable: {updateTime: true, updateGroup: false, overrideItems: 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 property for an editable: {updateTime} override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true}}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, undefined);
});
it('should redraw() and then have the correct property for an editable: {updateTime} override item (with boolean option false)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true}}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, undefined);
});
it('should redraw() and then have the correct property for an editable: {updateGroup} override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateGroup: true}}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, undefined);
assert.equal(pointItem.editable.updateGroup, true);
assert.equal(pointItem.editable.remove, undefined);
});
it('should redraw() and then have the correct property for an editable: {updateGroup} override item (with boolean option false)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateGroup: true}}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, undefined);
assert.equal(pointItem.editable.updateGroup, true);
assert.equal(pointItem.editable.remove, undefined);
});
it('should redraw() and then have the correct property for an editable: {remove} override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {remove: true}}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, undefined);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, true);
});
it('should redraw() and then have the correct property for an editable: {remove} override item (with boolean option false)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {remove: true}}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, undefined);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, true);
});
it('should redraw() and then have the correct property for an editable: {updateTime, remove} override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true, remove: true}}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, true);
});
it('should redraw() and then have the correct property for an editable: {updateTime, remove} override item (with boolean option false)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true, remove: true}}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, undefined);
assert.equal(pointItem.editable.remove, true);
});
it('should redraw() and then have the correct property for an editable: {updateTime, updateGroup, remove} override item (with boolean option)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true, updateGroup: true, remove: true}}, null, {editable: true});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, true);
assert.equal(pointItem.editable.remove, true);
});
it('should redraw() and then have the correct property for an editable: {updateTime, updateGroup, remove} override item (with boolean option false)', function() {
var pointItem = new PointItem({start: now.toDate(), editable: {updateTime: true, updateGroup: true, remove: true}}, null, {editable: false});
var parent = TestSupport.buildMockItemSet();
pointItem.setParent(parent);
pointItem.redraw();
assert.equal(pointItem.editable.updateTime, true);
assert.equal(pointItem.editable.updateGroup, true);
assert.equal(pointItem.editable.remove, true);
});
});

+ 21
- 1
test/TestSupport.js View File

@ -1,5 +1,20 @@
var vis = require('../dist/vis');
var DataSet = vis.DataSet;
module.exports = {
buildMockItemSet: function() {
var itemset = {
dom: {
foreground: document.createElement('div'),
content: document.createElement('div')
},
itemSet: {
itemsData: new DataSet()
}
};
return itemset;
},
buildSimpleTimelineRangeBody: function () {
var body = {
dom: {
@ -7,7 +22,12 @@ module.exports = {
clientWidth: 1000
}
},
domProps: this.props,
domProps: {
centerContainer: {
width: 900,
height: 600
}
},
emitter: {
on: function () {},
off: function () {},

+ 108
- 0
test/TimelineItemSet.test.js View File

@ -0,0 +1,108 @@
var assert = require('assert');
describe('Timeline ItemSet', function () {
before(function () {
delete require.cache[require.resolve('../dist/vis')]
this.jsdom = require('jsdom-global')();
this.vis = require('../dist/vis');
var TestSupport = require('./TestSupport');
var rangeBody = TestSupport.buildSimpleTimelineRangeBody();
this.testrange = new this.vis.timeline.Range(rangeBody);
this.testrange.setRange(new Date(2017, 1, 26, 13, 26, 3, 320), new Date(2017, 1, 26, 13, 26, 4, 320), false, false, null);
this.testitems = new this.vis.DataSet({
type: {
start: 'Date',
end: 'Date'
}
});
// add single items with different date types
this.testitems.add({id: 1, content: 'Item 1', start: new Date(2017, 1, 26, 13, 26, 3, 600), type: 'point'});
this.testitems.add({id: 2, content: 'Item 2', start: new Date(2017, 1, 26, 13, 26, 5, 600), type: 'point'});
})
after(function () {
this.jsdom();
})
var getBasicBody = function() {
var body = {
dom: {
container: document.createElement('div'),
leftContainer: document.createElement('div'),
centerContainer: document.createElement('div'),
top: document.createElement('div'),
left: document.createElement('div'),
center: document.createElement('div'),
backgroundVertical: document.createElement('div')
},
domProps: {
root: {},
background: {},
centerContainer: {},
leftContainer: {},
rightContainer: {},
center: {},
left: {},
right: {},
top: {},
bottom: {},
border: {},
scrollTop: 0,
scrollTopMin: 0
},
emitter: {
on: function() {return {};},
emit: function() {}
},
util: {
}
}
return body;
};
it('should initialise with minimal data', function () {
var body = getBasicBody();
var itemset = new this.vis.timeline.components.ItemSet(body, {});
assert(itemset);
});
it('should redraw() and have the right classNames', function () {
var body = getBasicBody();
body.range = this.testrange;
var itemset = new this.vis.timeline.components.ItemSet(body, {});
itemset.redraw();
assert.equal(itemset.dom.frame.className, 'vis-itemset');
assert.equal(itemset.dom.background.className, 'vis-background');
assert.equal(itemset.dom.foreground.className, 'vis-foreground');
assert.equal(itemset.dom.axis.className, 'vis-axis');
assert.equal(itemset.dom.labelSet.className, 'vis-labelset');
});
it('should start with no items', function () {
var body = getBasicBody();
var itemset = new this.vis.timeline.components.ItemSet(body, {});
assert.equal(itemset.getItems(), null);
});
it('should store items correctly', function() {
var body = getBasicBody();
body.range = this.testrange;
var DateUtil = this.vis.timeline.DateUtil;
body.util.toScreen = function(time) {
return DateUtil.toScreen({
body: {
hiddenDates: []
},
range: {
conversion: function() {
return {offset: 0, scale: 100};
}
}
}, time, 900)
};
var itemset = new this.vis.timeline.components.ItemSet(body, {});
itemset.setItems(this.testitems);
assert.equal(itemset.getItems().length, 2);
assert.deepEqual(itemset.getItems(), this.testitems);
});
});

Loading…
Cancel
Save