From 7e6810955d34ff127f58ab47d58ad050cd9add94 Mon Sep 17 00:00:00 2001
From: josdejong
Date: Fri, 3 Jan 2014 13:22:44 +0100
Subject: [PATCH] Switched to 2-space indentation
---
HISTORY.md | 2 +
Jakefile.js | 240 +-
README.md | 54 +-
bower.json | 42 +-
docs/css/prettify.css | 102 +-
docs/css/style.css | 78 +-
docs/dataset.html | 1102 +-
docs/dataview.html | 272 +-
docs/graph.html | 1628 +-
docs/index.html | 252 +-
docs/timeline.html | 918 +-
examples/graph/01_basic_usage.html | 68 +-
examples/graph/02_random_nodes.html | 168 +-
examples/graph/03_images.html | 144 +-
examples/graph/04_shapes.html | 114 +-
examples/graph/05_social_network.html | 116 +-
examples/graph/06_groups.html | 282 +-
examples/graph/07_selections.html | 88 +-
examples/graph/08_mobile_friendly.html | 174 +-
examples/graph/09_sizing.html | 126 +-
examples/graph/10_multiline_text.html | 72 +-
examples/graph/11_custom_style.html | 228 +-
examples/graph/12_scalable_images.html | 132 +-
examples/graph/13_dashed_lines.html | 104 +-
examples/graph/14_dot_language.html | 20 +-
.../graph/15_dot_language_playground.html | 344 +-
examples/graph/16_dynamic_data.html | 448 +-
examples/graph/17_network_info.html | 280 +-
examples/graph/graphviz/graphviz_gallery.html | 120 +-
examples/graph/index.html | 42 +-
package.json | 64 +-
src/DataSet.js | 1180 +-
src/DataView.js | 335 +-
src/EventBus.js | 86 +-
src/events.js | 196 +-
src/graph/Edge.js | 766 +-
src/graph/Graph.js | 2348 +--
src/graph/Groups.js | 74 +-
src/graph/Images.js | 34 +-
src/graph/Node.js | 750 +-
src/graph/Popup.js | 124 +-
src/graph/dotparser.js | 1502 +-
src/graph/shapes.js | 438 +-
src/module/exports.js | 76 +-
src/shim.js | 346 +-
src/timeline/Controller.js | 220 +-
src/timeline/Range.js | 564 +-
src/timeline/Stack.js | 214 +-
src/timeline/TimeStep.js | 566 +-
src/timeline/Timeline.js | 544 +-
src/timeline/component/Component.js | 116 +-
src/timeline/component/ContentPanel.js | 104 +-
src/timeline/component/CurrentTime.js | 114 +-
src/timeline/component/CustomTime.js | 282 +-
src/timeline/component/Group.js | 122 +-
src/timeline/component/GroupSet.js | 668 +-
src/timeline/component/ItemSet.js | 676 +-
src/timeline/component/Panel.js | 104 +-
src/timeline/component/RootPanel.js | 252 +-
src/timeline/component/TimeAxis.js | 716 +-
src/timeline/component/css/currenttime.css | 6 +-
src/timeline/component/css/customtime.css | 8 +-
src/timeline/component/css/groupset.css | 68 +-
src/timeline/component/css/item.css | 88 +-
src/timeline/component/css/itemset.css | 10 +-
src/timeline/component/css/panel.css | 14 +-
src/timeline/component/css/timeaxis.css | 42 +-
src/timeline/component/item/Item.js | 40 +-
src/timeline/component/item/ItemBox.js | 468 +-
src/timeline/component/item/ItemPoint.js | 334 +-
src/timeline/component/item/ItemRange.js | 338 +-
.../component/item/ItemRangeOverflow.js | 110 +-
src/util.js | 820 +-
test/dataset.html | 120 +-
test/dataset.js | 122 +-
test/dataview.js | 32 +-
test/dotparser.js | 328 +-
test/eventbus.js | 26 +-
test/timeline.html | 132 +-
test/timeline_groups.html | 118 +-
test/timestep.html | 44 +-
tools/watch.js | 14 +-
vis.js | 16023 ++++++++--------
vis.min.js | 13 +-
84 files changed, 20071 insertions(+), 20588 deletions(-)
diff --git a/HISTORY.md b/HISTORY.md
index c647a7a6..173f4445 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,9 +1,11 @@
vis.js history
http://visjs.org
+
## , version 0.3.0
- Implemented options `showCurrentTime` and `showCustomTime`. Thanks fi0dor.
+- Implemented touch support for Timeline.
- Fixed broken Timeline options `min` and `max`.
- Fixed not being able to load vis.js in node.js.
diff --git a/Jakefile.js b/Jakefile.js
index c2961da0..d5599feb 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -18,7 +18,7 @@ var VIS_MIN = './vis.min.js';
*/
desc('Execute all tasks: build all libraries');
task('default', ['build', 'minify', 'test'], function () {
- console.log('done');
+ console.log('done');
});
/**
@@ -26,97 +26,97 @@ task('default', ['build', 'minify', 'test'], function () {
*/
desc('Build the visualization library vis.js');
task('build', {async: true}, function () {
- // concatenate and stringify the css files
- var result = concat({
- src: [
- './src/timeline/component/css/timeline.css',
- './src/timeline/component/css/panel.css',
- './src/timeline/component/css/groupset.css',
- './src/timeline/component/css/itemset.css',
- './src/timeline/component/css/item.css',
- './src/timeline/component/css/timeaxis.css',
- './src/timeline/component/css/currenttime.css',
- './src/timeline/component/css/customtime.css'
- ],
- header: '/* vis.js stylesheet */',
- separator: '\n'
- });
- var cssText = JSON.stringify(result.code);
-
- // concatenate the script files
- concat({
- dest: VIS_TMP,
- src: [
- './src/module/imports.js',
-
- './src/shim.js',
- './src/util.js',
- './src/events.js',
- './src/EventBus.js',
- './src/DataSet.js',
- './src/DataView.js',
-
- './src/timeline/TimeStep.js',
- './src/timeline/Stack.js',
- './src/timeline/Range.js',
- './src/timeline/Controller.js',
- './src/timeline/component/Component.js',
- './src/timeline/component/Panel.js',
- './src/timeline/component/RootPanel.js',
- './src/timeline/component/TimeAxis.js',
- './src/timeline/component/CurrentTime.js',
- './src/timeline/component/CustomTime.js',
- './src/timeline/component/ItemSet.js',
- './src/timeline/component/item/*.js',
- './src/timeline/component/Group.js',
- './src/timeline/component/GroupSet.js',
- './src/timeline/Timeline.js',
-
- './src/graph/dotparser.js',
- './src/graph/shapes.js',
- './src/graph/Node.js',
- './src/graph/Edge.js',
- './src/graph/Popup.js',
- './src/graph/Groups.js',
- './src/graph/Images.js',
- './src/graph/Graph.js',
-
- './src/module/exports.js'
- ],
-
- separator: '\n',
-
- // Note: we insert the css as a string in the javascript code here
- // the css will be injected on load of the javascript library
- footer: '// inject css\n' +
- 'util.loadCss(' + cssText + ');\n'
- });
-
- // bundle the concatenated script and dependencies into one file
- var b = browserify();
- b.add(VIS_TMP);
- b.bundle({
- standalone: 'vis'
- }, function (err, code) {
- if(err) {
- throw err;
- }
-
- // add header and footer
- var lib = read('./src/module/header.js') + code;
-
- // write bundled file
- write(VIS, lib);
- console.log('created ' + VIS);
-
- // remove temporary file
- fs.unlinkSync(VIS_TMP);
-
- // update version number and stuff in the javascript files
- replacePlaceholders(VIS);
-
- complete();
- });
+ // concatenate and stringify the css files
+ var result = concat({
+ src: [
+ './src/timeline/component/css/timeline.css',
+ './src/timeline/component/css/panel.css',
+ './src/timeline/component/css/groupset.css',
+ './src/timeline/component/css/itemset.css',
+ './src/timeline/component/css/item.css',
+ './src/timeline/component/css/timeaxis.css',
+ './src/timeline/component/css/currenttime.css',
+ './src/timeline/component/css/customtime.css'
+ ],
+ header: '/* vis.js stylesheet */',
+ separator: '\n'
+ });
+ var cssText = JSON.stringify(result.code);
+
+ // concatenate the script files
+ concat({
+ dest: VIS_TMP,
+ src: [
+ './src/module/imports.js',
+
+ './src/shim.js',
+ './src/util.js',
+ './src/events.js',
+ './src/EventBus.js',
+ './src/DataSet.js',
+ './src/DataView.js',
+
+ './src/timeline/TimeStep.js',
+ './src/timeline/Stack.js',
+ './src/timeline/Range.js',
+ './src/timeline/Controller.js',
+ './src/timeline/component/Component.js',
+ './src/timeline/component/Panel.js',
+ './src/timeline/component/RootPanel.js',
+ './src/timeline/component/TimeAxis.js',
+ './src/timeline/component/CurrentTime.js',
+ './src/timeline/component/CustomTime.js',
+ './src/timeline/component/ItemSet.js',
+ './src/timeline/component/item/*.js',
+ './src/timeline/component/Group.js',
+ './src/timeline/component/GroupSet.js',
+ './src/timeline/Timeline.js',
+
+ './src/graph/dotparser.js',
+ './src/graph/shapes.js',
+ './src/graph/Node.js',
+ './src/graph/Edge.js',
+ './src/graph/Popup.js',
+ './src/graph/Groups.js',
+ './src/graph/Images.js',
+ './src/graph/Graph.js',
+
+ './src/module/exports.js'
+ ],
+
+ separator: '\n',
+
+ // Note: we insert the css as a string in the javascript code here
+ // the css will be injected on load of the javascript library
+ footer: '// inject css\n' +
+ 'util.loadCss(' + cssText + ');\n'
+ });
+
+ // bundle the concatenated script and dependencies into one file
+ var b = browserify();
+ b.add(VIS_TMP);
+ b.bundle({
+ standalone: 'vis'
+ }, function (err, code) {
+ if(err) {
+ throw err;
+ }
+
+ // add header and footer
+ var lib = read('./src/module/header.js') + code;
+
+ // write bundled file
+ write(VIS, lib);
+ console.log('created ' + VIS);
+
+ // remove temporary file
+ fs.unlinkSync(VIS_TMP);
+
+ // update version number and stuff in the javascript files
+ replacePlaceholders(VIS);
+
+ complete();
+ });
});
/**
@@ -124,17 +124,17 @@ task('build', {async: true}, function () {
*/
desc('Minify the visualization library vis.js');
task('minify', function () {
- // minify javascript
- minify({
- src: VIS,
- dest: VIS_MIN,
- header: read('./src/module/header.js')
- });
+ // minify javascript
+ minify({
+ src: VIS,
+ dest: VIS_MIN,
+ header: read('./src/module/header.js')
+ });
- // update version number and stuff in the javascript files
- replacePlaceholders(VIS_MIN);
+ // update version number and stuff in the javascript files
+ replacePlaceholders(VIS_MIN);
- console.log('created ' + VIS_MIN);
+ console.log('created ' + VIS_MIN);
});
/**
@@ -142,18 +142,18 @@ task('minify', function () {
*/
desc('Test the library');
task('test', ['build'], function () {
- // TODO: use a testing suite for testing: nodeunit, mocha, tap, ...
- var filelist = new jake.FileList();
- filelist.include([
- './test/**/*.js'
- ]);
-
- var files = filelist.toArray();
- files.forEach(function (file) {
- require('./' + file);
- });
-
- console.log('Executed ' + files.length + ' test files successfully');
+ // TODO: use a testing suite for testing: nodeunit, mocha, tap, ...
+ var filelist = new jake.FileList();
+ filelist.include([
+ './test/**/*.js'
+ ]);
+
+ var files = filelist.toArray();
+ files.forEach(function (file) {
+ require('./' + file);
+ });
+
+ console.log('Executed ' + files.length + ' test files successfully');
});
/**
@@ -161,11 +161,11 @@ task('test', ['build'], function () {
* @param {String} filename
*/
var replacePlaceholders = function (filename) {
- replace({
- replacements: [
- {pattern: '@@date', replacement: today()},
- {pattern: '@@version', replacement: version()}
- ],
- src: filename
- });
+ replace({
+ replacements: [
+ {pattern: '@@date', replacement: today()},
+ {pattern: '@@version', replacement: version()}
+ ],
+ src: filename
+ });
};
diff --git a/README.md b/README.md
index ea589e3c..a97439b5 100644
--- a/README.md
+++ b/README.md
@@ -40,12 +40,12 @@ To use a component, include the javascript file of vis in your web page:
-
+
-
+
```
@@ -54,12 +54,12 @@ or load vis.js using require.js:
```js
require.config({
- paths: {
- vis: 'path/to/vis',
- }
+ paths: {
+ vis: 'path/to/vis',
+ }
});
require(['vis'], function (math) {
- // ... load a visualization
+ // ... load a visualization
});
```
@@ -85,30 +85,30 @@ of the project.
- Timeline basic demo
-
-
-
+ Timeline basic demo
+
+
+
diff --git a/bower.json b/bower.json
index 29dec11a..742c61d9 100644
--- a/bower.json
+++ b/bower.json
@@ -1,23 +1,23 @@
{
- "name": "vis",
- "version": "0.3.0-SNAPSHOT",
- "description": "A dynamic, browser-based visualization library.",
- "homepage": "http://visjs.org/",
- "repository": {
- "type": "git",
- "url": "git://github.com/almende/vis.git"
- },
- "ignore": [
- "node_modules",
- "src",
- "test",
- "tools",
- ".idea",
- "Jakefile.js",
- "package.json",
- ".npmignore",
- ".gitignore"
- ],
- "dependencies": {},
- "devDependencies": {}
+ "name": "vis",
+ "version": "0.3.0-SNAPSHOT",
+ "description": "A dynamic, browser-based visualization library.",
+ "homepage": "http://visjs.org/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/almende/vis.git"
+ },
+ "ignore": [
+ "node_modules",
+ "src",
+ "test",
+ "tools",
+ ".idea",
+ "Jakefile.js",
+ "package.json",
+ ".npmignore",
+ ".gitignore"
+ ],
+ "dependencies": {},
+ "devDependencies": {}
}
diff --git a/docs/css/prettify.css b/docs/css/prettify.css
index b4ec4ca0..3c7acd2e 100644
--- a/docs/css/prettify.css
+++ b/docs/css/prettify.css
@@ -1,87 +1,87 @@
.com {
- color: gray;
+ color: gray;
}
.lit {
- color: red;
+ color: red;
}
.pun {
- color: gray;
+ color: gray;
}
.pln {
- color: #333333;
+ color: #333333;
}
pre.prettyprint {
- border: 1px solid lightgray;
- background-color: #fcfcfc;
- padding: 5px;
+ border: 1px solid lightgray;
+ background-color: #fcfcfc;
+ padding: 5px;
- font-size: 10pt;
- line-height: 1.5em;
- font-family: monospace;
+ font-size: 10pt;
+ line-height: 1.5em;
+ font-family: monospace;
}
ol.linenums {
- margin-top:0;
- margin-bottom:0;
+ margin-top:0;
+ margin-bottom:0;
}
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 {
- list-style:none;
+ list-style:none;
}
li.L1,li.L3,li.L5,li.L7,li.L9 {
- background:#eee;
+ background:#eee;
}
.str,.atv {
- color: green;
+ color: green;
}
.kwd,.tag {
- color:#2B7CE9;
+ color:#2B7CE9;
}
.typ,.atn,.dec {
- color: darkorange;
+ color: darkorange;
}
@media print {
- .com {
- color:#600;
- font-style:italic;
- }
-
- .typ {
- color:#404;
- font-weight:700;
- }
-
- .lit {
- color:#044;
- }
-
- .pun {
- color:#440;
- }
-
- .pln {
- color:#000;
- }
-
- .atn {
- color:#404;
- }
-
- .str,.atv {
- color:#060;
- }
-
- .kwd,.tag {
- color:#006;
- font-weight:700;
- }
+ .com {
+ color:#600;
+ font-style:italic;
+ }
+
+ .typ {
+ color:#404;
+ font-weight:700;
+ }
+
+ .lit {
+ color:#044;
+ }
+
+ .pun {
+ color:#440;
+ }
+
+ .pln {
+ color:#000;
+ }
+
+ .atn {
+ color:#404;
+ }
+
+ .str,.atv {
+ color:#060;
+ }
+
+ .kwd,.tag {
+ color:#006;
+ font-weight:700;
+ }
}
\ No newline at end of file
diff --git a/docs/css/style.css b/docs/css/style.css
index 14ba2183..12b5ac45 100644
--- a/docs/css/style.css
+++ b/docs/css/style.css
@@ -1,77 +1,77 @@
html, body {
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
}
body, td, th {
- font-family: arial, sans-serif;
- font-size: 11pt;
- color: #4D4D4D;
- line-height: 1.7em;
+ font-family: arial, sans-serif;
+ font-size: 11pt;
+ color: #4D4D4D;
+ line-height: 1.7em;
}
#container {
- position: relative;
- margin: 0 auto;
- padding: 10px 10px 50px 10px;
- width: 700px;
- max-width: 100%;
- box-sizing: border-box;
+ position: relative;
+ margin: 0 auto;
+ padding: 10px 10px 50px 10px;
+ width: 700px;
+ max-width: 100%;
+ box-sizing: border-box;
}
h1 {
- font-size: 180%;
- font-weight: bold;
- padding: 0;
- margin: 1em 0 1em 0;
+ font-size: 180%;
+ font-weight: bold;
+ padding: 0;
+ margin: 1em 0 1em 0;
}
h2 {
- padding-top: 20px;
- padding-bottom: 10px;
- border-bottom: 1px solid #a0c0f0;
- color: #2B7CE9;
+ padding-top: 20px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #a0c0f0;
+ color: #2B7CE9;
}
h3 {
- font-size: 140%;
+ font-size: 140%;
}
a > img {
- border: none;
+ border: none;
}
a {
- color: #2B7CE9;
- text-decoration: none;
+ color: #2B7CE9;
+ text-decoration: none;
}
a:visited {
- color: #2E60A4;
+ color: #2E60A4;
}
a:hover {
- color: red;
- text-decoration: underline;
+ color: red;
+ text-decoration: underline;
}
table {
- border-collapse: collapse;
+ border-collapse: collapse;
}
th {
- font-weight: bold;
- border: 1px solid lightgray;
- background-color: #E5E5E5;
- text-align: left;
- vertical-align: top;
- padding: 5px;
+ font-weight: bold;
+ border: 1px solid lightgray;
+ background-color: #E5E5E5;
+ text-align: left;
+ vertical-align: top;
+ padding: 5px;
}
td {
- border: 1px solid lightgray;
- padding: 5px;
- vertical-align: top;
+ border: 1px solid lightgray;
+ padding: 5px;
+ vertical-align: top;
}
diff --git a/docs/dataset.html b/docs/dataset.html
index be0ee783..e6e8310b 100644
--- a/docs/dataset.html
+++ b/docs/dataset.html
@@ -2,50 +2,50 @@
- vis.js | DataSet documentation
+ vis.js | DataSet documentation
-
-
+
+
-
+
-
DataSet documentation
+
DataSet documentation
-
Contents
-
+
Contents
+
-
Overview
+
Overview
-
- Vis.js comes with a flexible DataSet, which can be used to hold and
- manipulate unstructured data and listen for changes in the data.
- The DataSet is key/value based. Data items can be added, updated and
- removed from the DatSet, and one can subscribe to changes in the DataSet.
- The data in the DataSet can be filtered and ordered, and fields (like
- dates) can be converted to a specific type. Data can be normalized when
- appending it to the DataSet as well.
-
+
+ Vis.js comes with a flexible DataSet, which can be used to hold and
+ manipulate unstructured data and listen for changes in the data.
+ The DataSet is key/value based. Data items can be added, updated and
+ removed from the DatSet, and one can subscribe to changes in the DataSet.
+ The data in the DataSet can be filtered and ordered, and fields (like
+ dates) can be converted to a specific type. Data can be normalized when
+ appending it to the DataSet as well.
+
-
Example
+
Example
-
- The following example shows how to use a DataSet.
-
+
+ The following example shows how to use a DataSet.
+
// create a DataSet
@@ -55,15 +55,15 @@ var data = new vis.DataSet(options);
// add items
// note that the data items can contain different properties and data formats
data.add([
- {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
- {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
- {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
- {id: 4, text: 'item 4'}
+ {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
+ {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
+ {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
+ {id: 4, text: 'item 4'}
]);
// subscribe to any change in the DataSet
data.subscribe('*', function (event, params, senderId) {
- console.log('event', event, params);
+ console.log('event', event, params);
});
// update an existing item
@@ -82,94 +82,94 @@ console.log('item1', item1);
// retrieve a filtered subset of the data
var items = data.get({
- filter: function (item) {
- return item.group == 1;
- }
+ filter: function (item) {
+ return item.group == 1;
+ }
});
console.log('filtered items', items);
// retrieve formatted items
var items = data.get({
- fields: ['id', 'date'],
- convert: {
- date: 'ISODate'
- }
+ fields: ['id', 'date'],
+ convert: {
+ date: 'ISODate'
+ }
});
console.log('formatted items', items);
-
Construction
+
Construction
-
- A DataSet can be constructed as:
-
+
+ A DataSet can be constructed as:
+
var data = new vis.DataSet(options)
-
- After construction, data can be added to the DataSet using the methods
- add
and update
, as described in section
- Data Manipulation .
-
-
-
- The parameter options
is optional and is an object which can
- contain the following properties:
-
-
-
-
- Name
- Type
- Default value
- Description
-
-
- fieldId
- String
- "id"
-
- The name of the field containing the id of the items.
-
- When data is fetched from a server which uses some specific
- field to identify items, this field name can be specified
- in the DataSet using the option fieldId
.
- For example CouchDB uses the field
- "_id"
to identify documents.
-
-
-
- convert
- Object.<String, String>
- none
-
- An object containing field names as key, and data types as
- value. By default, the type of the properties of items are left
- unchanged. Item properties can be normalized by specifying a
- field type. This is useful for example to automatically convert
- stringified dates coming from a server into JavaScript Date
- objects. The available data types are listed in section
- Data Types .
-
-
-
-
-
-
Data Manipulation
-
-
- The data in a DataSet can be manipulated using the methods
- add
,
- update
,
- and remove
.
- The DataSet can be emptied using the method
- clear
.
-
+
+ After construction, data can be added to the DataSet using the methods
+ add
and update
, as described in section
+ Data Manipulation .
+
+
+
+ The parameter options
is optional and is an object which can
+ contain the following properties:
+
+
+
+
+ Name
+ Type
+ Default value
+ Description
+
+
+ fieldId
+ String
+ "id"
+
+ The name of the field containing the id of the items.
+
+ When data is fetched from a server which uses some specific
+ field to identify items, this field name can be specified
+ in the DataSet using the option fieldId
.
+ For example CouchDB uses the field
+ "_id"
to identify documents.
+
+
+
+ convert
+ Object.<String, String>
+ none
+
+ An object containing field names as key, and data types as
+ value. By default, the type of the properties of items are left
+ unchanged. Item properties can be normalized by specifying a
+ field type. This is useful for example to automatically convert
+ stringified dates coming from a server into JavaScript Date
+ objects. The available data types are listed in section
+ Data Types .
+
+
+
+
+
+
Data Manipulation
+
+
+ The data in a DataSet can be manipulated using the methods
+ add
,
+ update
,
+ and remove
.
+ The DataSet can be emptied using the method
+ clear
.
+
// create a DataSet
@@ -177,9 +177,9 @@ var data = new vis.DataSet();
// add items
data.add([
- {id: 1, text: 'item 1'},
- {id: 2, text: 'item 2'},
- {id: 3, text: 'item 3'}
+ {id: 1, text: 'item 1'},
+ {id: 2, text: 'item 2'},
+ {id: 3, text: 'item 3'}
]);
// update an item
@@ -189,193 +189,193 @@ data.update({id: 2, text: 'item 2 (updated)'});
data.remove(3);
-
Add
-
-
- Add a data item or an array with items.
-
-
- Syntax:
-
var addedIds = DataSet.add(data [, senderId])
-
- The argument
data
can contain:
-
-
- An Object
containing a single item to be
- added. The item must contain an id.
-
-
- An Array
or
- google.visualization.DataTable
containing
- a list with items to be added. Each item must contain
- an id.
-
-
-
-
- After the items are added to the DataSet, the DataSet will
- trigger an event add
. When a senderId
- is provided, this id will be passed with the triggered
- event to all subscribers.
-
-
-
- The method will throw an Error when an item with the same id
- as any of the added items already exists.
-
-
-
Update
-
-
- Update a data item or an array with items.
-
-
- Syntax:
-
var updatedIds = DataSet.update(data [, senderId])
-
- The argument
data
can contain:
-
-
- An Object
containing a single item to be
- updated. The item must contain an id.
-
-
- An Array
or
- google.visualization.DataTable
containing
- a list with items to be updated. Each item must contain
- an id.
-
-
-
-
- The provided properties will be merged in the existing item.
- When an item does not exist, it will be created.
-
-
-
- After the items are updated, the DataSet will
- trigger an event add
for the added items, and
- an event update
. When a senderId
- is provided, this id will be passed with the triggered
- event to all subscribers.
-
-
-
Remove
-
-
- Remove a data item or an array with items.
-
-
- Syntax:
-
var removedIds = DataSet.remove(id [, senderId])
-
-
- The argument id
can be:
-
-
-
- A Number
or String
containing the id
- of a single item to be removed.
-
-
- An Object
containing the item to be deleted.
- The item will be deleted by its id.
-
-
- An Array containing ids or items to be removed.
-
-
-
-
- The method ignores removal of non-existing items, and returns an array
- containing the ids of the items which are actually removed from the
- DataSet.
-
-
-
- After the items are removed, the DataSet will
- trigger an event remove
for the removed items.
- When a senderId
is provided, this id will be passed with
- the triggered event to all subscribers.
-
-
-
-
Clear
-
-
- Clear the complete DataSet.
-
-
- Syntax:
-
var removedIds = DataSet.clear([senderId])
-
-
- After the items are removed, the DataSet will
- trigger an event remove
for all removed items.
- When a senderId
is provided, this id will be passed with
- the triggered event to all subscribers.
-
-
-
-
Data Filtering
-
-
- Data can be retrieved from the DataSet using the method get
.
- This method can return a single item or a list with items.
-
-
-
A single item can be retrieved by its id:
+
Add
+
+
+ Add a data item or an array with items.
+
+
+Syntax:
+
var addedIds = DataSet.add(data [, senderId])
+
+The argument
data
can contain:
+
+
+ An Object
containing a single item to be
+ added. The item must contain an id.
+
+
+ An Array
or
+ google.visualization.DataTable
containing
+ a list with items to be added. Each item must contain
+ an id.
+
+
+
+
+ After the items are added to the DataSet, the DataSet will
+ trigger an event add
. When a senderId
+ is provided, this id will be passed with the triggered
+ event to all subscribers.
+
+
+
+ The method will throw an Error when an item with the same id
+ as any of the added items already exists.
+
+
+
Update
+
+
+ Update a data item or an array with items.
+
+
+Syntax:
+
var updatedIds = DataSet.update(data [, senderId])
+
+The argument
data
can contain:
+
+
+ An Object
containing a single item to be
+ updated. The item must contain an id.
+
+
+ An Array
or
+ google.visualization.DataTable
containing
+ a list with items to be updated. Each item must contain
+ an id.
+
+
+
+
+ The provided properties will be merged in the existing item.
+ When an item does not exist, it will be created.
+
+
+
+ After the items are updated, the DataSet will
+ trigger an event add
for the added items, and
+ an event update
. When a senderId
+ is provided, this id will be passed with the triggered
+ event to all subscribers.
+
+
+
Remove
+
+
+ Remove a data item or an array with items.
+
+
+Syntax:
+
var removedIds = DataSet.remove(id [, senderId])
+
+
+ The argument id
can be:
+
+
+
+ A Number
or String
containing the id
+ of a single item to be removed.
+
+
+ An Object
containing the item to be deleted.
+ The item will be deleted by its id.
+
+
+ An Array containing ids or items to be removed.
+
+
+
+
+ The method ignores removal of non-existing items, and returns an array
+ containing the ids of the items which are actually removed from the
+ DataSet.
+
+
+
+ After the items are removed, the DataSet will
+ trigger an event remove
for the removed items.
+ When a senderId
is provided, this id will be passed with
+ the triggered event to all subscribers.
+
+
+
+
Clear
+
+
+ Clear the complete DataSet.
+
+
+Syntax:
+
var removedIds = DataSet.clear([senderId])
+
+
+ After the items are removed, the DataSet will
+ trigger an event remove
for all removed items.
+ When a senderId
is provided, this id will be passed with
+ the triggered event to all subscribers.
+
+
+
+
Data Filtering
+
+
+ Data can be retrieved from the DataSet using the method get
.
+ This method can return a single item or a list with items.
+
+
+
A single item can be retrieved by its id:
var item1 = dataset.get(1);
-
A selection of items can be retrieved by providing an array with ids:
+
A selection of items can be retrieved by providing an array with ids:
var items = dataset.get([1, 3, 4]); // retrieve items 1, 3, and 4
-
All items can be retrieved by simply calling get
without
- specifying an id:
+
All items can be retrieved by simply calling get
without
+ specifying an id:
var items = dataset.get(); // retrieve all items
-
- Items can be filtered on specific properties by providing a filter
- function. A filter function is executed for each of the items in the
- DataSet, and is called with the item as parameter. The function must
- return a boolean. All items for which the filter function returns
- true will be emitted.
-
+
+ Items can be filtered on specific properties by providing a filter
+ function. A filter function is executed for each of the items in the
+ DataSet, and is called with the item as parameter. The function must
+ return a boolean. All items for which the filter function returns
+ true will be emitted.
+
// retrieve all items having a property group with value 2
var group2 = dataset.get({
- filter: function (item) {
- return (item.group == 2);
- }
+ filter: function (item) {
+ return (item.group == 2);
+ }
});
// retrieve all items having a property balance with a value above zero
var positiveBalance = dataset.get({
- filter: function (item) {
- return (item.balance > 0);
- }
+ filter: function (item) {
+ return (item.balance > 0);
+ }
});
-
+
-
- The DataSet contains functionality to format data retrieved via the
- method get
. The method get
has the following
- syntax:
-
+
+ The DataSet contains functionality to format data retrieved via the
+ method get
. The method get
has the following
+ syntax:
+
var item = DataSet.get(id, options); // retrieve a single item
@@ -383,171 +383,171 @@ var items = DataSet.get(ids, options); // retrieve a selection of items
var items = DataSet.get(options); // retrieve all items or a filtered set
-
- Where options
is an Object which can have the following
- properties:
-
-
-
-
- Name
- Type
- Description
-
-
-
- fields
- String[ ]
-
- An array with field names.
- By default, all properties of the items are emitted.
- When fields
is defined, only the properties
- whose name is specified in fields
will be included
- in the returned items.
-
-
-
-
- convert
- Object.<String, String>
-
- An object containing field names as key, and data types as value.
- By default, the type of the properties of an item are left
- unchanged. When a field type is specified, this field in the
- items will be converted to the specified type. This can be used
- for example to convert ISO strings containing a date to a
- JavaScript Date object, or convert strings to numbers or vice
- versa. The available data types are listed in section
- Data Types .
-
-
-
-
- filter
- Function
- Items can be filtered on specific properties by providing a filter
- function. A filter function is executed for each of the items in the
- DataSet, and is called with the item as parameter. The function must
- return a boolean. All items for which the filter function returns
- true will be emitted.
- See section Data Filtering .
-
-
-
- order
- String | Function
- Order the items by a field name or custom sort function.
-
-
-
-
-
- The following example demonstrates formatting properties and filtering
- properties from items.
-
+
+ Where options
is an Object which can have the following
+ properties:
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+ fields
+ String[ ]
+
+ An array with field names.
+ By default, all properties of the items are emitted.
+ When fields
is defined, only the properties
+ whose name is specified in fields
will be included
+ in the returned items.
+
+
+
+
+ convert
+ Object.<String, String>
+
+ An object containing field names as key, and data types as value.
+ By default, the type of the properties of an item are left
+ unchanged. When a field type is specified, this field in the
+ items will be converted to the specified type. This can be used
+ for example to convert ISO strings containing a date to a
+ JavaScript Date object, or convert strings to numbers or vice
+ versa. The available data types are listed in section
+ Data Types .
+
+
+
+
+ filter
+ Function
+ Items can be filtered on specific properties by providing a filter
+ function. A filter function is executed for each of the items in the
+ DataSet, and is called with the item as parameter. The function must
+ return a boolean. All items for which the filter function returns
+ true will be emitted.
+ See section Data Filtering .
+
+
+
+ order
+ String | Function
+ Order the items by a field name or custom sort function.
+
+
+
+
+
+ The following example demonstrates formatting properties and filtering
+ properties from items.
+
// create a DataSet
var data = new vis.DataSet();
data.add([
- {id: 1, text: 'item 1', date: '2013-06-20', group: 1, first: true},
- {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
- {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
- {id: 4, text: 'item 4'}
+ {id: 1, text: 'item 1', date: '2013-06-20', group: 1, first: true},
+ {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
+ {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
+ {id: 4, text: 'item 4'}
]);
// retrieve formatted items
var items = data.get({
- fields: ['id', 'date', 'group'], // output the specified fields only
- convert: {
- date: 'Date', // convert the date fields to Date objects
- group: 'String' // convert the group fields to Strings
- }
+ fields: ['id', 'date', 'group'], // output the specified fields only
+ convert: {
+ date: 'Date', // convert the date fields to Date objects
+ group: 'String' // convert the group fields to Strings
+ }
});
-
Data Types
-
-
- DataSet supports the following data types:
-
-
-
-
- Name
- Description
- Examples
-
-
- Boolean
- A JavaScript Boolean
-
- true
- false
-
-
-
- Number
- A JavaScript Number
-
- 32
- 2.4
-
-
-
- String
- A JavaScript String
-
- "hello world"
- "2013-06-28"
-
-
-
- Date
- A JavaScript Date object
-
- new Date()
- new Date(2013, 5, 28)
- new Date(1372370400000)
-
-
-
- Moment
- A Moment object, created with
- moment.js
-
- moment()
- moment('2013-06-28')
-
-
-
- ISODate
- A string containing an ISO Date
-
- new Date().toISOString()
- "2013-06-27T22:00:00.000Z"
-
-
-
- ASPDate
- A string containing an ASP Date
-
- "/Date(1372370400000)/"
- "/Date(1198908717056-0700)/"
-
-
-
-
-
-
Subscriptions
-
-
- One can subscribe on changes in a DataSet.
- A subscription can be created using the method subscribe
,
- and removed with unsubscribe
.
-
+
Data Types
+
+
+ DataSet supports the following data types:
+
+
+
+
+ Name
+ Description
+ Examples
+
+
+ Boolean
+ A JavaScript Boolean
+
+ true
+ false
+
+
+
+ Number
+ A JavaScript Number
+
+ 32
+ 2.4
+
+
+
+ String
+ A JavaScript String
+
+ "hello world"
+ "2013-06-28"
+
+
+
+ Date
+ A JavaScript Date object
+
+ new Date()
+ new Date(2013, 5, 28)
+ new Date(1372370400000)
+
+
+
+ Moment
+ A Moment object, created with
+ moment.js
+
+ moment()
+ moment('2013-06-28')
+
+
+
+ ISODate
+ A string containing an ISO Date
+
+ new Date().toISOString()
+ "2013-06-27T22:00:00.000Z"
+
+
+
+ ASPDate
+ A string containing an ASP Date
+
+ "/Date(1372370400000)/"
+ "/Date(1198908717056-0700)/"
+
+
+
+
+
+
Subscriptions
+
+
+ One can subscribe on changes in a DataSet.
+ A subscription can be created using the method subscribe
,
+ and removed with unsubscribe
.
+
// create a DataSet
@@ -555,7 +555,7 @@ var data = new vis.DataSet();
// subscribe to any change in the DataSet
data.subscribe('*', function (event, params, senderId) {
- console.log('event:', event, 'params:', params, 'senderId:', senderId);
+ console.log('event:', event, 'params:', params, 'senderId:', senderId);
});
// add an item
@@ -565,144 +565,144 @@ data.remove(1); // triggers an 'remove' event
-
Subscribe
-
-
- Subscribe to an event.
-
-
- Syntax:
-
DataSet.subscribe(event, callback)
-
- Where:
-
-
- event
is a String containing any of the events listed
- in section Events .
-
-
- callback
is a callback function which will be called
- each time the event occurs. The callback function is described in
- section Callback .
-
-
-
-
Unsubscribe
-
-
- Unsubscribe from an event.
-
-
- Syntax:
-
DataSet.unsubscribe(event, callback)
-
- Where
event
and
callback
correspond with the
- parameters used to
subscribe to the event.
-
-
Events
-
-
- The following events are available for subscription:
-
-
-
-
- Event
- Description
-
-
- add
-
- The add
event is triggered when an item
- or a set of items is added, or when an item is updated while
- not yet existing.
-
-
-
- update
-
- The update
event is triggered when an existing item
- or a set of existing items is updated.
-
-
-
- remove
-
- The remove
event is triggered when an item
- or a set of items is removed.
-
-
-
- *
-
- The *
event is triggered when any of the events
- add
, update
, and remove
- occurs.
-
-
-
-
-
Callback
-
-
- The callback functions of subscribers are called with the following
- parameters:
-
+
Subscribe
+
+
+ Subscribe to an event.
+
+
+Syntax:
+
DataSet.subscribe(event, callback)
+
+Where:
+
+
+ event
is a String containing any of the events listed
+ in section Events .
+
+
+ callback
is a callback function which will be called
+ each time the event occurs. The callback function is described in
+ section Callback .
+
+
+
+
Unsubscribe
+
+
+ Unsubscribe from an event.
+
+
+Syntax:
+
DataSet.unsubscribe(event, callback)
+
+Where
event
and
callback
correspond with the
+parameters used to
subscribe to the event.
+
+
Events
+
+
+ The following events are available for subscription:
+
+
+
+
+ Event
+ Description
+
+
+ add
+
+ The add
event is triggered when an item
+ or a set of items is added, or when an item is updated while
+ not yet existing.
+
+
+
+ update
+
+ The update
event is triggered when an existing item
+ or a set of existing items is updated.
+
+
+
+ remove
+
+ The remove
event is triggered when an item
+ or a set of items is removed.
+
+
+
+ *
+
+ The *
event is triggered when any of the events
+ add
, update
, and remove
+ occurs.
+
+
+
+
+
Callback
+
+
+ The callback functions of subscribers are called with the following
+ parameters:
+
function (event, params, senderId) {
- // handle the event
+ // handle the event
});
-
- where the parameters are defined as
-
-
-
-
- Parameter
- Type
- Description
-
-
- event
- String
-
- Any of the available events: add
,
- update
, or remove
.
-
-
-
- params
- Object | null
-
- Optional parameters providing more information on the event.
- In case of the events add
,
- update
, and remove
,
- params
is always an object containing a property
- items, which contains an array with the ids of the affected
- items.
-
-
-
- senderId
- String | Number
-
- An senderId, optionally provided by the application code
- which triggered the event. If senderId is not provided, the
- argument will be null
.
-
-
-
-
-
-
-
Data Policy
-
- All code and data is processed and rendered in the browser.
- No data is sent to any server.
-
+
+ where the parameters are defined as
+
+
+
+
+ Parameter
+ Type
+ Description
+
+
+ event
+ String
+
+ Any of the available events: add
,
+ update
, or remove
.
+
+
+
+ params
+ Object | null
+
+ Optional parameters providing more information on the event.
+ In case of the events add
,
+ update
, and remove
,
+ params
is always an object containing a property
+ items, which contains an array with the ids of the affected
+ items.
+
+
+
+ senderId
+ String | Number
+
+ An senderId, optionally provided by the application code
+ which triggered the event. If senderId is not provided, the
+ argument will be null
.
+
+
+
+
+
+
+
Data Policy
+
+ All code and data is processed and rendered in the browser.
+ No data is sent to any server.
+
diff --git a/docs/dataview.html b/docs/dataview.html
index bb459d9c..1698ffb1 100644
--- a/docs/dataview.html
+++ b/docs/dataview.html
@@ -2,69 +2,69 @@
- vis.js | DataView documentation
+ vis.js | DataView documentation
-
-
+
+
-
+
-
DataView documentation
+
DataView documentation
-
Contents
-
+
Contents
+
-
Overview
+
Overview
-
- A DataView offers a filtered and/or formatted view on a
- DataSet .
- One can subscribe on changes in a DataView, and easily get filtered or
- formatted data without having to specify filters and field types all
- the time.
-
+
+ A DataView offers a filtered and/or formatted view on a
+ DataSet .
+ One can subscribe on changes in a DataView, and easily get filtered or
+ formatted data without having to specify filters and field types all
+ the time.
+
-
Example
+
Example
-
- The following example shows how to use a DataView.
-
+
+ The following example shows how to use a DataView.
+
// create a DataSet
var data = new vis.DataSet();
data.add([
- {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
- {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
- {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
- {id: 4, text: 'item 4'}
+ {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
+ {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
+ {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
+ {id: 4, text: 'item 4'}
]);
// create a DataView
// the view will only contain items having a property group with value 1,
// and will only output fields id, text, and date.
var view = new vis.DataView(data, {
- filter: function (item) {
- return (item.group == 1);
- },
- fields: ['id', 'text', 'date']
+ filter: function (item) {
+ return (item.group == 1);
+ },
+ fields: ['id', 'text', 'date']
});
// subscribe to any change in the DataView
view.subscribe('*', function (event, params, senderId) {
- console.log('event', event, params);
+ console.log('event', event, params);
});
// update an item in the data set
@@ -78,131 +78,131 @@ console.log('ids', ids); // will output [1, 2]
var items = view.get();
-
Construction
+
Construction
-
- A DataView can be constructed as:
-
+
+ A DataView can be constructed as:
+
var data = new vis.DataView(dataset, options)
-
- where:
-
-
-
-
- dataset
is a DataSet or DataView.
-
-
- options
is an object which can
- contain the following properties. Note that these properties
- are exactly the same as the properties available in methods
- DataSet.get
and DataView.get
.
-
-
-
-
-
- Name
- Type
- Description
-
-
-
- convert
- Object.<String, String>
-
- An object containing field names as key, and data types as value.
- By default, the type of the properties of an item are left
- unchanged. When a field type is specified, this field in the
- items will be converted to the specified type. This can be used
- for example to convert ISO strings containing a date to a
- JavaScript Date object, or convert strings to numbers or vice
- versa. The available data types are listed in section
- Data Types .
-
-
-
-
- fields
- String[ ]
-
- An array with field names.
- By default, all properties of the items are emitted.
- When fields
is defined, only the properties
- whose name is specified in fields
will be included
- in the returned items.
-
-
-
-
- filter
- function
- Items can be filtered on specific properties by providing a filter
- function. A filter function is executed for each of the items in the
- DataSet, and is called with the item as parameter. The function must
- return a boolean. All items for which the filter function returns
- true will be emitted.
- See also section Data Filtering .
-
-
-
-
-
-
-
Getting Data
-
-
- Data of the DataView can be retrieved using the method get
.
-
+
+ where:
+
+
+
+
+ dataset
is a DataSet or DataView.
+
+
+ options
is an object which can
+ contain the following properties. Note that these properties
+ are exactly the same as the properties available in methods
+ DataSet.get
and DataView.get
.
+
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+ convert
+ Object.<String, String>
+
+ An object containing field names as key, and data types as value.
+ By default, the type of the properties of an item are left
+ unchanged. When a field type is specified, this field in the
+ items will be converted to the specified type. This can be used
+ for example to convert ISO strings containing a date to a
+ JavaScript Date object, or convert strings to numbers or vice
+ versa. The available data types are listed in section
+ Data Types .
+
+
+
+
+ fields
+ String[ ]
+
+ An array with field names.
+ By default, all properties of the items are emitted.
+ When fields
is defined, only the properties
+ whose name is specified in fields
will be included
+ in the returned items.
+
+
+
+
+ filter
+ function
+ Items can be filtered on specific properties by providing a filter
+ function. A filter function is executed for each of the items in the
+ DataSet, and is called with the item as parameter. The function must
+ return a boolean. All items for which the filter function returns
+ true will be emitted.
+ See also section Data Filtering .
+
+
+
+
+
+
+
Getting Data
+
+
+ Data of the DataView can be retrieved using the method get
.
+
var items = view.get();
-
- Data of a DataView can be filtered and formatted again, in exactly the
- same way as in a DataSet. See sections
- Data Filtering and
- Data Formatting for more
- information.
-
+
+ Data of a DataView can be filtered and formatted again, in exactly the
+ same way as in a DataSet. See sections
+ Data Filtering and
+ Data Formatting for more
+ information.
+
var items = view.get({
- fields: ['id', 'score'],
- filter: function (item) {
- return (item.score > 50);
- }
+ fields: ['id', 'score'],
+ filter: function (item) {
+ return (item.score > 50);
+ }
});
-
Subscriptions
-
- One can subscribe on changes in the DataView. Subscription works exactly
- the same as for DataSets. See the documentation on
- subscriptions in a DataSet
- for more information.
-
+
Subscriptions
+
+ One can subscribe on changes in the DataView. Subscription works exactly
+ the same as for DataSets. See the documentation on
+ subscriptions in a DataSet
+ for more information.
+
// create a DataSet and a view on the data set
var data = new vis.DataSet();
var view = new vis.DataView({
- filter: function (item) {
- return (item.group == 2);
- }
+ filter: function (item) {
+ return (item.group == 2);
+ }
});
// subscribe to any change in the DataView
view.subscribe('*', function (event, params, senderId) {
- console.log('event:', event, 'params:', params, 'senderId:', senderId);
+ console.log('event:', event, 'params:', params, 'senderId:', senderId);
});
// add, update, and remove data in the DataSet...
@@ -210,11 +210,11 @@ view.subscribe('*', function (event, params, senderId) {
- Data Policy
-
- All code and data is processed and rendered in the browser.
- No data is sent to any server.
-
+Data Policy
+
+ All code and data is processed and rendered in the browser.
+ No data is sent to any server.
+
diff --git a/docs/graph.html b/docs/graph.html
index f196a732..fec8affb 100644
--- a/docs/graph.html
+++ b/docs/graph.html
@@ -2,12 +2,12 @@
- vis.js | graph documentation
+ vis.js | graph documentation
-
-
+
+
-
+
@@ -17,52 +17,52 @@
Contents
Overview
- Graph is a visualization to display graphs and networks consisting of nodes
- and edges. The visualization is easy to use and supports custom shapes,
- styles, colors, sizes, images, and more.
+ Graph is a visualization to display graphs and networks consisting of nodes
+ and edges. The visualization is easy to use and supports custom shapes,
+ styles, colors, sizes, images, and more.
- The graph visualization works smooth on any modern browser for up to a
- few hundred nodes and edges.
+ The graph visualization works smooth on any modern browser for up to a
+ few hundred nodes and edges.
- To get started with Graph, install or download the
- vis.js library.
+ To get started with Graph, install or download the
+ vis.js library.
Example
- Here a basic graph example. More examples can be found in the
- examples directory .
+ Here a basic graph example. More examples can be found in the
+ examples directory .
<!doctype html>
<html>
<head>
- <title>Graph | Basic usage</title>
+ <title>Graph | Basic usage</title>
- <script type="text/javascript" src="../../vis.js"></script>
+ <script type="text/javascript" src="../../vis.js"></script>
</head>
<body>
@@ -70,34 +70,34 @@
<div id="mygraph"></div>
<script type="text/javascript">
- // create an array with nodes
- var nodes = [
- {id: 1, label: 'Node 1'},
- {id: 2, label: 'Node 2'},
- {id: 3, label: 'Node 3'},
- {id: 4, label: 'Node 4'},
- {id: 5, label: 'Node 5'}
- ];
-
- // create an array with edges
- var edges = [
- {from: 1, to: 2},
- {from: 1, to: 3},
- {from: 2, to: 4},
- {from: 2, to: 5}
- ];
-
- // create a graph
- var container = document.getElementById('mygraph');
- var data= {
- nodes: nodes,
- edges: edges,
- };
- var options = {
- width: '400px',
- height: '400px'
- };
- var graph = new vis.Graph(container, data, options);
+ // create an array with nodes
+ var nodes = [
+ {id: 1, label: 'Node 1'},
+ {id: 2, label: 'Node 2'},
+ {id: 3, label: 'Node 3'},
+ {id: 4, label: 'Node 4'},
+ {id: 5, label: 'Node 5'}
+ ];
+
+ // create an array with edges
+ var edges = [
+ {from: 1, to: 2},
+ {from: 1, to: 3},
+ {from: 2, to: 4},
+ {from: 2, to: 5}
+ ];
+
+ // create a graph
+ var container = document.getElementById('mygraph');
+ var data= {
+ nodes: nodes,
+ edges: edges,
+ };
+ var options = {
+ width: '400px',
+ height: '400px'
+ };
+ var graph = new vis.Graph(container, data, options);
</script>
</body>
@@ -107,8 +107,8 @@
Loading
- Install or download the vis.js library.
- in a subfolder of your project. Include the library script in the head of your html code:
+ Install or download the vis.js library.
+ in a subfolder of your project. Include the library script in the head of your html code:
@@ -121,278 +121,278 @@ The constructor of the Graph is vis.Graph
.
The constructor accepts three parameters:
-
- container
is the DOM element in which to create the graph.
-
-
- data
is an Object containing properties nodes
and
- edges
, which both contain an array with objects.
- Optionally, data may contain an options
object.
- The parameter data
is optional, data can also be set using
- the method setData
. Section Data Format
- describes the data object.
-
-
- options
is an optional Object containing a name-value map
- with options. Options can also be set using the method
- setOptions
.
- Section Configuration Options
- describes the available options.
-
+
+ container
is the DOM element in which to create the graph.
+
+
+ data
is an Object containing properties nodes
and
+ edges
, which both contain an array with objects.
+ Optionally, data may contain an options
object.
+ The parameter data
is optional, data can also be set using
+ the method setData
. Section Data Format
+ describes the data object.
+
+
+ options
is an optional Object containing a name-value map
+ with options. Options can also be set using the method
+ setOptions
.
+ Section Configuration Options
+ describes the available options.
+
- The data
parameter of the Graph constructor is an object
- which can contain different types of data.
- The following properties are supported in the data
object:
+ The data
parameter of the Graph constructor is an object
+ which can contain different types of data.
+ The following properties are supported in the data
object:
-
- A property pair nodes
and edges
,
- both containing an Array with objects. The data formats are described
- in the sections Nodes and Edges .
- Example:
+
+ A property pair nodes
and edges
,
+ both containing an Array with objects. The data formats are described
+ in the sections Nodes and Edges .
+ Example:
var data = {
- nodes: [...],
- edges: [...]
+ nodes: [...],
+ edges: [...]
};
-
-
- A property dot
,
- containing a string with data in the
- DOT language .
- DOT support is described in section DOT_language .
-
- Example:
+
+
+ A property dot
,
+ containing a string with data in the
+ DOT language .
+ DOT support is described in section DOT_language .
+
+ Example:
var data = {
- dot: '...'
+ dot: '...'
};
-
-
- A property options
,
- containing an object with global options.
- Options can be provided as third parameter in the graph constructor
- as well. Section Configuration Options
- describes the available options.
-
-
+
+
+ A property options
,
+ containing an object with global options.
+ Options can be provided as third parameter in the graph constructor
+ as well. Section Configuration Options
+ describes the available options.
+
+
Nodes
- Nodes typically have an id
and label
.
- A node must contain at least a property id
.
- Nodes can have extra properties, used to define the shape and style of the
- nodes.
+ Nodes typically have an id
and label
.
+ A node must contain at least a property id
.
+ Nodes can have extra properties, used to define the shape and style of the
+ nodes.
- A JavaScript Array with nodes is constructed like:
+ A JavaScript Array with nodes is constructed like:
var nodes = [
- {
- id: 1,
- label: 'Node 1'
- },
- // ... more nodes
+ {
+ id: 1,
+ label: 'Node 1'
+ },
+ // ... more nodes
];
- Nodes support the following properties:
+ Nodes support the following properties:
-
- Name
- Type
- Required
- Description
-
-
-
- color
- String | Object
- no
- Color for the node.
-
-
-
- color.background
- String
- no
- Background color for the node.
-
-
-
- color.border
- String
- no
- Border color for the node.
-
-
-
- color.highlight
- String | Object
- no
- Color of the node when selected.
-
-
-
- color.highlight.background
- String
- no
- Background color of the node when selected.
-
-
-
- color.highlight.border
- String
- no
- Border color of the node when selected.
-
-
-
- group
- Number | String
- no
- A group number or name. The type can be number
,
- string
, or an other type. All nodes with the same group get
- the same color schema.
-
-
-
- fontColor
- String
- no
- Font color for label in the node.
-
-
-
- fontFace
- String
- no
- Font face for label in the node, for example "verdana" or "arial".
-
-
-
- fontSize
- Number
- no
- Font size in pixels for label in the node.
-
-
-
- id
- Number | String
- yes
- A unique id for this node.
- Nodes may not have duplicate id's.
- Id's do not need to be consecutive.
- An id is normally a number, but may be any type.
-
-
-
- image
- string
- no
- Url of an image. Only applicable when the shape of the node is
- image
.
-
-
-
- radius
- number
- no
- Radius for the node. Applicable for all shapes except box
,
- circle
, ellipse
and database
.
- The value of radius
will override a value in
- property value
.
-
-
-
- shape
- string
- no
- Define the shape for the node.
- Choose from
- ellipse
(default), circle
, box
,
- database
, image
, label
, dot
,
- star
, triangle
, triangleDown
, and square
.
-
-
- In case of image
, a property with name image
must
- be provided, containing image urls.
-
-
- The shapes dot
, star
, triangle
,
- triangleDown
, and square
, are scalable.
- The size is determined by the properties radius
or
- value
.
-
-
- When a property label
is provided,
- this label will be displayed inside the shape in case of shapes
- box
, circle
, ellipse
,
- and database
.
- For all other shapes, the label will be displayed right below the shape.
-
-
-
-
-
- label
- string
- no
- Text label to be displayed in the node or under the image of the node.
- Multiple lines can be separated by a newline character \n
.
-
-
-
- title
- string
- no
- Title to be displayed when the user hovers over the node.
- The title can contain HTML code.
-
-
-
- value
- number
- no
- A value for the node.
- The radius of the nodes will be scaled automatically from minimum to
- maximum value.
- Only applicable when the shape of the node is dot
.
- If a radius
is provided for the node too, it will override the
- radius calculated from the value.
-
-
-
- x
- number
- no
- Horizontal position in pixels.
- The horizontal position of the node will be fixed.
- The vertical position y may remain undefined.
-
-
- y
- number
- no
- Vertical position in pixels.
- The vertical position of the node will be fixed.
- The horizontal position x may remain undefined.
-
+
+ Name
+ Type
+ Required
+ Description
+
+
+
+ color
+ String | Object
+ no
+ Color for the node.
+
+
+
+ color.background
+ String
+ no
+ Background color for the node.
+
+
+
+ color.border
+ String
+ no
+ Border color for the node.
+
+
+
+ color.highlight
+ String | Object
+ no
+ Color of the node when selected.
+
+
+
+ color.highlight.background
+ String
+ no
+ Background color of the node when selected.
+
+
+
+ color.highlight.border
+ String
+ no
+ Border color of the node when selected.
+
+
+
+ group
+ Number | String
+ no
+ A group number or name. The type can be number
,
+ string
, or an other type. All nodes with the same group get
+ the same color schema.
+
+
+
+ fontColor
+ String
+ no
+ Font color for label in the node.
+
+
+
+ fontFace
+ String
+ no
+ Font face for label in the node, for example "verdana" or "arial".
+
+
+
+ fontSize
+ Number
+ no
+ Font size in pixels for label in the node.
+
+
+
+ id
+ Number | String
+ yes
+ A unique id for this node.
+ Nodes may not have duplicate id's.
+ Id's do not need to be consecutive.
+ An id is normally a number, but may be any type.
+
+
+
+ image
+ string
+ no
+ Url of an image. Only applicable when the shape of the node is
+ image
.
+
+
+
+ radius
+ number
+ no
+ Radius for the node. Applicable for all shapes except box
,
+ circle
, ellipse
and database
.
+ The value of radius
will override a value in
+ property value
.
+
+
+
+ shape
+ string
+ no
+ Define the shape for the node.
+ Choose from
+ ellipse
(default), circle
, box
,
+ database
, image
, label
, dot
,
+ star
, triangle
, triangleDown
, and square
.
+
+
+ In case of image
, a property with name image
must
+ be provided, containing image urls.
+
+
+ The shapes dot
, star
, triangle
,
+ triangleDown
, and square
, are scalable.
+ The size is determined by the properties radius
or
+ value
.
+
+
+ When a property label
is provided,
+ this label will be displayed inside the shape in case of shapes
+ box
, circle
, ellipse
,
+ and database
.
+ For all other shapes, the label will be displayed right below the shape.
+
+
+
+
+
+ label
+ string
+ no
+ Text label to be displayed in the node or under the image of the node.
+ Multiple lines can be separated by a newline character \n
.
+
+
+
+ title
+ string
+ no
+ Title to be displayed when the user hovers over the node.
+ The title can contain HTML code.
+
+
+
+ value
+ number
+ no
+ A value for the node.
+ The radius of the nodes will be scaled automatically from minimum to
+ maximum value.
+ Only applicable when the shape of the node is dot
.
+ If a radius
is provided for the node too, it will override the
+ radius calculated from the value.
+
+
+
+ x
+ number
+ no
+ Horizontal position in pixels.
+ The horizontal position of the node will be fixed.
+ The vertical position y may remain undefined.
+
+
+ y
+ number
+ no
+ Vertical position in pixels.
+ The vertical position of the node will be fixed.
+ The horizontal position x may remain undefined.
+
@@ -400,176 +400,176 @@ var nodes = [
Edges
- Edges are connections between nodes.
- An edge must at least contain properties from
and
- to
, both referring to the id
of a node.
- Edges can have extra properties, used to define the type and style.
+ Edges are connections between nodes.
+ An edge must at least contain properties from
and
+ to
, both referring to the id
of a node.
+ Edges can have extra properties, used to define the type and style.
- A JavaScript Array with edges is constructed as:
+ A JavaScript Array with edges is constructed as:
var edges = [
- {
- from: 1,
- to: 3
- },
- // ... more edges
+ {
+ from: 1,
+ to: 3
+ },
+ // ... more edges
];
- Edges support the following properties:
+ Edges support the following properties:
-
- Name
- Type
- Required
- Description
-
-
-
- color
- string
- no
- A HTML color for the edge.
-
-
-
- dash
- Object
- no
-
- Object containing properties for dashed lines.
- Available properties: length
, gap
,
- altLength
.
-
-
-
-
- dash.altLength
- number
- no
- Length of the alternated dash in pixels on a dashed line.
- Specifying dash.altLength
allows for creating
- a dashed line with a dash-dot style, for example when
- dash.length=10
and dash.altLength=5
.
- See also the option dahs.length
.
- Only applicable when the line style is dash-line
.
-
-
-
- dash.length
- number
- no
- Length of a dash in pixels on a dashed line.
- Only applicable when the line style is dash-line
.
-
-
-
- dash.gap
- number
- no
- Length of a gap in pixels on a dashed line.
- Only applicable when the line style is dash-line
.
-
-
-
- fontColor
- String
- no
- Font color for the text label of the edge.
- Only applicable when property label
is defined.
-
-
-
- fontFace
- String
- no
- Font face for the text label of the edge,
- for example "verdana" or "arial".
- Only applicable when property label
is defined.
-
-
-
- fontSize
- Number
- no
- Font size in pixels for the text label of the edge.
- Only applicable when property label
is defined.
-
-
-
- from
- Number | String
- yes
- The id of a node where the edge starts. The type must correspond with
- the type of the node id's. This is normally a number, but can be any
- type.
-
-
-
- length
- number
- no
- The length of the edge in pixels.
-
-
-
- style
- string
- no
- Define a line style for the edge.
- Choose from line
(default), arrow
,
- arrow-center
, or dash-line
.
-
-
-
-
- label
- string
- no
- Text label to be displayed halfway the edge.
-
-
-
- title
- string
- no
- Title to be displayed when the user hovers over the edge.
- The title can contain HTML code.
-
-
-
- to
- Number | String
- yes
- The id of a node where the edge ends. The type must correspond with
- the type of the node id's. This is normally a number, but can be any
- type.
-
-
- value
- number
- no
- A value for the edge.
- The width of the edges will be scaled automatically from minimum to
- maximum value.
- If a width
is provided for the edge too, it will override the
- width calculated from the value.
-
-
-
- width
- number
- no
- Width of the line in pixels. The width
will
- override a specified value
, if a value
is
- specified too.
-
+
+ Name
+ Type
+ Required
+ Description
+
+
+
+ color
+ string
+ no
+ A HTML color for the edge.
+
+
+
+ dash
+ Object
+ no
+
+ Object containing properties for dashed lines.
+ Available properties: length
, gap
,
+ altLength
.
+
+
+
+
+ dash.altLength
+ number
+ no
+ Length of the alternated dash in pixels on a dashed line.
+ Specifying dash.altLength
allows for creating
+ a dashed line with a dash-dot style, for example when
+ dash.length=10
and dash.altLength=5
.
+ See also the option dahs.length
.
+ Only applicable when the line style is dash-line
.
+
+
+
+ dash.length
+ number
+ no
+ Length of a dash in pixels on a dashed line.
+ Only applicable when the line style is dash-line
.
+
+
+
+ dash.gap
+ number
+ no
+ Length of a gap in pixels on a dashed line.
+ Only applicable when the line style is dash-line
.
+
+
+
+ fontColor
+ String
+ no
+ Font color for the text label of the edge.
+ Only applicable when property label
is defined.
+
+
+
+ fontFace
+ String
+ no
+ Font face for the text label of the edge,
+ for example "verdana" or "arial".
+ Only applicable when property label
is defined.
+
+
+
+ fontSize
+ Number
+ no
+ Font size in pixels for the text label of the edge.
+ Only applicable when property label
is defined.
+
+
+
+ from
+ Number | String
+ yes
+ The id of a node where the edge starts. The type must correspond with
+ the type of the node id's. This is normally a number, but can be any
+ type.
+
+
+
+ length
+ number
+ no
+ The length of the edge in pixels.
+
+
+
+ style
+ string
+ no
+ Define a line style for the edge.
+ Choose from line
(default), arrow
,
+ arrow-center
, or dash-line
.
+
+
+
+
+ label
+ string
+ no
+ Text label to be displayed halfway the edge.
+
+
+
+ title
+ string
+ no
+ Title to be displayed when the user hovers over the edge.
+ The title can contain HTML code.
+
+
+
+ to
+ Number | String
+ yes
+ The id of a node where the edge ends. The type must correspond with
+ the type of the node id's. This is normally a number, but can be any
+ type.
+
+
+ value
+ number
+ no
+ A value for the edge.
+ The width of the edges will be scaled automatically from minimum to
+ maximum value.
+ If a width
is provided for the edge too, it will override the
+ width calculated from the value.
+
+
+
+ width
+ number
+ no
+ Width of the line in pixels. The width
will
+ override a specified value
, if a value
is
+ specified too.
+
@@ -577,20 +577,20 @@ var edges = [
DOT language
- Graph supports data in the
- DOT language .
- To provide data in the DOT language, the data
object must contain
- a property dot
with a String containing the data.
+ Graph supports data in the
+ DOT language .
+ To provide data in the DOT language, the data
object must contain
+ a property dot
with a String containing the data.
- Example usage:
+ Example usage:
// provide data in the DOT language
var data = {
- dot: 'digraph {1 -> 1 -> 2; 2 -> 3; 2 -- 4; 2 -> 1 }'
+ dot: 'digraph {1 -> 1 -> 2; 2 -> 3; 2 -- 4; 2 -> 1 }'
};
// create a graph
@@ -602,252 +602,252 @@ var graph = new vis.Graph(container, data);
Configuration Options
- Options can be used to customize the graph. Options are defined as a JSON object.
- All options are optional.
+ Options can be used to customize the graph. Options are defined as a JSON object.
+ All options are optional.
var options = {
- width: '100%',
- height: '400px',
- edges: {
- color: 'red',
- width: 2
- }
+ width: '100%',
+ height: '400px',
+ edges: {
+ color: 'red',
+ width: 2
+ }
};
- The following options are available.
+ The following options are available.
- Name
- Type
- Default
- Description
+ Name
+ Type
+ Default
+ Description
- edges.color
- String
- "#2B7CE9"
- The default color of a edge.
+ edges.color
+ String
+ "#2B7CE9"
+ The default color of a edge.
- edges.dash
- Object
- Object
-
- Object containing default properties for dashed lines.
- Available properties: length
, gap
,
- altLength
.
-
+ edges.dash
+ Object
+ Object
+
+ Object containing default properties for dashed lines.
+ Available properties: length
, gap
,
+ altLength
.
+
- edges.dash.altLength
- number
- none
- Default length of the alternated dash in pixels on a dashed line.
- Specifying dash.altLength
allows for creating
- a dashed line with a dash-dot style, for example when
- dash.length=10
and dash.altLength=5
.
- See also the option dahs.length
.
- Only applicable when the line style is dash-line
.
+ edges.dash.altLength
+ number
+ none
+ Default length of the alternated dash in pixels on a dashed line.
+ Specifying dash.altLength
allows for creating
+ a dashed line with a dash-dot style, for example when
+ dash.length=10
and dash.altLength=5
.
+ See also the option dahs.length
.
+ Only applicable when the line style is dash-line
.
- edges.dash.length
- number
- 10
- Default length of a dash in pixels on a dashed line.
- Only applicable when the line style is dash-line
.
+ edges.dash.length
+ number
+ 10
+ Default length of a dash in pixels on a dashed line.
+ Only applicable when the line style is dash-line
.
- edges.dash.gap
- number
- 5
- Default length of a gap in pixels on a dashed line.
- Only applicable when the line style is dash-line
.
+ edges.dash.gap
+ number
+ 5
+ Default length of a gap in pixels on a dashed line.
+ Only applicable when the line style is dash-line
.
- edges.length
- Number
- 100
- The default length of a edge.
+ edges.length
+ Number
+ 100
+ The default length of a edge.
- edges.style
- String
- "line"
- The default style of a edge.
- Choose from line
(default), arrow
,
- arrow-center
, dash-line
.
+ edges.style
+ String
+ "line"
+ The default style of a edge.
+ Choose from line
(default), arrow
,
+ arrow-center
, dash-line
.
- edges.width
- Number
- 1
- The default width of a edge.
+ edges.width
+ Number
+ 1
+ The default width of a edge.
- groups
- Object
- none
- It is possible to specify custom styles for groups.
- Each node assigned a group gets the specified style.
- See Groups for an overview of the available styles
- and an example.
-
+ groups
+ Object
+ none
+ It is possible to specify custom styles for groups.
+ Each node assigned a group gets the specified style.
+ See Groups for an overview of the available styles
+ and an example.
+
- height
- String
- "400px"
- The height of the graph in pixels or as a percentage.
+ height
+ String
+ "400px"
+ The height of the graph in pixels or as a percentage.
- nodes.color
- String | Object
- Object
- Default color of the nodes. When color is a string, the color is applied
+ nodes.color
+ String | Object
+ Object
+ Default color of the nodes. When color is a string, the color is applied
to both background as well as the border of the node.
- nodes.color.background
- String
- "#97C2FC"
- Default background color of the nodes
+ nodes.color.background
+ String
+ "#97C2FC"
+ Default background color of the nodes
- nodes.color.border
- String
- "#2B7CE9"
- Default border color of the nodes
+ nodes.color.border
+ String
+ "#2B7CE9"
+ Default border color of the nodes
- nodes.color.highlight
- String | Object
- Object
- Default color of the node when the node is selected. Applied to
+ nodes.color.highlight
+ String | Object
+ Object
+ Default color of the node when the node is selected. Applied to
both border and background of the node.
- nodes.color.highlight.background
- String
- "#D2E5FF"
- Default background color of the node when selected.
+ nodes.color.highlight.background
+ String
+ "#D2E5FF"
+ Default background color of the node when selected.
- nodes.color.highlight.border
- String
- "#2B7CE9"
- Default border color of the node when selected.
+ nodes.color.highlight.border
+ String
+ "#2B7CE9"
+ Default border color of the node when selected.
- nodes.fontColor
- String
- "black"
- Default font color for the text label in the nodes.
+ nodes.fontColor
+ String
+ "black"
+ Default font color for the text label in the nodes.
- nodes.fontFace
- String
- "sans"
- Default font face for the text label in the nodes, for example "verdana" or "arial".
+ nodes.fontFace
+ String
+ "sans"
+ Default font face for the text label in the nodes, for example "verdana" or "arial".
- nodes.fontSize
- Number
- 14
- Default font size in pixels for the text label in the nodes.
+ nodes.fontSize
+ Number
+ 14
+ Default font size in pixels for the text label in the nodes.
- nodes.group
- String
- none
- Default group for the nodes.
+ nodes.group
+ String
+ none
+ Default group for the nodes.
- nodes.image
- String
- none
- Default image url for the nodes. only applicable with shape image
.
+ nodes.image
+ String
+ none
+ Default image url for the nodes. only applicable with shape image
.
- nodes.widthMin
- Number
- 16
- The minimum width for a scaled image. Only applicable with shape image
.
+ nodes.widthMin
+ Number
+ 16
+ The minimum width for a scaled image. Only applicable with shape image
.
- nodes.widthMax
- Number
- 64
- The maximum width for a scaled image. Only applicable with shape image
.
+ nodes.widthMax
+ Number
+ 64
+ The maximum width for a scaled image. Only applicable with shape image
.
- nodes.shape
- String
- "ellipse"
- The default shape for all nodes.
- Choose from
- ellipse
(default), circle
, box
,
- database
, image
, label
, dot
,
- star
, triangle
, triangleDown
, and square
.
- This shape can be overridden by a group shape, or by a shape of an individual node.
+ nodes.shape
+ String
+ "ellipse"
+ The default shape for all nodes.
+ Choose from
+ ellipse
(default), circle
, box
,
+ database
, image
, label
, dot
,
+ star
, triangle
, triangleDown
, and square
.
+ This shape can be overridden by a group shape, or by a shape of an individual node.
- nodes.radius
- Number
- 5
- The default radius for a node. Only applicable with shape dot
.
+ nodes.radius
+ Number
+ 5
+ The default radius for a node. Only applicable with shape dot
.
- nodes.radiusMin
- Number
- 5
- The minimum radius for a scaled node. Only applicable with shape dot
.
+ nodes.radiusMin
+ Number
+ 5
+ The minimum radius for a scaled node. Only applicable with shape dot
.
- nodes.radiusMax
- Number
- 20
- The maximum radius for a scaled node. Only applicable with shape dot
.
+ nodes.radiusMax
+ Number
+ 20
+ The maximum radius for a scaled node. Only applicable with shape dot
.
- selectable
- Boolean
- true
- If true, nodes in the graph can be selected by clicking them.
- Long press can be used to select multiple nodes.
+ selectable
+ Boolean
+ true
+ If true, nodes in the graph can be selected by clicking them.
+ Long press can be used to select multiple nodes.
- stabilize
- Boolean
- true
- If true, the graph is stabilized before displaying it. If false,
- the nodes move to a stabe position visibly in an animated way.
+ stabilize
+ Boolean
+ true
+ If true, the graph is stabilized before displaying it. If false,
+ the nodes move to a stabe position visibly in an animated way.
- width
- String
- "400px"
- The width of the graph in pixels or as a percentage.
+ width
+ String
+ "400px"
+ The width of the graph in pixels or as a percentage.
@@ -857,254 +857,254 @@ var options = {
Groups
It is possible to specify custom styles for groups of nodes.
- Each node having assigned to this group gets the specified style.
- The options groups
is an object containing one or multiple groups,
- identified by a unique string, the groupname.
+ Each node having assigned to this group gets the specified style.
+ The options groups
is an object containing one or multiple groups,
+ identified by a unique string, the groupname.
- A group can have the following styles:
+ A group can have the following styles:
var options = {
- // ...
-
- groups: {
- mygroup: {
- shape: 'circle',
- color: {
- border: 'black',
- background: 'white',
- highlight: {
- border: 'yellow',
- background: 'orange'
- }
- }
- fontColor: 'red',
- fontSize: 18
+ // ...
+
+ groups: {
+ mygroup: {
+ shape: 'circle',
+ color: {
+ border: 'black',
+ background: 'white',
+ highlight: {
+ border: 'yellow',
+ background: 'orange'
}
- // add more groups here
+ }
+ fontColor: 'red',
+ fontSize: 18
}
+ // add more groups here
+ }
};
var nodes = [
- {id: 1, label: 'Node 1'}, // will get the default style
- {id: 2, label: 'Node 2', group: 'mygroup'}, // will get the style from 'mygroup'
- // ... more nodes
+ {id: 1, label: 'Node 1'}, // will get the default style
+ {id: 2, label: 'Node 2', group: 'mygroup'}, // will get the style from 'mygroup'
+ // ... more nodes
];
The following styles are available for groups:
-
- Name
- Type
- Default
- Description
-
-
-
- color
- String | Object
- Object
- Color of the node
-
-
-
- color.border
- String
- "#2B7CE9"
- Border color of the node
-
-
-
- color.background
- String
- "#97C2FC"
- Background color of the node
-
-
- color.highlight
- String
- "#D2E5FF"
- Color of the node when selected.
-
-
- color.highlight.background
- String
- "#D2E5FF"
- Background color of the node when selected.
-
-
- color.highlight.border
- String
- "#D2E5FF"
- Border color of the node when selected.
-
-
- image
- String
- none
- Default image for the nodes. Only applicable in combination with
- shape image
.
-
-
-
- fontColor
- String
- "black"
- Font color of the node.
-
-
- fontFace
- String
- "sans"
- Font name of the node, for example "verdana" or "arial".
-
-
- fontSize
- Number
- 14
- Font size for the node in pixels.
-
-
- shape
- String
- "ellipse"
- Choose from
- ellipse
(default), circle
, box
,
- database
, image
, label
, dot
,
- star
, triangle
, triangleDown
, and square
.
- In case of image, a property with name image must be provided, containing
- image urls.
-
-
- radius
- Number
- 5
- Default radius for the node. Only applicable in combination with
- shapes box
and dot
.
-
+
+ Name
+ Type
+ Default
+ Description
+
+
+
+ color
+ String | Object
+ Object
+ Color of the node
+
+
+
+ color.border
+ String
+ "#2B7CE9"
+ Border color of the node
+
+
+
+ color.background
+ String
+ "#97C2FC"
+ Background color of the node
+
+
+ color.highlight
+ String
+ "#D2E5FF"
+ Color of the node when selected.
+
+
+ color.highlight.background
+ String
+ "#D2E5FF"
+ Background color of the node when selected.
+
+
+ color.highlight.border
+ String
+ "#D2E5FF"
+ Border color of the node when selected.
+
+
+ image
+ String
+ none
+ Default image for the nodes. Only applicable in combination with
+ shape image
.
+
+
+
+ fontColor
+ String
+ "black"
+ Font color of the node.
+
+
+ fontFace
+ String
+ "sans"
+ Font name of the node, for example "verdana" or "arial".
+
+
+ fontSize
+ Number
+ 14
+ Font size for the node in pixels.
+
+
+ shape
+ String
+ "ellipse"
+ Choose from
+ ellipse
(default), circle
, box
,
+ database
, image
, label
, dot
,
+ star
, triangle
, triangleDown
, and square
.
+ In case of image, a property with name image must be provided, containing
+ image urls.
+
+
+ radius
+ Number
+ 5
+ Default radius for the node. Only applicable in combination with
+ shapes box
and dot
.
+
Methods
- Graph supports the following methods.
+ Graph supports the following methods.
-
- Method
- Return Type
- Description
-
-
-
- setData(data)
- none
- Loads data. Parameter data
is an object containing
- nodes, edges, and options. Parameters nodes, edges are an Array.
- Options is a name-value map and is optional.
-
-
-
-
- setOptions(options)
- none
- Set options for the graph. The available options are described in
- the section Configuration Options .
-
-
-
-
- getSelection()
- Array of ids
- Returns an array with the ids of the selected nodes.
- Returns an empty array if no nodes are selected.
- The selections are not ordered.
-
-
-
-
- redraw()
- none
- Redraw the graph. Useful when the layout of the webpage changed.
-
-
-
- setSelection(selection)
- none
- Select nodes.
- selection
is an array with ids of nodes to be selected.
- The array selection
can contain zero or multiple ids.
- Example usage: graph.setSelection([3, 5]);
will select
- nodes with id 3 and 5.
-
-
-
-
- setSize(width, height)
- none
- Parameters width
and height
are strings,
- containing a new size for the visualization. Size can be provided in pixels
- or in percentages.
-
+
+ Method
+ Return Type
+ Description
+
+
+
+ setData(data)
+ none
+ Loads data. Parameter data
is an object containing
+ nodes, edges, and options. Parameters nodes, edges are an Array.
+ Options is a name-value map and is optional.
+
+
+
+
+ setOptions(options)
+ none
+ Set options for the graph. The available options are described in
+ the section Configuration Options .
+
+
+
+
+ getSelection()
+ Array of ids
+ Returns an array with the ids of the selected nodes.
+ Returns an empty array if no nodes are selected.
+ The selections are not ordered.
+
+
+
+
+ redraw()
+ none
+ Redraw the graph. Useful when the layout of the webpage changed.
+
+
+
+ setSelection(selection)
+ none
+ Select nodes.
+ selection
is an array with ids of nodes to be selected.
+ The array selection
can contain zero or multiple ids.
+ Example usage: graph.setSelection([3, 5]);
will select
+ nodes with id 3 and 5.
+
+
+
+
+ setSize(width, height)
+ none
+ Parameters width
and height
are strings,
+ containing a new size for the visualization. Size can be provided in pixels
+ or in percentages.
+
Events
- Graph fires events after one or multiple nodes are selected.
- The event can be catched by creating a listener.
+ Graph fires events after one or multiple nodes are selected.
+ The event can be catched by creating a listener.
- Here an example on how to catch a select
event.
+ Here an example on how to catch a select
event.
function onSelect() {
- alert('selected nodes: ' + graph.getSelection());
+ alert('selected nodes: ' + graph.getSelection());
}
vis.events.addListener(graph, 'select', onSelect);
- The following events are available.
+ The following events are available.
-
-
-
-
-
- name
- Description
- Properties
-
-
-
- select
- Fired after the user selects or unselects a node by clicking it,
- or when selecting a number of nodes by dragging a selection area
- around them. Not fired when the method setSelection
- is executed. The ids of the selected nodes can be retrieved via the
- method getSelection
.
-
- none
-
+
+
+
+
+
+ name
+ Description
+ Properties
+
+
+
+ select
+ Fired after the user selects or unselects a node by clicking it,
+ or when selecting a number of nodes by dragging a selection area
+ around them. Not fired when the method setSelection
+ is executed. The ids of the selected nodes can be retrieved via the
+ method getSelection
.
+
+ none
+
Data Policy
- All code and data is processed and rendered in the browser.
- No data is sent to any server.
+ All code and data is processed and rendered in the browser.
+ No data is sent to any server.
diff --git a/docs/index.html b/docs/index.html
index 6f907a0f..8be44e9c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -2,201 +2,201 @@
- vis.js | documentation
+ vis.js | documentation
-
-
+
+
-
+
-
vis.js documentation
-
-
- Vis.js is a dynamic, browser based visualization library.
- The library is designed to be easy to use, handle large amounts
- of dynamic data, and enable manipulation of the data.
-
-
-
- The library is developed by
- Almende B.V.
-
-
-
Components
-
-
- Vis.js contains of the following components:
-
-
-
-
- DataSet .
- A flexible key/value based data set.
- Add, update, and remove items. Subscribe on changes in the data set.
- A DataSet can filter and order items, and convert fields of items.
-
-
- DataView .
- A filtered and/or formatted view on a DataSet.
-
-
- Graph .
- Display a graph or network with nodes and edges.
-
-
- Timeline .
- Display different types of data on a timeline. The timeline and the
- items on the timeline can be interactively moved, zoomed, and
- manipulated.
-
-
-
-
-
-
-
Install
-
-
npm
+
vis.js documentation
+
+
+ Vis.js is a dynamic, browser based visualization library.
+ The library is designed to be easy to use, handle large amounts
+ of dynamic data, and enable manipulation of the data.
+
+
+
+ The library is developed by
+ Almende B.V.
+
+
+
Components
+
+
+ Vis.js contains of the following components:
+
+
+
+
+ DataSet .
+ A flexible key/value based data set.
+ Add, update, and remove items. Subscribe on changes in the data set.
+ A DataSet can filter and order items, and convert fields of items.
+
+
+ DataView .
+ A filtered and/or formatted view on a DataSet.
+
+
+ Graph .
+ Display a graph or network with nodes and edges.
+
+
+ Timeline .
+ Display different types of data on a timeline. The timeline and the
+ items on the timeline can be interactively moved, zoomed, and
+ manipulated.
+
+
+
+
+
+
+
Install
+
+
npm
npm install vis
-
bower
+
bower
bower install vis
-
download
- Download the library from the website:
-
http://visjs.org .
+
download
+ Download the library from the website:
+
http://visjs.org .
-
Load
+
Load
-
- To use a component, include the javascript file of vis in your web page:
-
+
+ To use a component, include the javascript file of vis in your web page:
+
<!DOCTYPE HTML>
<html>
<head>
- <script src="components/vis/vis.js"></script>
+ <script src="components/vis/vis.js"></script>
</head>
<body>
<script type="text/javascript">
- // ... load a visualization
+ // ... load a visualization
</script>
</body>
</html>
-
- or load vis.js using require.js:
-
+
+ or load vis.js using require.js:
+
require.config({
- paths: {
- vis: 'path/to/vis',
- }
+ paths: {
+ vis: 'path/to/vis',
+ }
});
require(['vis'], function (math) {
- // ... load a visualization
+ // ... load a visualization
});
-
- A timeline can be instantiated as follows. Other components can be
- created in a similar way.
-
+
+ A timeline can be instantiated as follows. Other components can be
+ created in a similar way.
+
var timeline = new vis.Timeline(container, data, options);
-
- Where container
is an HTML element, data
is
- an Array with data or a DataSet, and options
is an optional
- object with configuration options for the component.
-
+
+ Where container
is an HTML element, data
is
+ an Array with data or a DataSet, and options
is an optional
+ object with configuration options for the component.
+
-
Use
+
Use
-
+
A basic example on using a Timeline is shown below. More examples can be
found in the examples directory of the project.
-
+ target="_blank">examples directory of the project.
+
<!DOCTYPE HTML>
<html>
<head>
- <title>Timeline basic demo</title>
- <script src="components/vis/vis.js"></script>
-
- <style type="text/css">
- body, html {
- font-family: sans-serif;
- }
- </style>
+ <title>Timeline basic demo</title>
+ <script src="components/vis/vis.js"></script>
+
+ <style type="text/css">
+ body, html {
+ font-family: sans-serif;
+ }
+ </style>
</head>
<body>
<div id="visualization"></div>
<script type="text/javascript">
- var container = document.getElementById('visualization');
- var data = [
- {id: 1, content: 'item 1', start: '2013-04-20'},
- {id: 2, content: 'item 2', start: '2013-04-14'},
- {id: 3, content: 'item 3', start: '2013-04-18'},
- {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
- {id: 5, content: 'item 5', start: '2013-04-25'},
- {id: 6, content: 'item 6', start: '2013-04-27'}
- ];
- var options = {};
- var timeline = new vis.Timeline(container, data, options);
+ var container = document.getElementById('visualization');
+ var data = [
+ {id: 1, content: 'item 1', start: '2013-04-20'},
+ {id: 2, content: 'item 2', start: '2013-04-14'},
+ {id: 3, content: 'item 3', start: '2013-04-18'},
+ {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
+ {id: 5, content: 'item 5', start: '2013-04-25'},
+ {id: 6, content: 'item 6', start: '2013-04-27'}
+ ];
+ var options = {};
+ var timeline = new vis.Timeline(container, data, options);
</script>
</body>
</html>
-
License
+
License
-
- Copyright (C) 2010-2013 Almende B.V.
-
+
+ Copyright (C) 2010-2013 Almende B.V.
+
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
-
- http://www.apache.org/licenses/LICENSE-2.0
-
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/docs/timeline.html b/docs/timeline.html
index 5c4d0424..ae3d1370 100644
--- a/docs/timeline.html
+++ b/docs/timeline.html
@@ -1,12 +1,12 @@
- vis.js | timeline documentation
+ vis.js | timeline documentation
-
-
+
+
-
+
@@ -17,65 +17,65 @@
Contents
Overview
- The Timeline is an interactive visualization chart to visualize data in time.
- The data items can take place on a single date, or have a start and end date (a range).
- You can freely move and zoom in the timeline by dragging and scrolling in the
- Timeline. Items can be created, edited, and deleted in the timeline.
- The time scale on the axis is adjusted automatically, and supports scales ranging
- from milliseconds to years.
+ The Timeline is an interactive visualization chart to visualize data in time.
+ The data items can take place on a single date, or have a start and end date (a range).
+ You can freely move and zoom in the timeline by dragging and scrolling in the
+ Timeline. Items can be created, edited, and deleted in the timeline.
+ The time scale on the axis is adjusted automatically, and supports scales ranging
+ from milliseconds to years.
Example
- The following code shows how to create a Timeline and provide it with data.
- More examples can be found in the examples directory.
+ The following code shows how to create a Timeline and provide it with data.
+ More examples can be found in the examples directory.
<!DOCTYPE HTML>
<html>
<head>
- <title>Timeline | Basic demo</title>
+ <title>Timeline | Basic demo</title>
- <style type="text/css">
- body, html {
- font-family: sans-serif;
- }
- </style>
+ <style type="text/css">
+ body, html {
+ font-family: sans-serif;
+ }
+ </style>
- <script src="../../vis.js"></script>
+ <script src="../../vis.js"></script>
</head>
<body>
<div id="visualization"></div>
<script type="text/javascript">
- var container = document.getElementById('visualization');
- var items = [
- {id: 1, content: 'item 1', start: '2013-04-20'},
- {id: 2, content: 'item 2', start: '2013-04-14'},
- {id: 3, content: 'item 3', start: '2013-04-18'},
- {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
- {id: 5, content: 'item 5', start: '2013-04-25'},
- {id: 6, content: 'item 6', start: '2013-04-27'}
- ];
- var options = {};
- var timeline = new vis.Timeline(container, items, options);
+ var container = document.getElementById('visualization');
+ var items = [
+ {id: 1, content: 'item 1', start: '2013-04-20'},
+ {id: 2, content: 'item 2', start: '2013-04-14'},
+ {id: 3, content: 'item 3', start: '2013-04-18'},
+ {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
+ {id: 5, content: 'item 5', start: '2013-04-25'},
+ {id: 6, content: 'item 6', start: '2013-04-27'}
+ ];
+ var options = {};
+ var timeline = new vis.Timeline(container, items, options);
</script>
</body>
</html>
@@ -84,8 +84,8 @@
Loading
- Install or download the vis.js library.
- in a subfolder of your project. Include the library script in the head of your html code:
+ Install or download the vis.js library.
+ in a subfolder of your project. Include the library script in the head of your html code:
@@ -98,199 +98,199 @@ The constructor of the Timeline is vis.Timeline
The constructor accepts three parameters:
-
- container
is the DOM element in which to create the graph.
-
-
- items
is an Array containing items. The properties of an
- item are described in section Data Format .
-
-
- options
is an optional Object containing a name-value map
- with options. Options can also be set using the method
- setOptions
.
-
+
+ container
is the DOM element in which to create the graph.
+
+
+ items
is an Array containing items. The properties of an
+ item are described in section Data Format .
+
+
+ options
is an optional Object containing a name-value map
+ with options. Options can also be set using the method
+ setOptions
.
+
- The timeline can be provided with two types of data:
+ The timeline can be provided with two types of data:
- Items containing a set of items to be displayed in time.
- Groups containing a set of groups used to group items
+ Items containing a set of items to be displayed in time.
+ Groups containing a set of groups used to group items
together.
Items
- The Timeline uses regular Arrays and Objects as data format.
- Data items can contain the properties start
,
- end
(optional), content
,
- group
(optional), and className
(optional).
+ The Timeline uses regular Arrays and Objects as data format.
+ Data items can contain the properties start
,
+ end
(optional), content
,
+ group
(optional), and className
(optional).
- A table is constructed as:
+ A table is constructed as:
var items = [
- {
- start: new Date(2010, 7, 15),
- end: new Date(2010, 8, 2), // end is optional
- content: 'Trajectory A'
- // Optional: fields 'id', 'type', 'group', 'className'
- }
- // more items...
+ {
+ start: new Date(2010, 7, 15),
+ end: new Date(2010, 8, 2), // end is optional
+ content: 'Trajectory A'
+ // Optional: fields 'id', 'type', 'group', 'className'
+ }
+ // more items...
]);
- The item properties are defined as:
+ The item properties are defined as:
-
- Name
- Type
- Required
- Description
-
-
- id
- String | Number
- no
- An id for the item. Using an id is not required but highly
- recommended. An id is needed when dynamically adding, updating,
- and removing items in a DataSet.
-
-
- start
- Date
- yes
- The start date of the item, for example new Date(2010,09,23)
.
-
-
- end
- Date
- no
- The end date of the item. The end date is optional, and can be left null
.
- If end date is provided, the item is displayed as a range.
- If not, the item is displayed as a box.
-
-
- content
- String
- yes
- The contents of the item. This can be plain text or html code.
-
-
- type
- String
- 'box'
- The type of the item. Can be 'box' (default), 'range', or 'point'.
-
-
-
-
- group
- any type
- no
- This field is optional. When the group column is provided,
- all items with the same group are placed on one line.
- A vertical axis is displayed showing the groups.
- Grouping items can be useful for example when showing availability of multiple
- people, rooms, or other resources next to each other.
-
-
-
- className
- String
- no
- This field is optional. A className can be used to give items
- an individual css style. For example, when an item has className
- 'red', one can define a css style
-
- .red {
- background-color: red;
- border-color: dark-red;
- }
-
.
- More details on how to style items can be found in the section
- Styles .
-
-
+
+ Name
+ Type
+ Required
+ Description
+
+
+ id
+ String | Number
+ no
+ An id for the item. Using an id is not required but highly
+ recommended. An id is needed when dynamically adding, updating,
+ and removing items in a DataSet.
+
+
+ start
+ Date
+ yes
+ The start date of the item, for example new Date(2010,09,23)
.
+
+
+ end
+ Date
+ no
+ The end date of the item. The end date is optional, and can be left null
.
+ If end date is provided, the item is displayed as a range.
+ If not, the item is displayed as a box.
+
+
+ content
+ String
+ yes
+ The contents of the item. This can be plain text or html code.
+
+
+ type
+ String
+ 'box'
+ The type of the item. Can be 'box' (default), 'range', or 'point'.
+
+
+
+
+ group
+ any type
+ no
+ This field is optional. When the group column is provided,
+ all items with the same group are placed on one line.
+ A vertical axis is displayed showing the groups.
+ Grouping items can be useful for example when showing availability of multiple
+ people, rooms, or other resources next to each other.
+
+
+
+ className
+ String
+ no
+ This field is optional. A className can be used to give items
+ an individual css style. For example, when an item has className
+ 'red', one can define a css style
+
+ .red {
+ background-color: red;
+ border-color: dark-red;
+ }
+
.
+ More details on how to style items can be found in the section
+ Styles .
+
+
Groups
- Like the items, groups are regular JavaScript Arrays and Objects.
- Using groups, items can be grouped together.
- Items are filtered per group, and displayed as
+ Like the items, groups are regular JavaScript Arrays and Objects.
+ Using groups, items can be grouped together.
+ Items are filtered per group, and displayed as
- Group items can contain the properties id
,
- content
, and className
(optional).
+ Group items can contain the properties id
,
+ content
, and className
(optional).
- Groups can be applied to a timeline using the method setGroups
.
- A table with groups can be created like:
+ Groups can be applied to a timeline using the method setGroups
.
+ A table with groups can be created like:
var groups = [
- {
- id: 1,
- content: 'Group 1'
- // Optional: a field 'className'
- }
- // more groups...
+ {
+ id: 1,
+ content: 'Group 1'
+ // Optional: a field 'className'
+ }
+ // more groups...
]);
- Groups can have the following properties:
+ Groups can have the following properties:
-
- Name
- Type
- Required
- Description
-
-
- id
- String | Number
- yes
- An id for the group. The group will display all items having a
- property group
which matches the id
- of the group.
-
-
- content
- String
- yes
- The contents of the group. This can be plain text or html code.
-
-
- className
- String
- no
- This field is optional. A className can be used to give groups
- an individual css style. For example, when a group has className
- 'red', one can define a css style
-
- .red {
- color: red;
- }
-
.
- More details on how to style groups can be found in the section
- Styles .
-
-
+
+ Name
+ Type
+ Required
+ Description
+
+
+ id
+ String | Number
+ yes
+ An id for the group. The group will display all items having a
+ property group
which matches the id
+ of the group.
+
+
+ content
+ String
+ yes
+ The contents of the group. This can be plain text or html code.
+
+
+ className
+ String
+ no
+ This field is optional. A className can be used to give groups
+ an individual css style. For example, when a group has className
+ 'red', one can define a css style
+
+ .red {
+ color: red;
+ }
+
.
+ More details on how to style groups can be found in the section
+ Styles .
+
+
@@ -298,282 +298,282 @@ var groups = [
Configuration Options
- Options can be used to customize the timeline.
- Options are defined as a JSON object. All options are optional.
+ Options can be used to customize the timeline.
+ Options are defined as a JSON object. All options are optional.
var options = {
- width: '100%',
- height: '30px'
+ width: '100%',
+ height: '30px'
};
- The following options are available.
+ The following options are available.
-
- Name
- Type
- Default
- Description
-
-
-
- align
- String
- "center"
- Alignment of items with type 'box'. Available values are
- 'center' (default), 'left', or 'right').
-
-
-
- autoResize
- boolean
- false
- If true, the Timeline will automatically detect when its
- container is resized, and redraw itself accordingly.
-
-
-
- end
- Date
- none
- The initial end date for the axis of the timeline.
- If not provided, the latest date present in the items set is taken as
- end date.
-
-
-
- groupOrder
- String | Function
- none
- Order the groups by a field name or custom sort function.
- By default, groups are not ordered.
-
-
-
-
- height
- String
- none
- The height of the timeline in pixels or as a percentage.
- When height is undefined or null, the height of the timeline is automatically
- adjusted to fit the contents.
- It is possible to set a maximum height using option maxHeight
- to prevent the timeline from getting too high in case of automatically
- calculated height.
-
-
-
-
- margin.axis
- Number
- 20
- The minimal margin in pixels between items and the time axis.
-
-
-
- margin.item
- Number
- 10
- The minimal margin in pixels between items.
-
-
-
- max
- Date
- none
- Set a maximum Date for the visible range.
- It will not be possible to move beyond this maximum.
-
-
-
-
- maxHeight
- Number
- none
- Specifies a maximum height for the Timeline in pixels.
-
-
-
-
- min
- Date
- none
- Set a minimum Date for the visible range.
- It will not be possible to move beyond this minimum.
-
-
-
-
- order
- Function
- none
- Provide a custom sort function to order the items. The order of the
- items is determining the way they are stacked. The function
- order is called with two parameters, both of type
- `vis.components.items.Item`.
-
-
-
-
- orientation
- String
- 'bottom'
- Orientation of the timeline: 'top' or 'bottom' (default).
- If orientation is 'bottom', the time axis is drawn at the bottom,
- and if 'top', the axis is drawn on top.
-
-
-
- padding
- Number
- 5
- The padding of items, needed to correctly calculate the size
- of item ranges. Must correspond with the css of item ranges.
-
-
-
- showCurrentTime
- boolean
- false
- Show a vertical bar at the current time.
-
-
-
- showCustomTime
- boolean
- false
- Show a vertical bar displaying a custom time. This line can be dragged by the user. The custom time can be utilized to show a state in the past or in the future.
-
-
-
-
-
- showMajorLabels
- boolean
- true
- By default, the timeline shows both minor and major date labels on the
- time axis.
- For example the minor labels show minutes and the major labels show hours.
- When showMajorLabels
is false
, no major labels
- are shown.
-
-
-
- showMinorLabels
- boolean
- true
- By default, the timeline shows both minor and major date labels on the
- time axis.
- For example the minor labels show minutes and the major labels show hours.
- When showMinorLabels
is false
, no minor labels
- are shown. When both showMajorLabels
and
- showMinorLabels
are false, no horizontal axis will be
- visible.
-
-
-
- start
- Date
- none
- The initial start date for the axis of the timeline.
- If not provided, the earliest date present in the events is taken as start date.
-
-
-
- type
- String
- 'box'
- Specifies the type for the timeline items. Choose from 'dot' or 'point'.
- Note that individual items can override this global type.
-
-
-
-
- width
- String
- '100%'
- The width of the timeline in pixels or as a percentage.
-
-
-
- zoomMax
- Number
- 315360000000000
- Set a maximum zoom interval for the visible range in milliseconds.
- It will not be possible to zoom out further than this maximum.
- Default value equals about 10000 years.
-
-
-
-
- zoomMin
- Number
- 10
- Set a minimum zoom interval for the visible range in milliseconds.
- It will not be possible to zoom in further than this minimum.
-
-
+
+ Name
+ Type
+ Default
+ Description
+
+
+
+ align
+ String
+ "center"
+ Alignment of items with type 'box'. Available values are
+ 'center' (default), 'left', or 'right').
+
+
+
+ autoResize
+ boolean
+ false
+ If true, the Timeline will automatically detect when its
+ container is resized, and redraw itself accordingly.
+
+
+
+ end
+ Date
+ none
+ The initial end date for the axis of the timeline.
+ If not provided, the latest date present in the items set is taken as
+ end date.
+
+
+
+ groupOrder
+ String | Function
+ none
+ Order the groups by a field name or custom sort function.
+ By default, groups are not ordered.
+
+
+
+
+ height
+ String
+ none
+ The height of the timeline in pixels or as a percentage.
+ When height is undefined or null, the height of the timeline is automatically
+ adjusted to fit the contents.
+ It is possible to set a maximum height using option maxHeight
+ to prevent the timeline from getting too high in case of automatically
+ calculated height.
+
+
+
+
+ margin.axis
+ Number
+ 20
+ The minimal margin in pixels between items and the time axis.
+
+
+
+ margin.item
+ Number
+ 10
+ The minimal margin in pixels between items.
+
+
+
+ max
+ Date
+ none
+ Set a maximum Date for the visible range.
+ It will not be possible to move beyond this maximum.
+
+
+
+
+ maxHeight
+ Number
+ none
+ Specifies a maximum height for the Timeline in pixels.
+
+
+
+
+ min
+ Date
+ none
+ Set a minimum Date for the visible range.
+ It will not be possible to move beyond this minimum.
+
+
+
+
+ order
+ Function
+ none
+ Provide a custom sort function to order the items. The order of the
+ items is determining the way they are stacked. The function
+ order is called with two parameters, both of type
+ `vis.components.items.Item`.
+
+
+
+
+ orientation
+ String
+ 'bottom'
+ Orientation of the timeline: 'top' or 'bottom' (default).
+ If orientation is 'bottom', the time axis is drawn at the bottom,
+ and if 'top', the axis is drawn on top.
+
+
+
+ padding
+ Number
+ 5
+ The padding of items, needed to correctly calculate the size
+ of item ranges. Must correspond with the css of item ranges.
+
+
+
+ showCurrentTime
+ boolean
+ false
+ Show a vertical bar at the current time.
+
+
+
+ showCustomTime
+ boolean
+ false
+ Show a vertical bar displaying a custom time. This line can be dragged by the user. The custom time can be utilized to show a state in the past or in the future.
+
+
+
+
+
+ showMajorLabels
+ boolean
+ true
+ By default, the timeline shows both minor and major date labels on the
+ time axis.
+ For example the minor labels show minutes and the major labels show hours.
+ When showMajorLabels
is false
, no major labels
+ are shown.
+
+
+
+ showMinorLabels
+ boolean
+ true
+ By default, the timeline shows both minor and major date labels on the
+ time axis.
+ For example the minor labels show minutes and the major labels show hours.
+ When showMinorLabels
is false
, no minor labels
+ are shown. When both showMajorLabels
and
+ showMinorLabels
are false, no horizontal axis will be
+ visible.
+
+
+
+ start
+ Date
+ none
+ The initial start date for the axis of the timeline.
+ If not provided, the earliest date present in the events is taken as start date.
+
+
+
+ type
+ String
+ 'box'
+ Specifies the type for the timeline items. Choose from 'dot' or 'point'.
+ Note that individual items can override this global type.
+
+
+
+
+ width
+ String
+ '100%'
+ The width of the timeline in pixels or as a percentage.
+
+
+
+ zoomMax
+ Number
+ 315360000000000
+ Set a maximum zoom interval for the visible range in milliseconds.
+ It will not be possible to zoom out further than this maximum.
+ Default value equals about 10000 years.
+
+
+
+
+ zoomMin
+ Number
+ 10
+ Set a minimum zoom interval for the visible range in milliseconds.
+ It will not be possible to zoom in further than this minimum.
+
+
Methods
- The Timeline supports the following methods.
+ The Timeline supports the following methods.
-
- Method
- Return Type
- Description
-
-
-
- getCustomTime()
- Date
- Retrieve the custom time. Only applicable when the option showCustomTime
is true.
-
-
-
- setCustomTime(time)
- none
- Adjust the custom time bar. Only applicable when the option showCustomTime
is true. time
is a Date object.
-
-
-
- setGroups(groups)
- none
- Set a data set with groups for the Timeline.
- groups
can be an Array with Objects,
- a DataSet, or a DataView. For each of the groups, the items of the
- timeline are filtered on the property group
, which
- must correspond with the id of the group.
-
-
-
- setItems(items)
- none
- Set a data set with items for the Timeline.
- items
can be an Array with Objects,
- a DataSet, or a DataView.
-
-
-
-
- setOptions(options)
- none
- Set or update options. It is possible to change any option
- of the timeline at any time. You can for example switch orientation
- on the fly.
-
-
+
+ Method
+ Return Type
+ Description
+
+
+
+ getCustomTime()
+ Date
+ Retrieve the custom time. Only applicable when the option showCustomTime
is true.
+
+
+
+ setCustomTime(time)
+ none
+ Adjust the custom time bar. Only applicable when the option showCustomTime
is true. time
is a Date object.
+
+
+
+ setGroups(groups)
+ none
+ Set a data set with groups for the Timeline.
+ groups
can be an Array with Objects,
+ a DataSet, or a DataView. For each of the groups, the items of the
+ timeline are filtered on the property group
, which
+ must correspond with the id of the group.
+
+
+
+ setItems(items)
+ none
+ Set a data set with items for the Timeline.
+ items
can be an Array with Objects,
+ a DataSet, or a DataView.
+
+
+
+
+ setOptions(options)
+ none
+ Set or update options. It is possible to change any option
+ of the timeline at any time. You can for example switch orientation
+ on the fly.
+
+
@@ -581,26 +581,26 @@ var options = {
Styles
- All parts of the Timeline have a class name and a default css style.
- The styles can be overwritten, which enables full customization of the layout
- of the Timeline.
+ All parts of the Timeline have a class name and a default css style.
+ The styles can be overwritten, which enables full customization of the layout
+ of the Timeline.
For example, to change the border and background color of all items, include the
- following code inside the head of your html code or in a separate stylesheet.
+ following code inside the head of your html code or in a separate stylesheet.
<style>
- .graph .item {
- border-color: orange;
- background-color: yellow;
- }
+ .graph .item {
+ border-color: orange;
+ background-color: yellow;
+ }
</style>
Data Policy
- All code and data is processed and rendered in the browser.
- No data is sent to any server.
+ All code and data is processed and rendered in the browser.
+ No data is sent to any server.
diff --git a/examples/graph/01_basic_usage.html b/examples/graph/01_basic_usage.html
index 3bed9c75..0698b9cb 100644
--- a/examples/graph/01_basic_usage.html
+++ b/examples/graph/01_basic_usage.html
@@ -1,17 +1,17 @@
- Graph | Basic usage
+ Graph | Basic usage
-
+
-
+
@@ -19,31 +19,31 @@
diff --git a/examples/graph/02_random_nodes.html b/examples/graph/02_random_nodes.html
index b048a69d..3d4b5a96 100755
--- a/examples/graph/02_random_nodes.html
+++ b/examples/graph/02_random_nodes.html
@@ -1,105 +1,105 @@
- Graph | Random nodes
+ Graph | Random nodes
-
+
-
+
-
+ // add event listeners
+ vis.events.addListener(graph, 'select', function(params) {
+ document.getElementById('selection').innerHTML =
+ 'Selection: ' + graph.getSelection();
+ });
+ }
+
diff --git a/examples/graph/03_images.html b/examples/graph/03_images.html
index f0235fb6..74e080cb 100755
--- a/examples/graph/03_images.html
+++ b/examples/graph/03_images.html
@@ -1,78 +1,78 @@
- Graph | Images
-
-
-
-
-
-
+ Graph | Images
+
+
+
+
+
+
diff --git a/examples/graph/04_shapes.html b/examples/graph/04_shapes.html
index 25b3f3ea..a13c8070 100755
--- a/examples/graph/04_shapes.html
+++ b/examples/graph/04_shapes.html
@@ -1,71 +1,71 @@
- Graph | Shapes
+ Graph | Shapes
-
+
-
+
-
+ }
+
+ // create a graph
+ var container = document.getElementById('mygraph');
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ var options = {
+ stabilize: false
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/05_social_network.html b/examples/graph/05_social_network.html
index 480eca6b..327145db 100644
--- a/examples/graph/05_social_network.html
+++ b/examples/graph/05_social_network.html
@@ -1,76 +1,76 @@
- Graph | Social Network
+ Graph | Social Network
-
+
-
+
-
+ // create a graph
+ var container = document.getElementById('mygraph');
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ var options = {};
+ graph = new vis.Graph(container, data, options);
+ }
+
- Icons: Scrap Icons by Deleket
+ Icons: Scrap Icons by Deleket
diff --git a/examples/graph/06_groups.html b/examples/graph/06_groups.html
index 59adfb51..f40b8e6a 100644
--- a/examples/graph/06_groups.html
+++ b/examples/graph/06_groups.html
@@ -1,156 +1,156 @@
- Graph | Groups
+ Graph | Groups
+
+
+
+
+
+
+
-
-
-
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/07_selections.html b/examples/graph/07_selections.html
index 421c3794..0cb31796 100644
--- a/examples/graph/07_selections.html
+++ b/examples/graph/07_selections.html
@@ -1,17 +1,17 @@
- Graph | Selections
+ Graph | Selections
-
+
-
+
@@ -20,45 +20,45 @@
diff --git a/examples/graph/08_mobile_friendly.html b/examples/graph/08_mobile_friendly.html
index de44f4a8..6601bee9 100755
--- a/examples/graph/08_mobile_friendly.html
+++ b/examples/graph/08_mobile_friendly.html
@@ -1,105 +1,105 @@
- Graph | Mobile friendly
+ Graph | Mobile friendly
-
+ #mygraph {
+ width: 100%;
+ height: 100%;
+ }
+
-
-
+
+
-
+
-
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/09_sizing.html b/examples/graph/09_sizing.html
index 67a48f8e..42ed7273 100644
--- a/examples/graph/09_sizing.html
+++ b/examples/graph/09_sizing.html
@@ -1,77 +1,77 @@
- Graph | Sizing
+ Graph | Sizing
-
+
-
+
-
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/10_multiline_text.html b/examples/graph/10_multiline_text.html
index 695d5fba..d7e2d1dd 100755
--- a/examples/graph/10_multiline_text.html
+++ b/examples/graph/10_multiline_text.html
@@ -1,47 +1,47 @@
- Graph | Multiline text
+ Graph | Multiline text
-
+
-
+
-
+ // create a graph
+ var container = document.getElementById('mygraph');
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ var options = {};
+ var graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/11_custom_style.html b/examples/graph/11_custom_style.html
index 20e9dd59..99225208 100644
--- a/examples/graph/11_custom_style.html
+++ b/examples/graph/11_custom_style.html
@@ -1,128 +1,128 @@
- Graph | Custom style
+ Graph | Custom style
-
+
-
+
-
+ };
+
+ // create the graph
+ var container = document.getElementById('mygraph');
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/12_scalable_images.html b/examples/graph/12_scalable_images.html
index 8a3da963..2a87ac62 100644
--- a/examples/graph/12_scalable_images.html
+++ b/examples/graph/12_scalable_images.html
@@ -1,80 +1,80 @@
- Graph | Scalable images
+ Graph | Scalable images
-
+
-
+
-
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/13_dashed_lines.html b/examples/graph/13_dashed_lines.html
index 6f954672..3fdc2f5f 100644
--- a/examples/graph/13_dashed_lines.html
+++ b/examples/graph/13_dashed_lines.html
@@ -1,65 +1,65 @@
- Graph | Dashed lines
+ Graph | Dashed lines
-
+
-
+
-
+ // create the graph
+ var container = document.getElementById('mygraph');
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ var options = {
+ nodes: {
+ shape: 'box'
+ },
+ edges: {
+ length: 180
+ },
+ stabilize: false
+ };
+ var graph = new vis.Graph(container, data, options);
+ }
+
-
- This example shows the different options for dashed lines.
-
+
+ This example shows the different options for dashed lines.
+
-
+
diff --git a/examples/graph/14_dot_language.html b/examples/graph/14_dot_language.html
index 7d86df95..241f4403 100644
--- a/examples/graph/14_dot_language.html
+++ b/examples/graph/14_dot_language.html
@@ -1,18 +1,18 @@
- Graph | DOT Language
+ Graph | DOT Language
-
+
-
+
-
+
diff --git a/examples/graph/15_dot_language_playground.html b/examples/graph/15_dot_language_playground.html
index bf757cd8..dc5c6931 100644
--- a/examples/graph/15_dot_language_playground.html
+++ b/examples/graph/15_dot_language_playground.html
@@ -1,201 +1,201 @@
- Graph | DOT language playground
-
-
-
-
+ textarea.example {
+ display: none;
+ }
+
-
-
-
-
- DOT language playground
-
-
-
- Draw
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ DOT language playground
+
+
+
+ Draw
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/graph/16_dynamic_data.html b/examples/graph/16_dynamic_data.html
index b4ccb596..ea3040de 100644
--- a/examples/graph/16_dynamic_data.html
+++ b/examples/graph/16_dynamic_data.html
@@ -1,264 +1,264 @@
- Graph | DataSet
+ Graph | DataSet
-
+ #graph {
+ width: 100%;
+ height: 400px;
+ border: 1px solid lightgray;
+ }
+
-
-
+
+
-
+ // create a graph
+ var container = $('#graph').get(0);
+ var data = {
+ nodes: nodes,
+ edges: edges
+ };
+ var options = {};
+ graph = new vis.Graph(container, data, options);
+ });
+
- This example demonstrates dynamically adding, updating and removing nodes
- and edges using a DataSet.
+ This example demonstrates dynamically adding, updating and removing nodes
+ and edges using a DataSet.
Adjust
-
-
- Node
-
-
-
- Edge
-
-
-
+
+
+ Node
+
+
+
+ Edge
+
+
+
View
-
-
-
-
-
-
-
- Nodes
-
-
+
+
+
+
+
+
+
+ Nodes
+
+
-
- Edges
-
-
+
+ Edges
+
+
-
- Graph
-
-
-
+
+ Graph
+
+
+
diff --git a/examples/graph/17_network_info.html b/examples/graph/17_network_info.html
index 62cd9eef..ab73f517 100644
--- a/examples/graph/17_network_info.html
+++ b/examples/graph/17_network_info.html
@@ -1,149 +1,149 @@
- Graph | Images
-
-
+
+
+
+
-
-
+ };
+ graph = new vis.Graph(container, data, options);
+ }
+
diff --git a/examples/graph/graphviz/graphviz_gallery.html b/examples/graph/graphviz/graphviz_gallery.html
index 3260187d..6e17feb2 100644
--- a/examples/graph/graphviz/graphviz_gallery.html
+++ b/examples/graph/graphviz/graphviz_gallery.html
@@ -1,86 +1,86 @@
- Graph | Graphviz Gallery
+ Graph | Graphviz Gallery
-
-
+
+
-
+
- The following examples are unmodified copies from the
- Graphviz Gallery .
+ The following examples are unmodified copies from the
+ Graphviz Gallery .
- Note that some style attributes of Graphviz are not supported by vis.js,
- and that vis.js offers options not supported by Graphviz (which could make
- some examples look much nicer).
+ Note that some style attributes of Graphviz are not supported by vis.js,
+ and that vis.js offers options not supported by Graphviz (which could make
+ some examples look much nicer).
- Select an example:
-
- fsm
- hello
- process
- siblings
- softmaint
- traffic_lights
- transparency
- twopi2
- unix
- world
-
+ Select an example:
+
+ fsm
+ hello
+ process
+ siblings
+ softmaint
+ traffic_lights
+ transparency
+ twopi2
+ unix
+ world
+
diff --git a/examples/graph/index.html b/examples/graph/index.html
index 0fd44cfb..7cb11657 100644
--- a/examples/graph/index.html
+++ b/examples/graph/index.html
@@ -2,34 +2,34 @@
- vis.js | graph examples
+ vis.js | graph examples
-
+
diff --git a/package.json b/package.json
index 61bfcefb..c5954b62 100644
--- a/package.json
+++ b/package.json
@@ -1,34 +1,34 @@
{
- "name": "vis",
- "version": "0.3.0-SNAPSHOT",
- "description": "A dynamic, browser-based visualization library.",
- "homepage": "http://visjs.org/",
- "repository": {
- "type": "git",
- "url": "git://github.com/almende/vis.git"
- },
- "keywords": [
- "vis",
- "visualization",
- "web based",
- "browser based",
- "javascript",
- "chart",
- "linechart",
- "timeline",
- "graph",
- "network",
- "browser"
- ],
- "scripts": {
- "test": "jake test --trace"
- },
- "dependencies": {},
- "devDependencies": {
- "jake": "latest",
- "jake-utils": "latest",
- "browserify": "latest",
- "moment": "latest",
- "hammerjs": "latest"
- }
+ "name": "vis",
+ "version": "0.3.0-SNAPSHOT",
+ "description": "A dynamic, browser-based visualization library.",
+ "homepage": "http://visjs.org/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/almende/vis.git"
+ },
+ "keywords": [
+ "vis",
+ "visualization",
+ "web based",
+ "browser based",
+ "javascript",
+ "chart",
+ "linechart",
+ "timeline",
+ "graph",
+ "network",
+ "browser"
+ ],
+ "scripts": {
+ "test": "jake test --trace"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "jake": "latest",
+ "jake-utils": "latest",
+ "browserify": "latest",
+ "moment": "latest",
+ "hammerjs": "latest"
+ }
}
diff --git a/src/DataSet.js b/src/DataSet.js
index fb274a44..36604a58 100644
--- a/src/DataSet.js
+++ b/src/DataSet.js
@@ -36,31 +36,31 @@
*/
// TODO: add a DataSet constructor DataSet(data, options)
function DataSet (options) {
- this.id = util.randomUUID();
-
- this.options = options || {};
- this.data = {}; // map with data indexed by id
- this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
- this.convert = {}; // field types by field name
-
- if (this.options.convert) {
- for (var field in this.options.convert) {
- if (this.options.convert.hasOwnProperty(field)) {
- var value = this.options.convert[field];
- if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
- this.convert[field] = 'Date';
- }
- else {
- this.convert[field] = value;
- }
- }
+ this.id = util.randomUUID();
+
+ this.options = options || {};
+ this.data = {}; // map with data indexed by id
+ this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
+ this.convert = {}; // field types by field name
+
+ if (this.options.convert) {
+ for (var field in this.options.convert) {
+ if (this.options.convert.hasOwnProperty(field)) {
+ var value = this.options.convert[field];
+ if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
+ this.convert[field] = 'Date';
}
+ else {
+ this.convert[field] = value;
+ }
+ }
}
+ }
- // event subscribers
- this.subscribers = {};
+ // event subscribers
+ this.subscribers = {};
- this.internalIds = {}; // internally generated id's
+ this.internalIds = {}; // internally generated id's
}
/**
@@ -73,15 +73,15 @@ function DataSet (options) {
* {String | Number} senderId
*/
DataSet.prototype.subscribe = function (event, callback) {
- var subscribers = this.subscribers[event];
- if (!subscribers) {
- subscribers = [];
- this.subscribers[event] = subscribers;
- }
-
- subscribers.push({
- callback: callback
- });
+ var subscribers = this.subscribers[event];
+ if (!subscribers) {
+ subscribers = [];
+ this.subscribers[event] = subscribers;
+ }
+
+ subscribers.push({
+ callback: callback
+ });
};
/**
@@ -90,12 +90,12 @@ DataSet.prototype.subscribe = function (event, callback) {
* @param {function} callback
*/
DataSet.prototype.unsubscribe = function (event, callback) {
- var subscribers = this.subscribers[event];
- if (subscribers) {
- this.subscribers[event] = subscribers.filter(function (listener) {
- return (listener.callback != callback);
- });
- }
+ var subscribers = this.subscribers[event];
+ if (subscribers) {
+ this.subscribers[event] = subscribers.filter(function (listener) {
+ return (listener.callback != callback);
+ });
+ }
};
/**
@@ -106,24 +106,24 @@ DataSet.prototype.unsubscribe = function (event, callback) {
* @private
*/
DataSet.prototype._trigger = function (event, params, senderId) {
- if (event == '*') {
- throw new Error('Cannot trigger event *');
- }
-
- var subscribers = [];
- if (event in this.subscribers) {
- subscribers = subscribers.concat(this.subscribers[event]);
- }
- if ('*' in this.subscribers) {
- subscribers = subscribers.concat(this.subscribers['*']);
- }
-
- for (var i = 0; i < subscribers.length; i++) {
- var subscriber = subscribers[i];
- if (subscriber.callback) {
- subscriber.callback(event, params, senderId || null);
- }
- }
+ if (event == '*') {
+ throw new Error('Cannot trigger event *');
+ }
+
+ var subscribers = [];
+ if (event in this.subscribers) {
+ subscribers = subscribers.concat(this.subscribers[event]);
+ }
+ if ('*' in this.subscribers) {
+ subscribers = subscribers.concat(this.subscribers['*']);
+ }
+
+ for (var i = 0; i < subscribers.length; i++) {
+ var subscriber = subscribers[i];
+ if (subscriber.callback) {
+ subscriber.callback(event, params, senderId || null);
+ }
+ }
};
/**
@@ -134,45 +134,45 @@ DataSet.prototype._trigger = function (event, params, senderId) {
* @return {Array} addedIds Array with the ids of the added items
*/
DataSet.prototype.add = function (data, senderId) {
- var addedIds = [],
- id,
- me = this;
-
- if (data instanceof Array) {
- // Array
- for (var i = 0, len = data.length; i < len; i++) {
- id = me._addItem(data[i]);
- addedIds.push(id);
- }
- }
- else if (util.isDataTable(data)) {
- // Google DataTable
- var columns = this._getColumnNames(data);
- for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
- var item = {};
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- item[field] = data.getValue(row, col);
- }
-
- id = me._addItem(item);
- addedIds.push(id);
- }
- }
- else if (data instanceof Object) {
- // Single item
- id = me._addItem(data);
- addedIds.push(id);
- }
- else {
- throw new Error('Unknown dataType');
- }
-
- if (addedIds.length) {
- this._trigger('add', {items: addedIds}, senderId);
- }
-
- return addedIds;
+ var addedIds = [],
+ id,
+ me = this;
+
+ if (data instanceof Array) {
+ // Array
+ for (var i = 0, len = data.length; i < len; i++) {
+ id = me._addItem(data[i]);
+ addedIds.push(id);
+ }
+ }
+ else if (util.isDataTable(data)) {
+ // Google DataTable
+ var columns = this._getColumnNames(data);
+ for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
+ var item = {};
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ item[field] = data.getValue(row, col);
+ }
+
+ id = me._addItem(item);
+ addedIds.push(id);
+ }
+ }
+ else if (data instanceof Object) {
+ // Single item
+ id = me._addItem(data);
+ addedIds.push(id);
+ }
+ else {
+ throw new Error('Unknown dataType');
+ }
+
+ if (addedIds.length) {
+ this._trigger('add', {items: addedIds}, senderId);
+ }
+
+ return addedIds;
};
/**
@@ -182,60 +182,60 @@ DataSet.prototype.add = function (data, senderId) {
* @return {Array} updatedIds The ids of the added or updated items
*/
DataSet.prototype.update = function (data, senderId) {
- var addedIds = [],
- updatedIds = [],
- me = this,
- fieldId = me.fieldId;
-
- var addOrUpdate = function (item) {
- var id = item[fieldId];
- if (me.data[id]) {
- // update item
- id = me._updateItem(item);
- updatedIds.push(id);
- }
- else {
- // add new item
- id = me._addItem(item);
- addedIds.push(id);
- }
- };
-
- if (data instanceof Array) {
- // Array
- for (var i = 0, len = data.length; i < len; i++) {
- addOrUpdate(data[i]);
- }
- }
- else if (util.isDataTable(data)) {
- // Google DataTable
- var columns = this._getColumnNames(data);
- for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
- var item = {};
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- item[field] = data.getValue(row, col);
- }
-
- addOrUpdate(item);
- }
- }
- else if (data instanceof Object) {
- // Single item
- addOrUpdate(data);
+ var addedIds = [],
+ updatedIds = [],
+ me = this,
+ fieldId = me.fieldId;
+
+ var addOrUpdate = function (item) {
+ var id = item[fieldId];
+ if (me.data[id]) {
+ // update item
+ id = me._updateItem(item);
+ updatedIds.push(id);
}
else {
- throw new Error('Unknown dataType');
- }
-
- if (addedIds.length) {
- this._trigger('add', {items: addedIds}, senderId);
- }
- if (updatedIds.length) {
- this._trigger('update', {items: updatedIds}, senderId);
- }
-
- return addedIds.concat(updatedIds);
+ // add new item
+ id = me._addItem(item);
+ addedIds.push(id);
+ }
+ };
+
+ if (data instanceof Array) {
+ // Array
+ for (var i = 0, len = data.length; i < len; i++) {
+ addOrUpdate(data[i]);
+ }
+ }
+ else if (util.isDataTable(data)) {
+ // Google DataTable
+ var columns = this._getColumnNames(data);
+ for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
+ var item = {};
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ item[field] = data.getValue(row, col);
+ }
+
+ addOrUpdate(item);
+ }
+ }
+ else if (data instanceof Object) {
+ // Single item
+ addOrUpdate(data);
+ }
+ else {
+ throw new Error('Unknown dataType');
+ }
+
+ if (addedIds.length) {
+ this._trigger('add', {items: addedIds}, senderId);
+ }
+ if (updatedIds.length) {
+ this._trigger('update', {items: updatedIds}, senderId);
+ }
+
+ return addedIds.concat(updatedIds);
};
/**
@@ -274,138 +274,138 @@ DataSet.prototype.update = function (data, senderId) {
* @throws Error
*/
DataSet.prototype.get = function (args) {
- var me = this;
-
- // parse the arguments
- var id, ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number') {
- // get(id [, options] [, data])
- id = arguments[0];
- options = arguments[1];
- data = arguments[2];
- }
- else if (firstType == 'Array') {
- // get(ids [, options] [, data])
- ids = arguments[0];
- options = arguments[1];
- data = arguments[2];
+ var me = this;
+
+ // parse the arguments
+ var id, ids, options, data;
+ var firstType = util.getType(arguments[0]);
+ if (firstType == 'String' || firstType == 'Number') {
+ // get(id [, options] [, data])
+ id = arguments[0];
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else if (firstType == 'Array') {
+ // get(ids [, options] [, data])
+ ids = arguments[0];
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else {
+ // get([, options] [, data])
+ options = arguments[0];
+ data = arguments[1];
+ }
+
+ // determine the return type
+ var type;
+ if (options && options.type) {
+ type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
+
+ if (data && (type != util.getType(data))) {
+ throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
+ 'does not correspond with specified options.type (' + options.type + ')');
+ }
+ if (type == 'DataTable' && !util.isDataTable(data)) {
+ throw new Error('Parameter "data" must be a DataTable ' +
+ 'when options.type is "DataTable"');
+ }
+ }
+ else if (data) {
+ type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
+ }
+ else {
+ type = 'Array';
+ }
+
+ // build options
+ var convert = options && options.convert || this.options.convert;
+ var filter = options && options.filter;
+ var items = [], item, itemId, i, len;
+
+ // convert items
+ if (id != undefined) {
+ // return a single item
+ item = me._getItem(id, convert);
+ if (filter && !filter(item)) {
+ item = null;
+ }
+ }
+ else if (ids != undefined) {
+ // return a subset of items
+ for (i = 0, len = ids.length; i < len; i++) {
+ item = me._getItem(ids[i], convert);
+ if (!filter || filter(item)) {
+ items.push(item);
+ }
+ }
+ }
+ else {
+ // return all items
+ for (itemId in this.data) {
+ if (this.data.hasOwnProperty(itemId)) {
+ item = me._getItem(itemId, convert);
+ if (!filter || filter(item)) {
+ items.push(item);
+ }
+ }
+ }
+ }
+
+ // order the results
+ if (options && options.order && id == undefined) {
+ this._sort(items, options.order);
+ }
+
+ // filter fields of the items
+ if (options && options.fields) {
+ var fields = options.fields;
+ if (id != undefined) {
+ item = this._filterFields(item, fields);
}
else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
+ for (i = 0, len = items.length; i < len; i++) {
+ items[i] = this._filterFields(items[i], fields);
+ }
}
+ }
- // determine the return type
- var type;
- if (options && options.type) {
- type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
-
- if (data && (type != util.getType(data))) {
- throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
- 'does not correspond with specified options.type (' + options.type + ')');
- }
- if (type == 'DataTable' && !util.isDataTable(data)) {
- throw new Error('Parameter "data" must be a DataTable ' +
- 'when options.type is "DataTable"');
- }
- }
- else if (data) {
- type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
+ // return the results
+ if (type == 'DataTable') {
+ var columns = this._getColumnNames(data);
+ if (id != undefined) {
+ // append a single item to the data table
+ me._appendRow(data, columns, item);
}
else {
- type = 'Array';
- }
-
- // build options
- var convert = options && options.convert || this.options.convert;
- var filter = options && options.filter;
- var items = [], item, itemId, i, len;
-
- // convert items
+ // copy the items to the provided data table
+ for (i = 0, len = items.length; i < len; i++) {
+ me._appendRow(data, columns, items[i]);
+ }
+ }
+ return data;
+ }
+ else {
+ // return an array
if (id != undefined) {
- // return a single item
- item = me._getItem(id, convert);
- if (filter && !filter(item)) {
- item = null;
- }
- }
- else if (ids != undefined) {
- // return a subset of items
- for (i = 0, len = ids.length; i < len; i++) {
- item = me._getItem(ids[i], convert);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
+ // a single item
+ return item;
}
else {
- // return all items
- for (itemId in this.data) {
- if (this.data.hasOwnProperty(itemId)) {
- item = me._getItem(itemId, convert);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
- }
- }
-
- // order the results
- if (options && options.order && id == undefined) {
- this._sort(items, options.order);
- }
-
- // filter fields of the items
- if (options && options.fields) {
- var fields = options.fields;
- if (id != undefined) {
- item = this._filterFields(item, fields);
- }
- else {
- for (i = 0, len = items.length; i < len; i++) {
- items[i] = this._filterFields(items[i], fields);
- }
- }
- }
-
- // return the results
- if (type == 'DataTable') {
- var columns = this._getColumnNames(data);
- if (id != undefined) {
- // append a single item to the data table
- me._appendRow(data, columns, item);
- }
- else {
- // copy the items to the provided data table
- for (i = 0, len = items.length; i < len; i++) {
- me._appendRow(data, columns, items[i]);
- }
+ // multiple items
+ if (data) {
+ // copy the items to the provided array
+ for (i = 0, len = items.length; i < len; i++) {
+ data.push(items[i]);
}
return data;
+ }
+ else {
+ // just return our array
+ return items;
+ }
}
- else {
- // return an array
- if (id != undefined) {
- // a single item
- return item;
- }
- else {
- // multiple items
- if (data) {
- // copy the items to the provided array
- for (i = 0, len = items.length; i < len; i++) {
- data.push(items[i]);
- }
- return data;
- }
- else {
- // just return our array
- return items;
- }
- }
- }
+ }
};
/**
@@ -417,78 +417,78 @@ DataSet.prototype.get = function (args) {
* @return {Array} ids
*/
DataSet.prototype.getIds = function (options) {
- var data = this.data,
- filter = options && options.filter,
- order = options && options.order,
- convert = options && options.convert || this.options.convert,
- i,
- len,
- id,
- item,
- items,
- ids = [];
-
- if (filter) {
- // get filtered items
- if (order) {
- // create ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (filter(item)) {
- items.push(item);
- }
- }
- }
-
- this._sort(items, order);
-
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
- }
- }
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (filter(item)) {
- ids.push(item[this.fieldId]);
- }
- }
- }
+ var data = this.data,
+ filter = options && options.filter,
+ order = options && options.order,
+ convert = options && options.convert || this.options.convert,
+ i,
+ len,
+ id,
+ item,
+ items,
+ ids = [];
+
+ if (filter) {
+ // get filtered items
+ if (order) {
+ // create ordered list
+ items = [];
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (filter(item)) {
+ items.push(item);
+ }
}
+ }
+
+ this._sort(items, order);
+
+ for (i = 0, len = items.length; i < len; i++) {
+ ids[i] = items[i][this.fieldId];
+ }
}
else {
- // get all items
- if (order) {
- // create an ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- items.push(data[id]);
- }
- }
-
- this._sort(items, order);
-
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
- }
+ // create unordered list
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (filter(item)) {
+ ids.push(item[this.fieldId]);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // get all items
+ if (order) {
+ // create an ordered list
+ items = [];
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ items.push(data[id]);
}
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = data[id];
- ids.push(item[this.fieldId]);
- }
- }
+ }
+
+ this._sort(items, order);
+
+ for (i = 0, len = items.length; i < len; i++) {
+ ids[i] = items[i][this.fieldId];
+ }
+ }
+ else {
+ // create unordered list
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = data[id];
+ ids.push(item[this.fieldId]);
}
+ }
}
+ }
- return ids;
+ return ids;
};
/**
@@ -503,33 +503,33 @@ DataSet.prototype.getIds = function (options) {
* a field name or custom sort function.
*/
DataSet.prototype.forEach = function (callback, options) {
- var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
- data = this.data,
- item,
- id;
-
- if (options && options.order) {
- // execute forEach on ordered list
- var items = this.get(options);
-
- for (var i = 0, len = items.length; i < len; i++) {
- item = items[i];
- id = item[this.fieldId];
- callback(item, id);
- }
- }
- else {
- // unordered
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (!filter || filter(item)) {
- callback(item, id);
- }
- }
- }
- }
+ var filter = options && options.filter,
+ convert = options && options.convert || this.options.convert,
+ data = this.data,
+ item,
+ id;
+
+ if (options && options.order) {
+ // execute forEach on ordered list
+ var items = this.get(options);
+
+ for (var i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ id = item[this.fieldId];
+ callback(item, id);
+ }
+ }
+ else {
+ // unordered
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (!filter || filter(item)) {
+ callback(item, id);
+ }
+ }
+ }
+ }
};
/**
@@ -544,28 +544,28 @@ DataSet.prototype.forEach = function (callback, options) {
* @return {Object[]} mappedItems
*/
DataSet.prototype.map = function (callback, options) {
- var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
- mappedItems = [],
- data = this.data,
- item;
-
- // convert and filter items
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (!filter || filter(item)) {
- mappedItems.push(callback(item, id));
- }
- }
- }
-
- // order items
- if (options && options.order) {
- this._sort(mappedItems, options.order);
- }
-
- return mappedItems;
+ var filter = options && options.filter,
+ convert = options && options.convert || this.options.convert,
+ mappedItems = [],
+ data = this.data,
+ item;
+
+ // convert and filter items
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (!filter || filter(item)) {
+ mappedItems.push(callback(item, id));
+ }
+ }
+ }
+
+ // order items
+ if (options && options.order) {
+ this._sort(mappedItems, options.order);
+ }
+
+ return mappedItems;
};
/**
@@ -576,15 +576,15 @@ DataSet.prototype.map = function (callback, options) {
* @private
*/
DataSet.prototype._filterFields = function (item, fields) {
- var filteredItem = {};
+ var filteredItem = {};
- for (var field in item) {
- if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
- filteredItem[field] = item[field];
- }
+ for (var field in item) {
+ if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
+ filteredItem[field] = item[field];
}
+ }
- return filteredItem;
+ return filteredItem;
};
/**
@@ -594,24 +594,24 @@ DataSet.prototype._filterFields = function (item, fields) {
* @private
*/
DataSet.prototype._sort = function (items, order) {
- if (util.isString(order)) {
- // order by provided field name
- var name = order; // field name
- items.sort(function (a, b) {
- var av = a[name];
- var bv = b[name];
- return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
- });
- }
- else if (typeof order === 'function') {
- // order by sort function
- items.sort(order);
- }
- // TODO: extend order by an Object {field:String, direction:String}
- // where direction can be 'asc' or 'desc'
- else {
- throw new TypeError('Order must be a function or a string');
- }
+ if (util.isString(order)) {
+ // order by provided field name
+ var name = order; // field name
+ items.sort(function (a, b) {
+ var av = a[name];
+ var bv = b[name];
+ return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
+ });
+ }
+ else if (typeof order === 'function') {
+ // order by sort function
+ items.sort(order);
+ }
+ // TODO: extend order by an Object {field:String, direction:String}
+ // where direction can be 'asc' or 'desc'
+ else {
+ throw new TypeError('Order must be a function or a string');
+ }
};
/**
@@ -622,29 +622,29 @@ DataSet.prototype._sort = function (items, order) {
* @return {Array} removedIds
*/
DataSet.prototype.remove = function (id, senderId) {
- var removedIds = [],
- i, len, removedId;
-
- if (id instanceof Array) {
- for (i = 0, len = id.length; i < len; i++) {
- removedId = this._remove(id[i]);
- if (removedId != null) {
- removedIds.push(removedId);
- }
- }
+ var removedIds = [],
+ i, len, removedId;
+
+ if (id instanceof Array) {
+ for (i = 0, len = id.length; i < len; i++) {
+ removedId = this._remove(id[i]);
+ if (removedId != null) {
+ removedIds.push(removedId);
+ }
}
- else {
- removedId = this._remove(id);
- if (removedId != null) {
- removedIds.push(removedId);
- }
+ }
+ else {
+ removedId = this._remove(id);
+ if (removedId != null) {
+ removedIds.push(removedId);
}
+ }
- if (removedIds.length) {
- this._trigger('remove', {items: removedIds}, senderId);
- }
+ if (removedIds.length) {
+ this._trigger('remove', {items: removedIds}, senderId);
+ }
- return removedIds;
+ return removedIds;
};
/**
@@ -654,22 +654,22 @@ DataSet.prototype.remove = function (id, senderId) {
* @private
*/
DataSet.prototype._remove = function (id) {
- if (util.isNumber(id) || util.isString(id)) {
- if (this.data[id]) {
- delete this.data[id];
- delete this.internalIds[id];
- return id;
- }
- }
- else if (id instanceof Object) {
- var itemId = id[this.fieldId];
- if (itemId && this.data[itemId]) {
- delete this.data[itemId];
- delete this.internalIds[itemId];
- return itemId;
- }
- }
- return null;
+ if (util.isNumber(id) || util.isString(id)) {
+ if (this.data[id]) {
+ delete this.data[id];
+ delete this.internalIds[id];
+ return id;
+ }
+ }
+ else if (id instanceof Object) {
+ var itemId = id[this.fieldId];
+ if (itemId && this.data[itemId]) {
+ delete this.data[itemId];
+ delete this.internalIds[itemId];
+ return itemId;
+ }
+ }
+ return null;
};
/**
@@ -678,14 +678,14 @@ DataSet.prototype._remove = function (id) {
* @return {Array} removedIds The ids of all removed items
*/
DataSet.prototype.clear = function (senderId) {
- var ids = Object.keys(this.data);
+ var ids = Object.keys(this.data);
- this.data = {};
- this.internalIds = {};
+ this.data = {};
+ this.internalIds = {};
- this._trigger('remove', {items: ids}, senderId);
+ this._trigger('remove', {items: ids}, senderId);
- return ids;
+ return ids;
};
/**
@@ -694,22 +694,22 @@ DataSet.prototype.clear = function (senderId) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.max = function (field) {
- var data = this.data,
- max = null,
- maxField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!max || itemField > maxField)) {
- max = item;
- maxField = itemField;
- }
- }
- }
-
- return max;
+ var data = this.data,
+ max = null,
+ maxField = null;
+
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ var item = data[id];
+ var itemField = item[field];
+ if (itemField != null && (!max || itemField > maxField)) {
+ max = item;
+ maxField = itemField;
+ }
+ }
+ }
+
+ return max;
};
/**
@@ -718,22 +718,22 @@ DataSet.prototype.max = function (field) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.min = function (field) {
- var data = this.data,
- min = null,
- minField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!min || itemField < minField)) {
- min = item;
- minField = itemField;
- }
- }
- }
-
- return min;
+ var data = this.data,
+ min = null,
+ minField = null;
+
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ var item = data[id];
+ var itemField = item[field];
+ if (itemField != null && (!min || itemField < minField)) {
+ min = item;
+ minField = itemField;
+ }
+ }
+ }
+
+ return min;
};
/**
@@ -745,30 +745,30 @@ DataSet.prototype.min = function (field) {
* The returned array is unordered.
*/
DataSet.prototype.distinct = function (field) {
- var data = this.data,
- values = [],
- fieldType = this.options.convert[field],
- count = 0;
-
- for (var prop in data) {
- if (data.hasOwnProperty(prop)) {
- var item = data[prop];
- var value = util.convert(item[field], fieldType);
- var exists = false;
- for (var i = 0; i < count; i++) {
- if (values[i] == value) {
- exists = true;
- break;
- }
- }
- if (!exists) {
- values[count] = value;
- count++;
- }
- }
- }
-
- return values;
+ var data = this.data,
+ values = [],
+ fieldType = this.options.convert[field],
+ count = 0;
+
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ var item = data[prop];
+ var value = util.convert(item[field], fieldType);
+ var exists = false;
+ for (var i = 0; i < count; i++) {
+ if (values[i] == value) {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) {
+ values[count] = value;
+ count++;
+ }
+ }
+ }
+
+ return values;
};
/**
@@ -778,32 +778,32 @@ DataSet.prototype.distinct = function (field) {
* @private
*/
DataSet.prototype._addItem = function (item) {
- var id = item[this.fieldId];
-
- if (id != undefined) {
- // check whether this id is already taken
- if (this.data[id]) {
- // item already exists
- throw new Error('Cannot add item: item with id ' + id + ' already exists');
- }
- }
- else {
- // generate an id
- id = util.randomUUID();
- item[this.fieldId] = id;
- this.internalIds[id] = item;
- }
-
- var d = {};
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
- }
- this.data[id] = d;
-
- return id;
+ var id = item[this.fieldId];
+
+ if (id != undefined) {
+ // check whether this id is already taken
+ if (this.data[id]) {
+ // item already exists
+ throw new Error('Cannot add item: item with id ' + id + ' already exists');
+ }
+ }
+ else {
+ // generate an id
+ id = util.randomUUID();
+ item[this.fieldId] = id;
+ this.internalIds[id] = item;
+ }
+
+ var d = {};
+ for (var field in item) {
+ if (item.hasOwnProperty(field)) {
+ var fieldType = this.convert[field]; // type may be undefined
+ d[field] = util.convert(item[field], fieldType);
+ }
+ }
+ this.data[id] = d;
+
+ return id;
};
/**
@@ -814,43 +814,43 @@ DataSet.prototype._addItem = function (item) {
* @private
*/
DataSet.prototype._getItem = function (id, convert) {
- var field, value;
-
- // get the item from the dataset
- var raw = this.data[id];
- if (!raw) {
- return null;
- }
-
- // convert the items field types
- var converted = {},
- fieldId = this.fieldId,
- internalIds = this.internalIds;
- if (convert) {
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || !(value in internalIds)) {
- converted[field] = util.convert(value, convert[field]);
- }
- }
- }
- }
- else {
- // no field types specified, no converting needed
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || !(value in internalIds)) {
- converted[field] = value;
- }
- }
- }
- }
+ var field, value;
- return converted;
+ // get the item from the dataset
+ var raw = this.data[id];
+ if (!raw) {
+ return null;
+ }
+
+ // convert the items field types
+ var converted = {},
+ fieldId = this.fieldId,
+ internalIds = this.internalIds;
+ if (convert) {
+ for (field in raw) {
+ if (raw.hasOwnProperty(field)) {
+ value = raw[field];
+ // output all fields, except internal ids
+ if ((field != fieldId) || !(value in internalIds)) {
+ converted[field] = util.convert(value, convert[field]);
+ }
+ }
+ }
+ }
+ else {
+ // no field types specified, no converting needed
+ for (field in raw) {
+ if (raw.hasOwnProperty(field)) {
+ value = raw[field];
+ // output all fields, except internal ids
+ if ((field != fieldId) || !(value in internalIds)) {
+ converted[field] = value;
+ }
+ }
+ }
+ }
+
+ return converted;
};
/**
@@ -862,25 +862,25 @@ DataSet.prototype._getItem = function (id, convert) {
* @private
*/
DataSet.prototype._updateItem = function (item) {
- var id = item[this.fieldId];
- if (id == undefined) {
- throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
- }
- var d = this.data[id];
- if (!d) {
- // item doesn't exist
- throw new Error('Cannot update item: no item with id ' + id + ' found');
- }
-
- // merge with current item
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
- }
-
- return id;
+ var id = item[this.fieldId];
+ if (id == undefined) {
+ throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
+ }
+ var d = this.data[id];
+ if (!d) {
+ // item doesn't exist
+ throw new Error('Cannot update item: no item with id ' + id + ' found');
+ }
+
+ // merge with current item
+ for (var field in item) {
+ if (item.hasOwnProperty(field)) {
+ var fieldType = this.convert[field]; // type may be undefined
+ d[field] = util.convert(item[field], fieldType);
+ }
+ }
+
+ return id;
};
/**
@@ -890,11 +890,11 @@ DataSet.prototype._updateItem = function (item) {
* @private
*/
DataSet.prototype._getColumnNames = function (dataTable) {
- var columns = [];
- for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
- columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
- }
- return columns;
+ var columns = [];
+ for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
+ columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
+ }
+ return columns;
};
/**
@@ -905,10 +905,10 @@ DataSet.prototype._getColumnNames = function (dataTable) {
* @private
*/
DataSet.prototype._appendRow = function (dataTable, columns, item) {
- var row = dataTable.addRow();
+ var row = dataTable.addRow();
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- dataTable.setValue(row, col, item[field]);
- }
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ dataTable.setValue(row, col, item[field]);
+ }
};
diff --git a/src/DataView.js b/src/DataView.js
index 540fef17..d02ffdac 100644
--- a/src/DataView.js
+++ b/src/DataView.js
@@ -9,67 +9,70 @@
* @constructor DataView
*/
function DataView (data, options) {
- this.id = util.randomUUID();
+ this.id = util.randomUUID();
- this.data = null;
- this.ids = {}; // ids of the items currently in memory (just contains a boolean true)
- this.options = options || {};
- this.fieldId = 'id'; // name of the field containing id
- this.subscribers = {}; // event subscribers
+ this.data = null;
+ this.ids = {}; // ids of the items currently in memory (just contains a boolean true)
+ this.options = options || {};
+ this.fieldId = 'id'; // name of the field containing id
+ this.subscribers = {}; // event subscribers
- var me = this;
- this.listener = function () {
- me._onEvent.apply(me, arguments);
- };
+ var me = this;
+ this.listener = function () {
+ me._onEvent.apply(me, arguments);
+ };
- this.setData(data);
+ this.setData(data);
}
+// TODO: implement a function .config() to dynamically update things like configured filter
+// and trigger changes accordingly
+
/**
* Set a data source for the view
* @param {DataSet | DataView} data
*/
DataView.prototype.setData = function (data) {
- var ids, dataItems, i, len;
+ var ids, dataItems, i, len;
- if (this.data) {
- // unsubscribe from current dataset
- if (this.data.unsubscribe) {
- this.data.unsubscribe('*', this.listener);
- }
+ if (this.data) {
+ // unsubscribe from current dataset
+ if (this.data.unsubscribe) {
+ this.data.unsubscribe('*', this.listener);
+ }
- // trigger a remove of all items in memory
- ids = [];
- for (var id in this.ids) {
- if (this.ids.hasOwnProperty(id)) {
- ids.push(id);
- }
- }
- this.ids = {};
- this._trigger('remove', {items: ids});
+ // trigger a remove of all items in memory
+ ids = [];
+ for (var id in this.ids) {
+ if (this.ids.hasOwnProperty(id)) {
+ ids.push(id);
+ }
}
+ this.ids = {};
+ this._trigger('remove', {items: ids});
+ }
- this.data = data;
+ this.data = data;
- if (this.data) {
- // update fieldId
- this.fieldId = this.options.fieldId ||
- (this.data && this.data.options && this.data.options.fieldId) ||
- 'id';
+ if (this.data) {
+ // update fieldId
+ this.fieldId = this.options.fieldId ||
+ (this.data && this.data.options && this.data.options.fieldId) ||
+ 'id';
- // trigger an add of all added items
- ids = this.data.getIds({filter: this.options && this.options.filter});
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- this.ids[id] = true;
- }
- this._trigger('add', {items: ids});
+ // trigger an add of all added items
+ ids = this.data.getIds({filter: this.options && this.options.filter});
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ this.ids[id] = true;
+ }
+ this._trigger('add', {items: ids});
- // subscribe to new dataset
- if (this.data.subscribe) {
- this.data.subscribe('*', this.listener);
- }
+ // subscribe to new dataset
+ if (this.data.subscribe) {
+ this.data.subscribe('*', this.listener);
}
+ }
};
/**
@@ -107,42 +110,42 @@ DataView.prototype.setData = function (data) {
* @param args
*/
DataView.prototype.get = function (args) {
- var me = this;
-
- // parse the arguments
- var ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
- // get(id(s) [, options] [, data])
- ids = arguments[0]; // can be a single id or an array with ids
- options = arguments[1];
- data = arguments[2];
- }
- else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
- }
+ var me = this;
- // extend the options with the default options and provided options
- var viewOptions = util.extend({}, this.options, options);
+ // parse the arguments
+ var ids, options, data;
+ var firstType = util.getType(arguments[0]);
+ if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
+ // get(id(s) [, options] [, data])
+ ids = arguments[0]; // can be a single id or an array with ids
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else {
+ // get([, options] [, data])
+ options = arguments[0];
+ data = arguments[1];
+ }
- // create a combined filter method when needed
- if (this.options.filter && options && options.filter) {
- viewOptions.filter = function (item) {
- return me.options.filter(item) && options.filter(item);
- }
- }
+ // extend the options with the default options and provided options
+ var viewOptions = util.extend({}, this.options, options);
- // build up the call to the linked data set
- var getArguments = [];
- if (ids != undefined) {
- getArguments.push(ids);
+ // create a combined filter method when needed
+ if (this.options.filter && options && options.filter) {
+ viewOptions.filter = function (item) {
+ return me.options.filter(item) && options.filter(item);
}
- getArguments.push(viewOptions);
- getArguments.push(data);
+ }
+
+ // build up the call to the linked data set
+ var getArguments = [];
+ if (ids != undefined) {
+ getArguments.push(ids);
+ }
+ getArguments.push(viewOptions);
+ getArguments.push(data);
- return this.data && this.data.get.apply(this.data, getArguments);
+ return this.data && this.data.get.apply(this.data, getArguments);
};
/**
@@ -154,36 +157,36 @@ DataView.prototype.get = function (args) {
* @return {Array} ids
*/
DataView.prototype.getIds = function (options) {
- var ids;
+ var ids;
- if (this.data) {
- var defaultFilter = this.options.filter;
- var filter;
+ if (this.data) {
+ var defaultFilter = this.options.filter;
+ var filter;
- if (options && options.filter) {
- if (defaultFilter) {
- filter = function (item) {
- return defaultFilter(item) && options.filter(item);
- }
- }
- else {
- filter = options.filter;
- }
- }
- else {
- filter = defaultFilter;
+ if (options && options.filter) {
+ if (defaultFilter) {
+ filter = function (item) {
+ return defaultFilter(item) && options.filter(item);
}
-
- ids = this.data.getIds({
- filter: filter,
- order: options && options.order
- });
+ }
+ else {
+ filter = options.filter;
+ }
}
else {
- ids = [];
+ filter = defaultFilter;
}
- return ids;
+ ids = this.data.getIds({
+ filter: filter,
+ order: options && options.order
+ });
+ }
+ else {
+ ids = [];
+ }
+
+ return ids;
};
/**
@@ -196,80 +199,80 @@ DataView.prototype.getIds = function (options) {
* @private
*/
DataView.prototype._onEvent = function (event, params, senderId) {
- var i, len, id, item,
- ids = params && params.items,
- data = this.data,
- added = [],
- updated = [],
- removed = [];
-
- if (ids && data) {
- switch (event) {
- case 'add':
- // filter the ids of the added items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
- if (item) {
- this.ids[id] = true;
- added.push(id);
- }
- }
-
- break;
-
- case 'update':
- // determine the event from the views viewpoint: an updated
- // item can be added, updated, or removed from this view.
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
-
- if (item) {
- if (this.ids[id]) {
- updated.push(id);
- }
- else {
- this.ids[id] = true;
- added.push(id);
- }
- }
- else {
- if (this.ids[id]) {
- delete this.ids[id];
- removed.push(id);
- }
- else {
- // nothing interesting for me :-(
- }
- }
- }
-
- break;
-
- case 'remove':
- // filter the ids of the removed items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- if (this.ids[id]) {
- delete this.ids[id];
- removed.push(id);
- }
- }
-
- break;
- }
+ var i, len, id, item,
+ ids = params && params.items,
+ data = this.data,
+ added = [],
+ updated = [],
+ removed = [];
- if (added.length) {
- this._trigger('add', {items: added}, senderId);
+ if (ids && data) {
+ switch (event) {
+ case 'add':
+ // filter the ids of the added items
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ item = this.get(id);
+ if (item) {
+ this.ids[id] = true;
+ added.push(id);
+ }
}
- if (updated.length) {
- this._trigger('update', {items: updated}, senderId);
+
+ break;
+
+ case 'update':
+ // determine the event from the views viewpoint: an updated
+ // item can be added, updated, or removed from this view.
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ item = this.get(id);
+
+ if (item) {
+ if (this.ids[id]) {
+ updated.push(id);
+ }
+ else {
+ this.ids[id] = true;
+ added.push(id);
+ }
+ }
+ else {
+ if (this.ids[id]) {
+ delete this.ids[id];
+ removed.push(id);
+ }
+ else {
+ // nothing interesting for me :-(
+ }
+ }
}
- if (removed.length) {
- this._trigger('remove', {items: removed}, senderId);
+
+ break;
+
+ case 'remove':
+ // filter the ids of the removed items
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ if (this.ids[id]) {
+ delete this.ids[id];
+ removed.push(id);
+ }
}
+
+ break;
+ }
+
+ if (added.length) {
+ this._trigger('add', {items: added}, senderId);
+ }
+ if (updated.length) {
+ this._trigger('update', {items: updated}, senderId);
+ }
+ if (removed.length) {
+ this._trigger('remove', {items: removed}, senderId);
}
+ }
};
// copy subscription functionality from DataSet
diff --git a/src/EventBus.js b/src/EventBus.js
index ae761a37..017739b1 100644
--- a/src/EventBus.js
+++ b/src/EventBus.js
@@ -3,7 +3,7 @@
* @constructor EventBus
*/
function EventBus() {
- this.subscriptions = [];
+ this.subscriptions = [];
}
/**
@@ -16,21 +16,21 @@ function EventBus() {
* @returns {String} id A subscription id
*/
EventBus.prototype.on = function (event, callback, target) {
- var regexp = (event instanceof RegExp) ?
- event :
- new RegExp(event.replace('*', '\\w+'));
+ var regexp = (event instanceof RegExp) ?
+ event :
+ new RegExp(event.replace('*', '\\w+'));
- var subscription = {
- id: util.randomUUID(),
- event: event,
- regexp: regexp,
- callback: (typeof callback === 'function') ? callback : null,
- target: target
- };
+ var subscription = {
+ id: util.randomUUID(),
+ event: event,
+ regexp: regexp,
+ callback: (typeof callback === 'function') ? callback : null,
+ target: target
+ };
- this.subscriptions.push(subscription);
+ this.subscriptions.push(subscription);
- return subscription.id;
+ return subscription.id;
};
/**
@@ -42,33 +42,33 @@ EventBus.prototype.on = function (event, callback, target) {
* callback, and target.
*/
EventBus.prototype.off = function (filter) {
- var i = 0;
- while (i < this.subscriptions.length) {
- var subscription = this.subscriptions[i];
+ var i = 0;
+ while (i < this.subscriptions.length) {
+ var subscription = this.subscriptions[i];
- var match = true;
- if (filter instanceof Object) {
- // filter is an object. All fields must match
- for (var prop in filter) {
- if (filter.hasOwnProperty(prop)) {
- if (filter[prop] !== subscription[prop]) {
- match = false;
- }
- }
- }
- }
- else {
- // filter is a string, filter on id
- match = (subscription.id == filter);
+ var match = true;
+ if (filter instanceof Object) {
+ // filter is an object. All fields must match
+ for (var prop in filter) {
+ if (filter.hasOwnProperty(prop)) {
+ if (filter[prop] !== subscription[prop]) {
+ match = false;
+ }
}
+ }
+ }
+ else {
+ // filter is a string, filter on id
+ match = (subscription.id == filter);
+ }
- if (match) {
- this.subscriptions.splice(i, 1);
- }
- else {
- i++;
- }
+ if (match) {
+ this.subscriptions.splice(i, 1);
+ }
+ else {
+ i++;
}
+ }
};
/**
@@ -78,12 +78,12 @@ EventBus.prototype.off = function (filter) {
* @param {*} [source]
*/
EventBus.prototype.emit = function (event, data, source) {
- for (var i =0; i < this.subscriptions.length; i++) {
- var subscription = this.subscriptions[i];
- if (subscription.regexp.test(event)) {
- if (subscription.callback) {
- subscription.callback(event, data, source);
- }
- }
+ for (var i =0; i < this.subscriptions.length; i++) {
+ var subscription = this.subscriptions[i];
+ if (subscription.regexp.test(event)) {
+ if (subscription.callback) {
+ subscription.callback(event, data, source);
+ }
}
+ }
};
diff --git a/src/events.js b/src/events.js
index 1356be25..4676aded 100644
--- a/src/events.js
+++ b/src/events.js
@@ -3,114 +3,114 @@
*/
// TODO: replace usage of the event listener for the EventBus
var events = {
- 'listeners': [],
+ 'listeners': [],
- /**
- * Find a single listener by its object
- * @param {Object} object
- * @return {Number} index -1 when not found
- */
- 'indexOf': function (object) {
- var listeners = this.listeners;
- for (var i = 0, iMax = this.listeners.length; i < iMax; i++) {
- var listener = listeners[i];
- if (listener && listener.object == object) {
- return i;
- }
- }
- return -1;
- },
+ /**
+ * Find a single listener by its object
+ * @param {Object} object
+ * @return {Number} index -1 when not found
+ */
+ 'indexOf': function (object) {
+ var listeners = this.listeners;
+ for (var i = 0, iMax = this.listeners.length; i < iMax; i++) {
+ var listener = listeners[i];
+ if (listener && listener.object == object) {
+ return i;
+ }
+ }
+ return -1;
+ },
- /**
- * Add an event listener
- * @param {Object} object
- * @param {String} event The name of an event, for example 'select'
- * @param {function} callback The callback method, called when the
- * event takes place
- */
- 'addListener': function (object, event, callback) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (!listener) {
- listener = {
- 'object': object,
- 'events': {}
- };
- this.listeners.push(listener);
- }
+ /**
+ * Add an event listener
+ * @param {Object} object
+ * @param {String} event The name of an event, for example 'select'
+ * @param {function} callback The callback method, called when the
+ * event takes place
+ */
+ 'addListener': function (object, event, callback) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (!listener) {
+ listener = {
+ 'object': object,
+ 'events': {}
+ };
+ this.listeners.push(listener);
+ }
- var callbacks = listener.events[event];
- if (!callbacks) {
- callbacks = [];
- listener.events[event] = callbacks;
- }
+ var callbacks = listener.events[event];
+ if (!callbacks) {
+ callbacks = [];
+ listener.events[event] = callbacks;
+ }
- // add the callback if it does not yet exist
- if (callbacks.indexOf(callback) == -1) {
- callbacks.push(callback);
- }
- },
+ // add the callback if it does not yet exist
+ if (callbacks.indexOf(callback) == -1) {
+ callbacks.push(callback);
+ }
+ },
- /**
- * Remove an event listener
- * @param {Object} object
- * @param {String} event The name of an event, for example 'select'
- * @param {function} callback The registered callback method
- */
- 'removeListener': function (object, event, callback) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (listener) {
- var callbacks = listener.events[event];
- if (callbacks) {
- index = callbacks.indexOf(callback);
- if (index != -1) {
- callbacks.splice(index, 1);
- }
+ /**
+ * Remove an event listener
+ * @param {Object} object
+ * @param {String} event The name of an event, for example 'select'
+ * @param {function} callback The registered callback method
+ */
+ 'removeListener': function (object, event, callback) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (listener) {
+ var callbacks = listener.events[event];
+ if (callbacks) {
+ index = callbacks.indexOf(callback);
+ if (index != -1) {
+ callbacks.splice(index, 1);
+ }
- // remove the array when empty
- if (callbacks.length == 0) {
- delete listener.events[event];
- }
- }
+ // remove the array when empty
+ if (callbacks.length == 0) {
+ delete listener.events[event];
+ }
+ }
- // count the number of registered events. remove listener when empty
- var count = 0;
- var events = listener.events;
- for (var e in events) {
- if (events.hasOwnProperty(e)) {
- count++;
- }
- }
- if (count == 0) {
- delete this.listeners[index];
- }
+ // count the number of registered events. remove listener when empty
+ var count = 0;
+ var events = listener.events;
+ for (var e in events) {
+ if (events.hasOwnProperty(e)) {
+ count++;
}
- },
+ }
+ if (count == 0) {
+ delete this.listeners[index];
+ }
+ }
+ },
- /**
- * Remove all registered event listeners
- */
- 'removeAllListeners': function () {
- this.listeners = [];
- },
+ /**
+ * Remove all registered event listeners
+ */
+ 'removeAllListeners': function () {
+ this.listeners = [];
+ },
- /**
- * Trigger an event. All registered event handlers will be called
- * @param {Object} object
- * @param {String} event
- * @param {Object} properties (optional)
- */
- 'trigger': function (object, event, properties) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (listener) {
- var callbacks = listener.events[event];
- if (callbacks) {
- for (var i = 0, iMax = callbacks.length; i < iMax; i++) {
- callbacks[i](properties);
- }
- }
+ /**
+ * Trigger an event. All registered event handlers will be called
+ * @param {Object} object
+ * @param {String} event
+ * @param {Object} properties (optional)
+ */
+ 'trigger': function (object, event, properties) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (listener) {
+ var callbacks = listener.events[event];
+ if (callbacks) {
+ for (var i = 0, iMax = callbacks.length; i < iMax; i++) {
+ callbacks[i](properties);
}
+ }
}
+ }
};
diff --git a/src/graph/Edge.js b/src/graph/Edge.js
index d4f5f25b..28aaa7cc 100644
--- a/src/graph/Edge.js
+++ b/src/graph/Edge.js
@@ -14,40 +14,40 @@
* example for the color
*/
function Edge (properties, graph, constants) {
- if (!graph) {
- throw "No graph provided";
- }
- this.graph = graph;
-
- // initialize constants
- this.widthMin = constants.edges.widthMin;
- this.widthMax = constants.edges.widthMax;
-
- // initialize variables
- this.id = undefined;
- this.fromId = undefined;
- this.toId = undefined;
- this.style = constants.edges.style;
- this.title = undefined;
- this.width = constants.edges.width;
- this.value = undefined;
- this.length = constants.edges.length;
-
- this.from = null; // a node
- this.to = null; // a node
- this.connected = false;
-
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
-
- this.stiffness = undefined; // depends on the length of the edge
- this.color = constants.edges.color;
- this.widthFixed = false;
- this.lengthFixed = false;
-
- this.setProperties(properties, constants);
+ if (!graph) {
+ throw "No graph provided";
+ }
+ this.graph = graph;
+
+ // initialize constants
+ this.widthMin = constants.edges.widthMin;
+ this.widthMax = constants.edges.widthMax;
+
+ // initialize variables
+ this.id = undefined;
+ this.fromId = undefined;
+ this.toId = undefined;
+ this.style = constants.edges.style;
+ this.title = undefined;
+ this.width = constants.edges.width;
+ this.value = undefined;
+ this.length = constants.edges.length;
+
+ this.from = null; // a node
+ this.to = null; // a node
+ this.connected = false;
+
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
+
+ this.stiffness = undefined; // depends on the length of the edge
+ this.color = constants.edges.color;
+ this.widthFixed = false;
+ this.lengthFixed = false;
+
+ this.setProperties(properties, constants);
}
/**
@@ -56,95 +56,95 @@ function Edge (properties, graph, constants) {
* @param {Object} constants and object with default, global properties
*/
Edge.prototype.setProperties = function(properties, constants) {
- if (!properties) {
- return;
- }
-
- if (properties.from != undefined) {this.fromId = properties.from;}
- if (properties.to != undefined) {this.toId = properties.to;}
-
- if (properties.id != undefined) {this.id = properties.id;}
- if (properties.style != undefined) {this.style = properties.style;}
- if (properties.label != undefined) {this.label = properties.label;}
- if (this.label) {
- this.fontSize = constants.edges.fontSize;
- this.fontFace = constants.edges.fontFace;
- this.fontColor = constants.edges.fontColor;
- if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
- if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
- if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
- }
- if (properties.title != undefined) {this.title = properties.title;}
- if (properties.width != undefined) {this.width = properties.width;}
- if (properties.value != undefined) {this.value = properties.value;}
- if (properties.length != undefined) {this.length = properties.length;}
-
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- if (properties.dash) {
- if (properties.dash.length != undefined) {this.dash.length = properties.dash.length;}
- if (properties.dash.gap != undefined) {this.dash.gap = properties.dash.gap;}
- if (properties.dash.altLength != undefined) {this.dash.altLength = properties.dash.altLength;}
- }
-
- if (properties.color != undefined) {this.color = properties.color;}
-
- // A node is connected when it has a from and to node.
- this.connect();
-
- this.widthFixed = this.widthFixed || (properties.width != undefined);
- this.lengthFixed = this.lengthFixed || (properties.length != undefined);
- this.stiffness = 1 / this.length;
-
- // set draw method based on style
- switch (this.style) {
- case 'line': this.draw = this._drawLine; break;
- case 'arrow': this.draw = this._drawArrow; break;
- case 'arrow-center': this.draw = this._drawArrowCenter; break;
- case 'dash-line': this.draw = this._drawDashLine; break;
- default: this.draw = this._drawLine; break;
- }
+ if (!properties) {
+ return;
+ }
+
+ if (properties.from != undefined) {this.fromId = properties.from;}
+ if (properties.to != undefined) {this.toId = properties.to;}
+
+ if (properties.id != undefined) {this.id = properties.id;}
+ if (properties.style != undefined) {this.style = properties.style;}
+ if (properties.label != undefined) {this.label = properties.label;}
+ if (this.label) {
+ this.fontSize = constants.edges.fontSize;
+ this.fontFace = constants.edges.fontFace;
+ this.fontColor = constants.edges.fontColor;
+ if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
+ if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
+ if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
+ }
+ if (properties.title != undefined) {this.title = properties.title;}
+ if (properties.width != undefined) {this.width = properties.width;}
+ if (properties.value != undefined) {this.value = properties.value;}
+ if (properties.length != undefined) {this.length = properties.length;}
+
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ if (properties.dash) {
+ if (properties.dash.length != undefined) {this.dash.length = properties.dash.length;}
+ if (properties.dash.gap != undefined) {this.dash.gap = properties.dash.gap;}
+ if (properties.dash.altLength != undefined) {this.dash.altLength = properties.dash.altLength;}
+ }
+
+ if (properties.color != undefined) {this.color = properties.color;}
+
+ // A node is connected when it has a from and to node.
+ this.connect();
+
+ this.widthFixed = this.widthFixed || (properties.width != undefined);
+ this.lengthFixed = this.lengthFixed || (properties.length != undefined);
+ this.stiffness = 1 / this.length;
+
+ // set draw method based on style
+ switch (this.style) {
+ case 'line': this.draw = this._drawLine; break;
+ case 'arrow': this.draw = this._drawArrow; break;
+ case 'arrow-center': this.draw = this._drawArrowCenter; break;
+ case 'dash-line': this.draw = this._drawDashLine; break;
+ default: this.draw = this._drawLine; break;
+ }
};
/**
* Connect an edge to its nodes
*/
Edge.prototype.connect = function () {
- this.disconnect();
+ this.disconnect();
- this.from = this.graph.nodes[this.fromId] || null;
- this.to = this.graph.nodes[this.toId] || null;
- this.connected = (this.from && this.to);
+ this.from = this.graph.nodes[this.fromId] || null;
+ this.to = this.graph.nodes[this.toId] || null;
+ this.connected = (this.from && this.to);
- if (this.connected) {
- this.from.attachEdge(this);
- this.to.attachEdge(this);
+ if (this.connected) {
+ this.from.attachEdge(this);
+ this.to.attachEdge(this);
+ }
+ else {
+ if (this.from) {
+ this.from.detachEdge(this);
}
- else {
- if (this.from) {
- this.from.detachEdge(this);
- }
- if (this.to) {
- this.to.detachEdge(this);
- }
+ if (this.to) {
+ this.to.detachEdge(this);
}
+ }
};
/**
* Disconnect an edge from its nodes
*/
Edge.prototype.disconnect = function () {
- if (this.from) {
- this.from.detachEdge(this);
- this.from = null;
- }
- if (this.to) {
- this.to.detachEdge(this);
- this.to = null;
- }
-
- this.connected = false;
+ if (this.from) {
+ this.from.detachEdge(this);
+ this.from = null;
+ }
+ if (this.to) {
+ this.to.detachEdge(this);
+ this.to = null;
+ }
+
+ this.connected = false;
};
/**
@@ -153,7 +153,7 @@ Edge.prototype.disconnect = function () {
* has been set.
*/
Edge.prototype.getTitle = function() {
- return this.title;
+ return this.title;
};
@@ -162,7 +162,7 @@ Edge.prototype.getTitle = function() {
* @return {Number} value
*/
Edge.prototype.getValue = function() {
- return this.value;
+ return this.value;
};
/**
@@ -172,10 +172,10 @@ Edge.prototype.getValue = function() {
* @param {Number} max
*/
Edge.prototype.setValueRange = function(min, max) {
- if (!this.widthFixed && this.value !== undefined) {
- var scale = (this.widthMax - this.widthMin) / (max - min);
- this.width = (this.value - min) * scale + this.widthMin;
- }
+ if (!this.widthFixed && this.value !== undefined) {
+ var scale = (this.widthMax - this.widthMin) / (max - min);
+ this.width = (this.value - min) * scale + this.widthMin;
+ }
};
/**
@@ -185,7 +185,7 @@ Edge.prototype.setValueRange = function(min, max) {
* @param {CanvasRenderingContext2D} ctx
*/
Edge.prototype.draw = function(ctx) {
- throw "Method draw not initialized in edge";
+ throw "Method draw not initialized in edge";
};
/**
@@ -194,19 +194,19 @@ Edge.prototype.draw = function(ctx) {
* @return {boolean} True if location is located on the edge
*/
Edge.prototype.isOverlappingWith = function(obj) {
- var distMax = 10;
+ var distMax = 10;
- var xFrom = this.from.x;
- var yFrom = this.from.y;
- var xTo = this.to.x;
- var yTo = this.to.y;
- var xObj = obj.left;
- var yObj = obj.top;
+ var xFrom = this.from.x;
+ var yFrom = this.from.y;
+ var xTo = this.to.x;
+ var yTo = this.to.y;
+ var xObj = obj.left;
+ var yObj = obj.top;
- var dist = Edge._dist(xFrom, yFrom, xTo, yTo, xObj, yObj);
+ var dist = Edge._dist(xFrom, yFrom, xTo, yTo, xObj, yObj);
- return (dist < distMax);
+ return (dist < distMax);
};
@@ -218,40 +218,40 @@ Edge.prototype.isOverlappingWith = function(obj) {
* @private
*/
Edge.prototype._drawLine = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
-
- var point;
- if (this.from != this.to) {
- // draw line
- this._line(ctx);
-
- // draw label
- if (this.label) {
- point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ var point;
+ if (this.from != this.to) {
+ // draw line
+ this._line(ctx);
+
+ // draw label
+ if (this.label) {
+ point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ var x, y;
+ var radius = this.length / 4;
+ var node = this.from;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
}
else {
- var x, y;
- var radius = this.length / 4;
- var node = this.from;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- }
- this._circle(ctx, x, y, radius);
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
+ x = node.x + radius;
+ y = node.y - node.height / 2;
}
+ this._circle(ctx, x, y, radius);
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
};
/**
@@ -261,12 +261,12 @@ Edge.prototype._drawLine = function(ctx) {
* @private
*/
Edge.prototype._getLineWidth = function() {
- if (this.from.selected || this.to.selected) {
- return Math.min(this.width * 2, this.widthMax);
- }
- else {
- return this.width;
- }
+ if (this.from.selected || this.to.selected) {
+ return Math.min(this.width * 2, this.widthMax);
+ }
+ else {
+ return this.width;
+ }
};
/**
@@ -275,11 +275,11 @@ Edge.prototype._getLineWidth = function() {
* @private
*/
Edge.prototype._line = function (ctx) {
- // draw a straight line
- ctx.beginPath();
- ctx.moveTo(this.from.x, this.from.y);
- ctx.lineTo(this.to.x, this.to.y);
- ctx.stroke();
+ // draw a straight line
+ ctx.beginPath();
+ ctx.moveTo(this.from.x, this.from.y);
+ ctx.lineTo(this.to.x, this.to.y);
+ ctx.stroke();
};
/**
@@ -291,10 +291,10 @@ Edge.prototype._line = function (ctx) {
* @private
*/
Edge.prototype._circle = function (ctx, x, y, radius) {
- // draw a circle
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
- ctx.stroke();
+ // draw a circle
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ ctx.stroke();
};
/**
@@ -306,24 +306,24 @@ Edge.prototype._circle = function (ctx, x, y, radius) {
* @private
*/
Edge.prototype._label = function (ctx, text, x, y) {
- if (text) {
- // TODO: cache the calculated size
- ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
- this.fontSize + "px " + this.fontFace;
- ctx.fillStyle = 'white';
- var width = ctx.measureText(text).width;
- var height = this.fontSize;
- var left = x - width / 2;
- var top = y - height / 2;
-
- ctx.fillRect(left, top, width, height);
-
- // draw text
- ctx.fillStyle = this.fontColor || "black";
- ctx.textAlign = "left";
- ctx.textBaseline = "top";
- ctx.fillText(text, left, top);
- }
+ if (text) {
+ // TODO: cache the calculated size
+ ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
+ this.fontSize + "px " + this.fontFace;
+ ctx.fillStyle = 'white';
+ var width = ctx.measureText(text).width;
+ var height = this.fontSize;
+ var left = x - width / 2;
+ var top = y - height / 2;
+
+ ctx.fillRect(left, top, width, height);
+
+ // draw text
+ ctx.fillStyle = this.fontColor || "black";
+ ctx.textAlign = "left";
+ ctx.textBaseline = "top";
+ ctx.fillText(text, left, top);
+ }
};
/**
@@ -336,35 +336,35 @@ Edge.prototype._label = function (ctx, text, x, y) {
* @private
*/
Edge.prototype._drawDashLine = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
-
- // draw dashed line
- ctx.beginPath();
- ctx.lineCap = 'round';
- if (this.dash.altLength != undefined) //If an alt dash value has been set add to the array this value
- {
- ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
- [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
- }
- else if (this.dash.length != undefined && this.dash.gap != undefined) //If a dash and gap value has been set add to the array this value
- {
- ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
- [this.dash.length,this.dash.gap]);
- }
- else //If all else fails draw a line
- {
- ctx.moveTo(this.from.x, this.from.y);
- ctx.lineTo(this.to.x, this.to.y);
- }
- ctx.stroke();
-
- // draw label
- if (this.label) {
- var point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ // draw dashed line
+ ctx.beginPath();
+ ctx.lineCap = 'round';
+ if (this.dash.altLength != undefined) //If an alt dash value has been set add to the array this value
+ {
+ ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
+ [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
+ }
+ else if (this.dash.length != undefined && this.dash.gap != undefined) //If a dash and gap value has been set add to the array this value
+ {
+ ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
+ [this.dash.length,this.dash.gap]);
+ }
+ else //If all else fails draw a line
+ {
+ ctx.moveTo(this.from.x, this.from.y);
+ ctx.lineTo(this.to.x, this.to.y);
+ }
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ var point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
};
/**
@@ -374,10 +374,10 @@ Edge.prototype._drawDashLine = function(ctx) {
* @private
*/
Edge.prototype._pointOnLine = function (percentage) {
- return {
- x: (1 - percentage) * this.from.x + percentage * this.to.x,
- y: (1 - percentage) * this.from.y + percentage * this.to.y
- }
+ return {
+ x: (1 - percentage) * this.from.x + percentage * this.to.x,
+ y: (1 - percentage) * this.from.y + percentage * this.to.y
+ }
};
/**
@@ -390,11 +390,11 @@ Edge.prototype._pointOnLine = function (percentage) {
* @private
*/
Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
- var angle = (percentage - 3/8) * 2 * Math.PI;
- return {
- x: x + radius * Math.cos(angle),
- y: y - radius * Math.sin(angle)
- }
+ var angle = (percentage - 3/8) * 2 * Math.PI;
+ return {
+ x: x + radius * Math.cos(angle),
+ y: y - radius * Math.sin(angle)
+ }
};
/**
@@ -405,62 +405,62 @@ Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
* @private
*/
Edge.prototype._drawArrowCenter = function(ctx) {
- var point;
- // set style
- ctx.strokeStyle = this.color;
- ctx.fillStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
-
- if (this.from != this.to) {
- // draw line
- this._line(ctx);
-
- // draw an arrow halfway the line
- var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
- var length = 10 + 5 * this.width; // TODO: make customizable?
- point = this._pointOnLine(0.5);
- ctx.arrow(point.x, point.y, angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ var point;
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.fillStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ if (this.from != this.to) {
+ // draw line
+ this._line(ctx);
+
+ // draw an arrow halfway the line
+ var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
+ var length = 10 + 5 * this.width; // TODO: make customizable?
+ point = this._pointOnLine(0.5);
+ ctx.arrow(point.x, point.y, angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ // draw circle
+ var x, y;
+ var radius = this.length / 4;
+ var node = this.from;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
}
else {
- // draw circle
- var x, y;
- var radius = this.length / 4;
- var node = this.from;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- }
- this._circle(ctx, x, y, radius);
-
- // draw all arrows
- var angle = 0.2 * Math.PI;
- var length = 10 + 5 * this.width; // TODO: make customizable?
- point = this._pointOnCircle(x, y, radius, 0.5);
- ctx.arrow(point.x, point.y, angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ x = node.x + radius;
+ y = node.y - node.height / 2;
}
+ this._circle(ctx, x, y, radius);
+
+ // draw all arrows
+ var angle = 0.2 * Math.PI;
+ var length = 10 + 5 * this.width; // TODO: make customizable?
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ ctx.arrow(point.x, point.y, angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
};
@@ -473,91 +473,91 @@ Edge.prototype._drawArrowCenter = function(ctx) {
* @private
*/
Edge.prototype._drawArrow = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.fillStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.fillStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ // draw line
+ var angle, length;
+ if (this.from != this.to) {
+ // calculate length and angle of the line
+ angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
+ var dx = (this.to.x - this.from.x);
+ var dy = (this.to.y - this.from.y);
+ var lEdge = Math.sqrt(dx * dx + dy * dy);
+
+ var lFrom = this.from.distanceToBorder(ctx, angle + Math.PI);
+ var pFrom = (lEdge - lFrom) / lEdge;
+ var xFrom = (pFrom) * this.from.x + (1 - pFrom) * this.to.x;
+ var yFrom = (pFrom) * this.from.y + (1 - pFrom) * this.to.y;
+
+ var lTo = this.to.distanceToBorder(ctx, angle);
+ var pTo = (lEdge - lTo) / lEdge;
+ var xTo = (1 - pTo) * this.from.x + pTo * this.to.x;
+ var yTo = (1 - pTo) * this.from.y + pTo * this.to.y;
- // draw line
- var angle, length;
- if (this.from != this.to) {
- // calculate length and angle of the line
- angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
- var dx = (this.to.x - this.from.x);
- var dy = (this.to.y - this.from.y);
- var lEdge = Math.sqrt(dx * dx + dy * dy);
-
- var lFrom = this.from.distanceToBorder(ctx, angle + Math.PI);
- var pFrom = (lEdge - lFrom) / lEdge;
- var xFrom = (pFrom) * this.from.x + (1 - pFrom) * this.to.x;
- var yFrom = (pFrom) * this.from.y + (1 - pFrom) * this.to.y;
-
- var lTo = this.to.distanceToBorder(ctx, angle);
- var pTo = (lEdge - lTo) / lEdge;
- var xTo = (1 - pTo) * this.from.x + pTo * this.to.x;
- var yTo = (1 - pTo) * this.from.y + pTo * this.to.y;
-
- ctx.beginPath();
- ctx.moveTo(xFrom, yFrom);
- ctx.lineTo(xTo, yTo);
- ctx.stroke();
-
- // draw arrow at the end of the line
- length = 10 + 5 * this.width; // TODO: make customizable?
- ctx.arrow(xTo, yTo, angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- var point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ ctx.beginPath();
+ ctx.moveTo(xFrom, yFrom);
+ ctx.lineTo(xTo, yTo);
+ ctx.stroke();
+
+ // draw arrow at the end of the line
+ length = 10 + 5 * this.width; // TODO: make customizable?
+ ctx.arrow(xTo, yTo, angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ var point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ // draw circle
+ var node = this.from;
+ var x, y, arrow;
+ var radius = this.length / 4;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
+ arrow = {
+ x: x,
+ y: node.y,
+ angle: 0.9 * Math.PI
+ };
}
else {
- // draw circle
- var node = this.from;
- var x, y, arrow;
- var radius = this.length / 4;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- arrow = {
- x: x,
- y: node.y,
- angle: 0.9 * Math.PI
- };
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- arrow = {
- x: node.x,
- y: y,
- angle: 0.6 * Math.PI
- };
- }
- ctx.beginPath();
- // TODO: do not draw a circle, but an arc
- // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
- ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
- ctx.stroke();
-
- // draw all arrows
- length = 10 + 5 * this.width; // TODO: make customizable?
- ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ x = node.x + radius;
+ y = node.y - node.height / 2;
+ arrow = {
+ x: node.x,
+ y: y,
+ angle: 0.6 * Math.PI
+ };
}
+ ctx.beginPath();
+ // TODO: do not draw a circle, but an arc
+ // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
+ ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ ctx.stroke();
+
+ // draw all arrows
+ length = 10 + 5 * this.width; // TODO: make customizable?
+ ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
};
@@ -575,28 +575,28 @@ Edge.prototype._drawArrow = function(ctx) {
* @private
*/
Edge._dist = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
- var px = x2-x1,
- py = y2-y1,
- something = px*px + py*py,
- u = ((x3 - x1) * px + (y3 - y1) * py) / something;
-
- if (u > 1) {
- u = 1;
- }
- else if (u < 0) {
- u = 0;
- }
-
- var x = x1 + u * px,
- y = y1 + u * py,
- dx = x - x3,
- dy = y - y3;
-
- //# Note: If the actual distance does not matter,
- //# if you only want to compare what this function
- //# returns to other results of this function, you
- //# can just return the squared distance instead
- //# (i.e. remove the sqrt) to gain a little performance
-
- return Math.sqrt(dx*dx + dy*dy);
+ var px = x2-x1,
+ py = y2-y1,
+ something = px*px + py*py,
+ u = ((x3 - x1) * px + (y3 - y1) * py) / something;
+
+ if (u > 1) {
+ u = 1;
+ }
+ else if (u < 0) {
+ u = 0;
+ }
+
+ var x = x1 + u * px,
+ y = y1 + u * py,
+ dx = x - x3,
+ dy = y - y3;
+
+ //# Note: If the actual distance does not matter,
+ //# if you only want to compare what this function
+ //# returns to other results of this function, you
+ //# can just return the squared distance instead
+ //# (i.e. remove the sqrt) to gain a little performance
+
+ return Math.sqrt(dx*dx + dy*dy);
};
diff --git a/src/graph/Graph.js b/src/graph/Graph.js
index 7e26fc5c..8b783cb3 100644
--- a/src/graph/Graph.js
+++ b/src/graph/Graph.js
@@ -1,7 +1,7 @@
/**
* @constructor Graph
* Create a graph visualization, displaying nodes and edges.
- *
+ *
* @param {Element} container The DOM element in which the Graph will
* be created. Normally a div element.
* @param {Object} data An object containing parameters
@@ -10,125 +10,125 @@
* @param {Object} options Options
*/
function Graph (container, data, options) {
- // create variables and set default values
- this.containerElement = container;
- this.width = '100%';
- this.height = '100%';
- this.refreshRate = 50; // milliseconds
- this.stabilize = true; // stabilize before displaying the graph
- this.selectable = true;
-
- // set constant values
- this.constants = {
- nodes: {
- radiusMin: 5,
- radiusMax: 20,
- radius: 5,
- distance: 100, // px
- shape: 'ellipse',
- image: undefined,
- widthMin: 16, // px
- widthMax: 64, // px
- fontColor: 'black',
- fontSize: 14, // px
- //fontFace: verdana,
- fontFace: 'arial',
- color: {
- border: '#2B7CE9',
- background: '#97C2FC',
- highlight: {
- border: '#2B7CE9',
- background: '#D2E5FF'
- }
- },
- borderColor: '#2B7CE9',
- backgroundColor: '#97C2FC',
- highlightColor: '#D2E5FF',
- group: undefined
- },
- edges: {
- widthMin: 1,
- widthMax: 15,
- width: 1,
- style: 'line',
- color: '#343434',
- fontColor: '#343434',
- fontSize: 14, // px
- fontFace: 'arial',
- //distance: 100, //px
- length: 100, // px
- dash: {
- length: 10,
- gap: 5,
- altLength: undefined
- }
- },
- minForce: 0.05,
- minVelocity: 0.02, // px/s
- maxIterations: 1000 // maximum number of iteration to stabilize
- };
-
- var graph = this;
- this.nodes = {}; // object with Node objects
- this.edges = {}; // object with Edge objects
- // TODO: create a counter to keep track on the number of nodes having values
- // TODO: create a counter to keep track on the number of nodes currently moving
- // TODO: create a counter to keep track on the number of edges having values
-
- this.nodesData = null; // A DataSet or DataView
- this.edgesData = null; // A DataSet or DataView
-
- // create event listeners used to subscribe on the DataSets of the nodes and edges
- var me = this;
- this.nodesListeners = {
- 'add': function (event, params) {
- me._addNodes(params.items);
- me.start();
- },
- 'update': function (event, params) {
- me._updateNodes(params.items);
- me.start();
- },
- 'remove': function (event, params) {
- me._removeNodes(params.items);
- me.start();
+ // create variables and set default values
+ this.containerElement = container;
+ this.width = '100%';
+ this.height = '100%';
+ this.refreshRate = 50; // milliseconds
+ this.stabilize = true; // stabilize before displaying the graph
+ this.selectable = true;
+
+ // set constant values
+ this.constants = {
+ nodes: {
+ radiusMin: 5,
+ radiusMax: 20,
+ radius: 5,
+ distance: 100, // px
+ shape: 'ellipse',
+ image: undefined,
+ widthMin: 16, // px
+ widthMax: 64, // px
+ fontColor: 'black',
+ fontSize: 14, // px
+ //fontFace: verdana,
+ fontFace: 'arial',
+ color: {
+ border: '#2B7CE9',
+ background: '#97C2FC',
+ highlight: {
+ border: '#2B7CE9',
+ background: '#D2E5FF'
}
- };
- this.edgesListeners = {
- 'add': function (event, params) {
- me._addEdges(params.items);
- me.start();
- },
- 'update': function (event, params) {
- me._updateEdges(params.items);
- me.start();
- },
- 'remove': function (event, params) {
- me._removeEdges(params.items);
- me.start();
- }
- };
-
- this.groups = new Groups(); // object with groups
- this.images = new Images(); // object with images
- this.images.setOnloadCallback(function () {
- graph._redraw();
- });
-
- // properties of the data
- this.moving = false; // True if any of the nodes have an undefined position
-
- this.selection = [];
- this.timer = undefined;
-
- // create a frame and canvas
- this._create();
-
- // apply options
- this.setOptions(options);
-
- // draw data
- this.setData(data);
+ },
+ borderColor: '#2B7CE9',
+ backgroundColor: '#97C2FC',
+ highlightColor: '#D2E5FF',
+ group: undefined
+ },
+ edges: {
+ widthMin: 1,
+ widthMax: 15,
+ width: 1,
+ style: 'line',
+ color: '#343434',
+ fontColor: '#343434',
+ fontSize: 14, // px
+ fontFace: 'arial',
+ //distance: 100, //px
+ length: 100, // px
+ dash: {
+ length: 10,
+ gap: 5,
+ altLength: undefined
+ }
+ },
+ minForce: 0.05,
+ minVelocity: 0.02, // px/s
+ maxIterations: 1000 // maximum number of iteration to stabilize
+ };
+
+ var graph = this;
+ this.nodes = {}; // object with Node objects
+ this.edges = {}; // object with Edge objects
+ // TODO: create a counter to keep track on the number of nodes having values
+ // TODO: create a counter to keep track on the number of nodes currently moving
+ // TODO: create a counter to keep track on the number of edges having values
+
+ this.nodesData = null; // A DataSet or DataView
+ this.edgesData = null; // A DataSet or DataView
+
+ // create event listeners used to subscribe on the DataSets of the nodes and edges
+ var me = this;
+ this.nodesListeners = {
+ 'add': function (event, params) {
+ me._addNodes(params.items);
+ me.start();
+ },
+ 'update': function (event, params) {
+ me._updateNodes(params.items);
+ me.start();
+ },
+ 'remove': function (event, params) {
+ me._removeNodes(params.items);
+ me.start();
+ }
+ };
+ this.edgesListeners = {
+ 'add': function (event, params) {
+ me._addEdges(params.items);
+ me.start();
+ },
+ 'update': function (event, params) {
+ me._updateEdges(params.items);
+ me.start();
+ },
+ 'remove': function (event, params) {
+ me._removeEdges(params.items);
+ me.start();
+ }
+ };
+
+ this.groups = new Groups(); // object with groups
+ this.images = new Images(); // object with images
+ this.images.setOnloadCallback(function () {
+ graph._redraw();
+ });
+
+ // properties of the data
+ this.moving = false; // True if any of the nodes have an undefined position
+
+ this.selection = [];
+ this.timer = undefined;
+
+ // create a frame and canvas
+ this._create();
+
+ // apply options
+ this.setOptions(options);
+
+ // draw data
+ this.setData(data);
}
/**
@@ -141,33 +141,33 @@ function Graph (container, data, options) {
* {Options} [options] Object with options
*/
Graph.prototype.setData = function(data) {
- if (data && data.dot && (data.nodes || data.edges)) {
- throw new SyntaxError('Data must contain either parameter "dot" or ' +
- ' parameter pair "nodes" and "edges", but not both.');
- }
-
- // set options
- this.setOptions(data && data.options);
-
- // set all data
- if (data && data.dot) {
- // parse DOT file
- if(data && data.dot) {
- var dotData = vis.util.DOTToGraph(data.dot);
- this.setData(dotData);
- return;
- }
- }
- else {
- this._setNodes(data && data.nodes);
- this._setEdges(data && data.edges);
- }
-
- // find a stable position or start animating to a stable position
- if (this.stabilize) {
- this._doStabilize();
- }
- this.start();
+ if (data && data.dot && (data.nodes || data.edges)) {
+ throw new SyntaxError('Data must contain either parameter "dot" or ' +
+ ' parameter pair "nodes" and "edges", but not both.');
+ }
+
+ // set options
+ this.setOptions(data && data.options);
+
+ // set all data
+ if (data && data.dot) {
+ // parse DOT file
+ if(data && data.dot) {
+ var dotData = vis.util.DOTToGraph(data.dot);
+ this.setData(dotData);
+ return;
+ }
+ }
+ else {
+ this._setNodes(data && data.nodes);
+ this._setEdges(data && data.edges);
+ }
+
+ // find a stable position or start animating to a stable position
+ if (this.stabilize) {
+ this._doStabilize();
+ }
+ this.start();
};
/**
@@ -175,77 +175,77 @@ Graph.prototype.setData = function(data) {
* @param {Object} options
*/
Graph.prototype.setOptions = function (options) {
- if (options) {
- // retrieve parameter values
- if (options.width != undefined) {this.width = options.width;}
- if (options.height != undefined) {this.height = options.height;}
- if (options.stabilize != undefined) {this.stabilize = options.stabilize;}
- if (options.selectable != undefined) {this.selectable = options.selectable;}
-
- // TODO: work out these options and document them
- if (options.edges) {
- for (var prop in options.edges) {
- if (options.edges.hasOwnProperty(prop)) {
- this.constants.edges[prop] = options.edges[prop];
- }
- }
-
- if (options.edges.length != undefined &&
- options.nodes && options.nodes.distance == undefined) {
- this.constants.edges.length = options.edges.length;
- this.constants.nodes.distance = options.edges.length * 1.25;
- }
-
- if (!options.edges.fontColor) {
- this.constants.edges.fontColor = options.edges.color;
- }
-
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- if (options.edges.dash) {
- if (options.edges.dash.length != undefined) {
- this.constants.edges.dash.length = options.edges.dash.length;
- }
- if (options.edges.dash.gap != undefined) {
- this.constants.edges.dash.gap = options.edges.dash.gap;
- }
- if (options.edges.dash.altLength != undefined) {
- this.constants.edges.dash.altLength = options.edges.dash.altLength;
- }
- }
+ if (options) {
+ // retrieve parameter values
+ if (options.width != undefined) {this.width = options.width;}
+ if (options.height != undefined) {this.height = options.height;}
+ if (options.stabilize != undefined) {this.stabilize = options.stabilize;}
+ if (options.selectable != undefined) {this.selectable = options.selectable;}
+
+ // TODO: work out these options and document them
+ if (options.edges) {
+ for (var prop in options.edges) {
+ if (options.edges.hasOwnProperty(prop)) {
+ this.constants.edges[prop] = options.edges[prop];
+ }
+ }
+
+ if (options.edges.length != undefined &&
+ options.nodes && options.nodes.distance == undefined) {
+ this.constants.edges.length = options.edges.length;
+ this.constants.nodes.distance = options.edges.length * 1.25;
+ }
+
+ if (!options.edges.fontColor) {
+ this.constants.edges.fontColor = options.edges.color;
+ }
+
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ if (options.edges.dash) {
+ if (options.edges.dash.length != undefined) {
+ this.constants.edges.dash.length = options.edges.dash.length;
+ }
+ if (options.edges.dash.gap != undefined) {
+ this.constants.edges.dash.gap = options.edges.dash.gap;
}
+ if (options.edges.dash.altLength != undefined) {
+ this.constants.edges.dash.altLength = options.edges.dash.altLength;
+ }
+ }
+ }
- if (options.nodes) {
- for (prop in options.nodes) {
- if (options.nodes.hasOwnProperty(prop)) {
- this.constants.nodes[prop] = options.nodes[prop];
- }
- }
+ if (options.nodes) {
+ for (prop in options.nodes) {
+ if (options.nodes.hasOwnProperty(prop)) {
+ this.constants.nodes[prop] = options.nodes[prop];
+ }
+ }
- if (options.nodes.color) {
- this.constants.nodes.color = Node.parseColor(options.nodes.color);
- }
+ if (options.nodes.color) {
+ this.constants.nodes.color = Node.parseColor(options.nodes.color);
+ }
- /*
- if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin;
- if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax;
- */
- }
+ /*
+ if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin;
+ if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax;
+ */
+ }
- if (options.groups) {
- for (var groupname in options.groups) {
- if (options.groups.hasOwnProperty(groupname)) {
- var group = options.groups[groupname];
- this.groups.add(groupname, group);
- }
- }
+ if (options.groups) {
+ for (var groupname in options.groups) {
+ if (options.groups.hasOwnProperty(groupname)) {
+ var group = options.groups[groupname];
+ this.groups.add(groupname, group);
}
+ }
}
+ }
- this.setSize(this.width, this.height);
- this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
- this._setScale(1);
+ this.setSize(this.width, this.height);
+ this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
+ this._setScale(1);
};
/**
@@ -255,7 +255,7 @@ Graph.prototype.setOptions = function (options) {
* @private
*/
Graph.prototype._trigger = function (event, params) {
- events.trigger(this, event, params);
+ events.trigger(this, event, params);
};
@@ -267,48 +267,48 @@ Graph.prototype._trigger = function (event, params) {
* @private
*/
Graph.prototype._create = function () {
- // remove all elements from the container element.
- while (this.containerElement.hasChildNodes()) {
- this.containerElement.removeChild(this.containerElement.firstChild);
- }
-
- this.frame = document.createElement('div');
- this.frame.className = 'graph-frame';
- this.frame.style.position = 'relative';
- this.frame.style.overflow = 'hidden';
-
- // create the graph canvas (HTML canvas element)
- this.frame.canvas = document.createElement( 'canvas' );
- this.frame.canvas.style.position = 'relative';
- this.frame.appendChild(this.frame.canvas);
- if (!this.frame.canvas.getContext) {
- var noCanvas = document.createElement( 'DIV' );
- noCanvas.style.color = 'red';
- noCanvas.style.fontWeight = 'bold' ;
- noCanvas.style.padding = '10px';
- noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
- this.frame.canvas.appendChild(noCanvas);
- }
-
- var me = this;
- this.drag = {};
- this.pinch = {};
- this.hammer = Hammer(this.frame.canvas, {
- prevent_default: true
- });
- this.hammer.on('tap', me._onTap.bind(me) );
- this.hammer.on('hold', me._onHold.bind(me) );
- this.hammer.on('pinch', me._onPinch.bind(me) );
- this.hammer.on('touch', me._onTouch.bind(me) );
- this.hammer.on('dragstart', me._onDragStart.bind(me) );
- this.hammer.on('drag', me._onDrag.bind(me) );
- this.hammer.on('dragend', me._onDragEnd.bind(me) );
- this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
- this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
- this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
-
- // add the frame to the container element
- this.containerElement.appendChild(this.frame);
+ // remove all elements from the container element.
+ while (this.containerElement.hasChildNodes()) {
+ this.containerElement.removeChild(this.containerElement.firstChild);
+ }
+
+ this.frame = document.createElement('div');
+ this.frame.className = 'graph-frame';
+ this.frame.style.position = 'relative';
+ this.frame.style.overflow = 'hidden';
+
+ // create the graph canvas (HTML canvas element)
+ this.frame.canvas = document.createElement( 'canvas' );
+ this.frame.canvas.style.position = 'relative';
+ this.frame.appendChild(this.frame.canvas);
+ if (!this.frame.canvas.getContext) {
+ var noCanvas = document.createElement( 'DIV' );
+ noCanvas.style.color = 'red';
+ noCanvas.style.fontWeight = 'bold' ;
+ noCanvas.style.padding = '10px';
+ noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
+ this.frame.canvas.appendChild(noCanvas);
+ }
+
+ var me = this;
+ this.drag = {};
+ this.pinch = {};
+ this.hammer = Hammer(this.frame.canvas, {
+ prevent_default: true
+ });
+ this.hammer.on('tap', me._onTap.bind(me) );
+ this.hammer.on('hold', me._onHold.bind(me) );
+ this.hammer.on('pinch', me._onPinch.bind(me) );
+ this.hammer.on('touch', me._onTouch.bind(me) );
+ this.hammer.on('dragstart', me._onDragStart.bind(me) );
+ this.hammer.on('drag', me._onDrag.bind(me) );
+ this.hammer.on('dragend', me._onDragEnd.bind(me) );
+ this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
+ this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
+ this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
+
+ // add the frame to the container element
+ this.containerElement.appendChild(this.frame);
};
/**
@@ -318,21 +318,21 @@ Graph.prototype._create = function () {
* @private
*/
Graph.prototype._getNodeAt = function (pointer) {
- var x = this._canvasToX(pointer.x);
- var y = this._canvasToY(pointer.y);
-
- var obj = {
- left: x,
- top: y,
- right: x,
- bottom: y
- };
-
- // if there are overlapping nodes, select the last one, this is the
- // one which is drawn on top of the others
- var overlappingNodes = this._getNodesOverlappingWith(obj);
- return (overlappingNodes.length > 0) ?
- overlappingNodes[overlappingNodes.length - 1] : null;
+ var x = this._canvasToX(pointer.x);
+ var y = this._canvasToY(pointer.y);
+
+ var obj = {
+ left: x,
+ top: y,
+ right: x,
+ bottom: y
+ };
+
+ // if there are overlapping nodes, select the last one, this is the
+ // one which is drawn on top of the others
+ var overlappingNodes = this._getNodesOverlappingWith(obj);
+ return (overlappingNodes.length > 0) ?
+ overlappingNodes[overlappingNodes.length - 1] : null;
};
/**
@@ -342,10 +342,10 @@ Graph.prototype._getNodeAt = function (pointer) {
* @private
*/
Graph.prototype._getPointer = function (touch) {
- return {
- x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas),
- y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas)
- };
+ return {
+ x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas),
+ y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas)
+ };
};
/**
@@ -354,9 +354,9 @@ Graph.prototype._getPointer = function (touch) {
* @private
*/
Graph.prototype._onTouch = function (event) {
- this.drag.pointer = this._getPointer(event.gesture.touches[0]);
- this.drag.pinched = false;
- this.pinch.scale = this._getScale();
+ this.drag.pointer = this._getPointer(event.gesture.touches[0]);
+ this.drag.pinched = false;
+ this.pinch.scale = this._getScale();
};
/**
@@ -364,44 +364,44 @@ Graph.prototype._onTouch = function (event) {
* @private
*/
Graph.prototype._onDragStart = function () {
- var drag = this.drag;
+ var drag = this.drag;
- drag.selection = [];
- drag.translation = this._getTranslation();
- drag.nodeId = this._getNodeAt(drag.pointer);
- // note: drag.pointer is set in _onTouch to get the initial touch location
+ drag.selection = [];
+ drag.translation = this._getTranslation();
+ drag.nodeId = this._getNodeAt(drag.pointer);
+ // note: drag.pointer is set in _onTouch to get the initial touch location
- var node = this.nodes[drag.nodeId];
- if (node) {
- // select the clicked node if not yet selected
- if (!node.isSelected()) {
- this._selectNodes([drag.nodeId]);
- }
+ var node = this.nodes[drag.nodeId];
+ if (node) {
+ // select the clicked node if not yet selected
+ if (!node.isSelected()) {
+ this._selectNodes([drag.nodeId]);
+ }
- // create an array with the selected nodes and their original location and status
- var me = this;
- this.selection.forEach(function (id) {
- var node = me.nodes[id];
- if (node) {
- var s = {
- id: id,
- node: node,
-
- // store original x, y, xFixed and yFixed, make the node temporarily Fixed
- x: node.x,
- y: node.y,
- xFixed: node.xFixed,
- yFixed: node.yFixed
- };
-
- node.xFixed = true;
- node.yFixed = true;
-
- drag.selection.push(s);
- }
- });
+ // create an array with the selected nodes and their original location and status
+ var me = this;
+ this.selection.forEach(function (id) {
+ var node = me.nodes[id];
+ if (node) {
+ var s = {
+ id: id,
+ node: node,
+
+ // store original x, y, xFixed and yFixed, make the node temporarily Fixed
+ x: node.x,
+ y: node.y,
+ xFixed: node.xFixed,
+ yFixed: node.yFixed
+ };
- }
+ node.xFixed = true;
+ node.yFixed = true;
+
+ drag.selection.push(s);
+ }
+ });
+
+ }
};
/**
@@ -409,51 +409,51 @@ Graph.prototype._onDragStart = function () {
* @private
*/
Graph.prototype._onDrag = function (event) {
- if (this.drag.pinched) {
- return;
- }
-
- var pointer = this._getPointer(event.gesture.touches[0]);
-
- var me = this,
- drag = this.drag,
- selection = drag.selection;
- if (selection && selection.length) {
- // calculate delta's and new location
- var deltaX = pointer.x - drag.pointer.x,
- deltaY = pointer.y - drag.pointer.y;
-
- // update position of all selected nodes
- selection.forEach(function (s) {
- var node = s.node;
-
- if (!s.xFixed) {
- node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
- }
-
- if (!s.yFixed) {
- node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
- }
- });
+ if (this.drag.pinched) {
+ return;
+ }
+
+ var pointer = this._getPointer(event.gesture.touches[0]);
+
+ var me = this,
+ drag = this.drag,
+ selection = drag.selection;
+ if (selection && selection.length) {
+ // calculate delta's and new location
+ var deltaX = pointer.x - drag.pointer.x,
+ deltaY = pointer.y - drag.pointer.y;
+
+ // update position of all selected nodes
+ selection.forEach(function (s) {
+ var node = s.node;
+
+ if (!s.xFixed) {
+ node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
+ }
+
+ if (!s.yFixed) {
+ node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
+ }
+ });
- // start animation if not yet running
- if (!this.moving) {
- this.moving = true;
- this.start();
- }
+ // start animation if not yet running
+ if (!this.moving) {
+ this.moving = true;
+ this.start();
}
- else {
- // move the graph
- var diffX = pointer.x - this.drag.pointer.x;
- var diffY = pointer.y - this.drag.pointer.y;
+ }
+ else {
+ // move the graph
+ var diffX = pointer.x - this.drag.pointer.x;
+ var diffY = pointer.y - this.drag.pointer.y;
- this._setTranslation(
- this.drag.translation.x + diffX,
- this.drag.translation.y + diffY);
- this._redraw();
+ this._setTranslation(
+ this.drag.translation.x + diffX,
+ this.drag.translation.y + diffY);
+ this._redraw();
- this.moved = true;
- }
+ this.moved = true;
+ }
};
/**
@@ -461,14 +461,14 @@ Graph.prototype._onDrag = function (event) {
* @private
*/
Graph.prototype._onDragEnd = function () {
- var selection = this.drag.selection;
- if (selection) {
- selection.forEach(function (s) {
- // restore original xFixed and yFixed
- s.node.xFixed = s.xFixed;
- s.node.yFixed = s.yFixed;
- });
- }
+ var selection = this.drag.selection;
+ if (selection) {
+ selection.forEach(function (s) {
+ // restore original xFixed and yFixed
+ s.node.xFixed = s.xFixed;
+ s.node.yFixed = s.yFixed;
+ });
+ }
};
/**
@@ -476,23 +476,23 @@ Graph.prototype._onDragEnd = function () {
* @private
*/
Graph.prototype._onTap = function (event) {
- var pointer = this._getPointer(event.gesture.touches[0]);
+ var pointer = this._getPointer(event.gesture.touches[0]);
- var nodeId = this._getNodeAt(pointer);
- var node = this.nodes[nodeId];
- if (node) {
- // select this node
- this._selectNodes([nodeId]);
+ var nodeId = this._getNodeAt(pointer);
+ var node = this.nodes[nodeId];
+ if (node) {
+ // select this node
+ this._selectNodes([nodeId]);
- if (!this.moving) {
- this._redraw();
- }
- }
- else {
- // remove selection
- this._unselectNodes();
- this._redraw();
+ if (!this.moving) {
+ this._redraw();
}
+ }
+ else {
+ // remove selection
+ this._unselectNodes();
+ this._redraw();
+ }
};
/**
@@ -500,26 +500,26 @@ Graph.prototype._onTap = function (event) {
* @private
*/
Graph.prototype._onHold = function (event) {
- var pointer = this._getPointer(event.gesture.touches[0]);
- var nodeId = this._getNodeAt(pointer);
- var node = this.nodes[nodeId];
- if (node) {
- if (!node.isSelected()) {
- // select this node, keep previous selection
- var append = true;
- this._selectNodes([nodeId], append);
- }
- else {
- this._unselectNodes([nodeId]);
- }
-
- if (!this.moving) {
- this._redraw();
- }
+ var pointer = this._getPointer(event.gesture.touches[0]);
+ var nodeId = this._getNodeAt(pointer);
+ var node = this.nodes[nodeId];
+ if (node) {
+ if (!node.isSelected()) {
+ // select this node, keep previous selection
+ var append = true;
+ this._selectNodes([nodeId], append);
}
else {
- // Do nothing
+ this._unselectNodes([nodeId]);
}
+
+ if (!this.moving) {
+ this._redraw();
+ }
+ }
+ else {
+ // Do nothing
+ }
};
/**
@@ -528,16 +528,16 @@ Graph.prototype._onHold = function (event) {
* @private
*/
Graph.prototype._onPinch = function (event) {
- var pointer = this._getPointer(event.gesture.center);
+ var pointer = this._getPointer(event.gesture.center);
- this.drag.pinched = true;
- if (!('scale' in this.pinch)) {
- this.pinch.scale = 1;
- }
+ this.drag.pinched = true;
+ if (!('scale' in this.pinch)) {
+ this.pinch.scale = 1;
+ }
- // TODO: enable moving while pinching?
- var scale = this.pinch.scale * event.gesture.scale;
- this._zoom(scale, pointer)
+ // TODO: enable moving while pinching?
+ var scale = this.pinch.scale * event.gesture.scale;
+ this._zoom(scale, pointer)
};
/**
@@ -548,24 +548,24 @@ Graph.prototype._onPinch = function (event) {
* @private
*/
Graph.prototype._zoom = function(scale, pointer) {
- var scaleOld = this._getScale();
- if (scale < 0.01) {
- scale = 0.01;
- }
- if (scale > 10) {
- scale = 10;
- }
-
- var translation = this._getTranslation();
- var scaleFrac = scale / scaleOld;
- var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
- var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
-
- this._setScale(scale);
- this._setTranslation(tx, ty);
- this._redraw();
-
- return scale;
+ var scaleOld = this._getScale();
+ if (scale < 0.01) {
+ scale = 0.01;
+ }
+ if (scale > 10) {
+ scale = 10;
+ }
+
+ var translation = this._getTranslation();
+ var scaleFrac = scale / scaleOld;
+ var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
+ var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
+
+ this._setScale(scale);
+ this._setTranslation(tx, ty);
+ this._redraw();
+
+ return scale;
};
/**
@@ -576,45 +576,45 @@ Graph.prototype._zoom = function(scale, pointer) {
* @private
*/
Graph.prototype._onMouseWheel = function(event) {
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta/120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail/3;
- }
-
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- if (!('mouswheelScale' in this.pinch)) {
- this.pinch.mouswheelScale = 1;
- }
-
- // calculate the new scale
- var scale = this.pinch.mouswheelScale;
- var zoom = delta / 10;
- if (delta < 0) {
- zoom = zoom / (1 - zoom);
- }
- scale *= (1 + zoom);
-
- // calculate the pointer location
- var gesture = Hammer.event.collectEventData(this, 'scroll', event);
- var pointer = this._getPointer(gesture.center);
+ // retrieve delta
+ var delta = 0;
+ if (event.wheelDelta) { /* IE/Opera. */
+ delta = event.wheelDelta/120;
+ } else if (event.detail) { /* Mozilla case. */
+ // In Mozilla, sign of delta is different than in IE.
+ // Also, delta is multiple of 3.
+ delta = -event.detail/3;
+ }
+
+ // If delta is nonzero, handle it.
+ // Basically, delta is now positive if wheel was scrolled up,
+ // and negative, if wheel was scrolled down.
+ if (delta) {
+ if (!('mouswheelScale' in this.pinch)) {
+ this.pinch.mouswheelScale = 1;
+ }
+
+ // calculate the new scale
+ var scale = this.pinch.mouswheelScale;
+ var zoom = delta / 10;
+ if (delta < 0) {
+ zoom = zoom / (1 - zoom);
+ }
+ scale *= (1 + zoom);
+
+ // calculate the pointer location
+ var gesture = Hammer.event.collectEventData(this, 'scroll', event);
+ var pointer = this._getPointer(gesture.center);
- // apply the new scale
- scale = this._zoom(scale, pointer);
+ // apply the new scale
+ scale = this._zoom(scale, pointer);
- // store the new, applied scale
- this.pinch.mouswheelScale = scale;
- }
+ // store the new, applied scale
+ this.pinch.mouswheelScale = scale;
+ }
- // Prevent default actions caused by mouse wheel.
- event.preventDefault();
+ // Prevent default actions caused by mouse wheel.
+ event.preventDefault();
};
@@ -624,26 +624,26 @@ Graph.prototype._onMouseWheel = function(event) {
* @private
*/
Graph.prototype._onMouseMoveTitle = function (event) {
- var gesture = Hammer.event.collectEventData(this, 'mousemove', event);
- var pointer = this._getPointer(gesture.center);
-
- // check if the previously selected node is still selected
- if (this.popupNode) {
- this._checkHidePopup(pointer);
- }
-
- // start a timeout that will check if the mouse is positioned above
- // an element
- var me = this;
- var checkShow = function() {
- me._checkShowPopup(pointer);
- };
- if (this.popupTimer) {
- clearInterval(this.popupTimer); // stop any running timer
- }
- if (!this.leftButtonDown) {
- this.popupTimer = setTimeout(checkShow, 300);
- }
+ var gesture = Hammer.event.collectEventData(this, 'mousemove', event);
+ var pointer = this._getPointer(gesture.center);
+
+ // check if the previously selected node is still selected
+ if (this.popupNode) {
+ this._checkHidePopup(pointer);
+ }
+
+ // start a timeout that will check if the mouse is positioned above
+ // an element
+ var me = this;
+ var checkShow = function() {
+ me._checkShowPopup(pointer);
+ };
+ if (this.popupTimer) {
+ clearInterval(this.popupTimer); // stop any running timer
+ }
+ if (!this.leftButtonDown) {
+ this.popupTimer = setTimeout(checkShow, 300);
+ }
};
/**
@@ -655,66 +655,66 @@ Graph.prototype._onMouseMoveTitle = function (event) {
* @private
*/
Graph.prototype._checkShowPopup = function (pointer) {
- var obj = {
- left: this._canvasToX(pointer.x),
- top: this._canvasToY(pointer.y),
- right: this._canvasToX(pointer.x),
- bottom: this._canvasToY(pointer.y)
- };
-
- var id;
- var lastPopupNode = this.popupNode;
-
- if (this.popupNode == undefined) {
- // search the nodes for overlap, select the top one in case of multiple nodes
- var nodes = this.nodes;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var node = nodes[id];
- if (node.getTitle() != undefined && node.isOverlappingWith(obj)) {
- this.popupNode = node;
- break;
- }
- }
- }
- }
-
- if (this.popupNode == undefined) {
- // search the edges for overlap
- var edges = this.edges;
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected && (edge.getTitle() != undefined) &&
- edge.isOverlappingWith(obj)) {
- this.popupNode = edge;
- break;
- }
- }
+ var obj = {
+ left: this._canvasToX(pointer.x),
+ top: this._canvasToY(pointer.y),
+ right: this._canvasToX(pointer.x),
+ bottom: this._canvasToY(pointer.y)
+ };
+
+ var id;
+ var lastPopupNode = this.popupNode;
+
+ if (this.popupNode == undefined) {
+ // search the nodes for overlap, select the top one in case of multiple nodes
+ var nodes = this.nodes;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var node = nodes[id];
+ if (node.getTitle() != undefined && node.isOverlappingWith(obj)) {
+ this.popupNode = node;
+ break;
}
+ }
}
+ }
- if (this.popupNode) {
- // show popup message window
- if (this.popupNode != lastPopupNode) {
- var me = this;
- if (!me.popup) {
- me.popup = new Popup(me.frame);
- }
-
- // adjust a small offset such that the mouse cursor is located in the
- // bottom left location of the popup, and you can easily move over the
- // popup area
- me.popup.setPosition(pointer.x - 3, pointer.y - 3);
- me.popup.setText(me.popupNode.getTitle());
- me.popup.show();
- }
- }
- else {
- if (this.popup) {
- this.popup.hide();
+ if (this.popupNode == undefined) {
+ // search the edges for overlap
+ var edges = this.edges;
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected && (edge.getTitle() != undefined) &&
+ edge.isOverlappingWith(obj)) {
+ this.popupNode = edge;
+ break;
}
- }
+ }
+ }
+ }
+
+ if (this.popupNode) {
+ // show popup message window
+ if (this.popupNode != lastPopupNode) {
+ var me = this;
+ if (!me.popup) {
+ me.popup = new Popup(me.frame);
+ }
+
+ // adjust a small offset such that the mouse cursor is located in the
+ // bottom left location of the popup, and you can easily move over the
+ // popup area
+ me.popup.setPosition(pointer.x - 3, pointer.y - 3);
+ me.popup.setText(me.popupNode.getTitle());
+ me.popup.show();
+ }
+ }
+ else {
+ if (this.popup) {
+ this.popup.hide();
+ }
+ }
};
/**
@@ -724,12 +724,12 @@ Graph.prototype._checkShowPopup = function (pointer) {
* @private
*/
Graph.prototype._checkHidePopup = function (pointer) {
- if (!this.popupNode || !this._getNodeAt(pointer) ) {
- this.popupNode = undefined;
- if (this.popup) {
- this.popup.hide();
- }
+ if (!this.popupNode || !this._getNodeAt(pointer) ) {
+ this.popupNode = undefined;
+ if (this.popup) {
+ this.popup.hide();
}
+ }
};
/**
@@ -743,43 +743,43 @@ Graph.prototype._checkHidePopup = function (pointer) {
* @private
*/
Graph.prototype._unselectNodes = function(selection, triggerSelect) {
- var changed = false;
- var i, iMax, id;
-
- if (selection) {
- // remove provided selections
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- id = selection[i];
- this.nodes[id].unselect();
-
- var j = 0;
- while (j < this.selection.length) {
- if (this.selection[j] == id) {
- this.selection.splice(j, 1);
- changed = true;
- }
- else {
- j++;
- }
- }
+ var changed = false;
+ var i, iMax, id;
+
+ if (selection) {
+ // remove provided selections
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ id = selection[i];
+ this.nodes[id].unselect();
+
+ var j = 0;
+ while (j < this.selection.length) {
+ if (this.selection[j] == id) {
+ this.selection.splice(j, 1);
+ changed = true;
}
- }
- else if (this.selection && this.selection.length) {
- // remove all selections
- for (i = 0, iMax = this.selection.length; i < iMax; i++) {
- id = this.selection[i];
- this.nodes[id].unselect();
- changed = true;
+ else {
+ j++;
}
- this.selection = [];
+ }
}
-
- if (changed && (triggerSelect == true || triggerSelect == undefined)) {
- // fire the select event
- this._trigger('select');
+ }
+ else if (this.selection && this.selection.length) {
+ // remove all selections
+ for (i = 0, iMax = this.selection.length; i < iMax; i++) {
+ id = this.selection[i];
+ this.nodes[id].unselect();
+ changed = true;
}
+ this.selection = [];
+ }
- return changed;
+ if (changed && (triggerSelect == true || triggerSelect == undefined)) {
+ // fire the select event
+ this._trigger('select');
+ }
+
+ return changed;
};
/**
@@ -791,51 +791,51 @@ Graph.prototype._unselectNodes = function(selection, triggerSelect) {
* @private
*/
Graph.prototype._selectNodes = function(selection, append) {
- var changed = false;
- var i, iMax;
-
- // TODO: the selectNodes method is a little messy, rework this
-
- // check if the current selection equals the desired selection
- var selectionAlreadyThere = true;
- if (selection.length != this.selection.length) {
+ var changed = false;
+ var i, iMax;
+
+ // TODO: the selectNodes method is a little messy, rework this
+
+ // check if the current selection equals the desired selection
+ var selectionAlreadyThere = true;
+ if (selection.length != this.selection.length) {
+ selectionAlreadyThere = false;
+ }
+ else {
+ for (i = 0, iMax = Math.min(selection.length, this.selection.length); i < iMax; i++) {
+ if (selection[i] != this.selection[i]) {
selectionAlreadyThere = false;
+ break;
+ }
}
- else {
- for (i = 0, iMax = Math.min(selection.length, this.selection.length); i < iMax; i++) {
- if (selection[i] != this.selection[i]) {
- selectionAlreadyThere = false;
- break;
- }
- }
- }
- if (selectionAlreadyThere) {
- return changed;
- }
-
- if (append == undefined || append == false) {
- // first deselect any selected node
- var triggerSelect = false;
- changed = this._unselectNodes(undefined, triggerSelect);
- }
-
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- // add each of the new selections, but only when they are not duplicate
- var id = selection[i];
- var isDuplicate = (this.selection.indexOf(id) != -1);
- if (!isDuplicate) {
- this.nodes[id].select();
- this.selection.push(id);
- changed = true;
- }
- }
-
- if (changed) {
- // fire the select event
- this._trigger('select');
- }
-
+ }
+ if (selectionAlreadyThere) {
return changed;
+ }
+
+ if (append == undefined || append == false) {
+ // first deselect any selected node
+ var triggerSelect = false;
+ changed = this._unselectNodes(undefined, triggerSelect);
+ }
+
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ // add each of the new selections, but only when they are not duplicate
+ var id = selection[i];
+ var isDuplicate = (this.selection.indexOf(id) != -1);
+ if (!isDuplicate) {
+ this.nodes[id].select();
+ this.selection.push(id);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ // fire the select event
+ this._trigger('select');
+ }
+
+ return changed;
};
/**
@@ -845,18 +845,18 @@ Graph.prototype._selectNodes = function(selection, append) {
* @private
*/
Graph.prototype._getNodesOverlappingWith = function (obj) {
- var nodes = this.nodes,
- overlappingNodes = [];
+ var nodes = this.nodes,
+ overlappingNodes = [];
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- if (nodes[id].isOverlappingWith(obj)) {
- overlappingNodes.push(id);
- }
- }
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ if (nodes[id].isOverlappingWith(obj)) {
+ overlappingNodes.push(id);
+ }
}
+ }
- return overlappingNodes;
+ return overlappingNodes;
};
/**
@@ -865,7 +865,7 @@ Graph.prototype._getNodesOverlappingWith = function (obj) {
* selected nodes.
*/
Graph.prototype.getSelection = function() {
- return this.selection.concat([]);
+ return this.selection.concat([]);
};
/**
@@ -874,31 +874,31 @@ Graph.prototype.getSelection = function() {
* selected nodes.
*/
Graph.prototype.setSelection = function(selection) {
- var i, iMax, id;
+ var i, iMax, id;
- if (!selection || (selection.length == undefined))
- throw 'Selection must be an array with ids';
+ if (!selection || (selection.length == undefined))
+ throw 'Selection must be an array with ids';
- // first unselect any selected node
- for (i = 0, iMax = this.selection.length; i < iMax; i++) {
- id = this.selection[i];
- this.nodes[id].unselect();
- }
+ // first unselect any selected node
+ for (i = 0, iMax = this.selection.length; i < iMax; i++) {
+ id = this.selection[i];
+ this.nodes[id].unselect();
+ }
- this.selection = [];
+ this.selection = [];
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- id = selection[i];
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ id = selection[i];
- var node = this.nodes[id];
- if (!node) {
- throw new RangeError('Node with id "' + id + '" not found');
- }
- node.select();
- this.selection.push(id);
+ var node = this.nodes[id];
+ if (!node) {
+ throw new RangeError('Node with id "' + id + '" not found');
}
+ node.select();
+ this.selection.push(id);
+ }
- this.redraw();
+ this.redraw();
};
/**
@@ -906,16 +906,16 @@ Graph.prototype.setSelection = function(selection) {
* @private
*/
Graph.prototype._updateSelection = function () {
- var i = 0;
- while (i < this.selection.length) {
- var id = this.selection[i];
- if (!this.nodes[id]) {
- this.selection.splice(i, 1);
- }
- else {
- i++;
- }
+ var i = 0;
+ while (i < this.selection.length) {
+ var id = this.selection[i];
+ if (!this.nodes[id]) {
+ this.selection.splice(i, 1);
}
+ else {
+ i++;
+ }
+ }
};
/**
@@ -927,74 +927,74 @@ Graph.prototype._updateSelection = function () {
* @private
*/
Graph.prototype._getConnectionCount = function(level) {
- if (level == undefined) {
- level = 1;
- }
-
- // get the nodes connected to given nodes
- function getConnectedNodes(nodes) {
- var connectedNodes = [];
-
- for (var j = 0, jMax = nodes.length; j < jMax; j++) {
- var node = nodes[j];
-
- // find all nodes connected to this node
- var edges = node.edges;
- for (var i = 0, iMax = edges.length; i < iMax; i++) {
- var edge = edges[i];
- var other = null;
-
- // check if connected
- if (edge.from == node)
- other = edge.to;
- else if (edge.to == node)
- other = edge.from;
-
- // check if the other node is not already in the list with nodes
- var k, kMax;
- if (other) {
- for (k = 0, kMax = nodes.length; k < kMax; k++) {
- if (nodes[k] == other) {
- other = null;
- break;
- }
- }
- }
- if (other) {
- for (k = 0, kMax = connectedNodes.length; k < kMax; k++) {
- if (connectedNodes[k] == other) {
- other = null;
- break;
- }
- }
- }
-
- if (other)
- connectedNodes.push(other);
+ if (level == undefined) {
+ level = 1;
+ }
+
+ // get the nodes connected to given nodes
+ function getConnectedNodes(nodes) {
+ var connectedNodes = [];
+
+ for (var j = 0, jMax = nodes.length; j < jMax; j++) {
+ var node = nodes[j];
+
+ // find all nodes connected to this node
+ var edges = node.edges;
+ for (var i = 0, iMax = edges.length; i < iMax; i++) {
+ var edge = edges[i];
+ var other = null;
+
+ // check if connected
+ if (edge.from == node)
+ other = edge.to;
+ else if (edge.to == node)
+ other = edge.from;
+
+ // check if the other node is not already in the list with nodes
+ var k, kMax;
+ if (other) {
+ for (k = 0, kMax = nodes.length; k < kMax; k++) {
+ if (nodes[k] == other) {
+ other = null;
+ break;
}
+ }
}
-
- return connectedNodes;
- }
-
- var connections = [];
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var c = [nodes[id]];
- for (var l = 0; l < level; l++) {
- c = c.concat(getConnectedNodes(c));
+ if (other) {
+ for (k = 0, kMax = connectedNodes.length; k < kMax; k++) {
+ if (connectedNodes[k] == other) {
+ other = null;
+ break;
}
- connections.push(c);
+ }
}
+
+ if (other)
+ connectedNodes.push(other);
+ }
}
- var hubs = [];
- for (var i = 0, len = connections.length; i < len; i++) {
- hubs.push(connections[i].length);
+ return connectedNodes;
+ }
+
+ var connections = [];
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var c = [nodes[id]];
+ for (var l = 0; l < level; l++) {
+ c = c.concat(getConnectedNodes(c));
+ }
+ connections.push(c);
}
+ }
+
+ var hubs = [];
+ for (var i = 0, len = connections.length; i < len; i++) {
+ hubs.push(connections[i].length);
+ }
- return hubs;
+ return hubs;
};
@@ -1006,14 +1006,14 @@ Graph.prototype._getConnectionCount = function(level) {
* or '30%')
*/
Graph.prototype.setSize = function(width, height) {
- this.frame.style.width = width;
- this.frame.style.height = height;
+ this.frame.style.width = width;
+ this.frame.style.height = height;
- this.frame.canvas.style.width = '100%';
- this.frame.canvas.style.height = '100%';
+ this.frame.canvas.style.width = '100%';
+ this.frame.canvas.style.height = '100%';
- this.frame.canvas.width = this.frame.canvas.clientWidth;
- this.frame.canvas.height = this.frame.canvas.clientHeight;
+ this.frame.canvas.width = this.frame.canvas.clientWidth;
+ this.frame.canvas.height = this.frame.canvas.clientHeight;
};
/**
@@ -1022,45 +1022,45 @@ Graph.prototype.setSize = function(width, height) {
* @private
*/
Graph.prototype._setNodes = function(nodes) {
- var oldNodesData = this.nodesData;
-
- if (nodes instanceof DataSet || nodes instanceof DataView) {
- this.nodesData = nodes;
- }
- else if (nodes instanceof Array) {
- this.nodesData = new DataSet();
- this.nodesData.add(nodes);
- }
- else if (!nodes) {
- this.nodesData = new DataSet();
- }
- else {
- throw new TypeError('Array or DataSet expected');
- }
-
- if (oldNodesData) {
- // unsubscribe from old dataset
- util.forEach(this.nodesListeners, function (callback, event) {
- oldNodesData.unsubscribe(event, callback);
- });
- }
+ var oldNodesData = this.nodesData;
+
+ if (nodes instanceof DataSet || nodes instanceof DataView) {
+ this.nodesData = nodes;
+ }
+ else if (nodes instanceof Array) {
+ this.nodesData = new DataSet();
+ this.nodesData.add(nodes);
+ }
+ else if (!nodes) {
+ this.nodesData = new DataSet();
+ }
+ else {
+ throw new TypeError('Array or DataSet expected');
+ }
+
+ if (oldNodesData) {
+ // unsubscribe from old dataset
+ util.forEach(this.nodesListeners, function (callback, event) {
+ oldNodesData.unsubscribe(event, callback);
+ });
+ }
- // remove drawn nodes
- this.nodes = {};
+ // remove drawn nodes
+ this.nodes = {};
- if (this.nodesData) {
- // subscribe to new dataset
- var me = this;
- util.forEach(this.nodesListeners, function (callback, event) {
- me.nodesData.subscribe(event, callback);
- });
+ if (this.nodesData) {
+ // subscribe to new dataset
+ var me = this;
+ util.forEach(this.nodesListeners, function (callback, event) {
+ me.nodesData.subscribe(event, callback);
+ });
- // draw all new nodes
- var ids = this.nodesData.getIds();
- this._addNodes(ids);
- }
+ // draw all new nodes
+ var ids = this.nodesData.getIds();
+ this._addNodes(ids);
+ }
- this._updateSelection();
+ this._updateSelection();
};
/**
@@ -1069,29 +1069,29 @@ Graph.prototype._setNodes = function(nodes) {
* @private
*/
Graph.prototype._addNodes = function(ids) {
- var id;
- for (var i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- var data = this.nodesData.get(id);
- var node = new Node(data, this.images, this.groups, this.constants);
- this.nodes[id] = node; // note: this may replace an existing node
-
- if (!node.isFixed()) {
- // TODO: position new nodes in a smarter way!
- var radius = this.constants.edges.length * 2;
- var count = ids.length;
- var angle = 2 * Math.PI * (i / count);
- node.x = radius * Math.cos(angle);
- node.y = radius * Math.sin(angle);
-
- // note: no not use node.isMoving() here, as that gives the current
- // velocity of the node, which is zero after creation of the node.
- this.moving = true;
- }
- }
-
- this._reconnectEdges();
- this._updateValueRange(this.nodes);
+ var id;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ var data = this.nodesData.get(id);
+ var node = new Node(data, this.images, this.groups, this.constants);
+ this.nodes[id] = node; // note: this may replace an existing node
+
+ if (!node.isFixed()) {
+ // TODO: position new nodes in a smarter way!
+ var radius = this.constants.edges.length * 2;
+ var count = ids.length;
+ var angle = 2 * Math.PI * (i / count);
+ node.x = radius * Math.cos(angle);
+ node.y = radius * Math.sin(angle);
+
+ // note: no not use node.isMoving() here, as that gives the current
+ // velocity of the node, which is zero after creation of the node.
+ this.moving = true;
+ }
+ }
+
+ this._reconnectEdges();
+ this._updateValueRange(this.nodes);
};
/**
@@ -1100,29 +1100,29 @@ Graph.prototype._addNodes = function(ids) {
* @private
*/
Graph.prototype._updateNodes = function(ids) {
- var nodes = this.nodes,
- nodesData = this.nodesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- var node = nodes[id];
- var data = nodesData.get(id);
- if (node) {
- // update node
- node.setProperties(data, this.constants);
- }
- else {
- // create node
- node = new Node(properties, this.images, this.groups, this.constants);
- nodes[id] = node;
+ var nodes = this.nodes,
+ nodesData = this.nodesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ var node = nodes[id];
+ var data = nodesData.get(id);
+ if (node) {
+ // update node
+ node.setProperties(data, this.constants);
+ }
+ else {
+ // create node
+ node = new Node(properties, this.images, this.groups, this.constants);
+ nodes[id] = node;
- if (!node.isFixed()) {
- this.moving = true;
- }
- }
+ if (!node.isFixed()) {
+ this.moving = true;
+ }
}
+ }
- this._reconnectEdges();
- this._updateValueRange(nodes);
+ this._reconnectEdges();
+ this._updateValueRange(nodes);
};
/**
@@ -1131,15 +1131,15 @@ Graph.prototype._updateNodes = function(ids) {
* @private
*/
Graph.prototype._removeNodes = function(ids) {
- var nodes = this.nodes;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- delete nodes[id];
- }
-
- this._reconnectEdges();
- this._updateSelection();
- this._updateValueRange(nodes);
+ var nodes = this.nodes;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ delete nodes[id];
+ }
+
+ this._reconnectEdges();
+ this._updateSelection();
+ this._updateValueRange(nodes);
};
/**
@@ -1149,45 +1149,45 @@ Graph.prototype._removeNodes = function(ids) {
* @private
*/
Graph.prototype._setEdges = function(edges) {
- var oldEdgesData = this.edgesData;
-
- if (edges instanceof DataSet || edges instanceof DataView) {
- this.edgesData = edges;
- }
- else if (edges instanceof Array) {
- this.edgesData = new DataSet();
- this.edgesData.add(edges);
- }
- else if (!edges) {
- this.edgesData = new DataSet();
- }
- else {
- throw new TypeError('Array or DataSet expected');
- }
-
- if (oldEdgesData) {
- // unsubscribe from old dataset
- util.forEach(this.edgesListeners, function (callback, event) {
- oldEdgesData.unsubscribe(event, callback);
- });
- }
+ var oldEdgesData = this.edgesData;
+
+ if (edges instanceof DataSet || edges instanceof DataView) {
+ this.edgesData = edges;
+ }
+ else if (edges instanceof Array) {
+ this.edgesData = new DataSet();
+ this.edgesData.add(edges);
+ }
+ else if (!edges) {
+ this.edgesData = new DataSet();
+ }
+ else {
+ throw new TypeError('Array or DataSet expected');
+ }
+
+ if (oldEdgesData) {
+ // unsubscribe from old dataset
+ util.forEach(this.edgesListeners, function (callback, event) {
+ oldEdgesData.unsubscribe(event, callback);
+ });
+ }
- // remove drawn edges
- this.edges = {};
+ // remove drawn edges
+ this.edges = {};
- if (this.edgesData) {
- // subscribe to new dataset
- var me = this;
- util.forEach(this.edgesListeners, function (callback, event) {
- me.edgesData.subscribe(event, callback);
- });
+ if (this.edgesData) {
+ // subscribe to new dataset
+ var me = this;
+ util.forEach(this.edgesListeners, function (callback, event) {
+ me.edgesData.subscribe(event, callback);
+ });
- // draw all new nodes
- var ids = this.edgesData.getIds();
- this._addEdges(ids);
- }
+ // draw all new nodes
+ var ids = this.edgesData.getIds();
+ this._addEdges(ids);
+ }
- this._reconnectEdges();
+ this._reconnectEdges();
};
/**
@@ -1196,22 +1196,22 @@ Graph.prototype._setEdges = function(edges) {
* @private
*/
Graph.prototype._addEdges = function (ids) {
- var edges = this.edges,
- edgesData = this.edgesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
-
- var oldEdge = edges[id];
- if (oldEdge) {
- oldEdge.disconnect();
- }
+ var edges = this.edges,
+ edgesData = this.edgesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
- var data = edgesData.get(id);
- edges[id] = new Edge(data, this, this.constants);
+ var oldEdge = edges[id];
+ if (oldEdge) {
+ oldEdge.disconnect();
}
- this.moving = true;
- this._updateValueRange(edges);
+ var data = edgesData.get(id);
+ edges[id] = new Edge(data, this, this.constants);
+ }
+
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -1220,28 +1220,28 @@ Graph.prototype._addEdges = function (ids) {
* @private
*/
Graph.prototype._updateEdges = function (ids) {
- var edges = this.edges,
- edgesData = this.edgesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
-
- var data = edgesData.get(id);
- var edge = edges[id];
- if (edge) {
- // update edge
- edge.disconnect();
- edge.setProperties(data, this.constants);
- edge.connect();
- }
- else {
- // create edge
- edge = new Edge(data, this, this.constants);
- this.edges[id] = edge;
- }
+ var edges = this.edges,
+ edgesData = this.edgesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+
+ var data = edgesData.get(id);
+ var edge = edges[id];
+ if (edge) {
+ // update edge
+ edge.disconnect();
+ edge.setProperties(data, this.constants);
+ edge.connect();
+ }
+ else {
+ // create edge
+ edge = new Edge(data, this, this.constants);
+ this.edges[id] = edge;
}
+ }
- this.moving = true;
- this._updateValueRange(edges);
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -1250,18 +1250,18 @@ Graph.prototype._updateEdges = function (ids) {
* @private
*/
Graph.prototype._removeEdges = function (ids) {
- var edges = this.edges;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- var edge = edges[id];
- if (edge) {
- edge.disconnect();
- delete edges[id];
- }
- }
-
- this.moving = true;
- this._updateValueRange(edges);
+ var edges = this.edges;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ var edge = edges[id];
+ if (edge) {
+ edge.disconnect();
+ delete edges[id];
+ }
+ }
+
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -1269,23 +1269,23 @@ Graph.prototype._removeEdges = function (ids) {
* @private
*/
Graph.prototype._reconnectEdges = function() {
- var id,
- nodes = this.nodes,
- edges = this.edges;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- nodes[id].edges = [];
- }
- }
-
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- edge.from = null;
- edge.to = null;
- edge.connect();
- }
- }
+ var id,
+ nodes = this.nodes,
+ edges = this.edges;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ nodes[id].edges = [];
+ }
+ }
+
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ edge.from = null;
+ edge.to = null;
+ edge.connect();
+ }
+ }
};
/**
@@ -1297,29 +1297,29 @@ Graph.prototype._reconnectEdges = function() {
* @private
*/
Graph.prototype._updateValueRange = function(obj) {
- var id;
-
- // determine the range of the objects
- var valueMin = undefined;
- var valueMax = undefined;
+ var id;
+
+ // determine the range of the objects
+ var valueMin = undefined;
+ var valueMax = undefined;
+ for (id in obj) {
+ if (obj.hasOwnProperty(id)) {
+ var value = obj[id].getValue();
+ if (value !== undefined) {
+ valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
+ valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
+ }
+ }
+ }
+
+ // adjust the range of all objects
+ if (valueMin !== undefined && valueMax !== undefined) {
for (id in obj) {
- if (obj.hasOwnProperty(id)) {
- var value = obj[id].getValue();
- if (value !== undefined) {
- valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
- valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
- }
- }
- }
-
- // adjust the range of all objects
- if (valueMin !== undefined && valueMax !== undefined) {
- for (id in obj) {
- if (obj.hasOwnProperty(id)) {
- obj[id].setValueRange(valueMin, valueMax);
- }
- }
+ if (obj.hasOwnProperty(id)) {
+ obj[id].setValueRange(valueMin, valueMax);
+ }
}
+ }
};
/**
@@ -1327,9 +1327,9 @@ Graph.prototype._updateValueRange = function(obj) {
* chart will be resized too.
*/
Graph.prototype.redraw = function() {
- this.setSize(this.width, this.height);
+ this.setSize(this.width, this.height);
- this._redraw();
+ this._redraw();
};
/**
@@ -1337,23 +1337,23 @@ Graph.prototype.redraw = function() {
* @private
*/
Graph.prototype._redraw = function() {
- var ctx = this.frame.canvas.getContext('2d');
+ var ctx = this.frame.canvas.getContext('2d');
- // clear the canvas
- var w = this.frame.canvas.width;
- var h = this.frame.canvas.height;
- ctx.clearRect(0, 0, w, h);
+ // clear the canvas
+ var w = this.frame.canvas.width;
+ var h = this.frame.canvas.height;
+ ctx.clearRect(0, 0, w, h);
- // set scaling and translation
- ctx.save();
- ctx.translate(this.translation.x, this.translation.y);
- ctx.scale(this.scale, this.scale);
+ // set scaling and translation
+ ctx.save();
+ ctx.translate(this.translation.x, this.translation.y);
+ ctx.scale(this.scale, this.scale);
- this._drawEdges(ctx);
- this._drawNodes(ctx);
+ this._drawEdges(ctx);
+ this._drawNodes(ctx);
- // restore original scaling and translation
- ctx.restore();
+ // restore original scaling and translation
+ ctx.restore();
};
/**
@@ -1363,19 +1363,19 @@ Graph.prototype._redraw = function() {
* @private
*/
Graph.prototype._setTranslation = function(offsetX, offsetY) {
- if (this.translation === undefined) {
- this.translation = {
- x: 0,
- y: 0
- };
- }
-
- if (offsetX !== undefined) {
- this.translation.x = offsetX;
- }
- if (offsetY !== undefined) {
- this.translation.y = offsetY;
- }
+ if (this.translation === undefined) {
+ this.translation = {
+ x: 0,
+ y: 0
+ };
+ }
+
+ if (offsetX !== undefined) {
+ this.translation.x = offsetX;
+ }
+ if (offsetY !== undefined) {
+ this.translation.y = offsetY;
+ }
};
/**
@@ -1384,10 +1384,10 @@ Graph.prototype._setTranslation = function(offsetX, offsetY) {
* @private
*/
Graph.prototype._getTranslation = function() {
- return {
- x: this.translation.x,
- y: this.translation.y
- };
+ return {
+ x: this.translation.x,
+ y: this.translation.y
+ };
};
/**
@@ -1396,7 +1396,7 @@ Graph.prototype._getTranslation = function() {
* @private
*/
Graph.prototype._setScale = function(scale) {
- this.scale = scale;
+ this.scale = scale;
};
/**
* Get the current scale of the graph
@@ -1404,7 +1404,7 @@ Graph.prototype._setScale = function(scale) {
* @private
*/
Graph.prototype._getScale = function() {
- return this.scale;
+ return this.scale;
};
/**
@@ -1414,7 +1414,7 @@ Graph.prototype._getScale = function() {
* @private
*/
Graph.prototype._canvasToX = function(x) {
- return (x - this.translation.x) / this.scale;
+ return (x - this.translation.x) / this.scale;
};
/**
@@ -1424,7 +1424,7 @@ Graph.prototype._canvasToX = function(x) {
* @private
*/
Graph.prototype._xToCanvas = function(x) {
- return x * this.scale + this.translation.x;
+ return x * this.scale + this.translation.x;
};
/**
@@ -1434,7 +1434,7 @@ Graph.prototype._xToCanvas = function(x) {
* @private
*/
Graph.prototype._canvasToY = function(y) {
- return (y - this.translation.y) / this.scale;
+ return (y - this.translation.y) / this.scale;
};
/**
@@ -1444,7 +1444,7 @@ Graph.prototype._canvasToY = function(y) {
* @private
*/
Graph.prototype._yToCanvas = function(y) {
- return y * this.scale + this.translation.y ;
+ return y * this.scale + this.translation.y ;
};
/**
@@ -1454,24 +1454,24 @@ Graph.prototype._yToCanvas = function(y) {
* @private
*/
Graph.prototype._drawNodes = function(ctx) {
- // first draw the unselected nodes
- var nodes = this.nodes;
- var selected = [];
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- if (nodes[id].isSelected()) {
- selected.push(id);
- }
- else {
- nodes[id].draw(ctx);
- }
- }
- }
-
- // draw the selected nodes on top
- for (var s = 0, sMax = selected.length; s < sMax; s++) {
- nodes[selected[s]].draw(ctx);
- }
+ // first draw the unselected nodes
+ var nodes = this.nodes;
+ var selected = [];
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ if (nodes[id].isSelected()) {
+ selected.push(id);
+ }
+ else {
+ nodes[id].draw(ctx);
+ }
+ }
+ }
+
+ // draw the selected nodes on top
+ for (var s = 0, sMax = selected.length; s < sMax; s++) {
+ nodes[selected[s]].draw(ctx);
+ }
};
/**
@@ -1481,15 +1481,15 @@ Graph.prototype._drawNodes = function(ctx) {
* @private
*/
Graph.prototype._drawEdges = function(ctx) {
- var edges = this.edges;
- for (var id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected) {
- edges[id].draw(ctx);
- }
- }
- }
+ var edges = this.edges;
+ for (var id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected) {
+ edges[id].draw(ctx);
+ }
+ }
+ }
};
/**
@@ -1497,22 +1497,22 @@ Graph.prototype._drawEdges = function(ctx) {
* @private
*/
Graph.prototype._doStabilize = function() {
- var start = new Date();
-
- // find stable position
- var count = 0;
- var vmin = this.constants.minVelocity;
- var stable = false;
- while (!stable && count < this.constants.maxIterations) {
- this._calculateForces();
- this._discreteStepNodes();
- stable = !this._isMoving(vmin);
- count++;
- }
-
- var end = new Date();
-
- // console.log('Stabilized in ' + (end-start) + ' ms, ' + count + ' iterations' ); // TODO: cleanup
+ var start = new Date();
+
+ // find stable position
+ var count = 0;
+ var vmin = this.constants.minVelocity;
+ var stable = false;
+ while (!stable && count < this.constants.maxIterations) {
+ this._calculateForces();
+ this._discreteStepNodes();
+ stable = !this._isMoving(vmin);
+ count++;
+ }
+
+ var end = new Date();
+
+ // console.log('Stabilized in ' + (end-start) + ' ms, ' + count + ' iterations' ); // TODO: cleanup
};
/**
@@ -1521,149 +1521,149 @@ Graph.prototype._doStabilize = function() {
* @private
*/
Graph.prototype._calculateForces = function() {
- // create a local edge to the nodes and edges, that is faster
- var id, dx, dy, angle, distance, fx, fy,
- repulsingForce, springForce, length, edgeLength,
- nodes = this.nodes,
- edges = this.edges;
-
- // gravity, add a small constant force to pull the nodes towards the center of
- // the graph
- // Also, the forces are reset to zero in this loop by using _setForce instead
- // of _addForce
- var gravity = 0.01,
- gx = this.frame.canvas.clientWidth / 2,
- gy = this.frame.canvas.clientHeight / 2;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var node = nodes[id];
- dx = gx - node.x;
- dy = gy - node.y;
- angle = Math.atan2(dy, dx);
- fx = Math.cos(angle) * gravity;
- fy = Math.sin(angle) * gravity;
-
- node._setForce(fx, fy);
+ // create a local edge to the nodes and edges, that is faster
+ var id, dx, dy, angle, distance, fx, fy,
+ repulsingForce, springForce, length, edgeLength,
+ nodes = this.nodes,
+ edges = this.edges;
+
+ // gravity, add a small constant force to pull the nodes towards the center of
+ // the graph
+ // Also, the forces are reset to zero in this loop by using _setForce instead
+ // of _addForce
+ var gravity = 0.01,
+ gx = this.frame.canvas.clientWidth / 2,
+ gy = this.frame.canvas.clientHeight / 2;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var node = nodes[id];
+ dx = gx - node.x;
+ dy = gy - node.y;
+ angle = Math.atan2(dy, dx);
+ fx = Math.cos(angle) * gravity;
+ fy = Math.sin(angle) * gravity;
+
+ node._setForce(fx, fy);
+ }
+ }
+
+ // repulsing forces between nodes
+ var minimumDistance = this.constants.nodes.distance,
+ steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
+
+ for (var id1 in nodes) {
+ if (nodes.hasOwnProperty(id1)) {
+ var node1 = nodes[id1];
+ for (var id2 in nodes) {
+ if (nodes.hasOwnProperty(id2)) {
+ var node2 = nodes[id2];
+ // calculate normally distributed force
+ dx = node2.x - node1.x;
+ dy = node2.y - node1.y;
+ distance = Math.sqrt(dx * dx + dy * dy);
+ angle = Math.atan2(dy, dx);
+
+ // TODO: correct factor for repulsing force
+ //repulsingForce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingForce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ repulsingForce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)); // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingForce;
+ fy = Math.sin(angle) * repulsingForce;
+
+ node1._addForce(-fx, -fy);
+ node2._addForce(fx, fy);
}
- }
-
- // repulsing forces between nodes
- var minimumDistance = this.constants.nodes.distance,
- steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
-
- for (var id1 in nodes) {
- if (nodes.hasOwnProperty(id1)) {
- var node1 = nodes[id1];
- for (var id2 in nodes) {
- if (nodes.hasOwnProperty(id2)) {
- var node2 = nodes[id2];
- // calculate normally distributed force
- dx = node2.x - node1.x;
- dy = node2.y - node1.y;
- distance = Math.sqrt(dx * dx + dy * dy);
- angle = Math.atan2(dy, dx);
-
- // TODO: correct factor for repulsing force
- //repulsingForce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingForce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- repulsingForce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)); // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingForce;
- fy = Math.sin(angle) * repulsingForce;
-
- node1._addForce(-fx, -fy);
- node2._addForce(fx, fy);
- }
- }
- }
- }
-
- /* TODO: re-implement repulsion of edges
- for (var n = 0; n < nodes.length; n++) {
- for (var l = 0; l < edges.length; l++) {
- var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
- ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
-
- // calculate normally distributed force
- dx = nodes[n].x - lx,
- dy = nodes[n].y - ly,
- distance = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(dy, dx),
-
-
- // TODO: correct factor for repulsing force
- //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
- repulsingforce = 1 / (1 + Math.exp((distance / (minimumDistance / 2) - 1) * steepness)), // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingforce,
- fy = Math.sin(angle) * repulsingforce;
- nodes[n]._addForce(fx, fy);
- edges[l].from._addForce(-fx/2,-fy/2);
- edges[l].to._addForce(-fx/2,-fy/2);
- }
- }
- */
-
- // forces caused by the edges, modelled as springs
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected) {
- dx = (edge.to.x - edge.from.x);
- dy = (edge.to.y - edge.from.y);
- //edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
- //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
- //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
- edgeLength = edge.length;
- length = Math.sqrt(dx * dx + dy * dy);
- angle = Math.atan2(dy, dx);
-
- springForce = edge.stiffness * (edgeLength - length);
-
- fx = Math.cos(angle) * springForce;
- fy = Math.sin(angle) * springForce;
-
- edge.from._addForce(-fx, -fy);
- edge.to._addForce(fx, fy);
- }
- }
- }
-
- /* TODO: re-implement repulsion of edges
- // repulsing forces between edges
- var minimumDistance = this.constants.edges.distance,
- steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
- for (var l = 0; l < edges.length; l++) {
- //Keep distance from other edge centers
- for (var l2 = l + 1; l2 < this.edges.length; l2++) {
- //var dmin = (nodes[n].width + nodes[n].height + nodes[n2].width + nodes[n2].height) / 1 || minimumDistance, // TODO: dmin
- //var dmin = (nodes[n].width + nodes[n2].width)/2 || minimumDistance, // TODO: dmin
- //dmin = 40 + ((nodes[n].width/2 + nodes[n2].width/2) || 0),
- var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
- ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
- l2x = edges[l2].from.x+(edges[l2].to.x - edges[l2].from.x)/2,
- l2y = edges[l2].from.y+(edges[l2].to.y - edges[l2].from.y)/2,
-
- // calculate normally distributed force
- dx = l2x - lx,
- dy = l2y - ly,
- distance = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(dy, dx),
-
-
- // TODO: correct factor for repulsing force
- //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
- repulsingforce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)), // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingforce,
- fy = Math.sin(angle) * repulsingforce;
-
- edges[l].from._addForce(-fx, -fy);
- edges[l].to._addForce(-fx, -fy);
- edges[l2].from._addForce(fx, fy);
- edges[l2].to._addForce(fx, fy);
- }
- }
- */
+ }
+ }
+ }
+
+ /* TODO: re-implement repulsion of edges
+ for (var n = 0; n < nodes.length; n++) {
+ for (var l = 0; l < edges.length; l++) {
+ var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
+ ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
+
+ // calculate normally distributed force
+ dx = nodes[n].x - lx,
+ dy = nodes[n].y - ly,
+ distance = Math.sqrt(dx * dx + dy * dy),
+ angle = Math.atan2(dy, dx),
+
+
+ // TODO: correct factor for repulsing force
+ //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
+ repulsingforce = 1 / (1 + Math.exp((distance / (minimumDistance / 2) - 1) * steepness)), // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingforce,
+ fy = Math.sin(angle) * repulsingforce;
+ nodes[n]._addForce(fx, fy);
+ edges[l].from._addForce(-fx/2,-fy/2);
+ edges[l].to._addForce(-fx/2,-fy/2);
+ }
+ }
+ */
+
+ // forces caused by the edges, modelled as springs
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected) {
+ dx = (edge.to.x - edge.from.x);
+ dy = (edge.to.y - edge.from.y);
+ //edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
+ //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
+ //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
+ edgeLength = edge.length;
+ length = Math.sqrt(dx * dx + dy * dy);
+ angle = Math.atan2(dy, dx);
+
+ springForce = edge.stiffness * (edgeLength - length);
+
+ fx = Math.cos(angle) * springForce;
+ fy = Math.sin(angle) * springForce;
+
+ edge.from._addForce(-fx, -fy);
+ edge.to._addForce(fx, fy);
+ }
+ }
+ }
+
+ /* TODO: re-implement repulsion of edges
+ // repulsing forces between edges
+ var minimumDistance = this.constants.edges.distance,
+ steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
+ for (var l = 0; l < edges.length; l++) {
+ //Keep distance from other edge centers
+ for (var l2 = l + 1; l2 < this.edges.length; l2++) {
+ //var dmin = (nodes[n].width + nodes[n].height + nodes[n2].width + nodes[n2].height) / 1 || minimumDistance, // TODO: dmin
+ //var dmin = (nodes[n].width + nodes[n2].width)/2 || minimumDistance, // TODO: dmin
+ //dmin = 40 + ((nodes[n].width/2 + nodes[n2].width/2) || 0),
+ var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
+ ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
+ l2x = edges[l2].from.x+(edges[l2].to.x - edges[l2].from.x)/2,
+ l2y = edges[l2].from.y+(edges[l2].to.y - edges[l2].from.y)/2,
+
+ // calculate normally distributed force
+ dx = l2x - lx,
+ dy = l2y - ly,
+ distance = Math.sqrt(dx * dx + dy * dy),
+ angle = Math.atan2(dy, dx),
+
+
+ // TODO: correct factor for repulsing force
+ //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
+ repulsingforce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)), // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingforce,
+ fy = Math.sin(angle) * repulsingforce;
+
+ edges[l].from._addForce(-fx, -fy);
+ edges[l].to._addForce(-fx, -fy);
+ edges[l2].from._addForce(fx, fy);
+ edges[l2].to._addForce(fx, fy);
+ }
+ }
+ */
};
@@ -1674,14 +1674,14 @@ Graph.prototype._calculateForces = function() {
* @private
*/
Graph.prototype._isMoving = function(vmin) {
- // TODO: ismoving does not work well: should check the kinetic energy, not its velocity
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) {
- return true;
- }
- }
- return false;
+ // TODO: ismoving does not work well: should check the kinetic energy, not its velocity
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) {
+ return true;
+ }
+ }
+ return false;
};
@@ -1690,49 +1690,49 @@ Graph.prototype._isMoving = function(vmin) {
* @private
*/
Graph.prototype._discreteStepNodes = function() {
- var interval = this.refreshRate / 1000.0; // in seconds
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- nodes[id].discreteStep(interval);
- }
+ var interval = this.refreshRate / 1000.0; // in seconds
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ nodes[id].discreteStep(interval);
}
+ }
};
/**
* Start animating nodes and edges
*/
Graph.prototype.start = function() {
- if (this.moving) {
- this._calculateForces();
- this._discreteStepNodes();
-
- var vmin = this.constants.minVelocity;
- this.moving = this._isMoving(vmin);
- }
+ if (this.moving) {
+ this._calculateForces();
+ this._discreteStepNodes();
- if (this.moving) {
- // start animation. only start timer if it is not already running
- if (!this.timer) {
- var graph = this;
- this.timer = window.setTimeout(function () {
- graph.timer = undefined;
- graph.start();
- graph._redraw();
- }, this.refreshRate);
- }
- }
- else {
- this._redraw();
+ var vmin = this.constants.minVelocity;
+ this.moving = this._isMoving(vmin);
+ }
+
+ if (this.moving) {
+ // start animation. only start timer if it is not already running
+ if (!this.timer) {
+ var graph = this;
+ this.timer = window.setTimeout(function () {
+ graph.timer = undefined;
+ graph.start();
+ graph._redraw();
+ }, this.refreshRate);
}
+ }
+ else {
+ this._redraw();
+ }
};
/**
* Stop animating nodes and edges.
*/
Graph.prototype.stop = function () {
- if (this.timer) {
- window.clearInterval(this.timer);
- this.timer = undefined;
- }
+ if (this.timer) {
+ window.clearInterval(this.timer);
+ this.timer = undefined;
+ }
};
diff --git a/src/graph/Groups.js b/src/graph/Groups.js
index a33f5e47..4d92ba7d 100644
--- a/src/graph/Groups.js
+++ b/src/graph/Groups.js
@@ -3,8 +3,8 @@
* This class can store groups and properties specific for groups.
*/
Groups = function () {
- this.clear();
- this.defaultIndex = 0;
+ this.clear();
+ this.defaultIndex = 0;
};
@@ -12,16 +12,16 @@ Groups = function () {
* default constants for group colors
*/
Groups.DEFAULT = [
- {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
- {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
- {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red
- {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green
- {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
- {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
- {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange
- {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
- {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
- {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint
+ {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
+ {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
+ {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red
+ {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green
+ {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
+ {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
+ {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange
+ {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
+ {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
+ {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint
];
@@ -29,17 +29,17 @@ Groups.DEFAULT = [
* Clear all groups
*/
Groups.prototype.clear = function () {
- this.groups = {};
- this.groups.length = function()
- {
- var i = 0;
- for ( var p in this ) {
- if (this.hasOwnProperty(p)) {
- i++;
- }
- }
- return i;
+ this.groups = {};
+ this.groups.length = function()
+ {
+ var i = 0;
+ for ( var p in this ) {
+ if (this.hasOwnProperty(p)) {
+ i++;
+ }
}
+ return i;
+ }
};
@@ -50,18 +50,18 @@ Groups.prototype.clear = function () {
* @return {Object} group The created group, containing all group properties
*/
Groups.prototype.get = function (groupname) {
- var group = this.groups[groupname];
+ var group = this.groups[groupname];
- if (group == undefined) {
- // create new group
- var index = this.defaultIndex % Groups.DEFAULT.length;
- this.defaultIndex++;
- group = {};
- group.color = Groups.DEFAULT[index];
- this.groups[groupname] = group;
- }
+ if (group == undefined) {
+ // create new group
+ var index = this.defaultIndex % Groups.DEFAULT.length;
+ this.defaultIndex++;
+ group = {};
+ group.color = Groups.DEFAULT[index];
+ this.groups[groupname] = group;
+ }
- return group;
+ return group;
};
/**
@@ -72,9 +72,9 @@ Groups.prototype.get = function (groupname) {
* @return {Object} group The created group object
*/
Groups.prototype.add = function (groupname, style) {
- this.groups[groupname] = style;
- if (style.color) {
- style.color = Node.parseColor(style.color);
- }
- return style;
+ this.groups[groupname] = style;
+ if (style.color) {
+ style.color = Node.parseColor(style.color);
+ }
+ return style;
};
diff --git a/src/graph/Images.js b/src/graph/Images.js
index 6dc20d22..48517dde 100644
--- a/src/graph/Images.js
+++ b/src/graph/Images.js
@@ -3,9 +3,9 @@
* This class loads images and keeps them stored.
*/
Images = function () {
- this.images = {};
+ this.images = {};
- this.callback = undefined;
+ this.callback = undefined;
};
/**
@@ -14,7 +14,7 @@ Images = function () {
* @param {function} callback
*/
Images.prototype.setOnloadCallback = function(callback) {
- this.callback = callback;
+ this.callback = callback;
};
/**
@@ -23,19 +23,19 @@ Images.prototype.setOnloadCallback = function(callback) {
* @return {Image} img The image object
*/
Images.prototype.load = function(url) {
- var img = this.images[url];
- if (img == undefined) {
- // create the image
- var images = this;
- img = new Image();
- this.images[url] = img;
- img.onload = function() {
- if (images.callback) {
- images.callback(this);
- }
- };
- img.src = url;
- }
+ var img = this.images[url];
+ if (img == undefined) {
+ // create the image
+ var images = this;
+ img = new Image();
+ this.images[url] = img;
+ img.onload = function() {
+ if (images.callback) {
+ images.callback(this);
+ }
+ };
+ img.src = url;
+ }
- return img;
+ return img;
};
diff --git a/src/graph/Node.js b/src/graph/Node.js
index edb10a6c..934c1399 100644
--- a/src/graph/Node.js
+++ b/src/graph/Node.js
@@ -23,43 +23,43 @@
* example for the color
*/
function Node(properties, imagelist, grouplist, constants) {
- this.selected = false;
+ this.selected = false;
- this.edges = []; // all edges connected to this node
- this.group = constants.nodes.group;
+ this.edges = []; // all edges connected to this node
+ this.group = constants.nodes.group;
- this.fontSize = constants.nodes.fontSize;
- this.fontFace = constants.nodes.fontFace;
- this.fontColor = constants.nodes.fontColor;
+ this.fontSize = constants.nodes.fontSize;
+ this.fontFace = constants.nodes.fontFace;
+ this.fontColor = constants.nodes.fontColor;
- this.color = constants.nodes.color;
+ this.color = constants.nodes.color;
- // set defaults for the properties
- this.id = undefined;
- this.shape = constants.nodes.shape;
- this.image = constants.nodes.image;
- this.x = 0;
- this.y = 0;
- this.xFixed = false;
- this.yFixed = false;
- this.radius = constants.nodes.radius;
- this.radiusFixed = false;
- this.radiusMin = constants.nodes.radiusMin;
- this.radiusMax = constants.nodes.radiusMax;
+ // set defaults for the properties
+ this.id = undefined;
+ this.shape = constants.nodes.shape;
+ this.image = constants.nodes.image;
+ this.x = 0;
+ this.y = 0;
+ this.xFixed = false;
+ this.yFixed = false;
+ this.radius = constants.nodes.radius;
+ this.radiusFixed = false;
+ this.radiusMin = constants.nodes.radiusMin;
+ this.radiusMax = constants.nodes.radiusMax;
- this.imagelist = imagelist;
- this.grouplist = grouplist;
+ this.imagelist = imagelist;
+ this.grouplist = grouplist;
- this.setProperties(properties, constants);
+ this.setProperties(properties, constants);
- // mass, force, velocity
- this.mass = 50; // kg (mass is adjusted for the number of connected edges)
- this.fx = 0.0; // external force x
- this.fy = 0.0; // external force y
- this.vx = 0.0; // velocity x
- this.vy = 0.0; // velocity y
- this.minForce = constants.minForce;
- this.damping = 0.9; // damping factor
+ // mass, force, velocity
+ this.mass = 50; // kg (mass is adjusted for the number of connected edges)
+ this.fx = 0.0; // external force x
+ this.fy = 0.0; // external force y
+ this.vx = 0.0; // velocity x
+ this.vy = 0.0; // velocity y
+ this.minForce = constants.minForce;
+ this.damping = 0.9; // damping factor
};
/**
@@ -67,10 +67,10 @@ function Node(properties, imagelist, grouplist, constants) {
* @param {Edge} edge
*/
Node.prototype.attachEdge = function(edge) {
- if (this.edges.indexOf(edge) == -1) {
- this.edges.push(edge);
- }
- this._updateMass();
+ if (this.edges.indexOf(edge) == -1) {
+ this.edges.push(edge);
+ }
+ this._updateMass();
};
/**
@@ -78,11 +78,11 @@ Node.prototype.attachEdge = function(edge) {
* @param {Edge} edge
*/
Node.prototype.detachEdge = function(edge) {
- var index = this.edges.indexOf(edge);
- if (index != -1) {
- this.edges.splice(index, 1);
- }
- this._updateMass();
+ var index = this.edges.indexOf(edge);
+ if (index != -1) {
+ this.edges.splice(index, 1);
+ }
+ this._updateMass();
};
/**
@@ -91,7 +91,7 @@ Node.prototype.detachEdge = function(edge) {
* @private
*/
Node.prototype._updateMass = function() {
- this.mass = 50 + 20 * this.edges.length; // kg
+ this.mass = 50 + 20 * this.edges.length; // kg
};
/**
@@ -100,81 +100,81 @@ Node.prototype._updateMass = function() {
* @param {Object} constants and object with default, global properties
*/
Node.prototype.setProperties = function(properties, constants) {
- if (!properties) {
- return;
- }
-
- // basic properties
- if (properties.id != undefined) {this.id = properties.id;}
- if (properties.label != undefined) {this.label = properties.label;}
- if (properties.title != undefined) {this.title = properties.title;}
- if (properties.group != undefined) {this.group = properties.group;}
- if (properties.x != undefined) {this.x = properties.x;}
- if (properties.y != undefined) {this.y = properties.y;}
- if (properties.value != undefined) {this.value = properties.value;}
-
- if (this.id === undefined) {
- throw "Node must have an id";
- }
-
- // copy group properties
- if (this.group) {
- var groupObj = this.grouplist.get(this.group);
- for (var prop in groupObj) {
- if (groupObj.hasOwnProperty(prop)) {
- this[prop] = groupObj[prop];
- }
- }
+ if (!properties) {
+ return;
+ }
+
+ // basic properties
+ if (properties.id != undefined) {this.id = properties.id;}
+ if (properties.label != undefined) {this.label = properties.label;}
+ if (properties.title != undefined) {this.title = properties.title;}
+ if (properties.group != undefined) {this.group = properties.group;}
+ if (properties.x != undefined) {this.x = properties.x;}
+ if (properties.y != undefined) {this.y = properties.y;}
+ if (properties.value != undefined) {this.value = properties.value;}
+
+ if (this.id === undefined) {
+ throw "Node must have an id";
+ }
+
+ // copy group properties
+ if (this.group) {
+ var groupObj = this.grouplist.get(this.group);
+ for (var prop in groupObj) {
+ if (groupObj.hasOwnProperty(prop)) {
+ this[prop] = groupObj[prop];
+ }
}
+ }
- // individual shape properties
- if (properties.shape != undefined) {this.shape = properties.shape;}
- if (properties.image != undefined) {this.image = properties.image;}
- if (properties.radius != undefined) {this.radius = properties.radius;}
- if (properties.color != undefined) {this.color = Node.parseColor(properties.color);}
-
- if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
- if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
- if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
-
+ // individual shape properties
+ if (properties.shape != undefined) {this.shape = properties.shape;}
+ if (properties.image != undefined) {this.image = properties.image;}
+ if (properties.radius != undefined) {this.radius = properties.radius;}
+ if (properties.color != undefined) {this.color = Node.parseColor(properties.color);}
- if (this.image != undefined) {
- if (this.imagelist) {
- this.imageObj = this.imagelist.load(this.image);
- }
- else {
- throw "No imagelist provided";
- }
- }
+ if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
+ if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
+ if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
- this.xFixed = this.xFixed || (properties.x != undefined);
- this.yFixed = this.yFixed || (properties.y != undefined);
- this.radiusFixed = this.radiusFixed || (properties.radius != undefined);
- if (this.shape == 'image') {
- this.radiusMin = constants.nodes.widthMin;
- this.radiusMax = constants.nodes.widthMax;
+ if (this.image != undefined) {
+ if (this.imagelist) {
+ this.imageObj = this.imagelist.load(this.image);
}
-
- // choose draw method depending on the shape
- switch (this.shape) {
- case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break;
- case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break;
- case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break;
- case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
- // TODO: add diamond shape
- case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
- case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
- case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
- case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
- case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break;
- case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break;
- case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break;
- default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
+ else {
+ throw "No imagelist provided";
}
-
- // reset the size of the node, this can be changed
- this._reset();
+ }
+
+ this.xFixed = this.xFixed || (properties.x != undefined);
+ this.yFixed = this.yFixed || (properties.y != undefined);
+ this.radiusFixed = this.radiusFixed || (properties.radius != undefined);
+
+ if (this.shape == 'image') {
+ this.radiusMin = constants.nodes.widthMin;
+ this.radiusMax = constants.nodes.widthMax;
+ }
+
+ // choose draw method depending on the shape
+ switch (this.shape) {
+ case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break;
+ case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break;
+ case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break;
+ case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
+ // TODO: add diamond shape
+ case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
+ case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
+ case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
+ case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
+ case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break;
+ case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break;
+ case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break;
+ default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
+ }
+
+ // reset the size of the node, this can be changed
+ this._reset();
};
/**
@@ -184,51 +184,51 @@ Node.prototype.setProperties = function(properties, constants) {
* @return {Object} colorObject
*/
Node.parseColor = function(color) {
- var c;
- if (util.isString(color)) {
- c = {
- border: color,
- background: color,
- highlight: {
- border: color,
- background: color
- }
- };
- // TODO: automatically generate a nice highlight color
+ var c;
+ if (util.isString(color)) {
+ c = {
+ border: color,
+ background: color,
+ highlight: {
+ border: color,
+ background: color
+ }
+ };
+ // TODO: automatically generate a nice highlight color
+ }
+ else {
+ c = {};
+ c.background = color.background || 'white';
+ c.border = color.border || c.background;
+ if (util.isString(color.highlight)) {
+ c.highlight = {
+ border: color.highlight,
+ background: color.highlight
+ }
}
else {
- c = {};
- c.background = color.background || 'white';
- c.border = color.border || c.background;
- if (util.isString(color.highlight)) {
- c.highlight = {
- border: color.highlight,
- background: color.highlight
- }
- }
- else {
- c.highlight = {};
- c.highlight.background = color.highlight && color.highlight.background || c.background;
- c.highlight.border = color.highlight && color.highlight.border || c.border;
- }
+ c.highlight = {};
+ c.highlight.background = color.highlight && color.highlight.background || c.background;
+ c.highlight.border = color.highlight && color.highlight.border || c.border;
}
- return c;
+ }
+ return c;
};
/**
* select this node
*/
Node.prototype.select = function() {
- this.selected = true;
- this._reset();
+ this.selected = true;
+ this._reset();
};
/**
* unselect this node
*/
Node.prototype.unselect = function() {
- this.selected = false;
- this._reset();
+ this.selected = false;
+ this._reset();
};
/**
@@ -236,8 +236,8 @@ Node.prototype.unselect = function() {
* @private
*/
Node.prototype._reset = function() {
- this.width = undefined;
- this.height = undefined;
+ this.width = undefined;
+ this.height = undefined;
};
/**
@@ -246,7 +246,7 @@ Node.prototype._reset = function() {
* has been set.
*/
Node.prototype.getTitle = function() {
- return this.title;
+ return this.title;
};
/**
@@ -256,46 +256,46 @@ Node.prototype.getTitle = function() {
* @returns {number} distance Distance to the border in pixels
*/
Node.prototype.distanceToBorder = function (ctx, angle) {
- var borderWidth = 1;
-
- if (!this.width) {
- this.resize(ctx);
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (this.shape) {
- case 'circle':
- case 'dot':
- return this.radius + borderWidth;
-
- case 'ellipse':
- var a = this.width / 2;
- var b = this.height / 2;
- var w = (Math.sin(angle) * a);
- var h = (Math.cos(angle) * b);
- return a * b / Math.sqrt(w * w + h * h);
-
- // TODO: implement distanceToBorder for database
- // TODO: implement distanceToBorder for triangle
- // TODO: implement distanceToBorder for triangleDown
-
- case 'box':
- case 'image':
- case 'text':
- default:
- if (this.width) {
- return Math.min(
- Math.abs(this.width / 2 / Math.cos(angle)),
- Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
- // TODO: reckon with border radius too in case of box
- }
- else {
- return 0;
- }
+ var borderWidth = 1;
+
+ if (!this.width) {
+ this.resize(ctx);
+ }
+
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.shape) {
+ case 'circle':
+ case 'dot':
+ return this.radius + borderWidth;
+
+ case 'ellipse':
+ var a = this.width / 2;
+ var b = this.height / 2;
+ var w = (Math.sin(angle) * a);
+ var h = (Math.cos(angle) * b);
+ return a * b / Math.sqrt(w * w + h * h);
+
+ // TODO: implement distanceToBorder for database
+ // TODO: implement distanceToBorder for triangle
+ // TODO: implement distanceToBorder for triangleDown
+
+ case 'box':
+ case 'image':
+ case 'text':
+ default:
+ if (this.width) {
+ return Math.min(
+ Math.abs(this.width / 2 / Math.cos(angle)),
+ Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
+ // TODO: reckon with border radius too in case of box
+ }
+ else {
+ return 0;
+ }
- }
+ }
- // TODO: implement calculation of distance to border for all shapes
+ // TODO: implement calculation of distance to border for all shapes
};
/**
@@ -304,8 +304,8 @@ Node.prototype.distanceToBorder = function (ctx, angle) {
* @param {number} fy Force in vertical direction
*/
Node.prototype._setForce = function(fx, fy) {
- this.fx = fx;
- this.fy = fy;
+ this.fx = fx;
+ this.fy = fy;
};
/**
@@ -315,8 +315,8 @@ Node.prototype._setForce = function(fx, fy) {
* @private
*/
Node.prototype._addForce = function(fx, fy) {
- this.fx += fx;
- this.fy += fy;
+ this.fx += fx;
+ this.fy += fy;
};
/**
@@ -324,19 +324,19 @@ Node.prototype._addForce = function(fx, fy) {
* @param {number} interval Time interval in seconds
*/
Node.prototype.discreteStep = function(interval) {
- if (!this.xFixed) {
- var dx = -this.damping * this.vx; // damping force
- var ax = (this.fx + dx) / this.mass; // acceleration
- this.vx += ax / interval; // velocity
- this.x += this.vx / interval; // position
- }
+ if (!this.xFixed) {
+ var dx = -this.damping * this.vx; // damping force
+ var ax = (this.fx + dx) / this.mass; // acceleration
+ this.vx += ax / interval; // velocity
+ this.x += this.vx / interval; // position
+ }
- if (!this.yFixed) {
- var dy = -this.damping * this.vy; // damping force
- var ay = (this.fy + dy) / this.mass; // acceleration
- this.vy += ay / interval; // velocity
- this.y += this.vy / interval; // position
- }
+ if (!this.yFixed) {
+ var dy = -this.damping * this.vy; // damping force
+ var ay = (this.fy + dy) / this.mass; // acceleration
+ this.vy += ay / interval; // velocity
+ this.y += this.vy / interval; // position
+ }
};
@@ -345,7 +345,7 @@ Node.prototype.discreteStep = function(interval) {
* @return {boolean} true if fixed, false if not
*/
Node.prototype.isFixed = function() {
- return (this.xFixed && this.yFixed);
+ return (this.xFixed && this.yFixed);
};
/**
@@ -355,9 +355,9 @@ Node.prototype.isFixed = function() {
*/
// TODO: replace this method with calculating the kinetic energy
Node.prototype.isMoving = function(vmin) {
- return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin ||
- (!this.xFixed && Math.abs(this.fx) > this.minForce) ||
- (!this.yFixed && Math.abs(this.fy) > this.minForce));
+ return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin ||
+ (!this.xFixed && Math.abs(this.fx) > this.minForce) ||
+ (!this.yFixed && Math.abs(this.fy) > this.minForce));
};
/**
@@ -365,7 +365,7 @@ Node.prototype.isMoving = function(vmin) {
* @return {boolean} selected True if node is selected, else false
*/
Node.prototype.isSelected = function() {
- return this.selected;
+ return this.selected;
};
/**
@@ -373,7 +373,7 @@ Node.prototype.isSelected = function() {
* @return {Number} value
*/
Node.prototype.getValue = function() {
- return this.value;
+ return this.value;
};
/**
@@ -383,9 +383,9 @@ Node.prototype.getValue = function() {
* @return {Number} value
*/
Node.prototype.getDistance = function(x, y) {
- var dx = this.x - x,
- dy = this.y - y;
- return Math.sqrt(dx * dx + dy * dy);
+ var dx = this.x - x,
+ dy = this.y - y;
+ return Math.sqrt(dx * dx + dy * dy);
};
@@ -396,15 +396,15 @@ Node.prototype.getDistance = function(x, y) {
* @param {Number} max
*/
Node.prototype.setValueRange = function(min, max) {
- if (!this.radiusFixed && this.value !== undefined) {
- if (max == min) {
- this.radius = (this.radiusMin + this.radiusMax) / 2;
- }
- else {
- var scale = (this.radiusMax - this.radiusMin) / (max - min);
- this.radius = (this.value - min) * scale + this.radiusMin;
- }
+ if (!this.radiusFixed && this.value !== undefined) {
+ if (max == min) {
+ this.radius = (this.radiusMin + this.radiusMax) / 2;
+ }
+ else {
+ var scale = (this.radiusMax - this.radiusMin) / (max - min);
+ this.radius = (this.value - min) * scale + this.radiusMin;
}
+ }
};
/**
@@ -413,7 +413,7 @@ Node.prototype.setValueRange = function(min, max) {
* @param {CanvasRenderingContext2D} ctx
*/
Node.prototype.draw = function(ctx) {
- throw "Draw method not initialized for node";
+ throw "Draw method not initialized for node";
};
/**
@@ -422,7 +422,7 @@ Node.prototype.draw = function(ctx) {
* @param {CanvasRenderingContext2D} ctx
*/
Node.prototype.resize = function(ctx) {
- throw "Resize method not initialized for node";
+ throw "Resize method not initialized for node";
};
/**
@@ -431,256 +431,256 @@ Node.prototype.resize = function(ctx) {
* @return {boolean} True if location is located on node
*/
Node.prototype.isOverlappingWith = function(obj) {
- return (this.left < obj.right &&
- this.left + this.width > obj.left &&
- this.top < obj.bottom &&
- this.top + this.height > obj.top);
+ return (this.left < obj.right &&
+ this.left + this.width > obj.left &&
+ this.top < obj.bottom &&
+ this.top + this.height > obj.top);
};
Node.prototype._resizeImage = function (ctx) {
- // TODO: pre calculate the image size
- if (!this.width) { // undefined or 0
- var width, height;
- if (this.value) {
- var scale = this.imageObj.height / this.imageObj.width;
- width = this.radius || this.imageObj.width;
- height = this.radius * scale || this.imageObj.height;
- }
- else {
- width = this.imageObj.width;
- height = this.imageObj.height;
- }
- this.width = width;
- this.height = height;
+ // TODO: pre calculate the image size
+ if (!this.width) { // undefined or 0
+ var width, height;
+ if (this.value) {
+ var scale = this.imageObj.height / this.imageObj.width;
+ width = this.radius || this.imageObj.width;
+ height = this.radius * scale || this.imageObj.height;
}
+ else {
+ width = this.imageObj.width;
+ height = this.imageObj.height;
+ }
+ this.width = width;
+ this.height = height;
+ }
};
Node.prototype._drawImage = function (ctx) {
- this._resizeImage(ctx);
+ this._resizeImage(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- var yLabel;
- if (this.imageObj) {
- ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
- yLabel = this.y + this.height / 2;
- }
- else {
- // image still loading... just draw the label for now
- yLabel = this.y;
- }
+ var yLabel;
+ if (this.imageObj) {
+ ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
+ yLabel = this.y + this.height / 2;
+ }
+ else {
+ // image still loading... just draw the label for now
+ yLabel = this.y;
+ }
- this._label(ctx, this.label, this.x, yLabel, undefined, "top");
+ this._label(ctx, this.label, this.x, yLabel, undefined, "top");
};
Node.prototype._resizeBox = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- this.width = textSize.width + 2 * margin;
- this.height = textSize.height + 2 * margin;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ this.width = textSize.width + 2 * margin;
+ this.height = textSize.height + 2 * margin;
+ }
};
Node.prototype._drawBox = function (ctx) {
- this._resizeBox(ctx);
+ this._resizeBox(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.roundRect(this.left, this.top, this.width, this.height, this.radius);
- ctx.fill();
- ctx.stroke();
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.roundRect(this.left, this.top, this.width, this.height, this.radius);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeDatabase = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- var size = textSize.width + 2 * margin;
- this.width = size;
- this.height = size;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ var size = textSize.width + 2 * margin;
+ this.width = size;
+ this.height = size;
+ }
};
Node.prototype._drawDatabase = function (ctx) {
- this._resizeDatabase(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this._resizeDatabase(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height);
- ctx.fill();
- ctx.stroke();
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeCircle = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
- this.radius = diameter / 2;
-
- this.width = diameter;
- this.height = diameter;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
+ this.radius = diameter / 2;
+
+ this.width = diameter;
+ this.height = diameter;
+ }
};
Node.prototype._drawCircle = function (ctx) {
- this._resizeCircle(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this._resizeCircle(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.circle(this.x, this.y, this.radius);
- ctx.fill();
- ctx.stroke();
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.circle(this.x, this.y, this.radius);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeEllipse = function (ctx) {
- if (!this.width) {
- var textSize = this.getTextSize(ctx);
-
- this.width = textSize.width * 1.5;
- this.height = textSize.height * 2;
- if (this.width < this.height) {
- this.width = this.height;
- }
+ if (!this.width) {
+ var textSize = this.getTextSize(ctx);
+
+ this.width = textSize.width * 1.5;
+ this.height = textSize.height * 2;
+ if (this.width < this.height) {
+ this.width = this.height;
}
+ }
};
Node.prototype._drawEllipse = function (ctx) {
- this._resizeEllipse(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this._resizeEllipse(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.ellipse(this.left, this.top, this.width, this.height);
- ctx.fill();
- ctx.stroke();
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.ellipse(this.left, this.top, this.width, this.height);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._drawDot = function (ctx) {
- this._drawShape(ctx, 'circle');
+ this._drawShape(ctx, 'circle');
};
Node.prototype._drawTriangle = function (ctx) {
- this._drawShape(ctx, 'triangle');
+ this._drawShape(ctx, 'triangle');
};
Node.prototype._drawTriangleDown = function (ctx) {
- this._drawShape(ctx, 'triangleDown');
+ this._drawShape(ctx, 'triangleDown');
};
Node.prototype._drawSquare = function (ctx) {
- this._drawShape(ctx, 'square');
+ this._drawShape(ctx, 'square');
};
Node.prototype._drawStar = function (ctx) {
- this._drawShape(ctx, 'star');
+ this._drawShape(ctx, 'star');
};
Node.prototype._resizeShape = function (ctx) {
- if (!this.width) {
- var size = 2 * this.radius;
- this.width = size;
- this.height = size;
- }
+ if (!this.width) {
+ var size = 2 * this.radius;
+ this.width = size;
+ this.height = size;
+ }
};
Node.prototype._drawShape = function (ctx, shape) {
- this._resizeShape(ctx);
+ this._resizeShape(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx[shape](this.x, this.y, this.radius);
- ctx.fill();
- ctx.stroke();
+ ctx[shape](this.x, this.y, this.radius);
+ ctx.fill();
+ ctx.stroke();
- if (this.label) {
- this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top');
- }
+ if (this.label) {
+ this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top');
+ }
};
Node.prototype._resizeText = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- this.width = textSize.width + 2 * margin;
- this.height = textSize.height + 2 * margin;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ this.width = textSize.width + 2 * margin;
+ this.height = textSize.height + 2 * margin;
+ }
};
Node.prototype._drawText = function (ctx) {
- this._resizeText(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this._resizeText(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._label = function (ctx, text, x, y, align, baseline) {
- if (text) {
- ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
- ctx.fillStyle = this.fontColor || "black";
- ctx.textAlign = align || "center";
- ctx.textBaseline = baseline || "middle";
-
- var lines = text.split('\n'),
- lineCount = lines.length,
- fontSize = (this.fontSize + 4),
- yLine = y + (1 - lineCount) / 2 * fontSize;
-
- for (var i = 0; i < lineCount; i++) {
- ctx.fillText(lines[i], x, yLine);
- yLine += fontSize;
- }
+ if (text) {
+ ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
+ ctx.fillStyle = this.fontColor || "black";
+ ctx.textAlign = align || "center";
+ ctx.textBaseline = baseline || "middle";
+
+ var lines = text.split('\n'),
+ lineCount = lines.length,
+ fontSize = (this.fontSize + 4),
+ yLine = y + (1 - lineCount) / 2 * fontSize;
+
+ for (var i = 0; i < lineCount; i++) {
+ ctx.fillText(lines[i], x, yLine);
+ yLine += fontSize;
}
+ }
};
Node.prototype.getTextSize = function(ctx) {
- if (this.label != undefined) {
- ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
-
- var lines = this.label.split('\n'),
- height = (this.fontSize + 4) * lines.length,
- width = 0;
+ if (this.label != undefined) {
+ ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
- for (var i = 0, iMax = lines.length; i < iMax; i++) {
- width = Math.max(width, ctx.measureText(lines[i]).width);
- }
+ var lines = this.label.split('\n'),
+ height = (this.fontSize + 4) * lines.length,
+ width = 0;
- return {"width": width, "height": height};
- }
- else {
- return {"width": 0, "height": 0};
+ for (var i = 0, iMax = lines.length; i < iMax; i++) {
+ width = Math.max(width, ctx.measureText(lines[i]).width);
}
+
+ return {"width": width, "height": height};
+ }
+ else {
+ return {"width": 0, "height": 0};
+ }
};
diff --git a/src/graph/Popup.js b/src/graph/Popup.js
index 2d0121a3..7dc8ffe8 100644
--- a/src/graph/Popup.js
+++ b/src/graph/Popup.js
@@ -6,38 +6,38 @@
* @param {String} [text]
*/
function Popup(container, x, y, text) {
- if (container) {
- this.container = container;
- }
- else {
- this.container = document.body;
- }
- this.x = 0;
- this.y = 0;
- this.padding = 5;
+ if (container) {
+ this.container = container;
+ }
+ else {
+ this.container = document.body;
+ }
+ this.x = 0;
+ this.y = 0;
+ this.padding = 5;
- if (x !== undefined && y !== undefined ) {
- this.setPosition(x, y);
- }
- if (text !== undefined) {
- this.setText(text);
- }
+ if (x !== undefined && y !== undefined ) {
+ this.setPosition(x, y);
+ }
+ if (text !== undefined) {
+ this.setText(text);
+ }
- // create the frame
- this.frame = document.createElement("div");
- var style = this.frame.style;
- style.position = "absolute";
- style.visibility = "hidden";
- style.border = "1px solid #666";
- style.color = "black";
- style.padding = this.padding + "px";
- style.backgroundColor = "#FFFFC6";
- style.borderRadius = "3px";
- style.MozBorderRadius = "3px";
- style.WebkitBorderRadius = "3px";
- style.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)";
- style.whiteSpace = "nowrap";
- this.container.appendChild(this.frame);
+ // create the frame
+ this.frame = document.createElement("div");
+ var style = this.frame.style;
+ style.position = "absolute";
+ style.visibility = "hidden";
+ style.border = "1px solid #666";
+ style.color = "black";
+ style.padding = this.padding + "px";
+ style.backgroundColor = "#FFFFC6";
+ style.borderRadius = "3px";
+ style.MozBorderRadius = "3px";
+ style.WebkitBorderRadius = "3px";
+ style.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)";
+ style.whiteSpace = "nowrap";
+ this.container.appendChild(this.frame);
};
/**
@@ -45,8 +45,8 @@ function Popup(container, x, y, text) {
* @param {number} y Vertical position of the popup window
*/
Popup.prototype.setPosition = function(x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
+ this.x = parseInt(x);
+ this.y = parseInt(y);
};
/**
@@ -54,7 +54,7 @@ Popup.prototype.setPosition = function(x, y) {
* @param {string} text
*/
Popup.prototype.setText = function(text) {
- this.frame.innerHTML = text;
+ this.frame.innerHTML = text;
};
/**
@@ -62,44 +62,44 @@ Popup.prototype.setText = function(text) {
* @param {boolean} show Optional. Show or hide the window
*/
Popup.prototype.show = function (show) {
- if (show === undefined) {
- show = true;
- }
+ if (show === undefined) {
+ show = true;
+ }
- if (show) {
- var height = this.frame.clientHeight;
- var width = this.frame.clientWidth;
- var maxHeight = this.frame.parentNode.clientHeight;
- var maxWidth = this.frame.parentNode.clientWidth;
+ if (show) {
+ var height = this.frame.clientHeight;
+ var width = this.frame.clientWidth;
+ var maxHeight = this.frame.parentNode.clientHeight;
+ var maxWidth = this.frame.parentNode.clientWidth;
- var top = (this.y - height);
- if (top + height + this.padding > maxHeight) {
- top = maxHeight - height - this.padding;
- }
- if (top < this.padding) {
- top = this.padding;
- }
-
- var left = this.x;
- if (left + width + this.padding > maxWidth) {
- left = maxWidth - width - this.padding;
- }
- if (left < this.padding) {
- left = this.padding;
- }
+ var top = (this.y - height);
+ if (top + height + this.padding > maxHeight) {
+ top = maxHeight - height - this.padding;
+ }
+ if (top < this.padding) {
+ top = this.padding;
+ }
- this.frame.style.left = left + "px";
- this.frame.style.top = top + "px";
- this.frame.style.visibility = "visible";
+ var left = this.x;
+ if (left + width + this.padding > maxWidth) {
+ left = maxWidth - width - this.padding;
}
- else {
- this.hide();
+ if (left < this.padding) {
+ left = this.padding;
}
+
+ this.frame.style.left = left + "px";
+ this.frame.style.top = top + "px";
+ this.frame.style.visibility = "visible";
+ }
+ else {
+ this.hide();
+ }
};
/**
* Hide the popup window
*/
Popup.prototype.hide = function () {
- this.frame.style.visibility = "hidden";
+ this.frame.style.visibility = "hidden";
};
diff --git a/src/graph/dotparser.js b/src/graph/dotparser.js
index 46d73d05..71576079 100644
--- a/src/graph/dotparser.js
+++ b/src/graph/dotparser.js
@@ -1,829 +1,829 @@
(function(exports) {
- /**
- * Parse a text source containing data in DOT language into a JSON object.
- * The object contains two lists: one with nodes and one with edges.
- *
- * DOT language reference: http://www.graphviz.org/doc/info/lang.html
- *
- * @param {String} data Text containing a graph in DOT-notation
- * @return {Object} graph An object containing two parameters:
- * {Object[]} nodes
- * {Object[]} edges
- */
- function parseDOT (data) {
- dot = data;
- return parseGraph();
- }
-
- // token types enumeration
- var TOKENTYPE = {
- NULL : 0,
- DELIMITER : 1,
- IDENTIFIER: 2,
- UNKNOWN : 3
- };
-
- // map with all delimiters
- var DELIMITERS = {
- '{': true,
- '}': true,
- '[': true,
- ']': true,
- ';': true,
- '=': true,
- ',': true,
-
- '->': true,
- '--': true
- };
-
- var dot = ''; // current dot file
- var index = 0; // current index in dot file
- var c = ''; // current token character in expr
- var token = ''; // current token
- var tokenType = TOKENTYPE.NULL; // type of the token
-
- /**
- * Get the first character from the dot file.
- * The character is stored into the char c. If the end of the dot file is
- * reached, the function puts an empty string in c.
- */
- function first() {
- index = 0;
- c = dot.charAt(0);
- }
-
- /**
- * Get the next character from the dot file.
- * The character is stored into the char c. If the end of the dot file is
- * reached, the function puts an empty string in c.
- */
- function next() {
- index++;
- c = dot.charAt(index);
- }
-
- /**
- * Preview the next character from the dot file.
- * @return {String} cNext
- */
- function nextPreview() {
- return dot.charAt(index + 1);
- }
-
- /**
- * Test whether given character is alphabetic or numeric
- * @param {String} c
- * @return {Boolean} isAlphaNumeric
- */
- var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
- function isAlphaNumeric(c) {
- return regexAlphaNumeric.test(c);
- }
-
- /**
- * Merge all properties of object b into object b
- * @param {Object} a
- * @param {Object} b
- * @return {Object} a
- */
- function merge (a, b) {
- if (!a) {
- a = {};
- }
-
- if (b) {
- for (var name in b) {
- if (b.hasOwnProperty(name)) {
- a[name] = b[name];
- }
- }
- }
- return a;
- }
-
- /**
- * Set a value in an object, where the provided parameter name can be a
- * path with nested parameters. For example:
- *
- * var obj = {a: 2};
- * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}}
- *
- * @param {Object} obj
- * @param {String} path A parameter name or dot-separated parameter path,
- * like "color.highlight.border".
- * @param {*} value
- */
- function setValue(obj, path, value) {
- var keys = path.split('.');
- var o = obj;
- while (keys.length) {
- var key = keys.shift();
- if (keys.length) {
- // this isn't the end point
- if (!o[key]) {
- o[key] = {};
- }
- o = o[key];
- }
- else {
- // this is the end point
- o[key] = value;
- }
- }
+ /**
+ * Parse a text source containing data in DOT language into a JSON object.
+ * The object contains two lists: one with nodes and one with edges.
+ *
+ * DOT language reference: http://www.graphviz.org/doc/info/lang.html
+ *
+ * @param {String} data Text containing a graph in DOT-notation
+ * @return {Object} graph An object containing two parameters:
+ * {Object[]} nodes
+ * {Object[]} edges
+ */
+ function parseDOT (data) {
+ dot = data;
+ return parseGraph();
+ }
+
+ // token types enumeration
+ var TOKENTYPE = {
+ NULL : 0,
+ DELIMITER : 1,
+ IDENTIFIER: 2,
+ UNKNOWN : 3
+ };
+
+ // map with all delimiters
+ var DELIMITERS = {
+ '{': true,
+ '}': true,
+ '[': true,
+ ']': true,
+ ';': true,
+ '=': true,
+ ',': true,
+
+ '->': true,
+ '--': true
+ };
+
+ var dot = ''; // current dot file
+ var index = 0; // current index in dot file
+ var c = ''; // current token character in expr
+ var token = ''; // current token
+ var tokenType = TOKENTYPE.NULL; // type of the token
+
+ /**
+ * Get the first character from the dot file.
+ * The character is stored into the char c. If the end of the dot file is
+ * reached, the function puts an empty string in c.
+ */
+ function first() {
+ index = 0;
+ c = dot.charAt(0);
+ }
+
+ /**
+ * Get the next character from the dot file.
+ * The character is stored into the char c. If the end of the dot file is
+ * reached, the function puts an empty string in c.
+ */
+ function next() {
+ index++;
+ c = dot.charAt(index);
+ }
+
+ /**
+ * Preview the next character from the dot file.
+ * @return {String} cNext
+ */
+ function nextPreview() {
+ return dot.charAt(index + 1);
+ }
+
+ /**
+ * Test whether given character is alphabetic or numeric
+ * @param {String} c
+ * @return {Boolean} isAlphaNumeric
+ */
+ var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
+ function isAlphaNumeric(c) {
+ return regexAlphaNumeric.test(c);
+ }
+
+ /**
+ * Merge all properties of object b into object b
+ * @param {Object} a
+ * @param {Object} b
+ * @return {Object} a
+ */
+ function merge (a, b) {
+ if (!a) {
+ a = {};
}
- /**
- * Add a node to a graph object. If there is already a node with
- * the same id, their attributes will be merged.
- * @param {Object} graph
- * @param {Object} node
- */
- function addNode(graph, node) {
- var i, len;
- var current = null;
-
- // find root graph (in case of subgraph)
- var graphs = [graph]; // list with all graphs from current graph to root graph
- var root = graph;
- while (root.parent) {
- graphs.push(root.parent);
- root = root.parent;
- }
-
- // find existing node (at root level) by its id
- if (root.nodes) {
- for (i = 0, len = root.nodes.length; i < len; i++) {
- if (node.id === root.nodes[i].id) {
- current = root.nodes[i];
- break;
- }
- }
- }
-
- if (!current) {
- // this is a new node
- current = {
- id: node.id
- };
- if (graph.node) {
- // clone default attributes
- current.attr = merge(current.attr, graph.node);
- }
- }
-
- // add node to this (sub)graph and all its parent graphs
- for (i = graphs.length - 1; i >= 0; i--) {
- var g = graphs[i];
-
- if (!g.nodes) {
- g.nodes = [];
- }
- if (g.nodes.indexOf(current) == -1) {
- g.nodes.push(current);
- }
- }
-
- // merge attributes
- if (node.attr) {
- current.attr = merge(current.attr, node.attr);
+ if (b) {
+ for (var name in b) {
+ if (b.hasOwnProperty(name)) {
+ a[name] = b[name];
}
+ }
+ }
+ return a;
+ }
+
+ /**
+ * Set a value in an object, where the provided parameter name can be a
+ * path with nested parameters. For example:
+ *
+ * var obj = {a: 2};
+ * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}}
+ *
+ * @param {Object} obj
+ * @param {String} path A parameter name or dot-separated parameter path,
+ * like "color.highlight.border".
+ * @param {*} value
+ */
+ function setValue(obj, path, value) {
+ var keys = path.split('.');
+ var o = obj;
+ while (keys.length) {
+ var key = keys.shift();
+ if (keys.length) {
+ // this isn't the end point
+ if (!o[key]) {
+ o[key] = {};
+ }
+ o = o[key];
+ }
+ else {
+ // this is the end point
+ o[key] = value;
+ }
+ }
+ }
+
+ /**
+ * Add a node to a graph object. If there is already a node with
+ * the same id, their attributes will be merged.
+ * @param {Object} graph
+ * @param {Object} node
+ */
+ function addNode(graph, node) {
+ var i, len;
+ var current = null;
+
+ // find root graph (in case of subgraph)
+ var graphs = [graph]; // list with all graphs from current graph to root graph
+ var root = graph;
+ while (root.parent) {
+ graphs.push(root.parent);
+ root = root.parent;
}
- /**
- * Add an edge to a graph object
- * @param {Object} graph
- * @param {Object} edge
- */
- function addEdge(graph, edge) {
- if (!graph.edges) {
- graph.edges = [];
- }
- graph.edges.push(edge);
- if (graph.edge) {
- var attr = merge({}, graph.edge); // clone default attributes
- edge.attr = merge(attr, edge.attr); // merge attributes
+ // find existing node (at root level) by its id
+ if (root.nodes) {
+ for (i = 0, len = root.nodes.length; i < len; i++) {
+ if (node.id === root.nodes[i].id) {
+ current = root.nodes[i];
+ break;
}
+ }
}
- /**
- * Create an edge to a graph object
- * @param {Object} graph
- * @param {String | Number | Object} from
- * @param {String | Number | Object} to
- * @param {String} type
- * @param {Object | null} attr
- * @return {Object} edge
- */
- function createEdge(graph, from, to, type, attr) {
- var edge = {
- from: from,
- to: to,
- type: type
- };
+ if (!current) {
+ // this is a new node
+ current = {
+ id: node.id
+ };
+ if (graph.node) {
+ // clone default attributes
+ current.attr = merge(current.attr, graph.node);
+ }
+ }
- if (graph.edge) {
- edge.attr = merge({}, graph.edge); // clone default attributes
- }
- edge.attr = merge(edge.attr || {}, attr); // merge attributes
+ // add node to this (sub)graph and all its parent graphs
+ for (i = graphs.length - 1; i >= 0; i--) {
+ var g = graphs[i];
- return edge;
+ if (!g.nodes) {
+ g.nodes = [];
+ }
+ if (g.nodes.indexOf(current) == -1) {
+ g.nodes.push(current);
+ }
}
- /**
- * Get next token in the current dot file.
- * The token and token type are available as token and tokenType
- */
- function getToken() {
- tokenType = TOKENTYPE.NULL;
- token = '';
+ // merge attributes
+ if (node.attr) {
+ current.attr = merge(current.attr, node.attr);
+ }
+ }
+
+ /**
+ * Add an edge to a graph object
+ * @param {Object} graph
+ * @param {Object} edge
+ */
+ function addEdge(graph, edge) {
+ if (!graph.edges) {
+ graph.edges = [];
+ }
+ graph.edges.push(edge);
+ if (graph.edge) {
+ var attr = merge({}, graph.edge); // clone default attributes
+ edge.attr = merge(attr, edge.attr); // merge attributes
+ }
+ }
+
+ /**
+ * Create an edge to a graph object
+ * @param {Object} graph
+ * @param {String | Number | Object} from
+ * @param {String | Number | Object} to
+ * @param {String} type
+ * @param {Object | null} attr
+ * @return {Object} edge
+ */
+ function createEdge(graph, from, to, type, attr) {
+ var edge = {
+ from: from,
+ to: to,
+ type: type
+ };
- // skip over whitespaces
- while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
- next();
- }
+ if (graph.edge) {
+ edge.attr = merge({}, graph.edge); // clone default attributes
+ }
+ edge.attr = merge(edge.attr || {}, attr); // merge attributes
+
+ return edge;
+ }
+
+ /**
+ * Get next token in the current dot file.
+ * The token and token type are available as token and tokenType
+ */
+ function getToken() {
+ tokenType = TOKENTYPE.NULL;
+ token = '';
+
+ // skip over whitespaces
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
+ next();
+ }
- do {
- var isComment = false;
-
- // skip comment
- if (c == '#') {
- // find the previous non-space character
- var i = index - 1;
- while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') {
- i--;
- }
- if (dot.charAt(i) == '\n' || dot.charAt(i) == '') {
- // the # is at the start of a line, this is indeed a line comment
- while (c != '' && c != '\n') {
- next();
- }
- isComment = true;
- }
- }
- if (c == '/' && nextPreview() == '/') {
- // skip line comment
- while (c != '' && c != '\n') {
- next();
- }
- isComment = true;
- }
- if (c == '/' && nextPreview() == '*') {
- // skip block comment
- while (c != '') {
- if (c == '*' && nextPreview() == '/') {
- // end of block comment found. skip these last two characters
- next();
- next();
- break;
- }
- else {
- next();
- }
- }
- isComment = true;
- }
-
- // skip over whitespaces
- while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
- next();
- }
- }
- while (isComment);
+ do {
+ var isComment = false;
- // check for end of dot file
- if (c == '') {
- // token is still empty
- tokenType = TOKENTYPE.DELIMITER;
- return;
+ // skip comment
+ if (c == '#') {
+ // find the previous non-space character
+ var i = index - 1;
+ while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') {
+ i--;
}
-
- // check for delimiters consisting of 2 characters
- var c2 = c + nextPreview();
- if (DELIMITERS[c2]) {
- tokenType = TOKENTYPE.DELIMITER;
- token = c2;
+ if (dot.charAt(i) == '\n' || dot.charAt(i) == '') {
+ // the # is at the start of a line, this is indeed a line comment
+ while (c != '' && c != '\n') {
next();
+ }
+ isComment = true;
+ }
+ }
+ if (c == '/' && nextPreview() == '/') {
+ // skip line comment
+ while (c != '' && c != '\n') {
+ next();
+ }
+ isComment = true;
+ }
+ if (c == '/' && nextPreview() == '*') {
+ // skip block comment
+ while (c != '') {
+ if (c == '*' && nextPreview() == '/') {
+ // end of block comment found. skip these last two characters
next();
- return;
- }
-
- // check for delimiters consisting of 1 character
- if (DELIMITERS[c]) {
- tokenType = TOKENTYPE.DELIMITER;
- token = c;
next();
- return;
+ break;
+ }
+ else {
+ next();
+ }
}
+ isComment = true;
+ }
- // check for an identifier (number or string)
- // TODO: more precise parsing of numbers/strings (and the port separator ':')
- if (isAlphaNumeric(c) || c == '-') {
- token += c;
- next();
+ // skip over whitespaces
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
+ next();
+ }
+ }
+ while (isComment);
- while (isAlphaNumeric(c)) {
- token += c;
- next();
- }
- if (token == 'false') {
- token = false; // convert to boolean
- }
- else if (token == 'true') {
- token = true; // convert to boolean
- }
- else if (!isNaN(Number(token))) {
- token = Number(token); // convert to number
- }
- tokenType = TOKENTYPE.IDENTIFIER;
- return;
- }
+ // check for end of dot file
+ if (c == '') {
+ // token is still empty
+ tokenType = TOKENTYPE.DELIMITER;
+ return;
+ }
- // check for a string enclosed by double quotes
- if (c == '"') {
- next();
- while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) {
- token += c;
- if (c == '"') { // skip the escape character
- next();
- }
- next();
- }
- if (c != '"') {
- throw newSyntaxError('End of string " expected');
- }
- next();
- tokenType = TOKENTYPE.IDENTIFIER;
- return;
- }
+ // check for delimiters consisting of 2 characters
+ var c2 = c + nextPreview();
+ if (DELIMITERS[c2]) {
+ tokenType = TOKENTYPE.DELIMITER;
+ token = c2;
+ next();
+ next();
+ return;
+ }
- // something unknown is found, wrong characters, a syntax error
- tokenType = TOKENTYPE.UNKNOWN;
- while (c != '') {
- token += c;
- next();
- }
- throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
+ // check for delimiters consisting of 1 character
+ if (DELIMITERS[c]) {
+ tokenType = TOKENTYPE.DELIMITER;
+ token = c;
+ next();
+ return;
}
- /**
- * Parse a graph.
- * @returns {Object} graph
- */
- function parseGraph() {
- var graph = {};
+ // check for an identifier (number or string)
+ // TODO: more precise parsing of numbers/strings (and the port separator ':')
+ if (isAlphaNumeric(c) || c == '-') {
+ token += c;
+ next();
+
+ while (isAlphaNumeric(c)) {
+ token += c;
+ next();
+ }
+ if (token == 'false') {
+ token = false; // convert to boolean
+ }
+ else if (token == 'true') {
+ token = true; // convert to boolean
+ }
+ else if (!isNaN(Number(token))) {
+ token = Number(token); // convert to number
+ }
+ tokenType = TOKENTYPE.IDENTIFIER;
+ return;
+ }
- first();
- getToken();
+ // check for a string enclosed by double quotes
+ if (c == '"') {
+ next();
+ while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) {
+ token += c;
+ if (c == '"') { // skip the escape character
+ next();
+ }
+ next();
+ }
+ if (c != '"') {
+ throw newSyntaxError('End of string " expected');
+ }
+ next();
+ tokenType = TOKENTYPE.IDENTIFIER;
+ return;
+ }
- // optional strict keyword
- if (token == 'strict') {
- graph.strict = true;
- getToken();
- }
+ // something unknown is found, wrong characters, a syntax error
+ tokenType = TOKENTYPE.UNKNOWN;
+ while (c != '') {
+ token += c;
+ next();
+ }
+ throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
+ }
+
+ /**
+ * Parse a graph.
+ * @returns {Object} graph
+ */
+ function parseGraph() {
+ var graph = {};
+
+ first();
+ getToken();
+
+ // optional strict keyword
+ if (token == 'strict') {
+ graph.strict = true;
+ getToken();
+ }
- // graph or digraph keyword
- if (token == 'graph' || token == 'digraph') {
- graph.type = token;
- getToken();
- }
+ // graph or digraph keyword
+ if (token == 'graph' || token == 'digraph') {
+ graph.type = token;
+ getToken();
+ }
- // optional graph id
- if (tokenType == TOKENTYPE.IDENTIFIER) {
- graph.id = token;
- getToken();
- }
+ // optional graph id
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
+ graph.id = token;
+ getToken();
+ }
- // open angle bracket
- if (token != '{') {
- throw newSyntaxError('Angle bracket { expected');
- }
- getToken();
+ // open angle bracket
+ if (token != '{') {
+ throw newSyntaxError('Angle bracket { expected');
+ }
+ getToken();
- // statements
- parseStatements(graph);
+ // statements
+ parseStatements(graph);
- // close angle bracket
- if (token != '}') {
- throw newSyntaxError('Angle bracket } expected');
- }
- getToken();
+ // close angle bracket
+ if (token != '}') {
+ throw newSyntaxError('Angle bracket } expected');
+ }
+ getToken();
- // end of file
- if (token !== '') {
- throw newSyntaxError('End of file expected');
- }
+ // end of file
+ if (token !== '') {
+ throw newSyntaxError('End of file expected');
+ }
+ getToken();
+
+ // remove temporary default properties
+ delete graph.node;
+ delete graph.edge;
+ delete graph.graph;
+
+ return graph;
+ }
+
+ /**
+ * Parse a list with statements.
+ * @param {Object} graph
+ */
+ function parseStatements (graph) {
+ while (token !== '' && token != '}') {
+ parseStatement(graph);
+ if (token == ';') {
getToken();
+ }
+ }
+ }
+
+ /**
+ * Parse a single statement. Can be a an attribute statement, node
+ * statement, a series of node statements and edge statements, or a
+ * parameter.
+ * @param {Object} graph
+ */
+ function parseStatement(graph) {
+ // parse subgraph
+ var subgraph = parseSubgraph(graph);
+ if (subgraph) {
+ // edge statements
+ parseEdge(graph, subgraph);
+
+ return;
+ }
- // remove temporary default properties
- delete graph.node;
- delete graph.edge;
- delete graph.graph;
+ // parse an attribute statement
+ var attr = parseAttributeStatement(graph);
+ if (attr) {
+ return;
+ }
- return graph;
+ // parse node
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Identifier expected');
+ }
+ var id = token; // id can be a string or a number
+ getToken();
+
+ if (token == '=') {
+ // id statement
+ getToken();
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Identifier expected');
+ }
+ graph[id] = token;
+ getToken();
+ // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
+ }
+ else {
+ parseNodeStatement(graph, id);
+ }
+ }
+
+ /**
+ * Parse a subgraph
+ * @param {Object} graph parent graph object
+ * @return {Object | null} subgraph
+ */
+ function parseSubgraph (graph) {
+ var subgraph = null;
+
+ // optional subgraph keyword
+ if (token == 'subgraph') {
+ subgraph = {};
+ subgraph.type = 'subgraph';
+ getToken();
+
+ // optional graph id
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
+ subgraph.id = token;
+ getToken();
+ }
}
- /**
- * Parse a list with statements.
- * @param {Object} graph
- */
- function parseStatements (graph) {
- while (token !== '' && token != '}') {
- parseStatement(graph);
- if (token == ';') {
- getToken();
- }
- }
+ // open angle bracket
+ if (token == '{') {
+ getToken();
+
+ if (!subgraph) {
+ subgraph = {};
+ }
+ subgraph.parent = graph;
+ subgraph.node = graph.node;
+ subgraph.edge = graph.edge;
+ subgraph.graph = graph.graph;
+
+ // statements
+ parseStatements(subgraph);
+
+ // close angle bracket
+ if (token != '}') {
+ throw newSyntaxError('Angle bracket } expected');
+ }
+ getToken();
+
+ // remove temporary default properties
+ delete subgraph.node;
+ delete subgraph.edge;
+ delete subgraph.graph;
+ delete subgraph.parent;
+
+ // register at the parent graph
+ if (!graph.subgraphs) {
+ graph.subgraphs = [];
+ }
+ graph.subgraphs.push(subgraph);
}
- /**
- * Parse a single statement. Can be a an attribute statement, node
- * statement, a series of node statements and edge statements, or a
- * parameter.
- * @param {Object} graph
- */
- function parseStatement(graph) {
- // parse subgraph
- var subgraph = parseSubgraph(graph);
- if (subgraph) {
- // edge statements
- parseEdge(graph, subgraph);
-
- return;
- }
+ return subgraph;
+ }
+
+ /**
+ * parse an attribute statement like "node [shape=circle fontSize=16]".
+ * Available keywords are 'node', 'edge', 'graph'.
+ * The previous list with default attributes will be replaced
+ * @param {Object} graph
+ * @returns {String | null} keyword Returns the name of the parsed attribute
+ * (node, edge, graph), or null if nothing
+ * is parsed.
+ */
+ function parseAttributeStatement (graph) {
+ // attribute statements
+ if (token == 'node') {
+ getToken();
+
+ // node attributes
+ graph.node = parseAttributeList();
+ return 'node';
+ }
+ else if (token == 'edge') {
+ getToken();
- // parse an attribute statement
- var attr = parseAttributeStatement(graph);
- if (attr) {
- return;
- }
+ // edge attributes
+ graph.edge = parseAttributeList();
+ return 'edge';
+ }
+ else if (token == 'graph') {
+ getToken();
+
+ // graph attributes
+ graph.graph = parseAttributeList();
+ return 'graph';
+ }
- // parse node
+ return null;
+ }
+
+ /**
+ * parse a node statement
+ * @param {Object} graph
+ * @param {String | Number} id
+ */
+ function parseNodeStatement(graph, id) {
+ // node statement
+ var node = {
+ id: id
+ };
+ var attr = parseAttributeList();
+ if (attr) {
+ node.attr = attr;
+ }
+ addNode(graph, node);
+
+ // edge statements
+ parseEdge(graph, id);
+ }
+
+ /**
+ * Parse an edge or a series of edges
+ * @param {Object} graph
+ * @param {String | Number} from Id of the from node
+ */
+ function parseEdge(graph, from) {
+ while (token == '->' || token == '--') {
+ var to;
+ var type = token;
+ getToken();
+
+ var subgraph = parseSubgraph(graph);
+ if (subgraph) {
+ to = subgraph;
+ }
+ else {
if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier expected');
+ throw newSyntaxError('Identifier or subgraph expected');
}
- var id = token; // id can be a string or a number
+ to = token;
+ addNode(graph, {
+ id: to
+ });
getToken();
+ }
- if (token == '=') {
- // id statement
- getToken();
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier expected');
- }
- graph[id] = token;
- getToken();
- // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
- }
- else {
- parseNodeStatement(graph, id);
- }
- }
+ // parse edge attributes
+ var attr = parseAttributeList();
- /**
- * Parse a subgraph
- * @param {Object} graph parent graph object
- * @return {Object | null} subgraph
- */
- function parseSubgraph (graph) {
- var subgraph = null;
-
- // optional subgraph keyword
- if (token == 'subgraph') {
- subgraph = {};
- subgraph.type = 'subgraph';
- getToken();
-
- // optional graph id
- if (tokenType == TOKENTYPE.IDENTIFIER) {
- subgraph.id = token;
- getToken();
- }
- }
+ // create edge
+ var edge = createEdge(graph, from, to, type, attr);
+ addEdge(graph, edge);
- // open angle bracket
- if (token == '{') {
- getToken();
-
- if (!subgraph) {
- subgraph = {};
- }
- subgraph.parent = graph;
- subgraph.node = graph.node;
- subgraph.edge = graph.edge;
- subgraph.graph = graph.graph;
-
- // statements
- parseStatements(subgraph);
-
- // close angle bracket
- if (token != '}') {
- throw newSyntaxError('Angle bracket } expected');
- }
- getToken();
-
- // remove temporary default properties
- delete subgraph.node;
- delete subgraph.edge;
- delete subgraph.graph;
- delete subgraph.parent;
-
- // register at the parent graph
- if (!graph.subgraphs) {
- graph.subgraphs = [];
- }
- graph.subgraphs.push(subgraph);
+ from = to;
+ }
+ }
+
+ /**
+ * Parse a set with attributes,
+ * for example [label="1.000", shape=solid]
+ * @return {Object | null} attr
+ */
+ function parseAttributeList() {
+ var attr = null;
+
+ while (token == '[') {
+ getToken();
+ attr = {};
+ while (token !== '' && token != ']') {
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Attribute name expected');
}
+ var name = token;
- return subgraph;
- }
-
- /**
- * parse an attribute statement like "node [shape=circle fontSize=16]".
- * Available keywords are 'node', 'edge', 'graph'.
- * The previous list with default attributes will be replaced
- * @param {Object} graph
- * @returns {String | null} keyword Returns the name of the parsed attribute
- * (node, edge, graph), or null if nothing
- * is parsed.
- */
- function parseAttributeStatement (graph) {
- // attribute statements
- if (token == 'node') {
- getToken();
-
- // node attributes
- graph.node = parseAttributeList();
- return 'node';
+ getToken();
+ if (token != '=') {
+ throw newSyntaxError('Equal sign = expected');
}
- else if (token == 'edge') {
- getToken();
+ getToken();
- // edge attributes
- graph.edge = parseAttributeList();
- return 'edge';
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Attribute value expected');
}
- else if (token == 'graph') {
- getToken();
+ var value = token;
+ setValue(attr, name, value); // name can be a path
- // graph attributes
- graph.graph = parseAttributeList();
- return 'graph';
+ getToken();
+ if (token ==',') {
+ getToken();
}
+ }
- return null;
+ if (token != ']') {
+ throw newSyntaxError('Bracket ] expected');
+ }
+ getToken();
}
- /**
- * parse a node statement
- * @param {Object} graph
- * @param {String | Number} id
- */
- function parseNodeStatement(graph, id) {
- // node statement
- var node = {
- id: id
- };
- var attr = parseAttributeList();
- if (attr) {
- node.attr = attr;
+ return attr;
+ }
+
+ /**
+ * Create a syntax error with extra information on current token and index.
+ * @param {String} message
+ * @returns {SyntaxError} err
+ */
+ function newSyntaxError(message) {
+ return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
+ }
+
+ /**
+ * Chop off text after a maximum length
+ * @param {String} text
+ * @param {Number} maxLength
+ * @returns {String}
+ */
+ function chop (text, maxLength) {
+ return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...');
+ }
+
+ /**
+ * Execute a function fn for each pair of elements in two arrays
+ * @param {Array | *} array1
+ * @param {Array | *} array2
+ * @param {function} fn
+ */
+ function forEach2(array1, array2, fn) {
+ if (array1 instanceof Array) {
+ array1.forEach(function (elem1) {
+ if (array2 instanceof Array) {
+ array2.forEach(function (elem2) {
+ fn(elem1, elem2);
+ });
}
- addNode(graph, node);
-
- // edge statements
- parseEdge(graph, id);
- }
-
- /**
- * Parse an edge or a series of edges
- * @param {Object} graph
- * @param {String | Number} from Id of the from node
- */
- function parseEdge(graph, from) {
- while (token == '->' || token == '--') {
- var to;
- var type = token;
- getToken();
-
- var subgraph = parseSubgraph(graph);
- if (subgraph) {
- to = subgraph;
- }
- else {
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier or subgraph expected');
- }
- to = token;
- addNode(graph, {
- id: to
- });
- getToken();
- }
-
- // parse edge attributes
- var attr = parseAttributeList();
-
- // create edge
- var edge = createEdge(graph, from, to, type, attr);
- addEdge(graph, edge);
-
- from = to;
+ else {
+ fn(elem1, array2);
}
+ });
+ }
+ else {
+ if (array2 instanceof Array) {
+ array2.forEach(function (elem2) {
+ fn(array1, elem2);
+ });
+ }
+ else {
+ fn(array1, array2);
+ }
}
+ }
+
+ /**
+ * Convert a string containing a graph in DOT language into a map containing
+ * with nodes and edges in the format of graph.
+ * @param {String} data Text containing a graph in DOT-notation
+ * @return {Object} graphData
+ */
+ function DOTToGraph (data) {
+ // parse the DOT file
+ var dotData = parseDOT(data);
+ var graphData = {
+ nodes: [],
+ edges: [],
+ options: {}
+ };
- /**
- * Parse a set with attributes,
- * for example [label="1.000", shape=solid]
- * @return {Object | null} attr
- */
- function parseAttributeList() {
- var attr = null;
-
- while (token == '[') {
- getToken();
- attr = {};
- while (token !== '' && token != ']') {
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Attribute name expected');
- }
- var name = token;
-
- getToken();
- if (token != '=') {
- throw newSyntaxError('Equal sign = expected');
- }
- getToken();
-
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Attribute value expected');
- }
- var value = token;
- setValue(attr, name, value); // name can be a path
-
- getToken();
- if (token ==',') {
- getToken();
- }
- }
-
- if (token != ']') {
- throw newSyntaxError('Bracket ] expected');
- }
- getToken();
+ // copy the nodes
+ if (dotData.nodes) {
+ dotData.nodes.forEach(function (dotNode) {
+ var graphNode = {
+ id: dotNode.id,
+ label: String(dotNode.label || dotNode.id)
+ };
+ merge(graphNode, dotNode.attr);
+ if (graphNode.image) {
+ graphNode.shape = 'image';
}
+ graphData.nodes.push(graphNode);
+ });
+ }
- return attr;
- }
-
- /**
- * Create a syntax error with extra information on current token and index.
- * @param {String} message
- * @returns {SyntaxError} err
- */
- function newSyntaxError(message) {
- return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
- }
-
- /**
- * Chop off text after a maximum length
- * @param {String} text
- * @param {Number} maxLength
- * @returns {String}
- */
- function chop (text, maxLength) {
- return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...');
- }
-
- /**
- * Execute a function fn for each pair of elements in two arrays
- * @param {Array | *} array1
- * @param {Array | *} array2
- * @param {function} fn
- */
- function forEach2(array1, array2, fn) {
- if (array1 instanceof Array) {
- array1.forEach(function (elem1) {
- if (array2 instanceof Array) {
- array2.forEach(function (elem2) {
- fn(elem1, elem2);
- });
- }
- else {
- fn(elem1, array2);
- }
- });
- }
- else {
- if (array2 instanceof Array) {
- array2.forEach(function (elem2) {
- fn(array1, elem2);
- });
- }
- else {
- fn(array1, array2);
- }
- }
- }
-
- /**
- * Convert a string containing a graph in DOT language into a map containing
- * with nodes and edges in the format of graph.
- * @param {String} data Text containing a graph in DOT-notation
- * @return {Object} graphData
- */
- function DOTToGraph (data) {
- // parse the DOT file
- var dotData = parseDOT(data);
- var graphData = {
- nodes: [],
- edges: [],
- options: {}
+ // copy the edges
+ if (dotData.edges) {
+ /**
+ * Convert an edge in DOT format to an edge with VisGraph format
+ * @param {Object} dotEdge
+ * @returns {Object} graphEdge
+ */
+ function convertEdge(dotEdge) {
+ var graphEdge = {
+ from: dotEdge.from,
+ to: dotEdge.to
};
+ merge(graphEdge, dotEdge.attr);
+ graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line';
+ return graphEdge;
+ }
- // copy the nodes
- if (dotData.nodes) {
- dotData.nodes.forEach(function (dotNode) {
- var graphNode = {
- id: dotNode.id,
- label: String(dotNode.label || dotNode.id)
- };
- merge(graphNode, dotNode.attr);
- if (graphNode.image) {
- graphNode.shape = 'image';
- }
- graphData.nodes.push(graphNode);
- });
+ dotData.edges.forEach(function (dotEdge) {
+ var from, to;
+ if (dotEdge.from instanceof Object) {
+ from = dotEdge.from.nodes;
+ }
+ else {
+ from = {
+ id: dotEdge.from
+ }
}
- // copy the edges
- if (dotData.edges) {
- /**
- * Convert an edge in DOT format to an edge with VisGraph format
- * @param {Object} dotEdge
- * @returns {Object} graphEdge
- */
- function convertEdge(dotEdge) {
- var graphEdge = {
- from: dotEdge.from,
- to: dotEdge.to
- };
- merge(graphEdge, dotEdge.attr);
- graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line';
- return graphEdge;
- }
-
- dotData.edges.forEach(function (dotEdge) {
- var from, to;
- if (dotEdge.from instanceof Object) {
- from = dotEdge.from.nodes;
- }
- else {
- from = {
- id: dotEdge.from
- }
- }
-
- if (dotEdge.to instanceof Object) {
- to = dotEdge.to.nodes;
- }
- else {
- to = {
- id: dotEdge.to
- }
- }
-
- if (dotEdge.from instanceof Object && dotEdge.from.edges) {
- dotEdge.from.edges.forEach(function (subEdge) {
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
- }
-
- forEach2(from, to, function (from, to) {
- var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
-
- if (dotEdge.to instanceof Object && dotEdge.to.edges) {
- dotEdge.to.edges.forEach(function (subEdge) {
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
- }
- });
+ if (dotEdge.to instanceof Object) {
+ to = dotEdge.to.nodes;
+ }
+ else {
+ to = {
+ id: dotEdge.to
+ }
+ }
+
+ if (dotEdge.from instanceof Object && dotEdge.from.edges) {
+ dotEdge.from.edges.forEach(function (subEdge) {
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
}
- // copy the options
- if (dotData.attr) {
- graphData.options = dotData.attr;
+ forEach2(from, to, function (from, to) {
+ var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
+
+ if (dotEdge.to instanceof Object && dotEdge.to.edges) {
+ dotEdge.to.edges.forEach(function (subEdge) {
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
}
+ });
+ }
- return graphData;
+ // copy the options
+ if (dotData.attr) {
+ graphData.options = dotData.attr;
}
- // exports
- exports.parseDOT = parseDOT;
- exports.DOTToGraph = DOTToGraph;
+ return graphData;
+ }
+
+ // exports
+ exports.parseDOT = parseDOT;
+ exports.DOTToGraph = DOTToGraph;
})(typeof util !== 'undefined' ? util : exports);
diff --git a/src/graph/shapes.js b/src/graph/shapes.js
index 93919306..ed80372b 100644
--- a/src/graph/shapes.js
+++ b/src/graph/shapes.js
@@ -3,223 +3,223 @@
*/
if (typeof CanvasRenderingContext2D !== 'undefined') {
- /**
- * Draw a circle shape
- */
- CanvasRenderingContext2D.prototype.circle = function(x, y, r) {
- this.beginPath();
- this.arc(x, y, r, 0, 2*Math.PI, false);
- };
-
- /**
- * Draw a square shape
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r size, width and height of the square
- */
- CanvasRenderingContext2D.prototype.square = function(x, y, r) {
- this.beginPath();
- this.rect(x - r, y - r, r * 2, r * 2);
- };
-
- /**
- * Draw a triangle shape
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius, half the length of the sides of the triangle
- */
- CanvasRenderingContext2D.prototype.triangle = function(x, y, r) {
- // http://en.wikipedia.org/wiki/Equilateral_triangle
- this.beginPath();
-
- var s = r * 2;
- var s2 = s / 2;
- var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
- var h = Math.sqrt(s * s - s2 * s2); // height
-
- this.moveTo(x, y - (h - ir));
- this.lineTo(x + s2, y + ir);
- this.lineTo(x - s2, y + ir);
- this.lineTo(x, y - (h - ir));
- this.closePath();
- };
-
- /**
- * Draw a triangle shape in downward orientation
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius
- */
- CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) {
- // http://en.wikipedia.org/wiki/Equilateral_triangle
- this.beginPath();
-
- var s = r * 2;
- var s2 = s / 2;
- var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
- var h = Math.sqrt(s * s - s2 * s2); // height
-
- this.moveTo(x, y + (h - ir));
- this.lineTo(x + s2, y - ir);
- this.lineTo(x - s2, y - ir);
- this.lineTo(x, y + (h - ir));
- this.closePath();
- };
-
- /**
- * Draw a star shape, a star with 5 points
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius, half the length of the sides of the triangle
- */
- CanvasRenderingContext2D.prototype.star = function(x, y, r) {
- // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
- this.beginPath();
-
- for (var n = 0; n < 10; n++) {
- var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5;
- this.lineTo(
- x + radius * Math.sin(n * 2 * Math.PI / 10),
- y - radius * Math.cos(n * 2 * Math.PI / 10)
- );
- }
-
- this.closePath();
- };
-
- /**
- * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
- */
- CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
- var r2d = Math.PI/180;
- if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x
- if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y
- this.beginPath();
- this.moveTo(x+r,y);
- this.lineTo(x+w-r,y);
- this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false);
- this.lineTo(x+w,y+h-r);
- this.arc(x+w-r,y+h-r,r,0,r2d*90,false);
- this.lineTo(x+r,y+h);
- this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false);
- this.lineTo(x,y+r);
- this.arc(x+r,y+r,r,r2d*180,r2d*270,false);
- };
-
- /**
- * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
- */
- CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) {
- var kappa = .5522848,
- ox = (w / 2) * kappa, // control point offset horizontal
- oy = (h / 2) * kappa, // control point offset vertical
- xe = x + w, // x-end
- ye = y + h, // y-end
- xm = x + w / 2, // x-middle
- ym = y + h / 2; // y-middle
-
- this.beginPath();
- this.moveTo(x, ym);
- this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
- this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
- };
-
-
-
- /**
- * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
- */
- CanvasRenderingContext2D.prototype.database = function(x, y, w, h) {
- var f = 1/3;
- var wEllipse = w;
- var hEllipse = h * f;
-
- var kappa = .5522848,
- ox = (wEllipse / 2) * kappa, // control point offset horizontal
- oy = (hEllipse / 2) * kappa, // control point offset vertical
- xe = x + wEllipse, // x-end
- ye = y + hEllipse, // y-end
- xm = x + wEllipse / 2, // x-middle
- ym = y + hEllipse / 2, // y-middle
- ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse
- yeb = y + h; // y-end, bottom ellipse
-
- this.beginPath();
- this.moveTo(xe, ym);
-
- this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
-
- this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
-
- this.lineTo(xe, ymb);
-
- this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
- this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
-
- this.lineTo(x, ym);
- };
-
-
- /**
- * Draw an arrow point (no line)
- */
- CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) {
- // tail
- var xt = x - length * Math.cos(angle);
- var yt = y - length * Math.sin(angle);
-
- // inner tail
- // TODO: allow to customize different shapes
- var xi = x - length * 0.9 * Math.cos(angle);
- var yi = y - length * 0.9 * Math.sin(angle);
-
- // left
- var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
- var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);
-
- // right
- var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
- var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);
-
- this.beginPath();
- this.moveTo(x, y);
- this.lineTo(xl, yl);
- this.lineTo(xi, yi);
- this.lineTo(xr, yr);
- this.closePath();
- };
-
- /**
- * Sets up the dashedLine functionality for drawing
- * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
- * @author David Jordan
- * @date 2012-08-08
- */
- CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){
- if (!dashArray) dashArray=[10,5];
- if (dashLength==0) dashLength = 0.001; // Hack for Safari
- var dashCount = dashArray.length;
- this.moveTo(x, y);
- var dx = (x2-x), dy = (y2-y);
- var slope = dy/dx;
- var distRemaining = Math.sqrt( dx*dx + dy*dy );
- var dashIndex=0, draw=true;
- while (distRemaining>=0.1){
- var dashLength = dashArray[dashIndex++%dashCount];
- if (dashLength > distRemaining) dashLength = distRemaining;
- var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) );
- if (dx<0) xStep = -xStep;
- x += xStep;
- y += slope*xStep;
- this[draw ? 'lineTo' : 'moveTo'](x,y);
- distRemaining -= dashLength;
- draw = !draw;
- }
- };
-
- // TODO: add diamond shape
+ /**
+ * Draw a circle shape
+ */
+ CanvasRenderingContext2D.prototype.circle = function(x, y, r) {
+ this.beginPath();
+ this.arc(x, y, r, 0, 2*Math.PI, false);
+ };
+
+ /**
+ * Draw a square shape
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r size, width and height of the square
+ */
+ CanvasRenderingContext2D.prototype.square = function(x, y, r) {
+ this.beginPath();
+ this.rect(x - r, y - r, r * 2, r * 2);
+ };
+
+ /**
+ * Draw a triangle shape
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius, half the length of the sides of the triangle
+ */
+ CanvasRenderingContext2D.prototype.triangle = function(x, y, r) {
+ // http://en.wikipedia.org/wiki/Equilateral_triangle
+ this.beginPath();
+
+ var s = r * 2;
+ var s2 = s / 2;
+ var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
+ var h = Math.sqrt(s * s - s2 * s2); // height
+
+ this.moveTo(x, y - (h - ir));
+ this.lineTo(x + s2, y + ir);
+ this.lineTo(x - s2, y + ir);
+ this.lineTo(x, y - (h - ir));
+ this.closePath();
+ };
+
+ /**
+ * Draw a triangle shape in downward orientation
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius
+ */
+ CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) {
+ // http://en.wikipedia.org/wiki/Equilateral_triangle
+ this.beginPath();
+
+ var s = r * 2;
+ var s2 = s / 2;
+ var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
+ var h = Math.sqrt(s * s - s2 * s2); // height
+
+ this.moveTo(x, y + (h - ir));
+ this.lineTo(x + s2, y - ir);
+ this.lineTo(x - s2, y - ir);
+ this.lineTo(x, y + (h - ir));
+ this.closePath();
+ };
+
+ /**
+ * Draw a star shape, a star with 5 points
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius, half the length of the sides of the triangle
+ */
+ CanvasRenderingContext2D.prototype.star = function(x, y, r) {
+ // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
+ this.beginPath();
+
+ for (var n = 0; n < 10; n++) {
+ var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5;
+ this.lineTo(
+ x + radius * Math.sin(n * 2 * Math.PI / 10),
+ y - radius * Math.cos(n * 2 * Math.PI / 10)
+ );
+ }
+
+ this.closePath();
+ };
+
+ /**
+ * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
+ */
+ CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
+ var r2d = Math.PI/180;
+ if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x
+ if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y
+ this.beginPath();
+ this.moveTo(x+r,y);
+ this.lineTo(x+w-r,y);
+ this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false);
+ this.lineTo(x+w,y+h-r);
+ this.arc(x+w-r,y+h-r,r,0,r2d*90,false);
+ this.lineTo(x+r,y+h);
+ this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false);
+ this.lineTo(x,y+r);
+ this.arc(x+r,y+r,r,r2d*180,r2d*270,false);
+ };
+
+ /**
+ * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ */
+ CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) {
+ var kappa = .5522848,
+ ox = (w / 2) * kappa, // control point offset horizontal
+ oy = (h / 2) * kappa, // control point offset vertical
+ xe = x + w, // x-end
+ ye = y + h, // y-end
+ xm = x + w / 2, // x-middle
+ ym = y + h / 2; // y-middle
+
+ this.beginPath();
+ this.moveTo(x, ym);
+ this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+ this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+ };
+
+
+
+ /**
+ * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ */
+ CanvasRenderingContext2D.prototype.database = function(x, y, w, h) {
+ var f = 1/3;
+ var wEllipse = w;
+ var hEllipse = h * f;
+
+ var kappa = .5522848,
+ ox = (wEllipse / 2) * kappa, // control point offset horizontal
+ oy = (hEllipse / 2) * kappa, // control point offset vertical
+ xe = x + wEllipse, // x-end
+ ye = y + hEllipse, // y-end
+ xm = x + wEllipse / 2, // x-middle
+ ym = y + hEllipse / 2, // y-middle
+ ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse
+ yeb = y + h; // y-end, bottom ellipse
+
+ this.beginPath();
+ this.moveTo(xe, ym);
+
+ this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+
+ this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+
+ this.lineTo(xe, ymb);
+
+ this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
+ this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
+
+ this.lineTo(x, ym);
+ };
+
+
+ /**
+ * Draw an arrow point (no line)
+ */
+ CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) {
+ // tail
+ var xt = x - length * Math.cos(angle);
+ var yt = y - length * Math.sin(angle);
+
+ // inner tail
+ // TODO: allow to customize different shapes
+ var xi = x - length * 0.9 * Math.cos(angle);
+ var yi = y - length * 0.9 * Math.sin(angle);
+
+ // left
+ var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
+ var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);
+
+ // right
+ var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
+ var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);
+
+ this.beginPath();
+ this.moveTo(x, y);
+ this.lineTo(xl, yl);
+ this.lineTo(xi, yi);
+ this.lineTo(xr, yr);
+ this.closePath();
+ };
+
+ /**
+ * Sets up the dashedLine functionality for drawing
+ * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
+ * @author David Jordan
+ * @date 2012-08-08
+ */
+ CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){
+ if (!dashArray) dashArray=[10,5];
+ if (dashLength==0) dashLength = 0.001; // Hack for Safari
+ var dashCount = dashArray.length;
+ this.moveTo(x, y);
+ var dx = (x2-x), dy = (y2-y);
+ var slope = dy/dx;
+ var distRemaining = Math.sqrt( dx*dx + dy*dy );
+ var dashIndex=0, draw=true;
+ while (distRemaining>=0.1){
+ var dashLength = dashArray[dashIndex++%dashCount];
+ if (dashLength > distRemaining) dashLength = distRemaining;
+ var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) );
+ if (dx<0) xStep = -xStep;
+ x += xStep;
+ y += slope*xStep;
+ this[draw ? 'lineTo' : 'moveTo'](x,y);
+ distRemaining -= dashLength;
+ draw = !draw;
+ }
+ };
+
+ // TODO: add diamond shape
}
diff --git a/src/module/exports.js b/src/module/exports.js
index da62569f..e4c70cbf 100644
--- a/src/module/exports.js
+++ b/src/module/exports.js
@@ -2,67 +2,67 @@
* vis.js module exports
*/
var vis = {
- util: util,
- events: events,
+ util: util,
+ events: events,
- Controller: Controller,
- DataSet: DataSet,
- DataView: DataView,
- Range: Range,
- Stack: Stack,
- TimeStep: TimeStep,
- EventBus: EventBus,
+ Controller: Controller,
+ DataSet: DataSet,
+ DataView: DataView,
+ Range: Range,
+ Stack: Stack,
+ TimeStep: TimeStep,
+ EventBus: EventBus,
- components: {
- items: {
- Item: Item,
- ItemBox: ItemBox,
- ItemPoint: ItemPoint,
- ItemRange: ItemRange
- },
-
- Component: Component,
- Panel: Panel,
- RootPanel: RootPanel,
- ItemSet: ItemSet,
- TimeAxis: TimeAxis
+ components: {
+ items: {
+ Item: Item,
+ ItemBox: ItemBox,
+ ItemPoint: ItemPoint,
+ ItemRange: ItemRange
},
- graph: {
- Node: Node,
- Edge: Edge,
- Popup: Popup,
- Groups: Groups,
- Images: Images
- },
+ Component: Component,
+ Panel: Panel,
+ RootPanel: RootPanel,
+ ItemSet: ItemSet,
+ TimeAxis: TimeAxis
+ },
+
+ graph: {
+ Node: Node,
+ Edge: Edge,
+ Popup: Popup,
+ Groups: Groups,
+ Images: Images
+ },
- Timeline: Timeline,
- Graph: Graph
+ Timeline: Timeline,
+ Graph: Graph
};
/**
* CommonJS module exports
*/
if (typeof exports !== 'undefined') {
- exports = vis;
+ exports = vis;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = vis;
+ module.exports = vis;
}
/**
* AMD module exports
*/
if (typeof(define) === 'function') {
- define(function () {
- return vis;
- });
+ define(function () {
+ return vis;
+ });
}
/**
* Window exports
*/
if (typeof window !== 'undefined') {
- // attach the module to the window, load as a regular javascript file
- window['vis'] = vis;
+ // attach the module to the window, load as a regular javascript file
+ window['vis'] = vis;
}
diff --git a/src/shim.js b/src/shim.js
index 3bb50d55..b7b71691 100644
--- a/src/shim.js
+++ b/src/shim.js
@@ -3,31 +3,31 @@
// it here in that case.
// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
if(!Array.prototype.indexOf) {
- Array.prototype.indexOf = function(obj){
- for(var i = 0; i < this.length; i++){
- if(this[i] == obj){
- return i;
- }
- }
- return -1;
- };
-
- try {
- console.log("Warning: Ancient browser detected. Please update your browser");
- }
- catch (err) {
+ Array.prototype.indexOf = function(obj){
+ for(var i = 0; i < this.length; i++){
+ if(this[i] == obj){
+ return i;
+ }
}
+ return -1;
+ };
+
+ try {
+ console.log("Warning: Ancient browser detected. Please update your browser");
+ }
+ catch (err) {
+ }
}
// Internet Explorer 8 and older does not support Array.forEach, so we define
// it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach
if (!Array.prototype.forEach) {
- Array.prototype.forEach = function(fn, scope) {
- for(var i = 0, len = this.length; i < len; ++i) {
- fn.call(scope || this, this[i], i, this);
- }
+ Array.prototype.forEach = function(fn, scope) {
+ for(var i = 0, len = this.length; i < len; ++i) {
+ fn.call(scope || this, this[i], i, this);
}
+ }
}
// Internet Explorer 8 and older does not support Array.map, so we define it
@@ -36,106 +36,106 @@ if (!Array.prototype.forEach) {
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
- Array.prototype.map = function(callback, thisArg) {
+ Array.prototype.map = function(callback, thisArg) {
- var T, A, k;
+ var T, A, k;
- if (this == null) {
- throw new TypeError(" this is null or not defined");
- }
+ if (this == null) {
+ throw new TypeError(" this is null or not defined");
+ }
- // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
- var O = Object(this);
+ // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+ var O = Object(this);
- // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
- // 3. Let len be ToUint32(lenValue).
- var len = O.length >>> 0;
+ // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
+ // 3. Let len be ToUint32(lenValue).
+ var len = O.length >>> 0;
- // 4. If IsCallable(callback) is false, throw a TypeError exception.
- // See: http://es5.github.com/#x9.11
- if (typeof callback !== "function") {
- throw new TypeError(callback + " is not a function");
- }
+ // 4. If IsCallable(callback) is false, throw a TypeError exception.
+ // See: http://es5.github.com/#x9.11
+ if (typeof callback !== "function") {
+ throw new TypeError(callback + " is not a function");
+ }
- // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
- if (thisArg) {
- T = thisArg;
- }
+ // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ if (thisArg) {
+ T = thisArg;
+ }
- // 6. Let A be a new array created as if by the expression new Array(len) where Array is
- // the standard built-in constructor with that name and len is the value of len.
- A = new Array(len);
+ // 6. Let A be a new array created as if by the expression new Array(len) where Array is
+ // the standard built-in constructor with that name and len is the value of len.
+ A = new Array(len);
- // 7. Let k be 0
- k = 0;
+ // 7. Let k be 0
+ k = 0;
- // 8. Repeat, while k < len
- while(k < len) {
+ // 8. Repeat, while k < len
+ while(k < len) {
- var kValue, mappedValue;
+ var kValue, mappedValue;
- // a. Let Pk be ToString(k).
- // This is implicit for LHS operands of the in operator
- // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
- // This step can be combined with c
- // c. If kPresent is true, then
- if (k in O) {
+ // a. Let Pk be ToString(k).
+ // This is implicit for LHS operands of the in operator
+ // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
+ // This step can be combined with c
+ // c. If kPresent is true, then
+ if (k in O) {
- // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
- kValue = O[ k ];
+ // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+ kValue = O[ k ];
- // ii. Let mappedValue be the result of calling the Call internal method of callback
- // with T as the this value and argument list containing kValue, k, and O.
- mappedValue = callback.call(T, kValue, k, O);
+ // ii. Let mappedValue be the result of calling the Call internal method of callback
+ // with T as the this value and argument list containing kValue, k, and O.
+ mappedValue = callback.call(T, kValue, k, O);
- // iii. Call the DefineOwnProperty internal method of A with arguments
- // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
- // and false.
+ // iii. Call the DefineOwnProperty internal method of A with arguments
+ // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
+ // and false.
- // In browsers that support Object.defineProperty, use the following:
- // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
+ // In browsers that support Object.defineProperty, use the following:
+ // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
- // For best browser support, use the following:
- A[ k ] = mappedValue;
- }
- // d. Increase k by 1.
- k++;
- }
+ // For best browser support, use the following:
+ A[ k ] = mappedValue;
+ }
+ // d. Increase k by 1.
+ k++;
+ }
- // 9. return A
- return A;
- };
+ // 9. return A
+ return A;
+ };
}
// Internet Explorer 8 and older does not support Array.filter, so we define it
// here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
- Array.prototype.filter = function(fun /*, thisp */) {
- "use strict";
+ Array.prototype.filter = function(fun /*, thisp */) {
+ "use strict";
- if (this == null) {
- throw new TypeError();
- }
+ if (this == null) {
+ throw new TypeError();
+ }
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun != "function") {
- throw new TypeError();
- }
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (typeof fun != "function") {
+ throw new TypeError();
+ }
- var res = [];
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- var val = t[i]; // in case fun mutates this
- if (fun.call(thisp, val, i, t))
- res.push(val);
- }
- }
+ var res = [];
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in t) {
+ var val = t[i]; // in case fun mutates this
+ if (fun.call(thisp, val, i, t))
+ res.push(val);
+ }
+ }
- return res;
- };
+ return res;
+ };
}
@@ -143,110 +143,110 @@ if (!Array.prototype.filter) {
// here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
- Object.keys = (function () {
- var hasOwnProperty = Object.prototype.hasOwnProperty,
- hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
- dontEnums = [
- 'toString',
- 'toLocaleString',
- 'valueOf',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'constructor'
- ],
- dontEnumsLength = dontEnums.length;
-
- return function (obj) {
- if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
- throw new TypeError('Object.keys called on non-object');
- }
-
- var result = [];
-
- for (var prop in obj) {
- if (hasOwnProperty.call(obj, prop)) result.push(prop);
- }
-
- if (hasDontEnumBug) {
- for (var i=0; i < dontEnumsLength; i++) {
- if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
- }
- }
- return result;
+ Object.keys = (function () {
+ var hasOwnProperty = Object.prototype.hasOwnProperty,
+ hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length;
+
+ return function (obj) {
+ if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var result = [];
+
+ for (var prop in obj) {
+ if (hasOwnProperty.call(obj, prop)) result.push(prop);
+ }
+
+ if (hasDontEnumBug) {
+ for (var i=0; i < dontEnumsLength; i++) {
+ if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
- })()
+ }
+ return result;
+ }
+ })()
}
// Internet Explorer 8 and older does not support Array.isArray,
// so we define it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray
if(!Array.isArray) {
- Array.isArray = function (vArg) {
- return Object.prototype.toString.call(vArg) === "[object Array]";
- };
+ Array.isArray = function (vArg) {
+ return Object.prototype.toString.call(vArg) === "[object Array]";
+ };
}
// Internet Explorer 8 and older does not support Function.bind,
// so we define it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
-
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
-
- return fBound;
- };
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
+
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
}
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
if (!Object.create) {
- Object.create = function (o) {
- if (arguments.length > 1) {
- throw new Error('Object.create implementation only accepts the first parameter.');
- }
- function F() {}
- F.prototype = o;
- return new F();
- };
+ Object.create = function (o) {
+ if (arguments.length > 1) {
+ throw new Error('Object.create implementation only accepts the first parameter.');
+ }
+ function F() {}
+ F.prototype = o;
+ return new F();
+ };
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
-
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
-
- return fBound;
- };
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
+
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
}
diff --git a/src/timeline/Controller.js b/src/timeline/Controller.js
index 7ec69a63..185d341b 100644
--- a/src/timeline/Controller.js
+++ b/src/timeline/Controller.js
@@ -4,11 +4,11 @@
* A Controller controls the reflows and repaints of all visual components
*/
function Controller () {
- this.id = util.randomUUID();
- this.components = {};
+ this.id = util.randomUUID();
+ this.components = {};
- this.repaintTimer = undefined;
- this.reflowTimer = undefined;
+ this.repaintTimer = undefined;
+ this.reflowTimer = undefined;
}
/**
@@ -16,18 +16,18 @@ function Controller () {
* @param {Component} component
*/
Controller.prototype.add = function add(component) {
- // validate the component
- if (component.id == undefined) {
- throw new Error('Component has no field id');
- }
- if (!(component instanceof Component) && !(component instanceof Controller)) {
- throw new TypeError('Component must be an instance of ' +
- 'prototype Component or Controller');
- }
-
- // add the component
- component.controller = this;
- this.components[component.id] = component;
+ // validate the component
+ if (component.id == undefined) {
+ throw new Error('Component has no field id');
+ }
+ if (!(component instanceof Component) && !(component instanceof Controller)) {
+ throw new TypeError('Component must be an instance of ' +
+ 'prototype Component or Controller');
+ }
+
+ // add the component
+ component.controller = this;
+ this.components[component.id] = component;
};
/**
@@ -35,18 +35,18 @@ Controller.prototype.add = function add(component) {
* @param {Component | String} component
*/
Controller.prototype.remove = function remove(component) {
- var id;
- for (id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- if (id == component || this.components[id] == component) {
- break;
- }
- }
+ var id;
+ for (id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ if (id == component || this.components[id] == component) {
+ break;
+ }
}
+ }
- if (id) {
- delete this.components[id];
- }
+ if (id) {
+ delete this.components[id];
+ }
};
/**
@@ -55,18 +55,18 @@ Controller.prototype.remove = function remove(component) {
* is false.
*/
Controller.prototype.requestReflow = function requestReflow(force) {
- if (force) {
- this.reflow();
- }
- else {
- if (!this.reflowTimer) {
- var me = this;
- this.reflowTimer = setTimeout(function () {
- me.reflowTimer = undefined;
- me.reflow();
- }, 0);
- }
+ if (force) {
+ this.reflow();
+ }
+ else {
+ if (!this.reflowTimer) {
+ var me = this;
+ this.reflowTimer = setTimeout(function () {
+ me.reflowTimer = undefined;
+ me.reflow();
+ }, 0);
}
+ }
};
/**
@@ -75,98 +75,98 @@ Controller.prototype.requestReflow = function requestReflow(force) {
* is false.
*/
Controller.prototype.requestRepaint = function requestRepaint(force) {
- if (force) {
- this.repaint();
- }
- else {
- if (!this.repaintTimer) {
- var me = this;
- this.repaintTimer = setTimeout(function () {
- me.repaintTimer = undefined;
- me.repaint();
- }, 0);
- }
+ if (force) {
+ this.repaint();
+ }
+ else {
+ if (!this.repaintTimer) {
+ var me = this;
+ this.repaintTimer = setTimeout(function () {
+ me.repaintTimer = undefined;
+ me.repaint();
+ }, 0);
}
+ }
};
/**
* Repaint all components
*/
Controller.prototype.repaint = function repaint() {
- var changed = false;
+ var changed = false;
- // cancel any running repaint request
- if (this.repaintTimer) {
- clearTimeout(this.repaintTimer);
- this.repaintTimer = undefined;
- }
-
- var done = {};
-
- function repaint(component, id) {
- if (!(id in done)) {
- // first repaint the components on which this component is dependent
- if (component.depends) {
- component.depends.forEach(function (dep) {
- repaint(dep, dep.id);
- });
- }
- if (component.parent) {
- repaint(component.parent, component.parent.id);
- }
-
- // repaint the component itself and mark as done
- changed = component.repaint() || changed;
- done[id] = true;
- }
+ // cancel any running repaint request
+ if (this.repaintTimer) {
+ clearTimeout(this.repaintTimer);
+ this.repaintTimer = undefined;
+ }
+
+ var done = {};
+
+ function repaint(component, id) {
+ if (!(id in done)) {
+ // first repaint the components on which this component is dependent
+ if (component.depends) {
+ component.depends.forEach(function (dep) {
+ repaint(dep, dep.id);
+ });
+ }
+ if (component.parent) {
+ repaint(component.parent, component.parent.id);
+ }
+
+ // repaint the component itself and mark as done
+ changed = component.repaint() || changed;
+ done[id] = true;
}
+ }
- util.forEach(this.components, repaint);
+ util.forEach(this.components, repaint);
- // immediately reflow when needed
- if (changed) {
- this.reflow();
- }
- // TODO: limit the number of nested reflows/repaints, prevent loop
+ // immediately reflow when needed
+ if (changed) {
+ this.reflow();
+ }
+ // TODO: limit the number of nested reflows/repaints, prevent loop
};
/**
* Reflow all components
*/
Controller.prototype.reflow = function reflow() {
- var resized = false;
+ var resized = false;
- // cancel any running repaint request
- if (this.reflowTimer) {
- clearTimeout(this.reflowTimer);
- this.reflowTimer = undefined;
- }
-
- var done = {};
-
- function reflow(component, id) {
- if (!(id in done)) {
- // first reflow the components on which this component is dependent
- if (component.depends) {
- component.depends.forEach(function (dep) {
- reflow(dep, dep.id);
- });
- }
- if (component.parent) {
- reflow(component.parent, component.parent.id);
- }
-
- // reflow the component itself and mark as done
- resized = component.reflow() || resized;
- done[id] = true;
- }
+ // cancel any running repaint request
+ if (this.reflowTimer) {
+ clearTimeout(this.reflowTimer);
+ this.reflowTimer = undefined;
+ }
+
+ var done = {};
+
+ function reflow(component, id) {
+ if (!(id in done)) {
+ // first reflow the components on which this component is dependent
+ if (component.depends) {
+ component.depends.forEach(function (dep) {
+ reflow(dep, dep.id);
+ });
+ }
+ if (component.parent) {
+ reflow(component.parent, component.parent.id);
+ }
+
+ // reflow the component itself and mark as done
+ resized = component.reflow() || resized;
+ done[id] = true;
}
+ }
- util.forEach(this.components, reflow);
+ util.forEach(this.components, reflow);
- // immediately repaint when needed
- if (resized) {
- this.repaint();
- }
- // TODO: limit the number of nested reflows/repaints, prevent loop
+ // immediately repaint when needed
+ if (resized) {
+ this.repaint();
+ }
+ // TODO: limit the number of nested reflows/repaints, prevent loop
};
diff --git a/src/timeline/Range.js b/src/timeline/Range.js
index 09a943dd..1ebe7a45 100644
--- a/src/timeline/Range.js
+++ b/src/timeline/Range.js
@@ -7,13 +7,13 @@
* @extends Controller
*/
function Range(options) {
- this.id = util.randomUUID();
- this.start = null; // Number
- this.end = null; // Number
+ this.id = util.randomUUID();
+ this.start = null; // Number
+ this.end = null; // Number
- this.options = options || {};
+ this.options = options || {};
- this.setOptions(options);
+ this.setOptions(options);
}
/**
@@ -27,12 +27,12 @@ function Range(options) {
* (end - start).
*/
Range.prototype.setOptions = function (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // re-apply range with new limitations
- if (this.start !== null && this.end !== null) {
- this.setRange(this.start, this.end);
- }
+ // re-apply range with new limitations
+ if (this.start !== null && this.end !== null) {
+ this.setRange(this.start, this.end);
+ }
};
/**
@@ -40,10 +40,10 @@ Range.prototype.setOptions = function (options) {
* @param {String} direction 'horizontal' or 'vertical'
*/
function validateDirection (direction) {
- if (direction != 'horizontal' && direction != 'vertical') {
- throw new TypeError('Unknown direction "' + direction + '". ' +
- 'Choose "horizontal" or "vertical".');
- }
+ if (direction != 'horizontal' && direction != 'vertical') {
+ throw new TypeError('Unknown direction "' + direction + '". ' +
+ 'Choose "horizontal" or "vertical".');
+ }
}
/**
@@ -53,44 +53,44 @@ function validateDirection (direction) {
* @param {String} direction Available directions: 'horizontal', 'vertical'
*/
Range.prototype.subscribe = function (component, event, direction) {
- var me = this;
-
- if (event == 'move') {
- // drag start listener
- component.on('dragstart', function (event) {
- me._onDragStart(event, component);
- });
-
- // drag listener
- component.on('drag', function (event) {
- me._onDrag(event, component, direction);
- });
-
- // drag end listener
- component.on('dragend', function (event) {
- me._onDragEnd(event, component);
- });
- }
- else if (event == 'zoom') {
- // mouse wheel
- function mousewheel (event) {
- me._onMouseWheel(event, component, direction);
- }
- component.on('mousewheel', mousewheel);
- component.on('DOMMouseScroll', mousewheel); // For FF
-
- // pinch
- component.on('touch', function (event) {
- me._onTouch();
- });
- component.on('pinch', function (event) {
- me._onPinch(event, component, direction);
- });
- }
- else {
- throw new TypeError('Unknown event "' + event + '". ' +
- 'Choose "move" or "zoom".');
+ var me = this;
+
+ if (event == 'move') {
+ // drag start listener
+ component.on('dragstart', function (event) {
+ me._onDragStart(event, component);
+ });
+
+ // drag listener
+ component.on('drag', function (event) {
+ me._onDrag(event, component, direction);
+ });
+
+ // drag end listener
+ component.on('dragend', function (event) {
+ me._onDragEnd(event, component);
+ });
+ }
+ else if (event == 'zoom') {
+ // mouse wheel
+ function mousewheel (event) {
+ me._onMouseWheel(event, component, direction);
}
+ component.on('mousewheel', mousewheel);
+ component.on('DOMMouseScroll', mousewheel); // For FF
+
+ // pinch
+ component.on('touch', function (event) {
+ me._onTouch();
+ });
+ component.on('pinch', function (event) {
+ me._onPinch(event, component, direction);
+ });
+ }
+ else {
+ throw new TypeError('Unknown event "' + event + '". ' +
+ 'Choose "move" or "zoom".');
+ }
};
/**
@@ -100,7 +100,7 @@ Range.prototype.subscribe = function (component, event, direction) {
* as parameter.
*/
Range.prototype.on = function (event, callback) {
- events.addListener(this, event, callback);
+ events.addListener(this, event, callback);
};
/**
@@ -110,10 +110,10 @@ Range.prototype.on = function (event, callback) {
* @private
*/
Range.prototype._trigger = function (event) {
- events.trigger(this, event, {
- start: this.start,
- end: this.end
- });
+ events.trigger(this, event, {
+ start: this.start,
+ end: this.end
+ });
};
/**
@@ -122,11 +122,11 @@ Range.prototype._trigger = function (event) {
* @param {Number} [end]
*/
Range.prototype.setRange = function(start, end) {
- var changed = this._applyRange(start, end);
- if (changed) {
- this._trigger('rangechange');
- this._trigger('rangechanged');
- }
+ var changed = this._applyRange(start, end);
+ if (changed) {
+ this._trigger('rangechange');
+ this._trigger('rangechanged');
+ }
};
/**
@@ -139,105 +139,105 @@ Range.prototype.setRange = function(start, end) {
* @private
*/
Range.prototype._applyRange = function(start, end) {
- var newStart = (start != null) ? util.convert(start, 'Number') : this.start,
- newEnd = (end != null) ? util.convert(end, 'Number') : this.end,
- max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null,
- min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null,
- diff;
-
- // check for valid number
- if (isNaN(newStart) || newStart === null) {
- throw new Error('Invalid start "' + start + '"');
- }
- if (isNaN(newEnd) || newEnd === null) {
- throw new Error('Invalid end "' + end + '"');
+ var newStart = (start != null) ? util.convert(start, 'Number') : this.start,
+ newEnd = (end != null) ? util.convert(end, 'Number') : this.end,
+ max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null,
+ min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null,
+ diff;
+
+ // check for valid number
+ if (isNaN(newStart) || newStart === null) {
+ throw new Error('Invalid start "' + start + '"');
+ }
+ if (isNaN(newEnd) || newEnd === null) {
+ throw new Error('Invalid end "' + end + '"');
+ }
+
+ // prevent start < end
+ if (newEnd < newStart) {
+ newEnd = newStart;
+ }
+
+ // prevent start < min
+ if (min !== null) {
+ if (newStart < min) {
+ diff = (min - newStart);
+ newStart += diff;
+ newEnd += diff;
+
+ // prevent end > max
+ if (max != null) {
+ if (newEnd > max) {
+ newEnd = max;
+ }
+ }
}
+ }
- // prevent start < end
- if (newEnd < newStart) {
- newEnd = newStart;
- }
+ // prevent end > max
+ if (max !== null) {
+ if (newEnd > max) {
+ diff = (newEnd - max);
+ newStart -= diff;
+ newEnd -= diff;
- // prevent start < min
- if (min !== null) {
+ // prevent start < min
+ if (min != null) {
if (newStart < min) {
- diff = (min - newStart);
- newStart += diff;
- newEnd += diff;
-
- // prevent end > max
- if (max != null) {
- if (newEnd > max) {
- newEnd = max;
- }
- }
+ newStart = min;
}
+ }
}
+ }
- // prevent end > max
- if (max !== null) {
- if (newEnd > max) {
- diff = (newEnd - max);
- newStart -= diff;
- newEnd -= diff;
-
- // prevent start < min
- if (min != null) {
- if (newStart < min) {
- newStart = min;
- }
- }
- }
+ // prevent (end-start) < zoomMin
+ if (this.options.zoomMin !== null) {
+ var zoomMin = parseFloat(this.options.zoomMin);
+ if (zoomMin < 0) {
+ zoomMin = 0;
}
-
- // prevent (end-start) < zoomMin
- if (this.options.zoomMin !== null) {
- var zoomMin = parseFloat(this.options.zoomMin);
- if (zoomMin < 0) {
- zoomMin = 0;
- }
- if ((newEnd - newStart) < zoomMin) {
- if ((this.end - this.start) === zoomMin) {
- // ignore this action, we are already zoomed to the minimum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the minimum
- diff = (zoomMin - (newEnd - newStart));
- newStart -= diff / 2;
- newEnd += diff / 2;
- }
- }
+ if ((newEnd - newStart) < zoomMin) {
+ if ((this.end - this.start) === zoomMin) {
+ // ignore this action, we are already zoomed to the minimum
+ newStart = this.start;
+ newEnd = this.end;
+ }
+ else {
+ // zoom to the minimum
+ diff = (zoomMin - (newEnd - newStart));
+ newStart -= diff / 2;
+ newEnd += diff / 2;
+ }
}
+ }
- // prevent (end-start) > zoomMax
- if (this.options.zoomMax !== null) {
- var zoomMax = parseFloat(this.options.zoomMax);
- if (zoomMax < 0) {
- zoomMax = 0;
- }
- if ((newEnd - newStart) > zoomMax) {
- if ((this.end - this.start) === zoomMax) {
- // ignore this action, we are already zoomed to the maximum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the maximum
- diff = ((newEnd - newStart) - zoomMax);
- newStart += diff / 2;
- newEnd -= diff / 2;
- }
- }
+ // prevent (end-start) > zoomMax
+ if (this.options.zoomMax !== null) {
+ var zoomMax = parseFloat(this.options.zoomMax);
+ if (zoomMax < 0) {
+ zoomMax = 0;
}
+ if ((newEnd - newStart) > zoomMax) {
+ if ((this.end - this.start) === zoomMax) {
+ // ignore this action, we are already zoomed to the maximum
+ newStart = this.start;
+ newEnd = this.end;
+ }
+ else {
+ // zoom to the maximum
+ diff = ((newEnd - newStart) - zoomMax);
+ newStart += diff / 2;
+ newEnd -= diff / 2;
+ }
+ }
+ }
- var changed = (this.start != newStart || this.end != newEnd);
+ var changed = (this.start != newStart || this.end != newEnd);
- this.start = newStart;
- this.end = newEnd;
+ this.start = newStart;
+ this.end = newEnd;
- return changed;
+ return changed;
};
/**
@@ -245,10 +245,10 @@ Range.prototype._applyRange = function(start, end) {
* @return {Object} An object with start and end properties
*/
Range.prototype.getRange = function() {
- return {
- start: this.start,
- end: this.end
- };
+ return {
+ start: this.start,
+ end: this.end
+ };
};
/**
@@ -258,7 +258,7 @@ Range.prototype.getRange = function() {
* @returns {{offset: number, scale: number}} conversion
*/
Range.prototype.conversion = function (width) {
- return Range.conversion(this.start, this.end, width);
+ return Range.conversion(this.start, this.end, width);
};
/**
@@ -270,18 +270,18 @@ Range.prototype.conversion = function (width) {
* @returns {{offset: number, scale: number}} conversion
*/
Range.conversion = function (start, end, width) {
- if (width != 0 && (end - start != 0)) {
- return {
- offset: start,
- scale: width / (end - start)
- }
- }
- else {
- return {
- offset: 0,
- scale: 1
- };
+ if (width != 0 && (end - start != 0)) {
+ return {
+ offset: start,
+ scale: width / (end - start)
}
+ }
+ else {
+ return {
+ offset: 0,
+ scale: 1
+ };
+ }
};
// global (private) object to store drag params
@@ -294,17 +294,17 @@ var touchParams = {};
* @private
*/
Range.prototype._onDragStart = function(event, component) {
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (touchParams.pinching) return;
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- touchParams.start = this.start;
- touchParams.end = this.end;
+ touchParams.start = this.start;
+ touchParams.end = this.end;
- var frame = component.frame;
- if (frame) {
- frame.style.cursor = 'move';
- }
+ var frame = component.frame;
+ if (frame) {
+ frame.style.cursor = 'move';
+ }
};
/**
@@ -315,21 +315,21 @@ Range.prototype._onDragStart = function(event, component) {
* @private
*/
Range.prototype._onDrag = function (event, component, direction) {
- validateDirection(direction);
+ validateDirection(direction);
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (touchParams.pinching) return;
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
- interval = (touchParams.end - touchParams.start),
- width = (direction == 'horizontal') ? component.width : component.height,
- diffRange = -delta / width * interval;
+ var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
+ interval = (touchParams.end - touchParams.start),
+ width = (direction == 'horizontal') ? component.width : component.height,
+ diffRange = -delta / width * interval;
- this._applyRange(touchParams.start + diffRange, touchParams.end + diffRange);
+ this._applyRange(touchParams.start + diffRange, touchParams.end + diffRange);
- // fire a rangechange event
- this._trigger('rangechange');
+ // fire a rangechange event
+ this._trigger('rangechange');
};
/**
@@ -339,16 +339,16 @@ Range.prototype._onDrag = function (event, component, direction) {
* @private
*/
Range.prototype._onDragEnd = function (event, component) {
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (touchParams.pinching) return;
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- if (component.frame) {
- component.frame.style.cursor = 'auto';
- }
+ if (component.frame) {
+ component.frame.style.cursor = 'auto';
+ }
- // fire a rangechanged event
- this._trigger('rangechanged');
+ // fire a rangechanged event
+ this._trigger('rangechanged');
};
/**
@@ -360,45 +360,45 @@ Range.prototype._onDragEnd = function (event, component) {
* @private
*/
Range.prototype._onMouseWheel = function(event, component, direction) {
- validateDirection(direction);
-
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta / 120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail / 3;
+ validateDirection(direction);
+
+ // retrieve delta
+ var delta = 0;
+ if (event.wheelDelta) { /* IE/Opera. */
+ delta = event.wheelDelta / 120;
+ } else if (event.detail) { /* Mozilla case. */
+ // In Mozilla, sign of delta is different than in IE.
+ // Also, delta is multiple of 3.
+ delta = -event.detail / 3;
+ }
+
+ // If delta is nonzero, handle it.
+ // Basically, delta is now positive if wheel was scrolled up,
+ // and negative, if wheel was scrolled down.
+ if (delta) {
+ // perform the zoom action. Delta is normally 1 or -1
+
+ // adjust a negative delta such that zooming in with delta 0.1
+ // equals zooming out with a delta -0.1
+ var scale;
+ if (delta < 0) {
+ scale = 1 - (delta / 5);
+ }
+ else {
+ scale = 1 / (1 + (delta / 5)) ;
}
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- // perform the zoom action. Delta is normally 1 or -1
-
- // adjust a negative delta such that zooming in with delta 0.1
- // equals zooming out with a delta -0.1
- var scale;
- if (delta < 0) {
- scale = 1 - (delta / 5);
- }
- else {
- scale = 1 / (1 + (delta / 5)) ;
- }
-
- // calculate center, the date to zoom around
- var gesture = Hammer.event.collectEventData(this, 'scroll', event),
- pointer = getPointer(gesture.touches[0], component.frame),
- pointerDate = this._pointerToDate(component, direction, pointer);
+ // calculate center, the date to zoom around
+ var gesture = Hammer.event.collectEventData(this, 'scroll', event),
+ pointer = getPointer(gesture.touches[0], component.frame),
+ pointerDate = this._pointerToDate(component, direction, pointer);
- this.zoom(scale, pointerDate);
- }
+ this.zoom(scale, pointerDate);
+ }
- // Prevent default actions caused by mouse wheel
- // (else the page and timeline both zoom and scroll)
- util.preventDefault(event);
+ // Prevent default actions caused by mouse wheel
+ // (else the page and timeline both zoom and scroll)
+ util.preventDefault(event);
};
/**
@@ -406,10 +406,10 @@ Range.prototype._onMouseWheel = function(event, component, direction) {
* @private
*/
Range.prototype._onTouch = function () {
- touchParams.start = this.start;
- touchParams.end = this.end;
- touchParams.pinching = false;
- touchParams.center = null;
+ touchParams.start = this.start;
+ touchParams.end = this.end;
+ touchParams.pinching = false;
+ touchParams.center = null;
};
/**
@@ -420,26 +420,26 @@ Range.prototype._onTouch = function () {
* @private
*/
Range.prototype._onPinch = function (event, component, direction) {
- touchParams.pinching = true;
+ touchParams.pinching = true;
- if (event.gesture.touches.length > 1) {
- if (!touchParams.center) {
- touchParams.center = getPointer(event.gesture.center, component.frame);
- }
+ if (event.gesture.touches.length > 1) {
+ if (!touchParams.center) {
+ touchParams.center = getPointer(event.gesture.center, component.frame);
+ }
- var scale = 1 / event.gesture.scale,
- initDate = this._pointerToDate(component, direction, touchParams.center),
- center = getPointer(event.gesture.center, component.frame),
- date = this._pointerToDate(component, direction, center),
- delta = date - initDate; // TODO: utilize delta
+ var scale = 1 / event.gesture.scale,
+ initDate = this._pointerToDate(component, direction, touchParams.center),
+ center = getPointer(event.gesture.center, component.frame),
+ date = this._pointerToDate(component, direction, center),
+ delta = date - initDate; // TODO: utilize delta
- // calculate new start and end
- var newStart = parseInt(initDate + (touchParams.start - initDate) * scale);
- var newEnd = parseInt(initDate + (touchParams.end - initDate) * scale);
+ // calculate new start and end
+ var newStart = parseInt(initDate + (touchParams.start - initDate) * scale);
+ var newEnd = parseInt(initDate + (touchParams.end - initDate) * scale);
- // apply new range
- this.setRange(newStart, newEnd);
- }
+ // apply new range
+ this.setRange(newStart, newEnd);
+ }
};
/**
@@ -451,17 +451,17 @@ Range.prototype._onPinch = function (event, component, direction) {
* @private
*/
Range.prototype._pointerToDate = function (component, direction, pointer) {
- var conversion;
- if (direction == 'horizontal') {
- var width = component.width;
- conversion = this.conversion(width);
- return pointer.x / conversion.scale + conversion.offset;
- }
- else {
- var height = component.height;
- conversion = this.conversion(height);
- return pointer.y / conversion.scale + conversion.offset;
- }
+ var conversion;
+ if (direction == 'horizontal') {
+ var width = component.width;
+ conversion = this.conversion(width);
+ return pointer.x / conversion.scale + conversion.offset;
+ }
+ else {
+ var height = component.height;
+ conversion = this.conversion(height);
+ return pointer.y / conversion.scale + conversion.offset;
+ }
};
/**
@@ -472,10 +472,10 @@ Range.prototype._pointerToDate = function (component, direction, pointer) {
* @private
*/
function getPointer (touch, element) {
- return {
- x: touch.pageX - vis.util.getAbsoluteLeft(element),
- y: touch.pageY - vis.util.getAbsoluteTop(element)
- };
+ return {
+ x: touch.pageX - vis.util.getAbsoluteLeft(element),
+ y: touch.pageY - vis.util.getAbsoluteTop(element)
+ };
}
/**
@@ -489,16 +489,16 @@ function getPointer (touch, element) {
* be zoomed.
*/
Range.prototype.zoom = function(scale, center) {
- // if centerDate is not provided, take it half between start Date and end Date
- if (center == null) {
- center = (this.start + this.end) / 2;
- }
+ // if centerDate is not provided, take it half between start Date and end Date
+ if (center == null) {
+ center = (this.start + this.end) / 2;
+ }
- // calculate new start and end
- var newStart = center + (this.start - center) * scale;
- var newEnd = center + (this.end - center) * scale;
+ // calculate new start and end
+ var newStart = center + (this.start - center) * scale;
+ var newEnd = center + (this.end - center) * scale;
- this.setRange(newStart, newEnd);
+ this.setRange(newStart, newEnd);
};
/**
@@ -508,17 +508,17 @@ Range.prototype.zoom = function(scale, center) {
* negative value will move left
*/
Range.prototype.move = function(delta) {
- // zoom start Date and end Date relative to the centerDate
- var diff = (this.end - this.start);
+ // zoom start Date and end Date relative to the centerDate
+ var diff = (this.end - this.start);
- // apply new values
- var newStart = this.start + diff * delta;
- var newEnd = this.end + diff * delta;
+ // apply new values
+ var newStart = this.start + diff * delta;
+ var newEnd = this.end + diff * delta;
- // TODO: reckon with min and max range
+ // TODO: reckon with min and max range
- this.start = newStart;
- this.end = newEnd;
+ this.start = newStart;
+ this.end = newEnd;
};
/**
@@ -526,13 +526,13 @@ Range.prototype.move = function(delta) {
* @param {Number} moveTo New center point of the range
*/
Range.prototype.moveTo = function(moveTo) {
- var center = (this.start + this.end) / 2;
+ var center = (this.start + this.end) / 2;
- var diff = center - moveTo;
+ var diff = center - moveTo;
- // calculate new start and end
- var newStart = this.start - diff;
- var newEnd = this.end - diff;
+ // calculate new start and end
+ var newStart = this.start - diff;
+ var newEnd = this.end - diff;
- this.setRange(newStart, newEnd);
+ this.setRange(newStart, newEnd);
};
diff --git a/src/timeline/Stack.js b/src/timeline/Stack.js
index e714aee4..017c98ce 100644
--- a/src/timeline/Stack.js
+++ b/src/timeline/Stack.js
@@ -5,39 +5,39 @@
* @param {Object} [options]
*/
function Stack (parent, options) {
- this.parent = parent;
-
- this.options = options || {};
- this.defaultOptions = {
- order: function (a, b) {
- //return (b.width - a.width) || (a.left - b.left); // TODO: cleanup
- // Order: ranges over non-ranges, ranged ordered by width, and
- // lastly ordered by start.
- if (a instanceof ItemRange) {
- if (b instanceof ItemRange) {
- var aInt = (a.data.end - a.data.start);
- var bInt = (b.data.end - b.data.start);
- return (aInt - bInt) || (a.data.start - b.data.start);
- }
- else {
- return -1;
- }
- }
- else {
- if (b instanceof ItemRange) {
- return 1;
- }
- else {
- return (a.data.start - b.data.start);
- }
- }
- },
- margin: {
- item: 10
+ this.parent = parent;
+
+ this.options = options || {};
+ this.defaultOptions = {
+ order: function (a, b) {
+ //return (b.width - a.width) || (a.left - b.left); // TODO: cleanup
+ // Order: ranges over non-ranges, ranged ordered by width, and
+ // lastly ordered by start.
+ if (a instanceof ItemRange) {
+ if (b instanceof ItemRange) {
+ var aInt = (a.data.end - a.data.start);
+ var bInt = (b.data.end - b.data.start);
+ return (aInt - bInt) || (a.data.start - b.data.start);
}
- };
+ else {
+ return -1;
+ }
+ }
+ else {
+ if (b instanceof ItemRange) {
+ return 1;
+ }
+ else {
+ return (a.data.start - b.data.start);
+ }
+ }
+ },
+ margin: {
+ item: 10
+ }
+ };
- this.ordered = []; // ordered items
+ this.ordered = []; // ordered items
}
/**
@@ -48,9 +48,9 @@ function Stack (parent, options) {
* {function} order Stacking order
*/
Stack.prototype.setOptions = function setOptions (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // TODO: register on data changes at the connected parent itemset, and update the changed part only and immediately
+ // TODO: register on data changes at the connected parent itemset, and update the changed part only and immediately
};
/**
@@ -58,8 +58,8 @@ Stack.prototype.setOptions = function setOptions (options) {
* distance equal to options.margin.item.
*/
Stack.prototype.update = function update() {
- this._order();
- this._stack();
+ this._order();
+ this._stack();
};
/**
@@ -70,31 +70,31 @@ Stack.prototype.update = function update() {
* @private
*/
Stack.prototype._order = function _order () {
- var items = this.parent.items;
- if (!items) {
- throw new Error('Cannot stack items: parent does not contain items');
+ var items = this.parent.items;
+ if (!items) {
+ throw new Error('Cannot stack items: parent does not contain items');
+ }
+
+ // TODO: store the sorted items, to have less work later on
+ var ordered = [];
+ var index = 0;
+ // items is a map (no array)
+ util.forEach(items, function (item) {
+ if (item.visible) {
+ ordered[index] = item;
+ index++;
}
+ });
- // TODO: store the sorted items, to have less work later on
- var ordered = [];
- var index = 0;
- // items is a map (no array)
- util.forEach(items, function (item) {
- if (item.visible) {
- ordered[index] = item;
- index++;
- }
- });
-
- //if a customer stack order function exists, use it.
- var order = this.options.order || this.defaultOptions.order;
- if (!(typeof order === 'function')) {
- throw new Error('Option order must be a function');
- }
+ //if a customer stack order function exists, use it.
+ var order = this.options.order || this.defaultOptions.order;
+ if (!(typeof order === 'function')) {
+ throw new Error('Option order must be a function');
+ }
- ordered.sort(order);
+ ordered.sort(order);
- this.ordered = ordered;
+ this.ordered = ordered;
};
/**
@@ -103,40 +103,40 @@ Stack.prototype._order = function _order () {
* @private
*/
Stack.prototype._stack = function _stack () {
- var i,
- iMax,
- ordered = this.ordered,
- options = this.options,
- orientation = options.orientation || this.defaultOptions.orientation,
- axisOnTop = (orientation == 'top'),
- margin;
-
- if (options.margin && options.margin.item !== undefined) {
- margin = options.margin.item;
- }
- else {
- margin = this.defaultOptions.margin.item
- }
-
- // calculate new, non-overlapping positions
- for (i = 0, iMax = ordered.length; i < iMax; i++) {
- var item = ordered[i];
- var collidingItem = null;
- do {
- // TODO: optimize checking for overlap. when there is a gap without items,
- // you only need to check for items from the next item on, not from zero
- collidingItem = this.checkOverlap(ordered, i, 0, i - 1, margin);
- if (collidingItem != null) {
- // There is a collision. Reposition the event above the colliding element
- if (axisOnTop) {
- item.top = collidingItem.top + collidingItem.height + margin;
- }
- else {
- item.top = collidingItem.top - item.height - margin;
- }
- }
- } while (collidingItem);
- }
+ var i,
+ iMax,
+ ordered = this.ordered,
+ options = this.options,
+ orientation = options.orientation || this.defaultOptions.orientation,
+ axisOnTop = (orientation == 'top'),
+ margin;
+
+ if (options.margin && options.margin.item !== undefined) {
+ margin = options.margin.item;
+ }
+ else {
+ margin = this.defaultOptions.margin.item
+ }
+
+ // calculate new, non-overlapping positions
+ for (i = 0, iMax = ordered.length; i < iMax; i++) {
+ var item = ordered[i];
+ var collidingItem = null;
+ do {
+ // TODO: optimize checking for overlap. when there is a gap without items,
+ // you only need to check for items from the next item on, not from zero
+ collidingItem = this.checkOverlap(ordered, i, 0, i - 1, margin);
+ if (collidingItem != null) {
+ // There is a collision. Reposition the event above the colliding element
+ if (axisOnTop) {
+ item.top = collidingItem.top + collidingItem.height + margin;
+ }
+ else {
+ item.top = collidingItem.top - item.height - margin;
+ }
+ }
+ } while (collidingItem);
+ }
};
/**
@@ -155,21 +155,21 @@ Stack.prototype._stack = function _stack () {
*/
Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
itemStart, itemEnd, margin) {
- var collision = this.collision;
-
- // we loop from end to start, as we suppose that the chance of a
- // collision is larger for items at the end, so check these first.
- var a = items[itemIndex];
- for (var i = itemEnd; i >= itemStart; i--) {
- var b = items[i];
- if (collision(a, b, margin)) {
- if (i != itemIndex) {
- return b;
- }
- }
+ var collision = this.collision;
+
+ // we loop from end to start, as we suppose that the chance of a
+ // collision is larger for items at the end, so check these first.
+ var a = items[itemIndex];
+ for (var i = itemEnd; i >= itemStart; i--) {
+ var b = items[i];
+ if (collision(a, b, margin)) {
+ if (i != itemIndex) {
+ return b;
+ }
}
+ }
- return null;
+ return null;
};
/**
@@ -185,8 +185,8 @@ Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
* @return {boolean} true if a and b collide, else false
*/
Stack.prototype.collision = function collision (a, b, margin) {
- return ((a.left - margin) < (b.left + b.getWidth()) &&
- (a.left + a.getWidth() + margin) > b.left &&
- (a.top - margin) < (b.top + b.height) &&
- (a.top + a.height + margin) > b.top);
+ return ((a.left - margin) < (b.left + b.getWidth()) &&
+ (a.left + a.getWidth() + margin) > b.left &&
+ (a.top - margin) < (b.top + b.height) &&
+ (a.top + a.height + margin) > b.top);
};
diff --git a/src/timeline/TimeStep.js b/src/timeline/TimeStep.js
index ccbc6c9f..fc56b518 100644
--- a/src/timeline/TimeStep.js
+++ b/src/timeline/TimeStep.js
@@ -25,29 +25,29 @@
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
TimeStep = function(start, end, minimumStep) {
- // variables
- this.current = new Date();
- this._start = new Date();
- this._end = new Date();
+ // variables
+ this.current = new Date();
+ this._start = new Date();
+ this._end = new Date();
- this.autoScale = true;
- this.scale = TimeStep.SCALE.DAY;
- this.step = 1;
+ this.autoScale = true;
+ this.scale = TimeStep.SCALE.DAY;
+ this.step = 1;
- // initialize the range
- this.setRange(start, end, minimumStep);
+ // initialize the range
+ this.setRange(start, end, minimumStep);
};
/// enum scale
TimeStep.SCALE = {
- MILLISECOND: 1,
- SECOND: 2,
- MINUTE: 3,
- HOUR: 4,
- DAY: 5,
- WEEKDAY: 6,
- MONTH: 7,
- YEAR: 8
+ MILLISECOND: 1,
+ SECOND: 2,
+ MINUTE: 3,
+ HOUR: 4,
+ DAY: 5,
+ WEEKDAY: 6,
+ MONTH: 7,
+ YEAR: 8
};
@@ -62,24 +62,24 @@ TimeStep.SCALE = {
* @param {int} [minimumStep] Optional. Minimum step size in milliseconds
*/
TimeStep.prototype.setRange = function(start, end, minimumStep) {
- if (!(start instanceof Date) || !(end instanceof Date)) {
- throw "No legal start or end date in method setRange";
- }
+ if (!(start instanceof Date) || !(end instanceof Date)) {
+ throw "No legal start or end date in method setRange";
+ }
- this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
- this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
+ this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
+ this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
- if (this.autoScale) {
- this.setMinimumStep(minimumStep);
- }
+ if (this.autoScale) {
+ this.setMinimumStep(minimumStep);
+ }
};
/**
* Set the range iterator to the start date.
*/
TimeStep.prototype.first = function() {
- this.current = new Date(this._start.valueOf());
- this.roundToMinor();
+ this.current = new Date(this._start.valueOf());
+ this.roundToMinor();
};
/**
@@ -87,36 +87,36 @@ TimeStep.prototype.first = function() {
* This must be executed once when the current date is set to start Date
*/
TimeStep.prototype.roundToMinor = function() {
- // round to floor
- // IMPORTANT: we have no breaks in this switch! (this is no bug)
- //noinspection FallthroughInSwitchStatementJS
+ // round to floor
+ // IMPORTANT: we have no breaks in this switch! (this is no bug)
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.scale) {
+ case TimeStep.SCALE.YEAR:
+ this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
+ this.current.setMonth(0);
+ case TimeStep.SCALE.MONTH: this.current.setDate(1);
+ case TimeStep.SCALE.DAY: // intentional fall through
+ case TimeStep.SCALE.WEEKDAY: this.current.setHours(0);
+ case TimeStep.SCALE.HOUR: this.current.setMinutes(0);
+ case TimeStep.SCALE.MINUTE: this.current.setSeconds(0);
+ case TimeStep.SCALE.SECOND: this.current.setMilliseconds(0);
+ //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds
+ }
+
+ if (this.step != 1) {
+ // round down to the first minor value that is a multiple of the current step size
switch (this.scale) {
- case TimeStep.SCALE.YEAR:
- this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
- this.current.setMonth(0);
- case TimeStep.SCALE.MONTH: this.current.setDate(1);
- case TimeStep.SCALE.DAY: // intentional fall through
- case TimeStep.SCALE.WEEKDAY: this.current.setHours(0);
- case TimeStep.SCALE.HOUR: this.current.setMinutes(0);
- case TimeStep.SCALE.MINUTE: this.current.setSeconds(0);
- case TimeStep.SCALE.SECOND: this.current.setMilliseconds(0);
- //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds
- }
-
- if (this.step != 1) {
- // round down to the first minor value that is a multiple of the current step size
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
- case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
- case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
- case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
- default: break;
- }
+ case TimeStep.SCALE.MILLISECOND: this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
+ case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
+ case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
+ case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
+ default: break;
}
+ }
};
/**
@@ -124,70 +124,70 @@ TimeStep.prototype.roundToMinor = function() {
* @return {boolean} true if the current date has not passed the end date
*/
TimeStep.prototype.hasNext = function () {
- return (this.current.valueOf() <= this._end.valueOf());
+ return (this.current.valueOf() <= this._end.valueOf());
};
/**
* Do the next step
*/
TimeStep.prototype.next = function() {
- var prev = this.current.valueOf();
-
- // Two cases, needed to prevent issues with switching daylight savings
- // (end of March and end of October)
- if (this.current.getMonth() < 6) {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:
-
- this.current = new Date(this.current.valueOf() + this.step); break;
- case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break;
- case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
- case TimeStep.SCALE.HOUR:
- this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
- // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
- var h = this.current.getHours();
- this.current.setHours(h - (h % this.step));
- break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
- default: break;
- }
+ var prev = this.current.valueOf();
+
+ // Two cases, needed to prevent issues with switching daylight savings
+ // (end of March and end of October)
+ if (this.current.getMonth() < 6) {
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:
+
+ this.current = new Date(this.current.valueOf() + this.step); break;
+ case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break;
+ case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
+ case TimeStep.SCALE.HOUR:
+ this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
+ // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
+ var h = this.current.getHours();
+ this.current.setHours(h - (h % this.step));
+ break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
+ default: break;
}
- else {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: this.current = new Date(this.current.valueOf() + this.step); break;
- case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() + this.step); break;
- case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() + this.step); break;
- case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() + this.step); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
- default: break;
- }
+ }
+ else {
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: this.current = new Date(this.current.valueOf() + this.step); break;
+ case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() + this.step); break;
+ case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() + this.step); break;
+ case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() + this.step); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
+ default: break;
}
+ }
- if (this.step != 1) {
- // round down to the correct major value
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
- case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
- case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
- case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
- case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
- case TimeStep.SCALE.YEAR: break; // nothing to do for year
- default: break;
- }
+ if (this.step != 1) {
+ // round down to the correct major value
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
+ case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
+ case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
+ case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
+ case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
+ case TimeStep.SCALE.YEAR: break; // nothing to do for year
+ default: break;
}
+ }
- // safety mechanism: if current time is still unchanged, move to the end
- if (this.current.valueOf() == prev) {
- this.current = new Date(this._end.valueOf());
- }
+ // safety mechanism: if current time is still unchanged, move to the end
+ if (this.current.valueOf() == prev) {
+ this.current = new Date(this._end.valueOf());
+ }
};
@@ -196,7 +196,7 @@ TimeStep.prototype.next = function() {
* @return {Date} current The current date
*/
TimeStep.prototype.getCurrent = function() {
- return this.current;
+ return this.current;
};
/**
@@ -213,13 +213,13 @@ TimeStep.prototype.getCurrent = function() {
* example 1, 2, 5, or 10.
*/
TimeStep.prototype.setScale = function(newScale, newStep) {
- this.scale = newScale;
+ this.scale = newScale;
- if (newStep > 0) {
- this.step = newStep;
- }
+ if (newStep > 0) {
+ this.step = newStep;
+ }
- this.autoScale = false;
+ this.autoScale = false;
};
/**
@@ -227,7 +227,7 @@ TimeStep.prototype.setScale = function(newScale, newStep) {
* @param {boolean} enable If true, autoascaling is set true
*/
TimeStep.prototype.setAutoScale = function (enable) {
- this.autoScale = enable;
+ this.autoScale = enable;
};
@@ -236,48 +236,48 @@ TimeStep.prototype.setAutoScale = function (enable) {
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
TimeStep.prototype.setMinimumStep = function(minimumStep) {
- if (minimumStep == undefined) {
- return;
- }
-
- var stepYear = (1000 * 60 * 60 * 24 * 30 * 12);
- var stepMonth = (1000 * 60 * 60 * 24 * 30);
- var stepDay = (1000 * 60 * 60 * 24);
- var stepHour = (1000 * 60 * 60);
- var stepMinute = (1000 * 60);
- var stepSecond = (1000);
- var stepMillisecond= (1);
-
- // find the smallest step that is larger than the provided minimumStep
- if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;}
- if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;}
- if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;}
- if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;}
- if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;}
- if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;}
- if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;}
- if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;}
- if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;}
- if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;}
- if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;}
- if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;}
- if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;}
- if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;}
- if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;}
- if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;}
- if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;}
- if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;}
- if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;}
- if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;}
- if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;}
- if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;}
- if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;}
- if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;}
- if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;}
- if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;}
- if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;}
- if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;}
- if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;}
+ if (minimumStep == undefined) {
+ return;
+ }
+
+ var stepYear = (1000 * 60 * 60 * 24 * 30 * 12);
+ var stepMonth = (1000 * 60 * 60 * 24 * 30);
+ var stepDay = (1000 * 60 * 60 * 24);
+ var stepHour = (1000 * 60 * 60);
+ var stepMinute = (1000 * 60);
+ var stepSecond = (1000);
+ var stepMillisecond= (1);
+
+ // find the smallest step that is larger than the provided minimumStep
+ if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;}
+ if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;}
+ if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;}
+ if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;}
+ if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;}
+ if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;}
+ if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;}
+ if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;}
+ if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;}
+ if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;}
+ if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;}
+ if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;}
+ if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;}
+ if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;}
+ if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;}
+ if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;}
+ if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;}
+ if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;}
+ if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;}
+ if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;}
+ if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;}
+ if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;}
+ if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;}
+ if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;}
+ if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;}
+ if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;}
+ if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;}
+ if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;}
+ if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;}
};
/**
@@ -286,87 +286,87 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) {
* @param {Date} date the date to be snapped
*/
TimeStep.prototype.snap = function(date) {
- if (this.scale == TimeStep.SCALE.YEAR) {
- var year = date.getFullYear() + Math.round(date.getMonth() / 12);
- date.setFullYear(Math.round(year / this.step) * this.step);
- date.setMonth(0);
- date.setDate(0);
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+ if (this.scale == TimeStep.SCALE.YEAR) {
+ var year = date.getFullYear() + Math.round(date.getMonth() / 12);
+ date.setFullYear(Math.round(year / this.step) * this.step);
+ date.setMonth(0);
+ date.setDate(0);
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.MONTH) {
+ if (date.getDate() > 15) {
+ date.setDate(1);
+ date.setMonth(date.getMonth() + 1);
+ // important: first set Date to 1, after that change the month.
}
- else if (this.scale == TimeStep.SCALE.MONTH) {
- if (date.getDate() > 15) {
- date.setDate(1);
- date.setMonth(date.getMonth() + 1);
- // important: first set Date to 1, after that change the month.
- }
- else {
- date.setDate(1);
- }
-
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+ else {
+ date.setDate(1);
}
- else if (this.scale == TimeStep.SCALE.DAY ||
- this.scale == TimeStep.SCALE.WEEKDAY) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 5:
- case 2:
- date.setHours(Math.round(date.getHours() / 24) * 24); break;
- default:
- date.setHours(Math.round(date.getHours() / 12) * 12); break;
- }
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.DAY ||
+ this.scale == TimeStep.SCALE.WEEKDAY) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 5:
+ case 2:
+ date.setHours(Math.round(date.getHours() / 24) * 24); break;
+ default:
+ date.setHours(Math.round(date.getHours() / 12) * 12); break;
}
- else if (this.scale == TimeStep.SCALE.HOUR) {
- switch (this.step) {
- case 4:
- date.setMinutes(Math.round(date.getMinutes() / 60) * 60); break;
- default:
- date.setMinutes(Math.round(date.getMinutes() / 30) * 30); break;
- }
- date.setSeconds(0);
- date.setMilliseconds(0);
- } else if (this.scale == TimeStep.SCALE.MINUTE) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 15:
- case 10:
- date.setMinutes(Math.round(date.getMinutes() / 5) * 5);
- date.setSeconds(0);
- break;
- case 5:
- date.setSeconds(Math.round(date.getSeconds() / 60) * 60); break;
- default:
- date.setSeconds(Math.round(date.getSeconds() / 30) * 30); break;
- }
- date.setMilliseconds(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.HOUR) {
+ switch (this.step) {
+ case 4:
+ date.setMinutes(Math.round(date.getMinutes() / 60) * 60); break;
+ default:
+ date.setMinutes(Math.round(date.getMinutes() / 30) * 30); break;
}
- else if (this.scale == TimeStep.SCALE.SECOND) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 15:
- case 10:
- date.setSeconds(Math.round(date.getSeconds() / 5) * 5);
- date.setMilliseconds(0);
- break;
- case 5:
- date.setMilliseconds(Math.round(date.getMilliseconds() / 1000) * 1000); break;
- default:
- date.setMilliseconds(Math.round(date.getMilliseconds() / 500) * 500); break;
- }
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ } else if (this.scale == TimeStep.SCALE.MINUTE) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 15:
+ case 10:
+ date.setMinutes(Math.round(date.getMinutes() / 5) * 5);
+ date.setSeconds(0);
+ break;
+ case 5:
+ date.setSeconds(Math.round(date.getSeconds() / 60) * 60); break;
+ default:
+ date.setSeconds(Math.round(date.getSeconds() / 30) * 30); break;
}
- else if (this.scale == TimeStep.SCALE.MILLISECOND) {
- var step = this.step > 5 ? this.step / 2 : 1;
- date.setMilliseconds(Math.round(date.getMilliseconds() / step) * step);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.SECOND) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 15:
+ case 10:
+ date.setSeconds(Math.round(date.getSeconds() / 5) * 5);
+ date.setMilliseconds(0);
+ break;
+ case 5:
+ date.setMilliseconds(Math.round(date.getMilliseconds() / 1000) * 1000); break;
+ default:
+ date.setMilliseconds(Math.round(date.getMilliseconds() / 500) * 500); break;
}
+ }
+ else if (this.scale == TimeStep.SCALE.MILLISECOND) {
+ var step = this.step > 5 ? this.step / 2 : 1;
+ date.setMilliseconds(Math.round(date.getMilliseconds() / step) * step);
+ }
};
/**
@@ -375,26 +375,26 @@ TimeStep.prototype.snap = function(date) {
* @return {boolean} true if current date is major, else false.
*/
TimeStep.prototype.isMajor = function() {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:
- return (this.current.getMilliseconds() == 0);
- case TimeStep.SCALE.SECOND:
- return (this.current.getSeconds() == 0);
- case TimeStep.SCALE.MINUTE:
- return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
- // Note: this is no bug. Major label is equal for both minute and hour scale
- case TimeStep.SCALE.HOUR:
- return (this.current.getHours() == 0);
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY:
- return (this.current.getDate() == 1);
- case TimeStep.SCALE.MONTH:
- return (this.current.getMonth() == 0);
- case TimeStep.SCALE.YEAR:
- return false;
- default:
- return false;
- }
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:
+ return (this.current.getMilliseconds() == 0);
+ case TimeStep.SCALE.SECOND:
+ return (this.current.getSeconds() == 0);
+ case TimeStep.SCALE.MINUTE:
+ return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
+ // Note: this is no bug. Major label is equal for both minute and hour scale
+ case TimeStep.SCALE.HOUR:
+ return (this.current.getHours() == 0);
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY:
+ return (this.current.getDate() == 1);
+ case TimeStep.SCALE.MONTH:
+ return (this.current.getMonth() == 0);
+ case TimeStep.SCALE.YEAR:
+ return false;
+ default:
+ return false;
+ }
};
@@ -405,21 +405,21 @@ TimeStep.prototype.isMajor = function() {
* @param {Date} [date] custom date. if not provided, current date is taken
*/
TimeStep.prototype.getLabelMinor = function(date) {
- if (date == undefined) {
- date = this.current;
- }
-
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS');
- case TimeStep.SCALE.SECOND: return moment(date).format('s');
- case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm');
- case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm');
- case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D');
- case TimeStep.SCALE.DAY: return moment(date).format('D');
- case TimeStep.SCALE.MONTH: return moment(date).format('MMM');
- case TimeStep.SCALE.YEAR: return moment(date).format('YYYY');
- default: return '';
- }
+ if (date == undefined) {
+ date = this.current;
+ }
+
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS');
+ case TimeStep.SCALE.SECOND: return moment(date).format('s');
+ case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm');
+ case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm');
+ case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D');
+ case TimeStep.SCALE.DAY: return moment(date).format('D');
+ case TimeStep.SCALE.MONTH: return moment(date).format('MMM');
+ case TimeStep.SCALE.YEAR: return moment(date).format('YYYY');
+ default: return '';
+ }
};
@@ -430,20 +430,20 @@ TimeStep.prototype.getLabelMinor = function(date) {
* @param {Date} [date] custom date. if not provided, current date is taken
*/
TimeStep.prototype.getLabelMajor = function(date) {
- if (date == undefined) {
- date = this.current;
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss');
- case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm');
- case TimeStep.SCALE.MINUTE:
- case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM');
- case TimeStep.SCALE.WEEKDAY:
- case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY');
- case TimeStep.SCALE.MONTH: return moment(date).format('YYYY');
- case TimeStep.SCALE.YEAR: return '';
- default: return '';
- }
+ if (date == undefined) {
+ date = this.current;
+ }
+
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss');
+ case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm');
+ case TimeStep.SCALE.MINUTE:
+ case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM');
+ case TimeStep.SCALE.WEEKDAY:
+ case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY');
+ case TimeStep.SCALE.MONTH: return moment(date).format('YYYY');
+ case TimeStep.SCALE.YEAR: return '';
+ default: return '';
+ }
};
diff --git a/src/timeline/Timeline.js b/src/timeline/Timeline.js
index 9122be85..cceebc7b 100644
--- a/src/timeline/Timeline.js
+++ b/src/timeline/Timeline.js
@@ -6,130 +6,130 @@
* @constructor
*/
function Timeline (container, items, options) {
- var me = this;
- var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
- this.options = {
- orientation: 'bottom',
- min: null,
- max: null,
- zoomMin: 10, // milliseconds
- zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
- // moveable: true, // TODO: option moveable
- // zoomable: true, // TODO: option zoomable
- showMinorLabels: true,
- showMajorLabels: true,
- showCurrentTime: false,
- showCustomTime: false,
- autoResize: false
- };
-
- // controller
- this.controller = new Controller();
-
- // root panel
- if (!container) {
- throw new Error('No container element provided');
+ var me = this;
+ var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
+ this.options = {
+ orientation: 'bottom',
+ min: null,
+ max: null,
+ zoomMin: 10, // milliseconds
+ zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
+ // moveable: true, // TODO: option moveable
+ // zoomable: true, // TODO: option zoomable
+ showMinorLabels: true,
+ showMajorLabels: true,
+ showCurrentTime: false,
+ showCustomTime: false,
+ autoResize: false
+ };
+
+ // controller
+ this.controller = new Controller();
+
+ // root panel
+ if (!container) {
+ throw new Error('No container element provided');
+ }
+ var rootOptions = Object.create(this.options);
+ rootOptions.height = function () {
+ // TODO: change to height
+ if (me.options.height) {
+ // fixed height
+ return me.options.height;
}
- var rootOptions = Object.create(this.options);
- rootOptions.height = function () {
- // TODO: change to height
- if (me.options.height) {
- // fixed height
- return me.options.height;
- }
- else {
- // auto height
- return (me.timeaxis.height + me.content.height) + 'px';
- }
- };
- this.rootPanel = new RootPanel(container, rootOptions);
- this.controller.add(this.rootPanel);
-
- // item panel
- var itemOptions = Object.create(this.options);
- itemOptions.left = function () {
- return me.labelPanel.width;
- };
- itemOptions.width = function () {
- return me.rootPanel.width - me.labelPanel.width;
- };
- itemOptions.top = null;
- itemOptions.height = null;
- this.itemPanel = new Panel(this.rootPanel, [], itemOptions);
- this.controller.add(this.itemPanel);
-
- // label panel
- var labelOptions = Object.create(this.options);
- labelOptions.top = null;
- labelOptions.left = null;
- labelOptions.height = null;
- labelOptions.width = function () {
- if (me.content && typeof me.content.getLabelsWidth === 'function') {
- return me.content.getLabelsWidth();
- }
- else {
- return 0;
- }
- };
- this.labelPanel = new Panel(this.rootPanel, [], labelOptions);
- this.controller.add(this.labelPanel);
-
- // range
- var rangeOptions = Object.create(this.options);
- this.range = new Range(rangeOptions);
- this.range.setRange(
- now.clone().add('days', -3).valueOf(),
- now.clone().add('days', 4).valueOf()
- );
-
- // TODO: reckon with options moveable and zoomable
- this.range.subscribe(this.rootPanel, 'move', 'horizontal');
- this.range.subscribe(this.rootPanel, 'zoom', 'horizontal');
- this.range.on('rangechange', function () {
- var force = true;
- me.controller.requestReflow(force);
- });
- this.range.on('rangechanged', function () {
- var force = true;
- me.controller.requestReflow(force);
- });
-
- // TODO: put the listeners in setOptions, be able to dynamically change with options moveable and zoomable
-
- // time axis
- var timeaxisOptions = Object.create(rootOptions);
- timeaxisOptions.range = this.range;
- timeaxisOptions.left = null;
- timeaxisOptions.top = null;
- timeaxisOptions.width = '100%';
- timeaxisOptions.height = null;
- this.timeaxis = new TimeAxis(this.itemPanel, [], timeaxisOptions);
- this.timeaxis.setRange(this.range);
- this.controller.add(this.timeaxis);
-
- // current time bar
- this.currenttime = new CurrentTime(this.timeaxis, [], rootOptions);
- this.controller.add(this.currenttime);
-
- // custom time bar
- this.customtime = new CustomTime(this.timeaxis, [], rootOptions);
- this.controller.add(this.customtime);
-
- // create groupset
- this.setGroups(null);
-
- this.itemsData = null; // DataSet
- this.groupsData = null; // DataSet
-
- // apply options
- if (options) {
- this.setOptions(options);
+ else {
+ // auto height
+ return (me.timeaxis.height + me.content.height) + 'px';
}
-
- // create itemset and groupset
- if (items) {
- this.setItems(items);
+ };
+ this.rootPanel = new RootPanel(container, rootOptions);
+ this.controller.add(this.rootPanel);
+
+ // item panel
+ var itemOptions = Object.create(this.options);
+ itemOptions.left = function () {
+ return me.labelPanel.width;
+ };
+ itemOptions.width = function () {
+ return me.rootPanel.width - me.labelPanel.width;
+ };
+ itemOptions.top = null;
+ itemOptions.height = null;
+ this.itemPanel = new Panel(this.rootPanel, [], itemOptions);
+ this.controller.add(this.itemPanel);
+
+ // label panel
+ var labelOptions = Object.create(this.options);
+ labelOptions.top = null;
+ labelOptions.left = null;
+ labelOptions.height = null;
+ labelOptions.width = function () {
+ if (me.content && typeof me.content.getLabelsWidth === 'function') {
+ return me.content.getLabelsWidth();
}
+ else {
+ return 0;
+ }
+ };
+ this.labelPanel = new Panel(this.rootPanel, [], labelOptions);
+ this.controller.add(this.labelPanel);
+
+ // range
+ var rangeOptions = Object.create(this.options);
+ this.range = new Range(rangeOptions);
+ this.range.setRange(
+ now.clone().add('days', -3).valueOf(),
+ now.clone().add('days', 4).valueOf()
+ );
+
+ // TODO: reckon with options moveable and zoomable
+ this.range.subscribe(this.rootPanel, 'move', 'horizontal');
+ this.range.subscribe(this.rootPanel, 'zoom', 'horizontal');
+ this.range.on('rangechange', function () {
+ var force = true;
+ me.controller.requestReflow(force);
+ });
+ this.range.on('rangechanged', function () {
+ var force = true;
+ me.controller.requestReflow(force);
+ });
+
+ // TODO: put the listeners in setOptions, be able to dynamically change with options moveable and zoomable
+
+ // time axis
+ var timeaxisOptions = Object.create(rootOptions);
+ timeaxisOptions.range = this.range;
+ timeaxisOptions.left = null;
+ timeaxisOptions.top = null;
+ timeaxisOptions.width = '100%';
+ timeaxisOptions.height = null;
+ this.timeaxis = new TimeAxis(this.itemPanel, [], timeaxisOptions);
+ this.timeaxis.setRange(this.range);
+ this.controller.add(this.timeaxis);
+
+ // current time bar
+ this.currenttime = new CurrentTime(this.timeaxis, [], rootOptions);
+ this.controller.add(this.currenttime);
+
+ // custom time bar
+ this.customtime = new CustomTime(this.timeaxis, [], rootOptions);
+ this.controller.add(this.customtime);
+
+ // create groupset
+ this.setGroups(null);
+
+ this.itemsData = null; // DataSet
+ this.groupsData = null; // DataSet
+
+ // apply options
+ if (options) {
+ this.setOptions(options);
+ }
+
+ // create itemset and groupset
+ if (items) {
+ this.setItems(items);
+ }
}
/**
@@ -137,15 +137,15 @@ function Timeline (container, items, options) {
* @param {Object} options TODO: describe the available options
*/
Timeline.prototype.setOptions = function (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // force update of range
- // options.start and options.end can be undefined
- //this.range.setRange(options.start, options.end);
- this.range.setRange();
+ // force update of range
+ // options.start and options.end can be undefined
+ //this.range.setRange(options.start, options.end);
+ this.range.setRange();
- this.controller.reflow();
- this.controller.repaint();
+ this.controller.reflow();
+ this.controller.repaint();
};
/**
@@ -153,7 +153,7 @@ Timeline.prototype.setOptions = function (options) {
* @param {Date} time
*/
Timeline.prototype.setCustomTime = function (time) {
- this.customtime._setCustomTime(time);
+ this.customtime._setCustomTime(time);
};
/**
@@ -161,7 +161,7 @@ Timeline.prototype.setCustomTime = function (time) {
* @return {Date} customTime
*/
Timeline.prototype.getCustomTime = function() {
- return new Date(this.customtime.customTime.valueOf());
+ return new Date(this.customtime.customTime.valueOf());
};
/**
@@ -169,60 +169,60 @@ Timeline.prototype.getCustomTime = function() {
* @param {vis.DataSet | Array | DataTable | null} items
*/
Timeline.prototype.setItems = function(items) {
- var initialLoad = (this.itemsData == null);
-
- // convert to type DataSet when needed
- var newItemSet;
- if (!items) {
- newItemSet = null;
+ var initialLoad = (this.itemsData == null);
+
+ // convert to type DataSet when needed
+ var newItemSet;
+ if (!items) {
+ newItemSet = null;
+ }
+ else if (items instanceof DataSet) {
+ newItemSet = items;
+ }
+ if (!(items instanceof DataSet)) {
+ newItemSet = new DataSet({
+ convert: {
+ start: 'Date',
+ end: 'Date'
+ }
+ });
+ newItemSet.add(items);
+ }
+
+ // set items
+ this.itemsData = newItemSet;
+ this.content.setItems(newItemSet);
+
+ if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
+ // apply the data range as range
+ var dataRange = this.getItemRange();
+
+ // add 5% space on both sides
+ var min = dataRange.min;
+ var max = dataRange.max;
+ if (min != null && max != null) {
+ var interval = (max.valueOf() - min.valueOf());
+ if (interval <= 0) {
+ // prevent an empty interval
+ interval = 24 * 60 * 60 * 1000; // 1 day
+ }
+ min = new Date(min.valueOf() - interval * 0.05);
+ max = new Date(max.valueOf() + interval * 0.05);
}
- else if (items instanceof DataSet) {
- newItemSet = items;
+
+ // override specified start and/or end date
+ if (this.options.start != undefined) {
+ min = util.convert(this.options.start, 'Date');
}
- if (!(items instanceof DataSet)) {
- newItemSet = new DataSet({
- convert: {
- start: 'Date',
- end: 'Date'
- }
- });
- newItemSet.add(items);
+ if (this.options.end != undefined) {
+ max = util.convert(this.options.end, 'Date');
}
- // set items
- this.itemsData = newItemSet;
- this.content.setItems(newItemSet);
-
- if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
- // apply the data range as range
- var dataRange = this.getItemRange();
-
- // add 5% space on both sides
- var min = dataRange.min;
- var max = dataRange.max;
- if (min != null && max != null) {
- var interval = (max.valueOf() - min.valueOf());
- if (interval <= 0) {
- // prevent an empty interval
- interval = 24 * 60 * 60 * 1000; // 1 day
- }
- min = new Date(min.valueOf() - interval * 0.05);
- max = new Date(max.valueOf() + interval * 0.05);
- }
-
- // override specified start and/or end date
- if (this.options.start != undefined) {
- min = util.convert(this.options.start, 'Date');
- }
- if (this.options.end != undefined) {
- max = util.convert(this.options.end, 'Date');
- }
-
- // apply range if there is a min or max available
- if (min != null || max != null) {
- this.range.setRange(min, max);
- }
+ // apply range if there is a min or max available
+ if (min != null || max != null) {
+ this.range.setRange(min, max);
}
+ }
};
/**
@@ -230,76 +230,76 @@ Timeline.prototype.setItems = function(items) {
* @param {vis.DataSet | Array | DataTable} groups
*/
Timeline.prototype.setGroups = function(groups) {
- var me = this;
- this.groupsData = groups;
-
- // switch content type between ItemSet or GroupSet when needed
- var Type = this.groupsData ? GroupSet : ItemSet;
- if (!(this.content instanceof Type)) {
- // remove old content set
- if (this.content) {
- this.content.hide();
- if (this.content.setItems) {
- this.content.setItems(); // disconnect from items
- }
- if (this.content.setGroups) {
- this.content.setGroups(); // disconnect from groups
- }
- this.controller.remove(this.content);
- }
+ var me = this;
+ this.groupsData = groups;
+
+ // switch content type between ItemSet or GroupSet when needed
+ var Type = this.groupsData ? GroupSet : ItemSet;
+ if (!(this.content instanceof Type)) {
+ // remove old content set
+ if (this.content) {
+ this.content.hide();
+ if (this.content.setItems) {
+ this.content.setItems(); // disconnect from items
+ }
+ if (this.content.setGroups) {
+ this.content.setGroups(); // disconnect from groups
+ }
+ this.controller.remove(this.content);
+ }
- // create new content set
- var options = Object.create(this.options);
- util.extend(options, {
- top: function () {
- if (me.options.orientation == 'top') {
- return me.timeaxis.height;
- }
- else {
- return me.itemPanel.height - me.timeaxis.height - me.content.height;
- }
- },
- left: null,
- width: '100%',
- height: function () {
- if (me.options.height) {
- // fixed height
- return me.itemPanel.height - me.timeaxis.height;
- }
- else {
- // auto height
- return null;
- }
- },
- maxHeight: function () {
- // TODO: change maxHeight to be a css string like '100%' or '300px'
- if (me.options.maxHeight) {
- if (!util.isNumber(me.options.maxHeight)) {
- throw new TypeError('Number expected for property maxHeight');
- }
- return me.options.maxHeight - me.timeaxis.height;
- }
- else {
- return null;
- }
- },
- labelContainer: function () {
- return me.labelPanel.getContainer();
- }
- });
-
- this.content = new Type(this.itemPanel, [this.timeaxis], options);
- if (this.content.setRange) {
- this.content.setRange(this.range);
+ // create new content set
+ var options = Object.create(this.options);
+ util.extend(options, {
+ top: function () {
+ if (me.options.orientation == 'top') {
+ return me.timeaxis.height;
}
- if (this.content.setItems) {
- this.content.setItems(this.itemsData);
+ else {
+ return me.itemPanel.height - me.timeaxis.height - me.content.height;
}
- if (this.content.setGroups) {
- this.content.setGroups(this.groupsData);
+ },
+ left: null,
+ width: '100%',
+ height: function () {
+ if (me.options.height) {
+ // fixed height
+ return me.itemPanel.height - me.timeaxis.height;
+ }
+ else {
+ // auto height
+ return null;
}
- this.controller.add(this.content);
+ },
+ maxHeight: function () {
+ // TODO: change maxHeight to be a css string like '100%' or '300px'
+ if (me.options.maxHeight) {
+ if (!util.isNumber(me.options.maxHeight)) {
+ throw new TypeError('Number expected for property maxHeight');
+ }
+ return me.options.maxHeight - me.timeaxis.height;
+ }
+ else {
+ return null;
+ }
+ },
+ labelContainer: function () {
+ return me.labelPanel.getContainer();
+ }
+ });
+
+ this.content = new Type(this.itemPanel, [this.timeaxis], options);
+ if (this.content.setRange) {
+ this.content.setRange(this.range);
}
+ if (this.content.setItems) {
+ this.content.setItems(this.itemsData);
+ }
+ if (this.content.setGroups) {
+ this.content.setGroups(this.groupsData);
+ }
+ this.controller.add(this.content);
+ }
};
/**
@@ -309,34 +309,34 @@ Timeline.prototype.setGroups = function(groups) {
* When no maximum is found, max==null
*/
Timeline.prototype.getItemRange = function getItemRange() {
- // calculate min from start filed
- var itemsData = this.itemsData,
- min = null,
- max = null;
-
- if (itemsData) {
- // calculate the minimum value of the field 'start'
- var minItem = itemsData.min('start');
- min = minItem ? minItem.start.valueOf() : null;
-
- // calculate maximum value of fields 'start' and 'end'
- var maxStartItem = itemsData.max('start');
- if (maxStartItem) {
- max = maxStartItem.start.valueOf();
- }
- var maxEndItem = itemsData.max('end');
- if (maxEndItem) {
- if (max == null) {
- max = maxEndItem.end.valueOf();
- }
- else {
- max = Math.max(max, maxEndItem.end.valueOf());
- }
- }
+ // calculate min from start filed
+ var itemsData = this.itemsData,
+ min = null,
+ max = null;
+
+ if (itemsData) {
+ // calculate the minimum value of the field 'start'
+ var minItem = itemsData.min('start');
+ min = minItem ? minItem.start.valueOf() : null;
+
+ // calculate maximum value of fields 'start' and 'end'
+ var maxStartItem = itemsData.max('start');
+ if (maxStartItem) {
+ max = maxStartItem.start.valueOf();
+ }
+ var maxEndItem = itemsData.max('end');
+ if (maxEndItem) {
+ if (max == null) {
+ max = maxEndItem.end.valueOf();
+ }
+ else {
+ max = Math.max(max, maxEndItem.end.valueOf());
+ }
}
+ }
- return {
- min: (min != null) ? new Date(min) : null,
- max: (max != null) ? new Date(max) : null
- };
+ return {
+ min: (min != null) ? new Date(min) : null,
+ max: (max != null) ? new Date(max) : null
+ };
};
diff --git a/src/timeline/component/Component.js b/src/timeline/component/Component.js
index 7c21e369..8d15e0c6 100644
--- a/src/timeline/component/Component.js
+++ b/src/timeline/component/Component.js
@@ -2,17 +2,17 @@
* Prototype for visual components
*/
function Component () {
- this.id = null;
- this.parent = null;
- this.depends = null;
- this.controller = null;
- this.options = null;
+ this.id = null;
+ this.parent = null;
+ this.depends = null;
+ this.controller = null;
+ this.options = null;
- this.frame = null; // main DOM element
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.frame = null; // main DOM element
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
/**
@@ -27,14 +27,14 @@ function Component () {
* {String | Number | function} [height]
*/
Component.prototype.setOptions = function setOptions(options) {
- if (options) {
- util.extend(this.options, options);
+ if (options) {
+ util.extend(this.options, options);
- if (this.controller) {
- this.requestRepaint();
- this.requestReflow();
- }
+ if (this.controller) {
+ this.requestRepaint();
+ this.requestReflow();
}
+ }
};
/**
@@ -45,14 +45,14 @@ Component.prototype.setOptions = function setOptions(options) {
* @return {*} value
*/
Component.prototype.getOption = function getOption(name) {
- var value;
- if (this.options) {
- value = this.options[name];
- }
- if (value === undefined && this.defaultOptions) {
- value = this.defaultOptions[name];
- }
- return value;
+ var value;
+ if (this.options) {
+ value = this.options[name];
+ }
+ if (value === undefined && this.defaultOptions) {
+ value = this.defaultOptions[name];
+ }
+ return value;
};
/**
@@ -63,8 +63,8 @@ Component.prototype.getOption = function getOption(name) {
*/
// TODO: get rid of the getContainer and getFrame methods, provide these via the options
Component.prototype.getContainer = function getContainer() {
- // should be implemented by the component
- return null;
+ // should be implemented by the component
+ return null;
};
/**
@@ -72,7 +72,7 @@ Component.prototype.getContainer = function getContainer() {
* @returns {HTMLElement | null} frame
*/
Component.prototype.getFrame = function getFrame() {
- return this.frame;
+ return this.frame;
};
/**
@@ -80,8 +80,8 @@ Component.prototype.getFrame = function getFrame() {
* @return {Boolean} changed
*/
Component.prototype.repaint = function repaint() {
- // should be implemented by the component
- return false;
+ // should be implemented by the component
+ return false;
};
/**
@@ -89,8 +89,8 @@ Component.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Component.prototype.reflow = function reflow() {
- // should be implemented by the component
- return false;
+ // should be implemented by the component
+ return false;
};
/**
@@ -98,13 +98,13 @@ Component.prototype.reflow = function reflow() {
* @return {Boolean} changed
*/
Component.prototype.hide = function hide() {
- if (this.frame && this.frame.parentNode) {
- this.frame.parentNode.removeChild(this.frame);
- return true;
- }
- else {
- return false;
- }
+ if (this.frame && this.frame.parentNode) {
+ this.frame.parentNode.removeChild(this.frame);
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -113,36 +113,36 @@ Component.prototype.hide = function hide() {
* @return {Boolean} changed
*/
Component.prototype.show = function show() {
- if (!this.frame || !this.frame.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.frame || !this.frame.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
* Request a repaint. The controller will schedule a repaint
*/
Component.prototype.requestRepaint = function requestRepaint() {
- if (this.controller) {
- this.controller.requestRepaint();
- }
- else {
- throw new Error('Cannot request a repaint: no controller configured');
- // TODO: just do a repaint when no parent is configured?
- }
+ if (this.controller) {
+ this.controller.requestRepaint();
+ }
+ else {
+ throw new Error('Cannot request a repaint: no controller configured');
+ // TODO: just do a repaint when no parent is configured?
+ }
};
/**
* Request a reflow. The controller will schedule a reflow
*/
Component.prototype.requestReflow = function requestReflow() {
- if (this.controller) {
- this.controller.requestReflow();
- }
- else {
- throw new Error('Cannot request a reflow: no controller configured');
- // TODO: just do a reflow when no parent is configured?
- }
+ if (this.controller) {
+ this.controller.requestReflow();
+ }
+ else {
+ throw new Error('Cannot request a reflow: no controller configured');
+ // TODO: just do a reflow when no parent is configured?
+ }
};
diff --git a/src/timeline/component/ContentPanel.js b/src/timeline/component/ContentPanel.js
index ff61a6e2..fcc27167 100644
--- a/src/timeline/component/ContentPanel.js
+++ b/src/timeline/component/ContentPanel.js
@@ -14,11 +14,11 @@
* @extends Panel
*/
function ContentPanel(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
+ this.options = options || {};
}
ContentPanel.prototype = new Component();
@@ -40,7 +40,7 @@ ContentPanel.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
ContentPanel.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -48,46 +48,46 @@ ContentPanel.prototype.getContainer = function () {
* @return {Boolean} changed
*/
ContentPanel.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'content-panel';
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ frame = this.frame;
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'content-panel';
- var className = options.className;
- if (className) {
- if (typeof className == 'function') {
- util.addClassName(frame, String(className()));
- }
- else {
- util.addClassName(frame, String(className));
- }
- }
+ var className = options.className;
+ if (className) {
+ if (typeof className == 'function') {
+ util.addClassName(frame, String(className()));
+ }
+ else {
+ util.addClassName(frame, String(className));
+ }
+ }
- this.frame = frame;
- changed += 1;
+ this.frame = frame;
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint panel: no parent attached');
}
- if (!frame.parentNode) {
- if (!this.parent) {
- throw new Error('Cannot repaint panel: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint panel: parent has no container element');
- }
- parentContainer.appendChild(frame);
- changed += 1;
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint panel: parent has no container element');
}
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, '100%'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -95,19 +95,19 @@ ContentPanel.prototype.repaint = function () {
* @return {Boolean} resized
*/
ContentPanel.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame;
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame;
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', frame.offsetHeight);
- }
- else {
- changed += 1;
- }
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', frame.offsetHeight);
+ }
+ else {
+ changed += 1;
+ }
- return (changed > 0);
+ return (changed > 0);
};
diff --git a/src/timeline/component/CurrentTime.js b/src/timeline/component/CurrentTime.js
index a94fd005..ddacf868 100644
--- a/src/timeline/component/CurrentTime.js
+++ b/src/timeline/component/CurrentTime.js
@@ -10,14 +10,14 @@
*/
function CurrentTime (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- this.options = options || {};
- this.defaultOptions = {
- showCurrentTime: false
- };
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ this.options = options || {};
+ this.defaultOptions = {
+ showCurrentTime: false
+ };
}
CurrentTime.prototype = new Component();
@@ -30,7 +30,7 @@ CurrentTime.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
CurrentTime.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -38,64 +38,64 @@ CurrentTime.prototype.getContainer = function () {
* @return {Boolean} changed
*/
CurrentTime.prototype.repaint = function () {
- var bar = this.frame,
- parent = this.parent,
- parentContainer = parent.parent.getContainer();
-
- if (!parent) {
- throw new Error('Cannot repaint bar: no parent attached');
- }
-
- if (!parentContainer) {
- throw new Error('Cannot repaint bar: parent has no container element');
+ var bar = this.frame,
+ parent = this.parent,
+ parentContainer = parent.parent.getContainer();
+
+ if (!parent) {
+ throw new Error('Cannot repaint bar: no parent attached');
+ }
+
+ if (!parentContainer) {
+ throw new Error('Cannot repaint bar: parent has no container element');
+ }
+
+ if (!this.getOption('showCurrentTime')) {
+ if (bar) {
+ parentContainer.removeChild(bar);
+ delete this.frame;
}
- if (!this.getOption('showCurrentTime')) {
- if (bar) {
- parentContainer.removeChild(bar);
- delete this.frame;
- }
+ return;
+ }
- return;
- }
+ if (!bar) {
+ bar = document.createElement('div');
+ bar.className = 'currenttime';
+ bar.style.position = 'absolute';
+ bar.style.top = '0px';
+ bar.style.height = '100%';
- if (!bar) {
- bar = document.createElement('div');
- bar.className = 'currenttime';
- bar.style.position = 'absolute';
- bar.style.top = '0px';
- bar.style.height = '100%';
+ parentContainer.appendChild(bar);
+ this.frame = bar;
+ }
- parentContainer.appendChild(bar);
- this.frame = bar;
- }
+ if (!parent.conversion) {
+ parent._updateConversion();
+ }
- if (!parent.conversion) {
- parent._updateConversion();
- }
+ var now = new Date();
+ var x = parent.toScreen(now);
- var now = new Date();
- var x = parent.toScreen(now);
+ bar.style.left = x + 'px';
+ bar.title = 'Current time: ' + now;
- bar.style.left = x + 'px';
- bar.title = 'Current time: ' + now;
+ // start a timer to adjust for the new time
+ if (this.currentTimeTimer !== undefined) {
+ clearTimeout(this.currentTimeTimer);
+ delete this.currentTimeTimer;
+ }
- // start a timer to adjust for the new time
- if (this.currentTimeTimer !== undefined) {
- clearTimeout(this.currentTimeTimer);
- delete this.currentTimeTimer;
- }
+ var timeline = this;
+ var interval = 1 / parent.conversion.scale / 2;
- var timeline = this;
- var interval = 1 / parent.conversion.scale / 2;
+ if (interval < 30) {
+ interval = 30;
+ }
- if (interval < 30) {
- interval = 30;
- }
-
- this.currentTimeTimer = setTimeout(function() {
- timeline.repaint();
- }, interval);
+ this.currentTimeTimer = setTimeout(function() {
+ timeline.repaint();
+ }, interval);
- return false;
+ return false;
};
diff --git a/src/timeline/component/CustomTime.js b/src/timeline/component/CustomTime.js
index 9efe6892..a5df5ca5 100644
--- a/src/timeline/component/CustomTime.js
+++ b/src/timeline/component/CustomTime.js
@@ -10,17 +10,17 @@
*/
function CustomTime (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
- this.defaultOptions = {
- showCustomTime: false
- };
+ this.options = options || {};
+ this.defaultOptions = {
+ showCustomTime: false
+ };
- this.listeners = [];
- this.customTime = new Date();
+ this.listeners = [];
+ this.customTime = new Date();
}
CustomTime.prototype = new Component();
@@ -33,7 +33,7 @@ CustomTime.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
CustomTime.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -41,59 +41,59 @@ CustomTime.prototype.getContainer = function () {
* @return {Boolean} changed
*/
CustomTime.prototype.repaint = function () {
- var bar = this.frame,
- parent = this.parent,
- parentContainer = parent.parent.getContainer();
-
- if (!parent) {
- throw new Error('Cannot repaint bar: no parent attached');
+ var bar = this.frame,
+ parent = this.parent,
+ parentContainer = parent.parent.getContainer();
+
+ if (!parent) {
+ throw new Error('Cannot repaint bar: no parent attached');
+ }
+
+ if (!parentContainer) {
+ throw new Error('Cannot repaint bar: parent has no container element');
+ }
+
+ if (!this.getOption('showCustomTime')) {
+ if (bar) {
+ parentContainer.removeChild(bar);
+ delete this.frame;
}
- if (!parentContainer) {
- throw new Error('Cannot repaint bar: parent has no container element');
- }
+ return;
+ }
- if (!this.getOption('showCustomTime')) {
- if (bar) {
- parentContainer.removeChild(bar);
- delete this.frame;
- }
+ if (!bar) {
+ bar = document.createElement('div');
+ bar.className = 'customtime';
+ bar.style.position = 'absolute';
+ bar.style.top = '0px';
+ bar.style.height = '100%';
- return;
- }
+ parentContainer.appendChild(bar);
- if (!bar) {
- bar = document.createElement('div');
- bar.className = 'customtime';
- bar.style.position = 'absolute';
- bar.style.top = '0px';
- bar.style.height = '100%';
+ var drag = document.createElement('div');
+ drag.style.position = 'relative';
+ drag.style.top = '0px';
+ drag.style.left = '-10px';
+ drag.style.height = '100%';
+ drag.style.width = '20px';
+ bar.appendChild(drag);
- parentContainer.appendChild(bar);
+ this.frame = bar;
- var drag = document.createElement('div');
- drag.style.position = 'relative';
- drag.style.top = '0px';
- drag.style.left = '-10px';
- drag.style.height = '100%';
- drag.style.width = '20px';
- bar.appendChild(drag);
+ this.subscribe(this, 'movetime');
+ }
- this.frame = bar;
+ if (!parent.conversion) {
+ parent._updateConversion();
+ }
- this.subscribe(this, 'movetime');
- }
-
- if (!parent.conversion) {
- parent._updateConversion();
- }
+ var x = parent.toScreen(this.customTime);
- var x = parent.toScreen(this.customTime);
+ bar.style.left = x + 'px';
+ bar.title = 'Time: ' + this.customTime;
- bar.style.left = x + 'px';
- bar.title = 'Time: ' + this.customTime;
-
- return false;
+ return false;
};
/**
@@ -101,8 +101,8 @@ CustomTime.prototype.repaint = function () {
* @param {Date} time
*/
CustomTime.prototype._setCustomTime = function(time) {
- this.customTime = new Date(time.valueOf());
- this.repaint();
+ this.customTime = new Date(time.valueOf());
+ this.repaint();
};
/**
@@ -110,7 +110,7 @@ CustomTime.prototype._setCustomTime = function(time) {
* @return {Date} customTime
*/
CustomTime.prototype._getCustomTime = function() {
- return new Date(this.customTime.valueOf());
+ return new Date(this.customTime.valueOf());
};
/**
@@ -118,18 +118,18 @@ CustomTime.prototype._getCustomTime = function() {
* @param {Component} component
*/
CustomTime.prototype.subscribe = function (component, event) {
- var me = this;
- var listener = {
- component: component,
- event: event,
- callback: function (event) {
- me._onMouseDown(event, listener);
- },
- params: {}
- };
-
- component.on('mousedown', listener.callback);
- me.listeners.push(listener);
+ var me = this;
+ var listener = {
+ component: component,
+ event: event,
+ callback: function (event) {
+ me._onMouseDown(event, listener);
+ },
+ params: {}
+ };
+
+ component.on('mousedown', listener.callback);
+ me.listeners.push(listener);
};
@@ -140,13 +140,13 @@ CustomTime.prototype.subscribe = function (component, event) {
* as parameter.
*/
CustomTime.prototype.on = function (event, callback) {
- var bar = this.frame;
- if (!bar) {
- throw new Error('Cannot add event listener: no parent attached');
- }
+ var bar = this.frame;
+ if (!bar) {
+ throw new Error('Cannot add event listener: no parent attached');
+ }
- events.addListener(this, event, callback);
- util.addEventListener(bar, event, callback);
+ events.addListener(this, event, callback);
+ util.addEventListener(bar, event, callback);
};
/**
@@ -156,38 +156,38 @@ CustomTime.prototype.on = function (event, callback) {
* @private
*/
CustomTime.prototype._onMouseDown = function(event, listener) {
- event = event || window.event;
- var params = listener.params;
-
- // only react on left mouse button down
- var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
- if (!leftButtonDown) {
- return;
- }
-
- // get mouse position
- params.mouseX = util.getPageX(event);
- params.moved = false;
-
- params.customTime = this.customTime;
-
- // add event listeners to handle moving the custom time bar
- var me = this;
- if (!params.onMouseMove) {
- params.onMouseMove = function (event) {
- me._onMouseMove(event, listener);
- };
- util.addEventListener(document, 'mousemove', params.onMouseMove);
- }
- if (!params.onMouseUp) {
- params.onMouseUp = function (event) {
- me._onMouseUp(event, listener);
- };
- util.addEventListener(document, 'mouseup', params.onMouseUp);
- }
+ event = event || window.event;
+ var params = listener.params;
+
+ // only react on left mouse button down
+ var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
+ if (!leftButtonDown) {
+ return;
+ }
+
+ // get mouse position
+ params.mouseX = util.getPageX(event);
+ params.moved = false;
+
+ params.customTime = this.customTime;
+
+ // add event listeners to handle moving the custom time bar
+ var me = this;
+ if (!params.onMouseMove) {
+ params.onMouseMove = function (event) {
+ me._onMouseMove(event, listener);
+ };
+ util.addEventListener(document, 'mousemove', params.onMouseMove);
+ }
+ if (!params.onMouseUp) {
+ params.onMouseUp = function (event) {
+ me._onMouseUp(event, listener);
+ };
+ util.addEventListener(document, 'mouseup', params.onMouseUp);
+ }
- util.stopPropagation(event);
- util.preventDefault(event);
+ util.stopPropagation(event);
+ util.preventDefault(event);
};
/**
@@ -198,33 +198,33 @@ CustomTime.prototype._onMouseDown = function(event, listener) {
* @private
*/
CustomTime.prototype._onMouseMove = function (event, listener) {
- event = event || window.event;
- var params = listener.params;
- var parent = this.parent;
+ event = event || window.event;
+ var params = listener.params;
+ var parent = this.parent;
- // calculate change in mouse position
- var mouseX = util.getPageX(event);
+ // calculate change in mouse position
+ var mouseX = util.getPageX(event);
- if (params.mouseX === undefined) {
- params.mouseX = mouseX;
- }
+ if (params.mouseX === undefined) {
+ params.mouseX = mouseX;
+ }
- var diff = mouseX - params.mouseX;
+ var diff = mouseX - params.mouseX;
- // if mouse movement is big enough, register it as a "moved" event
- if (Math.abs(diff) >= 1) {
- params.moved = true;
- }
+ // if mouse movement is big enough, register it as a "moved" event
+ if (Math.abs(diff) >= 1) {
+ params.moved = true;
+ }
- var x = parent.toScreen(params.customTime);
- var xnew = x + diff;
- var time = parent.toTime(xnew);
- this._setCustomTime(time);
+ var x = parent.toScreen(params.customTime);
+ var xnew = x + diff;
+ var time = parent.toTime(xnew);
+ this._setCustomTime(time);
- // fire a timechange event
- events.trigger(this, 'timechange', {customTime: this.customTime});
+ // fire a timechange event
+ events.trigger(this, 'timechange', {customTime: this.customTime});
- util.preventDefault(event);
+ util.preventDefault(event);
};
/**
@@ -235,21 +235,21 @@ CustomTime.prototype._onMouseMove = function (event, listener) {
* @private
*/
CustomTime.prototype._onMouseUp = function (event, listener) {
- event = event || window.event;
- var params = listener.params;
-
- // remove event listeners here, important for Safari
- if (params.onMouseMove) {
- util.removeEventListener(document, 'mousemove', params.onMouseMove);
- params.onMouseMove = null;
- }
- if (params.onMouseUp) {
- util.removeEventListener(document, 'mouseup', params.onMouseUp);
- params.onMouseUp = null;
- }
-
- if (params.moved) {
- // fire a timechanged event
- events.trigger(this, 'timechanged', {customTime: this.customTime});
- }
+ event = event || window.event;
+ var params = listener.params;
+
+ // remove event listeners here, important for Safari
+ if (params.onMouseMove) {
+ util.removeEventListener(document, 'mousemove', params.onMouseMove);
+ params.onMouseMove = null;
+ }
+ if (params.onMouseUp) {
+ util.removeEventListener(document, 'mouseup', params.onMouseUp);
+ params.onMouseUp = null;
+ }
+
+ if (params.moved) {
+ // fire a timechanged event
+ events.trigger(this, 'timechanged', {customTime: this.customTime});
+ }
};
diff --git a/src/timeline/component/Group.js b/src/timeline/component/Group.js
index 1198cd3c..74fac5dc 100644
--- a/src/timeline/component/Group.js
+++ b/src/timeline/component/Group.js
@@ -7,25 +7,25 @@
* @extends Component
*/
function Group (parent, groupId, options) {
- this.id = util.randomUUID();
- this.parent = parent;
-
- this.groupId = groupId;
- this.itemset = null; // ItemSet
- this.options = options || {};
- this.options.top = 0;
-
- this.props = {
- label: {
- width: 0,
- height: 0
- }
- };
-
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.id = util.randomUUID();
+ this.parent = parent;
+
+ this.groupId = groupId;
+ this.itemset = null; // ItemSet
+ this.options = options || {};
+ this.options.top = 0;
+
+ this.props = {
+ label: {
+ width: 0,
+ height: 0
+ }
+ };
+
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
Group.prototype = new Component();
@@ -39,7 +39,7 @@ Group.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
Group.prototype.getContainer = function () {
- return this.parent.getContainer();
+ return this.parent.getContainer();
};
/**
@@ -48,31 +48,31 @@ Group.prototype.getContainer = function () {
* @param {DataSet | DataView} items
*/
Group.prototype.setItems = function setItems(items) {
- if (this.itemset) {
- // remove current item set
- this.itemset.hide();
- this.itemset.setItems();
-
- this.parent.controller.remove(this.itemset);
- this.itemset = null;
- }
-
- if (items) {
- var groupId = this.groupId;
-
- var itemsetOptions = Object.create(this.options);
- this.itemset = new ItemSet(this, null, itemsetOptions);
- this.itemset.setRange(this.parent.range);
-
- this.view = new DataView(items, {
- filter: function (item) {
- return item.group == groupId;
- }
- });
- this.itemset.setItems(this.view);
-
- this.parent.controller.add(this.itemset);
- }
+ if (this.itemset) {
+ // remove current item set
+ this.itemset.hide();
+ this.itemset.setItems();
+
+ this.parent.controller.remove(this.itemset);
+ this.itemset = null;
+ }
+
+ if (items) {
+ var groupId = this.groupId;
+
+ var itemsetOptions = Object.create(this.options);
+ this.itemset = new ItemSet(this, null, itemsetOptions);
+ this.itemset.setRange(this.parent.range);
+
+ this.view = new DataView(items, {
+ filter: function (item) {
+ return item.group == groupId;
+ }
+ });
+ this.itemset.setItems(this.view);
+
+ this.parent.controller.add(this.itemset);
+ }
};
/**
@@ -80,7 +80,7 @@ Group.prototype.setItems = function setItems(items) {
* @return {Boolean} changed
*/
Group.prototype.repaint = function repaint() {
- return false;
+ return false;
};
/**
@@ -88,23 +88,23 @@ Group.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Group.prototype.reflow = function reflow() {
- var changed = 0,
- update = util.updateProperty;
+ var changed = 0,
+ update = util.updateProperty;
- changed += update(this, 'top', this.itemset ? this.itemset.top : 0);
- changed += update(this, 'height', this.itemset ? this.itemset.height : 0);
+ changed += update(this, 'top', this.itemset ? this.itemset.top : 0);
+ changed += update(this, 'height', this.itemset ? this.itemset.height : 0);
- // TODO: reckon with the height of the group label
+ // TODO: reckon with the height of the group label
- if (this.label) {
- var inner = this.label.firstChild;
- changed += update(this.props.label, 'width', inner.clientWidth);
- changed += update(this.props.label, 'height', inner.clientHeight);
- }
- else {
- changed += update(this.props.label, 'width', 0);
- changed += update(this.props.label, 'height', 0);
- }
+ if (this.label) {
+ var inner = this.label.firstChild;
+ changed += update(this.props.label, 'width', inner.clientWidth);
+ changed += update(this.props.label, 'height', inner.clientHeight);
+ }
+ else {
+ changed += update(this.props.label, 'width', 0);
+ changed += update(this.props.label, 'height', 0);
+ }
- return (changed > 0);
+ return (changed > 0);
};
diff --git a/src/timeline/component/GroupSet.js b/src/timeline/component/GroupSet.js
index 616bb42c..970a83d6 100644
--- a/src/timeline/component/GroupSet.js
+++ b/src/timeline/component/GroupSet.js
@@ -9,42 +9,42 @@
* @extends Panel
*/
function GroupSet(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
+ this.options = options || {};
- this.range = null; // Range or Object {start: number, end: number}
- this.itemsData = null; // DataSet with items
- this.groupsData = null; // DataSet with groups
+ this.range = null; // Range or Object {start: number, end: number}
+ this.itemsData = null; // DataSet with items
+ this.groupsData = null; // DataSet with groups
- this.groups = {}; // map with groups
+ this.groups = {}; // map with groups
- this.dom = {};
- this.props = {
- labels: {
- width: 0
- }
- };
-
- // TODO: implement right orientation of the labels
-
- // changes in groups are queued key/value map containing id/action
- this.queue = {};
-
- var me = this;
- this.listeners = {
- 'add': function (event, params) {
- me._onAdd(params.items);
- },
- 'update': function (event, params) {
- me._onUpdate(params.items);
- },
- 'remove': function (event, params) {
- me._onRemove(params.items);
- }
- };
+ this.dom = {};
+ this.props = {
+ labels: {
+ width: 0
+ }
+ };
+
+ // TODO: implement right orientation of the labels
+
+ // changes in groups are queued key/value map containing id/action
+ this.queue = {};
+
+ var me = this;
+ this.listeners = {
+ 'add': function (event, params) {
+ me._onAdd(params.items);
+ },
+ 'update': function (event, params) {
+ me._onUpdate(params.items);
+ },
+ 'remove': function (event, params) {
+ me._onRemove(params.items);
+ }
+ };
}
GroupSet.prototype = new Panel();
@@ -58,7 +58,7 @@ GroupSet.prototype = new Panel();
GroupSet.prototype.setOptions = Component.prototype.setOptions;
GroupSet.prototype.setRange = function (range) {
- // TODO: implement setRange
+ // TODO: implement setRange
};
/**
@@ -66,14 +66,14 @@ GroupSet.prototype.setRange = function (range) {
* @param {vis.DataSet | null} items
*/
GroupSet.prototype.setItems = function setItems(items) {
- this.itemsData = items;
+ this.itemsData = items;
- for (var id in this.groups) {
- if (this.groups.hasOwnProperty(id)) {
- var group = this.groups[id];
- group.setItems(items);
- }
+ for (var id in this.groups) {
+ if (this.groups.hasOwnProperty(id)) {
+ var group = this.groups[id];
+ group.setItems(items);
}
+ }
};
/**
@@ -81,7 +81,7 @@ GroupSet.prototype.setItems = function setItems(items) {
* @return {vis.DataSet | null} items
*/
GroupSet.prototype.getItems = function getItems() {
- return this.itemsData;
+ return this.itemsData;
};
/**
@@ -89,7 +89,7 @@ GroupSet.prototype.getItems = function getItems() {
* @param {Range | Object} range A Range or an object containing start and end.
*/
GroupSet.prototype.setRange = function setRange(range) {
- this.range = range;
+ this.range = range;
};
/**
@@ -97,48 +97,48 @@ GroupSet.prototype.setRange = function setRange(range) {
* @param {vis.DataSet} groups
*/
GroupSet.prototype.setGroups = function setGroups(groups) {
- var me = this,
- ids;
-
- // unsubscribe from current dataset
- if (this.groupsData) {
- util.forEach(this.listeners, function (callback, event) {
- me.groupsData.unsubscribe(event, callback);
- });
+ var me = this,
+ ids;
- // remove all drawn groups
- ids = this.groupsData.getIds();
- this._onRemove(ids);
- }
-
- // replace the dataset
- if (!groups) {
- this.groupsData = null;
- }
- else if (groups instanceof DataSet) {
- this.groupsData = groups;
- }
- else {
- this.groupsData = new DataSet({
- convert: {
- start: 'Date',
- end: 'Date'
- }
- });
- this.groupsData.add(groups);
- }
+ // unsubscribe from current dataset
+ if (this.groupsData) {
+ util.forEach(this.listeners, function (callback, event) {
+ me.groupsData.unsubscribe(event, callback);
+ });
- if (this.groupsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.listeners, function (callback, event) {
- me.groupsData.subscribe(event, callback, id);
- });
+ // remove all drawn groups
+ ids = this.groupsData.getIds();
+ this._onRemove(ids);
+ }
+
+ // replace the dataset
+ if (!groups) {
+ this.groupsData = null;
+ }
+ else if (groups instanceof DataSet) {
+ this.groupsData = groups;
+ }
+ else {
+ this.groupsData = new DataSet({
+ convert: {
+ start: 'Date',
+ end: 'Date'
+ }
+ });
+ this.groupsData.add(groups);
+ }
+
+ if (this.groupsData) {
+ // subscribe to new dataset
+ var id = this.id;
+ util.forEach(this.listeners, function (callback, event) {
+ me.groupsData.subscribe(event, callback, id);
+ });
- // draw all new groups
- ids = this.groupsData.getIds();
- this._onAdd(ids);
- }
+ // draw all new groups
+ ids = this.groupsData.getIds();
+ this._onAdd(ids);
+ }
};
/**
@@ -146,7 +146,7 @@ GroupSet.prototype.setGroups = function setGroups(groups) {
* @return {vis.DataSet | null} groups
*/
GroupSet.prototype.getGroups = function getGroups() {
- return this.groupsData;
+ return this.groupsData;
};
/**
@@ -154,179 +154,179 @@ GroupSet.prototype.getGroups = function getGroups() {
* @return {Boolean} changed
*/
GroupSet.prototype.repaint = function repaint() {
- var changed = 0,
- i, id, group, label,
- update = util.updateProperty,
- asSize = util.option.asSize,
- asElement = util.option.asElement,
- options = this.options,
- frame = this.dom.frame,
- labels = this.dom.labels,
- labelSet = this.dom.labelSet;
-
- // create frame
- if (!this.parent) {
- throw new Error('Cannot repaint groupset: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint groupset: parent has no container element');
- }
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'groupset';
- this.dom.frame = frame;
-
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
-
- changed += 1;
- }
- if (!frame.parentNode) {
- parentContainer.appendChild(frame);
- changed += 1;
+ var changed = 0,
+ i, id, group, label,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ asElement = util.option.asElement,
+ options = this.options,
+ frame = this.dom.frame,
+ labels = this.dom.labels,
+ labelSet = this.dom.labelSet;
+
+ // create frame
+ if (!this.parent) {
+ throw new Error('Cannot repaint groupset: no parent attached');
+ }
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint groupset: parent has no container element');
+ }
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'groupset';
+ this.dom.frame = frame;
+
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
}
- // create labels
- var labelContainer = asElement(options.labelContainer);
- if (!labelContainer) {
- throw new Error('Cannot repaint groupset: option "labelContainer" not defined');
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
+
+ // create labels
+ var labelContainer = asElement(options.labelContainer);
+ if (!labelContainer) {
+ throw new Error('Cannot repaint groupset: option "labelContainer" not defined');
+ }
+ if (!labels) {
+ labels = document.createElement('div');
+ labels.className = 'labels';
+ this.dom.labels = labels;
+ }
+ if (!labelSet) {
+ labelSet = document.createElement('div');
+ labelSet.className = 'label-set';
+ labels.appendChild(labelSet);
+ this.dom.labelSet = labelSet;
+ }
+ if (!labels.parentNode || labels.parentNode != labelContainer) {
+ if (labels.parentNode) {
+ labels.parentNode.removeChild(labels.parentNode);
}
- if (!labels) {
- labels = document.createElement('div');
- labels.className = 'labels';
- this.dom.labels = labels;
- }
- if (!labelSet) {
- labelSet = document.createElement('div');
- labelSet.className = 'label-set';
- labels.appendChild(labelSet);
- this.dom.labelSet = labelSet;
- }
- if (!labels.parentNode || labels.parentNode != labelContainer) {
- if (labels.parentNode) {
- labels.parentNode.removeChild(labels.parentNode);
- }
- labelContainer.appendChild(labels);
- }
-
- // reposition frame
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
-
- // reposition labels
- changed += update(labelSet.style, 'top', asSize(options.top, '0px'));
- changed += update(labelSet.style, 'height', asSize(options.height, this.height + 'px'));
-
- var me = this,
- queue = this.queue,
- groups = this.groups,
- groupsData = this.groupsData;
-
- // show/hide added/changed/removed groups
- var ids = Object.keys(queue);
- if (ids.length) {
- ids.forEach(function (id) {
- var action = queue[id];
- var group = groups[id];
-
- //noinspection FallthroughInSwitchStatementJS
- switch (action) {
- case 'add':
- case 'update':
- if (!group) {
- var groupOptions = Object.create(me.options);
- util.extend(groupOptions, {
- height: null,
- maxHeight: null
- });
-
- group = new Group(me, id, groupOptions);
- group.setItems(me.itemsData); // attach items data
- groups[id] = group;
-
- me.controller.add(group);
- }
-
- // TODO: update group data
- group.data = groupsData.get(id);
-
- delete queue[id];
- break;
-
- case 'remove':
- if (group) {
- group.setItems(); // detach items data
- delete groups[id];
-
- me.controller.remove(group);
- }
-
- // update lists
- delete queue[id];
- break;
-
- default:
- console.log('Error: unknown action "' + action + '"');
- }
- });
-
- // the groupset depends on each of the groups
- //this.depends = this.groups; // TODO: gives a circular reference through the parent
+ labelContainer.appendChild(labels);
+ }
+
+ // reposition frame
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+
+ // reposition labels
+ changed += update(labelSet.style, 'top', asSize(options.top, '0px'));
+ changed += update(labelSet.style, 'height', asSize(options.height, this.height + 'px'));
+
+ var me = this,
+ queue = this.queue,
+ groups = this.groups,
+ groupsData = this.groupsData;
+
+ // show/hide added/changed/removed groups
+ var ids = Object.keys(queue);
+ if (ids.length) {
+ ids.forEach(function (id) {
+ var action = queue[id];
+ var group = groups[id];
+
+ //noinspection FallthroughInSwitchStatementJS
+ switch (action) {
+ case 'add':
+ case 'update':
+ if (!group) {
+ var groupOptions = Object.create(me.options);
+ util.extend(groupOptions, {
+ height: null,
+ maxHeight: null
+ });
+
+ group = new Group(me, id, groupOptions);
+ group.setItems(me.itemsData); // attach items data
+ groups[id] = group;
+
+ me.controller.add(group);
+ }
+
+ // TODO: update group data
+ group.data = groupsData.get(id);
+
+ delete queue[id];
+ break;
+
+ case 'remove':
+ if (group) {
+ group.setItems(); // detach items data
+ delete groups[id];
+
+ me.controller.remove(group);
+ }
+
+ // update lists
+ delete queue[id];
+ break;
+
+ default:
+ console.log('Error: unknown action "' + action + '"');
+ }
+ });
- // TODO: apply dependencies of the groupset
+ // the groupset depends on each of the groups
+ //this.depends = this.groups; // TODO: gives a circular reference through the parent
- // update the top positions of the groups in the correct order
- var orderedGroups = this.groupsData.getIds({
- order: this.options.groupOrder
- });
- for (i = 0; i < orderedGroups.length; i++) {
- (function (group, prevGroup) {
- var top = 0;
- if (prevGroup) {
- top = function () {
- // TODO: top must reckon with options.maxHeight
- return prevGroup.top + prevGroup.height;
- }
- }
- group.setOptions({
- top: top
- });
- })(groups[orderedGroups[i]], groups[orderedGroups[i - 1]]);
- }
+ // TODO: apply dependencies of the groupset
- // (re)create the labels
- while (labelSet.firstChild) {
- labelSet.removeChild(labelSet.firstChild);
- }
- for (i = 0; i < orderedGroups.length; i++) {
- id = orderedGroups[i];
- label = this._createLabel(id);
- labelSet.appendChild(label);
+ // update the top positions of the groups in the correct order
+ var orderedGroups = this.groupsData.getIds({
+ order: this.options.groupOrder
+ });
+ for (i = 0; i < orderedGroups.length; i++) {
+ (function (group, prevGroup) {
+ var top = 0;
+ if (prevGroup) {
+ top = function () {
+ // TODO: top must reckon with options.maxHeight
+ return prevGroup.top + prevGroup.height;
+ }
}
+ group.setOptions({
+ top: top
+ });
+ })(groups[orderedGroups[i]], groups[orderedGroups[i - 1]]);
+ }
- changed++;
+ // (re)create the labels
+ while (labelSet.firstChild) {
+ labelSet.removeChild(labelSet.firstChild);
+ }
+ for (i = 0; i < orderedGroups.length; i++) {
+ id = orderedGroups[i];
+ label = this._createLabel(id);
+ labelSet.appendChild(label);
}
- // reposition the labels
- // TODO: labels are not displayed correctly when orientation=='top'
- // TODO: width of labelPanel is not immediately updated on a change in groups
- for (id in groups) {
- if (groups.hasOwnProperty(id)) {
- group = groups[id];
- label = group.label;
- if (label) {
- label.style.top = group.top + 'px';
- label.style.height = group.height + 'px';
- }
- }
+ changed++;
+ }
+
+ // reposition the labels
+ // TODO: labels are not displayed correctly when orientation=='top'
+ // TODO: width of labelPanel is not immediately updated on a change in groups
+ for (id in groups) {
+ if (groups.hasOwnProperty(id)) {
+ group = groups[id];
+ label = group.label;
+ if (label) {
+ label.style.top = group.top + 'px';
+ label.style.height = group.height + 'px';
+ }
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -336,29 +336,29 @@ GroupSet.prototype.repaint = function repaint() {
* @private
*/
GroupSet.prototype._createLabel = function(id) {
- var group = this.groups[id];
- var label = document.createElement('div');
- label.className = 'label';
- var inner = document.createElement('div');
- inner.className = 'inner';
- label.appendChild(inner);
-
- var content = group.data && group.data.content;
- if (content instanceof Element) {
- inner.appendChild(content);
- }
- else if (content != undefined) {
- inner.innerHTML = content;
- }
-
- var className = group.data && group.data.className;
- if (className) {
- util.addClassName(label, className);
- }
-
- group.label = label; // TODO: not so nice, parking labels in the group this way!!!
-
- return label;
+ var group = this.groups[id];
+ var label = document.createElement('div');
+ label.className = 'label';
+ var inner = document.createElement('div');
+ inner.className = 'inner';
+ label.appendChild(inner);
+
+ var content = group.data && group.data.content;
+ if (content instanceof Element) {
+ inner.appendChild(content);
+ }
+ else if (content != undefined) {
+ inner.innerHTML = content;
+ }
+
+ var className = group.data && group.data.className;
+ if (className) {
+ util.addClassName(label, className);
+ }
+
+ group.label = label; // TODO: not so nice, parking labels in the group this way!!!
+
+ return label;
};
/**
@@ -366,7 +366,7 @@ GroupSet.prototype._createLabel = function(id) {
* @return {HTMLElement} container
*/
GroupSet.prototype.getContainer = function getContainer() {
- return this.dom.frame;
+ return this.dom.frame;
};
/**
@@ -374,7 +374,7 @@ GroupSet.prototype.getContainer = function getContainer() {
* @return {Number} width
*/
GroupSet.prototype.getLabelsWidth = function getContainer() {
- return this.props.labels.width;
+ return this.props.labels.width;
};
/**
@@ -382,54 +382,54 @@ GroupSet.prototype.getLabelsWidth = function getContainer() {
* @return {Boolean} resized
*/
GroupSet.prototype.reflow = function reflow() {
- var changed = 0,
- id, group,
- options = this.options,
- update = util.updateProperty,
- asNumber = util.option.asNumber,
- asSize = util.option.asSize,
- frame = this.dom.frame;
-
- if (frame) {
- var maxHeight = asNumber(options.maxHeight);
- var fixedHeight = (asSize(options.height) != null);
- var height;
- if (fixedHeight) {
- height = frame.offsetHeight;
- }
- else {
- // height is not specified, calculate the sum of the height of all groups
- height = 0;
-
- for (id in this.groups) {
- if (this.groups.hasOwnProperty(id)) {
- group = this.groups[id];
- height += group.height;
- }
- }
- }
- if (maxHeight != null) {
- height = Math.min(height, maxHeight);
- }
- changed += update(this, 'height', height);
-
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
+ var changed = 0,
+ id, group,
+ options = this.options,
+ update = util.updateProperty,
+ asNumber = util.option.asNumber,
+ asSize = util.option.asSize,
+ frame = this.dom.frame;
+
+ if (frame) {
+ var maxHeight = asNumber(options.maxHeight);
+ var fixedHeight = (asSize(options.height) != null);
+ var height;
+ if (fixedHeight) {
+ height = frame.offsetHeight;
}
+ else {
+ // height is not specified, calculate the sum of the height of all groups
+ height = 0;
- // calculate the maximum width of the labels
- var width = 0;
- for (id in this.groups) {
+ for (id in this.groups) {
if (this.groups.hasOwnProperty(id)) {
- group = this.groups[id];
- var labelWidth = group.props && group.props.label && group.props.label.width || 0;
- width = Math.max(width, labelWidth);
+ group = this.groups[id];
+ height += group.height;
}
+ }
}
- changed += update(this.props.labels, 'width', width);
+ if (maxHeight != null) {
+ height = Math.min(height, maxHeight);
+ }
+ changed += update(this, 'height', height);
+
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ }
+
+ // calculate the maximum width of the labels
+ var width = 0;
+ for (id in this.groups) {
+ if (this.groups.hasOwnProperty(id)) {
+ group = this.groups[id];
+ var labelWidth = group.props && group.props.label && group.props.label.width || 0;
+ width = Math.max(width, labelWidth);
+ }
+ }
+ changed += update(this.props.labels, 'width', width);
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -437,13 +437,13 @@ GroupSet.prototype.reflow = function reflow() {
* @return {Boolean} changed
*/
GroupSet.prototype.hide = function hide() {
- if (this.dom.frame && this.dom.frame.parentNode) {
- this.dom.frame.parentNode.removeChild(this.dom.frame);
- return true;
- }
- else {
- return false;
- }
+ if (this.dom.frame && this.dom.frame.parentNode) {
+ this.dom.frame.parentNode.removeChild(this.dom.frame);
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -452,12 +452,12 @@ GroupSet.prototype.hide = function hide() {
* @return {Boolean} changed
*/
GroupSet.prototype.show = function show() {
- if (!this.dom.frame || !this.dom.frame.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom.frame || !this.dom.frame.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -466,7 +466,7 @@ GroupSet.prototype.show = function show() {
* @private
*/
GroupSet.prototype._onUpdate = function _onUpdate(ids) {
- this._toQueue(ids, 'update');
+ this._toQueue(ids, 'update');
};
/**
@@ -475,7 +475,7 @@ GroupSet.prototype._onUpdate = function _onUpdate(ids) {
* @private
*/
GroupSet.prototype._onAdd = function _onAdd(ids) {
- this._toQueue(ids, 'add');
+ this._toQueue(ids, 'add');
};
/**
@@ -484,7 +484,7 @@ GroupSet.prototype._onAdd = function _onAdd(ids) {
* @private
*/
GroupSet.prototype._onRemove = function _onRemove(ids) {
- this._toQueue(ids, 'remove');
+ this._toQueue(ids, 'remove');
};
/**
@@ -493,13 +493,13 @@ GroupSet.prototype._onRemove = function _onRemove(ids) {
* @param {String} action can be 'add', 'update', 'remove'
*/
GroupSet.prototype._toQueue = function _toQueue(ids, action) {
- var queue = this.queue;
- ids.forEach(function (id) {
- queue[id] = action;
- });
-
- if (this.controller) {
- //this.requestReflow();
- this.requestRepaint();
- }
+ var queue = this.queue;
+ ids.forEach(function (id) {
+ queue[id] = action;
+ });
+
+ if (this.controller) {
+ //this.requestReflow();
+ this.requestRepaint();
+ }
};
diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js
index 6973a375..f68a3572 100644
--- a/src/timeline/component/ItemSet.js
+++ b/src/timeline/component/ItemSet.js
@@ -12,63 +12,63 @@
*/
// TODO: improve performance by replacing all Array.forEach with a for loop
function ItemSet(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- // one options object is shared by this itemset and all its items
- this.options = options || {};
- this.defaultOptions = {
- type: 'box',
- align: 'center',
- orientation: 'bottom',
- margin: {
- axis: 20,
- item: 10
- },
- padding: 5
- };
-
- this.dom = {};
-
- var me = this;
- this.itemsData = null; // DataSet
- this.range = null; // Range or Object {start: number, end: number}
-
- this.listeners = {
- 'add': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onAdd(params.items);
- }
- },
- 'update': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onUpdate(params.items);
- }
- },
- 'remove': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onRemove(params.items);
- }
- }
- };
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ // one options object is shared by this itemset and all its items
+ this.options = options || {};
+ this.defaultOptions = {
+ type: 'box',
+ align: 'center',
+ orientation: 'bottom',
+ margin: {
+ axis: 20,
+ item: 10
+ },
+ padding: 5
+ };
+
+ this.dom = {};
+
+ var me = this;
+ this.itemsData = null; // DataSet
+ this.range = null; // Range or Object {start: number, end: number}
+
+ this.listeners = {
+ 'add': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onAdd(params.items);
+ }
+ },
+ 'update': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onUpdate(params.items);
+ }
+ },
+ 'remove': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onRemove(params.items);
+ }
+ }
+ };
- this.items = {}; // object with an Item for every data item
- this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
- this.stack = new Stack(this, Object.create(this.options));
- this.conversion = null;
+ this.items = {}; // object with an Item for every data item
+ this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
+ this.stack = new Stack(this, Object.create(this.options));
+ this.conversion = null;
- // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
+ // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
}
ItemSet.prototype = new Panel();
// available item types will be registered here
ItemSet.types = {
- box: ItemBox,
- range: ItemRange,
- rangeoverflow: ItemRangeOverflow,
- point: ItemPoint
+ box: ItemBox,
+ range: ItemRange,
+ rangeoverflow: ItemRangeOverflow,
+ point: ItemPoint
};
/**
@@ -103,11 +103,11 @@ ItemSet.prototype.setOptions = Component.prototype.setOptions;
* @param {Range | Object} range A Range or an object containing start and end.
*/
ItemSet.prototype.setRange = function setRange(range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
- }
- this.range = range;
+ if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
+ throw new TypeError('Range must be an instance of Range, ' +
+ 'or an object containing start and end.');
+ }
+ this.range = range;
};
/**
@@ -115,170 +115,170 @@ ItemSet.prototype.setRange = function setRange(range) {
* @return {Boolean} changed
*/
ItemSet.prototype.repaint = function repaint() {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- orientation = this.getOption('orientation'),
- defaultOptions = this.defaultOptions,
- frame = this.frame;
-
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'itemset';
-
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
-
- // create background panel
- var background = document.createElement('div');
- background.className = 'background';
- frame.appendChild(background);
- this.dom.background = background;
-
- // create foreground panel
- var foreground = document.createElement('div');
- foreground.className = 'foreground';
- frame.appendChild(foreground);
- this.dom.foreground = foreground;
-
- // create axis panel
- var axis = document.createElement('div');
- axis.className = 'itemset-axis';
- //frame.appendChild(axis);
- this.dom.axis = axis;
-
- this.frame = frame;
- changed += 1;
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ orientation = this.getOption('orientation'),
+ defaultOptions = this.defaultOptions,
+ frame = this.frame;
+
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'itemset';
+
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
}
- if (!this.parent) {
- throw new Error('Cannot repaint itemset: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint itemset: parent has no container element');
- }
- if (!frame.parentNode) {
- parentContainer.appendChild(frame);
- changed += 1;
- }
- if (!this.dom.axis.parentNode) {
- parentContainer.appendChild(this.dom.axis);
- changed += 1;
- }
+ // create background panel
+ var background = document.createElement('div');
+ background.className = 'background';
+ frame.appendChild(background);
+ this.dom.background = background;
+
+ // create foreground panel
+ var foreground = document.createElement('div');
+ foreground.className = 'foreground';
+ frame.appendChild(foreground);
+ this.dom.foreground = foreground;
+
+ // create axis panel
+ var axis = document.createElement('div');
+ axis.className = 'itemset-axis';
+ //frame.appendChild(axis);
+ this.dom.axis = axis;
+
+ this.frame = frame;
+ changed += 1;
+ }
+
+ if (!this.parent) {
+ throw new Error('Cannot repaint itemset: no parent attached');
+ }
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint itemset: parent has no container element');
+ }
+ if (!frame.parentNode) {
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
+ if (!this.dom.axis.parentNode) {
+ parentContainer.appendChild(this.dom.axis);
+ changed += 1;
+ }
+
+ // reposition frame
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+
+ // reposition axis
+ changed += update(this.dom.axis.style, 'left', asSize(options.left, '0px'));
+ changed += update(this.dom.axis.style, 'width', asSize(options.width, '100%'));
+ if (orientation == 'bottom') {
+ changed += update(this.dom.axis.style, 'top', (this.height + this.top) + 'px');
+ }
+ else { // orientation == 'top'
+ changed += update(this.dom.axis.style, 'top', this.top + 'px');
+ }
+
+ this._updateConversion();
+
+ var me = this,
+ queue = this.queue,
+ itemsData = this.itemsData,
+ items = this.items,
+ dataOptions = {
+ // TODO: cleanup
+ // fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
+ };
+
+ // show/hide added/changed/removed items
+ Object.keys(queue).forEach(function (id) {
+ //var entry = queue[id];
+ var action = queue[id];
+ var item = items[id];
+ //var item = entry.item;
+ //noinspection FallthroughInSwitchStatementJS
+ switch (action) {
+ case 'add':
+ case 'update':
+ var itemData = itemsData && itemsData.get(id, dataOptions);
+
+ if (itemData) {
+ var type = itemData.type ||
+ (itemData.start && itemData.end && 'range') ||
+ options.type ||
+ 'box';
+ var constructor = ItemSet.types[type];
+
+ // TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
+ if (item) {
+ // update item
+ if (!constructor || !(item instanceof constructor)) {
+ // item type has changed, hide and delete the item
+ changed += item.hide();
+ item = null;
+ }
+ else {
+ item.data = itemData; // TODO: create a method item.setData ?
+ changed++;
+ }
+ }
- // reposition frame
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
-
- // reposition axis
- changed += update(this.dom.axis.style, 'left', asSize(options.left, '0px'));
- changed += update(this.dom.axis.style, 'width', asSize(options.width, '100%'));
- if (orientation == 'bottom') {
- changed += update(this.dom.axis.style, 'top', (this.height + this.top) + 'px');
- }
- else { // orientation == 'top'
- changed += update(this.dom.axis.style, 'top', this.top + 'px');
- }
+ if (!item) {
+ // create item
+ if (constructor) {
+ item = new constructor(me, itemData, options, defaultOptions);
+ changed++;
+ }
+ else {
+ throw new TypeError('Unknown item type "' + type + '"');
+ }
+ }
- this._updateConversion();
+ // force a repaint (not only a reposition)
+ item.repaint();
- var me = this,
- queue = this.queue,
- itemsData = this.itemsData,
- items = this.items,
- dataOptions = {
- // TODO: cleanup
- // fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
- };
-
- // show/hide added/changed/removed items
- Object.keys(queue).forEach(function (id) {
- //var entry = queue[id];
- var action = queue[id];
- var item = items[id];
- //var item = entry.item;
- //noinspection FallthroughInSwitchStatementJS
- switch (action) {
- case 'add':
- case 'update':
- var itemData = itemsData && itemsData.get(id, dataOptions);
-
- if (itemData) {
- var type = itemData.type ||
- (itemData.start && itemData.end && 'range') ||
- options.type ||
- 'box';
- var constructor = ItemSet.types[type];
-
- // TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
- if (item) {
- // update item
- if (!constructor || !(item instanceof constructor)) {
- // item type has changed, hide and delete the item
- changed += item.hide();
- item = null;
- }
- else {
- item.data = itemData; // TODO: create a method item.setData ?
- changed++;
- }
- }
-
- if (!item) {
- // create item
- if (constructor) {
- item = new constructor(me, itemData, options, defaultOptions);
- changed++;
- }
- else {
- throw new TypeError('Unknown item type "' + type + '"');
- }
- }
-
- // force a repaint (not only a reposition)
- item.repaint();
-
- items[id] = item;
- }
-
- // update queue
- delete queue[id];
- break;
-
- case 'remove':
- if (item) {
- // remove DOM of the item
- changed += item.hide();
- }
-
- // update lists
- delete items[id];
- delete queue[id];
- break;
-
- default:
- console.log('Error: unknown action "' + action + '"');
+ items[id] = item;
}
- });
- // reposition all items. Show items only when in the visible area
- util.forEach(this.items, function (item) {
- if (item.visible) {
- changed += item.show();
- item.reposition();
- }
- else {
- changed += item.hide();
+ // update queue
+ delete queue[id];
+ break;
+
+ case 'remove':
+ if (item) {
+ // remove DOM of the item
+ changed += item.hide();
}
- });
- return (changed > 0);
+ // update lists
+ delete items[id];
+ delete queue[id];
+ break;
+
+ default:
+ console.log('Error: unknown action "' + action + '"');
+ }
+ });
+
+ // reposition all items. Show items only when in the visible area
+ util.forEach(this.items, function (item) {
+ if (item.visible) {
+ changed += item.show();
+ item.reposition();
+ }
+ else {
+ changed += item.hide();
+ }
+ });
+
+ return (changed > 0);
};
/**
@@ -286,7 +286,7 @@ ItemSet.prototype.repaint = function repaint() {
* @return {HTMLElement} foreground
*/
ItemSet.prototype.getForeground = function getForeground() {
- return this.dom.foreground;
+ return this.dom.foreground;
};
/**
@@ -294,7 +294,7 @@ ItemSet.prototype.getForeground = function getForeground() {
* @return {HTMLElement} background
*/
ItemSet.prototype.getBackground = function getBackground() {
- return this.dom.background;
+ return this.dom.background;
};
/**
@@ -302,7 +302,7 @@ ItemSet.prototype.getBackground = function getBackground() {
* @return {HTMLElement} axis
*/
ItemSet.prototype.getAxis = function getAxis() {
- return this.dom.axis;
+ return this.dom.axis;
};
/**
@@ -310,63 +310,63 @@ ItemSet.prototype.getAxis = function getAxis() {
* @return {Boolean} resized
*/
ItemSet.prototype.reflow = function reflow () {
- var changed = 0,
- options = this.options,
- marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
- marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
- update = util.updateProperty,
- asNumber = util.option.asNumber,
- asSize = util.option.asSize,
- frame = this.frame;
-
- if (frame) {
- this._updateConversion();
-
- util.forEach(this.items, function (item) {
- changed += item.reflow();
- });
+ var changed = 0,
+ options = this.options,
+ marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
+ marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
+ update = util.updateProperty,
+ asNumber = util.option.asNumber,
+ asSize = util.option.asSize,
+ frame = this.frame;
+
+ if (frame) {
+ this._updateConversion();
- // TODO: stack.update should be triggered via an event, in stack itself
- // TODO: only update the stack when there are changed items
- this.stack.update();
+ util.forEach(this.items, function (item) {
+ changed += item.reflow();
+ });
- var maxHeight = asNumber(options.maxHeight);
- var fixedHeight = (asSize(options.height) != null);
- var height;
- if (fixedHeight) {
- height = frame.offsetHeight;
- }
- else {
- // height is not specified, determine the height from the height and positioned items
- var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
- if (visibleItems.length) {
- var min = visibleItems[0].top;
- var max = visibleItems[0].top + visibleItems[0].height;
- util.forEach(visibleItems, function (item) {
- min = Math.min(min, item.top);
- max = Math.max(max, (item.top + item.height));
- });
- height = (max - min) + marginAxis + marginItem;
- }
- else {
- height = marginAxis + marginItem;
- }
- }
- if (maxHeight != null) {
- height = Math.min(height, maxHeight);
- }
- changed += update(this, 'height', height);
+ // TODO: stack.update should be triggered via an event, in stack itself
+ // TODO: only update the stack when there are changed items
+ this.stack.update();
- // calculate height from items
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
+ var maxHeight = asNumber(options.maxHeight);
+ var fixedHeight = (asSize(options.height) != null);
+ var height;
+ if (fixedHeight) {
+ height = frame.offsetHeight;
}
else {
- changed += 1;
+ // height is not specified, determine the height from the height and positioned items
+ var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
+ if (visibleItems.length) {
+ var min = visibleItems[0].top;
+ var max = visibleItems[0].top + visibleItems[0].height;
+ util.forEach(visibleItems, function (item) {
+ min = Math.min(min, item.top);
+ max = Math.max(max, (item.top + item.height));
+ });
+ height = (max - min) + marginAxis + marginItem;
+ }
+ else {
+ height = marginAxis + marginItem;
+ }
}
-
- return (changed > 0);
+ if (maxHeight != null) {
+ height = Math.min(height, maxHeight);
+ }
+ changed += update(this, 'height', height);
+
+ // calculate height from items
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ }
+ else {
+ changed += 1;
+ }
+
+ return (changed > 0);
};
/**
@@ -374,19 +374,19 @@ ItemSet.prototype.reflow = function reflow () {
* @return {Boolean} changed
*/
ItemSet.prototype.hide = function hide() {
- var changed = false;
-
- // remove the DOM
- if (this.frame && this.frame.parentNode) {
- this.frame.parentNode.removeChild(this.frame);
- changed = true;
- }
- if (this.dom.axis && this.dom.axis.parentNode) {
- this.dom.axis.parentNode.removeChild(this.dom.axis);
- changed = true;
- }
-
- return changed;
+ var changed = false;
+
+ // remove the DOM
+ if (this.frame && this.frame.parentNode) {
+ this.frame.parentNode.removeChild(this.frame);
+ changed = true;
+ }
+ if (this.dom.axis && this.dom.axis.parentNode) {
+ this.dom.axis.parentNode.removeChild(this.dom.axis);
+ changed = true;
+ }
+
+ return changed;
};
/**
@@ -394,43 +394,43 @@ ItemSet.prototype.hide = function hide() {
* @param {vis.DataSet | null} items
*/
ItemSet.prototype.setItems = function setItems(items) {
- var me = this,
- ids,
- oldItemsData = this.itemsData;
-
- // replace the dataset
- if (!items) {
- this.itemsData = null;
- }
- else if (items instanceof DataSet || items instanceof DataView) {
- this.itemsData = items;
- }
- else {
- throw new TypeError('Data must be an instance of DataSet');
- }
+ var me = this,
+ ids,
+ oldItemsData = this.itemsData;
+
+ // replace the dataset
+ if (!items) {
+ this.itemsData = null;
+ }
+ else if (items instanceof DataSet || items instanceof DataView) {
+ this.itemsData = items;
+ }
+ else {
+ throw new TypeError('Data must be an instance of DataSet');
+ }
+
+ if (oldItemsData) {
+ // unsubscribe from old dataset
+ util.forEach(this.listeners, function (callback, event) {
+ oldItemsData.unsubscribe(event, callback);
+ });
- if (oldItemsData) {
- // unsubscribe from old dataset
- util.forEach(this.listeners, function (callback, event) {
- oldItemsData.unsubscribe(event, callback);
- });
+ // remove all drawn items
+ ids = oldItemsData.getIds();
+ this._onRemove(ids);
+ }
- // remove all drawn items
- ids = oldItemsData.getIds();
- this._onRemove(ids);
- }
-
- if (this.itemsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.listeners, function (callback, event) {
- me.itemsData.subscribe(event, callback, id);
- });
+ if (this.itemsData) {
+ // subscribe to new dataset
+ var id = this.id;
+ util.forEach(this.listeners, function (callback, event) {
+ me.itemsData.subscribe(event, callback, id);
+ });
- // draw all new items
- ids = this.itemsData.getIds();
- this._onAdd(ids);
- }
+ // draw all new items
+ ids = this.itemsData.getIds();
+ this._onAdd(ids);
+ }
};
/**
@@ -438,7 +438,7 @@ ItemSet.prototype.setItems = function setItems(items) {
* @returns {vis.DataSet | null}
*/
ItemSet.prototype.getItems = function getItems() {
- return this.itemsData;
+ return this.itemsData;
};
/**
@@ -447,7 +447,7 @@ ItemSet.prototype.getItems = function getItems() {
* @private
*/
ItemSet.prototype._onUpdate = function _onUpdate(ids) {
- this._toQueue('update', ids);
+ this._toQueue('update', ids);
};
/**
@@ -456,7 +456,7 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
* @private
*/
ItemSet.prototype._onAdd = function _onAdd(ids) {
- this._toQueue('add', ids);
+ this._toQueue('add', ids);
};
/**
@@ -465,7 +465,7 @@ ItemSet.prototype._onAdd = function _onAdd(ids) {
* @private
*/
ItemSet.prototype._onRemove = function _onRemove(ids) {
- this._toQueue('remove', ids);
+ this._toQueue('remove', ids);
};
/**
@@ -474,15 +474,15 @@ ItemSet.prototype._onRemove = function _onRemove(ids) {
* @param {Number[]} ids
*/
ItemSet.prototype._toQueue = function _toQueue(action, ids) {
- var queue = this.queue;
- ids.forEach(function (id) {
- queue[id] = action;
- });
-
- if (this.controller) {
- //this.requestReflow();
- this.requestRepaint();
- }
+ var queue = this.queue;
+ ids.forEach(function (id) {
+ queue[id] = action;
+ });
+
+ if (this.controller) {
+ //this.requestReflow();
+ this.requestRepaint();
+ }
};
/**
@@ -493,17 +493,17 @@ ItemSet.prototype._toQueue = function _toQueue(action, ids) {
* @private
*/
ItemSet.prototype._updateConversion = function _updateConversion() {
- var range = this.range;
- if (!range) {
- throw new Error('No range configured');
- }
-
- if (range.conversion) {
- this.conversion = range.conversion(this.width);
- }
- else {
- this.conversion = Range.conversion(range.start, range.end, this.width);
- }
+ var range = this.range;
+ if (!range) {
+ throw new Error('No range configured');
+ }
+
+ if (range.conversion) {
+ this.conversion = range.conversion(this.width);
+ }
+ else {
+ this.conversion = Range.conversion(range.start, range.end, this.width);
+ }
};
/**
@@ -514,8 +514,8 @@ ItemSet.prototype._updateConversion = function _updateConversion() {
* @return {Date} time The datetime the corresponds with given position x
*/
ItemSet.prototype.toTime = function toTime(x) {
- var conversion = this.conversion;
- return new Date(x / conversion.scale + conversion.offset);
+ var conversion = this.conversion;
+ return new Date(x / conversion.scale + conversion.offset);
};
/**
@@ -527,6 +527,6 @@ ItemSet.prototype.toTime = function toTime(x) {
* with the given date.
*/
ItemSet.prototype.toScreen = function toScreen(time) {
- var conversion = this.conversion;
- return (time.valueOf() - conversion.offset) * conversion.scale;
+ var conversion = this.conversion;
+ return (time.valueOf() - conversion.offset) * conversion.scale;
};
diff --git a/src/timeline/component/Panel.js b/src/timeline/component/Panel.js
index 53b41412..e3badfae 100644
--- a/src/timeline/component/Panel.js
+++ b/src/timeline/component/Panel.js
@@ -13,11 +13,11 @@
* @extends Component
*/
function Panel(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
+ this.options = options || {};
}
Panel.prototype = new Component();
@@ -39,7 +39,7 @@ Panel.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
Panel.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -47,46 +47,46 @@ Panel.prototype.getContainer = function () {
* @return {Boolean} changed
*/
Panel.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'panel';
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ frame = this.frame;
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'panel';
- var className = options.className;
- if (className) {
- if (typeof className == 'function') {
- util.addClassName(frame, String(className()));
- }
- else {
- util.addClassName(frame, String(className));
- }
- }
+ var className = options.className;
+ if (className) {
+ if (typeof className == 'function') {
+ util.addClassName(frame, String(className()));
+ }
+ else {
+ util.addClassName(frame, String(className));
+ }
+ }
- this.frame = frame;
- changed += 1;
+ this.frame = frame;
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint panel: no parent attached');
}
- if (!frame.parentNode) {
- if (!this.parent) {
- throw new Error('Cannot repaint panel: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint panel: parent has no container element');
- }
- parentContainer.appendChild(frame);
- changed += 1;
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint panel: parent has no container element');
}
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, '100%'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -94,19 +94,19 @@ Panel.prototype.repaint = function () {
* @return {Boolean} resized
*/
Panel.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame;
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame;
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', frame.offsetHeight);
- }
- else {
- changed += 1;
- }
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', frame.offsetHeight);
+ }
+ else {
+ changed += 1;
+ }
- return (changed > 0);
+ return (changed > 0);
};
diff --git a/src/timeline/component/RootPanel.js b/src/timeline/component/RootPanel.js
index f55af08f..6bfd2db2 100644
--- a/src/timeline/component/RootPanel.js
+++ b/src/timeline/component/RootPanel.js
@@ -7,15 +7,15 @@
* @extends Panel
*/
function RootPanel(container, options) {
- this.id = util.randomUUID();
- this.container = container;
+ this.id = util.randomUUID();
+ this.container = container;
- this.options = options || {};
- this.defaultOptions = {
- autoResize: true
- };
+ this.options = options || {};
+ this.defaultOptions = {
+ autoResize: true
+ };
- this.listeners = {}; // event listeners
+ this.listeners = {}; // event listeners
}
RootPanel.prototype = new Panel();
@@ -37,42 +37,42 @@ RootPanel.prototype.setOptions = Component.prototype.setOptions;
* @return {Boolean} changed
*/
RootPanel.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- frame = this.frame;
-
- if (!frame) {
- frame = document.createElement('div');
-
- this.frame = frame;
-
- changed += 1;
- }
- if (!frame.parentNode) {
- if (!this.container) {
- throw new Error('Cannot repaint root panel: no container attached');
- }
- this.container.appendChild(frame);
- changed += 1;
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ frame = this.frame;
+
+ if (!frame) {
+ frame = document.createElement('div');
+
+ this.frame = frame;
+
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ if (!this.container) {
+ throw new Error('Cannot repaint root panel: no container attached');
}
+ this.container.appendChild(frame);
+ changed += 1;
+ }
- frame.className = 'vis timeline rootpanel ' + options.orientation;
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
+ frame.className = 'vis timeline rootpanel ' + options.orientation;
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
+ }
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, '100%'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
- this._updateEventEmitters();
- this._updateWatch();
+ this._updateEventEmitters();
+ this._updateWatch();
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -80,21 +80,21 @@ RootPanel.prototype.repaint = function () {
* @return {Boolean} resized
*/
RootPanel.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame;
-
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', frame.offsetHeight);
- }
- else {
- changed += 1;
- }
-
- return (changed > 0);
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame;
+
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', frame.offsetHeight);
+ }
+ else {
+ changed += 1;
+ }
+
+ return (changed > 0);
};
/**
@@ -102,13 +102,13 @@ RootPanel.prototype.reflow = function () {
* @private
*/
RootPanel.prototype._updateWatch = function () {
- var autoResize = this.getOption('autoResize');
- if (autoResize) {
- this._watch();
- }
- else {
- this._unwatch();
- }
+ var autoResize = this.getOption('autoResize');
+ if (autoResize) {
+ this._watch();
+ }
+ else {
+ this._unwatch();
+ }
};
/**
@@ -117,31 +117,31 @@ RootPanel.prototype._updateWatch = function () {
* @private
*/
RootPanel.prototype._watch = function () {
- var me = this;
+ var me = this;
- this._unwatch();
+ this._unwatch();
- var checkSize = function () {
- var autoResize = me.getOption('autoResize');
- if (!autoResize) {
- // stop watching when the option autoResize is changed to false
- me._unwatch();
- return;
- }
+ var checkSize = function () {
+ var autoResize = me.getOption('autoResize');
+ if (!autoResize) {
+ // stop watching when the option autoResize is changed to false
+ me._unwatch();
+ return;
+ }
- if (me.frame) {
- // check whether the frame is resized
- if ((me.frame.clientWidth != me.width) ||
- (me.frame.clientHeight != me.height)) {
- me.requestReflow();
- }
- }
- };
+ if (me.frame) {
+ // check whether the frame is resized
+ if ((me.frame.clientWidth != me.width) ||
+ (me.frame.clientHeight != me.height)) {
+ me.requestReflow();
+ }
+ }
+ };
- // TODO: automatically cleanup the event listener when the frame is deleted
- util.addEventListener(window, 'resize', checkSize);
+ // TODO: automatically cleanup the event listener when the frame is deleted
+ util.addEventListener(window, 'resize', checkSize);
- this.watchTimer = setInterval(checkSize, 1000);
+ this.watchTimer = setInterval(checkSize, 1000);
};
/**
@@ -149,12 +149,12 @@ RootPanel.prototype._watch = function () {
* @private
*/
RootPanel.prototype._unwatch = function () {
- if (this.watchTimer) {
- clearInterval(this.watchTimer);
- this.watchTimer = undefined;
- }
+ if (this.watchTimer) {
+ clearInterval(this.watchTimer);
+ this.watchTimer = undefined;
+ }
- // TODO: remove event listener on window.resize
+ // TODO: remove event listener on window.resize
};
/**
@@ -164,15 +164,15 @@ RootPanel.prototype._unwatch = function () {
* as parameter.
*/
RootPanel.prototype.on = function (event, callback) {
- // register the listener at this component
- var arr = this.listeners[event];
- if (!arr) {
- arr = [];
- this.listeners[event] = arr;
- }
- arr.push(callback);
-
- this._updateEventEmitters();
+ // register the listener at this component
+ var arr = this.listeners[event];
+ if (!arr) {
+ arr = [];
+ this.listeners[event] = arr;
+ }
+ arr.push(callback);
+
+ this._updateEventEmitters();
};
/**
@@ -180,36 +180,36 @@ RootPanel.prototype.on = function (event, callback) {
* @private
*/
RootPanel.prototype._updateEventEmitters = function () {
- if (this.listeners) {
- var me = this;
- util.forEach(this.listeners, function (listeners, event) {
- if (!me.emitters) {
- me.emitters = {};
- }
- if (!(event in me.emitters)) {
- // create event
- var frame = me.frame;
- if (frame) {
- //console.log('Created a listener for event ' + event + ' on component ' + me.id); // TODO: cleanup logging
- var callback = function(event) {
- listeners.forEach(function (listener) {
- // TODO: filter on event target!
- listener(event);
- });
- };
- me.emitters[event] = callback;
-
- if (!me.hammer) {
- me.hammer = Hammer(frame, {
- prevent_default: true
- });
- }
- me.hammer.on(event, callback);
- }
- }
- });
-
- // TODO: be able to delete event listeners
- // TODO: be able to move event listeners to a parent when available
- }
+ if (this.listeners) {
+ var me = this;
+ util.forEach(this.listeners, function (listeners, event) {
+ if (!me.emitters) {
+ me.emitters = {};
+ }
+ if (!(event in me.emitters)) {
+ // create event
+ var frame = me.frame;
+ if (frame) {
+ //console.log('Created a listener for event ' + event + ' on component ' + me.id); // TODO: cleanup logging
+ var callback = function(event) {
+ listeners.forEach(function (listener) {
+ // TODO: filter on event target!
+ listener(event);
+ });
+ };
+ me.emitters[event] = callback;
+
+ if (!me.hammer) {
+ me.hammer = Hammer(frame, {
+ prevent_default: true
+ });
+ }
+ me.hammer.on(event, callback);
+ }
+ }
+ });
+
+ // TODO: be able to delete event listeners
+ // TODO: be able to move event listeners to a parent when available
+ }
};
diff --git a/src/timeline/component/TimeAxis.js b/src/timeline/component/TimeAxis.js
index 090bd62c..dadb37d2 100644
--- a/src/timeline/component/TimeAxis.js
+++ b/src/timeline/component/TimeAxis.js
@@ -9,41 +9,41 @@
* @extends Component
*/
function TimeAxis (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- this.dom = {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: [],
- redundant: {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: []
- }
- };
- this.props = {
- range: {
- start: 0,
- end: 0,
- minimumStep: 0
- },
- lineTop: 0
- };
-
- this.options = options || {};
- this.defaultOptions = {
- orientation: 'bottom', // supported: 'top', 'bottom'
- // TODO: implement timeaxis orientations 'left' and 'right'
- showMinorLabels: true,
- showMajorLabels: true
- };
-
- this.conversion = null;
- this.range = null;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ this.dom = {
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: [],
+ redundant: {
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: []
+ }
+ };
+ this.props = {
+ range: {
+ start: 0,
+ end: 0,
+ minimumStep: 0
+ },
+ lineTop: 0
+ };
+
+ this.options = options || {};
+ this.defaultOptions = {
+ orientation: 'bottom', // supported: 'top', 'bottom'
+ // TODO: implement timeaxis orientations 'left' and 'right'
+ showMinorLabels: true,
+ showMajorLabels: true
+ };
+
+ this.conversion = null;
+ this.range = null;
}
TimeAxis.prototype = new Component();
@@ -56,11 +56,11 @@ TimeAxis.prototype.setOptions = Component.prototype.setOptions;
* @param {Range | Object} range A Range or an object containing start and end.
*/
TimeAxis.prototype.setRange = function (range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
- }
- this.range = range;
+ if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
+ throw new TypeError('Range must be an instance of Range, ' +
+ 'or an object containing start and end.');
+ }
+ this.range = range;
};
/**
@@ -69,8 +69,8 @@ TimeAxis.prototype.setRange = function (range) {
* @return {Date} time The datetime the corresponds with given position x
*/
TimeAxis.prototype.toTime = function(x) {
- var conversion = this.conversion;
- return new Date(x / conversion.scale + conversion.offset);
+ var conversion = this.conversion;
+ return new Date(x / conversion.scale + conversion.offset);
};
/**
@@ -81,8 +81,8 @@ TimeAxis.prototype.toTime = function(x) {
* @private
*/
TimeAxis.prototype.toScreen = function(time) {
- var conversion = this.conversion;
- return (time.valueOf() - conversion.offset) * conversion.scale;
+ var conversion = this.conversion;
+ return (time.valueOf() - conversion.offset) * conversion.scale;
};
/**
@@ -90,112 +90,112 @@ TimeAxis.prototype.toScreen = function(time) {
* @return {Boolean} changed
*/
TimeAxis.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- orientation = this.getOption('orientation'),
- props = this.props,
- step = this.step;
-
- var frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- this.frame = frame;
- changed += 1;
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ orientation = this.getOption('orientation'),
+ props = this.props,
+ step = this.step;
+
+ var frame = this.frame;
+ if (!frame) {
+ frame = document.createElement('div');
+ this.frame = frame;
+ changed += 1;
+ }
+ frame.className = 'axis';
+ // TODO: custom className?
+
+ if (!frame.parentNode) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint time axis: no parent attached');
}
- frame.className = 'axis';
- // TODO: custom className?
-
- if (!frame.parentNode) {
- if (!this.parent) {
- throw new Error('Cannot repaint time axis: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint time axis: parent has no container element');
- }
- parentContainer.appendChild(frame);
-
- changed += 1;
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint time axis: parent has no container element');
}
+ parentContainer.appendChild(frame);
+
+ changed += 1;
+ }
+
+ var parent = frame.parentNode;
+ if (parent) {
+ var beforeChild = frame.nextSibling;
+ parent.removeChild(frame); // take frame offline while updating (is almost twice as fast)
+
+ var defaultTop = (orientation == 'bottom' && this.props.parentHeight && this.height) ?
+ (this.props.parentHeight - this.height) + 'px' :
+ '0px';
+ changed += update(frame.style, 'top', asSize(options.top, defaultTop));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+
+ // get characters width and height
+ this._repaintMeasureChars();
+
+ if (this.step) {
+ this._repaintStart();
+
+ step.first();
+ var xFirstMajorLabel = undefined;
+ var max = 0;
+ while (step.hasNext() && max < 1000) {
+ max++;
+ var cur = step.getCurrent(),
+ x = this.toScreen(cur),
+ isMajor = step.isMajor();
+
+ // TODO: lines must have a width, such that we can create css backgrounds
+
+ if (this.getOption('showMinorLabels')) {
+ this._repaintMinorText(x, step.getLabelMinor());
+ }
- var parent = frame.parentNode;
- if (parent) {
- var beforeChild = frame.nextSibling;
- parent.removeChild(frame); // take frame offline while updating (is almost twice as fast)
-
- var defaultTop = (orientation == 'bottom' && this.props.parentHeight && this.height) ?
- (this.props.parentHeight - this.height) + 'px' :
- '0px';
- changed += update(frame.style, 'top', asSize(options.top, defaultTop));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
-
- // get characters width and height
- this._repaintMeasureChars();
-
- if (this.step) {
- this._repaintStart();
-
- step.first();
- var xFirstMajorLabel = undefined;
- var max = 0;
- while (step.hasNext() && max < 1000) {
- max++;
- var cur = step.getCurrent(),
- x = this.toScreen(cur),
- isMajor = step.isMajor();
-
- // TODO: lines must have a width, such that we can create css backgrounds
-
- if (this.getOption('showMinorLabels')) {
- this._repaintMinorText(x, step.getLabelMinor());
- }
-
- if (isMajor && this.getOption('showMajorLabels')) {
- if (x > 0) {
- if (xFirstMajorLabel == undefined) {
- xFirstMajorLabel = x;
- }
- this._repaintMajorText(x, step.getLabelMajor());
- }
- this._repaintMajorLine(x);
- }
- else {
- this._repaintMinorLine(x);
- }
-
- step.next();
+ if (isMajor && this.getOption('showMajorLabels')) {
+ if (x > 0) {
+ if (xFirstMajorLabel == undefined) {
+ xFirstMajorLabel = x;
}
+ this._repaintMajorText(x, step.getLabelMajor());
+ }
+ this._repaintMajorLine(x);
+ }
+ else {
+ this._repaintMinorLine(x);
+ }
- // create a major label on the left when needed
- if (this.getOption('showMajorLabels')) {
- var leftTime = this.toTime(0),
- leftText = step.getLabelMajor(leftTime),
- widthText = leftText.length * (props.majorCharWidth || 10) + 10; // upper bound estimation
+ step.next();
+ }
- if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
- this._repaintMajorText(0, leftText);
- }
- }
+ // create a major label on the left when needed
+ if (this.getOption('showMajorLabels')) {
+ var leftTime = this.toTime(0),
+ leftText = step.getLabelMajor(leftTime),
+ widthText = leftText.length * (props.majorCharWidth || 10) + 10; // upper bound estimation
- this._repaintEnd();
+ if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
+ this._repaintMajorText(0, leftText);
}
+ }
- this._repaintLine();
+ this._repaintEnd();
+ }
- // put frame online again
- if (beforeChild) {
- parent.insertBefore(frame, beforeChild);
- }
- else {
- parent.appendChild(frame)
- }
+ this._repaintLine();
+
+ // put frame online again
+ if (beforeChild) {
+ parent.insertBefore(frame, beforeChild);
}
+ else {
+ parent.appendChild(frame)
+ }
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -204,18 +204,18 @@ TimeAxis.prototype.repaint = function () {
* @private
*/
TimeAxis.prototype._repaintStart = function () {
- var dom = this.dom,
- redundant = dom.redundant;
-
- redundant.majorLines = dom.majorLines;
- redundant.majorTexts = dom.majorTexts;
- redundant.minorLines = dom.minorLines;
- redundant.minorTexts = dom.minorTexts;
-
- dom.majorLines = [];
- dom.majorTexts = [];
- dom.minorLines = [];
- dom.minorTexts = [];
+ var dom = this.dom,
+ redundant = dom.redundant;
+
+ redundant.majorLines = dom.majorLines;
+ redundant.majorTexts = dom.majorTexts;
+ redundant.minorLines = dom.minorLines;
+ redundant.minorTexts = dom.minorTexts;
+
+ dom.majorLines = [];
+ dom.majorTexts = [];
+ dom.minorLines = [];
+ dom.minorTexts = [];
};
/**
@@ -223,14 +223,14 @@ TimeAxis.prototype._repaintStart = function () {
* @private
*/
TimeAxis.prototype._repaintEnd = function () {
- util.forEach(this.dom.redundant, function (arr) {
- while (arr.length) {
- var elem = arr.pop();
- if (elem && elem.parentNode) {
- elem.parentNode.removeChild(elem);
- }
- }
- });
+ util.forEach(this.dom.redundant, function (arr) {
+ while (arr.length) {
+ var elem = arr.pop();
+ if (elem && elem.parentNode) {
+ elem.parentNode.removeChild(elem);
+ }
+ }
+ });
};
@@ -241,23 +241,23 @@ TimeAxis.prototype._repaintEnd = function () {
* @private
*/
TimeAxis.prototype._repaintMinorText = function (x, text) {
- // reuse redundant label
- var label = this.dom.redundant.minorTexts.shift();
-
- if (!label) {
- // create new label
- var content = document.createTextNode('');
- label = document.createElement('div');
- label.appendChild(content);
- label.className = 'text minor';
- this.frame.appendChild(label);
- }
- this.dom.minorTexts.push(label);
-
- label.childNodes[0].nodeValue = text;
- label.style.left = x + 'px';
- label.style.top = this.props.minorLabelTop + 'px';
- //label.title = title; // TODO: this is a heavy operation
+ // reuse redundant label
+ var label = this.dom.redundant.minorTexts.shift();
+
+ if (!label) {
+ // create new label
+ var content = document.createTextNode('');
+ label = document.createElement('div');
+ label.appendChild(content);
+ label.className = 'text minor';
+ this.frame.appendChild(label);
+ }
+ this.dom.minorTexts.push(label);
+
+ label.childNodes[0].nodeValue = text;
+ label.style.left = x + 'px';
+ label.style.top = this.props.minorLabelTop + 'px';
+ //label.title = title; // TODO: this is a heavy operation
};
/**
@@ -267,23 +267,23 @@ TimeAxis.prototype._repaintMinorText = function (x, text) {
* @private
*/
TimeAxis.prototype._repaintMajorText = function (x, text) {
- // reuse redundant label
- var label = this.dom.redundant.majorTexts.shift();
-
- if (!label) {
- // create label
- var content = document.createTextNode(text);
- label = document.createElement('div');
- label.className = 'text major';
- label.appendChild(content);
- this.frame.appendChild(label);
- }
- this.dom.majorTexts.push(label);
-
- label.childNodes[0].nodeValue = text;
- label.style.top = this.props.majorLabelTop + 'px';
- label.style.left = x + 'px';
- //label.title = title; // TODO: this is a heavy operation
+ // reuse redundant label
+ var label = this.dom.redundant.majorTexts.shift();
+
+ if (!label) {
+ // create label
+ var content = document.createTextNode(text);
+ label = document.createElement('div');
+ label.className = 'text major';
+ label.appendChild(content);
+ this.frame.appendChild(label);
+ }
+ this.dom.majorTexts.push(label);
+
+ label.childNodes[0].nodeValue = text;
+ label.style.top = this.props.majorLabelTop + 'px';
+ label.style.left = x + 'px';
+ //label.title = title; // TODO: this is a heavy operation
};
/**
@@ -292,21 +292,21 @@ TimeAxis.prototype._repaintMajorText = function (x, text) {
* @private
*/
TimeAxis.prototype._repaintMinorLine = function (x) {
- // reuse redundant line
- var line = this.dom.redundant.minorLines.shift();
-
- if (!line) {
- // create vertical line
- line = document.createElement('div');
- line.className = 'grid vertical minor';
- this.frame.appendChild(line);
- }
- this.dom.minorLines.push(line);
-
- var props = this.props;
- line.style.top = props.minorLineTop + 'px';
- line.style.height = props.minorLineHeight + 'px';
- line.style.left = (x - props.minorLineWidth / 2) + 'px';
+ // reuse redundant line
+ var line = this.dom.redundant.minorLines.shift();
+
+ if (!line) {
+ // create vertical line
+ line = document.createElement('div');
+ line.className = 'grid vertical minor';
+ this.frame.appendChild(line);
+ }
+ this.dom.minorLines.push(line);
+
+ var props = this.props;
+ line.style.top = props.minorLineTop + 'px';
+ line.style.height = props.minorLineHeight + 'px';
+ line.style.left = (x - props.minorLineWidth / 2) + 'px';
};
/**
@@ -315,21 +315,21 @@ TimeAxis.prototype._repaintMinorLine = function (x) {
* @private
*/
TimeAxis.prototype._repaintMajorLine = function (x) {
- // reuse redundant line
- var line = this.dom.redundant.majorLines.shift();
-
- if (!line) {
- // create vertical line
- line = document.createElement('DIV');
- line.className = 'grid vertical major';
- this.frame.appendChild(line);
- }
- this.dom.majorLines.push(line);
-
- var props = this.props;
- line.style.top = props.majorLineTop + 'px';
- line.style.left = (x - props.majorLineWidth / 2) + 'px';
- line.style.height = props.majorLineHeight + 'px';
+ // reuse redundant line
+ var line = this.dom.redundant.majorLines.shift();
+
+ if (!line) {
+ // create vertical line
+ line = document.createElement('DIV');
+ line.className = 'grid vertical major';
+ this.frame.appendChild(line);
+ }
+ this.dom.majorLines.push(line);
+
+ var props = this.props;
+ line.style.top = props.majorLineTop + 'px';
+ line.style.left = (x - props.majorLineWidth / 2) + 'px';
+ line.style.height = props.majorLineHeight + 'px';
};
@@ -338,33 +338,33 @@ TimeAxis.prototype._repaintMajorLine = function (x) {
* @private
*/
TimeAxis.prototype._repaintLine = function() {
- var line = this.dom.line,
- frame = this.frame,
- options = this.options;
-
- // line before all axis elements
- if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
- if (line) {
- // put this line at the end of all childs
- frame.removeChild(line);
- frame.appendChild(line);
- }
- else {
- // create the axis line
- line = document.createElement('div');
- line.className = 'grid horizontal major';
- frame.appendChild(line);
- this.dom.line = line;
- }
-
- line.style.top = this.props.lineTop + 'px';
+ var line = this.dom.line,
+ frame = this.frame,
+ options = this.options;
+
+ // line before all axis elements
+ if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
+ if (line) {
+ // put this line at the end of all childs
+ frame.removeChild(line);
+ frame.appendChild(line);
}
else {
- if (line && line.parentElement) {
- frame.removeChild(line.line);
- delete this.dom.line;
- }
+ // create the axis line
+ line = document.createElement('div');
+ line.className = 'grid horizontal major';
+ frame.appendChild(line);
+ this.dom.line = line;
+ }
+
+ line.style.top = this.props.lineTop + 'px';
+ }
+ else {
+ if (line && line.parentElement) {
+ frame.removeChild(line.line);
+ delete this.dom.line;
}
+ }
};
/**
@@ -372,31 +372,31 @@ TimeAxis.prototype._repaintLine = function() {
* @private
*/
TimeAxis.prototype._repaintMeasureChars = function () {
- // calculate the width and height of a single character
- // this is used to calculate the step size, and also the positioning of the
- // axis
- var dom = this.dom,
- text;
-
- if (!dom.measureCharMinor) {
- text = document.createTextNode('0');
- var measureCharMinor = document.createElement('DIV');
- measureCharMinor.className = 'text minor measure';
- measureCharMinor.appendChild(text);
- this.frame.appendChild(measureCharMinor);
-
- dom.measureCharMinor = measureCharMinor;
- }
-
- if (!dom.measureCharMajor) {
- text = document.createTextNode('0');
- var measureCharMajor = document.createElement('DIV');
- measureCharMajor.className = 'text major measure';
- measureCharMajor.appendChild(text);
- this.frame.appendChild(measureCharMajor);
-
- dom.measureCharMajor = measureCharMajor;
- }
+ // calculate the width and height of a single character
+ // this is used to calculate the step size, and also the positioning of the
+ // axis
+ var dom = this.dom,
+ text;
+
+ if (!dom.measureCharMinor) {
+ text = document.createTextNode('0');
+ var measureCharMinor = document.createElement('DIV');
+ measureCharMinor.className = 'text minor measure';
+ measureCharMinor.appendChild(text);
+ this.frame.appendChild(measureCharMinor);
+
+ dom.measureCharMinor = measureCharMinor;
+ }
+
+ if (!dom.measureCharMajor) {
+ text = document.createTextNode('0');
+ var measureCharMajor = document.createElement('DIV');
+ measureCharMajor.className = 'text major measure';
+ measureCharMajor.appendChild(text);
+ this.frame.appendChild(measureCharMajor);
+
+ dom.measureCharMajor = measureCharMajor;
+ }
};
/**
@@ -404,100 +404,100 @@ TimeAxis.prototype._repaintMeasureChars = function () {
* @return {Boolean} resized
*/
TimeAxis.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame,
- range = this.range;
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame,
+ range = this.range;
+
+ if (!range) {
+ throw new Error('Cannot repaint time axis: no range configured');
+ }
+
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+
+ // calculate size of a character
+ var props = this.props,
+ showMinorLabels = this.getOption('showMinorLabels'),
+ showMajorLabels = this.getOption('showMajorLabels'),
+ measureCharMinor = this.dom.measureCharMinor,
+ measureCharMajor = this.dom.measureCharMajor;
+ if (measureCharMinor) {
+ props.minorCharHeight = measureCharMinor.clientHeight;
+ props.minorCharWidth = measureCharMinor.clientWidth;
+ }
+ if (measureCharMajor) {
+ props.majorCharHeight = measureCharMajor.clientHeight;
+ props.majorCharWidth = measureCharMajor.clientWidth;
+ }
- if (!range) {
- throw new Error('Cannot repaint time axis: no range configured');
+ var parentHeight = frame.parentNode ? frame.parentNode.offsetHeight : 0;
+ if (parentHeight != props.parentHeight) {
+ props.parentHeight = parentHeight;
+ changed += 1;
}
+ switch (this.getOption('orientation')) {
+ case 'bottom':
+ props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
+ props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
-
- // calculate size of a character
- var props = this.props,
- showMinorLabels = this.getOption('showMinorLabels'),
- showMajorLabels = this.getOption('showMajorLabels'),
- measureCharMinor = this.dom.measureCharMinor,
- measureCharMajor = this.dom.measureCharMajor;
- if (measureCharMinor) {
- props.minorCharHeight = measureCharMinor.clientHeight;
- props.minorCharWidth = measureCharMinor.clientWidth;
- }
- if (measureCharMajor) {
- props.majorCharHeight = measureCharMajor.clientHeight;
- props.majorCharWidth = measureCharMajor.clientWidth;
- }
+ props.minorLabelTop = 0;
+ props.majorLabelTop = props.minorLabelTop + props.minorLabelHeight;
- var parentHeight = frame.parentNode ? frame.parentNode.offsetHeight : 0;
- if (parentHeight != props.parentHeight) {
- props.parentHeight = parentHeight;
- changed += 1;
- }
- switch (this.getOption('orientation')) {
- case 'bottom':
- props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
- props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
+ props.minorLineTop = -this.top;
+ props.minorLineHeight = Math.max(this.top + props.majorLabelHeight, 0);
+ props.minorLineWidth = 1; // TODO: really calculate width
- props.minorLabelTop = 0;
- props.majorLabelTop = props.minorLabelTop + props.minorLabelHeight;
+ props.majorLineTop = -this.top;
+ props.majorLineHeight = Math.max(this.top + props.minorLabelHeight + props.majorLabelHeight, 0);
+ props.majorLineWidth = 1; // TODO: really calculate width
- props.minorLineTop = -this.top;
- props.minorLineHeight = Math.max(this.top + props.majorLabelHeight, 0);
- props.minorLineWidth = 1; // TODO: really calculate width
+ props.lineTop = 0;
- props.majorLineTop = -this.top;
- props.majorLineHeight = Math.max(this.top + props.minorLabelHeight + props.majorLabelHeight, 0);
- props.majorLineWidth = 1; // TODO: really calculate width
+ break;
- props.lineTop = 0;
+ case 'top':
+ props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
+ props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
- break;
+ props.majorLabelTop = 0;
+ props.minorLabelTop = props.majorLabelTop + props.majorLabelHeight;
- case 'top':
- props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
- props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
+ props.minorLineTop = props.minorLabelTop;
+ props.minorLineHeight = Math.max(parentHeight - props.majorLabelHeight - this.top);
+ props.minorLineWidth = 1; // TODO: really calculate width
- props.majorLabelTop = 0;
- props.minorLabelTop = props.majorLabelTop + props.majorLabelHeight;
+ props.majorLineTop = 0;
+ props.majorLineHeight = Math.max(parentHeight - this.top);
+ props.majorLineWidth = 1; // TODO: really calculate width
- props.minorLineTop = props.minorLabelTop;
- props.minorLineHeight = Math.max(parentHeight - props.majorLabelHeight - this.top);
- props.minorLineWidth = 1; // TODO: really calculate width
+ props.lineTop = props.majorLabelHeight + props.minorLabelHeight;
- props.majorLineTop = 0;
- props.majorLineHeight = Math.max(parentHeight - this.top);
- props.majorLineWidth = 1; // TODO: really calculate width
+ break;
- props.lineTop = props.majorLabelHeight + props.minorLabelHeight;
+ default:
+ throw new Error('Unkown orientation "' + this.getOption('orientation') + '"');
+ }
- break;
+ var height = props.minorLabelHeight + props.majorLabelHeight;
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', height);
- default:
- throw new Error('Unkown orientation "' + this.getOption('orientation') + '"');
- }
+ // calculate range and step
+ this._updateConversion();
- var height = props.minorLabelHeight + props.majorLabelHeight;
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', height);
-
- // calculate range and step
- this._updateConversion();
-
- var start = util.convert(range.start, 'Number'),
- end = util.convert(range.end, 'Number'),
- minimumStep = this.toTime((props.minorCharWidth || 10) * 5).valueOf()
- -this.toTime(0).valueOf();
- this.step = new TimeStep(new Date(start), new Date(end), minimumStep);
- changed += update(props.range, 'start', start);
- changed += update(props.range, 'end', end);
- changed += update(props.range, 'minimumStep', minimumStep.valueOf());
- }
+ var start = util.convert(range.start, 'Number'),
+ end = util.convert(range.end, 'Number'),
+ minimumStep = this.toTime((props.minorCharWidth || 10) * 5).valueOf()
+ -this.toTime(0).valueOf();
+ this.step = new TimeStep(new Date(start), new Date(end), minimumStep);
+ changed += update(props.range, 'start', start);
+ changed += update(props.range, 'end', end);
+ changed += update(props.range, 'minimumStep', minimumStep.valueOf());
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -508,15 +508,15 @@ TimeAxis.prototype.reflow = function () {
* @private
*/
TimeAxis.prototype._updateConversion = function() {
- var range = this.range;
- if (!range) {
- throw new Error('No range configured');
- }
-
- if (range.conversion) {
- this.conversion = range.conversion(this.width);
- }
- else {
- this.conversion = Range.conversion(range.start, range.end, this.width);
- }
+ var range = this.range;
+ if (!range) {
+ throw new Error('No range configured');
+ }
+
+ if (range.conversion) {
+ this.conversion = range.conversion(this.width);
+ }
+ else {
+ this.conversion = Range.conversion(range.start, range.end, this.width);
+ }
};
diff --git a/src/timeline/component/css/currenttime.css b/src/timeline/component/css/currenttime.css
index 5ea0380b..73438693 100644
--- a/src/timeline/component/css/currenttime.css
+++ b/src/timeline/component/css/currenttime.css
@@ -1,5 +1,5 @@
.vis.timeline .currenttime {
- background-color: #FF7F6E;
- width: 2px;
- z-index: 9;
+ background-color: #FF7F6E;
+ width: 2px;
+ z-index: 9;
}
\ No newline at end of file
diff --git a/src/timeline/component/css/customtime.css b/src/timeline/component/css/customtime.css
index 15a3792a..76ce38fe 100644
--- a/src/timeline/component/css/customtime.css
+++ b/src/timeline/component/css/customtime.css
@@ -1,6 +1,6 @@
.vis.timeline .customtime {
- background-color: #6E94FF;
- width: 2px;
- cursor: move;
- z-index: 9;
+ background-color: #6E94FF;
+ width: 2px;
+ cursor: move;
+ z-index: 9;
}
\ No newline at end of file
diff --git a/src/timeline/component/css/groupset.css b/src/timeline/component/css/groupset.css
index 590c3b82..b6467a7f 100644
--- a/src/timeline/component/css/groupset.css
+++ b/src/timeline/component/css/groupset.css
@@ -1,59 +1,59 @@
.vis.timeline .groupset {
- position: absolute;
- padding: 0;
- margin: 0;
+ position: absolute;
+ padding: 0;
+ margin: 0;
}
.vis.timeline .labels {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-
- padding: 0;
- margin: 0;
-
- border-right: 1px solid #bfbfbf;
- box-sizing: border-box;
- -moz-box-sizing: border-box;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+
+ padding: 0;
+ margin: 0;
+
+ border-right: 1px solid #bfbfbf;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
}
.vis.timeline .labels .label-set {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
- overflow: hidden;
+ overflow: hidden;
- border-top: none;
- border-bottom: 1px solid #bfbfbf;
+ border-top: none;
+ border-bottom: 1px solid #bfbfbf;
}
.vis.timeline .labels .label-set .label {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- color: #4d4d4d;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ color: #4d4d4d;
}
.vis.timeline.top .labels .label-set .label,
.vis.timeline.top .groupset .itemset-axis {
-border-top: 1px solid #bfbfbf;
- border-bottom: none;
+ border-top: 1px solid #bfbfbf;
+ border-bottom: none;
}
.vis.timeline.bottom .labels .label-set .label,
.vis.timeline.bottom .groupset .itemset-axis {
- border-top: none;
- border-bottom: 1px solid #bfbfbf;
+ border-top: none;
+ border-bottom: 1px solid #bfbfbf;
}
.vis.timeline .labels .label-set .label .inner {
- display: inline-block;
- padding: 5px;
+ display: inline-block;
+ padding: 5px;
}
diff --git a/src/timeline/component/css/item.css b/src/timeline/component/css/item.css
index 13937964..579d1e5e 100644
--- a/src/timeline/component/css/item.css
+++ b/src/timeline/component/css/item.css
@@ -1,85 +1,85 @@
.vis.timeline .item {
- position: absolute;
- color: #1A1A1A;
- border-color: #97B0F8;
- background-color: #D5DDF6;
- display: inline-block;
+ position: absolute;
+ color: #1A1A1A;
+ border-color: #97B0F8;
+ background-color: #D5DDF6;
+ display: inline-block;
}
.vis.timeline .item.selected {
- border-color: #FFC200;
- background-color: #FFF785;
- z-index: 999;
+ border-color: #FFC200;
+ background-color: #FFF785;
+ z-index: 999;
}
.vis.timeline .item.cluster {
- /* TODO: use another color or pattern? */
- background: #97B0F8 url('img/cluster_bg.png');
- color: white;
+ /* TODO: use another color or pattern? */
+ background: #97B0F8 url('img/cluster_bg.png');
+ color: white;
}
.vis.timeline .item.cluster.point {
- border-color: #D5DDF6;
+ border-color: #D5DDF6;
}
.vis.timeline .item.box {
- text-align: center;
- border-style: solid;
- border-width: 1px;
- border-radius: 5px;
- -moz-border-radius: 5px; /* For Firefox 3.6 and older */
+ text-align: center;
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 5px;
+ -moz-border-radius: 5px; /* For Firefox 3.6 and older */
}
.vis.timeline .item.point {
- background: none;
+ background: none;
}
.vis.timeline .dot {
- border: 5px solid #97B0F8;
- position: absolute;
- border-radius: 5px;
- -moz-border-radius: 5px; /* For Firefox 3.6 and older */
+ border: 5px solid #97B0F8;
+ position: absolute;
+ border-radius: 5px;
+ -moz-border-radius: 5px; /* For Firefox 3.6 and older */
}
.vis.timeline .item.range {
- overflow: hidden;
- border-style: solid;
- border-width: 1px;
- border-radius: 2px;
- -moz-border-radius: 2px; /* For Firefox 3.6 and older */
+ overflow: hidden;
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 2px;
+ -moz-border-radius: 2px; /* For Firefox 3.6 and older */
}
.vis.timeline .item.rangeoverflow {
- border-style: solid;
- border-width: 1px;
- border-radius: 2px;
- -moz-border-radius: 2px; /* For Firefox 3.6 and older */
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 2px;
+ -moz-border-radius: 2px; /* For Firefox 3.6 and older */
}
.vis.timeline .item.range .drag-left, .vis.timeline .item.rangeoverflow .drag-left {
- cursor: w-resize;
- z-index: 1000;
+ cursor: w-resize;
+ z-index: 1000;
}
.vis.timeline .item.range .drag-right, .vis.timeline .item.rangeoverflow .drag-right {
- cursor: e-resize;
- z-index: 1000;
+ cursor: e-resize;
+ z-index: 1000;
}
.vis.timeline .item.range .content, .vis.timeline .item.rangeoverflow .content {
- position: relative;
- display: inline-block;
+ position: relative;
+ display: inline-block;
}
.vis.timeline .item.line {
- position: absolute;
- width: 0;
- border-left-width: 1px;
- border-left-style: solid;
+ position: absolute;
+ width: 0;
+ border-left-width: 1px;
+ border-left-style: solid;
}
.vis.timeline .item .content {
- margin: 5px;
- white-space: nowrap;
- overflow: hidden;
+ margin: 5px;
+ white-space: nowrap;
+ overflow: hidden;
}
diff --git a/src/timeline/component/css/itemset.css b/src/timeline/component/css/itemset.css
index 3e1d2885..c21d8fa1 100644
--- a/src/timeline/component/css/itemset.css
+++ b/src/timeline/component/css/itemset.css
@@ -1,9 +1,9 @@
.vis.timeline .itemset {
- position: absolute;
- padding: 0;
- margin: 0;
- overflow: hidden;
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
}
.vis.timeline .background {
@@ -13,5 +13,5 @@
}
.vis.timeline .itemset-axis {
- position: absolute;
+ position: absolute;
}
diff --git a/src/timeline/component/css/panel.css b/src/timeline/component/css/panel.css
index f87bbf3b..819f33f2 100644
--- a/src/timeline/component/css/panel.css
+++ b/src/timeline/component/css/panel.css
@@ -1,14 +1,14 @@
.vis.timeline.rootpanel {
- position: relative;
- overflow: hidden;
+ position: relative;
+ overflow: hidden;
- border: 1px solid #bfbfbf;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+ border: 1px solid #bfbfbf;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
.vis.timeline .panel {
- position: absolute;
- overflow: hidden;
+ position: absolute;
+ overflow: hidden;
}
diff --git a/src/timeline/component/css/timeaxis.css b/src/timeline/component/css/timeaxis.css
index 0d86b860..91b655b7 100644
--- a/src/timeline/component/css/timeaxis.css
+++ b/src/timeline/component/css/timeaxis.css
@@ -1,41 +1,41 @@
.vis.timeline .axis {
- position: relative;
+ position: relative;
}
.vis.timeline .axis .text {
- position: absolute;
- color: #4d4d4d;
- padding: 3px;
- white-space: nowrap;
+ position: absolute;
+ color: #4d4d4d;
+ padding: 3px;
+ white-space: nowrap;
}
.vis.timeline .axis .text.measure {
- position: absolute;
- padding-left: 0;
- padding-right: 0;
- margin-left: 0;
- margin-right: 0;
- visibility: hidden;
+ position: absolute;
+ padding-left: 0;
+ padding-right: 0;
+ margin-left: 0;
+ margin-right: 0;
+ visibility: hidden;
}
.vis.timeline .axis .grid.vertical {
- position: absolute;
- width: 0;
- border-right: 1px solid;
+ position: absolute;
+ width: 0;
+ border-right: 1px solid;
}
.vis.timeline .axis .grid.horizontal {
- position: absolute;
- left: 0;
- width: 100%;
- height: 0;
- border-bottom: 1px solid;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 0;
+ border-bottom: 1px solid;
}
.vis.timeline .axis .grid.minor {
- border-color: #e5e5e5;
+ border-color: #e5e5e5;
}
.vis.timeline .axis .grid.major {
- border-color: #bfbfbf;
+ border-color: #bfbfbf;
}
diff --git a/src/timeline/component/item/Item.js b/src/timeline/component/item/Item.js
index 0a5f623e..a706993f 100644
--- a/src/timeline/component/item/Item.js
+++ b/src/timeline/component/item/Item.js
@@ -8,32 +8,32 @@
* // TODO: describe available options
*/
function Item (parent, data, options, defaultOptions) {
- this.parent = parent;
- this.data = data;
- this.dom = null;
- this.options = options || {};
- this.defaultOptions = defaultOptions || {};
+ this.parent = parent;
+ this.data = data;
+ this.dom = null;
+ this.options = options || {};
+ this.defaultOptions = defaultOptions || {};
- this.selected = false;
- this.visible = false;
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.selected = false;
+ this.visible = false;
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
/**
* Select current item
*/
Item.prototype.select = function select() {
- this.selected = true;
+ this.selected = true;
};
/**
* Unselect current item
*/
Item.prototype.unselect = function unselect() {
- this.selected = false;
+ this.selected = false;
};
/**
@@ -41,7 +41,7 @@ Item.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
Item.prototype.show = function show() {
- return false;
+ return false;
};
/**
@@ -49,7 +49,7 @@ Item.prototype.show = function show() {
* @return {Boolean} changed
*/
Item.prototype.hide = function hide() {
- return false;
+ return false;
};
/**
@@ -57,8 +57,8 @@ Item.prototype.hide = function hide() {
* @return {Boolean} changed
*/
Item.prototype.repaint = function repaint() {
- // should be implemented by the item
- return false;
+ // should be implemented by the item
+ return false;
};
/**
@@ -66,8 +66,8 @@ Item.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Item.prototype.reflow = function reflow() {
- // should be implemented by the item
- return false;
+ // should be implemented by the item
+ return false;
};
/**
@@ -75,5 +75,5 @@ Item.prototype.reflow = function reflow() {
* @return {Integer} width
*/
Item.prototype.getWidth = function getWidth() {
- return this.width;
+ return this.width;
}
diff --git a/src/timeline/component/item/ItemBox.js b/src/timeline/component/item/ItemBox.js
index 48253cc4..4ad671c6 100644
--- a/src/timeline/component/item/ItemBox.js
+++ b/src/timeline/component/item/ItemBox.js
@@ -9,22 +9,22 @@
* // TODO: describe available options
*/
function ItemBox (parent, data, options, defaultOptions) {
- this.props = {
- dot: {
- left: 0,
- top: 0,
- width: 0,
- height: 0
- },
- line: {
- top: 0,
- left: 0,
- width: 0,
- height: 0
- }
- };
-
- Item.call(this, parent, data, options, defaultOptions);
+ this.props = {
+ dot: {
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0
+ },
+ line: {
+ top: 0,
+ left: 0,
+ width: 0,
+ height: 0
+ }
+ };
+
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemBox.prototype = new Item (null, null);
@@ -34,8 +34,8 @@ ItemBox.prototype = new Item (null, null);
* @override
*/
ItemBox.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -43,8 +43,8 @@ ItemBox.prototype.select = function select() {
* @override
*/
ItemBox.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -52,80 +52,80 @@ ItemBox.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemBox.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
-
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
+
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
+
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
}
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
-
- if (!dom.box.parentNode) {
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
- foreground.appendChild(dom.box);
- changed = true;
- }
-
- if (!dom.line.parentNode) {
- var background = this.parent.getBackground();
- if (!background) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no background container element');
- }
- background.appendChild(dom.line);
- changed = true;
- }
-
- if (!dom.dot.parentNode) {
- var axis = this.parent.getAxis();
- if (!background) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no axis container element');
- }
- axis.appendChild(dom.dot);
- changed = true;
- }
-
- // update contents
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
-
- // update class
- var className = (this.data.className? ' ' + this.data.className : '') +
- (this.selected ? ' selected' : '');
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item box' + className;
- dom.line.className = 'item line' + className;
- dom.dot.className = 'item dot' + className;
- changed = true;
- }
+ if (!dom.box.parentNode) {
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
+
+ if (!dom.line.parentNode) {
+ var background = this.parent.getBackground();
+ if (!background) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no background container element');
+ }
+ background.appendChild(dom.line);
+ changed = true;
+ }
+
+ if (!dom.dot.parentNode) {
+ var axis = this.parent.getAxis();
+ if (!background) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no axis container element');
+ }
+ axis.appendChild(dom.dot);
+ changed = true;
}
- return changed;
+ // update contents
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
+
+ // update class
+ var className = (this.data.className? ' ' + this.data.className : '') +
+ (this.selected ? ' selected' : '');
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item box' + className;
+ dom.line.className = 'item line' + className;
+ dom.dot.className = 'item dot' + className;
+ changed = true;
+ }
+ }
+
+ return changed;
};
/**
@@ -134,12 +134,12 @@ ItemBox.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemBox.prototype.show = function show() {
- if (!this.dom || !this.dom.box.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.box.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -147,21 +147,21 @@ ItemBox.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemBox.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.box.parentNode) {
- dom.box.parentNode.removeChild(dom.box);
- changed = true;
- }
- if (dom.line.parentNode) {
- dom.line.parentNode.removeChild(dom.line);
- }
- if (dom.dot.parentNode) {
- dom.dot.parentNode.removeChild(dom.dot);
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.box.parentNode) {
+ dom.box.parentNode.removeChild(dom.box);
+ changed = true;
+ }
+ if (dom.line.parentNode) {
+ dom.line.parentNode.removeChild(dom.line);
}
- return changed;
+ if (dom.dot.parentNode) {
+ dom.dot.parentNode.removeChild(dom.dot);
+ }
+ }
+ return changed;
};
/**
@@ -170,87 +170,87 @@ ItemBox.prototype.hide = function hide() {
* @override
*/
ItemBox.prototype.reflow = function reflow() {
- var changed = 0,
- update,
- dom,
- props,
- options,
- margin,
- start,
- align,
- orientation,
- top,
- left,
- data,
- range;
-
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
-
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item
- var interval = (range.end - range.start);
- this.visible = (data.start > range.start - interval) && (data.start < range.end + interval);
+ var changed = 0,
+ update,
+ dom,
+ props,
+ options,
+ margin,
+ start,
+ align,
+ orientation,
+ top,
+ left,
+ data,
+ range;
+
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
+
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item
+ var interval = (range.end - range.start);
+ this.visible = (data.start > range.start - interval) && (data.start < range.end + interval);
+ }
+ else {
+ this.visible = false;
+ }
+
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ update = util.updateProperty;
+ props = this.props;
+ options = this.options;
+ start = this.parent.toScreen(this.data.start);
+ align = options.align || this.defaultOptions.align;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ orientation = options.orientation || this.defaultOptions.orientation;
+
+ changed += update(props.dot, 'height', dom.dot.offsetHeight);
+ changed += update(props.dot, 'width', dom.dot.offsetWidth);
+ changed += update(props.line, 'width', dom.line.offsetWidth);
+ changed += update(props.line, 'height', dom.line.offsetHeight);
+ changed += update(props.line, 'top', dom.line.offsetTop);
+ changed += update(this, 'width', dom.box.offsetWidth);
+ changed += update(this, 'height', dom.box.offsetHeight);
+ if (align == 'right') {
+ left = start - this.width;
+ }
+ else if (align == 'left') {
+ left = start;
+ }
+ else {
+ // default or 'center'
+ left = start - this.width / 2;
+ }
+ changed += update(this, 'left', left);
+
+ changed += update(props.line, 'left', start - props.line.width / 2);
+ changed += update(props.dot, 'left', start - props.dot.width / 2);
+ changed += update(props.dot, 'top', -props.dot.height / 2);
+ if (orientation == 'top') {
+ top = margin;
+
+ changed += update(this, 'top', top);
+ }
+ else {
+ // default or 'bottom'
+ var parentHeight = this.parent.height;
+ top = parentHeight - this.height - margin;
+
+ changed += update(this, 'top', top);
+ }
}
else {
- this.visible = false;
- }
-
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- update = util.updateProperty;
- props = this.props;
- options = this.options;
- start = this.parent.toScreen(this.data.start);
- align = options.align || this.defaultOptions.align;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- orientation = options.orientation || this.defaultOptions.orientation;
-
- changed += update(props.dot, 'height', dom.dot.offsetHeight);
- changed += update(props.dot, 'width', dom.dot.offsetWidth);
- changed += update(props.line, 'width', dom.line.offsetWidth);
- changed += update(props.line, 'height', dom.line.offsetHeight);
- changed += update(props.line, 'top', dom.line.offsetTop);
- changed += update(this, 'width', dom.box.offsetWidth);
- changed += update(this, 'height', dom.box.offsetHeight);
- if (align == 'right') {
- left = start - this.width;
- }
- else if (align == 'left') {
- left = start;
- }
- else {
- // default or 'center'
- left = start - this.width / 2;
- }
- changed += update(this, 'left', left);
-
- changed += update(props.line, 'left', start - props.line.width / 2);
- changed += update(props.dot, 'left', start - props.dot.width / 2);
- changed += update(props.dot, 'top', -props.dot.height / 2);
- if (orientation == 'top') {
- top = margin;
-
- changed += update(this, 'top', top);
- }
- else {
- // default or 'bottom'
- var parentHeight = this.parent.height;
- top = parentHeight - this.height - margin;
-
- changed += update(this, 'top', top);
- }
- }
- else {
- changed += 1;
- }
+ changed += 1;
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -258,27 +258,27 @@ ItemBox.prototype.reflow = function reflow() {
* @private
*/
ItemBox.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
-
- // create the box
- dom.box = document.createElement('DIV');
- // className is updated in repaint()
-
- // contents box (inside the background box). used for making margins
- dom.content = document.createElement('DIV');
- dom.content.className = 'content';
- dom.box.appendChild(dom.content);
-
- // line to axis
- dom.line = document.createElement('DIV');
- dom.line.className = 'line';
-
- // dot on axis
- dom.dot = document.createElement('DIV');
- dom.dot.className = 'dot';
- }
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+
+ // create the box
+ dom.box = document.createElement('DIV');
+ // className is updated in repaint()
+
+ // contents box (inside the background box). used for making margins
+ dom.content = document.createElement('DIV');
+ dom.content.className = 'content';
+ dom.box.appendChild(dom.content);
+
+ // line to axis
+ dom.line = document.createElement('DIV');
+ dom.line.className = 'line';
+
+ // dot on axis
+ dom.dot = document.createElement('DIV');
+ dom.dot.className = 'dot';
+ }
};
/**
@@ -287,31 +287,31 @@ ItemBox.prototype._create = function _create() {
* @override
*/
ItemBox.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props,
- orientation = this.options.orientation || this.defaultOptions.orientation;
-
- if (dom) {
- var box = dom.box,
- line = dom.line,
- dot = dom.dot;
-
- box.style.left = this.left + 'px';
- box.style.top = this.top + 'px';
-
- line.style.left = props.line.left + 'px';
- if (orientation == 'top') {
- line.style.top = 0 + 'px';
- line.style.height = this.top + 'px';
- }
- else {
- // orientation 'bottom'
- line.style.top = (this.top + this.height) + 'px';
- line.style.height = Math.max(this.parent.height - this.top - this.height +
- this.props.dot.height / 2, 0) + 'px';
- }
-
- dot.style.left = props.dot.left + 'px';
- dot.style.top = props.dot.top + 'px';
+ var dom = this.dom,
+ props = this.props,
+ orientation = this.options.orientation || this.defaultOptions.orientation;
+
+ if (dom) {
+ var box = dom.box,
+ line = dom.line,
+ dot = dom.dot;
+
+ box.style.left = this.left + 'px';
+ box.style.top = this.top + 'px';
+
+ line.style.left = props.line.left + 'px';
+ if (orientation == 'top') {
+ line.style.top = 0 + 'px';
+ line.style.height = this.top + 'px';
}
+ else {
+ // orientation 'bottom'
+ line.style.top = (this.top + this.height) + 'px';
+ line.style.height = Math.max(this.parent.height - this.top - this.height +
+ this.props.dot.height / 2, 0) + 'px';
+ }
+
+ dot.style.left = props.dot.left + 'px';
+ dot.style.top = props.dot.top + 'px';
+ }
};
diff --git a/src/timeline/component/item/ItemPoint.js b/src/timeline/component/item/ItemPoint.js
index ec5b7d84..1d0d7ce7 100644
--- a/src/timeline/component/item/ItemPoint.js
+++ b/src/timeline/component/item/ItemPoint.js
@@ -9,19 +9,19 @@
* // TODO: describe available options
*/
function ItemPoint (parent, data, options, defaultOptions) {
- this.props = {
- dot: {
- top: 0,
- width: 0,
- height: 0
- },
- content: {
- height: 0,
- marginLeft: 0
- }
- };
-
- Item.call(this, parent, data, options, defaultOptions);
+ this.props = {
+ dot: {
+ top: 0,
+ width: 0,
+ height: 0
+ },
+ content: {
+ height: 0,
+ marginLeft: 0
+ }
+ };
+
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemPoint.prototype = new Item (null, null);
@@ -31,8 +31,8 @@ ItemPoint.prototype = new Item (null, null);
* @override
*/
ItemPoint.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -40,8 +40,8 @@ ItemPoint.prototype.select = function select() {
* @override
*/
ItemPoint.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -49,59 +49,59 @@ ItemPoint.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemPoint.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
-
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
+
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
+
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
}
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
-
- if (!dom.point.parentNode) {
- foreground.appendChild(dom.point);
- foreground.appendChild(dom.point);
- changed = true;
- }
-
- // update contents
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
-
- // update class
- var className = (this.data.className? ' ' + this.data.className : '') +
- (this.selected ? ' selected' : '');
- if (this.className != className) {
- this.className = className;
- dom.point.className = 'item point' + className;
- changed = true;
- }
+ if (!dom.point.parentNode) {
+ foreground.appendChild(dom.point);
+ foreground.appendChild(dom.point);
+ changed = true;
+ }
+
+ // update contents
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
+
+ // update class
+ var className = (this.data.className? ' ' + this.data.className : '') +
+ (this.selected ? ' selected' : '');
+ if (this.className != className) {
+ this.className = className;
+ dom.point.className = 'item point' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
@@ -110,12 +110,12 @@ ItemPoint.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemPoint.prototype.show = function show() {
- if (!this.dom || !this.dom.point.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.point.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -123,15 +123,15 @@ ItemPoint.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemPoint.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.point.parentNode) {
- dom.point.parentNode.removeChild(dom.point);
- changed = true;
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.point.parentNode) {
+ dom.point.parentNode.removeChild(dom.point);
+ changed = true;
}
- return changed;
+ }
+ return changed;
};
/**
@@ -140,70 +140,70 @@ ItemPoint.prototype.hide = function hide() {
* @override
*/
ItemPoint.prototype.reflow = function reflow() {
- var changed = 0,
- update,
- dom,
- props,
- options,
- margin,
- orientation,
- start,
- top,
- data,
- range;
-
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
-
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item
- var interval = (range.end - range.start);
- this.visible = (data.start > range.start - interval) && (data.start < range.end);
+ var changed = 0,
+ update,
+ dom,
+ props,
+ options,
+ margin,
+ orientation,
+ start,
+ top,
+ data,
+ range;
+
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
+
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item
+ var interval = (range.end - range.start);
+ this.visible = (data.start > range.start - interval) && (data.start < range.end);
+ }
+ else {
+ this.visible = false;
+ }
+
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ update = util.updateProperty;
+ props = this.props;
+ options = this.options;
+ orientation = options.orientation || this.defaultOptions.orientation;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ start = this.parent.toScreen(this.data.start);
+
+ changed += update(this, 'width', dom.point.offsetWidth);
+ changed += update(this, 'height', dom.point.offsetHeight);
+ changed += update(props.dot, 'width', dom.dot.offsetWidth);
+ changed += update(props.dot, 'height', dom.dot.offsetHeight);
+ changed += update(props.content, 'height', dom.content.offsetHeight);
+
+ if (orientation == 'top') {
+ top = margin;
+ }
+ else {
+ // default or 'bottom'
+ var parentHeight = this.parent.height;
+ top = Math.max(parentHeight - this.height - margin, 0);
+ }
+ changed += update(this, 'top', top);
+ changed += update(this, 'left', start - props.dot.width / 2);
+ changed += update(props.content, 'marginLeft', 1.5 * props.dot.width);
+ //changed += update(props.content, 'marginRight', 0.5 * props.dot.width); // TODO
+
+ changed += update(props.dot, 'top', (this.height - props.dot.height) / 2);
}
else {
- this.visible = false;
+ changed += 1;
}
+ }
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- update = util.updateProperty;
- props = this.props;
- options = this.options;
- orientation = options.orientation || this.defaultOptions.orientation;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- start = this.parent.toScreen(this.data.start);
-
- changed += update(this, 'width', dom.point.offsetWidth);
- changed += update(this, 'height', dom.point.offsetHeight);
- changed += update(props.dot, 'width', dom.dot.offsetWidth);
- changed += update(props.dot, 'height', dom.dot.offsetHeight);
- changed += update(props.content, 'height', dom.content.offsetHeight);
-
- if (orientation == 'top') {
- top = margin;
- }
- else {
- // default or 'bottom'
- var parentHeight = this.parent.height;
- top = Math.max(parentHeight - this.height - margin, 0);
- }
- changed += update(this, 'top', top);
- changed += update(this, 'left', start - props.dot.width / 2);
- changed += update(props.content, 'marginLeft', 1.5 * props.dot.width);
- //changed += update(props.content, 'marginRight', 0.5 * props.dot.width); // TODO
-
- changed += update(props.dot, 'top', (this.height - props.dot.height) / 2);
- }
- else {
- changed += 1;
- }
- }
-
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -211,24 +211,24 @@ ItemPoint.prototype.reflow = function reflow() {
* @private
*/
ItemPoint.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
-
- // background box
- dom.point = document.createElement('div');
- // className is updated in repaint()
-
- // contents box, right from the dot
- dom.content = document.createElement('div');
- dom.content.className = 'content';
- dom.point.appendChild(dom.content);
-
- // dot at start
- dom.dot = document.createElement('div');
- dom.dot.className = 'dot';
- dom.point.appendChild(dom.dot);
- }
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+
+ // background box
+ dom.point = document.createElement('div');
+ // className is updated in repaint()
+
+ // contents box, right from the dot
+ dom.content = document.createElement('div');
+ dom.content.className = 'content';
+ dom.point.appendChild(dom.content);
+
+ // dot at start
+ dom.dot = document.createElement('div');
+ dom.dot.className = 'dot';
+ dom.point.appendChild(dom.dot);
+ }
};
/**
@@ -237,16 +237,16 @@ ItemPoint.prototype._create = function _create() {
* @override
*/
ItemPoint.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props;
+ var dom = this.dom,
+ props = this.props;
- if (dom) {
- dom.point.style.top = this.top + 'px';
- dom.point.style.left = this.left + 'px';
+ if (dom) {
+ dom.point.style.top = this.top + 'px';
+ dom.point.style.left = this.left + 'px';
- dom.content.style.marginLeft = props.content.marginLeft + 'px';
- //dom.content.style.marginRight = props.content.marginRight + 'px'; // TODO
+ dom.content.style.marginLeft = props.content.marginLeft + 'px';
+ //dom.content.style.marginRight = props.content.marginRight + 'px'; // TODO
- dom.dot.style.top = props.dot.top + 'px';
- }
+ dom.dot.style.top = props.dot.top + 'px';
+ }
};
diff --git a/src/timeline/component/item/ItemRange.js b/src/timeline/component/item/ItemRange.js
index c42ffc03..50c800c3 100644
--- a/src/timeline/component/item/ItemRange.js
+++ b/src/timeline/component/item/ItemRange.js
@@ -9,14 +9,14 @@
* // TODO: describe available options
*/
function ItemRange (parent, data, options, defaultOptions) {
- this.props = {
- content: {
- left: 0,
- width: 0
- }
- };
+ this.props = {
+ content: {
+ left: 0,
+ width: 0
+ }
+ };
- Item.call(this, parent, data, options, defaultOptions);
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemRange.prototype = new Item (null, null);
@@ -26,8 +26,8 @@ ItemRange.prototype = new Item (null, null);
* @override
*/
ItemRange.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -35,8 +35,8 @@ ItemRange.prototype.select = function select() {
* @override
*/
ItemRange.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -44,57 +44,57 @@ ItemRange.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemRange.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
- }
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
- if (!dom.box.parentNode) {
- foreground.appendChild(dom.box);
- changed = true;
- }
+ if (!dom.box.parentNode) {
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
- // update content
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ // update content
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
- // update class
- var className = this.data.className ? (' ' + this.data.className) : '';
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item range' + className;
- changed = true;
- }
+ // update class
+ var className = this.data.className ? (' ' + this.data.className) : '';
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item range' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
@@ -103,12 +103,12 @@ ItemRange.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemRange.prototype.show = function show() {
- if (!this.dom || !this.dom.box.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.box.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -116,15 +116,15 @@ ItemRange.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemRange.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.box.parentNode) {
- dom.box.parentNode.removeChild(dom.box);
- changed = true;
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.box.parentNode) {
+ dom.box.parentNode.removeChild(dom.box);
+ changed = true;
}
- return changed;
+ }
+ return changed;
};
/**
@@ -133,98 +133,98 @@ ItemRange.prototype.hide = function hide() {
* @override
*/
ItemRange.prototype.reflow = function reflow() {
- var changed = 0,
- dom,
- props,
- options,
- margin,
- padding,
- parent,
- start,
- end,
- data,
- range,
- update,
- box,
- parentWidth,
- contentLeft,
- orientation,
- top;
+ var changed = 0,
+ dom,
+ props,
+ options,
+ margin,
+ padding,
+ parent,
+ start,
+ end,
+ data,
+ range,
+ update,
+ box,
+ parentWidth,
+ contentLeft,
+ orientation,
+ top;
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
- if (this.data.end == undefined) {
- throw new Error('Property "end" missing in item ' + this.data.id);
- }
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
+ if (this.data.end == undefined) {
+ throw new Error('Property "end" missing in item ' + this.data.id);
+ }
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item. Take some margin
- this.visible = (data.start < range.end) && (data.end > range.start);
- }
- else {
- this.visible = false;
- }
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item. Take some margin
+ this.visible = (data.start < range.end) && (data.end > range.start);
+ }
+ else {
+ this.visible = false;
+ }
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- props = this.props;
- options = this.options;
- parent = this.parent;
- start = parent.toScreen(this.data.start);
- end = parent.toScreen(this.data.end);
- update = util.updateProperty;
- box = dom.box;
- parentWidth = parent.width;
- orientation = options.orientation || this.defaultOptions.orientation;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- padding = options.padding || this.defaultOptions.padding;
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ props = this.props;
+ options = this.options;
+ parent = this.parent;
+ start = parent.toScreen(this.data.start);
+ end = parent.toScreen(this.data.end);
+ update = util.updateProperty;
+ box = dom.box;
+ parentWidth = parent.width;
+ orientation = options.orientation || this.defaultOptions.orientation;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ padding = options.padding || this.defaultOptions.padding;
- changed += update(props.content, 'width', dom.content.offsetWidth);
+ changed += update(props.content, 'width', dom.content.offsetWidth);
- changed += update(this, 'height', box.offsetHeight);
+ changed += update(this, 'height', box.offsetHeight);
- // limit the width of the this, as browsers cannot draw very wide divs
- if (start < -parentWidth) {
- start = -parentWidth;
- }
- if (end > 2 * parentWidth) {
- end = 2 * parentWidth;
- }
+ // limit the width of the this, as browsers cannot draw very wide divs
+ if (start < -parentWidth) {
+ start = -parentWidth;
+ }
+ if (end > 2 * parentWidth) {
+ end = 2 * parentWidth;
+ }
- // when range exceeds left of the window, position the contents at the left of the visible area
- if (start < 0) {
- contentLeft = Math.min(-start,
- (end - start - props.content.width - 2 * padding));
- // TODO: remove the need for options.padding. it's terrible.
- }
- else {
- contentLeft = 0;
- }
- changed += update(props.content, 'left', contentLeft);
+ // when range exceeds left of the window, position the contents at the left of the visible area
+ if (start < 0) {
+ contentLeft = Math.min(-start,
+ (end - start - props.content.width - 2 * padding));
+ // TODO: remove the need for options.padding. it's terrible.
+ }
+ else {
+ contentLeft = 0;
+ }
+ changed += update(props.content, 'left', contentLeft);
- if (orientation == 'top') {
- top = margin;
- changed += update(this, 'top', top);
- }
- else {
- // default or 'bottom'
- top = parent.height - this.height - margin;
- changed += update(this, 'top', top);
- }
+ if (orientation == 'top') {
+ top = margin;
+ changed += update(this, 'top', top);
+ }
+ else {
+ // default or 'bottom'
+ top = parent.height - this.height - margin;
+ changed += update(this, 'top', top);
+ }
- changed += update(this, 'left', start);
- changed += update(this, 'width', Math.max(end - start, 1)); // TODO: reckon with border width;
- }
- else {
- changed += 1;
- }
+ changed += update(this, 'left', start);
+ changed += update(this, 'width', Math.max(end - start, 1)); // TODO: reckon with border width;
}
+ else {
+ changed += 1;
+ }
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -232,18 +232,18 @@ ItemRange.prototype.reflow = function reflow() {
* @private
*/
ItemRange.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
- // background box
- dom.box = document.createElement('div');
- // className is updated in repaint()
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+ // background box
+ dom.box = document.createElement('div');
+ // className is updated in repaint()
- // contents box
- dom.content = document.createElement('div');
- dom.content.className = 'content';
- dom.box.appendChild(dom.content);
- }
+ // contents box
+ dom.content = document.createElement('div');
+ dom.content.className = 'content';
+ dom.box.appendChild(dom.content);
+ }
};
/**
@@ -252,14 +252,14 @@ ItemRange.prototype._create = function _create() {
* @override
*/
ItemRange.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props;
+ var dom = this.dom,
+ props = this.props;
- if (dom) {
- dom.box.style.top = this.top + 'px';
- dom.box.style.left = this.left + 'px';
- dom.box.style.width = this.width + 'px';
+ if (dom) {
+ dom.box.style.top = this.top + 'px';
+ dom.box.style.left = this.left + 'px';
+ dom.box.style.width = this.width + 'px';
- dom.content.style.left = props.content.left + 'px';
- }
+ dom.content.style.left = props.content.left + 'px';
+ }
};
diff --git a/src/timeline/component/item/ItemRangeOverflow.js b/src/timeline/component/item/ItemRangeOverflow.js
index 521d42aa..4e0d4cbb 100644
--- a/src/timeline/component/item/ItemRangeOverflow.js
+++ b/src/timeline/component/item/ItemRangeOverflow.js
@@ -9,14 +9,14 @@
* // TODO: describe available options
*/
function ItemRangeOverflow (parent, data, options, defaultOptions) {
- this.props = {
- content: {
- left: 0,
- width: 0
- }
- };
+ this.props = {
+ content: {
+ left: 0,
+ width: 0
+ }
+ };
- ItemRange.call(this, parent, data, options, defaultOptions);
+ ItemRange.call(this, parent, data, options, defaultOptions);
}
ItemRangeOverflow.prototype = new ItemRange (null, null);
@@ -26,57 +26,57 @@ ItemRangeOverflow.prototype = new ItemRange (null, null);
* @return {Boolean} changed
*/
ItemRangeOverflow.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
- }
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
- if (!dom.box.parentNode) {
- foreground.appendChild(dom.box);
- changed = true;
- }
+ if (!dom.box.parentNode) {
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
- // update content
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ // update content
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
- // update class
- var className = this.data.className ? (' ' + this.data.className) : '';
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item rangeoverflow' + className;
- changed = true;
- }
+ // update class
+ var className = this.data.className ? (' ' + this.data.className) : '';
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item rangeoverflow' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
@@ -84,8 +84,8 @@ ItemRangeOverflow.prototype.repaint = function repaint() {
* @return {Number} width
*/
ItemRangeOverflow.prototype.getWidth = function getWidth() {
- if (this.props.content !== undefined && this.width < this.props.content.width)
- return this.props.content.width;
- else
- return this.width;
+ if (this.props.content !== undefined && this.width < this.props.content.width)
+ return this.props.content.width;
+ else
+ return this.width;
};
diff --git a/src/util.js b/src/util.js
index 95dcafbc..e7abbf30 100644
--- a/src/util.js
+++ b/src/util.js
@@ -9,7 +9,7 @@ var util = {};
* @return {Boolean} isNumber
*/
util.isNumber = function isNumber(object) {
- return (object instanceof Number || typeof object == 'number');
+ return (object instanceof Number || typeof object == 'number');
};
/**
@@ -18,7 +18,7 @@ util.isNumber = function isNumber(object) {
* @return {Boolean} isString
*/
util.isString = function isString(object) {
- return (object instanceof String || typeof object == 'string');
+ return (object instanceof String || typeof object == 'string');
};
/**
@@ -27,21 +27,21 @@ util.isString = function isString(object) {
* @return {Boolean} isDate
*/
util.isDate = function isDate(object) {
- if (object instanceof Date) {
- return true;
+ if (object instanceof Date) {
+ return true;
+ }
+ else if (util.isString(object)) {
+ // test whether this string contains a date
+ var match = ASPDateRegex.exec(object);
+ if (match) {
+ return true;
}
- else if (util.isString(object)) {
- // test whether this string contains a date
- var match = ASPDateRegex.exec(object);
- if (match) {
- return true;
- }
- else if (!isNaN(Date.parse(object))) {
- return true;
- }
+ else if (!isNaN(Date.parse(object))) {
+ return true;
}
+ }
- return false;
+ return false;
};
/**
@@ -50,10 +50,10 @@ util.isDate = function isDate(object) {
* @return {Boolean} isDataTable
*/
util.isDataTable = function isDataTable(object) {
- return (typeof (google) !== 'undefined') &&
- (google.visualization) &&
- (google.visualization.DataTable) &&
- (object instanceof google.visualization.DataTable);
+ return (typeof (google) !== 'undefined') &&
+ (google.visualization) &&
+ (google.visualization.DataTable) &&
+ (object instanceof google.visualization.DataTable);
};
/**
@@ -62,19 +62,19 @@ util.isDataTable = function isDataTable(object) {
* @return {String} uuid
*/
util.randomUUID = function randomUUID () {
- var S4 = function () {
- return Math.floor(
- Math.random() * 0x10000 /* 65536 */
- ).toString(16);
- };
-
- return (
- S4() + S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + S4() + S4()
- );
+ var S4 = function () {
+ return Math.floor(
+ Math.random() * 0x10000 /* 65536 */
+ ).toString(16);
+ };
+
+ return (
+ S4() + S4() + '-' +
+ S4() + '-' +
+ S4() + '-' +
+ S4() + '-' +
+ S4() + S4() + S4()
+ );
};
/**
@@ -85,16 +85,16 @@ util.randomUUID = function randomUUID () {
* @return {Object} a
*/
util.extend = function (a, b) {
- for (var i = 1, len = arguments.length; i < len; i++) {
- var other = arguments[i];
- for (var prop in other) {
- if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
- a[prop] = other[prop];
- }
- }
+ for (var i = 1, len = arguments.length; i < len; i++) {
+ var other = arguments[i];
+ for (var prop in other) {
+ if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
+ a[prop] = other[prop];
+ }
}
+ }
- return a;
+ return a;
};
/**
@@ -107,143 +107,143 @@ util.extend = function (a, b) {
* @throws Error
*/
util.convert = function convert(object, type) {
- var match;
-
- if (object === undefined) {
- return undefined;
- }
- if (object === null) {
- return null;
- }
-
- if (!type) {
- return object;
- }
- if (!(typeof type === 'string') && !(type instanceof String)) {
- throw new Error('Type must be a string');
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (type) {
- case 'boolean':
- case 'Boolean':
- return Boolean(object);
-
- case 'number':
- case 'Number':
- return Number(object.valueOf());
-
- case 'string':
- case 'String':
- return String(object);
-
- case 'Date':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- if (object instanceof Date) {
- return new Date(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return new Date(object.valueOf());
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])); // parse number
- }
- else {
- return moment(object).toDate(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
-
- case 'Moment':
- if (util.isNumber(object)) {
- return moment(object);
- }
- if (object instanceof Date) {
- return moment(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return moment(object);
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return moment(Number(match[1])); // parse number
- }
- else {
- return moment(object); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
-
- case 'ISODate':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- else if (object instanceof Date) {
- return object.toISOString();
- }
- else if (moment.isMoment(object)) {
- return object.toDate().toISOString();
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])).toISOString(); // parse number
- }
- else {
- return new Date(object).toISOString(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ISODate');
- }
-
- case 'ASPDate':
- if (util.isNumber(object)) {
- return '/Date(' + object + ')/';
- }
- else if (object instanceof Date) {
- return '/Date(' + object.valueOf() + ')/';
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- var value;
- if (match) {
- // object is an ASP date
- value = new Date(Number(match[1])).valueOf(); // parse number
- }
- else {
- value = new Date(object).valueOf(); // parse string
- }
- return '/Date(' + value + ')/';
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ASPDate');
- }
-
- default:
- throw new Error('Cannot convert object of type ' + util.getType(object) +
- ' to type "' + type + '"');
- }
+ var match;
+
+ if (object === undefined) {
+ return undefined;
+ }
+ if (object === null) {
+ return null;
+ }
+
+ if (!type) {
+ return object;
+ }
+ if (!(typeof type === 'string') && !(type instanceof String)) {
+ throw new Error('Type must be a string');
+ }
+
+ //noinspection FallthroughInSwitchStatementJS
+ switch (type) {
+ case 'boolean':
+ case 'Boolean':
+ return Boolean(object);
+
+ case 'number':
+ case 'Number':
+ return Number(object.valueOf());
+
+ case 'string':
+ case 'String':
+ return String(object);
+
+ case 'Date':
+ if (util.isNumber(object)) {
+ return new Date(object);
+ }
+ if (object instanceof Date) {
+ return new Date(object.valueOf());
+ }
+ else if (moment.isMoment(object)) {
+ return new Date(object.valueOf());
+ }
+ if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return new Date(Number(match[1])); // parse number
+ }
+ else {
+ return moment(object).toDate(); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type Date');
+ }
+
+ case 'Moment':
+ if (util.isNumber(object)) {
+ return moment(object);
+ }
+ if (object instanceof Date) {
+ return moment(object.valueOf());
+ }
+ else if (moment.isMoment(object)) {
+ return moment(object);
+ }
+ if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return moment(Number(match[1])); // parse number
+ }
+ else {
+ return moment(object); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type Date');
+ }
+
+ case 'ISODate':
+ if (util.isNumber(object)) {
+ return new Date(object);
+ }
+ else if (object instanceof Date) {
+ return object.toISOString();
+ }
+ else if (moment.isMoment(object)) {
+ return object.toDate().toISOString();
+ }
+ else if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return new Date(Number(match[1])).toISOString(); // parse number
+ }
+ else {
+ return new Date(object).toISOString(); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type ISODate');
+ }
+
+ case 'ASPDate':
+ if (util.isNumber(object)) {
+ return '/Date(' + object + ')/';
+ }
+ else if (object instanceof Date) {
+ return '/Date(' + object.valueOf() + ')/';
+ }
+ else if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ var value;
+ if (match) {
+ // object is an ASP date
+ value = new Date(Number(match[1])).valueOf(); // parse number
+ }
+ else {
+ value = new Date(object).valueOf(); // parse string
+ }
+ return '/Date(' + value + ')/';
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type ASPDate');
+ }
+
+ default:
+ throw new Error('Cannot convert object of type ' + util.getType(object) +
+ ' to type "' + type + '"');
+ }
};
// parse ASP.Net Date pattern,
@@ -257,40 +257,40 @@ var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
* @return {String} type
*/
util.getType = function getType(object) {
- var type = typeof object;
+ var type = typeof object;
- if (type == 'object') {
- if (object == null) {
- return 'null';
- }
- if (object instanceof Boolean) {
- return 'Boolean';
- }
- if (object instanceof Number) {
- return 'Number';
- }
- if (object instanceof String) {
- return 'String';
- }
- if (object instanceof Array) {
- return 'Array';
- }
- if (object instanceof Date) {
- return 'Date';
- }
- return 'Object';
+ if (type == 'object') {
+ if (object == null) {
+ return 'null';
}
- else if (type == 'number') {
- return 'Number';
+ if (object instanceof Boolean) {
+ return 'Boolean';
}
- else if (type == 'boolean') {
- return 'Boolean';
+ if (object instanceof Number) {
+ return 'Number';
}
- else if (type == 'string') {
- return 'String';
+ if (object instanceof String) {
+ return 'String';
}
-
- return type;
+ if (object instanceof Array) {
+ return 'Array';
+ }
+ if (object instanceof Date) {
+ return 'Date';
+ }
+ return 'Object';
+ }
+ else if (type == 'number') {
+ return 'Number';
+ }
+ else if (type == 'boolean') {
+ return 'Boolean';
+ }
+ else if (type == 'string') {
+ return 'String';
+ }
+
+ return type;
};
/**
@@ -300,17 +300,17 @@ util.getType = function getType(object) {
* in the browser page.
*/
util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var left = elem.offsetLeft;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- left += e.offsetLeft;
- left -= e.scrollLeft;
- e = e.offsetParent;
- }
- return left;
+ var doc = document.documentElement;
+ var body = document.body;
+
+ var left = elem.offsetLeft;
+ var e = elem.offsetParent;
+ while (e != null && e != body && e != doc) {
+ left += e.offsetLeft;
+ left -= e.scrollLeft;
+ e = e.offsetParent;
+ }
+ return left;
};
/**
@@ -320,17 +320,17 @@ util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
* in the browser page.
*/
util.getAbsoluteTop = function getAbsoluteTop (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var top = elem.offsetTop;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- top += e.offsetTop;
- top -= e.scrollTop;
- e = e.offsetParent;
- }
- return top;
+ var doc = document.documentElement;
+ var body = document.body;
+
+ var top = elem.offsetTop;
+ var e = elem.offsetParent;
+ while (e != null && e != body && e != doc) {
+ top += e.offsetTop;
+ top -= e.scrollTop;
+ e = e.offsetParent;
+ }
+ return top;
};
/**
@@ -339,24 +339,24 @@ util.getAbsoluteTop = function getAbsoluteTop (elem) {
* @return {Number} pageY
*/
util.getPageY = function getPageY (event) {
- if ('pageY' in event) {
- return event.pageY;
+ if ('pageY' in event) {
+ return event.pageY;
+ }
+ else {
+ var clientY;
+ if (('targetTouches' in event) && event.targetTouches.length) {
+ clientY = event.targetTouches[0].clientY;
}
else {
- var clientY;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientY = event.targetTouches[0].clientY;
- }
- else {
- clientY = event.clientY;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientY +
- ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
- ( doc && doc.clientTop || body && body.clientTop || 0 );
+ clientY = event.clientY;
}
+
+ var doc = document.documentElement;
+ var body = document.body;
+ return clientY +
+ ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
+ ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
};
/**
@@ -365,24 +365,24 @@ util.getPageY = function getPageY (event) {
* @return {Number} pageX
*/
util.getPageX = function getPageX (event) {
- if ('pageY' in event) {
- return event.pageX;
+ if ('pageY' in event) {
+ return event.pageX;
+ }
+ else {
+ var clientX;
+ if (('targetTouches' in event) && event.targetTouches.length) {
+ clientX = event.targetTouches[0].clientX;
}
else {
- var clientX;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientX = event.targetTouches[0].clientX;
- }
- else {
- clientX = event.clientX;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientX +
- ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
- ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ clientX = event.clientX;
}
+
+ var doc = document.documentElement;
+ var body = document.body;
+ return clientX +
+ ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
+ ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ }
};
/**
@@ -391,11 +391,11 @@ util.getPageX = function getPageX (event) {
* @param {String} className
*/
util.addClassName = function addClassName(elem, className) {
- var classes = elem.className.split(' ');
- if (classes.indexOf(className) == -1) {
- classes.push(className); // add the class to the array
- elem.className = classes.join(' ');
- }
+ var classes = elem.className.split(' ');
+ if (classes.indexOf(className) == -1) {
+ classes.push(className); // add the class to the array
+ elem.className = classes.join(' ');
+ }
};
/**
@@ -404,12 +404,12 @@ util.addClassName = function addClassName(elem, className) {
* @param {String} className
*/
util.removeClassName = function removeClassname(elem, className) {
- var classes = elem.className.split(' ');
- var index = classes.indexOf(className);
- if (index != -1) {
- classes.splice(index, 1); // remove the class from the array
- elem.className = classes.join(' ');
- }
+ var classes = elem.className.split(' ');
+ var index = classes.indexOf(className);
+ if (index != -1) {
+ classes.splice(index, 1); // remove the class from the array
+ elem.className = classes.join(' ');
+ }
};
/**
@@ -422,22 +422,22 @@ util.removeClassName = function removeClassname(elem, className) {
* callback(value, index, object)
*/
util.forEach = function forEach (object, callback) {
- var i,
- len;
- if (object instanceof Array) {
- // array
- for (i = 0, len = object.length; i < len; i++) {
- callback(object[i], i, object);
- }
- }
- else {
- // object
- for (i in object) {
- if (object.hasOwnProperty(i)) {
- callback(object[i], i, object);
- }
- }
- }
+ var i,
+ len;
+ if (object instanceof Array) {
+ // array
+ for (i = 0, len = object.length; i < len; i++) {
+ callback(object[i], i, object);
+ }
+ }
+ else {
+ // object
+ for (i in object) {
+ if (object.hasOwnProperty(i)) {
+ callback(object[i], i, object);
+ }
+ }
+ }
};
/**
@@ -448,13 +448,13 @@ util.forEach = function forEach (object, callback) {
* @return {Boolean} changed
*/
util.updateProperty = function updateProp (object, key, value) {
- if (object[key] !== value) {
- object[key] = value;
- return true;
- }
- else {
- return false;
- }
+ if (object[key] !== value) {
+ object[key] = value;
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -466,18 +466,18 @@ util.updateProperty = function updateProp (object, key, value) {
* @param {boolean} [useCapture]
*/
util.addEventListener = function addEventListener(element, action, listener, useCapture) {
- if (element.addEventListener) {
- if (useCapture === undefined)
- useCapture = false;
+ if (element.addEventListener) {
+ if (useCapture === undefined)
+ useCapture = false;
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.addEventListener(action, listener, useCapture);
- } else {
- element.attachEvent("on" + action, listener); // IE browsers
+ if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
+ action = "DOMMouseScroll"; // For Firefox
}
+
+ element.addEventListener(action, listener, useCapture);
+ } else {
+ element.attachEvent("on" + action, listener); // IE browsers
+ }
};
/**
@@ -488,20 +488,20 @@ util.addEventListener = function addEventListener(element, action, listener, use
* @param {boolean} [useCapture]
*/
util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
- if (element.removeEventListener) {
- // non-IE browsers
- if (useCapture === undefined)
- useCapture = false;
+ if (element.removeEventListener) {
+ // non-IE browsers
+ if (useCapture === undefined)
+ useCapture = false;
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.removeEventListener(action, listener, useCapture);
- } else {
- // IE browsers
- element.detachEvent("on" + action, listener);
+ if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
+ action = "DOMMouseScroll"; // For Firefox
}
+
+ element.removeEventListener(action, listener, useCapture);
+ } else {
+ // IE browsers
+ element.detachEvent("on" + action, listener);
+ }
};
@@ -511,41 +511,41 @@ util.removeEventListener = function removeEventListener(element, action, listene
* @return {Element} target element
*/
util.getTarget = function getTarget(event) {
- // code from http://www.quirksmode.org/js/events_properties.html
- if (!event) {
- event = window.event;
- }
-
- var target;
-
- if (event.target) {
- target = event.target;
- }
- else if (event.srcElement) {
- target = event.srcElement;
- }
-
- if (target.nodeType != undefined && target.nodeType == 3) {
- // defeat Safari bug
- target = target.parentNode;
- }
-
- return target;
+ // code from http://www.quirksmode.org/js/events_properties.html
+ if (!event) {
+ event = window.event;
+ }
+
+ var target;
+
+ if (event.target) {
+ target = event.target;
+ }
+ else if (event.srcElement) {
+ target = event.srcElement;
+ }
+
+ if (target.nodeType != undefined && target.nodeType == 3) {
+ // defeat Safari bug
+ target = target.parentNode;
+ }
+
+ return target;
};
/**
* Stop event propagation
*/
util.stopPropagation = function stopPropagation(event) {
- if (!event)
- event = window.event;
-
- if (event.stopPropagation) {
- event.stopPropagation(); // non-IE browsers
- }
- else {
- event.cancelBubble = true; // IE browsers
- }
+ if (!event)
+ event = window.event;
+
+ if (event.stopPropagation) {
+ event.stopPropagation(); // non-IE browsers
+ }
+ else {
+ event.cancelBubble = true; // IE browsers
+ }
};
@@ -553,15 +553,15 @@ util.stopPropagation = function stopPropagation(event) {
* Cancels the event if it is cancelable, without stopping further propagation of the event.
*/
util.preventDefault = function preventDefault (event) {
- if (!event)
- event = window.event;
-
- if (event.preventDefault) {
- event.preventDefault(); // non-IE browsers
- }
- else {
- event.returnValue = false; // IE browsers
- }
+ if (!event)
+ event = window.event;
+
+ if (event.preventDefault) {
+ event.preventDefault(); // non-IE browsers
+ }
+ else {
+ event.returnValue = false; // IE browsers
+ }
};
@@ -574,15 +574,15 @@ util.option = {};
* @returns {Boolean} bool
*/
util.option.asBoolean = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return (value != false);
- }
+ if (value != null) {
+ return (value != false);
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -592,15 +592,15 @@ util.option.asBoolean = function (value, defaultValue) {
* @returns {Number} number
*/
util.option.asNumber = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return Number(value) || defaultValue || null;
- }
+ if (value != null) {
+ return Number(value) || defaultValue || null;
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -610,15 +610,15 @@ util.option.asNumber = function (value, defaultValue) {
* @returns {String} str
*/
util.option.asString = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return String(value);
- }
+ if (value != null) {
+ return String(value);
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -628,19 +628,19 @@ util.option.asString = function (value, defaultValue) {
* @returns {String} size
*/
util.option.asSize = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (util.isString(value)) {
- return value;
- }
- else if (util.isNumber(value)) {
- return value + 'px';
- }
- else {
- return defaultValue || null;
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
+
+ if (util.isString(value)) {
+ return value;
+ }
+ else if (util.isNumber(value)) {
+ return value + 'px';
+ }
+ else {
+ return defaultValue || null;
+ }
};
/**
@@ -650,11 +650,11 @@ util.option.asSize = function (value, defaultValue) {
* @returns {HTMLElement | null} dom
*/
util.option.asElement = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- return value || defaultValue || null;
+ return value || defaultValue || null;
};
/**
@@ -662,25 +662,25 @@ util.option.asElement = function (value, defaultValue) {
* @param {String} css Text containing css
*/
util.loadCss = function (css) {
- if (typeof document === 'undefined') {
- return;
- }
-
- // get the script location, and built the css file name from the js file name
- // http://stackoverflow.com/a/2161748/1262753
- // var scripts = document.getElementsByTagName('script');
- // var jsFile = scripts[scripts.length-1].src.split('?')[0];
- // var cssFile = jsFile.substring(0, jsFile.length - 2) + 'css';
-
- // inject css
- // http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript
- var style = document.createElement('style');
- style.type = 'text/css';
- if (style.styleSheet){
- style.styleSheet.cssText = css;
- } else {
- style.appendChild(document.createTextNode(css));
- }
-
- document.getElementsByTagName('head')[0].appendChild(style);
+ if (typeof document === 'undefined') {
+ return;
+ }
+
+ // get the script location, and built the css file name from the js file name
+ // http://stackoverflow.com/a/2161748/1262753
+ // var scripts = document.getElementsByTagName('script');
+ // var jsFile = scripts[scripts.length-1].src.split('?')[0];
+ // var cssFile = jsFile.substring(0, jsFile.length - 2) + 'css';
+
+ // inject css
+ // http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript
+ var style = document.createElement('style');
+ style.type = 'text/css';
+ if (style.styleSheet){
+ style.styleSheet.cssText = css;
+ } else {
+ style.appendChild(document.createTextNode(css));
+ }
+
+ document.getElementsByTagName('head')[0].appendChild(style);
};
diff --git a/test/dataset.html b/test/dataset.html
index 7cb93ee6..ceff8219 100644
--- a/test/dataset.html
+++ b/test/dataset.html
@@ -1,75 +1,75 @@
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/test/dataset.js b/test/dataset.js
index 649127fc..63446918 100644
--- a/test/dataset.js
+++ b/test/dataset.js
@@ -6,81 +6,81 @@ var assert = require('assert'),
var now = new Date();
var data = new DataSet({
- convert: {
- start: 'Date',
- end: 'Date'
- }
+ convert: {
+ start: 'Date',
+ end: 'Date'
+ }
});
// add single items with different date types
data.add({id: 1, content: 'Item 1', start: new Date(now.valueOf())});
data.add({id: 2, content: 'Item 2', start: now.toISOString()});
data.add([
- //{id: 3, content: 'Item 3', start: moment(now)}, // TODO: moment fails, not the same instance
- {id: 3, content: 'Item 3', start: now},
- {id: 4, content: 'Item 4', start: '/Date(' + now.valueOf() + ')/'}
+ //{id: 3, content: 'Item 3', start: moment(now)}, // TODO: moment fails, not the same instance
+ {id: 3, content: 'Item 3', start: now},
+ {id: 4, content: 'Item 4', start: '/Date(' + now.valueOf() + ')/'}
]);
var items = data.get();
assert.equal(items.length, 4);
items.forEach(function (item) {
- assert.ok(item.start instanceof Date);
+ assert.ok(item.start instanceof Date);
});
// get filtered fields only
var sort = function (a, b) {
- return a.id > b.id;
+ return a.id > b.id;
};
assert.deepEqual(data.get({
- fields: ['id', 'content']
+ fields: ['id', 'content']
}).sort(sort), [
- {id: 1, content: 'Item 1'},
- {id: 2, content: 'Item 2'},
- {id: 3, content: 'Item 3'},
- {id: 4, content: 'Item 4'}
+ {id: 1, content: 'Item 1'},
+ {id: 2, content: 'Item 2'},
+ {id: 3, content: 'Item 3'},
+ {id: 4, content: 'Item 4'}
]);
// convert dates
assert.deepEqual(data.get({
- fields: ['id', 'start'],
- convert: {start: 'Number'}
+ fields: ['id', 'start'],
+ convert: {start: 'Number'}
}).sort(sort), [
- {id: 1, start: now.valueOf()},
- {id: 2, start: now.valueOf()},
- {id: 3, start: now.valueOf()},
- {id: 4, start: now.valueOf()}
+ {id: 1, start: now.valueOf()},
+ {id: 2, start: now.valueOf()},
+ {id: 3, start: now.valueOf()},
+ {id: 4, start: now.valueOf()}
]);
// get a single item
assert.deepEqual(data.get(1, {
- fields: ['id', 'start'],
- convert: {start: 'ISODate'}
+ fields: ['id', 'start'],
+ convert: {start: 'ISODate'}
}), {
- id: 1,
- start: now.toISOString()
+ id: 1,
+ start: now.toISOString()
});
// remove an item
data.remove(2);
assert.deepEqual(data.get({
- fields: ['id']
+ fields: ['id']
}).sort(sort), [
- {id: 1},
- {id: 3},
- {id: 4}
+ {id: 1},
+ {id: 3},
+ {id: 4}
]);
// add an item
data.add({id: 5, content: 'Item 5', start: now.valueOf()});
assert.deepEqual(data.get({
- fields: ['id']
+ fields: ['id']
}).sort(sort), [
- {id: 1},
- {id: 3},
- {id: 4},
- {id: 5}
+ {id: 1},
+ {id: 3},
+ {id: 4},
+ {id: 5}
]);
// update an item
@@ -89,11 +89,11 @@ data.remove(3); // remove exi
data.add({id: 3, other: 'bla'}); // add new item
data.update({id: 6, content: 'created!', start: now.valueOf()}); // this item is not yet existing, create it
assert.deepEqual(data.get().sort(sort), [
- {id: 1, content: 'Item 1', start: now},
- {id: 3, other: 'bla'},
- {id: 4, content: 'Item 4', start: now},
- {id: 5, content: 'changed!', start: now},
- {id: 6, content: 'created!', start: now}
+ {id: 1, content: 'Item 1', start: now},
+ {id: 3, other: 'bla'},
+ {id: 4, content: 'Item 4', start: now},
+ {id: 5, content: 'changed!', start: now},
+ {id: 6, content: 'created!', start: now}
]);
data.clear();
@@ -104,42 +104,42 @@ assert.equal(data.get().length, 0);
// test filtering and sorting
data = new vis.DataSet();
data.add([
- {id:1, age: 30, group: 2},
- {id:2, age: 25, group: 4},
- {id:3, age: 17, group: 2},
- {id:4, age: 27, group: 3}
+ {id:1, age: 30, group: 2},
+ {id:2, age: 25, group: 4},
+ {id:3, age: 17, group: 2},
+ {id:4, age: 27, group: 3}
]);
assert.deepEqual(data.get({order: 'age'}), [
- {id:3, age: 17, group: 2},
- {id:2, age: 25, group: 4},
- {id:4, age: 27, group: 3},
- {id:1, age: 30, group: 2}
+ {id:3, age: 17, group: 2},
+ {id:2, age: 25, group: 4},
+ {id:4, age: 27, group: 3},
+ {id:1, age: 30, group: 2}
]);
assert.deepEqual(data.getIds({order: 'age'}), [3,2,4,1]);
assert.deepEqual(data.get({order: 'age', fields: ['id']}), [
- {id:3},
- {id:2},
- {id:4},
- {id:1}
+ {id:3},
+ {id:2},
+ {id:4},
+ {id:1}
]);
assert.deepEqual(data.get({
- order: 'age',
- filter: function (item) {
- return item.group == 2;
- },
- fields: ['id']
+ order: 'age',
+ filter: function (item) {
+ return item.group == 2;
+ },
+ fields: ['id']
}), [
- {id:3},
- {id:1}
+ {id:3},
+ {id:1}
]);
assert.deepEqual(data.getIds({
- order: 'age',
- filter: function (item) {
- return (item.group == 2);
- }
+ order: 'age',
+ filter: function (item) {
+ return (item.group == 2);
+ }
}), [3,1]);
diff --git a/test/dataview.js b/test/dataview.js
index c6769c84..412caa5f 100644
--- a/test/dataview.js
+++ b/test/dataview.js
@@ -9,42 +9,42 @@ var groups = new DataSet();
// add items with different groups
groups.add([
- {id: 1, content: 'Item 1', group: 1},
- {id: 2, content: 'Item 2', group: 2},
- {id: 3, content: 'Item 3', group: 2},
- {id: 4, content: 'Item 4', group: 1},
- {id: 5, content: 'Item 5', group: 3}
+ {id: 1, content: 'Item 1', group: 1},
+ {id: 2, content: 'Item 2', group: 2},
+ {id: 3, content: 'Item 3', group: 2},
+ {id: 4, content: 'Item 4', group: 1},
+ {id: 5, content: 'Item 5', group: 3}
]);
var group2 = new DataView(groups, {
- filter: function (item) {
- return item.group == 2;
- }
+ filter: function (item) {
+ return item.group == 2;
+ }
});
// test getting the filtered data
assert.deepEqual(group2.get(), [
- {id: 2, content: 'Item 2', group: 2},
- {id: 3, content: 'Item 3', group: 2}
+ {id: 2, content: 'Item 2', group: 2},
+ {id: 3, content: 'Item 3', group: 2}
]);
// test filtering the view contents
assert.deepEqual(group2.get({
- filter: function (item) {
- return item.id > 2;
- }
+ filter: function (item) {
+ return item.id > 2;
+ }
}), [
- {id: 3, content: 'Item 3', group: 2}
+ {id: 3, content: 'Item 3', group: 2}
]);
// test event subscription
var groupsTriggerCount = 0;
groups.subscribe('*', function () {
- groupsTriggerCount++;
+ groupsTriggerCount++;
});
var group2TriggerCount = 0;
group2.subscribe('*', function () {
- group2TriggerCount++;
+ group2TriggerCount++;
});
groups.update({id:2, content: 'Item 2 (changed)'});
diff --git a/test/dotparser.js b/test/dotparser.js
index 93c74214..5fccb7e9 100644
--- a/test/dotparser.js
+++ b/test/dotparser.js
@@ -3,177 +3,177 @@ var assert = require('assert'),
dot = require('../src/graph/dotparser.js');
fs.readFile('test/dot.txt', function (err, data) {
- data = String(data);
+ data = String(data);
- var graph = dot.parseDOT(data);
+ var graph = dot.parseDOT(data);
- assert.deepEqual(graph, {
- "type": "digraph",
- "id": "test_graph",
- "rankdir": "LR",
- "size": "8,5",
- "font": "arial",
- "nodes": [
- {
- "id": "node1",
- "attr": {
- "shape": "doublecircle"
- }
- },
- {
- "id": "node2",
- "attr": {
- "shape": "doublecircle"
- }
- },
- {
- "id": "node3",
- "attr": {
- "shape": "doublecircle"
- }
- },
- {
- "id": "node4",
- "attr": {
- "shape": "diamond",
- "color": "red"
- }
- },
- {
- "id": "node5",
- "attr": {
- "shape": "square",
- "color": "blue",
- "width": 3
- }
- },
- {
- "id": 6,
- "attr": {
- "shape": "circle"
- }
- },
- {
- "id": "A",
- "attr": {
- "shape": "circle"
- }
- },
- {
- "id": "B",
- "attr": {
- "shape": "circle"
- }
+ assert.deepEqual(graph, {
+ "type": "digraph",
+ "id": "test_graph",
+ "rankdir": "LR",
+ "size": "8,5",
+ "font": "arial",
+ "nodes": [
+ {
+ "id": "node1",
+ "attr": {
+ "shape": "doublecircle"
+ }
+ },
+ {
+ "id": "node2",
+ "attr": {
+ "shape": "doublecircle"
+ }
+ },
+ {
+ "id": "node3",
+ "attr": {
+ "shape": "doublecircle"
+ }
+ },
+ {
+ "id": "node4",
+ "attr": {
+ "shape": "diamond",
+ "color": "red"
+ }
+ },
+ {
+ "id": "node5",
+ "attr": {
+ "shape": "square",
+ "color": "blue",
+ "width": 3
+ }
+ },
+ {
+ "id": 6,
+ "attr": {
+ "shape": "circle"
+ }
+ },
+ {
+ "id": "A",
+ "attr": {
+ "shape": "circle"
+ }
+ },
+ {
+ "id": "B",
+ "attr": {
+ "shape": "circle"
+ }
+ },
+ {
+ "id": "C",
+ "attr": {
+ "shape": "circle"
+ }
+ }
+ ],
+ "edges": [
+ {
+ "from": "node1",
+ "to": "node1",
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12,
+ "label": "a"
+ }
+ },
+ {
+ "from": "node2",
+ "to": "node3",
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12,
+ "label": "b"
+ }
+ },
+ {
+ "from": "node1",
+ "to": "node4",
+ "type": "--",
+ "attr": {
+ "length": 170,
+ "fontSize": 12,
+ "label": "c"
+ }
+ },
+ {
+ "from": "node3",
+ "to": "node4",
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12,
+ "label": "d"
+ }
+ },
+ {
+ "from": "node4",
+ "to": "node5",
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12
+ }
+ },
+ {
+ "from": "node5",
+ "to": 6,
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12
+ }
+ },
+ {
+ "from": "A",
+ "to": {
+ "nodes": [
+ {
+ "id": "B",
+ "attr": {
+ "shape": "circle"
+ }
},
{
- "id": "C",
- "attr": {
- "shape": "circle"
- }
+ "id": "C",
+ "attr": {
+ "shape": "circle"
+ }
}
- ],
- "edges": [
- {
- "from": "node1",
- "to": "node1",
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12,
- "label": "a"
- }
- },
- {
- "from": "node2",
- "to": "node3",
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12,
- "label": "b"
- }
- },
- {
- "from": "node1",
- "to": "node4",
- "type": "--",
- "attr": {
- "length": 170,
- "fontSize": 12,
- "label": "c"
- }
- },
- {
- "from": "node3",
- "to": "node4",
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12,
- "label": "d"
- }
- },
- {
- "from": "node4",
- "to": "node5",
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12
- }
- },
- {
- "from": "node5",
- "to": 6,
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12
- }
- },
- {
- "from": "A",
- "to": {
- "nodes": [
- {
- "id": "B",
- "attr": {
- "shape": "circle"
- }
- },
- {
- "id": "C",
- "attr": {
- "shape": "circle"
- }
- }
- ]
- },
- "type": "->",
- "attr": {
- "length": 170,
- "fontSize": 12
- }
+ ]
+ },
+ "type": "->",
+ "attr": {
+ "length": 170,
+ "fontSize": 12
+ }
+ }
+ ],
+ "subgraphs": [
+ {
+ "nodes": [
+ {
+ "id": "B",
+ "attr": {
+ "shape": "circle"
}
- ],
- "subgraphs": [
- {
- "nodes": [
- {
- "id": "B",
- "attr": {
- "shape": "circle"
- }
- },
- {
- "id": "C",
- "attr": {
- "shape": "circle"
- }
- }
- ]
+ },
+ {
+ "id": "C",
+ "attr": {
+ "shape": "circle"
}
+ }
]
- });
+ }
+ ]
+ });
});
diff --git a/test/eventbus.js b/test/eventbus.js
index f213500b..df4d8a94 100644
--- a/test/eventbus.js
+++ b/test/eventbus.js
@@ -9,21 +9,21 @@ var received = [];
var id1 = '1';
bus.on('message', function (event, data, source) {
- received.push({
- event: event,
- data: data,
- source: source
- });
+ received.push({
+ event: event,
+ data: data,
+ source: source
+ });
}, id1);
var id2 = '2';
bus.emit('message', {text: 'hello world'}, id2);
bus.on('chat:*', function (event, data, source) {
- received.push({
- event: event,
- data: data,
- source: source
- });
+ received.push({
+ event: event,
+ data: data,
+ source: source
+ });
});
bus.emit('chat:1', null, id2);
@@ -31,8 +31,8 @@ bus.emit('chat:2', {text: 'hello world'}, id1);
// verify if the messages are received
assert.deepEqual(received, [
- {event: 'message', data: {text: 'hello world'}, source: id2},
- {event: 'chat:1', data: null, source: id2},
- {event: 'chat:2', data: {text: 'hello world'}, source: id1}
+ {event: 'message', data: {text: 'hello world'}, source: id2},
+ {event: 'chat:1', data: null, source: id2},
+ {event: 'chat:2', data: {text: 'hello world'}, source: id1}
]);
diff --git a/test/timeline.html b/test/timeline.html
index 96023a3a..471e41f7 100644
--- a/test/timeline.html
+++ b/test/timeline.html
@@ -1,82 +1,82 @@
-
-
-
+
+
+
-
+ #visualization .itemset {
+ /*background: rgba(255, 255, 0, 0.5);*/
+ }
+
-
- Orientation
-
- top
- bottom
-
-
-
-
+
+ Orientation
+
+ top
+ bottom
+
+
+
+
-
+
-
+
\ No newline at end of file
diff --git a/test/timeline_groups.html b/test/timeline_groups.html
index b9ed6abb..a8a95a10 100644
--- a/test/timeline_groups.html
+++ b/test/timeline_groups.html
@@ -1,85 +1,85 @@
- Timeline | Group example
+ Timeline | Group example
-
+ #visualization {
+ box-sizing: border-box;
+ width: 100%;
+ height: 300px;
+ }
+
-
-
+
+
-
+
- Orientation
-
- top
- bottom
-
+ Orientation
+
+ top
+ bottom
+
diff --git a/test/timestep.html b/test/timestep.html
index 2ed8b5de..473691c0 100644
--- a/test/timestep.html
+++ b/test/timestep.html
@@ -1,32 +1,32 @@
-
-
+
+
-
+
\ No newline at end of file
diff --git a/tools/watch.js b/tools/watch.js
index 6b18f529..79c99a9c 100644
--- a/tools/watch.js
+++ b/tools/watch.js
@@ -15,17 +15,17 @@ var BUILD_COMMAND = 'jake build';
// rebuilt vis.js on change of code
function rebuild() {
- var start = +new Date();
- child_process.exec(BUILD_COMMAND, function () {
- var end = +new Date();
- console.log('rebuilt in ' + (end - start) + ' ms');
- });
+ var start = +new Date();
+ child_process.exec(BUILD_COMMAND, function () {
+ var end = +new Date();
+ console.log('rebuilt in ' + (end - start) + ' ms');
+ });
}
// watch for changes in the code, rebuilt vis.js automatically
watch(WATCH_FOLDER, function(filename) {
- console.log(filename + ' changed');
- rebuild();
+ console.log(filename + ' changed');
+ rebuild();
});
rebuild();
diff --git a/vis.js b/vis.js
index c1dd372e..08ac4818 100644
--- a/vis.js
+++ b/vis.js
@@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library.
*
* @version 0.3.0-SNAPSHOT
- * @date 2013-10-30
+ * @date 2014-01-03
*
* @license
* Copyright (C) 2011-2013 Almende B.V, http://almende.com
@@ -22,7 +22,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.vis=e():"undefined"!=typeof global?global.vis=e():"undefined"!=typeof self&&(self.vis=e())}(function(){var define,module,exports;
+(function(e){if("function"==typeof bootstrap)bootstrap("vis",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeVis=e}else"undefined"!=typeof window?window.vis=e():global.vis=e()})(function(){var define,ses,bootstrap,module,exports;
return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) {
- value = Math.floor(coercedNumber);
- } else {
- value = Math.ceil(coercedNumber);
- }
- }
-
- return value;
- }
-
- function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
- }
-
- function daysInYear(year) {
- return isLeapYear(year) ? 366 : 365;
- }
-
- function isLeapYear(year) {
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
- }
-
- function checkOverflow(m) {
- var overflow;
- if (m._a && m._pf.overflow === -2) {
- overflow =
- m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
- m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
- m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
- m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
- m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
- m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
- -1;
-
- if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
- overflow = DATE;
- }
-
- m._pf.overflow = overflow;
- }
- }
-
- function initializeParsingFlags(config) {
- config._pf = {
- empty : false,
- unusedTokens : [],
- unusedInput : [],
- overflow : -2,
- charsLeftOver : 0,
- nullInput : false,
- invalidMonth : null,
- invalidFormat : false,
- userInvalidated : false,
- iso: false
- };
- }
-
- function isValid(m) {
- if (m._isValid == null) {
- m._isValid = !isNaN(m._d.getTime()) &&
- m._pf.overflow < 0 &&
- !m._pf.empty &&
- !m._pf.invalidMonth &&
- !m._pf.nullInput &&
- !m._pf.invalidFormat &&
- !m._pf.userInvalidated;
-
- if (m._strict) {
- m._isValid = m._isValid &&
- m._pf.charsLeftOver === 0 &&
- m._pf.unusedTokens.length === 0;
- }
- }
- return m._isValid;
+ return units ? unitAliases[units] || units.toLowerCase().replace(/(.)s$/, '$1') : units;
}
- function normalizeLanguage(key) {
- return key ? key.toLowerCase().replace('_', '-') : key;
- }
/************************************
Languages
@@ -2194,15 +1988,9 @@ else {
week : function (mom) {
return weekOfYear(mom, this._week.dow, this._week.doy).week;
},
-
_week : {
dow : 0, // Sunday is the first day of the week.
doy : 6 // The week that contains Jan 1st is the first week of the year.
- },
-
- _invalidDate: 'Invalid date',
- invalidDate: function () {
- return this._invalidDate;
}
});
@@ -2231,60 +2019,28 @@ else {
// definition for 'en', so long as 'en' has already been loaded using
// moment.lang.
function getLangDefinition(key) {
- var i = 0, j, lang, next, split,
- get = function (k) {
- if (!languages[k] && hasModule) {
- try {
- require('./lang/' + k);
- } catch (e) { }
- }
- return languages[k];
- };
-
if (!key) {
return moment.fn._lang;
}
-
- if (!isArray(key)) {
- //short-circuit everything else
- lang = get(key);
- if (lang) {
- return lang;
- }
- key = [key];
- }
-
- //pick the language from the array
- //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
- //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
- while (i < key.length) {
- split = normalizeLanguage(key[i]).split('-');
- j = split.length;
- next = normalizeLanguage(key[i + 1]);
- next = next ? next.split('-') : null;
- while (j > 0) {
- lang = get(split.slice(0, j).join('-'));
- if (lang) {
- return lang;
- }
- if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
- //the next array item is better than a shallower substring of this one
- break;
- }
- j--;
+ if (!languages[key] && hasModule) {
+ try {
+ require('./lang/' + key);
+ } catch (e) {
+ // call with no params to set to default
+ return moment.fn._lang;
}
- i++;
}
- return moment.fn._lang;
+ return languages[key] || moment.fn._lang;
}
+
/************************************
Formatting
************************************/
function removeFormattingTokens(input) {
- if (input.match(/\[[\s\S]/)) {
+ if (input.match(/\[.*\]/)) {
return input.replace(/^\[|\]$/g, "");
}
return input.replace(/\\/g, "");
@@ -2313,10 +2069,6 @@ else {
// format date using native date object
function formatMoment(m, format) {
- if (!m.isValid()) {
- return m.lang().invalidDate();
- }
-
format = expandFormat(format, m.lang());
if (!formatFunctions[format]) {
@@ -2333,11 +2085,9 @@ else {
return lang.longDateFormat(input) || input;
}
- localFormattingTokens.lastIndex = 0;
- while (i >= 0 && localFormattingTokens.test(format)) {
+ while (i-- && (localFormattingTokens.lastIndex = 0,
+ localFormattingTokens.test(format))) {
format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
- localFormattingTokens.lastIndex = 0;
- i -= 1;
}
return format;
@@ -2351,17 +2101,12 @@ else {
// get the regex to find the next token
function getParseRegexForToken(token, config) {
- var a;
switch (token) {
case 'DDDD':
return parseTokenThreeDigits;
case 'YYYY':
- case 'GGGG':
- case 'gggg':
return parseTokenFourDigits;
case 'YYYYY':
- case 'GGGGG':
- case 'ggggg':
return parseTokenSixDigits;
case 'S':
case 'SS':
@@ -2384,13 +2129,9 @@ else {
return parseTokenTimezone;
case 'T':
return parseTokenT;
- case 'SSSS':
- return parseTokenDigits;
case 'MM':
case 'DD':
case 'YY':
- case 'GG':
- case 'gg':
case 'HH':
case 'hh':
case 'mm':
@@ -2402,23 +2143,16 @@ else {
case 'h':
case 'm':
case 's':
- case 'w':
- case 'ww':
- case 'W':
- case 'WW':
- case 'e':
- case 'E':
return parseTokenOneOrTwoDigits;
default :
- a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i"));
- return a;
+ return new RegExp(token.replace('\\', ''));
}
}
function timezoneMinutesFromString(string) {
var tzchunk = (parseTokenTimezone.exec(string) || [])[0],
parts = (tzchunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
- minutes = +(parts[1] * 60) + toInt(parts[2]);
+ minutes = +(parts[1] * 60) + ~~parts[2];
return parts[0] === '+' ? -minutes : minutes;
}
@@ -2432,7 +2166,7 @@ else {
case 'M' : // fall through to MM
case 'MM' :
if (input != null) {
- datePartArray[MONTH] = toInt(input) - 1;
+ datePartArray[1] = ~~input - 1;
}
break;
case 'MMM' : // fall through to MMMM
@@ -2440,33 +2174,33 @@ else {
a = getLangDefinition(config._l).monthsParse(input);
// if we didn't find a month name, mark the date as invalid.
if (a != null) {
- datePartArray[MONTH] = a;
+ datePartArray[1] = a;
} else {
- config._pf.invalidMonth = input;
+ config._isValid = false;
}
break;
// DAY OF MONTH
case 'D' : // fall through to DD
case 'DD' :
if (input != null) {
- datePartArray[DATE] = toInt(input);
+ datePartArray[2] = ~~input;
}
break;
// DAY OF YEAR
case 'DDD' : // fall through to DDDD
case 'DDDD' :
if (input != null) {
- config._dayOfYear = toInt(input);
+ datePartArray[1] = 0;
+ datePartArray[2] = ~~input;
}
-
break;
// YEAR
case 'YY' :
- datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+ datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
break;
case 'YYYY' :
case 'YYYYY' :
- datePartArray[YEAR] = toInt(input);
+ datePartArray[0] = ~~input;
break;
// AM / PM
case 'a' : // fall through to A
@@ -2478,24 +2212,23 @@ else {
case 'HH' : // fall through to hh
case 'h' : // fall through to hh
case 'hh' :
- datePartArray[HOUR] = toInt(input);
+ datePartArray[3] = ~~input;
break;
// MINUTE
case 'm' : // fall through to mm
case 'mm' :
- datePartArray[MINUTE] = toInt(input);
+ datePartArray[4] = ~~input;
break;
// SECOND
case 's' : // fall through to ss
case 'ss' :
- datePartArray[SECOND] = toInt(input);
+ datePartArray[5] = ~~input;
break;
// MILLISECOND
case 'S' :
case 'SS' :
case 'SSS' :
- case 'SSSS' :
- datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
+ datePartArray[6] = ~~ (('0.' + input) * 1000);
break;
// UNIX TIMESTAMP WITH MS
case 'X':
@@ -2507,29 +2240,11 @@ else {
config._useUTC = true;
config._tzm = timezoneMinutesFromString(input);
break;
- case 'w':
- case 'ww':
- case 'W':
- case 'WW':
- case 'd':
- case 'dd':
- case 'ddd':
- case 'dddd':
- case 'e':
- case 'E':
- token = token.substr(0, 1);
- /* falls through */
- case 'gg':
- case 'gggg':
- case 'GG':
- case 'GGGG':
- case 'GGGGG':
- token = token.substr(0, 2);
- if (input) {
- config._w = config._w || {};
- config._w[token] = input;
- }
- break;
+ }
+
+ // if the input is null, the date is not valid
+ if (input == null) {
+ config._isValid = false;
}
}
@@ -2537,65 +2252,19 @@ else {
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
- function dateFromConfig(config) {
- var i, date, input = [], currentDate,
- yearToUse, fixYear, w, temp, lang, weekday, week;
+ function dateFromArray(config) {
+ var i, date, input = [], currentDate;
if (config._d) {
return;
}
- currentDate = currentDateArray(config);
-
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- fixYear = function (val) {
- return val ?
- (val.length < 3 ? (parseInt(val, 10) > 68 ? '19' + val : '20' + val) : val) :
- (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
- };
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
- }
- else {
- lang = getLangDefinition(config._l);
- weekday = w.d != null ? parseWeekday(w.d, lang) :
- (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0);
-
- week = parseInt(w.w, 10) || 1;
-
- //if we're parsing 'd', then the low day numbers may be next week
- if (w.d != null && weekday < lang._week.dow) {
- week++;
- }
-
- temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
- }
-
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
- }
-
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
-
- if (config._dayOfYear > daysInYear(yearToUse)) {
- config._pf._overflowDayOfYear = true;
- }
-
- date = makeUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
-
// Default to current date.
// * if no year, month, day of month are given, default to today
// * if day of month is given, default month and year
// * if month is given, default only year
// * if year is given, don't default anything
+ currentDate = currentDateArray(config);
for (i = 0; i < 3 && config._a[i] == null; ++i) {
config._a[i] = input[i] = currentDate[i];
}
@@ -2606,31 +2275,40 @@ else {
}
// add the offsets to the time to be parsed so that we can have a clean array for checking isValid
- input[HOUR] += toInt((config._tzm || 0) / 60);
- input[MINUTE] += toInt((config._tzm || 0) % 60);
+ input[3] += ~~((config._tzm || 0) / 60);
+ input[4] += ~~((config._tzm || 0) % 60);
+
+ date = new Date(0);
+
+ if (config._useUTC) {
+ date.setUTCFullYear(input[0], input[1], input[2]);
+ date.setUTCHours(input[3], input[4], input[5], input[6]);
+ } else {
+ date.setFullYear(input[0], input[1], input[2]);
+ date.setHours(input[3], input[4], input[5], input[6]);
+ }
- config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+ config._d = date;
}
function dateFromObject(config) {
- var normalizedInput;
+ var o = config._i;
if (config._d) {
return;
}
- normalizedInput = normalizeObjectUnits(config._i);
config._a = [
- normalizedInput.year,
- normalizedInput.month,
- normalizedInput.day,
- normalizedInput.hour,
- normalizedInput.minute,
- normalizedInput.second,
- normalizedInput.millisecond
+ o.years || o.year || o.y,
+ o.months || o.month || o.M,
+ o.days || o.day || o.d,
+ o.hours || o.hour || o.h,
+ o.minutes || o.minute || o.m,
+ o.seconds || o.second || o.s,
+ o.milliseconds || o.millisecond || o.ms
];
- dateFromConfig(config);
+ dateFromArray(config);
}
function currentDateArray(config) {
@@ -2648,116 +2326,74 @@ else {
// date from string and format string
function makeDateFromStringAndFormat(config) {
-
- config._a = [];
- config._pf.empty = true;
-
// This array is used to make a Date, either with `new Date` or `Date.UTC`
var lang = getLangDefinition(config._l),
string = '' + config._i,
- i, parsedInput, tokens, token, skipped,
- stringLength = string.length,
- totalParsedInputLength = 0;
+ i, parsedInput, tokens;
+
+ tokens = expandFormat(config._f, lang).match(formattingTokens);
- tokens = expandFormat(config._f, lang).match(formattingTokens) || [];
+ config._a = [];
for (i = 0; i < tokens.length; i++) {
- token = tokens[i];
- parsedInput = (getParseRegexForToken(token, config).exec(string) || [])[0];
+ parsedInput = (getParseRegexForToken(tokens[i], config).exec(string) || [])[0];
if (parsedInput) {
- skipped = string.substr(0, string.indexOf(parsedInput));
- if (skipped.length > 0) {
- config._pf.unusedInput.push(skipped);
- }
string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
- totalParsedInputLength += parsedInput.length;
}
- // don't parse if it's not a known token
- if (formatTokenFunctions[token]) {
- if (parsedInput) {
- config._pf.empty = false;
- }
- else {
- config._pf.unusedTokens.push(token);
- }
- addTimeToArrayFromToken(token, parsedInput, config);
- }
- else if (config._strict && !parsedInput) {
- config._pf.unusedTokens.push(token);
+ // don't parse if its not a known token
+ if (formatTokenFunctions[tokens[i]]) {
+ addTimeToArrayFromToken(tokens[i], parsedInput, config);
}
}
- // add remaining unparsed input length to the string
- config._pf.charsLeftOver = stringLength - totalParsedInputLength;
- if (string.length > 0) {
- config._pf.unusedInput.push(string);
+ // add remaining unparsed input to the string
+ if (string) {
+ config._il = string;
}
// handle am pm
- if (config._isPm && config._a[HOUR] < 12) {
- config._a[HOUR] += 12;
+ if (config._isPm && config._a[3] < 12) {
+ config._a[3] += 12;
}
// if is 12 am, change hours to 0
- if (config._isPm === false && config._a[HOUR] === 12) {
- config._a[HOUR] = 0;
+ if (config._isPm === false && config._a[3] === 12) {
+ config._a[3] = 0;
}
-
- dateFromConfig(config);
- checkOverflow(config);
- }
-
- function unescapeFormat(s) {
- return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
- return p1 || p2 || p3 || p4;
- });
- }
-
- // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
- function regexpEscape(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ // return
+ dateFromArray(config);
}
// date from string and array of format strings
function makeDateFromStringAndArray(config) {
var tempConfig,
+ tempMoment,
bestMoment,
- scoreToBeat,
+ scoreToBeat = 99,
i,
currentScore;
- if (config._f.length === 0) {
- config._pf.invalidFormat = true;
- config._d = new Date(NaN);
- return;
- }
-
for (i = 0; i < config._f.length; i++) {
- currentScore = 0;
tempConfig = extend({}, config);
- initializeParsingFlags(tempConfig);
tempConfig._f = config._f[i];
makeDateFromStringAndFormat(tempConfig);
+ tempMoment = new Moment(tempConfig);
- if (!isValid(tempConfig)) {
- continue;
- }
-
- // if there is any input that was not parsed add a penalty for that format
- currentScore += tempConfig._pf.charsLeftOver;
-
- //or tokens
- currentScore += tempConfig._pf.unusedTokens.length * 10;
+ currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
- tempConfig._pf.score = currentScore;
+ // if there is any input that was not parsed
+ // add a penalty for that format
+ if (tempMoment._il) {
+ currentScore += tempMoment._il.length;
+ }
- if (scoreToBeat == null || currentScore < scoreToBeat) {
+ if (currentScore < scoreToBeat) {
scoreToBeat = currentScore;
- bestMoment = tempConfig;
+ bestMoment = tempMoment;
}
}
- extend(config, bestMoment || tempConfig);
+ extend(config, bestMoment);
}
// date from iso format
@@ -2767,14 +2403,8 @@ else {
match = isoRegex.exec(string);
if (match) {
- config._pf.iso = true;
- for (i = 4; i > 0; i--) {
- if (match[i]) {
- // match[5] should be "T" or undefined
- config._f = isoDates[i - 1] + (match[6] || " ");
- break;
- }
- }
+ // match[2] should be "T" or undefined
+ config._f = 'YYYY-MM-DD' + (match[2] || " ");
for (i = 0; i < 4; i++) {
if (isoTimes[i][1].exec(string)) {
config._f += isoTimes[i][0];
@@ -2782,11 +2412,10 @@ else {
}
}
if (parseTokenTimezone.exec(string)) {
- config._f += "Z";
+ config._f += " Z";
}
makeDateFromStringAndFormat(config);
- }
- else {
+ } else {
config._d = new Date(string);
}
}
@@ -2803,8 +2432,8 @@ else {
makeDateFromString(config);
} else if (isArray(input)) {
config._a = input.slice(0);
- dateFromConfig(config);
- } else if (isDate(input)) {
+ dateFromArray(config);
+ } else if (input instanceof Date) {
config._d = new Date(+input);
} else if (typeof(input) === 'object') {
dateFromObject(config);
@@ -2813,40 +2442,6 @@ else {
}
}
- function makeDate(y, m, d, h, M, s, ms) {
- //can't just apply() to create a date:
- //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
- var date = new Date(y, m, d, h, M, s, ms);
-
- //the date constructor doesn't accept years < 1970
- if (y < 1970) {
- date.setFullYear(y);
- }
- return date;
- }
-
- function makeUTCDate(y) {
- var date = new Date(Date.UTC.apply(null, arguments));
- if (y < 1970) {
- date.setUTCFullYear(y);
- }
- return date;
- }
-
- function parseWeekday(input, language) {
- if (typeof input === 'string') {
- if (!isNaN(input)) {
- input = parseInt(input, 10);
- }
- else {
- input = language.weekdaysParse(input);
- if (typeof input !== 'number') {
- return null;
- }
- }
- }
- return input;
- }
/************************************
Relative Time
@@ -2914,20 +2509,6 @@ else {
};
}
- //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
- function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
- var d = new Date(Date.UTC(year, 0)).getUTCDay(),
- daysToAdd, dayOfYear;
-
- weekday = weekday != null ? weekday : firstDayOfWeek;
- daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0);
- dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
-
- return {
- year: dayOfYear > 0 ? year : year - 1,
- dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
- };
- }
/************************************
Top Level Functions
@@ -2937,12 +2518,8 @@ else {
var input = config._i,
format = config._f;
- if (typeof config._pf === 'undefined') {
- initializeParsingFlags(config);
- }
-
- if (input === null) {
- return moment.invalid({nullInput: true});
+ if (input === null || input === '') {
+ return null;
}
if (typeof input === 'string') {
@@ -2951,7 +2528,6 @@ else {
if (moment.isMoment(input)) {
config = extend({}, input);
-
config._d = new Date(+input._d);
} else if (format) {
if (isArray(format)) {
@@ -2966,38 +2542,24 @@ else {
return new Moment(config);
}
- moment = function (input, format, lang, strict) {
- if (typeof(lang) === "boolean") {
- strict = lang;
- lang = undefined;
- }
+ moment = function (input, format, lang) {
return makeMoment({
_i : input,
_f : format,
_l : lang,
- _strict : strict,
_isUTC : false
});
};
// creating with utc
- moment.utc = function (input, format, lang, strict) {
- var m;
-
- if (typeof(lang) === "boolean") {
- strict = lang;
- lang = undefined;
- }
- m = makeMoment({
+ moment.utc = function (input, format, lang) {
+ return makeMoment({
_useUTC : true,
_isUTC : true,
_l : lang,
_i : input,
- _f : format,
- _strict : strict
+ _f : format
}).utc();
-
- return m;
};
// creating with unix timestamp (in seconds)
@@ -3010,13 +2572,9 @@ else {
var isDuration = moment.isDuration(input),
isNumber = (typeof input === 'number'),
duration = (isDuration ? input._input : (isNumber ? {} : input)),
- // matching against regexp is expensive, do it on demand
- match = null,
+ matched = aspNetTimeSpanJsonRegex.exec(input),
sign,
- ret,
- parseIso,
- timeEmpty,
- dateTimeEmpty;
+ ret;
if (isNumber) {
if (key) {
@@ -3024,34 +2582,15 @@ else {
} else {
duration.milliseconds = input;
}
- } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
- sign = (match[1] === "-") ? -1 : 1;
+ } else if (matched) {
+ sign = (matched[1] === "-") ? -1 : 1;
duration = {
y: 0,
- d: toInt(match[DATE]) * sign,
- h: toInt(match[HOUR]) * sign,
- m: toInt(match[MINUTE]) * sign,
- s: toInt(match[SECOND]) * sign,
- ms: toInt(match[MILLISECOND]) * sign
- };
- } else if (!!(match = isoDurationRegex.exec(input))) {
- sign = (match[1] === "-") ? -1 : 1;
- parseIso = function (inp) {
- // We'd normally use ~~inp for this, but unfortunately it also
- // converts floats to ints.
- // inp may be undefined, so careful calling replace on it.
- var res = inp && parseFloat(inp.replace(',', '.'));
- // apply sign while we're at it
- return (isNaN(res) ? 0 : res) * sign;
- };
- duration = {
- y: parseIso(match[2]),
- M: parseIso(match[3]),
- d: parseIso(match[4]),
- h: parseIso(match[5]),
- m: parseIso(match[6]),
- s: parseIso(match[7]),
- w: parseIso(match[8])
+ d: ~~matched[2] * sign,
+ h: ~~matched[3] * sign,
+ m: ~~matched[4] * sign,
+ s: ~~matched[5] * sign,
+ ms: ~~matched[6] * sign
};
}
@@ -3078,20 +2617,20 @@ else {
// no arguments are passed in, it will simply return the current global
// language key.
moment.lang = function (key, values) {
- var r;
if (!key) {
return moment.fn._lang._abbr;
}
+ key = key.toLowerCase();
+ key = key.replace('_', '-');
if (values) {
- loadLang(normalizeLanguage(key), values);
+ loadLang(key, values);
} else if (values === null) {
unloadLang(key);
key = 'en';
} else if (!languages[key]) {
getLangDefinition(key);
}
- r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
- return r._abbr;
+ moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
};
// returns language data
@@ -3112,29 +2651,6 @@ else {
return obj instanceof Duration;
};
- for (i = lists.length - 1; i >= 0; --i) {
- makeList(lists[i]);
- }
-
- moment.normalizeUnits = function (units) {
- return normalizeUnits(units);
- };
-
- moment.invalid = function (flags) {
- var m = moment.utc(NaN);
- if (flags != null) {
- extend(m._pf, flags);
- }
- else {
- m._pf.userInvalidated = true;
- }
-
- return m;
- };
-
- moment.parseZone = function (input) {
- return moment(input).parseZone();
- };
/************************************
Moment Prototype
@@ -3156,7 +2672,7 @@ else {
},
toString : function () {
- return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
+ return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
},
toDate : function () {
@@ -3181,24 +2697,22 @@ else {
},
isValid : function () {
- return isValid(this);
- },
-
- isDSTShifted : function () {
-
- if (this._a) {
- return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
+ if (this._isValid == null) {
+ if (this._a) {
+ this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
+ } else {
+ this._isValid = !isNaN(this._d.getTime());
+ }
}
-
- return false;
- },
-
- parsingFlags : function () {
- return extend({}, this._pf);
+ return !!this._isValid;
},
invalidAt: function () {
- return this._pf.overflow;
+ var i, arr1 = this._a, arr2 = (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray();
+ for (i = 6; i >= 0 && arr1[i] === arr2[i]; --i) {
+ // empty loop body
+ }
+ return i;
},
utc : function () {
@@ -3294,7 +2808,8 @@ else {
},
isLeapYear : function () {
- return isLeapYear(this.year());
+ var year = this.year();
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
},
isDST : function () {
@@ -3305,7 +2820,12 @@ else {
day : function (input) {
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
if (input != null) {
- input = parseWeekday(input, this.lang());
+ if (typeof input === 'string') {
+ input = this.lang().weekdaysParse(input);
+ if (typeof input !== 'number') {
+ return this;
+ }
+ }
return this.add({ d : input - day });
} else {
return day;
@@ -3348,7 +2868,7 @@ else {
this.date(1);
/* falls through */
case 'week':
- case 'isoWeek':
+ case 'isoweek':
case 'day':
this.hours(0);
/* falls through */
@@ -3366,7 +2886,7 @@ else {
// weeks are a special case
if (units === 'week') {
this.weekday(0);
- } else if (units === 'isoWeek') {
+ } else if (units === 'isoweek') {
this.isoWeekday(1);
}
@@ -3375,7 +2895,7 @@ else {
endOf: function (units) {
units = normalizeUnits(units);
- return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1);
+ return this.startOf(units).add((units === 'isoweek' ? 'week' : units), 1).subtract('ms', 1);
},
isAfter: function (input, units) {
@@ -3431,13 +2951,6 @@ else {
return this._isUTC ? "Coordinated Universal Time" : "";
},
- parseZone : function () {
- if (typeof this._i === 'string') {
- this.zone(this._i);
- }
- return this;
- },
-
hasAlignedHourOffset : function (input) {
if (!input) {
input = 0;
@@ -3450,7 +2963,7 @@ else {
},
daysInMonth : function () {
- return daysInMonth(this.year(), this.month());
+ return moment.utc([this.year(), this.month() + 1, 0]).date();
},
dayOfYear : function (input) {
@@ -3479,7 +2992,7 @@ else {
},
weekday : function (input) {
- var weekday = (this.day() + 7 - this.lang()._week.dow) % 7;
+ var weekday = (this._d.getDay() + 7 - this.lang()._week.dow) % 7;
return input == null ? weekday : this.add("d", input - weekday);
},
@@ -3492,15 +3005,12 @@ else {
get : function (units) {
units = normalizeUnits(units);
- return this[units]();
+ return this[units.toLowerCase()]();
},
set : function (units, value) {
units = normalizeUnits(units);
- if (typeof this[units] === 'function') {
- this[units](value);
- }
- return this;
+ this[units.toLowerCase()](value);
},
// If passed a language key, it will set the language for this
@@ -3592,7 +3102,7 @@ else {
return this._milliseconds +
this._days * 864e5 +
(this._months % 12) * 2592e6 +
- toInt(this._months / 12) * 31536e6;
+ ~~(this._months / 12) * 31536e6;
},
humanize : function (withSuffix) {
@@ -3641,33 +3151,7 @@ else {
return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
},
- lang : moment.fn.lang,
-
- toIsoString : function () {
- // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
- var years = Math.abs(this.years()),
- months = Math.abs(this.months()),
- days = Math.abs(this.days()),
- hours = Math.abs(this.hours()),
- minutes = Math.abs(this.minutes()),
- seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
-
- if (!this.asSeconds()) {
- // this is the same as C#'s (Noda) and python (isodate)...
- // but not other JS (goog.date)
- return 'P0D';
- }
-
- return (this.asSeconds() < 0 ? '-' : '') +
- 'P' +
- (years ? years + 'Y' : '') +
- (months ? months + 'M' : '') +
- (days ? days + 'D' : '') +
- ((hours || minutes || seconds) ? 'T' : '') +
- (hours ? hours + 'H' : '') +
- (minutes ? minutes + 'M' : '') +
- (seconds ? seconds + 'S' : '');
- }
+ lang : moment.fn.lang
});
function makeDurationGetter(name) {
@@ -3704,7 +3188,7 @@ else {
moment.lang('en', {
ordinal : function (number) {
var b = number % 10,
- output = (toInt(number % 100 / 10) === 1) ? 'th' :
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
(b === 1) ? 'st' :
(b === 2) ? 'nd' :
(b === 3) ? 'rd' : 'th';
@@ -3718,46 +3202,23 @@ else {
Exposing Moment
************************************/
- function makeGlobal(deprecate) {
- var warned = false, local_moment = moment;
- /*global ender:false */
- if (typeof ender !== 'undefined') {
- return;
- }
- // here, `this` means `window` in the browser, or `global` on the server
- // add `moment` as a global object via a string identifier,
- // for Closure Compiler "advanced" mode
- if (deprecate) {
- this.moment = function () {
- if (!warned && console && console.warn) {
- warned = true;
- console.warn(
- "Accessing Moment through the global scope is " +
- "deprecated, and will be removed in an upcoming " +
- "release.");
- }
- return local_moment.apply(null, arguments);
- };
- } else {
- this['moment'] = moment;
- }
- }
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
- makeGlobal(true);
- } else if (typeof define === "function" && define.amd) {
- define("moment", function (require, exports, module) {
- if (module.config().noGlobal !== true) {
- // If user provided noGlobal, he is aware of global
- makeGlobal(module.config().noGlobal === undefined);
- }
-
+ }
+ /*global ender:false */
+ if (typeof ender === 'undefined') {
+ // here, `this` means `window` in the browser, or `global` on the server
+ // add `moment` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode
+ this['moment'] = moment;
+ }
+ /*global define:false */
+ if (typeof define === "function" && define.amd) {
+ define("moment", [], function () {
return moment;
});
- } else {
- makeGlobal();
}
}).call(this);
@@ -3786,31 +3247,31 @@ else {
// it here in that case.
// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
if(!Array.prototype.indexOf) {
- Array.prototype.indexOf = function(obj){
- for(var i = 0; i < this.length; i++){
- if(this[i] == obj){
- return i;
- }
- }
- return -1;
- };
-
- try {
- console.log("Warning: Ancient browser detected. Please update your browser");
- }
- catch (err) {
+ Array.prototype.indexOf = function(obj){
+ for(var i = 0; i < this.length; i++){
+ if(this[i] == obj){
+ return i;
+ }
}
+ return -1;
+ };
+
+ try {
+ console.log("Warning: Ancient browser detected. Please update your browser");
+ }
+ catch (err) {
+ }
}
// Internet Explorer 8 and older does not support Array.forEach, so we define
// it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach
if (!Array.prototype.forEach) {
- Array.prototype.forEach = function(fn, scope) {
- for(var i = 0, len = this.length; i < len; ++i) {
- fn.call(scope || this, this[i], i, this);
- }
+ Array.prototype.forEach = function(fn, scope) {
+ for(var i = 0, len = this.length; i < len; ++i) {
+ fn.call(scope || this, this[i], i, this);
}
+ }
}
// Internet Explorer 8 and older does not support Array.map, so we define it
@@ -3819,106 +3280,106 @@ if (!Array.prototype.forEach) {
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
- Array.prototype.map = function(callback, thisArg) {
-
- var T, A, k;
+ Array.prototype.map = function(callback, thisArg) {
- if (this == null) {
- throw new TypeError(" this is null or not defined");
- }
+ var T, A, k;
- // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
- var O = Object(this);
+ if (this == null) {
+ throw new TypeError(" this is null or not defined");
+ }
- // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
- // 3. Let len be ToUint32(lenValue).
- var len = O.length >>> 0;
+ // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+ var O = Object(this);
- // 4. If IsCallable(callback) is false, throw a TypeError exception.
- // See: http://es5.github.com/#x9.11
- if (typeof callback !== "function") {
- throw new TypeError(callback + " is not a function");
- }
+ // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
+ // 3. Let len be ToUint32(lenValue).
+ var len = O.length >>> 0;
- // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
- if (thisArg) {
- T = thisArg;
- }
+ // 4. If IsCallable(callback) is false, throw a TypeError exception.
+ // See: http://es5.github.com/#x9.11
+ if (typeof callback !== "function") {
+ throw new TypeError(callback + " is not a function");
+ }
- // 6. Let A be a new array created as if by the expression new Array(len) where Array is
- // the standard built-in constructor with that name and len is the value of len.
- A = new Array(len);
+ // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ if (thisArg) {
+ T = thisArg;
+ }
- // 7. Let k be 0
- k = 0;
+ // 6. Let A be a new array created as if by the expression new Array(len) where Array is
+ // the standard built-in constructor with that name and len is the value of len.
+ A = new Array(len);
- // 8. Repeat, while k < len
- while(k < len) {
+ // 7. Let k be 0
+ k = 0;
- var kValue, mappedValue;
+ // 8. Repeat, while k < len
+ while(k < len) {
- // a. Let Pk be ToString(k).
- // This is implicit for LHS operands of the in operator
- // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
- // This step can be combined with c
- // c. If kPresent is true, then
- if (k in O) {
+ var kValue, mappedValue;
- // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
- kValue = O[ k ];
+ // a. Let Pk be ToString(k).
+ // This is implicit for LHS operands of the in operator
+ // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
+ // This step can be combined with c
+ // c. If kPresent is true, then
+ if (k in O) {
- // ii. Let mappedValue be the result of calling the Call internal method of callback
- // with T as the this value and argument list containing kValue, k, and O.
- mappedValue = callback.call(T, kValue, k, O);
+ // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+ kValue = O[ k ];
- // iii. Call the DefineOwnProperty internal method of A with arguments
- // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
- // and false.
+ // ii. Let mappedValue be the result of calling the Call internal method of callback
+ // with T as the this value and argument list containing kValue, k, and O.
+ mappedValue = callback.call(T, kValue, k, O);
- // In browsers that support Object.defineProperty, use the following:
- // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
+ // iii. Call the DefineOwnProperty internal method of A with arguments
+ // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
+ // and false.
- // For best browser support, use the following:
- A[ k ] = mappedValue;
- }
- // d. Increase k by 1.
- k++;
- }
+ // In browsers that support Object.defineProperty, use the following:
+ // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
- // 9. return A
- return A;
- };
+ // For best browser support, use the following:
+ A[ k ] = mappedValue;
+ }
+ // d. Increase k by 1.
+ k++;
+ }
+
+ // 9. return A
+ return A;
+ };
}
// Internet Explorer 8 and older does not support Array.filter, so we define it
// here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
- Array.prototype.filter = function(fun /*, thisp */) {
- "use strict";
+ Array.prototype.filter = function(fun /*, thisp */) {
+ "use strict";
- if (this == null) {
- throw new TypeError();
- }
+ if (this == null) {
+ throw new TypeError();
+ }
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun != "function") {
- throw new TypeError();
- }
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (typeof fun != "function") {
+ throw new TypeError();
+ }
- var res = [];
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- var val = t[i]; // in case fun mutates this
- if (fun.call(thisp, val, i, t))
- res.push(val);
- }
- }
+ var res = [];
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in t) {
+ var val = t[i]; // in case fun mutates this
+ if (fun.call(thisp, val, i, t))
+ res.push(val);
+ }
+ }
- return res;
- };
+ return res;
+ };
}
@@ -3926,112 +3387,112 @@ if (!Array.prototype.filter) {
// here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
- Object.keys = (function () {
- var hasOwnProperty = Object.prototype.hasOwnProperty,
- hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
- dontEnums = [
- 'toString',
- 'toLocaleString',
- 'valueOf',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'constructor'
- ],
- dontEnumsLength = dontEnums.length;
-
- return function (obj) {
- if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
- throw new TypeError('Object.keys called on non-object');
- }
+ Object.keys = (function () {
+ var hasOwnProperty = Object.prototype.hasOwnProperty,
+ hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length;
+
+ return function (obj) {
+ if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
+ throw new TypeError('Object.keys called on non-object');
+ }
- var result = [];
+ var result = [];
- for (var prop in obj) {
- if (hasOwnProperty.call(obj, prop)) result.push(prop);
- }
+ for (var prop in obj) {
+ if (hasOwnProperty.call(obj, prop)) result.push(prop);
+ }
- if (hasDontEnumBug) {
- for (var i=0; i < dontEnumsLength; i++) {
- if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
- }
- }
- return result;
+ if (hasDontEnumBug) {
+ for (var i=0; i < dontEnumsLength; i++) {
+ if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
- })()
+ }
+ return result;
+ }
+ })()
}
// Internet Explorer 8 and older does not support Array.isArray,
// so we define it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray
if(!Array.isArray) {
- Array.isArray = function (vArg) {
- return Object.prototype.toString.call(vArg) === "[object Array]";
- };
+ Array.isArray = function (vArg) {
+ return Object.prototype.toString.call(vArg) === "[object Array]";
+ };
}
// Internet Explorer 8 and older does not support Function.bind,
// so we define it here in that case.
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
-
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
+
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
- return fBound;
- };
+ return fBound;
+ };
}
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
if (!Object.create) {
- Object.create = function (o) {
- if (arguments.length > 1) {
- throw new Error('Object.create implementation only accepts the first parameter.');
- }
- function F() {}
- F.prototype = o;
- return new F();
- };
+ Object.create = function (o) {
+ if (arguments.length > 1) {
+ throw new Error('Object.create implementation only accepts the first parameter.');
+ }
+ function F() {}
+ F.prototype = o;
+ return new F();
+ };
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
-
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
+
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
- return fBound;
- };
+ return fBound;
+ };
}
/**
@@ -4045,7 +3506,7 @@ var util = {};
* @return {Boolean} isNumber
*/
util.isNumber = function isNumber(object) {
- return (object instanceof Number || typeof object == 'number');
+ return (object instanceof Number || typeof object == 'number');
};
/**
@@ -4054,7 +3515,7 @@ util.isNumber = function isNumber(object) {
* @return {Boolean} isString
*/
util.isString = function isString(object) {
- return (object instanceof String || typeof object == 'string');
+ return (object instanceof String || typeof object == 'string');
};
/**
@@ -4063,21 +3524,21 @@ util.isString = function isString(object) {
* @return {Boolean} isDate
*/
util.isDate = function isDate(object) {
- if (object instanceof Date) {
- return true;
+ if (object instanceof Date) {
+ return true;
+ }
+ else if (util.isString(object)) {
+ // test whether this string contains a date
+ var match = ASPDateRegex.exec(object);
+ if (match) {
+ return true;
}
- else if (util.isString(object)) {
- // test whether this string contains a date
- var match = ASPDateRegex.exec(object);
- if (match) {
- return true;
- }
- else if (!isNaN(Date.parse(object))) {
- return true;
- }
+ else if (!isNaN(Date.parse(object))) {
+ return true;
}
+ }
- return false;
+ return false;
};
/**
@@ -4086,10 +3547,10 @@ util.isDate = function isDate(object) {
* @return {Boolean} isDataTable
*/
util.isDataTable = function isDataTable(object) {
- return (typeof (google) !== 'undefined') &&
- (google.visualization) &&
- (google.visualization.DataTable) &&
- (object instanceof google.visualization.DataTable);
+ return (typeof (google) !== 'undefined') &&
+ (google.visualization) &&
+ (google.visualization.DataTable) &&
+ (object instanceof google.visualization.DataTable);
};
/**
@@ -4098,19 +3559,19 @@ util.isDataTable = function isDataTable(object) {
* @return {String} uuid
*/
util.randomUUID = function randomUUID () {
- var S4 = function () {
- return Math.floor(
- Math.random() * 0x10000 /* 65536 */
- ).toString(16);
- };
+ var S4 = function () {
+ return Math.floor(
+ Math.random() * 0x10000 /* 65536 */
+ ).toString(16);
+ };
- return (
- S4() + S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + S4() + S4()
- );
+ return (
+ S4() + S4() + '-' +
+ S4() + '-' +
+ S4() + '-' +
+ S4() + '-' +
+ S4() + S4() + S4()
+ );
};
/**
@@ -4121,16 +3582,16 @@ util.randomUUID = function randomUUID () {
* @return {Object} a
*/
util.extend = function (a, b) {
- for (var i = 1, len = arguments.length; i < len; i++) {
- var other = arguments[i];
- for (var prop in other) {
- if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
- a[prop] = other[prop];
- }
- }
+ for (var i = 1, len = arguments.length; i < len; i++) {
+ var other = arguments[i];
+ for (var prop in other) {
+ if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
+ a[prop] = other[prop];
+ }
}
+ }
- return a;
+ return a;
};
/**
@@ -4143,143 +3604,143 @@ util.extend = function (a, b) {
* @throws Error
*/
util.convert = function convert(object, type) {
- var match;
+ var match;
- if (object === undefined) {
- return undefined;
- }
- if (object === null) {
- return null;
- }
+ if (object === undefined) {
+ return undefined;
+ }
+ if (object === null) {
+ return null;
+ }
- if (!type) {
- return object;
- }
- if (!(typeof type === 'string') && !(type instanceof String)) {
- throw new Error('Type must be a string');
- }
+ if (!type) {
+ return object;
+ }
+ if (!(typeof type === 'string') && !(type instanceof String)) {
+ throw new Error('Type must be a string');
+ }
- //noinspection FallthroughInSwitchStatementJS
- switch (type) {
- case 'boolean':
- case 'Boolean':
- return Boolean(object);
-
- case 'number':
- case 'Number':
- return Number(object.valueOf());
-
- case 'string':
- case 'String':
- return String(object);
-
- case 'Date':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- if (object instanceof Date) {
- return new Date(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return new Date(object.valueOf());
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])); // parse number
- }
- else {
- return moment(object).toDate(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
+ //noinspection FallthroughInSwitchStatementJS
+ switch (type) {
+ case 'boolean':
+ case 'Boolean':
+ return Boolean(object);
- case 'Moment':
- if (util.isNumber(object)) {
- return moment(object);
- }
- if (object instanceof Date) {
- return moment(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return moment(object);
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return moment(Number(match[1])); // parse number
- }
- else {
- return moment(object); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
+ case 'number':
+ case 'Number':
+ return Number(object.valueOf());
- case 'ISODate':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- else if (object instanceof Date) {
- return object.toISOString();
- }
- else if (moment.isMoment(object)) {
- return object.toDate().toISOString();
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])).toISOString(); // parse number
- }
- else {
- return new Date(object).toISOString(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ISODate');
- }
+ case 'string':
+ case 'String':
+ return String(object);
- case 'ASPDate':
- if (util.isNumber(object)) {
- return '/Date(' + object + ')/';
- }
- else if (object instanceof Date) {
- return '/Date(' + object.valueOf() + ')/';
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- var value;
- if (match) {
- // object is an ASP date
- value = new Date(Number(match[1])).valueOf(); // parse number
- }
- else {
- value = new Date(object).valueOf(); // parse string
- }
- return '/Date(' + value + ')/';
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ASPDate');
- }
+ case 'Date':
+ if (util.isNumber(object)) {
+ return new Date(object);
+ }
+ if (object instanceof Date) {
+ return new Date(object.valueOf());
+ }
+ else if (moment.isMoment(object)) {
+ return new Date(object.valueOf());
+ }
+ if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return new Date(Number(match[1])); // parse number
+ }
+ else {
+ return moment(object).toDate(); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type Date');
+ }
- default:
- throw new Error('Cannot convert object of type ' + util.getType(object) +
- ' to type "' + type + '"');
- }
+ case 'Moment':
+ if (util.isNumber(object)) {
+ return moment(object);
+ }
+ if (object instanceof Date) {
+ return moment(object.valueOf());
+ }
+ else if (moment.isMoment(object)) {
+ return moment(object);
+ }
+ if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return moment(Number(match[1])); // parse number
+ }
+ else {
+ return moment(object); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type Date');
+ }
+
+ case 'ISODate':
+ if (util.isNumber(object)) {
+ return new Date(object);
+ }
+ else if (object instanceof Date) {
+ return object.toISOString();
+ }
+ else if (moment.isMoment(object)) {
+ return object.toDate().toISOString();
+ }
+ else if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ if (match) {
+ // object is an ASP date
+ return new Date(Number(match[1])).toISOString(); // parse number
+ }
+ else {
+ return new Date(object).toISOString(); // parse string
+ }
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type ISODate');
+ }
+
+ case 'ASPDate':
+ if (util.isNumber(object)) {
+ return '/Date(' + object + ')/';
+ }
+ else if (object instanceof Date) {
+ return '/Date(' + object.valueOf() + ')/';
+ }
+ else if (util.isString(object)) {
+ match = ASPDateRegex.exec(object);
+ var value;
+ if (match) {
+ // object is an ASP date
+ value = new Date(Number(match[1])).valueOf(); // parse number
+ }
+ else {
+ value = new Date(object).valueOf(); // parse string
+ }
+ return '/Date(' + value + ')/';
+ }
+ else {
+ throw new Error(
+ 'Cannot convert object of type ' + util.getType(object) +
+ ' to type ASPDate');
+ }
+
+ default:
+ throw new Error('Cannot convert object of type ' + util.getType(object) +
+ ' to type "' + type + '"');
+ }
};
// parse ASP.Net Date pattern,
@@ -4293,40 +3754,40 @@ var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
* @return {String} type
*/
util.getType = function getType(object) {
- var type = typeof object;
+ var type = typeof object;
- if (type == 'object') {
- if (object == null) {
- return 'null';
- }
- if (object instanceof Boolean) {
- return 'Boolean';
- }
- if (object instanceof Number) {
- return 'Number';
- }
- if (object instanceof String) {
- return 'String';
- }
- if (object instanceof Array) {
- return 'Array';
- }
- if (object instanceof Date) {
- return 'Date';
- }
- return 'Object';
+ if (type == 'object') {
+ if (object == null) {
+ return 'null';
+ }
+ if (object instanceof Boolean) {
+ return 'Boolean';
}
- else if (type == 'number') {
- return 'Number';
+ if (object instanceof Number) {
+ return 'Number';
}
- else if (type == 'boolean') {
- return 'Boolean';
+ if (object instanceof String) {
+ return 'String';
+ }
+ if (object instanceof Array) {
+ return 'Array';
}
- else if (type == 'string') {
- return 'String';
+ if (object instanceof Date) {
+ return 'Date';
}
+ return 'Object';
+ }
+ else if (type == 'number') {
+ return 'Number';
+ }
+ else if (type == 'boolean') {
+ return 'Boolean';
+ }
+ else if (type == 'string') {
+ return 'String';
+ }
- return type;
+ return type;
};
/**
@@ -4336,17 +3797,17 @@ util.getType = function getType(object) {
* in the browser page.
*/
util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var left = elem.offsetLeft;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- left += e.offsetLeft;
- left -= e.scrollLeft;
- e = e.offsetParent;
- }
- return left;
+ var doc = document.documentElement;
+ var body = document.body;
+
+ var left = elem.offsetLeft;
+ var e = elem.offsetParent;
+ while (e != null && e != body && e != doc) {
+ left += e.offsetLeft;
+ left -= e.scrollLeft;
+ e = e.offsetParent;
+ }
+ return left;
};
/**
@@ -4356,17 +3817,17 @@ util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
* in the browser page.
*/
util.getAbsoluteTop = function getAbsoluteTop (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var top = elem.offsetTop;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- top += e.offsetTop;
- top -= e.scrollTop;
- e = e.offsetParent;
- }
- return top;
+ var doc = document.documentElement;
+ var body = document.body;
+
+ var top = elem.offsetTop;
+ var e = elem.offsetParent;
+ while (e != null && e != body && e != doc) {
+ top += e.offsetTop;
+ top -= e.scrollTop;
+ e = e.offsetParent;
+ }
+ return top;
};
/**
@@ -4375,24 +3836,24 @@ util.getAbsoluteTop = function getAbsoluteTop (elem) {
* @return {Number} pageY
*/
util.getPageY = function getPageY (event) {
- if ('pageY' in event) {
- return event.pageY;
+ if ('pageY' in event) {
+ return event.pageY;
+ }
+ else {
+ var clientY;
+ if (('targetTouches' in event) && event.targetTouches.length) {
+ clientY = event.targetTouches[0].clientY;
}
else {
- var clientY;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientY = event.targetTouches[0].clientY;
- }
- else {
- clientY = event.clientY;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientY +
- ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
- ( doc && doc.clientTop || body && body.clientTop || 0 );
+ clientY = event.clientY;
}
+
+ var doc = document.documentElement;
+ var body = document.body;
+ return clientY +
+ ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
+ ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
};
/**
@@ -4401,24 +3862,24 @@ util.getPageY = function getPageY (event) {
* @return {Number} pageX
*/
util.getPageX = function getPageX (event) {
- if ('pageY' in event) {
- return event.pageX;
+ if ('pageY' in event) {
+ return event.pageX;
+ }
+ else {
+ var clientX;
+ if (('targetTouches' in event) && event.targetTouches.length) {
+ clientX = event.targetTouches[0].clientX;
}
else {
- var clientX;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientX = event.targetTouches[0].clientX;
- }
- else {
- clientX = event.clientX;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientX +
- ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
- ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ clientX = event.clientX;
}
+
+ var doc = document.documentElement;
+ var body = document.body;
+ return clientX +
+ ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
+ ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ }
};
/**
@@ -4427,11 +3888,11 @@ util.getPageX = function getPageX (event) {
* @param {String} className
*/
util.addClassName = function addClassName(elem, className) {
- var classes = elem.className.split(' ');
- if (classes.indexOf(className) == -1) {
- classes.push(className); // add the class to the array
- elem.className = classes.join(' ');
- }
+ var classes = elem.className.split(' ');
+ if (classes.indexOf(className) == -1) {
+ classes.push(className); // add the class to the array
+ elem.className = classes.join(' ');
+ }
};
/**
@@ -4440,12 +3901,12 @@ util.addClassName = function addClassName(elem, className) {
* @param {String} className
*/
util.removeClassName = function removeClassname(elem, className) {
- var classes = elem.className.split(' ');
- var index = classes.indexOf(className);
- if (index != -1) {
- classes.splice(index, 1); // remove the class from the array
- elem.className = classes.join(' ');
- }
+ var classes = elem.className.split(' ');
+ var index = classes.indexOf(className);
+ if (index != -1) {
+ classes.splice(index, 1); // remove the class from the array
+ elem.className = classes.join(' ');
+ }
};
/**
@@ -4458,22 +3919,22 @@ util.removeClassName = function removeClassname(elem, className) {
* callback(value, index, object)
*/
util.forEach = function forEach (object, callback) {
- var i,
- len;
- if (object instanceof Array) {
- // array
- for (i = 0, len = object.length; i < len; i++) {
- callback(object[i], i, object);
- }
+ var i,
+ len;
+ if (object instanceof Array) {
+ // array
+ for (i = 0, len = object.length; i < len; i++) {
+ callback(object[i], i, object);
}
- else {
- // object
- for (i in object) {
- if (object.hasOwnProperty(i)) {
- callback(object[i], i, object);
- }
- }
+ }
+ else {
+ // object
+ for (i in object) {
+ if (object.hasOwnProperty(i)) {
+ callback(object[i], i, object);
+ }
}
+ }
};
/**
@@ -4484,13 +3945,13 @@ util.forEach = function forEach (object, callback) {
* @return {Boolean} changed
*/
util.updateProperty = function updateProp (object, key, value) {
- if (object[key] !== value) {
- object[key] = value;
- return true;
- }
- else {
- return false;
- }
+ if (object[key] !== value) {
+ object[key] = value;
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -4502,18 +3963,18 @@ util.updateProperty = function updateProp (object, key, value) {
* @param {boolean} [useCapture]
*/
util.addEventListener = function addEventListener(element, action, listener, useCapture) {
- if (element.addEventListener) {
- if (useCapture === undefined)
- useCapture = false;
+ if (element.addEventListener) {
+ if (useCapture === undefined)
+ useCapture = false;
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.addEventListener(action, listener, useCapture);
- } else {
- element.attachEvent("on" + action, listener); // IE browsers
+ if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
+ action = "DOMMouseScroll"; // For Firefox
}
+
+ element.addEventListener(action, listener, useCapture);
+ } else {
+ element.attachEvent("on" + action, listener); // IE browsers
+ }
};
/**
@@ -4524,20 +3985,20 @@ util.addEventListener = function addEventListener(element, action, listener, use
* @param {boolean} [useCapture]
*/
util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
- if (element.removeEventListener) {
- // non-IE browsers
- if (useCapture === undefined)
- useCapture = false;
+ if (element.removeEventListener) {
+ // non-IE browsers
+ if (useCapture === undefined)
+ useCapture = false;
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.removeEventListener(action, listener, useCapture);
- } else {
- // IE browsers
- element.detachEvent("on" + action, listener);
+ if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
+ action = "DOMMouseScroll"; // For Firefox
}
+
+ element.removeEventListener(action, listener, useCapture);
+ } else {
+ // IE browsers
+ element.detachEvent("on" + action, listener);
+ }
};
@@ -4547,41 +4008,41 @@ util.removeEventListener = function removeEventListener(element, action, listene
* @return {Element} target element
*/
util.getTarget = function getTarget(event) {
- // code from http://www.quirksmode.org/js/events_properties.html
- if (!event) {
- event = window.event;
- }
+ // code from http://www.quirksmode.org/js/events_properties.html
+ if (!event) {
+ event = window.event;
+ }
- var target;
+ var target;
- if (event.target) {
- target = event.target;
- }
- else if (event.srcElement) {
- target = event.srcElement;
- }
+ if (event.target) {
+ target = event.target;
+ }
+ else if (event.srcElement) {
+ target = event.srcElement;
+ }
- if (target.nodeType != undefined && target.nodeType == 3) {
- // defeat Safari bug
- target = target.parentNode;
- }
+ if (target.nodeType != undefined && target.nodeType == 3) {
+ // defeat Safari bug
+ target = target.parentNode;
+ }
- return target;
+ return target;
};
/**
* Stop event propagation
*/
util.stopPropagation = function stopPropagation(event) {
- if (!event)
- event = window.event;
+ if (!event)
+ event = window.event;
- if (event.stopPropagation) {
- event.stopPropagation(); // non-IE browsers
- }
- else {
- event.cancelBubble = true; // IE browsers
- }
+ if (event.stopPropagation) {
+ event.stopPropagation(); // non-IE browsers
+ }
+ else {
+ event.cancelBubble = true; // IE browsers
+ }
};
@@ -4589,15 +4050,15 @@ util.stopPropagation = function stopPropagation(event) {
* Cancels the event if it is cancelable, without stopping further propagation of the event.
*/
util.preventDefault = function preventDefault (event) {
- if (!event)
- event = window.event;
+ if (!event)
+ event = window.event;
- if (event.preventDefault) {
- event.preventDefault(); // non-IE browsers
- }
- else {
- event.returnValue = false; // IE browsers
- }
+ if (event.preventDefault) {
+ event.preventDefault(); // non-IE browsers
+ }
+ else {
+ event.returnValue = false; // IE browsers
+ }
};
@@ -4610,15 +4071,15 @@ util.option = {};
* @returns {Boolean} bool
*/
util.option.asBoolean = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return (value != false);
- }
+ if (value != null) {
+ return (value != false);
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -4628,15 +4089,15 @@ util.option.asBoolean = function (value, defaultValue) {
* @returns {Number} number
*/
util.option.asNumber = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return Number(value) || defaultValue || null;
- }
+ if (value != null) {
+ return Number(value) || defaultValue || null;
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -4646,15 +4107,15 @@ util.option.asNumber = function (value, defaultValue) {
* @returns {String} str
*/
util.option.asString = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (value != null) {
- return String(value);
- }
+ if (value != null) {
+ return String(value);
+ }
- return defaultValue || null;
+ return defaultValue || null;
};
/**
@@ -4664,19 +4125,19 @@ util.option.asString = function (value, defaultValue) {
* @returns {String} size
*/
util.option.asSize = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- if (util.isString(value)) {
- return value;
- }
- else if (util.isNumber(value)) {
- return value + 'px';
- }
- else {
- return defaultValue || null;
- }
+ if (util.isString(value)) {
+ return value;
+ }
+ else if (util.isNumber(value)) {
+ return value + 'px';
+ }
+ else {
+ return defaultValue || null;
+ }
};
/**
@@ -4686,11 +4147,11 @@ util.option.asSize = function (value, defaultValue) {
* @returns {HTMLElement | null} dom
*/
util.option.asElement = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
+ if (typeof value == 'function') {
+ value = value();
+ }
- return value || defaultValue || null;
+ return value || defaultValue || null;
};
/**
@@ -4698,27 +4159,27 @@ util.option.asElement = function (value, defaultValue) {
* @param {String} css Text containing css
*/
util.loadCss = function (css) {
- if (typeof document === 'undefined') {
- return;
- }
-
- // get the script location, and built the css file name from the js file name
- // http://stackoverflow.com/a/2161748/1262753
- // var scripts = document.getElementsByTagName('script');
- // var jsFile = scripts[scripts.length-1].src.split('?')[0];
- // var cssFile = jsFile.substring(0, jsFile.length - 2) + 'css';
+ if (typeof document === 'undefined') {
+ return;
+ }
- // inject css
- // http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript
- var style = document.createElement('style');
- style.type = 'text/css';
- if (style.styleSheet){
- style.styleSheet.cssText = css;
- } else {
- style.appendChild(document.createTextNode(css));
- }
+ // get the script location, and built the css file name from the js file name
+ // http://stackoverflow.com/a/2161748/1262753
+ // var scripts = document.getElementsByTagName('script');
+ // var jsFile = scripts[scripts.length-1].src.split('?')[0];
+ // var cssFile = jsFile.substring(0, jsFile.length - 2) + 'css';
+
+ // inject css
+ // http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript
+ var style = document.createElement('style');
+ style.type = 'text/css';
+ if (style.styleSheet){
+ style.styleSheet.cssText = css;
+ } else {
+ style.appendChild(document.createTextNode(css));
+ }
- document.getElementsByTagName('head')[0].appendChild(style);
+ document.getElementsByTagName('head')[0].appendChild(style);
};
/**
@@ -4726,116 +4187,116 @@ util.loadCss = function (css) {
*/
// TODO: replace usage of the event listener for the EventBus
var events = {
- 'listeners': [],
-
- /**
- * Find a single listener by its object
- * @param {Object} object
- * @return {Number} index -1 when not found
- */
- 'indexOf': function (object) {
- var listeners = this.listeners;
- for (var i = 0, iMax = this.listeners.length; i < iMax; i++) {
- var listener = listeners[i];
- if (listener && listener.object == object) {
- return i;
- }
- }
- return -1;
- },
-
- /**
- * Add an event listener
- * @param {Object} object
- * @param {String} event The name of an event, for example 'select'
- * @param {function} callback The callback method, called when the
- * event takes place
- */
- 'addListener': function (object, event, callback) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (!listener) {
- listener = {
- 'object': object,
- 'events': {}
- };
- this.listeners.push(listener);
- }
-
- var callbacks = listener.events[event];
- if (!callbacks) {
- callbacks = [];
- listener.events[event] = callbacks;
- }
-
- // add the callback if it does not yet exist
- if (callbacks.indexOf(callback) == -1) {
- callbacks.push(callback);
+ 'listeners': [],
+
+ /**
+ * Find a single listener by its object
+ * @param {Object} object
+ * @return {Number} index -1 when not found
+ */
+ 'indexOf': function (object) {
+ var listeners = this.listeners;
+ for (var i = 0, iMax = this.listeners.length; i < iMax; i++) {
+ var listener = listeners[i];
+ if (listener && listener.object == object) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ /**
+ * Add an event listener
+ * @param {Object} object
+ * @param {String} event The name of an event, for example 'select'
+ * @param {function} callback The callback method, called when the
+ * event takes place
+ */
+ 'addListener': function (object, event, callback) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (!listener) {
+ listener = {
+ 'object': object,
+ 'events': {}
+ };
+ this.listeners.push(listener);
+ }
+
+ var callbacks = listener.events[event];
+ if (!callbacks) {
+ callbacks = [];
+ listener.events[event] = callbacks;
+ }
+
+ // add the callback if it does not yet exist
+ if (callbacks.indexOf(callback) == -1) {
+ callbacks.push(callback);
+ }
+ },
+
+ /**
+ * Remove an event listener
+ * @param {Object} object
+ * @param {String} event The name of an event, for example 'select'
+ * @param {function} callback The registered callback method
+ */
+ 'removeListener': function (object, event, callback) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (listener) {
+ var callbacks = listener.events[event];
+ if (callbacks) {
+ index = callbacks.indexOf(callback);
+ if (index != -1) {
+ callbacks.splice(index, 1);
+ }
+
+ // remove the array when empty
+ if (callbacks.length == 0) {
+ delete listener.events[event];
}
- },
-
- /**
- * Remove an event listener
- * @param {Object} object
- * @param {String} event The name of an event, for example 'select'
- * @param {function} callback The registered callback method
- */
- 'removeListener': function (object, event, callback) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (listener) {
- var callbacks = listener.events[event];
- if (callbacks) {
- index = callbacks.indexOf(callback);
- if (index != -1) {
- callbacks.splice(index, 1);
- }
-
- // remove the array when empty
- if (callbacks.length == 0) {
- delete listener.events[event];
- }
- }
+ }
- // count the number of registered events. remove listener when empty
- var count = 0;
- var events = listener.events;
- for (var e in events) {
- if (events.hasOwnProperty(e)) {
- count++;
- }
- }
- if (count == 0) {
- delete this.listeners[index];
- }
+ // count the number of registered events. remove listener when empty
+ var count = 0;
+ var events = listener.events;
+ for (var e in events) {
+ if (events.hasOwnProperty(e)) {
+ count++;
}
- },
-
- /**
- * Remove all registered event listeners
- */
- 'removeAllListeners': function () {
- this.listeners = [];
- },
+ }
+ if (count == 0) {
+ delete this.listeners[index];
+ }
+ }
+ },
- /**
- * Trigger an event. All registered event handlers will be called
- * @param {Object} object
- * @param {String} event
- * @param {Object} properties (optional)
- */
- 'trigger': function (object, event, properties) {
- var index = this.indexOf(object);
- var listener = this.listeners[index];
- if (listener) {
- var callbacks = listener.events[event];
- if (callbacks) {
- for (var i = 0, iMax = callbacks.length; i < iMax; i++) {
- callbacks[i](properties);
- }
- }
+ /**
+ * Remove all registered event listeners
+ */
+ 'removeAllListeners': function () {
+ this.listeners = [];
+ },
+
+ /**
+ * Trigger an event. All registered event handlers will be called
+ * @param {Object} object
+ * @param {String} event
+ * @param {Object} properties (optional)
+ */
+ 'trigger': function (object, event, properties) {
+ var index = this.indexOf(object);
+ var listener = this.listeners[index];
+ if (listener) {
+ var callbacks = listener.events[event];
+ if (callbacks) {
+ for (var i = 0, iMax = callbacks.length; i < iMax; i++) {
+ callbacks[i](properties);
}
+ }
}
+ }
};
/**
@@ -4843,7 +4304,7 @@ var events = {
* @constructor EventBus
*/
function EventBus() {
- this.subscriptions = [];
+ this.subscriptions = [];
}
/**
@@ -4856,21 +4317,21 @@ function EventBus() {
* @returns {String} id A subscription id
*/
EventBus.prototype.on = function (event, callback, target) {
- var regexp = (event instanceof RegExp) ?
- event :
- new RegExp(event.replace('*', '\\w+'));
-
- var subscription = {
- id: util.randomUUID(),
- event: event,
- regexp: regexp,
- callback: (typeof callback === 'function') ? callback : null,
- target: target
- };
+ var regexp = (event instanceof RegExp) ?
+ event :
+ new RegExp(event.replace('*', '\\w+'));
+
+ var subscription = {
+ id: util.randomUUID(),
+ event: event,
+ regexp: regexp,
+ callback: (typeof callback === 'function') ? callback : null,
+ target: target
+ };
- this.subscriptions.push(subscription);
+ this.subscriptions.push(subscription);
- return subscription.id;
+ return subscription.id;
};
/**
@@ -4882,33 +4343,33 @@ EventBus.prototype.on = function (event, callback, target) {
* callback, and target.
*/
EventBus.prototype.off = function (filter) {
- var i = 0;
- while (i < this.subscriptions.length) {
- var subscription = this.subscriptions[i];
-
- var match = true;
- if (filter instanceof Object) {
- // filter is an object. All fields must match
- for (var prop in filter) {
- if (filter.hasOwnProperty(prop)) {
- if (filter[prop] !== subscription[prop]) {
- match = false;
- }
- }
- }
- }
- else {
- // filter is a string, filter on id
- match = (subscription.id == filter);
+ var i = 0;
+ while (i < this.subscriptions.length) {
+ var subscription = this.subscriptions[i];
+
+ var match = true;
+ if (filter instanceof Object) {
+ // filter is an object. All fields must match
+ for (var prop in filter) {
+ if (filter.hasOwnProperty(prop)) {
+ if (filter[prop] !== subscription[prop]) {
+ match = false;
+ }
}
+ }
+ }
+ else {
+ // filter is a string, filter on id
+ match = (subscription.id == filter);
+ }
- if (match) {
- this.subscriptions.splice(i, 1);
- }
- else {
- i++;
- }
+ if (match) {
+ this.subscriptions.splice(i, 1);
}
+ else {
+ i++;
+ }
+ }
};
/**
@@ -4918,14 +4379,14 @@ EventBus.prototype.off = function (filter) {
* @param {*} [source]
*/
EventBus.prototype.emit = function (event, data, source) {
- for (var i =0; i < this.subscriptions.length; i++) {
- var subscription = this.subscriptions[i];
- if (subscription.regexp.test(event)) {
- if (subscription.callback) {
- subscription.callback(event, data, source);
- }
- }
+ for (var i =0; i < this.subscriptions.length; i++) {
+ var subscription = this.subscriptions[i];
+ if (subscription.regexp.test(event)) {
+ if (subscription.callback) {
+ subscription.callback(event, data, source);
+ }
}
+ }
};
/**
@@ -4966,31 +4427,31 @@ EventBus.prototype.emit = function (event, data, source) {
*/
// TODO: add a DataSet constructor DataSet(data, options)
function DataSet (options) {
- this.id = util.randomUUID();
-
- this.options = options || {};
- this.data = {}; // map with data indexed by id
- this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
- this.convert = {}; // field types by field name
-
- if (this.options.convert) {
- for (var field in this.options.convert) {
- if (this.options.convert.hasOwnProperty(field)) {
- var value = this.options.convert[field];
- if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
- this.convert[field] = 'Date';
- }
- else {
- this.convert[field] = value;
- }
- }
+ this.id = util.randomUUID();
+
+ this.options = options || {};
+ this.data = {}; // map with data indexed by id
+ this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
+ this.convert = {}; // field types by field name
+
+ if (this.options.convert) {
+ for (var field in this.options.convert) {
+ if (this.options.convert.hasOwnProperty(field)) {
+ var value = this.options.convert[field];
+ if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
+ this.convert[field] = 'Date';
+ }
+ else {
+ this.convert[field] = value;
}
+ }
}
+ }
- // event subscribers
- this.subscribers = {};
+ // event subscribers
+ this.subscribers = {};
- this.internalIds = {}; // internally generated id's
+ this.internalIds = {}; // internally generated id's
}
/**
@@ -5003,15 +4464,15 @@ function DataSet (options) {
* {String | Number} senderId
*/
DataSet.prototype.subscribe = function (event, callback) {
- var subscribers = this.subscribers[event];
- if (!subscribers) {
- subscribers = [];
- this.subscribers[event] = subscribers;
- }
+ var subscribers = this.subscribers[event];
+ if (!subscribers) {
+ subscribers = [];
+ this.subscribers[event] = subscribers;
+ }
- subscribers.push({
- callback: callback
- });
+ subscribers.push({
+ callback: callback
+ });
};
/**
@@ -5020,12 +4481,12 @@ DataSet.prototype.subscribe = function (event, callback) {
* @param {function} callback
*/
DataSet.prototype.unsubscribe = function (event, callback) {
- var subscribers = this.subscribers[event];
- if (subscribers) {
- this.subscribers[event] = subscribers.filter(function (listener) {
- return (listener.callback != callback);
- });
- }
+ var subscribers = this.subscribers[event];
+ if (subscribers) {
+ this.subscribers[event] = subscribers.filter(function (listener) {
+ return (listener.callback != callback);
+ });
+ }
};
/**
@@ -5036,24 +4497,24 @@ DataSet.prototype.unsubscribe = function (event, callback) {
* @private
*/
DataSet.prototype._trigger = function (event, params, senderId) {
- if (event == '*') {
- throw new Error('Cannot trigger event *');
- }
+ if (event == '*') {
+ throw new Error('Cannot trigger event *');
+ }
- var subscribers = [];
- if (event in this.subscribers) {
- subscribers = subscribers.concat(this.subscribers[event]);
- }
- if ('*' in this.subscribers) {
- subscribers = subscribers.concat(this.subscribers['*']);
- }
+ var subscribers = [];
+ if (event in this.subscribers) {
+ subscribers = subscribers.concat(this.subscribers[event]);
+ }
+ if ('*' in this.subscribers) {
+ subscribers = subscribers.concat(this.subscribers['*']);
+ }
- for (var i = 0; i < subscribers.length; i++) {
- var subscriber = subscribers[i];
- if (subscriber.callback) {
- subscriber.callback(event, params, senderId || null);
- }
+ for (var i = 0; i < subscribers.length; i++) {
+ var subscriber = subscribers[i];
+ if (subscriber.callback) {
+ subscriber.callback(event, params, senderId || null);
}
+ }
};
/**
@@ -5064,45 +4525,45 @@ DataSet.prototype._trigger = function (event, params, senderId) {
* @return {Array} addedIds Array with the ids of the added items
*/
DataSet.prototype.add = function (data, senderId) {
- var addedIds = [],
- id,
- me = this;
-
- if (data instanceof Array) {
- // Array
- for (var i = 0, len = data.length; i < len; i++) {
- id = me._addItem(data[i]);
- addedIds.push(id);
- }
- }
- else if (util.isDataTable(data)) {
- // Google DataTable
- var columns = this._getColumnNames(data);
- for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
- var item = {};
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- item[field] = data.getValue(row, col);
- }
+ var addedIds = [],
+ id,
+ me = this;
- id = me._addItem(item);
- addedIds.push(id);
- }
- }
- else if (data instanceof Object) {
- // Single item
- id = me._addItem(data);
- addedIds.push(id);
- }
- else {
- throw new Error('Unknown dataType');
+ if (data instanceof Array) {
+ // Array
+ for (var i = 0, len = data.length; i < len; i++) {
+ id = me._addItem(data[i]);
+ addedIds.push(id);
}
+ }
+ else if (util.isDataTable(data)) {
+ // Google DataTable
+ var columns = this._getColumnNames(data);
+ for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
+ var item = {};
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ item[field] = data.getValue(row, col);
+ }
- if (addedIds.length) {
- this._trigger('add', {items: addedIds}, senderId);
+ id = me._addItem(item);
+ addedIds.push(id);
}
+ }
+ else if (data instanceof Object) {
+ // Single item
+ id = me._addItem(data);
+ addedIds.push(id);
+ }
+ else {
+ throw new Error('Unknown dataType');
+ }
+
+ if (addedIds.length) {
+ this._trigger('add', {items: addedIds}, senderId);
+ }
- return addedIds;
+ return addedIds;
};
/**
@@ -5112,60 +4573,60 @@ DataSet.prototype.add = function (data, senderId) {
* @return {Array} updatedIds The ids of the added or updated items
*/
DataSet.prototype.update = function (data, senderId) {
- var addedIds = [],
- updatedIds = [],
- me = this,
- fieldId = me.fieldId;
-
- var addOrUpdate = function (item) {
- var id = item[fieldId];
- if (me.data[id]) {
- // update item
- id = me._updateItem(item);
- updatedIds.push(id);
- }
- else {
- // add new item
- id = me._addItem(item);
- addedIds.push(id);
- }
- };
-
- if (data instanceof Array) {
- // Array
- for (var i = 0, len = data.length; i < len; i++) {
- addOrUpdate(data[i]);
- }
- }
- else if (util.isDataTable(data)) {
- // Google DataTable
- var columns = this._getColumnNames(data);
- for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
- var item = {};
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- item[field] = data.getValue(row, col);
- }
-
- addOrUpdate(item);
- }
- }
- else if (data instanceof Object) {
- // Single item
- addOrUpdate(data);
+ var addedIds = [],
+ updatedIds = [],
+ me = this,
+ fieldId = me.fieldId;
+
+ var addOrUpdate = function (item) {
+ var id = item[fieldId];
+ if (me.data[id]) {
+ // update item
+ id = me._updateItem(item);
+ updatedIds.push(id);
}
else {
- throw new Error('Unknown dataType');
+ // add new item
+ id = me._addItem(item);
+ addedIds.push(id);
}
+ };
- if (addedIds.length) {
- this._trigger('add', {items: addedIds}, senderId);
+ if (data instanceof Array) {
+ // Array
+ for (var i = 0, len = data.length; i < len; i++) {
+ addOrUpdate(data[i]);
}
- if (updatedIds.length) {
- this._trigger('update', {items: updatedIds}, senderId);
+ }
+ else if (util.isDataTable(data)) {
+ // Google DataTable
+ var columns = this._getColumnNames(data);
+ for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
+ var item = {};
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ item[field] = data.getValue(row, col);
+ }
+
+ addOrUpdate(item);
}
+ }
+ else if (data instanceof Object) {
+ // Single item
+ addOrUpdate(data);
+ }
+ else {
+ throw new Error('Unknown dataType');
+ }
- return addedIds.concat(updatedIds);
+ if (addedIds.length) {
+ this._trigger('add', {items: addedIds}, senderId);
+ }
+ if (updatedIds.length) {
+ this._trigger('update', {items: updatedIds}, senderId);
+ }
+
+ return addedIds.concat(updatedIds);
};
/**
@@ -5204,138 +4665,138 @@ DataSet.prototype.update = function (data, senderId) {
* @throws Error
*/
DataSet.prototype.get = function (args) {
- var me = this;
+ var me = this;
+
+ // parse the arguments
+ var id, ids, options, data;
+ var firstType = util.getType(arguments[0]);
+ if (firstType == 'String' || firstType == 'Number') {
+ // get(id [, options] [, data])
+ id = arguments[0];
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else if (firstType == 'Array') {
+ // get(ids [, options] [, data])
+ ids = arguments[0];
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else {
+ // get([, options] [, data])
+ options = arguments[0];
+ data = arguments[1];
+ }
+
+ // determine the return type
+ var type;
+ if (options && options.type) {
+ type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
- // parse the arguments
- var id, ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number') {
- // get(id [, options] [, data])
- id = arguments[0];
- options = arguments[1];
- data = arguments[2];
- }
- else if (firstType == 'Array') {
- // get(ids [, options] [, data])
- ids = arguments[0];
- options = arguments[1];
- data = arguments[2];
+ if (data && (type != util.getType(data))) {
+ throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
+ 'does not correspond with specified options.type (' + options.type + ')');
}
- else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
+ if (type == 'DataTable' && !util.isDataTable(data)) {
+ throw new Error('Parameter "data" must be a DataTable ' +
+ 'when options.type is "DataTable"');
}
+ }
+ else if (data) {
+ type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
+ }
+ else {
+ type = 'Array';
+ }
- // determine the return type
- var type;
- if (options && options.type) {
- type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
+ // build options
+ var convert = options && options.convert || this.options.convert;
+ var filter = options && options.filter;
+ var items = [], item, itemId, i, len;
- if (data && (type != util.getType(data))) {
- throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
- 'does not correspond with specified options.type (' + options.type + ')');
- }
- if (type == 'DataTable' && !util.isDataTable(data)) {
- throw new Error('Parameter "data" must be a DataTable ' +
- 'when options.type is "DataTable"');
- }
+ // convert items
+ if (id != undefined) {
+ // return a single item
+ item = me._getItem(id, convert);
+ if (filter && !filter(item)) {
+ item = null;
}
- else if (data) {
- type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
+ }
+ else if (ids != undefined) {
+ // return a subset of items
+ for (i = 0, len = ids.length; i < len; i++) {
+ item = me._getItem(ids[i], convert);
+ if (!filter || filter(item)) {
+ items.push(item);
+ }
}
- else {
- type = 'Array';
+ }
+ else {
+ // return all items
+ for (itemId in this.data) {
+ if (this.data.hasOwnProperty(itemId)) {
+ item = me._getItem(itemId, convert);
+ if (!filter || filter(item)) {
+ items.push(item);
+ }
+ }
}
+ }
- // build options
- var convert = options && options.convert || this.options.convert;
- var filter = options && options.filter;
- var items = [], item, itemId, i, len;
+ // order the results
+ if (options && options.order && id == undefined) {
+ this._sort(items, options.order);
+ }
- // convert items
+ // filter fields of the items
+ if (options && options.fields) {
+ var fields = options.fields;
if (id != undefined) {
- // return a single item
- item = me._getItem(id, convert);
- if (filter && !filter(item)) {
- item = null;
- }
- }
- else if (ids != undefined) {
- // return a subset of items
- for (i = 0, len = ids.length; i < len; i++) {
- item = me._getItem(ids[i], convert);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
+ item = this._filterFields(item, fields);
}
else {
- // return all items
- for (itemId in this.data) {
- if (this.data.hasOwnProperty(itemId)) {
- item = me._getItem(itemId, convert);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
- }
+ for (i = 0, len = items.length; i < len; i++) {
+ items[i] = this._filterFields(items[i], fields);
+ }
}
+ }
- // order the results
- if (options && options.order && id == undefined) {
- this._sort(items, options.order);
+ // return the results
+ if (type == 'DataTable') {
+ var columns = this._getColumnNames(data);
+ if (id != undefined) {
+ // append a single item to the data table
+ me._appendRow(data, columns, item);
}
-
- // filter fields of the items
- if (options && options.fields) {
- var fields = options.fields;
- if (id != undefined) {
- item = this._filterFields(item, fields);
- }
- else {
- for (i = 0, len = items.length; i < len; i++) {
- items[i] = this._filterFields(items[i], fields);
- }
- }
+ else {
+ // copy the items to the provided data table
+ for (i = 0, len = items.length; i < len; i++) {
+ me._appendRow(data, columns, items[i]);
+ }
}
-
- // return the results
- if (type == 'DataTable') {
- var columns = this._getColumnNames(data);
- if (id != undefined) {
- // append a single item to the data table
- me._appendRow(data, columns, item);
- }
- else {
- // copy the items to the provided data table
- for (i = 0, len = items.length; i < len; i++) {
- me._appendRow(data, columns, items[i]);
- }
- }
- return data;
+ return data;
+ }
+ else {
+ // return an array
+ if (id != undefined) {
+ // a single item
+ return item;
}
else {
- // return an array
- if (id != undefined) {
- // a single item
- return item;
- }
- else {
- // multiple items
- if (data) {
- // copy the items to the provided array
- for (i = 0, len = items.length; i < len; i++) {
- data.push(items[i]);
- }
- return data;
- }
- else {
- // just return our array
- return items;
- }
+ // multiple items
+ if (data) {
+ // copy the items to the provided array
+ for (i = 0, len = items.length; i < len; i++) {
+ data.push(items[i]);
}
+ return data;
+ }
+ else {
+ // just return our array
+ return items;
+ }
}
+ }
};
/**
@@ -5347,78 +4808,78 @@ DataSet.prototype.get = function (args) {
* @return {Array} ids
*/
DataSet.prototype.getIds = function (options) {
- var data = this.data,
- filter = options && options.filter,
- order = options && options.order,
- convert = options && options.convert || this.options.convert,
- i,
- len,
- id,
- item,
- items,
- ids = [];
-
- if (filter) {
- // get filtered items
- if (order) {
- // create ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (filter(item)) {
- items.push(item);
- }
- }
- }
+ var data = this.data,
+ filter = options && options.filter,
+ order = options && options.order,
+ convert = options && options.convert || this.options.convert,
+ i,
+ len,
+ id,
+ item,
+ items,
+ ids = [];
+
+ if (filter) {
+ // get filtered items
+ if (order) {
+ // create ordered list
+ items = [];
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (filter(item)) {
+ items.push(item);
+ }
+ }
+ }
- this._sort(items, order);
+ this._sort(items, order);
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
- }
- }
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (filter(item)) {
- ids.push(item[this.fieldId]);
- }
- }
- }
- }
+ for (i = 0, len = items.length; i < len; i++) {
+ ids[i] = items[i][this.fieldId];
+ }
}
else {
- // get all items
- if (order) {
- // create an ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- items.push(data[id]);
- }
- }
+ // create unordered list
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (filter(item)) {
+ ids.push(item[this.fieldId]);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // get all items
+ if (order) {
+ // create an ordered list
+ items = [];
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ items.push(data[id]);
+ }
+ }
- this._sort(items, order);
+ this._sort(items, order);
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
- }
- }
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = data[id];
- ids.push(item[this.fieldId]);
- }
- }
+ for (i = 0, len = items.length; i < len; i++) {
+ ids[i] = items[i][this.fieldId];
+ }
+ }
+ else {
+ // create unordered list
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = data[id];
+ ids.push(item[this.fieldId]);
}
+ }
}
+ }
- return ids;
+ return ids;
};
/**
@@ -5433,33 +4894,33 @@ DataSet.prototype.getIds = function (options) {
* a field name or custom sort function.
*/
DataSet.prototype.forEach = function (callback, options) {
- var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
- data = this.data,
- item,
- id;
+ var filter = options && options.filter,
+ convert = options && options.convert || this.options.convert,
+ data = this.data,
+ item,
+ id;
- if (options && options.order) {
- // execute forEach on ordered list
- var items = this.get(options);
+ if (options && options.order) {
+ // execute forEach on ordered list
+ var items = this.get(options);
- for (var i = 0, len = items.length; i < len; i++) {
- item = items[i];
- id = item[this.fieldId];
- callback(item, id);
- }
+ for (var i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ id = item[this.fieldId];
+ callback(item, id);
}
- else {
- // unordered
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (!filter || filter(item)) {
- callback(item, id);
- }
- }
+ }
+ else {
+ // unordered
+ for (id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (!filter || filter(item)) {
+ callback(item, id);
}
+ }
}
+ }
};
/**
@@ -5474,28 +4935,28 @@ DataSet.prototype.forEach = function (callback, options) {
* @return {Object[]} mappedItems
*/
DataSet.prototype.map = function (callback, options) {
- var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
- mappedItems = [],
- data = this.data,
- item;
-
- // convert and filter items
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
- if (!filter || filter(item)) {
- mappedItems.push(callback(item, id));
- }
- }
+ var filter = options && options.filter,
+ convert = options && options.convert || this.options.convert,
+ mappedItems = [],
+ data = this.data,
+ item;
+
+ // convert and filter items
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ item = this._getItem(id, convert);
+ if (!filter || filter(item)) {
+ mappedItems.push(callback(item, id));
+ }
}
+ }
- // order items
- if (options && options.order) {
- this._sort(mappedItems, options.order);
- }
+ // order items
+ if (options && options.order) {
+ this._sort(mappedItems, options.order);
+ }
- return mappedItems;
+ return mappedItems;
};
/**
@@ -5506,15 +4967,15 @@ DataSet.prototype.map = function (callback, options) {
* @private
*/
DataSet.prototype._filterFields = function (item, fields) {
- var filteredItem = {};
+ var filteredItem = {};
- for (var field in item) {
- if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
- filteredItem[field] = item[field];
- }
+ for (var field in item) {
+ if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
+ filteredItem[field] = item[field];
}
+ }
- return filteredItem;
+ return filteredItem;
};
/**
@@ -5524,24 +4985,24 @@ DataSet.prototype._filterFields = function (item, fields) {
* @private
*/
DataSet.prototype._sort = function (items, order) {
- if (util.isString(order)) {
- // order by provided field name
- var name = order; // field name
- items.sort(function (a, b) {
- var av = a[name];
- var bv = b[name];
- return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
- });
- }
- else if (typeof order === 'function') {
- // order by sort function
- items.sort(order);
- }
- // TODO: extend order by an Object {field:String, direction:String}
- // where direction can be 'asc' or 'desc'
- else {
- throw new TypeError('Order must be a function or a string');
- }
+ if (util.isString(order)) {
+ // order by provided field name
+ var name = order; // field name
+ items.sort(function (a, b) {
+ var av = a[name];
+ var bv = b[name];
+ return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
+ });
+ }
+ else if (typeof order === 'function') {
+ // order by sort function
+ items.sort(order);
+ }
+ // TODO: extend order by an Object {field:String, direction:String}
+ // where direction can be 'asc' or 'desc'
+ else {
+ throw new TypeError('Order must be a function or a string');
+ }
};
/**
@@ -5552,29 +5013,29 @@ DataSet.prototype._sort = function (items, order) {
* @return {Array} removedIds
*/
DataSet.prototype.remove = function (id, senderId) {
- var removedIds = [],
- i, len, removedId;
-
- if (id instanceof Array) {
- for (i = 0, len = id.length; i < len; i++) {
- removedId = this._remove(id[i]);
- if (removedId != null) {
- removedIds.push(removedId);
- }
- }
+ var removedIds = [],
+ i, len, removedId;
+
+ if (id instanceof Array) {
+ for (i = 0, len = id.length; i < len; i++) {
+ removedId = this._remove(id[i]);
+ if (removedId != null) {
+ removedIds.push(removedId);
+ }
}
- else {
- removedId = this._remove(id);
- if (removedId != null) {
- removedIds.push(removedId);
- }
+ }
+ else {
+ removedId = this._remove(id);
+ if (removedId != null) {
+ removedIds.push(removedId);
}
+ }
- if (removedIds.length) {
- this._trigger('remove', {items: removedIds}, senderId);
- }
+ if (removedIds.length) {
+ this._trigger('remove', {items: removedIds}, senderId);
+ }
- return removedIds;
+ return removedIds;
};
/**
@@ -5584,22 +5045,22 @@ DataSet.prototype.remove = function (id, senderId) {
* @private
*/
DataSet.prototype._remove = function (id) {
- if (util.isNumber(id) || util.isString(id)) {
- if (this.data[id]) {
- delete this.data[id];
- delete this.internalIds[id];
- return id;
- }
+ if (util.isNumber(id) || util.isString(id)) {
+ if (this.data[id]) {
+ delete this.data[id];
+ delete this.internalIds[id];
+ return id;
}
- else if (id instanceof Object) {
- var itemId = id[this.fieldId];
- if (itemId && this.data[itemId]) {
- delete this.data[itemId];
- delete this.internalIds[itemId];
- return itemId;
- }
+ }
+ else if (id instanceof Object) {
+ var itemId = id[this.fieldId];
+ if (itemId && this.data[itemId]) {
+ delete this.data[itemId];
+ delete this.internalIds[itemId];
+ return itemId;
}
- return null;
+ }
+ return null;
};
/**
@@ -5608,14 +5069,14 @@ DataSet.prototype._remove = function (id) {
* @return {Array} removedIds The ids of all removed items
*/
DataSet.prototype.clear = function (senderId) {
- var ids = Object.keys(this.data);
+ var ids = Object.keys(this.data);
- this.data = {};
- this.internalIds = {};
+ this.data = {};
+ this.internalIds = {};
- this._trigger('remove', {items: ids}, senderId);
+ this._trigger('remove', {items: ids}, senderId);
- return ids;
+ return ids;
};
/**
@@ -5624,22 +5085,22 @@ DataSet.prototype.clear = function (senderId) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.max = function (field) {
- var data = this.data,
- max = null,
- maxField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!max || itemField > maxField)) {
- max = item;
- maxField = itemField;
- }
- }
+ var data = this.data,
+ max = null,
+ maxField = null;
+
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ var item = data[id];
+ var itemField = item[field];
+ if (itemField != null && (!max || itemField > maxField)) {
+ max = item;
+ maxField = itemField;
+ }
}
+ }
- return max;
+ return max;
};
/**
@@ -5648,22 +5109,22 @@ DataSet.prototype.max = function (field) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.min = function (field) {
- var data = this.data,
- min = null,
- minField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!min || itemField < minField)) {
- min = item;
- minField = itemField;
- }
- }
+ var data = this.data,
+ min = null,
+ minField = null;
+
+ for (var id in data) {
+ if (data.hasOwnProperty(id)) {
+ var item = data[id];
+ var itemField = item[field];
+ if (itemField != null && (!min || itemField < minField)) {
+ min = item;
+ minField = itemField;
+ }
}
+ }
- return min;
+ return min;
};
/**
@@ -5675,30 +5136,30 @@ DataSet.prototype.min = function (field) {
* The returned array is unordered.
*/
DataSet.prototype.distinct = function (field) {
- var data = this.data,
- values = [],
- fieldType = this.options.convert[field],
- count = 0;
-
- for (var prop in data) {
- if (data.hasOwnProperty(prop)) {
- var item = data[prop];
- var value = util.convert(item[field], fieldType);
- var exists = false;
- for (var i = 0; i < count; i++) {
- if (values[i] == value) {
- exists = true;
- break;
- }
- }
- if (!exists) {
- values[count] = value;
- count++;
- }
+ var data = this.data,
+ values = [],
+ fieldType = this.options.convert[field],
+ count = 0;
+
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ var item = data[prop];
+ var value = util.convert(item[field], fieldType);
+ var exists = false;
+ for (var i = 0; i < count; i++) {
+ if (values[i] == value) {
+ exists = true;
+ break;
}
+ }
+ if (!exists) {
+ values[count] = value;
+ count++;
+ }
}
+ }
- return values;
+ return values;
};
/**
@@ -5708,32 +5169,32 @@ DataSet.prototype.distinct = function (field) {
* @private
*/
DataSet.prototype._addItem = function (item) {
- var id = item[this.fieldId];
+ var id = item[this.fieldId];
- if (id != undefined) {
- // check whether this id is already taken
- if (this.data[id]) {
- // item already exists
- throw new Error('Cannot add item: item with id ' + id + ' already exists');
- }
- }
- else {
- // generate an id
- id = util.randomUUID();
- item[this.fieldId] = id;
- this.internalIds[id] = item;
+ if (id != undefined) {
+ // check whether this id is already taken
+ if (this.data[id]) {
+ // item already exists
+ throw new Error('Cannot add item: item with id ' + id + ' already exists');
}
+ }
+ else {
+ // generate an id
+ id = util.randomUUID();
+ item[this.fieldId] = id;
+ this.internalIds[id] = item;
+ }
- var d = {};
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
+ var d = {};
+ for (var field in item) {
+ if (item.hasOwnProperty(field)) {
+ var fieldType = this.convert[field]; // type may be undefined
+ d[field] = util.convert(item[field], fieldType);
}
- this.data[id] = d;
+ }
+ this.data[id] = d;
- return id;
+ return id;
};
/**
@@ -5744,43 +5205,43 @@ DataSet.prototype._addItem = function (item) {
* @private
*/
DataSet.prototype._getItem = function (id, convert) {
- var field, value;
-
- // get the item from the dataset
- var raw = this.data[id];
- if (!raw) {
- return null;
- }
-
- // convert the items field types
- var converted = {},
- fieldId = this.fieldId,
- internalIds = this.internalIds;
- if (convert) {
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || !(value in internalIds)) {
- converted[field] = util.convert(value, convert[field]);
- }
- }
+ var field, value;
+
+ // get the item from the dataset
+ var raw = this.data[id];
+ if (!raw) {
+ return null;
+ }
+
+ // convert the items field types
+ var converted = {},
+ fieldId = this.fieldId,
+ internalIds = this.internalIds;
+ if (convert) {
+ for (field in raw) {
+ if (raw.hasOwnProperty(field)) {
+ value = raw[field];
+ // output all fields, except internal ids
+ if ((field != fieldId) || !(value in internalIds)) {
+ converted[field] = util.convert(value, convert[field]);
}
+ }
}
- else {
- // no field types specified, no converting needed
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || !(value in internalIds)) {
- converted[field] = value;
- }
- }
+ }
+ else {
+ // no field types specified, no converting needed
+ for (field in raw) {
+ if (raw.hasOwnProperty(field)) {
+ value = raw[field];
+ // output all fields, except internal ids
+ if ((field != fieldId) || !(value in internalIds)) {
+ converted[field] = value;
}
+ }
}
+ }
- return converted;
+ return converted;
};
/**
@@ -5792,25 +5253,25 @@ DataSet.prototype._getItem = function (id, convert) {
* @private
*/
DataSet.prototype._updateItem = function (item) {
- var id = item[this.fieldId];
- if (id == undefined) {
- throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
- }
- var d = this.data[id];
- if (!d) {
- // item doesn't exist
- throw new Error('Cannot update item: no item with id ' + id + ' found');
- }
+ var id = item[this.fieldId];
+ if (id == undefined) {
+ throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
+ }
+ var d = this.data[id];
+ if (!d) {
+ // item doesn't exist
+ throw new Error('Cannot update item: no item with id ' + id + ' found');
+ }
- // merge with current item
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
+ // merge with current item
+ for (var field in item) {
+ if (item.hasOwnProperty(field)) {
+ var fieldType = this.convert[field]; // type may be undefined
+ d[field] = util.convert(item[field], fieldType);
}
+ }
- return id;
+ return id;
};
/**
@@ -5820,11 +5281,11 @@ DataSet.prototype._updateItem = function (item) {
* @private
*/
DataSet.prototype._getColumnNames = function (dataTable) {
- var columns = [];
- for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
- columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
- }
- return columns;
+ var columns = [];
+ for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
+ columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
+ }
+ return columns;
};
/**
@@ -5835,12 +5296,12 @@ DataSet.prototype._getColumnNames = function (dataTable) {
* @private
*/
DataSet.prototype._appendRow = function (dataTable, columns, item) {
- var row = dataTable.addRow();
+ var row = dataTable.addRow();
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- dataTable.setValue(row, col, item[field]);
- }
+ for (var col = 0, cols = columns.length; col < cols; col++) {
+ var field = columns[col];
+ dataTable.setValue(row, col, item[field]);
+ }
};
/**
@@ -5854,67 +5315,70 @@ DataSet.prototype._appendRow = function (dataTable, columns, item) {
* @constructor DataView
*/
function DataView (data, options) {
- this.id = util.randomUUID();
+ this.id = util.randomUUID();
- this.data = null;
- this.ids = {}; // ids of the items currently in memory (just contains a boolean true)
- this.options = options || {};
- this.fieldId = 'id'; // name of the field containing id
- this.subscribers = {}; // event subscribers
+ this.data = null;
+ this.ids = {}; // ids of the items currently in memory (just contains a boolean true)
+ this.options = options || {};
+ this.fieldId = 'id'; // name of the field containing id
+ this.subscribers = {}; // event subscribers
- var me = this;
- this.listener = function () {
- me._onEvent.apply(me, arguments);
- };
+ var me = this;
+ this.listener = function () {
+ me._onEvent.apply(me, arguments);
+ };
- this.setData(data);
+ this.setData(data);
}
+// TODO: implement a function .config() to dynamically update things like configured filter
+// and trigger changes accordingly
+
/**
* Set a data source for the view
* @param {DataSet | DataView} data
*/
DataView.prototype.setData = function (data) {
- var ids, dataItems, i, len;
+ var ids, dataItems, i, len;
- if (this.data) {
- // unsubscribe from current dataset
- if (this.data.unsubscribe) {
- this.data.unsubscribe('*', this.listener);
- }
+ if (this.data) {
+ // unsubscribe from current dataset
+ if (this.data.unsubscribe) {
+ this.data.unsubscribe('*', this.listener);
+ }
- // trigger a remove of all items in memory
- ids = [];
- for (var id in this.ids) {
- if (this.ids.hasOwnProperty(id)) {
- ids.push(id);
- }
- }
- this.ids = {};
- this._trigger('remove', {items: ids});
+ // trigger a remove of all items in memory
+ ids = [];
+ for (var id in this.ids) {
+ if (this.ids.hasOwnProperty(id)) {
+ ids.push(id);
+ }
}
+ this.ids = {};
+ this._trigger('remove', {items: ids});
+ }
- this.data = data;
+ this.data = data;
- if (this.data) {
- // update fieldId
- this.fieldId = this.options.fieldId ||
- (this.data && this.data.options && this.data.options.fieldId) ||
- 'id';
+ if (this.data) {
+ // update fieldId
+ this.fieldId = this.options.fieldId ||
+ (this.data && this.data.options && this.data.options.fieldId) ||
+ 'id';
- // trigger an add of all added items
- ids = this.data.getIds({filter: this.options && this.options.filter});
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- this.ids[id] = true;
- }
- this._trigger('add', {items: ids});
+ // trigger an add of all added items
+ ids = this.data.getIds({filter: this.options && this.options.filter});
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ this.ids[id] = true;
+ }
+ this._trigger('add', {items: ids});
- // subscribe to new dataset
- if (this.data.subscribe) {
- this.data.subscribe('*', this.listener);
- }
+ // subscribe to new dataset
+ if (this.data.subscribe) {
+ this.data.subscribe('*', this.listener);
}
+ }
};
/**
@@ -5952,42 +5416,42 @@ DataView.prototype.setData = function (data) {
* @param args
*/
DataView.prototype.get = function (args) {
- var me = this;
-
- // parse the arguments
- var ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
- // get(id(s) [, options] [, data])
- ids = arguments[0]; // can be a single id or an array with ids
- options = arguments[1];
- data = arguments[2];
- }
- else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
- }
+ var me = this;
+
+ // parse the arguments
+ var ids, options, data;
+ var firstType = util.getType(arguments[0]);
+ if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
+ // get(id(s) [, options] [, data])
+ ids = arguments[0]; // can be a single id or an array with ids
+ options = arguments[1];
+ data = arguments[2];
+ }
+ else {
+ // get([, options] [, data])
+ options = arguments[0];
+ data = arguments[1];
+ }
- // extend the options with the default options and provided options
- var viewOptions = util.extend({}, this.options, options);
+ // extend the options with the default options and provided options
+ var viewOptions = util.extend({}, this.options, options);
- // create a combined filter method when needed
- if (this.options.filter && options && options.filter) {
- viewOptions.filter = function (item) {
- return me.options.filter(item) && options.filter(item);
- }
+ // create a combined filter method when needed
+ if (this.options.filter && options && options.filter) {
+ viewOptions.filter = function (item) {
+ return me.options.filter(item) && options.filter(item);
}
+ }
- // build up the call to the linked data set
- var getArguments = [];
- if (ids != undefined) {
- getArguments.push(ids);
- }
- getArguments.push(viewOptions);
- getArguments.push(data);
+ // build up the call to the linked data set
+ var getArguments = [];
+ if (ids != undefined) {
+ getArguments.push(ids);
+ }
+ getArguments.push(viewOptions);
+ getArguments.push(data);
- return this.data && this.data.get.apply(this.data, getArguments);
+ return this.data && this.data.get.apply(this.data, getArguments);
};
/**
@@ -5999,36 +5463,36 @@ DataView.prototype.get = function (args) {
* @return {Array} ids
*/
DataView.prototype.getIds = function (options) {
- var ids;
+ var ids;
- if (this.data) {
- var defaultFilter = this.options.filter;
- var filter;
+ if (this.data) {
+ var defaultFilter = this.options.filter;
+ var filter;
- if (options && options.filter) {
- if (defaultFilter) {
- filter = function (item) {
- return defaultFilter(item) && options.filter(item);
- }
- }
- else {
- filter = options.filter;
- }
+ if (options && options.filter) {
+ if (defaultFilter) {
+ filter = function (item) {
+ return defaultFilter(item) && options.filter(item);
}
- else {
- filter = defaultFilter;
- }
-
- ids = this.data.getIds({
- filter: filter,
- order: options && options.order
- });
+ }
+ else {
+ filter = options.filter;
+ }
}
else {
- ids = [];
+ filter = defaultFilter;
}
- return ids;
+ ids = this.data.getIds({
+ filter: filter,
+ order: options && options.order
+ });
+ }
+ else {
+ ids = [];
+ }
+
+ return ids;
};
/**
@@ -6041,80 +5505,80 @@ DataView.prototype.getIds = function (options) {
* @private
*/
DataView.prototype._onEvent = function (event, params, senderId) {
- var i, len, id, item,
- ids = params && params.items,
- data = this.data,
- added = [],
- updated = [],
- removed = [];
-
- if (ids && data) {
- switch (event) {
- case 'add':
- // filter the ids of the added items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
- if (item) {
- this.ids[id] = true;
- added.push(id);
- }
- }
+ var i, len, id, item,
+ ids = params && params.items,
+ data = this.data,
+ added = [],
+ updated = [],
+ removed = [];
+
+ if (ids && data) {
+ switch (event) {
+ case 'add':
+ // filter the ids of the added items
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ item = this.get(id);
+ if (item) {
+ this.ids[id] = true;
+ added.push(id);
+ }
+ }
- break;
+ break;
- case 'update':
- // determine the event from the views viewpoint: an updated
- // item can be added, updated, or removed from this view.
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
-
- if (item) {
- if (this.ids[id]) {
- updated.push(id);
- }
- else {
- this.ids[id] = true;
- added.push(id);
- }
- }
- else {
- if (this.ids[id]) {
- delete this.ids[id];
- removed.push(id);
- }
- else {
- // nothing interesting for me :-(
- }
- }
- }
+ case 'update':
+ // determine the event from the views viewpoint: an updated
+ // item can be added, updated, or removed from this view.
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ item = this.get(id);
- break;
+ if (item) {
+ if (this.ids[id]) {
+ updated.push(id);
+ }
+ else {
+ this.ids[id] = true;
+ added.push(id);
+ }
+ }
+ else {
+ if (this.ids[id]) {
+ delete this.ids[id];
+ removed.push(id);
+ }
+ else {
+ // nothing interesting for me :-(
+ }
+ }
+ }
- case 'remove':
- // filter the ids of the removed items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- if (this.ids[id]) {
- delete this.ids[id];
- removed.push(id);
- }
- }
+ break;
- break;
+ case 'remove':
+ // filter the ids of the removed items
+ for (i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ if (this.ids[id]) {
+ delete this.ids[id];
+ removed.push(id);
+ }
}
- if (added.length) {
- this._trigger('add', {items: added}, senderId);
- }
- if (updated.length) {
- this._trigger('update', {items: updated}, senderId);
- }
- if (removed.length) {
- this._trigger('remove', {items: removed}, senderId);
- }
+ break;
+ }
+
+ if (added.length) {
+ this._trigger('add', {items: added}, senderId);
+ }
+ if (updated.length) {
+ this._trigger('update', {items: updated}, senderId);
}
+ if (removed.length) {
+ this._trigger('remove', {items: removed}, senderId);
+ }
+ }
};
// copy subscription functionality from DataSet
@@ -6149,29 +5613,29 @@ DataView.prototype._trigger = DataSet.prototype._trigger;
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
TimeStep = function(start, end, minimumStep) {
- // variables
- this.current = new Date();
- this._start = new Date();
- this._end = new Date();
+ // variables
+ this.current = new Date();
+ this._start = new Date();
+ this._end = new Date();
- this.autoScale = true;
- this.scale = TimeStep.SCALE.DAY;
- this.step = 1;
+ this.autoScale = true;
+ this.scale = TimeStep.SCALE.DAY;
+ this.step = 1;
- // initialize the range
- this.setRange(start, end, minimumStep);
+ // initialize the range
+ this.setRange(start, end, minimumStep);
};
/// enum scale
TimeStep.SCALE = {
- MILLISECOND: 1,
- SECOND: 2,
- MINUTE: 3,
- HOUR: 4,
- DAY: 5,
- WEEKDAY: 6,
- MONTH: 7,
- YEAR: 8
+ MILLISECOND: 1,
+ SECOND: 2,
+ MINUTE: 3,
+ HOUR: 4,
+ DAY: 5,
+ WEEKDAY: 6,
+ MONTH: 7,
+ YEAR: 8
};
@@ -6186,24 +5650,24 @@ TimeStep.SCALE = {
* @param {int} [minimumStep] Optional. Minimum step size in milliseconds
*/
TimeStep.prototype.setRange = function(start, end, minimumStep) {
- if (!(start instanceof Date) || !(end instanceof Date)) {
- throw "No legal start or end date in method setRange";
- }
+ if (!(start instanceof Date) || !(end instanceof Date)) {
+ throw "No legal start or end date in method setRange";
+ }
- this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
- this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
+ this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
+ this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
- if (this.autoScale) {
- this.setMinimumStep(minimumStep);
- }
+ if (this.autoScale) {
+ this.setMinimumStep(minimumStep);
+ }
};
/**
* Set the range iterator to the start date.
*/
TimeStep.prototype.first = function() {
- this.current = new Date(this._start.valueOf());
- this.roundToMinor();
+ this.current = new Date(this._start.valueOf());
+ this.roundToMinor();
};
/**
@@ -6211,36 +5675,36 @@ TimeStep.prototype.first = function() {
* This must be executed once when the current date is set to start Date
*/
TimeStep.prototype.roundToMinor = function() {
- // round to floor
- // IMPORTANT: we have no breaks in this switch! (this is no bug)
- //noinspection FallthroughInSwitchStatementJS
- switch (this.scale) {
- case TimeStep.SCALE.YEAR:
- this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
- this.current.setMonth(0);
- case TimeStep.SCALE.MONTH: this.current.setDate(1);
- case TimeStep.SCALE.DAY: // intentional fall through
- case TimeStep.SCALE.WEEKDAY: this.current.setHours(0);
- case TimeStep.SCALE.HOUR: this.current.setMinutes(0);
- case TimeStep.SCALE.MINUTE: this.current.setSeconds(0);
- case TimeStep.SCALE.SECOND: this.current.setMilliseconds(0);
- //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds
- }
+ // round to floor
+ // IMPORTANT: we have no breaks in this switch! (this is no bug)
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.scale) {
+ case TimeStep.SCALE.YEAR:
+ this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
+ this.current.setMonth(0);
+ case TimeStep.SCALE.MONTH: this.current.setDate(1);
+ case TimeStep.SCALE.DAY: // intentional fall through
+ case TimeStep.SCALE.WEEKDAY: this.current.setHours(0);
+ case TimeStep.SCALE.HOUR: this.current.setMinutes(0);
+ case TimeStep.SCALE.MINUTE: this.current.setSeconds(0);
+ case TimeStep.SCALE.SECOND: this.current.setMilliseconds(0);
+ //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds
+ }
- if (this.step != 1) {
- // round down to the first minor value that is a multiple of the current step size
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
- case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
- case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
- case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
- default: break;
- }
+ if (this.step != 1) {
+ // round down to the first minor value that is a multiple of the current step size
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
+ case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
+ case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
+ case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
+ default: break;
}
+ }
};
/**
@@ -6248,70 +5712,70 @@ TimeStep.prototype.roundToMinor = function() {
* @return {boolean} true if the current date has not passed the end date
*/
TimeStep.prototype.hasNext = function () {
- return (this.current.valueOf() <= this._end.valueOf());
+ return (this.current.valueOf() <= this._end.valueOf());
};
/**
* Do the next step
*/
TimeStep.prototype.next = function() {
- var prev = this.current.valueOf();
-
- // Two cases, needed to prevent issues with switching daylight savings
- // (end of March and end of October)
- if (this.current.getMonth() < 6) {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:
-
- this.current = new Date(this.current.valueOf() + this.step); break;
- case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break;
- case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
- case TimeStep.SCALE.HOUR:
- this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
- // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
- var h = this.current.getHours();
- this.current.setHours(h - (h % this.step));
- break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
- default: break;
- }
+ var prev = this.current.valueOf();
+
+ // Two cases, needed to prevent issues with switching daylight savings
+ // (end of March and end of October)
+ if (this.current.getMonth() < 6) {
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:
+
+ this.current = new Date(this.current.valueOf() + this.step); break;
+ case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break;
+ case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
+ case TimeStep.SCALE.HOUR:
+ this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
+ // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
+ var h = this.current.getHours();
+ this.current.setHours(h - (h % this.step));
+ break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
+ default: break;
}
- else {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: this.current = new Date(this.current.valueOf() + this.step); break;
- case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() + this.step); break;
- case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() + this.step); break;
- case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() + this.step); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
- case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
- case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
- default: break;
- }
+ }
+ else {
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: this.current = new Date(this.current.valueOf() + this.step); break;
+ case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() + this.step); break;
+ case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() + this.step); break;
+ case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() + this.step); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
+ case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
+ case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
+ default: break;
}
+ }
- if (this.step != 1) {
- // round down to the correct major value
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
- case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
- case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
- case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break;
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
- case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
- case TimeStep.SCALE.YEAR: break; // nothing to do for year
- default: break;
- }
+ if (this.step != 1) {
+ // round down to the correct major value
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
+ case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
+ case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
+ case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break;
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
+ case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
+ case TimeStep.SCALE.YEAR: break; // nothing to do for year
+ default: break;
}
+ }
- // safety mechanism: if current time is still unchanged, move to the end
- if (this.current.valueOf() == prev) {
- this.current = new Date(this._end.valueOf());
- }
+ // safety mechanism: if current time is still unchanged, move to the end
+ if (this.current.valueOf() == prev) {
+ this.current = new Date(this._end.valueOf());
+ }
};
@@ -6320,7 +5784,7 @@ TimeStep.prototype.next = function() {
* @return {Date} current The current date
*/
TimeStep.prototype.getCurrent = function() {
- return this.current;
+ return this.current;
};
/**
@@ -6337,13 +5801,13 @@ TimeStep.prototype.getCurrent = function() {
* example 1, 2, 5, or 10.
*/
TimeStep.prototype.setScale = function(newScale, newStep) {
- this.scale = newScale;
+ this.scale = newScale;
- if (newStep > 0) {
- this.step = newStep;
- }
+ if (newStep > 0) {
+ this.step = newStep;
+ }
- this.autoScale = false;
+ this.autoScale = false;
};
/**
@@ -6351,7 +5815,7 @@ TimeStep.prototype.setScale = function(newScale, newStep) {
* @param {boolean} enable If true, autoascaling is set true
*/
TimeStep.prototype.setAutoScale = function (enable) {
- this.autoScale = enable;
+ this.autoScale = enable;
};
@@ -6360,48 +5824,48 @@ TimeStep.prototype.setAutoScale = function (enable) {
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
TimeStep.prototype.setMinimumStep = function(minimumStep) {
- if (minimumStep == undefined) {
- return;
- }
+ if (minimumStep == undefined) {
+ return;
+ }
- var stepYear = (1000 * 60 * 60 * 24 * 30 * 12);
- var stepMonth = (1000 * 60 * 60 * 24 * 30);
- var stepDay = (1000 * 60 * 60 * 24);
- var stepHour = (1000 * 60 * 60);
- var stepMinute = (1000 * 60);
- var stepSecond = (1000);
- var stepMillisecond= (1);
-
- // find the smallest step that is larger than the provided minimumStep
- if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;}
- if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;}
- if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;}
- if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;}
- if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;}
- if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;}
- if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;}
- if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;}
- if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;}
- if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;}
- if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;}
- if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;}
- if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;}
- if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;}
- if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;}
- if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;}
- if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;}
- if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;}
- if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;}
- if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;}
- if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;}
- if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;}
- if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;}
- if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;}
- if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;}
- if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;}
- if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;}
- if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;}
- if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;}
+ var stepYear = (1000 * 60 * 60 * 24 * 30 * 12);
+ var stepMonth = (1000 * 60 * 60 * 24 * 30);
+ var stepDay = (1000 * 60 * 60 * 24);
+ var stepHour = (1000 * 60 * 60);
+ var stepMinute = (1000 * 60);
+ var stepSecond = (1000);
+ var stepMillisecond= (1);
+
+ // find the smallest step that is larger than the provided minimumStep
+ if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;}
+ if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;}
+ if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;}
+ if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;}
+ if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;}
+ if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;}
+ if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;}
+ if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;}
+ if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;}
+ if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;}
+ if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;}
+ if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;}
+ if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;}
+ if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;}
+ if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;}
+ if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;}
+ if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;}
+ if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;}
+ if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;}
+ if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;}
+ if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;}
+ if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;}
+ if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;}
+ if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;}
+ if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;}
+ if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;}
+ if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;}
+ if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;}
+ if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;}
};
/**
@@ -6410,87 +5874,87 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) {
* @param {Date} date the date to be snapped
*/
TimeStep.prototype.snap = function(date) {
- if (this.scale == TimeStep.SCALE.YEAR) {
- var year = date.getFullYear() + Math.round(date.getMonth() / 12);
- date.setFullYear(Math.round(year / this.step) * this.step);
- date.setMonth(0);
- date.setDate(0);
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+ if (this.scale == TimeStep.SCALE.YEAR) {
+ var year = date.getFullYear() + Math.round(date.getMonth() / 12);
+ date.setFullYear(Math.round(year / this.step) * this.step);
+ date.setMonth(0);
+ date.setDate(0);
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.MONTH) {
+ if (date.getDate() > 15) {
+ date.setDate(1);
+ date.setMonth(date.getMonth() + 1);
+ // important: first set Date to 1, after that change the month.
}
- else if (this.scale == TimeStep.SCALE.MONTH) {
- if (date.getDate() > 15) {
- date.setDate(1);
- date.setMonth(date.getMonth() + 1);
- // important: first set Date to 1, after that change the month.
- }
- else {
- date.setDate(1);
- }
-
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
+ else {
+ date.setDate(1);
}
- else if (this.scale == TimeStep.SCALE.DAY ||
- this.scale == TimeStep.SCALE.WEEKDAY) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 5:
- case 2:
- date.setHours(Math.round(date.getHours() / 24) * 24); break;
- default:
- date.setHours(Math.round(date.getHours() / 12) * 12); break;
- }
- date.setMinutes(0);
+
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.DAY ||
+ this.scale == TimeStep.SCALE.WEEKDAY) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 5:
+ case 2:
+ date.setHours(Math.round(date.getHours() / 24) * 24); break;
+ default:
+ date.setHours(Math.round(date.getHours() / 12) * 12); break;
+ }
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.HOUR) {
+ switch (this.step) {
+ case 4:
+ date.setMinutes(Math.round(date.getMinutes() / 60) * 60); break;
+ default:
+ date.setMinutes(Math.round(date.getMinutes() / 30) * 30); break;
+ }
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+ } else if (this.scale == TimeStep.SCALE.MINUTE) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 15:
+ case 10:
+ date.setMinutes(Math.round(date.getMinutes() / 5) * 5);
date.setSeconds(0);
- date.setMilliseconds(0);
+ break;
+ case 5:
+ date.setSeconds(Math.round(date.getSeconds() / 60) * 60); break;
+ default:
+ date.setSeconds(Math.round(date.getSeconds() / 30) * 30); break;
}
- else if (this.scale == TimeStep.SCALE.HOUR) {
- switch (this.step) {
- case 4:
- date.setMinutes(Math.round(date.getMinutes() / 60) * 60); break;
- default:
- date.setMinutes(Math.round(date.getMinutes() / 30) * 30); break;
- }
- date.setSeconds(0);
- date.setMilliseconds(0);
- } else if (this.scale == TimeStep.SCALE.MINUTE) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 15:
- case 10:
- date.setMinutes(Math.round(date.getMinutes() / 5) * 5);
- date.setSeconds(0);
- break;
- case 5:
- date.setSeconds(Math.round(date.getSeconds() / 60) * 60); break;
- default:
- date.setSeconds(Math.round(date.getSeconds() / 30) * 30); break;
- }
+ date.setMilliseconds(0);
+ }
+ else if (this.scale == TimeStep.SCALE.SECOND) {
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.step) {
+ case 15:
+ case 10:
+ date.setSeconds(Math.round(date.getSeconds() / 5) * 5);
date.setMilliseconds(0);
+ break;
+ case 5:
+ date.setMilliseconds(Math.round(date.getMilliseconds() / 1000) * 1000); break;
+ default:
+ date.setMilliseconds(Math.round(date.getMilliseconds() / 500) * 500); break;
}
- else if (this.scale == TimeStep.SCALE.SECOND) {
- //noinspection FallthroughInSwitchStatementJS
- switch (this.step) {
- case 15:
- case 10:
- date.setSeconds(Math.round(date.getSeconds() / 5) * 5);
- date.setMilliseconds(0);
- break;
- case 5:
- date.setMilliseconds(Math.round(date.getMilliseconds() / 1000) * 1000); break;
- default:
- date.setMilliseconds(Math.round(date.getMilliseconds() / 500) * 500); break;
- }
- }
- else if (this.scale == TimeStep.SCALE.MILLISECOND) {
- var step = this.step > 5 ? this.step / 2 : 1;
- date.setMilliseconds(Math.round(date.getMilliseconds() / step) * step);
- }
+ }
+ else if (this.scale == TimeStep.SCALE.MILLISECOND) {
+ var step = this.step > 5 ? this.step / 2 : 1;
+ date.setMilliseconds(Math.round(date.getMilliseconds() / step) * step);
+ }
};
/**
@@ -6499,26 +5963,26 @@ TimeStep.prototype.snap = function(date) {
* @return {boolean} true if current date is major, else false.
*/
TimeStep.prototype.isMajor = function() {
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:
- return (this.current.getMilliseconds() == 0);
- case TimeStep.SCALE.SECOND:
- return (this.current.getSeconds() == 0);
- case TimeStep.SCALE.MINUTE:
- return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
- // Note: this is no bug. Major label is equal for both minute and hour scale
- case TimeStep.SCALE.HOUR:
- return (this.current.getHours() == 0);
- case TimeStep.SCALE.WEEKDAY: // intentional fall through
- case TimeStep.SCALE.DAY:
- return (this.current.getDate() == 1);
- case TimeStep.SCALE.MONTH:
- return (this.current.getMonth() == 0);
- case TimeStep.SCALE.YEAR:
- return false;
- default:
- return false;
- }
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:
+ return (this.current.getMilliseconds() == 0);
+ case TimeStep.SCALE.SECOND:
+ return (this.current.getSeconds() == 0);
+ case TimeStep.SCALE.MINUTE:
+ return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
+ // Note: this is no bug. Major label is equal for both minute and hour scale
+ case TimeStep.SCALE.HOUR:
+ return (this.current.getHours() == 0);
+ case TimeStep.SCALE.WEEKDAY: // intentional fall through
+ case TimeStep.SCALE.DAY:
+ return (this.current.getDate() == 1);
+ case TimeStep.SCALE.MONTH:
+ return (this.current.getMonth() == 0);
+ case TimeStep.SCALE.YEAR:
+ return false;
+ default:
+ return false;
+ }
};
@@ -6529,21 +5993,21 @@ TimeStep.prototype.isMajor = function() {
* @param {Date} [date] custom date. if not provided, current date is taken
*/
TimeStep.prototype.getLabelMinor = function(date) {
- if (date == undefined) {
- date = this.current;
- }
+ if (date == undefined) {
+ date = this.current;
+ }
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS');
- case TimeStep.SCALE.SECOND: return moment(date).format('s');
- case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm');
- case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm');
- case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D');
- case TimeStep.SCALE.DAY: return moment(date).format('D');
- case TimeStep.SCALE.MONTH: return moment(date).format('MMM');
- case TimeStep.SCALE.YEAR: return moment(date).format('YYYY');
- default: return '';
- }
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS');
+ case TimeStep.SCALE.SECOND: return moment(date).format('s');
+ case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm');
+ case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm');
+ case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D');
+ case TimeStep.SCALE.DAY: return moment(date).format('D');
+ case TimeStep.SCALE.MONTH: return moment(date).format('MMM');
+ case TimeStep.SCALE.YEAR: return moment(date).format('YYYY');
+ default: return '';
+ }
};
@@ -6554,22 +6018,22 @@ TimeStep.prototype.getLabelMinor = function(date) {
* @param {Date} [date] custom date. if not provided, current date is taken
*/
TimeStep.prototype.getLabelMajor = function(date) {
- if (date == undefined) {
- date = this.current;
- }
+ if (date == undefined) {
+ date = this.current;
+ }
- //noinspection FallthroughInSwitchStatementJS
- switch (this.scale) {
- case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss');
- case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm');
- case TimeStep.SCALE.MINUTE:
- case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM');
- case TimeStep.SCALE.WEEKDAY:
- case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY');
- case TimeStep.SCALE.MONTH: return moment(date).format('YYYY');
- case TimeStep.SCALE.YEAR: return '';
- default: return '';
- }
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.scale) {
+ case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss');
+ case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm');
+ case TimeStep.SCALE.MINUTE:
+ case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM');
+ case TimeStep.SCALE.WEEKDAY:
+ case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY');
+ case TimeStep.SCALE.MONTH: return moment(date).format('YYYY');
+ case TimeStep.SCALE.YEAR: return '';
+ default: return '';
+ }
};
/**
@@ -6579,39 +6043,39 @@ TimeStep.prototype.getLabelMajor = function(date) {
* @param {Object} [options]
*/
function Stack (parent, options) {
- this.parent = parent;
-
- this.options = options || {};
- this.defaultOptions = {
- order: function (a, b) {
- //return (b.width - a.width) || (a.left - b.left); // TODO: cleanup
- // Order: ranges over non-ranges, ranged ordered by width, and
- // lastly ordered by start.
- if (a instanceof ItemRange) {
- if (b instanceof ItemRange) {
- var aInt = (a.data.end - a.data.start);
- var bInt = (b.data.end - b.data.start);
- return (aInt - bInt) || (a.data.start - b.data.start);
- }
- else {
- return -1;
- }
- }
- else {
- if (b instanceof ItemRange) {
- return 1;
- }
- else {
- return (a.data.start - b.data.start);
- }
- }
- },
- margin: {
- item: 10
+ this.parent = parent;
+
+ this.options = options || {};
+ this.defaultOptions = {
+ order: function (a, b) {
+ //return (b.width - a.width) || (a.left - b.left); // TODO: cleanup
+ // Order: ranges over non-ranges, ranged ordered by width, and
+ // lastly ordered by start.
+ if (a instanceof ItemRange) {
+ if (b instanceof ItemRange) {
+ var aInt = (a.data.end - a.data.start);
+ var bInt = (b.data.end - b.data.start);
+ return (aInt - bInt) || (a.data.start - b.data.start);
}
- };
+ else {
+ return -1;
+ }
+ }
+ else {
+ if (b instanceof ItemRange) {
+ return 1;
+ }
+ else {
+ return (a.data.start - b.data.start);
+ }
+ }
+ },
+ margin: {
+ item: 10
+ }
+ };
- this.ordered = []; // ordered items
+ this.ordered = []; // ordered items
}
/**
@@ -6622,9 +6086,9 @@ function Stack (parent, options) {
* {function} order Stacking order
*/
Stack.prototype.setOptions = function setOptions (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // TODO: register on data changes at the connected parent itemset, and update the changed part only and immediately
+ // TODO: register on data changes at the connected parent itemset, and update the changed part only and immediately
};
/**
@@ -6632,8 +6096,8 @@ Stack.prototype.setOptions = function setOptions (options) {
* distance equal to options.margin.item.
*/
Stack.prototype.update = function update() {
- this._order();
- this._stack();
+ this._order();
+ this._stack();
};
/**
@@ -6644,31 +6108,31 @@ Stack.prototype.update = function update() {
* @private
*/
Stack.prototype._order = function _order () {
- var items = this.parent.items;
- if (!items) {
- throw new Error('Cannot stack items: parent does not contain items');
- }
-
- // TODO: store the sorted items, to have less work later on
- var ordered = [];
- var index = 0;
- // items is a map (no array)
- util.forEach(items, function (item) {
- if (item.visible) {
- ordered[index] = item;
- index++;
- }
- });
+ var items = this.parent.items;
+ if (!items) {
+ throw new Error('Cannot stack items: parent does not contain items');
+ }
- //if a customer stack order function exists, use it.
- var order = this.options.order || this.defaultOptions.order;
- if (!(typeof order === 'function')) {
- throw new Error('Option order must be a function');
- }
+ // TODO: store the sorted items, to have less work later on
+ var ordered = [];
+ var index = 0;
+ // items is a map (no array)
+ util.forEach(items, function (item) {
+ if (item.visible) {
+ ordered[index] = item;
+ index++;
+ }
+ });
+
+ //if a customer stack order function exists, use it.
+ var order = this.options.order || this.defaultOptions.order;
+ if (!(typeof order === 'function')) {
+ throw new Error('Option order must be a function');
+ }
- ordered.sort(order);
+ ordered.sort(order);
- this.ordered = ordered;
+ this.ordered = ordered;
};
/**
@@ -6677,40 +6141,40 @@ Stack.prototype._order = function _order () {
* @private
*/
Stack.prototype._stack = function _stack () {
- var i,
- iMax,
- ordered = this.ordered,
- options = this.options,
- orientation = options.orientation || this.defaultOptions.orientation,
- axisOnTop = (orientation == 'top'),
- margin;
+ var i,
+ iMax,
+ ordered = this.ordered,
+ options = this.options,
+ orientation = options.orientation || this.defaultOptions.orientation,
+ axisOnTop = (orientation == 'top'),
+ margin;
+
+ if (options.margin && options.margin.item !== undefined) {
+ margin = options.margin.item;
+ }
+ else {
+ margin = this.defaultOptions.margin.item
+ }
- if (options.margin && options.margin.item !== undefined) {
- margin = options.margin.item;
- }
- else {
- margin = this.defaultOptions.margin.item
- }
-
- // calculate new, non-overlapping positions
- for (i = 0, iMax = ordered.length; i < iMax; i++) {
- var item = ordered[i];
- var collidingItem = null;
- do {
- // TODO: optimize checking for overlap. when there is a gap without items,
- // you only need to check for items from the next item on, not from zero
- collidingItem = this.checkOverlap(ordered, i, 0, i - 1, margin);
- if (collidingItem != null) {
- // There is a collision. Reposition the event above the colliding element
- if (axisOnTop) {
- item.top = collidingItem.top + collidingItem.height + margin;
- }
- else {
- item.top = collidingItem.top - item.height - margin;
- }
- }
- } while (collidingItem);
- }
+ // calculate new, non-overlapping positions
+ for (i = 0, iMax = ordered.length; i < iMax; i++) {
+ var item = ordered[i];
+ var collidingItem = null;
+ do {
+ // TODO: optimize checking for overlap. when there is a gap without items,
+ // you only need to check for items from the next item on, not from zero
+ collidingItem = this.checkOverlap(ordered, i, 0, i - 1, margin);
+ if (collidingItem != null) {
+ // There is a collision. Reposition the event above the colliding element
+ if (axisOnTop) {
+ item.top = collidingItem.top + collidingItem.height + margin;
+ }
+ else {
+ item.top = collidingItem.top - item.height - margin;
+ }
+ }
+ } while (collidingItem);
+ }
};
/**
@@ -6729,21 +6193,21 @@ Stack.prototype._stack = function _stack () {
*/
Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
itemStart, itemEnd, margin) {
- var collision = this.collision;
-
- // we loop from end to start, as we suppose that the chance of a
- // collision is larger for items at the end, so check these first.
- var a = items[itemIndex];
- for (var i = itemEnd; i >= itemStart; i--) {
- var b = items[i];
- if (collision(a, b, margin)) {
- if (i != itemIndex) {
- return b;
- }
- }
+ var collision = this.collision;
+
+ // we loop from end to start, as we suppose that the chance of a
+ // collision is larger for items at the end, so check these first.
+ var a = items[itemIndex];
+ for (var i = itemEnd; i >= itemStart; i--) {
+ var b = items[i];
+ if (collision(a, b, margin)) {
+ if (i != itemIndex) {
+ return b;
+ }
}
+ }
- return null;
+ return null;
};
/**
@@ -6759,10 +6223,10 @@ Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
* @return {boolean} true if a and b collide, else false
*/
Stack.prototype.collision = function collision (a, b, margin) {
- return ((a.left - margin) < (b.left + b.getWidth()) &&
- (a.left + a.getWidth() + margin) > b.left &&
- (a.top - margin) < (b.top + b.height) &&
- (a.top + a.height + margin) > b.top);
+ return ((a.left - margin) < (b.left + b.getWidth()) &&
+ (a.left + a.getWidth() + margin) > b.left &&
+ (a.top - margin) < (b.top + b.height) &&
+ (a.top + a.height + margin) > b.top);
};
/**
@@ -6774,15 +6238,13 @@ Stack.prototype.collision = function collision (a, b, margin) {
* @extends Controller
*/
function Range(options) {
- this.id = util.randomUUID();
- this.start = null; // Number
- this.end = null; // Number
+ this.id = util.randomUUID();
+ this.start = null; // Number
+ this.end = null; // Number
- this.options = options || {};
-
- this.listeners = [];
+ this.options = options || {};
- this.setOptions(options);
+ this.setOptions(options);
}
/**
@@ -6796,14 +6258,25 @@ function Range(options) {
* (end - start).
*/
Range.prototype.setOptions = function (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // re-apply range with new limitations
- if (this.start !== null && this.end !== null) {
- this.setRange(this.start, this.end);
- }
+ // re-apply range with new limitations
+ if (this.start !== null && this.end !== null) {
+ this.setRange(this.start, this.end);
+ }
};
+/**
+ * Test whether direction has a valid value
+ * @param {String} direction 'horizontal' or 'vertical'
+ */
+function validateDirection (direction) {
+ if (direction != 'horizontal' && direction != 'vertical') {
+ throw new TypeError('Unknown direction "' + direction + '". ' +
+ 'Choose "horizontal" or "vertical".');
+ }
+}
+
/**
* Add listeners for mouse and touch events to the component
* @param {Component} component
@@ -6811,47 +6284,44 @@ Range.prototype.setOptions = function (options) {
* @param {String} direction Available directions: 'horizontal', 'vertical'
*/
Range.prototype.subscribe = function (component, event, direction) {
- var me = this;
- var listener;
+ var me = this;
- if (direction != 'horizontal' && direction != 'vertical') {
- throw new TypeError('Unknown direction "' + direction + '". ' +
- 'Choose "horizontal" or "vertical".');
- }
+ if (event == 'move') {
+ // drag start listener
+ component.on('dragstart', function (event) {
+ me._onDragStart(event, component);
+ });
- //noinspection FallthroughInSwitchStatementJS
- if (event == 'move') {
- listener = {
- component: component,
- event: event,
- direction: direction,
- callback: function (event) {
- me._onMouseDown(event, listener);
- },
- params: {}
- };
+ // drag listener
+ component.on('drag', function (event) {
+ me._onDrag(event, component, direction);
+ });
- component.on('mousedown', listener.callback);
- me.listeners.push(listener);
+ // drag end listener
+ component.on('dragend', function (event) {
+ me._onDragEnd(event, component);
+ });
+ }
+ else if (event == 'zoom') {
+ // mouse wheel
+ function mousewheel (event) {
+ me._onMouseWheel(event, component, direction);
}
- else if (event == 'zoom') {
- listener = {
- component: component,
- event: event,
- direction: direction,
- callback: function (event) {
- me._onMouseWheel(event, listener);
- },
- params: {}
- };
+ component.on('mousewheel', mousewheel);
+ component.on('DOMMouseScroll', mousewheel); // For FF
- component.on('mousewheel', listener.callback);
- me.listeners.push(listener);
- }
- else {
- throw new TypeError('Unknown event "' + event + '". ' +
- 'Choose "move" or "zoom".');
- }
+ // pinch
+ component.on('touch', function (event) {
+ me._onTouch();
+ });
+ component.on('pinch', function (event) {
+ me._onPinch(event, component, direction);
+ });
+ }
+ else {
+ throw new TypeError('Unknown event "' + event + '". ' +
+ 'Choose "move" or "zoom".');
+ }
};
/**
@@ -6861,7 +6331,7 @@ Range.prototype.subscribe = function (component, event, direction) {
* as parameter.
*/
Range.prototype.on = function (event, callback) {
- events.addListener(this, event, callback);
+ events.addListener(this, event, callback);
};
/**
@@ -6871,10 +6341,10 @@ Range.prototype.on = function (event, callback) {
* @private
*/
Range.prototype._trigger = function (event) {
- events.trigger(this, event, {
- start: this.start,
- end: this.end
- });
+ events.trigger(this, event, {
+ start: this.start,
+ end: this.end
+ });
};
/**
@@ -6883,11 +6353,11 @@ Range.prototype._trigger = function (event) {
* @param {Number} [end]
*/
Range.prototype.setRange = function(start, end) {
- var changed = this._applyRange(start, end);
- if (changed) {
- this._trigger('rangechange');
- this._trigger('rangechanged');
- }
+ var changed = this._applyRange(start, end);
+ if (changed) {
+ this._trigger('rangechange');
+ this._trigger('rangechanged');
+ }
};
/**
@@ -6900,105 +6370,105 @@ Range.prototype.setRange = function(start, end) {
* @private
*/
Range.prototype._applyRange = function(start, end) {
- var newStart = (start != null) ? util.convert(start, 'Number') : this.start,
- newEnd = (end != null) ? util.convert(end, 'Number') : this.end,
- max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null,
- min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null,
- diff;
+ var newStart = (start != null) ? util.convert(start, 'Number') : this.start,
+ newEnd = (end != null) ? util.convert(end, 'Number') : this.end,
+ max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null,
+ min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null,
+ diff;
+
+ // check for valid number
+ if (isNaN(newStart) || newStart === null) {
+ throw new Error('Invalid start "' + start + '"');
+ }
+ if (isNaN(newEnd) || newEnd === null) {
+ throw new Error('Invalid end "' + end + '"');
+ }
- // check for valid number
- if (isNaN(newStart) || newStart === null) {
- throw new Error('Invalid start "' + start + '"');
- }
- if (isNaN(newEnd) || newEnd === null) {
- throw new Error('Invalid end "' + end + '"');
- }
+ // prevent start < end
+ if (newEnd < newStart) {
+ newEnd = newStart;
+ }
- // prevent start < end
- if (newEnd < newStart) {
- newEnd = newStart;
- }
+ // prevent start < min
+ if (min !== null) {
+ if (newStart < min) {
+ diff = (min - newStart);
+ newStart += diff;
+ newEnd += diff;
- // prevent start < min
- if (min !== null) {
- if (newStart < min) {
- diff = (min - newStart);
- newStart += diff;
- newEnd += diff;
-
- // prevent end > max
- if (max != null) {
- if (newEnd > max) {
- newEnd = max;
- }
- }
+ // prevent end > max
+ if (max != null) {
+ if (newEnd > max) {
+ newEnd = max;
}
+ }
}
+ }
- // prevent end > max
- if (max !== null) {
- if (newEnd > max) {
- diff = (newEnd - max);
- newStart -= diff;
- newEnd -= diff;
-
- // prevent start < min
- if (min != null) {
- if (newStart < min) {
- newStart = min;
- }
- }
+ // prevent end > max
+ if (max !== null) {
+ if (newEnd > max) {
+ diff = (newEnd - max);
+ newStart -= diff;
+ newEnd -= diff;
+
+ // prevent start < min
+ if (min != null) {
+ if (newStart < min) {
+ newStart = min;
}
+ }
}
+ }
- // prevent (end-start) < zoomMin
- if (this.options.zoomMin !== null) {
- var zoomMin = parseFloat(this.options.zoomMin);
- if (zoomMin < 0) {
- zoomMin = 0;
- }
- if ((newEnd - newStart) < zoomMin) {
- if ((this.end - this.start) === zoomMin) {
- // ignore this action, we are already zoomed to the minimum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the minimum
- diff = (zoomMin - (newEnd - newStart));
- newStart -= diff / 2;
- newEnd += diff / 2;
- }
- }
+ // prevent (end-start) < zoomMin
+ if (this.options.zoomMin !== null) {
+ var zoomMin = parseFloat(this.options.zoomMin);
+ if (zoomMin < 0) {
+ zoomMin = 0;
+ }
+ if ((newEnd - newStart) < zoomMin) {
+ if ((this.end - this.start) === zoomMin) {
+ // ignore this action, we are already zoomed to the minimum
+ newStart = this.start;
+ newEnd = this.end;
+ }
+ else {
+ // zoom to the minimum
+ diff = (zoomMin - (newEnd - newStart));
+ newStart -= diff / 2;
+ newEnd += diff / 2;
+ }
}
+ }
- // prevent (end-start) > zoomMax
- if (this.options.zoomMax !== null) {
- var zoomMax = parseFloat(this.options.zoomMax);
- if (zoomMax < 0) {
- zoomMax = 0;
- }
- if ((newEnd - newStart) > zoomMax) {
- if ((this.end - this.start) === zoomMax) {
- // ignore this action, we are already zoomed to the maximum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the maximum
- diff = ((newEnd - newStart) - zoomMax);
- newStart += diff / 2;
- newEnd -= diff / 2;
- }
- }
+ // prevent (end-start) > zoomMax
+ if (this.options.zoomMax !== null) {
+ var zoomMax = parseFloat(this.options.zoomMax);
+ if (zoomMax < 0) {
+ zoomMax = 0;
+ }
+ if ((newEnd - newStart) > zoomMax) {
+ if ((this.end - this.start) === zoomMax) {
+ // ignore this action, we are already zoomed to the maximum
+ newStart = this.start;
+ newEnd = this.end;
+ }
+ else {
+ // zoom to the maximum
+ diff = ((newEnd - newStart) - zoomMax);
+ newStart += diff / 2;
+ newEnd -= diff / 2;
+ }
}
+ }
- var changed = (this.start != newStart || this.end != newEnd);
+ var changed = (this.start != newStart || this.end != newEnd);
- this.start = newStart;
- this.end = newEnd;
+ this.start = newStart;
+ this.end = newEnd;
- return changed;
+ return changed;
};
/**
@@ -7006,296 +6476,280 @@ Range.prototype._applyRange = function(start, end) {
* @return {Object} An object with start and end properties
*/
Range.prototype.getRange = function() {
- return {
- start: this.start,
- end: this.end
- };
+ return {
+ start: this.start,
+ end: this.end
+ };
};
/**
- * Calculate the conversion offset and factor for current range, based on
+ * Calculate the conversion offset and scale for current range, based on
* the provided width
* @param {Number} width
- * @returns {{offset: number, factor: number}} conversion
+ * @returns {{offset: number, scale: number}} conversion
*/
Range.prototype.conversion = function (width) {
- var start = this.start;
- var end = this.end;
-
- return Range.conversion(this.start, this.end, width);
+ return Range.conversion(this.start, this.end, width);
};
/**
- * Static method to calculate the conversion offset and factor for a range,
+ * Static method to calculate the conversion offset and scale for a range,
* based on the provided start, end, and width
* @param {Number} start
* @param {Number} end
* @param {Number} width
- * @returns {{offset: number, factor: number}} conversion
+ * @returns {{offset: number, scale: number}} conversion
*/
Range.conversion = function (start, end, width) {
- if (width != 0 && (end - start != 0)) {
- return {
- offset: start,
- factor: width / (end - start)
- }
- }
- else {
- return {
- offset: 0,
- factor: 1
- };
+ if (width != 0 && (end - start != 0)) {
+ return {
+ offset: start,
+ scale: width / (end - start)
}
+ }
+ else {
+ return {
+ offset: 0,
+ scale: 1
+ };
+ }
};
+// global (private) object to store drag params
+var touchParams = {};
+
/**
- * Start moving horizontally or vertically
+ * Start dragging horizontally or vertically
* @param {Event} event
- * @param {Object} listener Listener containing the component and params
+ * @param {Object} component
* @private
*/
-Range.prototype._onMouseDown = function(event, listener) {
- event = event || window.event;
- var params = listener.params;
-
- // only react on left mouse button down
- var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
- if (!leftButtonDown) {
- return;
- }
-
- // get mouse position
- params.mouseX = util.getPageX(event);
- params.mouseY = util.getPageY(event);
- params.previousLeft = 0;
- params.previousOffset = 0;
-
- params.moved = false;
- params.start = this.start;
- params.end = this.end;
-
- var frame = listener.component.frame;
- if (frame) {
- frame.style.cursor = 'move';
- }
+Range.prototype._onDragStart = function(event, component) {
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- // add event listeners to handle moving the contents
- // we store the function onmousemove and onmouseup in the timeaxis,
- // so we can remove the eventlisteners lateron in the function onmouseup
- var me = this;
- if (!params.onMouseMove) {
- params.onMouseMove = function (event) {
- me._onMouseMove(event, listener);
- };
- util.addEventListener(document, "mousemove", params.onMouseMove);
- }
- if (!params.onMouseUp) {
- params.onMouseUp = function (event) {
- me._onMouseUp(event, listener);
- };
- util.addEventListener(document, "mouseup", params.onMouseUp);
- }
+ touchParams.start = this.start;
+ touchParams.end = this.end;
- util.preventDefault(event);
+ var frame = component.frame;
+ if (frame) {
+ frame.style.cursor = 'move';
+ }
};
/**
- * Perform moving operating.
- * This function activated from within the funcion TimeAxis._onMouseDown().
+ * Perform dragging operating.
* @param {Event} event
- * @param {Object} listener
+ * @param {Component} component
+ * @param {String} direction 'horizontal' or 'vertical'
* @private
*/
-Range.prototype._onMouseMove = function (event, listener) {
- event = event || window.event;
-
- var params = listener.params;
-
- // calculate change in mouse position
- var mouseX = util.getPageX(event);
- var mouseY = util.getPageY(event);
-
- if (params.mouseX == undefined) {
- params.mouseX = mouseX;
- }
- if (params.mouseY == undefined) {
- params.mouseY = mouseY;
- }
+Range.prototype._onDrag = function (event, component, direction) {
+ validateDirection(direction);
- var diffX = mouseX - params.mouseX;
- var diffY = mouseY - params.mouseY;
- var diff = (listener.direction == 'horizontal') ? diffX : diffY;
-
- // if mouse movement is big enough, register it as a "moved" event
- if (Math.abs(diff) >= 1) {
- params.moved = true;
- }
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- var interval = (params.end - params.start);
- var width = (listener.direction == 'horizontal') ?
- listener.component.width : listener.component.height;
- var diffRange = -diff / width * interval;
- this._applyRange(params.start + diffRange, params.end + diffRange);
+ var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
+ interval = (touchParams.end - touchParams.start),
+ width = (direction == 'horizontal') ? component.width : component.height,
+ diffRange = -delta / width * interval;
- // fire a rangechange event
- this._trigger('rangechange');
+ this._applyRange(touchParams.start + diffRange, touchParams.end + diffRange);
- util.preventDefault(event);
+ // fire a rangechange event
+ this._trigger('rangechange');
};
/**
- * Stop moving operating.
- * This function activated from within the function Range._onMouseDown().
+ * Stop dragging operating.
* @param {event} event
- * @param {Object} listener
+ * @param {Component} component
* @private
*/
-Range.prototype._onMouseUp = function (event, listener) {
- event = event || window.event;
+Range.prototype._onDragEnd = function (event, component) {
+ // refuse to drag when we where pinching to prevent the timeline make a jump
+ // when releasing the fingers in opposite order from the touch screen
+ if (touchParams.pinching) return;
- var params = listener.params;
-
- if (listener.component.frame) {
- listener.component.frame.style.cursor = 'auto';
- }
-
- // remove event listeners here, important for Safari
- if (params.onMouseMove) {
- util.removeEventListener(document, "mousemove", params.onMouseMove);
- params.onMouseMove = null;
- }
- if (params.onMouseUp) {
- util.removeEventListener(document, "mouseup", params.onMouseUp);
- params.onMouseUp = null;
- }
- //util.preventDefault(event);
+ if (component.frame) {
+ component.frame.style.cursor = 'auto';
+ }
- if (params.moved) {
- // fire a rangechanged event
- this._trigger('rangechanged');
- }
+ // fire a rangechanged event
+ this._trigger('rangechanged');
};
/**
* Event handler for mouse wheel event, used to zoom
* Code from http://adomas.org/javascript-mouse-wheel/
* @param {Event} event
- * @param {Object} listener
+ * @param {Component} component
+ * @param {String} direction 'horizontal' or 'vertical'
* @private
*/
-Range.prototype._onMouseWheel = function(event, listener) {
- event = event || window.event;
-
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta / 120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail / 3;
- }
-
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- var me = this;
- var zoom = function () {
- // perform the zoom action. Delta is normally 1 or -1
- var zoomFactor = delta / 5.0;
- var zoomAround = null;
- var frame = listener.component.frame;
- if (frame) {
- var size, conversion;
- if (listener.direction == 'horizontal') {
- size = listener.component.width;
- conversion = me.conversion(size);
- var frameLeft = util.getAbsoluteLeft(frame);
- var mouseX = util.getPageX(event);
- zoomAround = (mouseX - frameLeft) / conversion.factor + conversion.offset;
- }
- else {
- size = listener.component.height;
- conversion = me.conversion(size);
- var frameTop = util.getAbsoluteTop(frame);
- var mouseY = util.getPageY(event);
- zoomAround = ((frameTop + size - mouseY) - frameTop) / conversion.factor + conversion.offset;
- }
- }
+Range.prototype._onMouseWheel = function(event, component, direction) {
+ validateDirection(direction);
+
+ // retrieve delta
+ var delta = 0;
+ if (event.wheelDelta) { /* IE/Opera. */
+ delta = event.wheelDelta / 120;
+ } else if (event.detail) { /* Mozilla case. */
+ // In Mozilla, sign of delta is different than in IE.
+ // Also, delta is multiple of 3.
+ delta = -event.detail / 3;
+ }
- me.zoom(zoomFactor, zoomAround);
- };
+ // If delta is nonzero, handle it.
+ // Basically, delta is now positive if wheel was scrolled up,
+ // and negative, if wheel was scrolled down.
+ if (delta) {
+ // perform the zoom action. Delta is normally 1 or -1
- zoom();
+ // adjust a negative delta such that zooming in with delta 0.1
+ // equals zooming out with a delta -0.1
+ var scale;
+ if (delta < 0) {
+ scale = 1 - (delta / 5);
+ }
+ else {
+ scale = 1 / (1 + (delta / 5)) ;
}
- // Prevent default actions caused by mouse wheel.
- // That might be ugly, but we handle scrolls somehow
- // anyway, so don't bother here...
- util.preventDefault(event);
-};
+ // calculate center, the date to zoom around
+ var gesture = Hammer.event.collectEventData(this, 'scroll', event),
+ pointer = getPointer(gesture.touches[0], component.frame),
+ pointerDate = this._pointerToDate(component, direction, pointer);
+
+ this.zoom(scale, pointerDate);
+ }
+ // Prevent default actions caused by mouse wheel
+ // (else the page and timeline both zoom and scroll)
+ util.preventDefault(event);
+};
/**
- * Zoom the range the given zoomfactor in or out. Start and end date will
- * be adjusted, and the timeline will be redrawn. You can optionally give a
- * date around which to zoom.
- * For example, try zoomfactor = 0.1 or -0.1
- * @param {Number} zoomFactor Zooming amount. Positive value will zoom in,
- * negative value will zoom out
- * @param {Number} zoomAround Value around which will be zoomed. Optional
+ * On start of a touch gesture, initialize scale to 1
+ * @private
*/
-Range.prototype.zoom = function(zoomFactor, zoomAround) {
- // if zoomAroundDate is not provided, take it half between start Date and end Date
- if (zoomAround == null) {
- zoomAround = (this.start + this.end) / 2;
- }
+Range.prototype._onTouch = function () {
+ touchParams.start = this.start;
+ touchParams.end = this.end;
+ touchParams.pinching = false;
+ touchParams.center = null;
+};
- // prevent zoom factor larger than 1 or smaller than -1 (larger than 1 will
- // result in a start>=end )
- if (zoomFactor >= 1) {
- zoomFactor = 0.9;
- }
- if (zoomFactor <= -1) {
- zoomFactor = -0.9;
- }
+/**
+ * Handle pinch event
+ * @param {Event} event
+ * @param {Component} component
+ * @param {String} direction 'horizontal' or 'vertical'
+ * @private
+ */
+Range.prototype._onPinch = function (event, component, direction) {
+ touchParams.pinching = true;
- // adjust a negative factor such that zooming in with 0.1 equals zooming
- // out with a factor -0.1
- if (zoomFactor < 0) {
- zoomFactor = zoomFactor / (1 + zoomFactor);
+ if (event.gesture.touches.length > 1) {
+ if (!touchParams.center) {
+ touchParams.center = getPointer(event.gesture.center, component.frame);
}
- // zoom start and end relative to the zoomAround value
- var startDiff = (this.start - zoomAround);
- var endDiff = (this.end - zoomAround);
+ var scale = 1 / event.gesture.scale,
+ initDate = this._pointerToDate(component, direction, touchParams.center),
+ center = getPointer(event.gesture.center, component.frame),
+ date = this._pointerToDate(component, direction, center),
+ delta = date - initDate; // TODO: utilize delta
// calculate new start and end
- var newStart = this.start - startDiff * zoomFactor;
- var newEnd = this.end - endDiff * zoomFactor;
+ var newStart = parseInt(initDate + (touchParams.start - initDate) * scale);
+ var newEnd = parseInt(initDate + (touchParams.end - initDate) * scale);
+ // apply new range
this.setRange(newStart, newEnd);
+ }
+};
+
+/**
+ * Helper function to calculate the center date for zooming
+ * @param {Component} component
+ * @param {{x: Number, y: Number}} pointer
+ * @param {String} direction 'horizontal' or 'vertical'
+ * @return {number} date
+ * @private
+ */
+Range.prototype._pointerToDate = function (component, direction, pointer) {
+ var conversion;
+ if (direction == 'horizontal') {
+ var width = component.width;
+ conversion = this.conversion(width);
+ return pointer.x / conversion.scale + conversion.offset;
+ }
+ else {
+ var height = component.height;
+ conversion = this.conversion(height);
+ return pointer.y / conversion.scale + conversion.offset;
+ }
+};
+
+/**
+ * Get the pointer location relative to the location of the dom element
+ * @param {{pageX: Number, pageY: Number}} touch
+ * @param {Element} element HTML DOM element
+ * @return {{x: Number, y: Number}} pointer
+ * @private
+ */
+function getPointer (touch, element) {
+ return {
+ x: touch.pageX - vis.util.getAbsoluteLeft(element),
+ y: touch.pageY - vis.util.getAbsoluteTop(element)
+ };
+}
+
+/**
+ * Zoom the range the given scale in or out. Start and end date will
+ * be adjusted, and the timeline will be redrawn. You can optionally give a
+ * date around which to zoom.
+ * For example, try scale = 0.9 or 1.1
+ * @param {Number} scale Scaling factor. Values above 1 will zoom out,
+ * values below 1 will zoom in.
+ * @param {Number} [center] Value representing a date around which will
+ * be zoomed.
+ */
+Range.prototype.zoom = function(scale, center) {
+ // if centerDate is not provided, take it half between start Date and end Date
+ if (center == null) {
+ center = (this.start + this.end) / 2;
+ }
+
+ // calculate new start and end
+ var newStart = center + (this.start - center) * scale;
+ var newEnd = center + (this.end - center) * scale;
+
+ this.setRange(newStart, newEnd);
};
/**
- * Move the range with a given factor to the left or right. Start and end
- * value will be adjusted. For example, try moveFactor = 0.1 or -0.1
- * @param {Number} moveFactor Moving amount. Positive value will move right,
- * negative value will move left
+ * Move the range with a given delta to the left or right. Start and end
+ * value will be adjusted. For example, try delta = 0.1 or -0.1
+ * @param {Number} delta Moving amount. Positive value will move right,
+ * negative value will move left
*/
-Range.prototype.move = function(moveFactor) {
- // zoom start Date and end Date relative to the zoomAroundDate
- var diff = (this.end - this.start);
+Range.prototype.move = function(delta) {
+ // zoom start Date and end Date relative to the centerDate
+ var diff = (this.end - this.start);
- // apply new values
- var newStart = this.start + diff * moveFactor;
- var newEnd = this.end + diff * moveFactor;
+ // apply new values
+ var newStart = this.start + diff * delta;
+ var newEnd = this.end + diff * delta;
- // TODO: reckon with min and max range
+ // TODO: reckon with min and max range
- this.start = newStart;
- this.end = newEnd;
+ this.start = newStart;
+ this.end = newEnd;
};
/**
@@ -7303,16 +6757,16 @@ Range.prototype.move = function(moveFactor) {
* @param {Number} moveTo New center point of the range
*/
Range.prototype.moveTo = function(moveTo) {
- var center = (this.start + this.end) / 2;
+ var center = (this.start + this.end) / 2;
- var diff = center - moveTo;
+ var diff = center - moveTo;
- // calculate new start and end
- var newStart = this.start - diff;
- var newEnd = this.end - diff;
+ // calculate new start and end
+ var newStart = this.start - diff;
+ var newEnd = this.end - diff;
- this.setRange(newStart, newEnd);
-}
+ this.setRange(newStart, newEnd);
+};
/**
* @constructor Controller
@@ -7320,11 +6774,11 @@ Range.prototype.moveTo = function(moveTo) {
* A Controller controls the reflows and repaints of all visual components
*/
function Controller () {
- this.id = util.randomUUID();
- this.components = {};
+ this.id = util.randomUUID();
+ this.components = {};
- this.repaintTimer = undefined;
- this.reflowTimer = undefined;
+ this.repaintTimer = undefined;
+ this.reflowTimer = undefined;
}
/**
@@ -7332,18 +6786,18 @@ function Controller () {
* @param {Component} component
*/
Controller.prototype.add = function add(component) {
- // validate the component
- if (component.id == undefined) {
- throw new Error('Component has no field id');
- }
- if (!(component instanceof Component) && !(component instanceof Controller)) {
- throw new TypeError('Component must be an instance of ' +
- 'prototype Component or Controller');
- }
+ // validate the component
+ if (component.id == undefined) {
+ throw new Error('Component has no field id');
+ }
+ if (!(component instanceof Component) && !(component instanceof Controller)) {
+ throw new TypeError('Component must be an instance of ' +
+ 'prototype Component or Controller');
+ }
- // add the component
- component.controller = this;
- this.components[component.id] = component;
+ // add the component
+ component.controller = this;
+ this.components[component.id] = component;
};
/**
@@ -7351,18 +6805,18 @@ Controller.prototype.add = function add(component) {
* @param {Component | String} component
*/
Controller.prototype.remove = function remove(component) {
- var id;
- for (id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- if (id == component || this.components[id] == component) {
- break;
- }
- }
+ var id;
+ for (id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ if (id == component || this.components[id] == component) {
+ break;
+ }
}
+ }
- if (id) {
- delete this.components[id];
- }
+ if (id) {
+ delete this.components[id];
+ }
};
/**
@@ -7371,18 +6825,18 @@ Controller.prototype.remove = function remove(component) {
* is false.
*/
Controller.prototype.requestReflow = function requestReflow(force) {
- if (force) {
- this.reflow();
- }
- else {
- if (!this.reflowTimer) {
- var me = this;
- this.reflowTimer = setTimeout(function () {
- me.reflowTimer = undefined;
- me.reflow();
- }, 0);
- }
+ if (force) {
+ this.reflow();
+ }
+ else {
+ if (!this.reflowTimer) {
+ var me = this;
+ this.reflowTimer = setTimeout(function () {
+ me.reflowTimer = undefined;
+ me.reflow();
+ }, 0);
}
+ }
};
/**
@@ -7391,117 +6845,117 @@ Controller.prototype.requestReflow = function requestReflow(force) {
* is false.
*/
Controller.prototype.requestRepaint = function requestRepaint(force) {
- if (force) {
- this.repaint();
- }
- else {
- if (!this.repaintTimer) {
- var me = this;
- this.repaintTimer = setTimeout(function () {
- me.repaintTimer = undefined;
- me.repaint();
- }, 0);
- }
+ if (force) {
+ this.repaint();
+ }
+ else {
+ if (!this.repaintTimer) {
+ var me = this;
+ this.repaintTimer = setTimeout(function () {
+ me.repaintTimer = undefined;
+ me.repaint();
+ }, 0);
}
+ }
};
/**
* Repaint all components
*/
Controller.prototype.repaint = function repaint() {
- var changed = false;
+ var changed = false;
- // cancel any running repaint request
- if (this.repaintTimer) {
- clearTimeout(this.repaintTimer);
- this.repaintTimer = undefined;
- }
+ // cancel any running repaint request
+ if (this.repaintTimer) {
+ clearTimeout(this.repaintTimer);
+ this.repaintTimer = undefined;
+ }
- var done = {};
+ var done = {};
- function repaint(component, id) {
- if (!(id in done)) {
- // first repaint the components on which this component is dependent
- if (component.depends) {
- component.depends.forEach(function (dep) {
- repaint(dep, dep.id);
- });
- }
- if (component.parent) {
- repaint(component.parent, component.parent.id);
- }
+ function repaint(component, id) {
+ if (!(id in done)) {
+ // first repaint the components on which this component is dependent
+ if (component.depends) {
+ component.depends.forEach(function (dep) {
+ repaint(dep, dep.id);
+ });
+ }
+ if (component.parent) {
+ repaint(component.parent, component.parent.id);
+ }
- // repaint the component itself and mark as done
- changed = component.repaint() || changed;
- done[id] = true;
- }
+ // repaint the component itself and mark as done
+ changed = component.repaint() || changed;
+ done[id] = true;
}
+ }
- util.forEach(this.components, repaint);
+ util.forEach(this.components, repaint);
- // immediately reflow when needed
- if (changed) {
- this.reflow();
- }
- // TODO: limit the number of nested reflows/repaints, prevent loop
+ // immediately reflow when needed
+ if (changed) {
+ this.reflow();
+ }
+ // TODO: limit the number of nested reflows/repaints, prevent loop
};
/**
* Reflow all components
*/
Controller.prototype.reflow = function reflow() {
- var resized = false;
+ var resized = false;
- // cancel any running repaint request
- if (this.reflowTimer) {
- clearTimeout(this.reflowTimer);
- this.reflowTimer = undefined;
- }
+ // cancel any running repaint request
+ if (this.reflowTimer) {
+ clearTimeout(this.reflowTimer);
+ this.reflowTimer = undefined;
+ }
- var done = {};
+ var done = {};
- function reflow(component, id) {
- if (!(id in done)) {
- // first reflow the components on which this component is dependent
- if (component.depends) {
- component.depends.forEach(function (dep) {
- reflow(dep, dep.id);
- });
- }
- if (component.parent) {
- reflow(component.parent, component.parent.id);
- }
+ function reflow(component, id) {
+ if (!(id in done)) {
+ // first reflow the components on which this component is dependent
+ if (component.depends) {
+ component.depends.forEach(function (dep) {
+ reflow(dep, dep.id);
+ });
+ }
+ if (component.parent) {
+ reflow(component.parent, component.parent.id);
+ }
- // reflow the component itself and mark as done
- resized = component.reflow() || resized;
- done[id] = true;
- }
+ // reflow the component itself and mark as done
+ resized = component.reflow() || resized;
+ done[id] = true;
}
+ }
- util.forEach(this.components, reflow);
+ util.forEach(this.components, reflow);
- // immediately repaint when needed
- if (resized) {
- this.repaint();
- }
- // TODO: limit the number of nested reflows/repaints, prevent loop
+ // immediately repaint when needed
+ if (resized) {
+ this.repaint();
+ }
+ // TODO: limit the number of nested reflows/repaints, prevent loop
};
/**
* Prototype for visual components
*/
function Component () {
- this.id = null;
- this.parent = null;
- this.depends = null;
- this.controller = null;
- this.options = null;
-
- this.frame = null; // main DOM element
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.id = null;
+ this.parent = null;
+ this.depends = null;
+ this.controller = null;
+ this.options = null;
+
+ this.frame = null; // main DOM element
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
/**
@@ -7516,14 +6970,14 @@ function Component () {
* {String | Number | function} [height]
*/
Component.prototype.setOptions = function setOptions(options) {
- if (options) {
- util.extend(this.options, options);
+ if (options) {
+ util.extend(this.options, options);
- if (this.controller) {
- this.requestRepaint();
- this.requestReflow();
- }
+ if (this.controller) {
+ this.requestRepaint();
+ this.requestReflow();
}
+ }
};
/**
@@ -7534,14 +6988,14 @@ Component.prototype.setOptions = function setOptions(options) {
* @return {*} value
*/
Component.prototype.getOption = function getOption(name) {
- var value;
- if (this.options) {
- value = this.options[name];
- }
- if (value === undefined && this.defaultOptions) {
- value = this.defaultOptions[name];
- }
- return value;
+ var value;
+ if (this.options) {
+ value = this.options[name];
+ }
+ if (value === undefined && this.defaultOptions) {
+ value = this.defaultOptions[name];
+ }
+ return value;
};
/**
@@ -7552,8 +7006,8 @@ Component.prototype.getOption = function getOption(name) {
*/
// TODO: get rid of the getContainer and getFrame methods, provide these via the options
Component.prototype.getContainer = function getContainer() {
- // should be implemented by the component
- return null;
+ // should be implemented by the component
+ return null;
};
/**
@@ -7561,7 +7015,7 @@ Component.prototype.getContainer = function getContainer() {
* @returns {HTMLElement | null} frame
*/
Component.prototype.getFrame = function getFrame() {
- return this.frame;
+ return this.frame;
};
/**
@@ -7569,8 +7023,8 @@ Component.prototype.getFrame = function getFrame() {
* @return {Boolean} changed
*/
Component.prototype.repaint = function repaint() {
- // should be implemented by the component
- return false;
+ // should be implemented by the component
+ return false;
};
/**
@@ -7578,8 +7032,8 @@ Component.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Component.prototype.reflow = function reflow() {
- // should be implemented by the component
- return false;
+ // should be implemented by the component
+ return false;
};
/**
@@ -7587,13 +7041,13 @@ Component.prototype.reflow = function reflow() {
* @return {Boolean} changed
*/
Component.prototype.hide = function hide() {
- if (this.frame && this.frame.parentNode) {
- this.frame.parentNode.removeChild(this.frame);
- return true;
- }
- else {
- return false;
- }
+ if (this.frame && this.frame.parentNode) {
+ this.frame.parentNode.removeChild(this.frame);
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -7602,38 +7056,38 @@ Component.prototype.hide = function hide() {
* @return {Boolean} changed
*/
Component.prototype.show = function show() {
- if (!this.frame || !this.frame.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.frame || !this.frame.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
* Request a repaint. The controller will schedule a repaint
*/
Component.prototype.requestRepaint = function requestRepaint() {
- if (this.controller) {
- this.controller.requestRepaint();
- }
- else {
- throw new Error('Cannot request a repaint: no controller configured');
- // TODO: just do a repaint when no parent is configured?
- }
+ if (this.controller) {
+ this.controller.requestRepaint();
+ }
+ else {
+ throw new Error('Cannot request a repaint: no controller configured');
+ // TODO: just do a repaint when no parent is configured?
+ }
};
/**
* Request a reflow. The controller will schedule a reflow
*/
Component.prototype.requestReflow = function requestReflow() {
- if (this.controller) {
- this.controller.requestReflow();
- }
- else {
- throw new Error('Cannot request a reflow: no controller configured');
- // TODO: just do a reflow when no parent is configured?
- }
+ if (this.controller) {
+ this.controller.requestReflow();
+ }
+ else {
+ throw new Error('Cannot request a reflow: no controller configured');
+ // TODO: just do a reflow when no parent is configured?
+ }
};
/**
@@ -7651,11 +7105,11 @@ Component.prototype.requestReflow = function requestReflow() {
* @extends Component
*/
function Panel(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
+ this.options = options || {};
}
Panel.prototype = new Component();
@@ -7677,7 +7131,7 @@ Panel.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
Panel.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -7685,46 +7139,46 @@ Panel.prototype.getContainer = function () {
* @return {Boolean} changed
*/
Panel.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'panel';
-
- var className = options.className;
- if (className) {
- if (typeof className == 'function') {
- util.addClassName(frame, String(className()));
- }
- else {
- util.addClassName(frame, String(className));
- }
- }
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ frame = this.frame;
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'panel';
+
+ var className = options.className;
+ if (className) {
+ if (typeof className == 'function') {
+ util.addClassName(frame, String(className()));
+ }
+ else {
+ util.addClassName(frame, String(className));
+ }
+ }
- this.frame = frame;
- changed += 1;
+ this.frame = frame;
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint panel: no parent attached');
}
- if (!frame.parentNode) {
- if (!this.parent) {
- throw new Error('Cannot repaint panel: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint panel: parent has no container element');
- }
- parentContainer.appendChild(frame);
- changed += 1;
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint panel: parent has no container element');
}
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, '100%'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -7732,21 +7186,21 @@ Panel.prototype.repaint = function () {
* @return {Boolean} resized
*/
Panel.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame;
-
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', frame.offsetHeight);
- }
- else {
- changed += 1;
- }
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame;
+
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', frame.offsetHeight);
+ }
+ else {
+ changed += 1;
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -7758,15 +7212,15 @@ Panel.prototype.reflow = function () {
* @extends Panel
*/
function RootPanel(container, options) {
- this.id = util.randomUUID();
- this.container = container;
+ this.id = util.randomUUID();
+ this.container = container;
- this.options = options || {};
- this.defaultOptions = {
- autoResize: true
- };
+ this.options = options || {};
+ this.defaultOptions = {
+ autoResize: true
+ };
- this.listeners = {}; // event listeners
+ this.listeners = {}; // event listeners
}
RootPanel.prototype = new Panel();
@@ -7788,42 +7242,42 @@ RootPanel.prototype.setOptions = Component.prototype.setOptions;
* @return {Boolean} changed
*/
RootPanel.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- frame = this.frame;
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'vis timeline rootpanel';
+ if (!frame) {
+ frame = document.createElement('div');
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
-
- this.frame = frame;
+ this.frame = frame;
- changed += 1;
- }
- if (!frame.parentNode) {
- if (!this.container) {
- throw new Error('Cannot repaint root panel: no container attached');
- }
- this.container.appendChild(frame);
- changed += 1;
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ if (!this.container) {
+ throw new Error('Cannot repaint root panel: no container attached');
}
+ this.container.appendChild(frame);
+ changed += 1;
+ }
+
+ frame.className = 'vis timeline rootpanel ' + options.orientation;
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
+ }
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, '100%'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, '100%'));
- this._updateEventEmitters();
- this._updateWatch();
+ this._updateEventEmitters();
+ this._updateWatch();
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -7831,21 +7285,21 @@ RootPanel.prototype.repaint = function () {
* @return {Boolean} resized
*/
RootPanel.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame;
-
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', frame.offsetHeight);
- }
- else {
- changed += 1;
- }
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame;
+
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', frame.offsetHeight);
+ }
+ else {
+ changed += 1;
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -7853,13 +7307,13 @@ RootPanel.prototype.reflow = function () {
* @private
*/
RootPanel.prototype._updateWatch = function () {
- var autoResize = this.getOption('autoResize');
- if (autoResize) {
- this._watch();
- }
- else {
- this._unwatch();
- }
+ var autoResize = this.getOption('autoResize');
+ if (autoResize) {
+ this._watch();
+ }
+ else {
+ this._unwatch();
+ }
};
/**
@@ -7868,31 +7322,31 @@ RootPanel.prototype._updateWatch = function () {
* @private
*/
RootPanel.prototype._watch = function () {
- var me = this;
+ var me = this;
- this._unwatch();
+ this._unwatch();
- var checkSize = function () {
- var autoResize = me.getOption('autoResize');
- if (!autoResize) {
- // stop watching when the option autoResize is changed to false
- me._unwatch();
- return;
- }
+ var checkSize = function () {
+ var autoResize = me.getOption('autoResize');
+ if (!autoResize) {
+ // stop watching when the option autoResize is changed to false
+ me._unwatch();
+ return;
+ }
- if (me.frame) {
- // check whether the frame is resized
- if ((me.frame.clientWidth != me.width) ||
- (me.frame.clientHeight != me.height)) {
- me.requestReflow();
- }
- }
- };
+ if (me.frame) {
+ // check whether the frame is resized
+ if ((me.frame.clientWidth != me.width) ||
+ (me.frame.clientHeight != me.height)) {
+ me.requestReflow();
+ }
+ }
+ };
- // TODO: automatically cleanup the event listener when the frame is deleted
- util.addEventListener(window, 'resize', checkSize);
+ // TODO: automatically cleanup the event listener when the frame is deleted
+ util.addEventListener(window, 'resize', checkSize);
- this.watchTimer = setInterval(checkSize, 1000);
+ this.watchTimer = setInterval(checkSize, 1000);
};
/**
@@ -7900,12 +7354,12 @@ RootPanel.prototype._watch = function () {
* @private
*/
RootPanel.prototype._unwatch = function () {
- if (this.watchTimer) {
- clearInterval(this.watchTimer);
- this.watchTimer = undefined;
- }
+ if (this.watchTimer) {
+ clearInterval(this.watchTimer);
+ this.watchTimer = undefined;
+ }
- // TODO: remove event listener on window.resize
+ // TODO: remove event listener on window.resize
};
/**
@@ -7915,15 +7369,15 @@ RootPanel.prototype._unwatch = function () {
* as parameter.
*/
RootPanel.prototype.on = function (event, callback) {
- // register the listener at this component
- var arr = this.listeners[event];
- if (!arr) {
- arr = [];
- this.listeners[event] = arr;
- }
- arr.push(callback);
+ // register the listener at this component
+ var arr = this.listeners[event];
+ if (!arr) {
+ arr = [];
+ this.listeners[event] = arr;
+ }
+ arr.push(callback);
- this._updateEventEmitters();
+ this._updateEventEmitters();
};
/**
@@ -7931,32 +7385,38 @@ RootPanel.prototype.on = function (event, callback) {
* @private
*/
RootPanel.prototype._updateEventEmitters = function () {
- if (this.listeners) {
- var me = this;
- util.forEach(this.listeners, function (listeners, event) {
- if (!me.emitters) {
- me.emitters = {};
- }
- if (!(event in me.emitters)) {
- // create event
- var frame = me.frame;
- if (frame) {
- //console.log('Created a listener for event ' + event + ' on component ' + me.id); // TODO: cleanup logging
- var callback = function(event) {
- listeners.forEach(function (listener) {
- // TODO: filter on event target!
- listener(event);
- });
- };
- me.emitters[event] = callback;
- util.addEventListener(frame, event, callback);
- }
- }
- });
+ if (this.listeners) {
+ var me = this;
+ util.forEach(this.listeners, function (listeners, event) {
+ if (!me.emitters) {
+ me.emitters = {};
+ }
+ if (!(event in me.emitters)) {
+ // create event
+ var frame = me.frame;
+ if (frame) {
+ //console.log('Created a listener for event ' + event + ' on component ' + me.id); // TODO: cleanup logging
+ var callback = function(event) {
+ listeners.forEach(function (listener) {
+ // TODO: filter on event target!
+ listener(event);
+ });
+ };
+ me.emitters[event] = callback;
- // TODO: be able to delete event listeners
- // TODO: be able to move event listeners to a parent when available
- }
+ if (!me.hammer) {
+ me.hammer = Hammer(frame, {
+ prevent_default: true
+ });
+ }
+ me.hammer.on(event, callback);
+ }
+ }
+ });
+
+ // TODO: be able to delete event listeners
+ // TODO: be able to move event listeners to a parent when available
+ }
};
/**
@@ -7970,41 +7430,41 @@ RootPanel.prototype._updateEventEmitters = function () {
* @extends Component
*/
function TimeAxis (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- this.dom = {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: [],
- redundant: {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: []
- }
- };
- this.props = {
- range: {
- start: 0,
- end: 0,
- minimumStep: 0
- },
- lineTop: 0
- };
-
- this.options = options || {};
- this.defaultOptions = {
- orientation: 'bottom', // supported: 'top', 'bottom'
- // TODO: implement timeaxis orientations 'left' and 'right'
- showMinorLabels: true,
- showMajorLabels: true
- };
-
- this.conversion = null;
- this.range = null;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ this.dom = {
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: [],
+ redundant: {
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: []
+ }
+ };
+ this.props = {
+ range: {
+ start: 0,
+ end: 0,
+ minimumStep: 0
+ },
+ lineTop: 0
+ };
+
+ this.options = options || {};
+ this.defaultOptions = {
+ orientation: 'bottom', // supported: 'top', 'bottom'
+ // TODO: implement timeaxis orientations 'left' and 'right'
+ showMinorLabels: true,
+ showMajorLabels: true
+ };
+
+ this.conversion = null;
+ this.range = null;
}
TimeAxis.prototype = new Component();
@@ -8017,11 +7477,11 @@ TimeAxis.prototype.setOptions = Component.prototype.setOptions;
* @param {Range | Object} range A Range or an object containing start and end.
*/
TimeAxis.prototype.setRange = function (range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
- }
- this.range = range;
+ if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
+ throw new TypeError('Range must be an instance of Range, ' +
+ 'or an object containing start and end.');
+ }
+ this.range = range;
};
/**
@@ -8030,8 +7490,8 @@ TimeAxis.prototype.setRange = function (range) {
* @return {Date} time The datetime the corresponds with given position x
*/
TimeAxis.prototype.toTime = function(x) {
- var conversion = this.conversion;
- return new Date(x / conversion.factor + conversion.offset);
+ var conversion = this.conversion;
+ return new Date(x / conversion.scale + conversion.offset);
};
/**
@@ -8042,8 +7502,8 @@ TimeAxis.prototype.toTime = function(x) {
* @private
*/
TimeAxis.prototype.toScreen = function(time) {
- var conversion = this.conversion;
- return (time.valueOf() - conversion.offset) * conversion.factor;
+ var conversion = this.conversion;
+ return (time.valueOf() - conversion.offset) * conversion.scale;
};
/**
@@ -8051,112 +7511,112 @@ TimeAxis.prototype.toScreen = function(time) {
* @return {Boolean} changed
*/
TimeAxis.prototype.repaint = function () {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- orientation = this.getOption('orientation'),
- props = this.props,
- step = this.step;
-
- var frame = this.frame;
- if (!frame) {
- frame = document.createElement('div');
- this.frame = frame;
- changed += 1;
- }
- frame.className = 'axis ' + orientation;
- // TODO: custom className?
-
- if (!frame.parentNode) {
- if (!this.parent) {
- throw new Error('Cannot repaint time axis: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint time axis: parent has no container element');
- }
- parentContainer.appendChild(frame);
-
- changed += 1;
- }
-
- var parent = frame.parentNode;
- if (parent) {
- var beforeChild = frame.nextSibling;
- parent.removeChild(frame); // take frame offline while updating (is almost twice as fast)
-
- var defaultTop = (orientation == 'bottom' && this.props.parentHeight && this.height) ?
- (this.props.parentHeight - this.height) + 'px' :
- '0px';
- changed += update(frame.style, 'top', asSize(options.top, defaultTop));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
-
- // get characters width and height
- this._repaintMeasureChars();
-
- if (this.step) {
- this._repaintStart();
-
- step.first();
- var xFirstMajorLabel = undefined;
- var max = 0;
- while (step.hasNext() && max < 1000) {
- max++;
- var cur = step.getCurrent(),
- x = this.toScreen(cur),
- isMajor = step.isMajor();
-
- // TODO: lines must have a width, such that we can create css backgrounds
-
- if (this.getOption('showMinorLabels')) {
- this._repaintMinorText(x, step.getLabelMinor());
- }
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ orientation = this.getOption('orientation'),
+ props = this.props,
+ step = this.step;
+
+ var frame = this.frame;
+ if (!frame) {
+ frame = document.createElement('div');
+ this.frame = frame;
+ changed += 1;
+ }
+ frame.className = 'axis';
+ // TODO: custom className?
- if (isMajor && this.getOption('showMajorLabels')) {
- if (x > 0) {
- if (xFirstMajorLabel == undefined) {
- xFirstMajorLabel = x;
- }
- this._repaintMajorText(x, step.getLabelMajor());
- }
- this._repaintMajorLine(x);
- }
- else {
- this._repaintMinorLine(x);
- }
+ if (!frame.parentNode) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint time axis: no parent attached');
+ }
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint time axis: parent has no container element');
+ }
+ parentContainer.appendChild(frame);
- step.next();
- }
+ changed += 1;
+ }
+
+ var parent = frame.parentNode;
+ if (parent) {
+ var beforeChild = frame.nextSibling;
+ parent.removeChild(frame); // take frame offline while updating (is almost twice as fast)
+
+ var defaultTop = (orientation == 'bottom' && this.props.parentHeight && this.height) ?
+ (this.props.parentHeight - this.height) + 'px' :
+ '0px';
+ changed += update(frame.style, 'top', asSize(options.top, defaultTop));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
- // create a major label on the left when needed
- if (this.getOption('showMajorLabels')) {
- var leftTime = this.toTime(0),
- leftText = step.getLabelMajor(leftTime),
- widthText = leftText.length * (props.majorCharWidth || 10) + 10; // upper bound estimation
+ // get characters width and height
+ this._repaintMeasureChars();
- if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
- this._repaintMajorText(0, leftText);
- }
- }
+ if (this.step) {
+ this._repaintStart();
- this._repaintEnd();
- }
+ step.first();
+ var xFirstMajorLabel = undefined;
+ var max = 0;
+ while (step.hasNext() && max < 1000) {
+ max++;
+ var cur = step.getCurrent(),
+ x = this.toScreen(cur),
+ isMajor = step.isMajor();
- this._repaintLine();
+ // TODO: lines must have a width, such that we can create css backgrounds
- // put frame online again
- if (beforeChild) {
- parent.insertBefore(frame, beforeChild);
+ if (this.getOption('showMinorLabels')) {
+ this._repaintMinorText(x, step.getLabelMinor());
+ }
+
+ if (isMajor && this.getOption('showMajorLabels')) {
+ if (x > 0) {
+ if (xFirstMajorLabel == undefined) {
+ xFirstMajorLabel = x;
+ }
+ this._repaintMajorText(x, step.getLabelMajor());
+ }
+ this._repaintMajorLine(x);
}
else {
- parent.appendChild(frame)
+ this._repaintMinorLine(x);
+ }
+
+ step.next();
+ }
+
+ // create a major label on the left when needed
+ if (this.getOption('showMajorLabels')) {
+ var leftTime = this.toTime(0),
+ leftText = step.getLabelMajor(leftTime),
+ widthText = leftText.length * (props.majorCharWidth || 10) + 10; // upper bound estimation
+
+ if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
+ this._repaintMajorText(0, leftText);
}
+ }
+
+ this._repaintEnd();
+ }
+
+ this._repaintLine();
+
+ // put frame online again
+ if (beforeChild) {
+ parent.insertBefore(frame, beforeChild);
+ }
+ else {
+ parent.appendChild(frame)
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -8165,18 +7625,18 @@ TimeAxis.prototype.repaint = function () {
* @private
*/
TimeAxis.prototype._repaintStart = function () {
- var dom = this.dom,
- redundant = dom.redundant;
+ var dom = this.dom,
+ redundant = dom.redundant;
- redundant.majorLines = dom.majorLines;
- redundant.majorTexts = dom.majorTexts;
- redundant.minorLines = dom.minorLines;
- redundant.minorTexts = dom.minorTexts;
+ redundant.majorLines = dom.majorLines;
+ redundant.majorTexts = dom.majorTexts;
+ redundant.minorLines = dom.minorLines;
+ redundant.minorTexts = dom.minorTexts;
- dom.majorLines = [];
- dom.majorTexts = [];
- dom.minorLines = [];
- dom.minorTexts = [];
+ dom.majorLines = [];
+ dom.majorTexts = [];
+ dom.minorLines = [];
+ dom.minorTexts = [];
};
/**
@@ -8184,14 +7644,14 @@ TimeAxis.prototype._repaintStart = function () {
* @private
*/
TimeAxis.prototype._repaintEnd = function () {
- util.forEach(this.dom.redundant, function (arr) {
- while (arr.length) {
- var elem = arr.pop();
- if (elem && elem.parentNode) {
- elem.parentNode.removeChild(elem);
- }
- }
- });
+ util.forEach(this.dom.redundant, function (arr) {
+ while (arr.length) {
+ var elem = arr.pop();
+ if (elem && elem.parentNode) {
+ elem.parentNode.removeChild(elem);
+ }
+ }
+ });
};
@@ -8202,23 +7662,23 @@ TimeAxis.prototype._repaintEnd = function () {
* @private
*/
TimeAxis.prototype._repaintMinorText = function (x, text) {
- // reuse redundant label
- var label = this.dom.redundant.minorTexts.shift();
-
- if (!label) {
- // create new label
- var content = document.createTextNode('');
- label = document.createElement('div');
- label.appendChild(content);
- label.className = 'text minor';
- this.frame.appendChild(label);
- }
- this.dom.minorTexts.push(label);
+ // reuse redundant label
+ var label = this.dom.redundant.minorTexts.shift();
+
+ if (!label) {
+ // create new label
+ var content = document.createTextNode('');
+ label = document.createElement('div');
+ label.appendChild(content);
+ label.className = 'text minor';
+ this.frame.appendChild(label);
+ }
+ this.dom.minorTexts.push(label);
- label.childNodes[0].nodeValue = text;
- label.style.left = x + 'px';
- label.style.top = this.props.minorLabelTop + 'px';
- //label.title = title; // TODO: this is a heavy operation
+ label.childNodes[0].nodeValue = text;
+ label.style.left = x + 'px';
+ label.style.top = this.props.minorLabelTop + 'px';
+ //label.title = title; // TODO: this is a heavy operation
};
/**
@@ -8228,23 +7688,23 @@ TimeAxis.prototype._repaintMinorText = function (x, text) {
* @private
*/
TimeAxis.prototype._repaintMajorText = function (x, text) {
- // reuse redundant label
- var label = this.dom.redundant.majorTexts.shift();
-
- if (!label) {
- // create label
- var content = document.createTextNode(text);
- label = document.createElement('div');
- label.className = 'text major';
- label.appendChild(content);
- this.frame.appendChild(label);
- }
- this.dom.majorTexts.push(label);
+ // reuse redundant label
+ var label = this.dom.redundant.majorTexts.shift();
+
+ if (!label) {
+ // create label
+ var content = document.createTextNode(text);
+ label = document.createElement('div');
+ label.className = 'text major';
+ label.appendChild(content);
+ this.frame.appendChild(label);
+ }
+ this.dom.majorTexts.push(label);
- label.childNodes[0].nodeValue = text;
- label.style.top = this.props.majorLabelTop + 'px';
- label.style.left = x + 'px';
- //label.title = title; // TODO: this is a heavy operation
+ label.childNodes[0].nodeValue = text;
+ label.style.top = this.props.majorLabelTop + 'px';
+ label.style.left = x + 'px';
+ //label.title = title; // TODO: this is a heavy operation
};
/**
@@ -8253,21 +7713,21 @@ TimeAxis.prototype._repaintMajorText = function (x, text) {
* @private
*/
TimeAxis.prototype._repaintMinorLine = function (x) {
- // reuse redundant line
- var line = this.dom.redundant.minorLines.shift();
-
- if (!line) {
- // create vertical line
- line = document.createElement('div');
- line.className = 'grid vertical minor';
- this.frame.appendChild(line);
- }
- this.dom.minorLines.push(line);
+ // reuse redundant line
+ var line = this.dom.redundant.minorLines.shift();
+
+ if (!line) {
+ // create vertical line
+ line = document.createElement('div');
+ line.className = 'grid vertical minor';
+ this.frame.appendChild(line);
+ }
+ this.dom.minorLines.push(line);
- var props = this.props;
- line.style.top = props.minorLineTop + 'px';
- line.style.height = props.minorLineHeight + 'px';
- line.style.left = (x - props.minorLineWidth / 2) + 'px';
+ var props = this.props;
+ line.style.top = props.minorLineTop + 'px';
+ line.style.height = props.minorLineHeight + 'px';
+ line.style.left = (x - props.minorLineWidth / 2) + 'px';
};
/**
@@ -8276,21 +7736,21 @@ TimeAxis.prototype._repaintMinorLine = function (x) {
* @private
*/
TimeAxis.prototype._repaintMajorLine = function (x) {
- // reuse redundant line
- var line = this.dom.redundant.majorLines.shift();
-
- if (!line) {
- // create vertical line
- line = document.createElement('DIV');
- line.className = 'grid vertical major';
- this.frame.appendChild(line);
- }
- this.dom.majorLines.push(line);
+ // reuse redundant line
+ var line = this.dom.redundant.majorLines.shift();
+
+ if (!line) {
+ // create vertical line
+ line = document.createElement('DIV');
+ line.className = 'grid vertical major';
+ this.frame.appendChild(line);
+ }
+ this.dom.majorLines.push(line);
- var props = this.props;
- line.style.top = props.majorLineTop + 'px';
- line.style.left = (x - props.majorLineWidth / 2) + 'px';
- line.style.height = props.majorLineHeight + 'px';
+ var props = this.props;
+ line.style.top = props.majorLineTop + 'px';
+ line.style.left = (x - props.majorLineWidth / 2) + 'px';
+ line.style.height = props.majorLineHeight + 'px';
};
@@ -8299,33 +7759,33 @@ TimeAxis.prototype._repaintMajorLine = function (x) {
* @private
*/
TimeAxis.prototype._repaintLine = function() {
- var line = this.dom.line,
- frame = this.frame,
- options = this.options;
-
- // line before all axis elements
- if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
- if (line) {
- // put this line at the end of all childs
- frame.removeChild(line);
- frame.appendChild(line);
- }
- else {
- // create the axis line
- line = document.createElement('div');
- line.className = 'grid horizontal major';
- frame.appendChild(line);
- this.dom.line = line;
- }
+ var line = this.dom.line,
+ frame = this.frame,
+ options = this.options;
- line.style.top = this.props.lineTop + 'px';
+ // line before all axis elements
+ if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
+ if (line) {
+ // put this line at the end of all childs
+ frame.removeChild(line);
+ frame.appendChild(line);
}
else {
- if (line && axis.parentElement) {
- frame.removeChild(axis.line);
- delete this.dom.line;
- }
+ // create the axis line
+ line = document.createElement('div');
+ line.className = 'grid horizontal major';
+ frame.appendChild(line);
+ this.dom.line = line;
+ }
+
+ line.style.top = this.props.lineTop + 'px';
+ }
+ else {
+ if (line && line.parentElement) {
+ frame.removeChild(line.line);
+ delete this.dom.line;
}
+ }
};
/**
@@ -8333,31 +7793,31 @@ TimeAxis.prototype._repaintLine = function() {
* @private
*/
TimeAxis.prototype._repaintMeasureChars = function () {
- // calculate the width and height of a single character
- // this is used to calculate the step size, and also the positioning of the
- // axis
- var dom = this.dom,
- text;
-
- if (!dom.measureCharMinor) {
- text = document.createTextNode('0');
- var measureCharMinor = document.createElement('DIV');
- measureCharMinor.className = 'text minor measure';
- measureCharMinor.appendChild(text);
- this.frame.appendChild(measureCharMinor);
-
- dom.measureCharMinor = measureCharMinor;
- }
+ // calculate the width and height of a single character
+ // this is used to calculate the step size, and also the positioning of the
+ // axis
+ var dom = this.dom,
+ text;
+
+ if (!dom.measureCharMinor) {
+ text = document.createTextNode('0');
+ var measureCharMinor = document.createElement('DIV');
+ measureCharMinor.className = 'text minor measure';
+ measureCharMinor.appendChild(text);
+ this.frame.appendChild(measureCharMinor);
+
+ dom.measureCharMinor = measureCharMinor;
+ }
- if (!dom.measureCharMajor) {
- text = document.createTextNode('0');
- var measureCharMajor = document.createElement('DIV');
- measureCharMajor.className = 'text major measure';
- measureCharMajor.appendChild(text);
- this.frame.appendChild(measureCharMajor);
+ if (!dom.measureCharMajor) {
+ text = document.createTextNode('0');
+ var measureCharMajor = document.createElement('DIV');
+ measureCharMajor.className = 'text major measure';
+ measureCharMajor.appendChild(text);
+ this.frame.appendChild(measureCharMajor);
- dom.measureCharMajor = measureCharMajor;
- }
+ dom.measureCharMajor = measureCharMajor;
+ }
};
/**
@@ -8365,121 +7825,121 @@ TimeAxis.prototype._repaintMeasureChars = function () {
* @return {Boolean} resized
*/
TimeAxis.prototype.reflow = function () {
- var changed = 0,
- update = util.updateProperty,
- frame = this.frame,
- range = this.range;
+ var changed = 0,
+ update = util.updateProperty,
+ frame = this.frame,
+ range = this.range;
- if (!range) {
- throw new Error('Cannot repaint time axis: no range configured');
- }
+ if (!range) {
+ throw new Error('Cannot repaint time axis: no range configured');
+ }
- if (frame) {
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
+ if (frame) {
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
- // calculate size of a character
- var props = this.props,
- showMinorLabels = this.getOption('showMinorLabels'),
- showMajorLabels = this.getOption('showMajorLabels'),
- measureCharMinor = this.dom.measureCharMinor,
- measureCharMajor = this.dom.measureCharMajor;
- if (measureCharMinor) {
- props.minorCharHeight = measureCharMinor.clientHeight;
- props.minorCharWidth = measureCharMinor.clientWidth;
- }
- if (measureCharMajor) {
- props.majorCharHeight = measureCharMajor.clientHeight;
- props.majorCharWidth = measureCharMajor.clientWidth;
- }
+ // calculate size of a character
+ var props = this.props,
+ showMinorLabels = this.getOption('showMinorLabels'),
+ showMajorLabels = this.getOption('showMajorLabels'),
+ measureCharMinor = this.dom.measureCharMinor,
+ measureCharMajor = this.dom.measureCharMajor;
+ if (measureCharMinor) {
+ props.minorCharHeight = measureCharMinor.clientHeight;
+ props.minorCharWidth = measureCharMinor.clientWidth;
+ }
+ if (measureCharMajor) {
+ props.majorCharHeight = measureCharMajor.clientHeight;
+ props.majorCharWidth = measureCharMajor.clientWidth;
+ }
- var parentHeight = frame.parentNode ? frame.parentNode.offsetHeight : 0;
- if (parentHeight != props.parentHeight) {
- props.parentHeight = parentHeight;
- changed += 1;
- }
- switch (this.getOption('orientation')) {
- case 'bottom':
- props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
- props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
+ var parentHeight = frame.parentNode ? frame.parentNode.offsetHeight : 0;
+ if (parentHeight != props.parentHeight) {
+ props.parentHeight = parentHeight;
+ changed += 1;
+ }
+ switch (this.getOption('orientation')) {
+ case 'bottom':
+ props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
+ props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
- props.minorLabelTop = 0;
- props.majorLabelTop = props.minorLabelTop + props.minorLabelHeight;
+ props.minorLabelTop = 0;
+ props.majorLabelTop = props.minorLabelTop + props.minorLabelHeight;
- props.minorLineTop = -this.top;
- props.minorLineHeight = Math.max(this.top + props.majorLabelHeight, 0);
- props.minorLineWidth = 1; // TODO: really calculate width
+ props.minorLineTop = -this.top;
+ props.minorLineHeight = Math.max(this.top + props.majorLabelHeight, 0);
+ props.minorLineWidth = 1; // TODO: really calculate width
- props.majorLineTop = -this.top;
- props.majorLineHeight = Math.max(this.top + props.minorLabelHeight + props.majorLabelHeight, 0);
- props.majorLineWidth = 1; // TODO: really calculate width
+ props.majorLineTop = -this.top;
+ props.majorLineHeight = Math.max(this.top + props.minorLabelHeight + props.majorLabelHeight, 0);
+ props.majorLineWidth = 1; // TODO: really calculate width
- props.lineTop = 0;
+ props.lineTop = 0;
- break;
+ break;
- case 'top':
- props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
- props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
+ case 'top':
+ props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
+ props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
- props.majorLabelTop = 0;
- props.minorLabelTop = props.majorLabelTop + props.majorLabelHeight;
+ props.majorLabelTop = 0;
+ props.minorLabelTop = props.majorLabelTop + props.majorLabelHeight;
- props.minorLineTop = props.minorLabelTop;
- props.minorLineHeight = Math.max(parentHeight - props.majorLabelHeight - this.top);
- props.minorLineWidth = 1; // TODO: really calculate width
+ props.minorLineTop = props.minorLabelTop;
+ props.minorLineHeight = Math.max(parentHeight - props.majorLabelHeight - this.top);
+ props.minorLineWidth = 1; // TODO: really calculate width
- props.majorLineTop = 0;
- props.majorLineHeight = Math.max(parentHeight - this.top);
- props.majorLineWidth = 1; // TODO: really calculate width
+ props.majorLineTop = 0;
+ props.majorLineHeight = Math.max(parentHeight - this.top);
+ props.majorLineWidth = 1; // TODO: really calculate width
- props.lineTop = props.majorLabelHeight + props.minorLabelHeight;
+ props.lineTop = props.majorLabelHeight + props.minorLabelHeight;
- break;
+ break;
- default:
- throw new Error('Unkown orientation "' + this.getOption('orientation') + '"');
- }
+ default:
+ throw new Error('Unkown orientation "' + this.getOption('orientation') + '"');
+ }
- var height = props.minorLabelHeight + props.majorLabelHeight;
- changed += update(this, 'width', frame.offsetWidth);
- changed += update(this, 'height', height);
+ var height = props.minorLabelHeight + props.majorLabelHeight;
+ changed += update(this, 'width', frame.offsetWidth);
+ changed += update(this, 'height', height);
- // calculate range and step
- this._updateConversion();
+ // calculate range and step
+ this._updateConversion();
- var start = util.convert(range.start, 'Number'),
- end = util.convert(range.end, 'Number'),
- minimumStep = this.toTime((props.minorCharWidth || 10) * 5).valueOf()
- -this.toTime(0).valueOf();
- this.step = new TimeStep(new Date(start), new Date(end), minimumStep);
- changed += update(props.range, 'start', start);
- changed += update(props.range, 'end', end);
- changed += update(props.range, 'minimumStep', minimumStep.valueOf());
- }
+ var start = util.convert(range.start, 'Number'),
+ end = util.convert(range.end, 'Number'),
+ minimumStep = this.toTime((props.minorCharWidth || 10) * 5).valueOf()
+ -this.toTime(0).valueOf();
+ this.step = new TimeStep(new Date(start), new Date(end), minimumStep);
+ changed += update(props.range, 'start', start);
+ changed += update(props.range, 'end', end);
+ changed += update(props.range, 'minimumStep', minimumStep.valueOf());
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
- * Calculate the factor and offset to convert a position on screen to the
+ * Calculate the scale and offset to convert a position on screen to the
* corresponding date and vice versa.
* After the method _updateConversion is executed once, the methods toTime
* and toScreen can be used.
* @private
*/
TimeAxis.prototype._updateConversion = function() {
- var range = this.range;
- if (!range) {
- throw new Error('No range configured');
- }
+ var range = this.range;
+ if (!range) {
+ throw new Error('No range configured');
+ }
- if (range.conversion) {
- this.conversion = range.conversion(this.width);
- }
- else {
- this.conversion = Range.conversion(range.start, range.end, this.width);
- }
+ if (range.conversion) {
+ this.conversion = range.conversion(this.width);
+ }
+ else {
+ this.conversion = Range.conversion(range.start, range.end, this.width);
+ }
};
/**
@@ -8494,14 +7954,14 @@ TimeAxis.prototype._updateConversion = function() {
*/
function CurrentTime (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- this.options = options || {};
- this.defaultOptions = {
- showCurrentTime: false
- };
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ this.options = options || {};
+ this.defaultOptions = {
+ showCurrentTime: false
+ };
}
CurrentTime.prototype = new Component();
@@ -8514,7 +7974,7 @@ CurrentTime.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
CurrentTime.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -8522,66 +7982,66 @@ CurrentTime.prototype.getContainer = function () {
* @return {Boolean} changed
*/
CurrentTime.prototype.repaint = function () {
- var bar = this.frame,
- parent = this.parent,
- parentContainer = parent.parent.getContainer();
+ var bar = this.frame,
+ parent = this.parent,
+ parentContainer = parent.parent.getContainer();
- if (!parent) {
- throw new Error('Cannot repaint bar: no parent attached');
- }
+ if (!parent) {
+ throw new Error('Cannot repaint bar: no parent attached');
+ }
- if (!parentContainer) {
- throw new Error('Cannot repaint bar: parent has no container element');
+ if (!parentContainer) {
+ throw new Error('Cannot repaint bar: parent has no container element');
+ }
+
+ if (!this.getOption('showCurrentTime')) {
+ if (bar) {
+ parentContainer.removeChild(bar);
+ delete this.frame;
}
- if (!this.getOption('showCurrentTime')) {
- if (bar) {
- parentContainer.removeChild(bar);
- delete this.frame;
- }
+ return;
+ }
- return;
- }
+ if (!bar) {
+ bar = document.createElement('div');
+ bar.className = 'currenttime';
+ bar.style.position = 'absolute';
+ bar.style.top = '0px';
+ bar.style.height = '100%';
- if (!bar) {
- bar = document.createElement('div');
- bar.className = 'currenttime';
- bar.style.position = 'absolute';
- bar.style.top = '0px';
- bar.style.height = '100%';
+ parentContainer.appendChild(bar);
+ this.frame = bar;
+ }
- parentContainer.appendChild(bar);
- this.frame = bar;
- }
+ if (!parent.conversion) {
+ parent._updateConversion();
+ }
- if (!parent.conversion) {
- parent._updateConversion();
- }
+ var now = new Date();
+ var x = parent.toScreen(now);
- var now = new Date();
- var x = parent.toScreen(now);
+ bar.style.left = x + 'px';
+ bar.title = 'Current time: ' + now;
- bar.style.left = x + 'px';
- bar.title = 'Current time: ' + now;
+ // start a timer to adjust for the new time
+ if (this.currentTimeTimer !== undefined) {
+ clearTimeout(this.currentTimeTimer);
+ delete this.currentTimeTimer;
+ }
- // start a timer to adjust for the new time
- if (this.currentTimeTimer !== undefined) {
- clearTimeout(this.currentTimeTimer);
- delete this.currentTimeTimer;
- }
+ var timeline = this;
+ var interval = 1 / parent.conversion.scale / 2;
- var timeline = this;
- var interval = 1 / parent.conversion.factor / 2;
+ if (interval < 30) {
+ interval = 30;
+ }
- if (interval < 30) {
- interval = 30;
- }
-
- this.currentTimeTimer = setTimeout(function() {
- timeline.repaint();
- }, interval);
+ this.currentTimeTimer = setTimeout(function() {
+ timeline.repaint();
+ }, interval);
- return false;
+ return false;
};
/**
@@ -8596,17 +8056,17 @@ CurrentTime.prototype.repaint = function () {
*/
function CustomTime (parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
- this.defaultOptions = {
- showCustomTime: false
- };
+ this.options = options || {};
+ this.defaultOptions = {
+ showCustomTime: false
+ };
- this.listeners = [];
- this.customTime = new Date();
+ this.listeners = [];
+ this.customTime = new Date();
}
CustomTime.prototype = new Component();
@@ -8619,7 +8079,7 @@ CustomTime.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
CustomTime.prototype.getContainer = function () {
- return this.frame;
+ return this.frame;
};
/**
@@ -8627,59 +8087,59 @@ CustomTime.prototype.getContainer = function () {
* @return {Boolean} changed
*/
CustomTime.prototype.repaint = function () {
- var bar = this.frame,
- parent = this.parent,
- parentContainer = parent.parent.getContainer();
+ var bar = this.frame,
+ parent = this.parent,
+ parentContainer = parent.parent.getContainer();
- if (!parent) {
- throw new Error('Cannot repaint bar: no parent attached');
- }
-
- if (!parentContainer) {
- throw new Error('Cannot repaint bar: parent has no container element');
- }
+ if (!parent) {
+ throw new Error('Cannot repaint bar: no parent attached');
+ }
- if (!this.getOption('showCustomTime')) {
- if (bar) {
- parentContainer.removeChild(bar);
- delete this.frame;
- }
+ if (!parentContainer) {
+ throw new Error('Cannot repaint bar: parent has no container element');
+ }
- return;
+ if (!this.getOption('showCustomTime')) {
+ if (bar) {
+ parentContainer.removeChild(bar);
+ delete this.frame;
}
- if (!bar) {
- bar = document.createElement('div');
- bar.className = 'customtime';
- bar.style.position = 'absolute';
- bar.style.top = '0px';
- bar.style.height = '100%';
+ return;
+ }
- parentContainer.appendChild(bar);
+ if (!bar) {
+ bar = document.createElement('div');
+ bar.className = 'customtime';
+ bar.style.position = 'absolute';
+ bar.style.top = '0px';
+ bar.style.height = '100%';
- var drag = document.createElement('div');
- drag.style.position = 'relative';
- drag.style.top = '0px';
- drag.style.left = '-10px';
- drag.style.height = '100%';
- drag.style.width = '20px';
- bar.appendChild(drag);
+ parentContainer.appendChild(bar);
- this.frame = bar;
+ var drag = document.createElement('div');
+ drag.style.position = 'relative';
+ drag.style.top = '0px';
+ drag.style.left = '-10px';
+ drag.style.height = '100%';
+ drag.style.width = '20px';
+ bar.appendChild(drag);
- this.subscribe(this, 'movetime');
- }
+ this.frame = bar;
- if (!parent.conversion) {
- parent._updateConversion();
- }
+ this.subscribe(this, 'movetime');
+ }
- var x = parent.toScreen(this.customTime);
+ if (!parent.conversion) {
+ parent._updateConversion();
+ }
- bar.style.left = x + 'px';
- bar.title = 'Time: ' + this.customTime;
+ var x = parent.toScreen(this.customTime);
- return false;
+ bar.style.left = x + 'px';
+ bar.title = 'Time: ' + this.customTime;
+
+ return false;
};
/**
@@ -8687,8 +8147,8 @@ CustomTime.prototype.repaint = function () {
* @param {Date} time
*/
CustomTime.prototype._setCustomTime = function(time) {
- this.customTime = new Date(time.valueOf());
- this.repaint();
+ this.customTime = new Date(time.valueOf());
+ this.repaint();
};
/**
@@ -8696,7 +8156,7 @@ CustomTime.prototype._setCustomTime = function(time) {
* @return {Date} customTime
*/
CustomTime.prototype._getCustomTime = function() {
- return new Date(this.customTime.valueOf());
+ return new Date(this.customTime.valueOf());
};
/**
@@ -8704,18 +8164,18 @@ CustomTime.prototype._getCustomTime = function() {
* @param {Component} component
*/
CustomTime.prototype.subscribe = function (component, event) {
- var me = this;
- var listener = {
- component: component,
- event: event,
- callback: function (event) {
- me._onMouseDown(event, listener);
- },
- params: {}
- };
+ var me = this;
+ var listener = {
+ component: component,
+ event: event,
+ callback: function (event) {
+ me._onMouseDown(event, listener);
+ },
+ params: {}
+ };
- component.on('mousedown', listener.callback);
- me.listeners.push(listener);
+ component.on('mousedown', listener.callback);
+ me.listeners.push(listener);
};
@@ -8726,13 +8186,13 @@ CustomTime.prototype.subscribe = function (component, event) {
* as parameter.
*/
CustomTime.prototype.on = function (event, callback) {
- var bar = this.frame;
- if (!bar) {
- throw new Error('Cannot add event listener: no parent attached');
- }
+ var bar = this.frame;
+ if (!bar) {
+ throw new Error('Cannot add event listener: no parent attached');
+ }
- events.addListener(this, event, callback);
- util.addEventListener(bar, event, callback);
+ events.addListener(this, event, callback);
+ util.addEventListener(bar, event, callback);
};
/**
@@ -8742,38 +8202,38 @@ CustomTime.prototype.on = function (event, callback) {
* @private
*/
CustomTime.prototype._onMouseDown = function(event, listener) {
- event = event || window.event;
- var params = listener.params;
+ event = event || window.event;
+ var params = listener.params;
- // only react on left mouse button down
- var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
- if (!leftButtonDown) {
- return;
- }
+ // only react on left mouse button down
+ var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
+ if (!leftButtonDown) {
+ return;
+ }
- // get mouse position
- params.mouseX = util.getPageX(event);
- params.moved = false;
-
- params.customTime = this.customTime;
+ // get mouse position
+ params.mouseX = util.getPageX(event);
+ params.moved = false;
- // add event listeners to handle moving the custom time bar
- var me = this;
- if (!params.onMouseMove) {
- params.onMouseMove = function (event) {
- me._onMouseMove(event, listener);
- };
- util.addEventListener(document, 'mousemove', params.onMouseMove);
- }
- if (!params.onMouseUp) {
- params.onMouseUp = function (event) {
- me._onMouseUp(event, listener);
- };
- util.addEventListener(document, 'mouseup', params.onMouseUp);
- }
+ params.customTime = this.customTime;
+
+ // add event listeners to handle moving the custom time bar
+ var me = this;
+ if (!params.onMouseMove) {
+ params.onMouseMove = function (event) {
+ me._onMouseMove(event, listener);
+ };
+ util.addEventListener(document, 'mousemove', params.onMouseMove);
+ }
+ if (!params.onMouseUp) {
+ params.onMouseUp = function (event) {
+ me._onMouseUp(event, listener);
+ };
+ util.addEventListener(document, 'mouseup', params.onMouseUp);
+ }
- util.stopPropagation(event);
- util.preventDefault(event);
+ util.stopPropagation(event);
+ util.preventDefault(event);
};
/**
@@ -8784,33 +8244,33 @@ CustomTime.prototype._onMouseDown = function(event, listener) {
* @private
*/
CustomTime.prototype._onMouseMove = function (event, listener) {
- event = event || window.event;
- var params = listener.params;
- var parent = this.parent;
+ event = event || window.event;
+ var params = listener.params;
+ var parent = this.parent;
- // calculate change in mouse position
- var mouseX = util.getPageX(event);
+ // calculate change in mouse position
+ var mouseX = util.getPageX(event);
- if (params.mouseX === undefined) {
- params.mouseX = mouseX;
- }
+ if (params.mouseX === undefined) {
+ params.mouseX = mouseX;
+ }
- var diff = mouseX - params.mouseX;
+ var diff = mouseX - params.mouseX;
- // if mouse movement is big enough, register it as a "moved" event
- if (Math.abs(diff) >= 1) {
- params.moved = true;
- }
+ // if mouse movement is big enough, register it as a "moved" event
+ if (Math.abs(diff) >= 1) {
+ params.moved = true;
+ }
- var x = parent.toScreen(params.customTime);
- var xnew = x + diff;
- var time = parent.toTime(xnew);
- this._setCustomTime(time);
+ var x = parent.toScreen(params.customTime);
+ var xnew = x + diff;
+ var time = parent.toTime(xnew);
+ this._setCustomTime(time);
- // fire a timechange event
- events.trigger(this, 'timechange', {customTime: this.customTime});
+ // fire a timechange event
+ events.trigger(this, 'timechange', {customTime: this.customTime});
- util.preventDefault(event);
+ util.preventDefault(event);
};
/**
@@ -8821,23 +8281,23 @@ CustomTime.prototype._onMouseMove = function (event, listener) {
* @private
*/
CustomTime.prototype._onMouseUp = function (event, listener) {
- event = event || window.event;
- var params = listener.params;
+ event = event || window.event;
+ var params = listener.params;
- // remove event listeners here, important for Safari
- if (params.onMouseMove) {
- util.removeEventListener(document, 'mousemove', params.onMouseMove);
- params.onMouseMove = null;
- }
- if (params.onMouseUp) {
- util.removeEventListener(document, 'mouseup', params.onMouseUp);
- params.onMouseUp = null;
- }
+ // remove event listeners here, important for Safari
+ if (params.onMouseMove) {
+ util.removeEventListener(document, 'mousemove', params.onMouseMove);
+ params.onMouseMove = null;
+ }
+ if (params.onMouseUp) {
+ util.removeEventListener(document, 'mouseup', params.onMouseUp);
+ params.onMouseUp = null;
+ }
- if (params.moved) {
- // fire a timechanged event
- events.trigger(this, 'timechanged', {customTime: this.customTime});
- }
+ if (params.moved) {
+ // fire a timechanged event
+ events.trigger(this, 'timechanged', {customTime: this.customTime});
+ }
};
/**
@@ -8854,63 +8314,63 @@ CustomTime.prototype._onMouseUp = function (event, listener) {
*/
// TODO: improve performance by replacing all Array.forEach with a for loop
function ItemSet(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
-
- // one options object is shared by this itemset and all its items
- this.options = options || {};
- this.defaultOptions = {
- type: 'box',
- align: 'center',
- orientation: 'bottom',
- margin: {
- axis: 20,
- item: 10
- },
- padding: 5
- };
-
- this.dom = {};
-
- var me = this;
- this.itemsData = null; // DataSet
- this.range = null; // Range or Object {start: number, end: number}
-
- this.listeners = {
- 'add': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onAdd(params.items);
- }
- },
- 'update': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onUpdate(params.items);
- }
- },
- 'remove': function (event, params, senderId) {
- if (senderId != me.id) {
- me._onRemove(params.items);
- }
- }
- };
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
+
+ // one options object is shared by this itemset and all its items
+ this.options = options || {};
+ this.defaultOptions = {
+ type: 'box',
+ align: 'center',
+ orientation: 'bottom',
+ margin: {
+ axis: 20,
+ item: 10
+ },
+ padding: 5
+ };
- this.items = {}; // object with an Item for every data item
- this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
- this.stack = new Stack(this, Object.create(this.options));
- this.conversion = null;
+ this.dom = {};
- // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
-}
+ var me = this;
+ this.itemsData = null; // DataSet
+ this.range = null; // Range or Object {start: number, end: number}
+
+ this.listeners = {
+ 'add': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onAdd(params.items);
+ }
+ },
+ 'update': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onUpdate(params.items);
+ }
+ },
+ 'remove': function (event, params, senderId) {
+ if (senderId != me.id) {
+ me._onRemove(params.items);
+ }
+ }
+ };
+
+ this.items = {}; // object with an Item for every data item
+ this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
+ this.stack = new Stack(this, Object.create(this.options));
+ this.conversion = null;
+
+ // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
+}
ItemSet.prototype = new Panel();
// available item types will be registered here
ItemSet.types = {
- box: ItemBox,
- range: ItemRange,
- rangeoverflow: ItemRangeOverflow,
- point: ItemPoint
+ box: ItemBox,
+ range: ItemRange,
+ rangeoverflow: ItemRangeOverflow,
+ point: ItemPoint
};
/**
@@ -8945,11 +8405,11 @@ ItemSet.prototype.setOptions = Component.prototype.setOptions;
* @param {Range | Object} range A Range or an object containing start and end.
*/
ItemSet.prototype.setRange = function setRange(range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
- }
- this.range = range;
+ if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
+ throw new TypeError('Range must be an instance of Range, ' +
+ 'or an object containing start and end.');
+ }
+ this.range = range;
};
/**
@@ -8957,170 +8417,170 @@ ItemSet.prototype.setRange = function setRange(range) {
* @return {Boolean} changed
*/
ItemSet.prototype.repaint = function repaint() {
- var changed = 0,
- update = util.updateProperty,
- asSize = util.option.asSize,
- options = this.options,
- orientation = this.getOption('orientation'),
- defaultOptions = this.defaultOptions,
- frame = this.frame;
-
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'itemset';
-
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
-
- // create background panel
- var background = document.createElement('div');
- background.className = 'background';
- frame.appendChild(background);
- this.dom.background = background;
-
- // create foreground panel
- var foreground = document.createElement('div');
- foreground.className = 'foreground';
- frame.appendChild(foreground);
- this.dom.foreground = foreground;
-
- // create axis panel
- var axis = document.createElement('div');
- axis.className = 'itemset-axis';
- //frame.appendChild(axis);
- this.dom.axis = axis;
-
- this.frame = frame;
- changed += 1;
- }
-
- if (!this.parent) {
- throw new Error('Cannot repaint itemset: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint itemset: parent has no container element');
- }
- if (!frame.parentNode) {
- parentContainer.appendChild(frame);
- changed += 1;
- }
- if (!this.dom.axis.parentNode) {
- parentContainer.appendChild(this.dom.axis);
- changed += 1;
+ var changed = 0,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ options = this.options,
+ orientation = this.getOption('orientation'),
+ defaultOptions = this.defaultOptions,
+ frame = this.frame;
+
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'itemset';
+
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
}
- // reposition frame
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+ // create background panel
+ var background = document.createElement('div');
+ background.className = 'background';
+ frame.appendChild(background);
+ this.dom.background = background;
- // reposition axis
- changed += update(this.dom.axis.style, 'left', asSize(options.left, '0px'));
- changed += update(this.dom.axis.style, 'width', asSize(options.width, '100%'));
- if (orientation == 'bottom') {
- changed += update(this.dom.axis.style, 'top', (this.height + this.top) + 'px');
- }
- else { // orientation == 'top'
- changed += update(this.dom.axis.style, 'top', this.top + 'px');
- }
+ // create foreground panel
+ var foreground = document.createElement('div');
+ foreground.className = 'foreground';
+ frame.appendChild(foreground);
+ this.dom.foreground = foreground;
- this._updateConversion();
+ // create axis panel
+ var axis = document.createElement('div');
+ axis.className = 'itemset-axis';
+ //frame.appendChild(axis);
+ this.dom.axis = axis;
- var me = this,
- queue = this.queue,
- itemsData = this.itemsData,
- items = this.items,
- dataOptions = {
- // TODO: cleanup
- // fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
- };
+ this.frame = frame;
+ changed += 1;
+ }
- // show/hide added/changed/removed items
- Object.keys(queue).forEach(function (id) {
- //var entry = queue[id];
- var action = queue[id];
- var item = items[id];
- //var item = entry.item;
- //noinspection FallthroughInSwitchStatementJS
- switch (action) {
- case 'add':
- case 'update':
- var itemData = itemsData && itemsData.get(id, dataOptions);
-
- if (itemData) {
- var type = itemData.type ||
- (itemData.start && itemData.end && 'range') ||
- options.type ||
- 'box';
- var constructor = ItemSet.types[type];
-
- // TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
- if (item) {
- // update item
- if (!constructor || !(item instanceof constructor)) {
- // item type has changed, hide and delete the item
- changed += item.hide();
- item = null;
- }
- else {
- item.data = itemData; // TODO: create a method item.setData ?
- changed++;
- }
- }
+ if (!this.parent) {
+ throw new Error('Cannot repaint itemset: no parent attached');
+ }
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint itemset: parent has no container element');
+ }
+ if (!frame.parentNode) {
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
+ if (!this.dom.axis.parentNode) {
+ parentContainer.appendChild(this.dom.axis);
+ changed += 1;
+ }
- if (!item) {
- // create item
- if (constructor) {
- item = new constructor(me, itemData, options, defaultOptions);
- changed++;
- }
- else {
- throw new TypeError('Unknown item type "' + type + '"');
- }
- }
+ // reposition frame
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+
+ // reposition axis
+ changed += update(this.dom.axis.style, 'left', asSize(options.left, '0px'));
+ changed += update(this.dom.axis.style, 'width', asSize(options.width, '100%'));
+ if (orientation == 'bottom') {
+ changed += update(this.dom.axis.style, 'top', (this.height + this.top) + 'px');
+ }
+ else { // orientation == 'top'
+ changed += update(this.dom.axis.style, 'top', this.top + 'px');
+ }
- // force a repaint (not only a reposition)
- item.repaint();
+ this._updateConversion();
+
+ var me = this,
+ queue = this.queue,
+ itemsData = this.itemsData,
+ items = this.items,
+ dataOptions = {
+ // TODO: cleanup
+ // fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
+ };
+
+ // show/hide added/changed/removed items
+ Object.keys(queue).forEach(function (id) {
+ //var entry = queue[id];
+ var action = queue[id];
+ var item = items[id];
+ //var item = entry.item;
+ //noinspection FallthroughInSwitchStatementJS
+ switch (action) {
+ case 'add':
+ case 'update':
+ var itemData = itemsData && itemsData.get(id, dataOptions);
+
+ if (itemData) {
+ var type = itemData.type ||
+ (itemData.start && itemData.end && 'range') ||
+ options.type ||
+ 'box';
+ var constructor = ItemSet.types[type];
+
+ // TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
+ if (item) {
+ // update item
+ if (!constructor || !(item instanceof constructor)) {
+ // item type has changed, hide and delete the item
+ changed += item.hide();
+ item = null;
+ }
+ else {
+ item.data = itemData; // TODO: create a method item.setData ?
+ changed++;
+ }
+ }
- items[id] = item;
- }
+ if (!item) {
+ // create item
+ if (constructor) {
+ item = new constructor(me, itemData, options, defaultOptions);
+ changed++;
+ }
+ else {
+ throw new TypeError('Unknown item type "' + type + '"');
+ }
+ }
- // update queue
- delete queue[id];
- break;
+ // force a repaint (not only a reposition)
+ item.repaint();
- case 'remove':
- if (item) {
- // remove DOM of the item
- changed += item.hide();
- }
+ items[id] = item;
+ }
- // update lists
- delete items[id];
- delete queue[id];
- break;
+ // update queue
+ delete queue[id];
+ break;
- default:
- console.log('Error: unknown action "' + action + '"');
+ case 'remove':
+ if (item) {
+ // remove DOM of the item
+ changed += item.hide();
}
- });
- // reposition all items. Show items only when in the visible area
- util.forEach(this.items, function (item) {
- if (item.visible) {
- changed += item.show();
- item.reposition();
- }
- else {
- changed += item.hide();
- }
- });
+ // update lists
+ delete items[id];
+ delete queue[id];
+ break;
+
+ default:
+ console.log('Error: unknown action "' + action + '"');
+ }
+ });
+
+ // reposition all items. Show items only when in the visible area
+ util.forEach(this.items, function (item) {
+ if (item.visible) {
+ changed += item.show();
+ item.reposition();
+ }
+ else {
+ changed += item.hide();
+ }
+ });
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -9128,7 +8588,7 @@ ItemSet.prototype.repaint = function repaint() {
* @return {HTMLElement} foreground
*/
ItemSet.prototype.getForeground = function getForeground() {
- return this.dom.foreground;
+ return this.dom.foreground;
};
/**
@@ -9136,7 +8596,7 @@ ItemSet.prototype.getForeground = function getForeground() {
* @return {HTMLElement} background
*/
ItemSet.prototype.getBackground = function getBackground() {
- return this.dom.background;
+ return this.dom.background;
};
/**
@@ -9144,7 +8604,7 @@ ItemSet.prototype.getBackground = function getBackground() {
* @return {HTMLElement} axis
*/
ItemSet.prototype.getAxis = function getAxis() {
- return this.dom.axis;
+ return this.dom.axis;
};
/**
@@ -9152,63 +8612,63 @@ ItemSet.prototype.getAxis = function getAxis() {
* @return {Boolean} resized
*/
ItemSet.prototype.reflow = function reflow () {
- var changed = 0,
- options = this.options,
- marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
- marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
- update = util.updateProperty,
- asNumber = util.option.asNumber,
- asSize = util.option.asSize,
- frame = this.frame;
-
- if (frame) {
- this._updateConversion();
-
- util.forEach(this.items, function (item) {
- changed += item.reflow();
- });
+ var changed = 0,
+ options = this.options,
+ marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
+ marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
+ update = util.updateProperty,
+ asNumber = util.option.asNumber,
+ asSize = util.option.asSize,
+ frame = this.frame;
+
+ if (frame) {
+ this._updateConversion();
- // TODO: stack.update should be triggered via an event, in stack itself
- // TODO: only update the stack when there are changed items
- this.stack.update();
+ util.forEach(this.items, function (item) {
+ changed += item.reflow();
+ });
- var maxHeight = asNumber(options.maxHeight);
- var fixedHeight = (asSize(options.height) != null);
- var height;
- if (fixedHeight) {
- height = frame.offsetHeight;
- }
- else {
- // height is not specified, determine the height from the height and positioned items
- var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
- if (visibleItems.length) {
- var min = visibleItems[0].top;
- var max = visibleItems[0].top + visibleItems[0].height;
- util.forEach(visibleItems, function (item) {
- min = Math.min(min, item.top);
- max = Math.max(max, (item.top + item.height));
- });
- height = (max - min) + marginAxis + marginItem;
- }
- else {
- height = marginAxis + marginItem;
- }
- }
- if (maxHeight != null) {
- height = Math.min(height, maxHeight);
- }
- changed += update(this, 'height', height);
+ // TODO: stack.update should be triggered via an event, in stack itself
+ // TODO: only update the stack when there are changed items
+ this.stack.update();
- // calculate height from items
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
+ var maxHeight = asNumber(options.maxHeight);
+ var fixedHeight = (asSize(options.height) != null);
+ var height;
+ if (fixedHeight) {
+ height = frame.offsetHeight;
}
else {
- changed += 1;
+ // height is not specified, determine the height from the height and positioned items
+ var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
+ if (visibleItems.length) {
+ var min = visibleItems[0].top;
+ var max = visibleItems[0].top + visibleItems[0].height;
+ util.forEach(visibleItems, function (item) {
+ min = Math.min(min, item.top);
+ max = Math.max(max, (item.top + item.height));
+ });
+ height = (max - min) + marginAxis + marginItem;
+ }
+ else {
+ height = marginAxis + marginItem;
+ }
+ }
+ if (maxHeight != null) {
+ height = Math.min(height, maxHeight);
}
+ changed += update(this, 'height', height);
- return (changed > 0);
+ // calculate height from items
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ }
+ else {
+ changed += 1;
+ }
+
+ return (changed > 0);
};
/**
@@ -9216,19 +8676,19 @@ ItemSet.prototype.reflow = function reflow () {
* @return {Boolean} changed
*/
ItemSet.prototype.hide = function hide() {
- var changed = false;
+ var changed = false;
- // remove the DOM
- if (this.frame && this.frame.parentNode) {
- this.frame.parentNode.removeChild(this.frame);
- changed = true;
- }
- if (this.dom.axis && this.dom.axis.parentNode) {
- this.dom.axis.parentNode.removeChild(this.dom.axis);
- changed = true;
- }
+ // remove the DOM
+ if (this.frame && this.frame.parentNode) {
+ this.frame.parentNode.removeChild(this.frame);
+ changed = true;
+ }
+ if (this.dom.axis && this.dom.axis.parentNode) {
+ this.dom.axis.parentNode.removeChild(this.dom.axis);
+ changed = true;
+ }
- return changed;
+ return changed;
};
/**
@@ -9236,43 +8696,43 @@ ItemSet.prototype.hide = function hide() {
* @param {vis.DataSet | null} items
*/
ItemSet.prototype.setItems = function setItems(items) {
- var me = this,
- ids,
- oldItemsData = this.itemsData;
+ var me = this,
+ ids,
+ oldItemsData = this.itemsData;
- // replace the dataset
- if (!items) {
- this.itemsData = null;
- }
- else if (items instanceof DataSet || items instanceof DataView) {
- this.itemsData = items;
- }
- else {
- throw new TypeError('Data must be an instance of DataSet');
- }
+ // replace the dataset
+ if (!items) {
+ this.itemsData = null;
+ }
+ else if (items instanceof DataSet || items instanceof DataView) {
+ this.itemsData = items;
+ }
+ else {
+ throw new TypeError('Data must be an instance of DataSet');
+ }
- if (oldItemsData) {
- // unsubscribe from old dataset
- util.forEach(this.listeners, function (callback, event) {
- oldItemsData.unsubscribe(event, callback);
- });
+ if (oldItemsData) {
+ // unsubscribe from old dataset
+ util.forEach(this.listeners, function (callback, event) {
+ oldItemsData.unsubscribe(event, callback);
+ });
- // remove all drawn items
- ids = oldItemsData.getIds();
- this._onRemove(ids);
- }
+ // remove all drawn items
+ ids = oldItemsData.getIds();
+ this._onRemove(ids);
+ }
- if (this.itemsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.listeners, function (callback, event) {
- me.itemsData.subscribe(event, callback, id);
- });
+ if (this.itemsData) {
+ // subscribe to new dataset
+ var id = this.id;
+ util.forEach(this.listeners, function (callback, event) {
+ me.itemsData.subscribe(event, callback, id);
+ });
- // draw all new items
- ids = this.itemsData.getIds();
- this._onAdd(ids);
- }
+ // draw all new items
+ ids = this.itemsData.getIds();
+ this._onAdd(ids);
+ }
};
/**
@@ -9280,7 +8740,7 @@ ItemSet.prototype.setItems = function setItems(items) {
* @returns {vis.DataSet | null}
*/
ItemSet.prototype.getItems = function getItems() {
- return this.itemsData;
+ return this.itemsData;
};
/**
@@ -9289,7 +8749,7 @@ ItemSet.prototype.getItems = function getItems() {
* @private
*/
ItemSet.prototype._onUpdate = function _onUpdate(ids) {
- this._toQueue('update', ids);
+ this._toQueue('update', ids);
};
/**
@@ -9298,7 +8758,7 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
* @private
*/
ItemSet.prototype._onAdd = function _onAdd(ids) {
- this._toQueue('add', ids);
+ this._toQueue('add', ids);
};
/**
@@ -9307,7 +8767,7 @@ ItemSet.prototype._onAdd = function _onAdd(ids) {
* @private
*/
ItemSet.prototype._onRemove = function _onRemove(ids) {
- this._toQueue('remove', ids);
+ this._toQueue('remove', ids);
};
/**
@@ -9316,36 +8776,36 @@ ItemSet.prototype._onRemove = function _onRemove(ids) {
* @param {Number[]} ids
*/
ItemSet.prototype._toQueue = function _toQueue(action, ids) {
- var queue = this.queue;
- ids.forEach(function (id) {
- queue[id] = action;
- });
-
- if (this.controller) {
- //this.requestReflow();
- this.requestRepaint();
- }
+ var queue = this.queue;
+ ids.forEach(function (id) {
+ queue[id] = action;
+ });
+
+ if (this.controller) {
+ //this.requestReflow();
+ this.requestRepaint();
+ }
};
/**
- * Calculate the factor and offset to convert a position on screen to the
+ * Calculate the scale and offset to convert a position on screen to the
* corresponding date and vice versa.
* After the method _updateConversion is executed once, the methods toTime
* and toScreen can be used.
* @private
*/
ItemSet.prototype._updateConversion = function _updateConversion() {
- var range = this.range;
- if (!range) {
- throw new Error('No range configured');
- }
+ var range = this.range;
+ if (!range) {
+ throw new Error('No range configured');
+ }
- if (range.conversion) {
- this.conversion = range.conversion(this.width);
- }
- else {
- this.conversion = Range.conversion(range.start, range.end, this.width);
- }
+ if (range.conversion) {
+ this.conversion = range.conversion(this.width);
+ }
+ else {
+ this.conversion = Range.conversion(range.start, range.end, this.width);
+ }
};
/**
@@ -9356,8 +8816,8 @@ ItemSet.prototype._updateConversion = function _updateConversion() {
* @return {Date} time The datetime the corresponds with given position x
*/
ItemSet.prototype.toTime = function toTime(x) {
- var conversion = this.conversion;
- return new Date(x / conversion.factor + conversion.offset);
+ var conversion = this.conversion;
+ return new Date(x / conversion.scale + conversion.offset);
};
/**
@@ -9369,8 +8829,8 @@ ItemSet.prototype.toTime = function toTime(x) {
* with the given date.
*/
ItemSet.prototype.toScreen = function toScreen(time) {
- var conversion = this.conversion;
- return (time.valueOf() - conversion.offset) * conversion.factor;
+ var conversion = this.conversion;
+ return (time.valueOf() - conversion.offset) * conversion.scale;
};
/**
@@ -9383,32 +8843,32 @@ ItemSet.prototype.toScreen = function toScreen(time) {
* // TODO: describe available options
*/
function Item (parent, data, options, defaultOptions) {
- this.parent = parent;
- this.data = data;
- this.dom = null;
- this.options = options || {};
- this.defaultOptions = defaultOptions || {};
-
- this.selected = false;
- this.visible = false;
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.parent = parent;
+ this.data = data;
+ this.dom = null;
+ this.options = options || {};
+ this.defaultOptions = defaultOptions || {};
+
+ this.selected = false;
+ this.visible = false;
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
/**
* Select current item
*/
Item.prototype.select = function select() {
- this.selected = true;
+ this.selected = true;
};
/**
* Unselect current item
*/
Item.prototype.unselect = function unselect() {
- this.selected = false;
+ this.selected = false;
};
/**
@@ -9416,7 +8876,7 @@ Item.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
Item.prototype.show = function show() {
- return false;
+ return false;
};
/**
@@ -9424,7 +8884,7 @@ Item.prototype.show = function show() {
* @return {Boolean} changed
*/
Item.prototype.hide = function hide() {
- return false;
+ return false;
};
/**
@@ -9432,8 +8892,8 @@ Item.prototype.hide = function hide() {
* @return {Boolean} changed
*/
Item.prototype.repaint = function repaint() {
- // should be implemented by the item
- return false;
+ // should be implemented by the item
+ return false;
};
/**
@@ -9441,8 +8901,8 @@ Item.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Item.prototype.reflow = function reflow() {
- // should be implemented by the item
- return false;
+ // should be implemented by the item
+ return false;
};
/**
@@ -9450,7 +8910,7 @@ Item.prototype.reflow = function reflow() {
* @return {Integer} width
*/
Item.prototype.getWidth = function getWidth() {
- return this.width;
+ return this.width;
}
/**
@@ -9464,22 +8924,22 @@ Item.prototype.getWidth = function getWidth() {
* // TODO: describe available options
*/
function ItemBox (parent, data, options, defaultOptions) {
- this.props = {
- dot: {
- left: 0,
- top: 0,
- width: 0,
- height: 0
- },
- line: {
- top: 0,
- left: 0,
- width: 0,
- height: 0
- }
- };
+ this.props = {
+ dot: {
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0
+ },
+ line: {
+ top: 0,
+ left: 0,
+ width: 0,
+ height: 0
+ }
+ };
- Item.call(this, parent, data, options, defaultOptions);
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemBox.prototype = new Item (null, null);
@@ -9489,8 +8949,8 @@ ItemBox.prototype = new Item (null, null);
* @override
*/
ItemBox.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -9498,8 +8958,8 @@ ItemBox.prototype.select = function select() {
* @override
*/
ItemBox.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -9507,78 +8967,80 @@ ItemBox.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemBox.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
+
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
}
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
- var background = this.parent.getBackground();
- if (!background) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no background container element');
- }
- var axis = this.parent.getAxis();
- if (!background) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no axis container element');
- }
+ if (!dom.box.parentNode) {
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
- if (!dom.box.parentNode) {
- foreground.appendChild(dom.box);
- changed = true;
- }
- if (!dom.line.parentNode) {
- background.appendChild(dom.line);
- changed = true;
- }
- if (!dom.dot.parentNode) {
- axis.appendChild(dom.dot);
- changed = true;
- }
+ if (!dom.line.parentNode) {
+ var background = this.parent.getBackground();
+ if (!background) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no background container element');
+ }
+ background.appendChild(dom.line);
+ changed = true;
+ }
- // update contents
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ if (!dom.dot.parentNode) {
+ var axis = this.parent.getAxis();
+ if (!background) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no axis container element');
+ }
+ axis.appendChild(dom.dot);
+ changed = true;
+ }
- // update class
- var className = (this.data.className? ' ' + this.data.className : '') +
- (this.selected ? ' selected' : '');
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item box' + className;
- dom.line.className = 'item line' + className;
- dom.dot.className = 'item dot' + className;
- changed = true;
- }
+ // update contents
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
}
- return changed;
+ // update class
+ var className = (this.data.className? ' ' + this.data.className : '') +
+ (this.selected ? ' selected' : '');
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item box' + className;
+ dom.line.className = 'item line' + className;
+ dom.dot.className = 'item dot' + className;
+ changed = true;
+ }
+ }
+
+ return changed;
};
/**
@@ -9587,12 +9049,12 @@ ItemBox.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemBox.prototype.show = function show() {
- if (!this.dom || !this.dom.box.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.box.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -9600,21 +9062,21 @@ ItemBox.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemBox.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.box.parentNode) {
- dom.box.parentNode.removeChild(dom.box);
- changed = true;
- }
- if (dom.line.parentNode) {
- dom.line.parentNode.removeChild(dom.line);
- }
- if (dom.dot.parentNode) {
- dom.dot.parentNode.removeChild(dom.dot);
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.box.parentNode) {
+ dom.box.parentNode.removeChild(dom.box);
+ changed = true;
}
- return changed;
+ if (dom.line.parentNode) {
+ dom.line.parentNode.removeChild(dom.line);
+ }
+ if (dom.dot.parentNode) {
+ dom.dot.parentNode.removeChild(dom.dot);
+ }
+ }
+ return changed;
};
/**
@@ -9623,87 +9085,87 @@ ItemBox.prototype.hide = function hide() {
* @override
*/
ItemBox.prototype.reflow = function reflow() {
- var changed = 0,
- update,
- dom,
- props,
- options,
- margin,
- start,
- align,
- orientation,
- top,
- left,
- data,
- range;
-
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
-
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item
- var interval = (range.end - range.start);
- this.visible = (data.start > range.start - interval) && (data.start < range.end + interval);
- }
- else {
- this.visible = false;
- }
-
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- update = util.updateProperty;
- props = this.props;
- options = this.options;
- start = this.parent.toScreen(this.data.start);
- align = options.align || this.defaultOptions.align;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- orientation = options.orientation || this.defaultOptions.orientation;
-
- changed += update(props.dot, 'height', dom.dot.offsetHeight);
- changed += update(props.dot, 'width', dom.dot.offsetWidth);
- changed += update(props.line, 'width', dom.line.offsetWidth);
- changed += update(props.line, 'height', dom.line.offsetHeight);
- changed += update(props.line, 'top', dom.line.offsetTop);
- changed += update(this, 'width', dom.box.offsetWidth);
- changed += update(this, 'height', dom.box.offsetHeight);
- if (align == 'right') {
- left = start - this.width;
- }
- else if (align == 'left') {
- left = start;
- }
- else {
- // default or 'center'
- left = start - this.width / 2;
- }
- changed += update(this, 'left', left);
+ var changed = 0,
+ update,
+ dom,
+ props,
+ options,
+ margin,
+ start,
+ align,
+ orientation,
+ top,
+ left,
+ data,
+ range;
+
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
- changed += update(props.line, 'left', start - props.line.width / 2);
- changed += update(props.dot, 'left', start - props.dot.width / 2);
- changed += update(props.dot, 'top', -props.dot.height / 2);
- if (orientation == 'top') {
- top = margin;
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item
+ var interval = (range.end - range.start);
+ this.visible = (data.start > range.start - interval) && (data.start < range.end + interval);
+ }
+ else {
+ this.visible = false;
+ }
- changed += update(this, 'top', top);
- }
- else {
- // default or 'bottom'
- var parentHeight = this.parent.height;
- top = parentHeight - this.height - margin;
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ update = util.updateProperty;
+ props = this.props;
+ options = this.options;
+ start = this.parent.toScreen(this.data.start);
+ align = options.align || this.defaultOptions.align;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ orientation = options.orientation || this.defaultOptions.orientation;
+
+ changed += update(props.dot, 'height', dom.dot.offsetHeight);
+ changed += update(props.dot, 'width', dom.dot.offsetWidth);
+ changed += update(props.line, 'width', dom.line.offsetWidth);
+ changed += update(props.line, 'height', dom.line.offsetHeight);
+ changed += update(props.line, 'top', dom.line.offsetTop);
+ changed += update(this, 'width', dom.box.offsetWidth);
+ changed += update(this, 'height', dom.box.offsetHeight);
+ if (align == 'right') {
+ left = start - this.width;
+ }
+ else if (align == 'left') {
+ left = start;
+ }
+ else {
+ // default or 'center'
+ left = start - this.width / 2;
+ }
+ changed += update(this, 'left', left);
- changed += update(this, 'top', top);
- }
- }
- else {
- changed += 1;
- }
+ changed += update(props.line, 'left', start - props.line.width / 2);
+ changed += update(props.dot, 'left', start - props.dot.width / 2);
+ changed += update(props.dot, 'top', -props.dot.height / 2);
+ if (orientation == 'top') {
+ top = margin;
+
+ changed += update(this, 'top', top);
+ }
+ else {
+ // default or 'bottom'
+ var parentHeight = this.parent.height;
+ top = parentHeight - this.height - margin;
+
+ changed += update(this, 'top', top);
+ }
+ }
+ else {
+ changed += 1;
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -9711,27 +9173,27 @@ ItemBox.prototype.reflow = function reflow() {
* @private
*/
ItemBox.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
-
- // create the box
- dom.box = document.createElement('DIV');
- // className is updated in repaint()
-
- // contents box (inside the background box). used for making margins
- dom.content = document.createElement('DIV');
- dom.content.className = 'content';
- dom.box.appendChild(dom.content);
-
- // line to axis
- dom.line = document.createElement('DIV');
- dom.line.className = 'line';
-
- // dot on axis
- dom.dot = document.createElement('DIV');
- dom.dot.className = 'dot';
- }
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+
+ // create the box
+ dom.box = document.createElement('DIV');
+ // className is updated in repaint()
+
+ // contents box (inside the background box). used for making margins
+ dom.content = document.createElement('DIV');
+ dom.content.className = 'content';
+ dom.box.appendChild(dom.content);
+
+ // line to axis
+ dom.line = document.createElement('DIV');
+ dom.line.className = 'line';
+
+ // dot on axis
+ dom.dot = document.createElement('DIV');
+ dom.dot.className = 'dot';
+ }
};
/**
@@ -9740,33 +9202,33 @@ ItemBox.prototype._create = function _create() {
* @override
*/
ItemBox.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props,
- orientation = this.options.orientation || this.defaultOptions.orientation;
-
- if (dom) {
- var box = dom.box,
- line = dom.line,
- dot = dom.dot;
+ var dom = this.dom,
+ props = this.props,
+ orientation = this.options.orientation || this.defaultOptions.orientation;
- box.style.left = this.left + 'px';
- box.style.top = this.top + 'px';
+ if (dom) {
+ var box = dom.box,
+ line = dom.line,
+ dot = dom.dot;
- line.style.left = props.line.left + 'px';
- if (orientation == 'top') {
- line.style.top = 0 + 'px';
- line.style.height = this.top + 'px';
- }
- else {
- // orientation 'bottom'
- line.style.top = (this.top + this.height) + 'px';
- line.style.height = Math.max(this.parent.height - this.top - this.height +
- this.props.dot.height / 2, 0) + 'px';
- }
+ box.style.left = this.left + 'px';
+ box.style.top = this.top + 'px';
- dot.style.left = props.dot.left + 'px';
- dot.style.top = props.dot.top + 'px';
+ line.style.left = props.line.left + 'px';
+ if (orientation == 'top') {
+ line.style.top = 0 + 'px';
+ line.style.height = this.top + 'px';
}
+ else {
+ // orientation 'bottom'
+ line.style.top = (this.top + this.height) + 'px';
+ line.style.height = Math.max(this.parent.height - this.top - this.height +
+ this.props.dot.height / 2, 0) + 'px';
+ }
+
+ dot.style.left = props.dot.left + 'px';
+ dot.style.top = props.dot.top + 'px';
+ }
};
/**
@@ -9780,19 +9242,19 @@ ItemBox.prototype.reposition = function reposition() {
* // TODO: describe available options
*/
function ItemPoint (parent, data, options, defaultOptions) {
- this.props = {
- dot: {
- top: 0,
- width: 0,
- height: 0
- },
- content: {
- height: 0,
- marginLeft: 0
- }
- };
+ this.props = {
+ dot: {
+ top: 0,
+ width: 0,
+ height: 0
+ },
+ content: {
+ height: 0,
+ marginLeft: 0
+ }
+ };
- Item.call(this, parent, data, options, defaultOptions);
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemPoint.prototype = new Item (null, null);
@@ -9802,8 +9264,8 @@ ItemPoint.prototype = new Item (null, null);
* @override
*/
ItemPoint.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -9811,8 +9273,8 @@ ItemPoint.prototype.select = function select() {
* @override
*/
ItemPoint.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -9820,59 +9282,59 @@ ItemPoint.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemPoint.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
- }
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
- if (!dom.point.parentNode) {
- foreground.appendChild(dom.point);
- foreground.appendChild(dom.point);
- changed = true;
- }
+ if (!dom.point.parentNode) {
+ foreground.appendChild(dom.point);
+ foreground.appendChild(dom.point);
+ changed = true;
+ }
- // update contents
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ // update contents
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
- // update class
- var className = (this.data.className? ' ' + this.data.className : '') +
- (this.selected ? ' selected' : '');
- if (this.className != className) {
- this.className = className;
- dom.point.className = 'item point' + className;
- changed = true;
- }
+ // update class
+ var className = (this.data.className? ' ' + this.data.className : '') +
+ (this.selected ? ' selected' : '');
+ if (this.className != className) {
+ this.className = className;
+ dom.point.className = 'item point' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
@@ -9881,12 +9343,12 @@ ItemPoint.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemPoint.prototype.show = function show() {
- if (!this.dom || !this.dom.point.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.point.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -9894,15 +9356,15 @@ ItemPoint.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemPoint.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.point.parentNode) {
- dom.point.parentNode.removeChild(dom.point);
- changed = true;
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.point.parentNode) {
+ dom.point.parentNode.removeChild(dom.point);
+ changed = true;
}
- return changed;
+ }
+ return changed;
};
/**
@@ -9911,70 +9373,70 @@ ItemPoint.prototype.hide = function hide() {
* @override
*/
ItemPoint.prototype.reflow = function reflow() {
- var changed = 0,
- update,
- dom,
- props,
- options,
- margin,
- orientation,
- start,
- top,
- data,
- range;
-
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
-
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item
- var interval = (range.end - range.start);
- this.visible = (data.start > range.start - interval) && (data.start < range.end);
+ var changed = 0,
+ update,
+ dom,
+ props,
+ options,
+ margin,
+ orientation,
+ start,
+ top,
+ data,
+ range;
+
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
+
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item
+ var interval = (range.end - range.start);
+ this.visible = (data.start > range.start - interval) && (data.start < range.end);
+ }
+ else {
+ this.visible = false;
+ }
+
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ update = util.updateProperty;
+ props = this.props;
+ options = this.options;
+ orientation = options.orientation || this.defaultOptions.orientation;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ start = this.parent.toScreen(this.data.start);
+
+ changed += update(this, 'width', dom.point.offsetWidth);
+ changed += update(this, 'height', dom.point.offsetHeight);
+ changed += update(props.dot, 'width', dom.dot.offsetWidth);
+ changed += update(props.dot, 'height', dom.dot.offsetHeight);
+ changed += update(props.content, 'height', dom.content.offsetHeight);
+
+ if (orientation == 'top') {
+ top = margin;
+ }
+ else {
+ // default or 'bottom'
+ var parentHeight = this.parent.height;
+ top = Math.max(parentHeight - this.height - margin, 0);
+ }
+ changed += update(this, 'top', top);
+ changed += update(this, 'left', start - props.dot.width / 2);
+ changed += update(props.content, 'marginLeft', 1.5 * props.dot.width);
+ //changed += update(props.content, 'marginRight', 0.5 * props.dot.width); // TODO
+
+ changed += update(props.dot, 'top', (this.height - props.dot.height) / 2);
}
else {
- this.visible = false;
- }
-
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- update = util.updateProperty;
- props = this.props;
- options = this.options;
- orientation = options.orientation || this.defaultOptions.orientation;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- start = this.parent.toScreen(this.data.start);
-
- changed += update(this, 'width', dom.point.offsetWidth);
- changed += update(this, 'height', dom.point.offsetHeight);
- changed += update(props.dot, 'width', dom.dot.offsetWidth);
- changed += update(props.dot, 'height', dom.dot.offsetHeight);
- changed += update(props.content, 'height', dom.content.offsetHeight);
-
- if (orientation == 'top') {
- top = margin;
- }
- else {
- // default or 'bottom'
- var parentHeight = this.parent.height;
- top = Math.max(parentHeight - this.height - margin, 0);
- }
- changed += update(this, 'top', top);
- changed += update(this, 'left', start - props.dot.width / 2);
- changed += update(props.content, 'marginLeft', 1.5 * props.dot.width);
- //changed += update(props.content, 'marginRight', 0.5 * props.dot.width); // TODO
-
- changed += update(props.dot, 'top', (this.height - props.dot.height) / 2);
- }
- else {
- changed += 1;
- }
+ changed += 1;
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -9982,24 +9444,24 @@ ItemPoint.prototype.reflow = function reflow() {
* @private
*/
ItemPoint.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
-
- // background box
- dom.point = document.createElement('div');
- // className is updated in repaint()
-
- // contents box, right from the dot
- dom.content = document.createElement('div');
- dom.content.className = 'content';
- dom.point.appendChild(dom.content);
-
- // dot at start
- dom.dot = document.createElement('div');
- dom.dot.className = 'dot';
- dom.point.appendChild(dom.dot);
- }
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+
+ // background box
+ dom.point = document.createElement('div');
+ // className is updated in repaint()
+
+ // contents box, right from the dot
+ dom.content = document.createElement('div');
+ dom.content.className = 'content';
+ dom.point.appendChild(dom.content);
+
+ // dot at start
+ dom.dot = document.createElement('div');
+ dom.dot.className = 'dot';
+ dom.point.appendChild(dom.dot);
+ }
};
/**
@@ -10008,18 +9470,18 @@ ItemPoint.prototype._create = function _create() {
* @override
*/
ItemPoint.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props;
+ var dom = this.dom,
+ props = this.props;
- if (dom) {
- dom.point.style.top = this.top + 'px';
- dom.point.style.left = this.left + 'px';
+ if (dom) {
+ dom.point.style.top = this.top + 'px';
+ dom.point.style.left = this.left + 'px';
- dom.content.style.marginLeft = props.content.marginLeft + 'px';
- //dom.content.style.marginRight = props.content.marginRight + 'px'; // TODO
+ dom.content.style.marginLeft = props.content.marginLeft + 'px';
+ //dom.content.style.marginRight = props.content.marginRight + 'px'; // TODO
- dom.dot.style.top = props.dot.top + 'px';
- }
+ dom.dot.style.top = props.dot.top + 'px';
+ }
};
/**
@@ -10033,14 +9495,14 @@ ItemPoint.prototype.reposition = function reposition() {
* // TODO: describe available options
*/
function ItemRange (parent, data, options, defaultOptions) {
- this.props = {
- content: {
- left: 0,
- width: 0
- }
- };
+ this.props = {
+ content: {
+ left: 0,
+ width: 0
+ }
+ };
- Item.call(this, parent, data, options, defaultOptions);
+ Item.call(this, parent, data, options, defaultOptions);
}
ItemRange.prototype = new Item (null, null);
@@ -10050,8 +9512,8 @@ ItemRange.prototype = new Item (null, null);
* @override
*/
ItemRange.prototype.select = function select() {
- this.selected = true;
- // TODO: select and unselect
+ this.selected = true;
+ // TODO: select and unselect
};
/**
@@ -10059,8 +9521,8 @@ ItemRange.prototype.select = function select() {
* @override
*/
ItemRange.prototype.unselect = function unselect() {
- this.selected = false;
- // TODO: select and unselect
+ this.selected = false;
+ // TODO: select and unselect
};
/**
@@ -10068,57 +9530,57 @@ ItemRange.prototype.unselect = function unselect() {
* @return {Boolean} changed
*/
ItemRange.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
- }
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
- if (!dom.box.parentNode) {
- foreground.appendChild(dom.box);
- changed = true;
- }
+ if (!dom.box.parentNode) {
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
- // update content
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ // update content
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
- // update class
- var className = this.data.className ? (' ' + this.data.className) : '';
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item range' + className;
- changed = true;
- }
+ // update class
+ var className = this.data.className ? (' ' + this.data.className) : '';
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item range' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
@@ -10127,12 +9589,12 @@ ItemRange.prototype.repaint = function repaint() {
* @return {Boolean} changed
*/
ItemRange.prototype.show = function show() {
- if (!this.dom || !this.dom.box.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom || !this.dom.box.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -10140,15 +9602,15 @@ ItemRange.prototype.show = function show() {
* @return {Boolean} changed
*/
ItemRange.prototype.hide = function hide() {
- var changed = false,
- dom = this.dom;
- if (dom) {
- if (dom.box.parentNode) {
- dom.box.parentNode.removeChild(dom.box);
- changed = true;
- }
+ var changed = false,
+ dom = this.dom;
+ if (dom) {
+ if (dom.box.parentNode) {
+ dom.box.parentNode.removeChild(dom.box);
+ changed = true;
}
- return changed;
+ }
+ return changed;
};
/**
@@ -10157,98 +9619,98 @@ ItemRange.prototype.hide = function hide() {
* @override
*/
ItemRange.prototype.reflow = function reflow() {
- var changed = 0,
- dom,
- props,
- options,
- margin,
- padding,
- parent,
- start,
- end,
- data,
- range,
- update,
- box,
- parentWidth,
- contentLeft,
- orientation,
- top;
-
- if (this.data.start == undefined) {
- throw new Error('Property "start" missing in item ' + this.data.id);
- }
- if (this.data.end == undefined) {
- throw new Error('Property "end" missing in item ' + this.data.id);
- }
-
- data = this.data;
- range = this.parent && this.parent.range;
- if (data && range) {
- // TODO: account for the width of the item. Take some margin
- this.visible = (data.start < range.end) && (data.end > range.start);
- }
- else {
- this.visible = false;
- }
-
- if (this.visible) {
- dom = this.dom;
- if (dom) {
- props = this.props;
- options = this.options;
- parent = this.parent;
- start = parent.toScreen(this.data.start);
- end = parent.toScreen(this.data.end);
- update = util.updateProperty;
- box = dom.box;
- parentWidth = parent.width;
- orientation = options.orientation || this.defaultOptions.orientation;
- margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
- padding = options.padding || this.defaultOptions.padding;
-
- changed += update(props.content, 'width', dom.content.offsetWidth);
-
- changed += update(this, 'height', box.offsetHeight);
-
- // limit the width of the this, as browsers cannot draw very wide divs
- if (start < -parentWidth) {
- start = -parentWidth;
- }
- if (end > 2 * parentWidth) {
- end = 2 * parentWidth;
- }
+ var changed = 0,
+ dom,
+ props,
+ options,
+ margin,
+ padding,
+ parent,
+ start,
+ end,
+ data,
+ range,
+ update,
+ box,
+ parentWidth,
+ contentLeft,
+ orientation,
+ top;
+
+ if (this.data.start == undefined) {
+ throw new Error('Property "start" missing in item ' + this.data.id);
+ }
+ if (this.data.end == undefined) {
+ throw new Error('Property "end" missing in item ' + this.data.id);
+ }
- // when range exceeds left of the window, position the contents at the left of the visible area
- if (start < 0) {
- contentLeft = Math.min(-start,
- (end - start - props.content.width - 2 * padding));
- // TODO: remove the need for options.padding. it's terrible.
- }
- else {
- contentLeft = 0;
- }
- changed += update(props.content, 'left', contentLeft);
+ data = this.data;
+ range = this.parent && this.parent.range;
+ if (data && range) {
+ // TODO: account for the width of the item. Take some margin
+ this.visible = (data.start < range.end) && (data.end > range.start);
+ }
+ else {
+ this.visible = false;
+ }
- if (orientation == 'top') {
- top = margin;
- changed += update(this, 'top', top);
- }
- else {
- // default or 'bottom'
- top = parent.height - this.height - margin;
- changed += update(this, 'top', top);
- }
+ if (this.visible) {
+ dom = this.dom;
+ if (dom) {
+ props = this.props;
+ options = this.options;
+ parent = this.parent;
+ start = parent.toScreen(this.data.start);
+ end = parent.toScreen(this.data.end);
+ update = util.updateProperty;
+ box = dom.box;
+ parentWidth = parent.width;
+ orientation = options.orientation || this.defaultOptions.orientation;
+ margin = options.margin && options.margin.axis || this.defaultOptions.margin.axis;
+ padding = options.padding || this.defaultOptions.padding;
+
+ changed += update(props.content, 'width', dom.content.offsetWidth);
+
+ changed += update(this, 'height', box.offsetHeight);
+
+ // limit the width of the this, as browsers cannot draw very wide divs
+ if (start < -parentWidth) {
+ start = -parentWidth;
+ }
+ if (end > 2 * parentWidth) {
+ end = 2 * parentWidth;
+ }
- changed += update(this, 'left', start);
- changed += update(this, 'width', Math.max(end - start, 1)); // TODO: reckon with border width;
- }
- else {
- changed += 1;
- }
+ // when range exceeds left of the window, position the contents at the left of the visible area
+ if (start < 0) {
+ contentLeft = Math.min(-start,
+ (end - start - props.content.width - 2 * padding));
+ // TODO: remove the need for options.padding. it's terrible.
+ }
+ else {
+ contentLeft = 0;
+ }
+ changed += update(props.content, 'left', contentLeft);
+
+ if (orientation == 'top') {
+ top = margin;
+ changed += update(this, 'top', top);
+ }
+ else {
+ // default or 'bottom'
+ top = parent.height - this.height - margin;
+ changed += update(this, 'top', top);
+ }
+
+ changed += update(this, 'left', start);
+ changed += update(this, 'width', Math.max(end - start, 1)); // TODO: reckon with border width;
+ }
+ else {
+ changed += 1;
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -10256,18 +9718,18 @@ ItemRange.prototype.reflow = function reflow() {
* @private
*/
ItemRange.prototype._create = function _create() {
- var dom = this.dom;
- if (!dom) {
- this.dom = dom = {};
- // background box
- dom.box = document.createElement('div');
- // className is updated in repaint()
-
- // contents box
- dom.content = document.createElement('div');
- dom.content.className = 'content';
- dom.box.appendChild(dom.content);
- }
+ var dom = this.dom;
+ if (!dom) {
+ this.dom = dom = {};
+ // background box
+ dom.box = document.createElement('div');
+ // className is updated in repaint()
+
+ // contents box
+ dom.content = document.createElement('div');
+ dom.content.className = 'content';
+ dom.box.appendChild(dom.content);
+ }
};
/**
@@ -10276,16 +9738,16 @@ ItemRange.prototype._create = function _create() {
* @override
*/
ItemRange.prototype.reposition = function reposition() {
- var dom = this.dom,
- props = this.props;
+ var dom = this.dom,
+ props = this.props;
- if (dom) {
- dom.box.style.top = this.top + 'px';
- dom.box.style.left = this.left + 'px';
- dom.box.style.width = this.width + 'px';
+ if (dom) {
+ dom.box.style.top = this.top + 'px';
+ dom.box.style.left = this.left + 'px';
+ dom.box.style.width = this.width + 'px';
- dom.content.style.left = props.content.left + 'px';
- }
+ dom.content.style.left = props.content.left + 'px';
+ }
};
/**
@@ -10299,14 +9761,14 @@ ItemRange.prototype.reposition = function reposition() {
* // TODO: describe available options
*/
function ItemRangeOverflow (parent, data, options, defaultOptions) {
- this.props = {
- content: {
- left: 0,
- width: 0
- }
- };
+ this.props = {
+ content: {
+ left: 0,
+ width: 0
+ }
+ };
- ItemRange.call(this, parent, data, options, defaultOptions);
+ ItemRange.call(this, parent, data, options, defaultOptions);
}
ItemRangeOverflow.prototype = new ItemRange (null, null);
@@ -10316,69 +9778,69 @@ ItemRangeOverflow.prototype = new ItemRange (null, null);
* @return {Boolean} changed
*/
ItemRangeOverflow.prototype.repaint = function repaint() {
- // TODO: make an efficient repaint
- var changed = false;
- var dom = this.dom;
+ // TODO: make an efficient repaint
+ var changed = false;
+ var dom = this.dom;
- if (!dom) {
- this._create();
- dom = this.dom;
- changed = true;
- }
+ if (!dom) {
+ this._create();
+ dom = this.dom;
+ changed = true;
+ }
- if (dom) {
- if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
- }
- var foreground = this.parent.getForeground();
- if (!foreground) {
- throw new Error('Cannot repaint time axis: ' +
- 'parent has no foreground container element');
- }
+ if (dom) {
+ if (!this.parent) {
+ throw new Error('Cannot repaint item: no parent attached');
+ }
+ var foreground = this.parent.getForeground();
+ if (!foreground) {
+ throw new Error('Cannot repaint time axis: ' +
+ 'parent has no foreground container element');
+ }
- if (!dom.box.parentNode) {
- foreground.appendChild(dom.box);
- changed = true;
- }
+ if (!dom.box.parentNode) {
+ foreground.appendChild(dom.box);
+ changed = true;
+ }
- // update content
- if (this.data.content != this.content) {
- this.content = this.data.content;
- if (this.content instanceof Element) {
- dom.content.innerHTML = '';
- dom.content.appendChild(this.content);
- }
- else if (this.data.content != undefined) {
- dom.content.innerHTML = this.content;
- }
- else {
- throw new Error('Property "content" missing in item ' + this.data.id);
- }
- changed = true;
- }
+ // update content
+ if (this.data.content != this.content) {
+ this.content = this.data.content;
+ if (this.content instanceof Element) {
+ dom.content.innerHTML = '';
+ dom.content.appendChild(this.content);
+ }
+ else if (this.data.content != undefined) {
+ dom.content.innerHTML = this.content;
+ }
+ else {
+ throw new Error('Property "content" missing in item ' + this.data.id);
+ }
+ changed = true;
+ }
- // update class
- var className = this.data.className ? (' ' + this.data.className) : '';
- if (this.className != className) {
- this.className = className;
- dom.box.className = 'item rangeoverflow' + className;
- changed = true;
- }
+ // update class
+ var className = this.data.className ? (' ' + this.data.className) : '';
+ if (this.className != className) {
+ this.className = className;
+ dom.box.className = 'item rangeoverflow' + className;
+ changed = true;
}
+ }
- return changed;
+ return changed;
};
/**
* Return the items width
- * @return {Integer} width
+ * @return {Number} width
*/
ItemRangeOverflow.prototype.getWidth = function getWidth() {
- if (this.props.content !== undefined && this.width < this.props.content.width)
- return this.props.content.width;
- else
- return this.width;
-}
+ if (this.props.content !== undefined && this.width < this.props.content.width)
+ return this.props.content.width;
+ else
+ return this.width;
+};
/**
* @constructor Group
@@ -10389,25 +9851,25 @@ ItemRangeOverflow.prototype.getWidth = function getWidth() {
* @extends Component
*/
function Group (parent, groupId, options) {
- this.id = util.randomUUID();
- this.parent = parent;
+ this.id = util.randomUUID();
+ this.parent = parent;
- this.groupId = groupId;
- this.itemset = null; // ItemSet
- this.options = options || {};
- this.options.top = 0;
+ this.groupId = groupId;
+ this.itemset = null; // ItemSet
+ this.options = options || {};
+ this.options.top = 0;
- this.props = {
- label: {
- width: 0,
- height: 0
- }
- };
+ this.props = {
+ label: {
+ width: 0,
+ height: 0
+ }
+ };
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.top = 0;
+ this.left = 0;
+ this.width = 0;
+ this.height = 0;
}
Group.prototype = new Component();
@@ -10421,7 +9883,7 @@ Group.prototype.setOptions = Component.prototype.setOptions;
* @returns {HTMLElement} container
*/
Group.prototype.getContainer = function () {
- return this.parent.getContainer();
+ return this.parent.getContainer();
};
/**
@@ -10430,31 +9892,31 @@ Group.prototype.getContainer = function () {
* @param {DataSet | DataView} items
*/
Group.prototype.setItems = function setItems(items) {
- if (this.itemset) {
- // remove current item set
- this.itemset.hide();
- this.itemset.setItems();
+ if (this.itemset) {
+ // remove current item set
+ this.itemset.hide();
+ this.itemset.setItems();
- this.parent.controller.remove(this.itemset);
- this.itemset = null;
- }
+ this.parent.controller.remove(this.itemset);
+ this.itemset = null;
+ }
- if (items) {
- var groupId = this.groupId;
+ if (items) {
+ var groupId = this.groupId;
- var itemsetOptions = Object.create(this.options);
- this.itemset = new ItemSet(this, null, itemsetOptions);
- this.itemset.setRange(this.parent.range);
+ var itemsetOptions = Object.create(this.options);
+ this.itemset = new ItemSet(this, null, itemsetOptions);
+ this.itemset.setRange(this.parent.range);
- this.view = new DataView(items, {
- filter: function (item) {
- return item.group == groupId;
- }
- });
- this.itemset.setItems(this.view);
+ this.view = new DataView(items, {
+ filter: function (item) {
+ return item.group == groupId;
+ }
+ });
+ this.itemset.setItems(this.view);
- this.parent.controller.add(this.itemset);
- }
+ this.parent.controller.add(this.itemset);
+ }
};
/**
@@ -10462,7 +9924,7 @@ Group.prototype.setItems = function setItems(items) {
* @return {Boolean} changed
*/
Group.prototype.repaint = function repaint() {
- return false;
+ return false;
};
/**
@@ -10470,25 +9932,25 @@ Group.prototype.repaint = function repaint() {
* @return {Boolean} resized
*/
Group.prototype.reflow = function reflow() {
- var changed = 0,
- update = util.updateProperty;
+ var changed = 0,
+ update = util.updateProperty;
- changed += update(this, 'top', this.itemset ? this.itemset.top : 0);
- changed += update(this, 'height', this.itemset ? this.itemset.height : 0);
+ changed += update(this, 'top', this.itemset ? this.itemset.top : 0);
+ changed += update(this, 'height', this.itemset ? this.itemset.height : 0);
- // TODO: reckon with the height of the group label
+ // TODO: reckon with the height of the group label
- if (this.label) {
- var inner = this.label.firstChild;
- changed += update(this.props.label, 'width', inner.clientWidth);
- changed += update(this.props.label, 'height', inner.clientHeight);
- }
- else {
- changed += update(this.props.label, 'width', 0);
- changed += update(this.props.label, 'height', 0);
- }
+ if (this.label) {
+ var inner = this.label.firstChild;
+ changed += update(this.props.label, 'width', inner.clientWidth);
+ changed += update(this.props.label, 'height', inner.clientHeight);
+ }
+ else {
+ changed += update(this.props.label, 'width', 0);
+ changed += update(this.props.label, 'height', 0);
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -10502,42 +9964,42 @@ Group.prototype.reflow = function reflow() {
* @extends Panel
*/
function GroupSet(parent, depends, options) {
- this.id = util.randomUUID();
- this.parent = parent;
- this.depends = depends;
+ this.id = util.randomUUID();
+ this.parent = parent;
+ this.depends = depends;
- this.options = options || {};
+ this.options = options || {};
- this.range = null; // Range or Object {start: number, end: number}
- this.itemsData = null; // DataSet with items
- this.groupsData = null; // DataSet with groups
+ this.range = null; // Range or Object {start: number, end: number}
+ this.itemsData = null; // DataSet with items
+ this.groupsData = null; // DataSet with groups
- this.groups = {}; // map with groups
+ this.groups = {}; // map with groups
- this.dom = {};
- this.props = {
- labels: {
- width: 0
- }
- };
+ this.dom = {};
+ this.props = {
+ labels: {
+ width: 0
+ }
+ };
- // TODO: implement right orientation of the labels
+ // TODO: implement right orientation of the labels
- // changes in groups are queued key/value map containing id/action
- this.queue = {};
+ // changes in groups are queued key/value map containing id/action
+ this.queue = {};
- var me = this;
- this.listeners = {
- 'add': function (event, params) {
- me._onAdd(params.items);
- },
- 'update': function (event, params) {
- me._onUpdate(params.items);
- },
- 'remove': function (event, params) {
- me._onRemove(params.items);
- }
- };
+ var me = this;
+ this.listeners = {
+ 'add': function (event, params) {
+ me._onAdd(params.items);
+ },
+ 'update': function (event, params) {
+ me._onUpdate(params.items);
+ },
+ 'remove': function (event, params) {
+ me._onRemove(params.items);
+ }
+ };
}
GroupSet.prototype = new Panel();
@@ -10551,7 +10013,7 @@ GroupSet.prototype = new Panel();
GroupSet.prototype.setOptions = Component.prototype.setOptions;
GroupSet.prototype.setRange = function (range) {
- // TODO: implement setRange
+ // TODO: implement setRange
};
/**
@@ -10559,14 +10021,14 @@ GroupSet.prototype.setRange = function (range) {
* @param {vis.DataSet | null} items
*/
GroupSet.prototype.setItems = function setItems(items) {
- this.itemsData = items;
+ this.itemsData = items;
- for (var id in this.groups) {
- if (this.groups.hasOwnProperty(id)) {
- var group = this.groups[id];
- group.setItems(items);
- }
+ for (var id in this.groups) {
+ if (this.groups.hasOwnProperty(id)) {
+ var group = this.groups[id];
+ group.setItems(items);
}
+ }
};
/**
@@ -10574,7 +10036,7 @@ GroupSet.prototype.setItems = function setItems(items) {
* @return {vis.DataSet | null} items
*/
GroupSet.prototype.getItems = function getItems() {
- return this.itemsData;
+ return this.itemsData;
};
/**
@@ -10582,7 +10044,7 @@ GroupSet.prototype.getItems = function getItems() {
* @param {Range | Object} range A Range or an object containing start and end.
*/
GroupSet.prototype.setRange = function setRange(range) {
- this.range = range;
+ this.range = range;
};
/**
@@ -10590,48 +10052,48 @@ GroupSet.prototype.setRange = function setRange(range) {
* @param {vis.DataSet} groups
*/
GroupSet.prototype.setGroups = function setGroups(groups) {
- var me = this,
- ids;
+ var me = this,
+ ids;
- // unsubscribe from current dataset
- if (this.groupsData) {
- util.forEach(this.listeners, function (callback, event) {
- me.groupsData.unsubscribe(event, callback);
- });
+ // unsubscribe from current dataset
+ if (this.groupsData) {
+ util.forEach(this.listeners, function (callback, event) {
+ me.groupsData.unsubscribe(event, callback);
+ });
- // remove all drawn groups
- ids = this.groupsData.getIds();
- this._onRemove(ids);
- }
+ // remove all drawn groups
+ ids = this.groupsData.getIds();
+ this._onRemove(ids);
+ }
- // replace the dataset
- if (!groups) {
- this.groupsData = null;
- }
- else if (groups instanceof DataSet) {
- this.groupsData = groups;
- }
- else {
- this.groupsData = new DataSet({
- convert: {
- start: 'Date',
- end: 'Date'
- }
- });
- this.groupsData.add(groups);
- }
+ // replace the dataset
+ if (!groups) {
+ this.groupsData = null;
+ }
+ else if (groups instanceof DataSet) {
+ this.groupsData = groups;
+ }
+ else {
+ this.groupsData = new DataSet({
+ convert: {
+ start: 'Date',
+ end: 'Date'
+ }
+ });
+ this.groupsData.add(groups);
+ }
- if (this.groupsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.listeners, function (callback, event) {
- me.groupsData.subscribe(event, callback, id);
- });
+ if (this.groupsData) {
+ // subscribe to new dataset
+ var id = this.id;
+ util.forEach(this.listeners, function (callback, event) {
+ me.groupsData.subscribe(event, callback, id);
+ });
- // draw all new groups
- ids = this.groupsData.getIds();
- this._onAdd(ids);
- }
+ // draw all new groups
+ ids = this.groupsData.getIds();
+ this._onAdd(ids);
+ }
};
/**
@@ -10639,7 +10101,7 @@ GroupSet.prototype.setGroups = function setGroups(groups) {
* @return {vis.DataSet | null} groups
*/
GroupSet.prototype.getGroups = function getGroups() {
- return this.groupsData;
+ return this.groupsData;
};
/**
@@ -10647,167 +10109,179 @@ GroupSet.prototype.getGroups = function getGroups() {
* @return {Boolean} changed
*/
GroupSet.prototype.repaint = function repaint() {
- var changed = 0,
- i, id, group, label,
- update = util.updateProperty,
- asSize = util.option.asSize,
- asElement = util.option.asElement,
- options = this.options,
- frame = this.dom.frame,
- labels = this.dom.labels;
-
- // create frame
- if (!this.parent) {
- throw new Error('Cannot repaint groupset: no parent attached');
- }
- var parentContainer = this.parent.getContainer();
- if (!parentContainer) {
- throw new Error('Cannot repaint groupset: parent has no container element');
+ var changed = 0,
+ i, id, group, label,
+ update = util.updateProperty,
+ asSize = util.option.asSize,
+ asElement = util.option.asElement,
+ options = this.options,
+ frame = this.dom.frame,
+ labels = this.dom.labels,
+ labelSet = this.dom.labelSet;
+
+ // create frame
+ if (!this.parent) {
+ throw new Error('Cannot repaint groupset: no parent attached');
+ }
+ var parentContainer = this.parent.getContainer();
+ if (!parentContainer) {
+ throw new Error('Cannot repaint groupset: parent has no container element');
+ }
+ if (!frame) {
+ frame = document.createElement('div');
+ frame.className = 'groupset';
+ this.dom.frame = frame;
+
+ var className = options.className;
+ if (className) {
+ util.addClassName(frame, util.option.asString(className));
}
- if (!frame) {
- frame = document.createElement('div');
- frame.className = 'groupset';
- this.dom.frame = frame;
- var className = options.className;
- if (className) {
- util.addClassName(frame, util.option.asString(className));
- }
+ changed += 1;
+ }
+ if (!frame.parentNode) {
+ parentContainer.appendChild(frame);
+ changed += 1;
+ }
- changed += 1;
- }
- if (!frame.parentNode) {
- parentContainer.appendChild(frame);
- changed += 1;
+ // create labels
+ var labelContainer = asElement(options.labelContainer);
+ if (!labelContainer) {
+ throw new Error('Cannot repaint groupset: option "labelContainer" not defined');
+ }
+ if (!labels) {
+ labels = document.createElement('div');
+ labels.className = 'labels';
+ this.dom.labels = labels;
+ }
+ if (!labelSet) {
+ labelSet = document.createElement('div');
+ labelSet.className = 'label-set';
+ labels.appendChild(labelSet);
+ this.dom.labelSet = labelSet;
+ }
+ if (!labels.parentNode || labels.parentNode != labelContainer) {
+ if (labels.parentNode) {
+ labels.parentNode.removeChild(labels.parentNode);
}
+ labelContainer.appendChild(labels);
+ }
- // create labels
- var labelContainer = asElement(options.labelContainer);
- if (!labelContainer) {
- throw new Error('Cannot repaint groupset: option "labelContainer" not defined');
- }
- if (!labels) {
- labels = document.createElement('div');
- labels.className = 'labels';
- //frame.appendChild(labels);
- this.dom.labels = labels;
- }
- if (!labels.parentNode || labels.parentNode != labelContainer) {
- if (labels.parentNode) {
- labels.parentNode.removeChild(labels.parentNode);
- }
- labelContainer.appendChild(labels);
- }
+ // reposition frame
+ changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
+ changed += update(frame.style, 'top', asSize(options.top, '0px'));
+ changed += update(frame.style, 'left', asSize(options.left, '0px'));
+ changed += update(frame.style, 'width', asSize(options.width, '100%'));
- // reposition frame
- changed += update(frame.style, 'height', asSize(options.height, this.height + 'px'));
- changed += update(frame.style, 'top', asSize(options.top, '0px'));
- changed += update(frame.style, 'left', asSize(options.left, '0px'));
- changed += update(frame.style, 'width', asSize(options.width, '100%'));
-
- // reposition labels
- changed += update(labels.style, 'top', asSize(options.top, '0px'));
-
- var me = this,
- queue = this.queue,
- groups = this.groups,
- groupsData = this.groupsData;
-
- // show/hide added/changed/removed groups
- var ids = Object.keys(queue);
- if (ids.length) {
- ids.forEach(function (id) {
- var action = queue[id];
- var group = groups[id];
-
- //noinspection FallthroughInSwitchStatementJS
- switch (action) {
- case 'add':
- case 'update':
- if (!group) {
- var groupOptions = Object.create(me.options);
- group = new Group(me, id, groupOptions);
- group.setItems(me.itemsData); // attach items data
- groups[id] = group;
-
- me.controller.add(group);
- }
+ // reposition labels
+ changed += update(labelSet.style, 'top', asSize(options.top, '0px'));
+ changed += update(labelSet.style, 'height', asSize(options.height, this.height + 'px'));
- // TODO: update group data
- group.data = groupsData.get(id);
+ var me = this,
+ queue = this.queue,
+ groups = this.groups,
+ groupsData = this.groupsData;
- delete queue[id];
- break;
+ // show/hide added/changed/removed groups
+ var ids = Object.keys(queue);
+ if (ids.length) {
+ ids.forEach(function (id) {
+ var action = queue[id];
+ var group = groups[id];
+
+ //noinspection FallthroughInSwitchStatementJS
+ switch (action) {
+ case 'add':
+ case 'update':
+ if (!group) {
+ var groupOptions = Object.create(me.options);
+ util.extend(groupOptions, {
+ height: null,
+ maxHeight: null
+ });
- case 'remove':
- if (group) {
- group.setItems(); // detach items data
- delete groups[id];
+ group = new Group(me, id, groupOptions);
+ group.setItems(me.itemsData); // attach items data
+ groups[id] = group;
- me.controller.remove(group);
- }
+ me.controller.add(group);
+ }
- // update lists
- delete queue[id];
- break;
+ // TODO: update group data
+ group.data = groupsData.get(id);
- default:
- console.log('Error: unknown action "' + action + '"');
- }
- });
+ delete queue[id];
+ break;
- // the groupset depends on each of the groups
- //this.depends = this.groups; // TODO: gives a circular reference through the parent
+ case 'remove':
+ if (group) {
+ group.setItems(); // detach items data
+ delete groups[id];
- // TODO: apply dependencies of the groupset
+ me.controller.remove(group);
+ }
- // update the top positions of the groups in the correct order
- var orderedGroups = this.groupsData.getIds({
- order: this.options.groupsOrder
- });
- for (i = 0; i < orderedGroups.length; i++) {
- (function (group, prevGroup) {
- var top = 0;
- if (prevGroup) {
- top = function () {
- // TODO: top must reckon with options.maxHeight
- return prevGroup.top + prevGroup.height;
- }
- }
- group.setOptions({
- top: top
- });
- })(groups[orderedGroups[i]], groups[orderedGroups[i - 1]]);
- }
+ // update lists
+ delete queue[id];
+ break;
- // (re)create the labels
- while (labels.firstChild) {
- labels.removeChild(labels.firstChild);
- }
- for (i = 0; i < orderedGroups.length; i++) {
- id = orderedGroups[i];
- label = this._createLabel(id);
- labels.appendChild(label);
- }
+ default:
+ console.log('Error: unknown action "' + action + '"');
+ }
+ });
+
+ // the groupset depends on each of the groups
+ //this.depends = this.groups; // TODO: gives a circular reference through the parent
+
+ // TODO: apply dependencies of the groupset
+
+ // update the top positions of the groups in the correct order
+ var orderedGroups = this.groupsData.getIds({
+ order: this.options.groupOrder
+ });
+ for (i = 0; i < orderedGroups.length; i++) {
+ (function (group, prevGroup) {
+ var top = 0;
+ if (prevGroup) {
+ top = function () {
+ // TODO: top must reckon with options.maxHeight
+ return prevGroup.top + prevGroup.height;
+ }
+ }
+ group.setOptions({
+ top: top
+ });
+ })(groups[orderedGroups[i]], groups[orderedGroups[i - 1]]);
+ }
- changed++;
+ // (re)create the labels
+ while (labelSet.firstChild) {
+ labelSet.removeChild(labelSet.firstChild);
+ }
+ for (i = 0; i < orderedGroups.length; i++) {
+ id = orderedGroups[i];
+ label = this._createLabel(id);
+ labelSet.appendChild(label);
}
- // reposition the labels
- // TODO: labels are not displayed correctly when orientation=='top'
- // TODO: width of labelPanel is not immediately updated on a change in groups
- for (id in groups) {
- if (groups.hasOwnProperty(id)) {
- group = groups[id];
- label = group.label;
- if (label) {
- label.style.top = group.top + 'px';
- label.style.height = group.height + 'px';
- }
- }
+ changed++;
+ }
+
+ // reposition the labels
+ // TODO: labels are not displayed correctly when orientation=='top'
+ // TODO: width of labelPanel is not immediately updated on a change in groups
+ for (id in groups) {
+ if (groups.hasOwnProperty(id)) {
+ group = groups[id];
+ label = group.label;
+ if (label) {
+ label.style.top = group.top + 'px';
+ label.style.height = group.height + 'px';
+ }
}
+ }
- return (changed > 0);
+ return (changed > 0);
};
/**
@@ -10817,29 +10291,29 @@ GroupSet.prototype.repaint = function repaint() {
* @private
*/
GroupSet.prototype._createLabel = function(id) {
- var group = this.groups[id];
- var label = document.createElement('div');
- label.className = 'label';
- var inner = document.createElement('div');
- inner.className = 'inner';
- label.appendChild(inner);
+ var group = this.groups[id];
+ var label = document.createElement('div');
+ label.className = 'label';
+ var inner = document.createElement('div');
+ inner.className = 'inner';
+ label.appendChild(inner);
+
+ var content = group.data && group.data.content;
+ if (content instanceof Element) {
+ inner.appendChild(content);
+ }
+ else if (content != undefined) {
+ inner.innerHTML = content;
+ }
- var content = group.data && group.data.content;
- if (content instanceof Element) {
- inner.appendChild(content);
- }
- else if (content != undefined) {
- inner.innerHTML = content;
- }
+ var className = group.data && group.data.className;
+ if (className) {
+ util.addClassName(label, className);
+ }
- var className = group.data && group.data.className;
- if (className) {
- util.addClassName(label, className);
- }
+ group.label = label; // TODO: not so nice, parking labels in the group this way!!!
- group.label = label; // TODO: not so nice, parking labels in the group this way!!!
-
- return label;
+ return label;
};
/**
@@ -10847,7 +10321,7 @@ GroupSet.prototype._createLabel = function(id) {
* @return {HTMLElement} container
*/
GroupSet.prototype.getContainer = function getContainer() {
- return this.dom.frame;
+ return this.dom.frame;
};
/**
@@ -10855,7 +10329,7 @@ GroupSet.prototype.getContainer = function getContainer() {
* @return {Number} width
*/
GroupSet.prototype.getLabelsWidth = function getContainer() {
- return this.props.labels.width;
+ return this.props.labels.width;
};
/**
@@ -10863,54 +10337,54 @@ GroupSet.prototype.getLabelsWidth = function getContainer() {
* @return {Boolean} resized
*/
GroupSet.prototype.reflow = function reflow() {
- var changed = 0,
- id, group,
- options = this.options,
- update = util.updateProperty,
- asNumber = util.option.asNumber,
- asSize = util.option.asSize,
- frame = this.dom.frame;
-
- if (frame) {
- var maxHeight = asNumber(options.maxHeight);
- var fixedHeight = (asSize(options.height) != null);
- var height;
- if (fixedHeight) {
- height = frame.offsetHeight;
- }
- else {
- // height is not specified, calculate the sum of the height of all groups
- height = 0;
-
- for (id in this.groups) {
- if (this.groups.hasOwnProperty(id)) {
- group = this.groups[id];
- height += group.height;
- }
- }
- }
- if (maxHeight != null) {
- height = Math.min(height, maxHeight);
- }
- changed += update(this, 'height', height);
-
- changed += update(this, 'top', frame.offsetTop);
- changed += update(this, 'left', frame.offsetLeft);
- changed += update(this, 'width', frame.offsetWidth);
+ var changed = 0,
+ id, group,
+ options = this.options,
+ update = util.updateProperty,
+ asNumber = util.option.asNumber,
+ asSize = util.option.asSize,
+ frame = this.dom.frame;
+
+ if (frame) {
+ var maxHeight = asNumber(options.maxHeight);
+ var fixedHeight = (asSize(options.height) != null);
+ var height;
+ if (fixedHeight) {
+ height = frame.offsetHeight;
}
+ else {
+ // height is not specified, calculate the sum of the height of all groups
+ height = 0;
- // calculate the maximum width of the labels
- var width = 0;
- for (id in this.groups) {
+ for (id in this.groups) {
if (this.groups.hasOwnProperty(id)) {
- group = this.groups[id];
- var labelWidth = group.props && group.props.label && group.props.label.width || 0;
- width = Math.max(width, labelWidth);
+ group = this.groups[id];
+ height += group.height;
}
+ }
}
- changed += update(this.props.labels, 'width', width);
+ if (maxHeight != null) {
+ height = Math.min(height, maxHeight);
+ }
+ changed += update(this, 'height', height);
+
+ changed += update(this, 'top', frame.offsetTop);
+ changed += update(this, 'left', frame.offsetLeft);
+ changed += update(this, 'width', frame.offsetWidth);
+ }
- return (changed > 0);
+ // calculate the maximum width of the labels
+ var width = 0;
+ for (id in this.groups) {
+ if (this.groups.hasOwnProperty(id)) {
+ group = this.groups[id];
+ var labelWidth = group.props && group.props.label && group.props.label.width || 0;
+ width = Math.max(width, labelWidth);
+ }
+ }
+ changed += update(this.props.labels, 'width', width);
+
+ return (changed > 0);
};
/**
@@ -10918,13 +10392,13 @@ GroupSet.prototype.reflow = function reflow() {
* @return {Boolean} changed
*/
GroupSet.prototype.hide = function hide() {
- if (this.dom.frame && this.dom.frame.parentNode) {
- this.dom.frame.parentNode.removeChild(this.dom.frame);
- return true;
- }
- else {
- return false;
- }
+ if (this.dom.frame && this.dom.frame.parentNode) {
+ this.dom.frame.parentNode.removeChild(this.dom.frame);
+ return true;
+ }
+ else {
+ return false;
+ }
};
/**
@@ -10933,12 +10407,12 @@ GroupSet.prototype.hide = function hide() {
* @return {Boolean} changed
*/
GroupSet.prototype.show = function show() {
- if (!this.dom.frame || !this.dom.frame.parentNode) {
- return this.repaint();
- }
- else {
- return false;
- }
+ if (!this.dom.frame || !this.dom.frame.parentNode) {
+ return this.repaint();
+ }
+ else {
+ return false;
+ }
};
/**
@@ -10947,7 +10421,7 @@ GroupSet.prototype.show = function show() {
* @private
*/
GroupSet.prototype._onUpdate = function _onUpdate(ids) {
- this._toQueue(ids, 'update');
+ this._toQueue(ids, 'update');
};
/**
@@ -10956,7 +10430,7 @@ GroupSet.prototype._onUpdate = function _onUpdate(ids) {
* @private
*/
GroupSet.prototype._onAdd = function _onAdd(ids) {
- this._toQueue(ids, 'add');
+ this._toQueue(ids, 'add');
};
/**
@@ -10965,7 +10439,7 @@ GroupSet.prototype._onAdd = function _onAdd(ids) {
* @private
*/
GroupSet.prototype._onRemove = function _onRemove(ids) {
- this._toQueue(ids, 'remove');
+ this._toQueue(ids, 'remove');
};
/**
@@ -10974,15 +10448,15 @@ GroupSet.prototype._onRemove = function _onRemove(ids) {
* @param {String} action can be 'add', 'update', 'remove'
*/
GroupSet.prototype._toQueue = function _toQueue(ids, action) {
- var queue = this.queue;
- ids.forEach(function (id) {
- queue[id] = action;
- });
-
- if (this.controller) {
- //this.requestReflow();
- this.requestRepaint();
- }
+ var queue = this.queue;
+ ids.forEach(function (id) {
+ queue[id] = action;
+ });
+
+ if (this.controller) {
+ //this.requestReflow();
+ this.requestRepaint();
+ }
};
/**
@@ -10993,129 +10467,130 @@ GroupSet.prototype._toQueue = function _toQueue(ids, action) {
* @constructor
*/
function Timeline (container, items, options) {
- var me = this;
- var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
- this.options = {
- orientation: 'bottom',
- min: null,
- max: null,
- zoomMin: 10, // milliseconds
- zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
- // moveable: true, // TODO: option moveable
- // zoomable: true, // TODO: option zoomable
- showMinorLabels: true,
- showMajorLabels: true,
- showCurrentTime: false,
- showCustomTime: false,
- autoResize: false
- };
-
- // controller
- this.controller = new Controller();
-
- // root panel
- if (!container) {
- throw new Error('No container element provided');
+ var me = this;
+ var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
+ this.options = {
+ orientation: 'bottom',
+ min: null,
+ max: null,
+ zoomMin: 10, // milliseconds
+ zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
+ // moveable: true, // TODO: option moveable
+ // zoomable: true, // TODO: option zoomable
+ showMinorLabels: true,
+ showMajorLabels: true,
+ showCurrentTime: false,
+ showCustomTime: false,
+ autoResize: false
+ };
+
+ // controller
+ this.controller = new Controller();
+
+ // root panel
+ if (!container) {
+ throw new Error('No container element provided');
+ }
+ var rootOptions = Object.create(this.options);
+ rootOptions.height = function () {
+ // TODO: change to height
+ if (me.options.height) {
+ // fixed height
+ return me.options.height;
}
- var rootOptions = Object.create(this.options);
- rootOptions.height = function () {
- if (me.options.height) {
- // fixed height
- return me.options.height;
- }
- else {
- // auto height
- return me.timeaxis.height + me.content.height;
- }
- };
- this.rootPanel = new RootPanel(container, rootOptions);
- this.controller.add(this.rootPanel);
-
- // item panel
- var itemOptions = Object.create(this.options);
- itemOptions.left = function () {
- return me.labelPanel.width;
- };
- itemOptions.width = function () {
- return me.rootPanel.width - me.labelPanel.width;
- };
- itemOptions.top = null;
- itemOptions.height = null;
- this.itemPanel = new Panel(this.rootPanel, [], itemOptions);
- this.controller.add(this.itemPanel);
-
- // label panel
- var labelOptions = Object.create(this.options);
- labelOptions.top = null;
- labelOptions.left = null;
- labelOptions.height = null;
- labelOptions.width = function () {
- if (me.content && typeof me.content.getLabelsWidth === 'function') {
- return me.content.getLabelsWidth();
- }
- else {
- return 0;
- }
- };
- this.labelPanel = new Panel(this.rootPanel, [], labelOptions);
- this.controller.add(this.labelPanel);
-
- // range
- var rangeOptions = Object.create(this.options);
- this.range = new Range(rangeOptions);
- this.range.setRange(
- now.clone().add('days', -3).valueOf(),
- now.clone().add('days', 4).valueOf()
- );
-
- // TODO: reckon with options moveable and zoomable
- this.range.subscribe(this.rootPanel, 'move', 'horizontal');
- this.range.subscribe(this.rootPanel, 'zoom', 'horizontal');
- this.range.on('rangechange', function () {
- var force = true;
- me.controller.requestReflow(force);
- });
- this.range.on('rangechanged', function () {
- var force = true;
- me.controller.requestReflow(force);
- });
-
- // TODO: put the listeners in setOptions, be able to dynamically change with options moveable and zoomable
-
- // time axis
- var timeaxisOptions = Object.create(rootOptions);
- timeaxisOptions.range = this.range;
- timeaxisOptions.left = null;
- timeaxisOptions.top = null;
- timeaxisOptions.width = '100%';
- timeaxisOptions.height = null;
- this.timeaxis = new TimeAxis(this.itemPanel, [], timeaxisOptions);
- this.timeaxis.setRange(this.range);
- this.controller.add(this.timeaxis);
-
- // current time bar
- this.currenttime = new CurrentTime(this.timeaxis, [], rootOptions);
- this.controller.add(this.currenttime);
-
- // custom time bar
- this.customtime = new CustomTime(this.timeaxis, [], rootOptions);
- this.controller.add(this.customtime);
-
- // create itemset or groupset
- this.setGroups(null);
-
- this.itemsData = null; // DataSet
- this.groupsData = null; // DataSet
-
- // apply options
- if (options) {
- this.setOptions(options);
+ else {
+ // auto height
+ return (me.timeaxis.height + me.content.height) + 'px';
+ }
+ };
+ this.rootPanel = new RootPanel(container, rootOptions);
+ this.controller.add(this.rootPanel);
+
+ // item panel
+ var itemOptions = Object.create(this.options);
+ itemOptions.left = function () {
+ return me.labelPanel.width;
+ };
+ itemOptions.width = function () {
+ return me.rootPanel.width - me.labelPanel.width;
+ };
+ itemOptions.top = null;
+ itemOptions.height = null;
+ this.itemPanel = new Panel(this.rootPanel, [], itemOptions);
+ this.controller.add(this.itemPanel);
+
+ // label panel
+ var labelOptions = Object.create(this.options);
+ labelOptions.top = null;
+ labelOptions.left = null;
+ labelOptions.height = null;
+ labelOptions.width = function () {
+ if (me.content && typeof me.content.getLabelsWidth === 'function') {
+ return me.content.getLabelsWidth();
}
+ else {
+ return 0;
+ }
+ };
+ this.labelPanel = new Panel(this.rootPanel, [], labelOptions);
+ this.controller.add(this.labelPanel);
+
+ // range
+ var rangeOptions = Object.create(this.options);
+ this.range = new Range(rangeOptions);
+ this.range.setRange(
+ now.clone().add('days', -3).valueOf(),
+ now.clone().add('days', 4).valueOf()
+ );
+
+ // TODO: reckon with options moveable and zoomable
+ this.range.subscribe(this.rootPanel, 'move', 'horizontal');
+ this.range.subscribe(this.rootPanel, 'zoom', 'horizontal');
+ this.range.on('rangechange', function () {
+ var force = true;
+ me.controller.requestReflow(force);
+ });
+ this.range.on('rangechanged', function () {
+ var force = true;
+ me.controller.requestReflow(force);
+ });
+
+ // TODO: put the listeners in setOptions, be able to dynamically change with options moveable and zoomable
+
+ // time axis
+ var timeaxisOptions = Object.create(rootOptions);
+ timeaxisOptions.range = this.range;
+ timeaxisOptions.left = null;
+ timeaxisOptions.top = null;
+ timeaxisOptions.width = '100%';
+ timeaxisOptions.height = null;
+ this.timeaxis = new TimeAxis(this.itemPanel, [], timeaxisOptions);
+ this.timeaxis.setRange(this.range);
+ this.controller.add(this.timeaxis);
+
+ // current time bar
+ this.currenttime = new CurrentTime(this.timeaxis, [], rootOptions);
+ this.controller.add(this.currenttime);
+
+ // custom time bar
+ this.customtime = new CustomTime(this.timeaxis, [], rootOptions);
+ this.controller.add(this.customtime);
+
+ // create groupset
+ this.setGroups(null);
+
+ this.itemsData = null; // DataSet
+ this.groupsData = null; // DataSet
+
+ // apply options
+ if (options) {
+ this.setOptions(options);
+ }
- // set data (must be after options are applied)
- if (items) {
- this.setItems(items);
- }
+ // create itemset and groupset
+ if (items) {
+ this.setItems(items);
+ }
}
/**
@@ -11123,15 +10598,15 @@ function Timeline (container, items, options) {
* @param {Object} options TODO: describe the available options
*/
Timeline.prototype.setOptions = function (options) {
- util.extend(this.options, options);
+ util.extend(this.options, options);
- // force update of range
- // options.start and options.end can be undefined
- //this.range.setRange(options.start, options.end);
- this.range.setRange();
+ // force update of range
+ // options.start and options.end can be undefined
+ //this.range.setRange(options.start, options.end);
+ this.range.setRange();
- this.controller.reflow();
- this.controller.repaint();
+ this.controller.reflow();
+ this.controller.repaint();
};
/**
@@ -11139,7 +10614,7 @@ Timeline.prototype.setOptions = function (options) {
* @param {Date} time
*/
Timeline.prototype.setCustomTime = function (time) {
- this.customtime._setCustomTime(time);
+ this.customtime._setCustomTime(time);
};
/**
@@ -11147,7 +10622,7 @@ Timeline.prototype.setCustomTime = function (time) {
* @return {Date} customTime
*/
Timeline.prototype.getCustomTime = function() {
- return new Date(this.customtime.customTime.valueOf());
+ return new Date(this.customtime.customTime.valueOf());
};
/**
@@ -11155,60 +10630,60 @@ Timeline.prototype.getCustomTime = function() {
* @param {vis.DataSet | Array | DataTable | null} items
*/
Timeline.prototype.setItems = function(items) {
- var initialLoad = (this.itemsData == null);
+ var initialLoad = (this.itemsData == null);
- // convert to type DataSet when needed
- var newItemSet;
- if (!items) {
- newItemSet = null;
+ // convert to type DataSet when needed
+ var newItemSet;
+ if (!items) {
+ newItemSet = null;
+ }
+ else if (items instanceof DataSet) {
+ newItemSet = items;
+ }
+ if (!(items instanceof DataSet)) {
+ newItemSet = new DataSet({
+ convert: {
+ start: 'Date',
+ end: 'Date'
+ }
+ });
+ newItemSet.add(items);
+ }
+
+ // set items
+ this.itemsData = newItemSet;
+ this.content.setItems(newItemSet);
+
+ if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
+ // apply the data range as range
+ var dataRange = this.getItemRange();
+
+ // add 5% space on both sides
+ var min = dataRange.min;
+ var max = dataRange.max;
+ if (min != null && max != null) {
+ var interval = (max.valueOf() - min.valueOf());
+ if (interval <= 0) {
+ // prevent an empty interval
+ interval = 24 * 60 * 60 * 1000; // 1 day
+ }
+ min = new Date(min.valueOf() - interval * 0.05);
+ max = new Date(max.valueOf() + interval * 0.05);
}
- else if (items instanceof DataSet) {
- newItemSet = items;
+
+ // override specified start and/or end date
+ if (this.options.start != undefined) {
+ min = util.convert(this.options.start, 'Date');
}
- if (!(items instanceof DataSet)) {
- newItemSet = new DataSet({
- convert: {
- start: 'Date',
- end: 'Date'
- }
- });
- newItemSet.add(items);
+ if (this.options.end != undefined) {
+ max = util.convert(this.options.end, 'Date');
}
- // set items
- this.itemsData = newItemSet;
- this.content.setItems(newItemSet);
-
- if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
- // apply the data range as range
- var dataRange = this.getItemRange();
-
- // add 5% space on both sides
- var min = dataRange.min;
- var max = dataRange.max;
- if (min != null && max != null) {
- var interval = (max.valueOf() - min.valueOf());
- if (interval <= 0) {
- // prevent an empty interval
- interval = 24 * 60 * 60 * 1000; // 1 day
- }
- min = new Date(min.valueOf() - interval * 0.05);
- max = new Date(max.valueOf() + interval * 0.05);
- }
-
- // override specified start and/or end date
- if (this.options.start != undefined) {
- min = util.convert(this.options.start, 'Date');
- }
- if (this.options.end != undefined) {
- max = util.convert(this.options.end, 'Date');
- }
-
- // apply range if there is a min or max available
- if (min != null || max != null) {
- this.range.setRange(min, max);
- }
+ // apply range if there is a min or max available
+ if (min != null || max != null) {
+ this.range.setRange(min, max);
}
+ }
};
/**
@@ -11216,72 +10691,76 @@ Timeline.prototype.setItems = function(items) {
* @param {vis.DataSet | Array | DataTable} groups
*/
Timeline.prototype.setGroups = function(groups) {
- var me = this;
- this.groupsData = groups;
+ var me = this;
+ this.groupsData = groups;
+
+ // switch content type between ItemSet or GroupSet when needed
+ var Type = this.groupsData ? GroupSet : ItemSet;
+ if (!(this.content instanceof Type)) {
+ // remove old content set
+ if (this.content) {
+ this.content.hide();
+ if (this.content.setItems) {
+ this.content.setItems(); // disconnect from items
+ }
+ if (this.content.setGroups) {
+ this.content.setGroups(); // disconnect from groups
+ }
+ this.controller.remove(this.content);
+ }
- // switch content type between ItemSet or GroupSet when needed
- var type = this.groupsData ? GroupSet : ItemSet;
- if (!(this.content instanceof type)) {
- // remove old content set
- if (this.content) {
- this.content.hide();
- if (this.content.setItems) {
- this.content.setItems(); // disconnect from items
- }
- if (this.content.setGroups) {
- this.content.setGroups(); // disconnect from groups
- }
- this.controller.remove(this.content);
+ // create new content set
+ var options = Object.create(this.options);
+ util.extend(options, {
+ top: function () {
+ if (me.options.orientation == 'top') {
+ return me.timeaxis.height;
}
-
- // create new content set
- var options = Object.create(this.options);
- util.extend(options, {
- top: function () {
- if (me.options.orientation == 'top') {
- return me.timeaxis.height;
- }
- else {
- return me.itemPanel.height - me.timeaxis.height - me.content.height;
- }
- },
- left: null,
- width: '100%',
- height: function () {
- if (me.options.height) {
- return me.itemPanel.height - me.timeaxis.height;
- }
- else {
- return null;
- }
- },
- maxHeight: function () {
- if (me.options.maxHeight) {
- if (!util.isNumber(me.options.maxHeight)) {
- throw new TypeError('Number expected for property maxHeight');
- }
- return me.options.maxHeight - me.timeaxis.height;
- }
- else {
- return null;
- }
- },
- labelContainer: function () {
- return me.labelPanel.getContainer();
- }
- });
- this.content = new type(this.itemPanel, [this.timeaxis], options);
- if (this.content.setRange) {
- this.content.setRange(this.range);
+ else {
+ return me.itemPanel.height - me.timeaxis.height - me.content.height;
}
- if (this.content.setItems) {
- this.content.setItems(this.itemsData);
+ },
+ left: null,
+ width: '100%',
+ height: function () {
+ if (me.options.height) {
+ // fixed height
+ return me.itemPanel.height - me.timeaxis.height;
}
- if (this.content.setGroups) {
- this.content.setGroups(this.groupsData);
+ else {
+ // auto height
+ return null;
+ }
+ },
+ maxHeight: function () {
+ // TODO: change maxHeight to be a css string like '100%' or '300px'
+ if (me.options.maxHeight) {
+ if (!util.isNumber(me.options.maxHeight)) {
+ throw new TypeError('Number expected for property maxHeight');
+ }
+ return me.options.maxHeight - me.timeaxis.height;
}
- this.controller.add(this.content);
+ else {
+ return null;
+ }
+ },
+ labelContainer: function () {
+ return me.labelPanel.getContainer();
+ }
+ });
+
+ this.content = new Type(this.itemPanel, [this.timeaxis], options);
+ if (this.content.setRange) {
+ this.content.setRange(this.range);
+ }
+ if (this.content.setItems) {
+ this.content.setItems(this.itemsData);
}
+ if (this.content.setGroups) {
+ this.content.setGroups(this.groupsData);
+ }
+ this.controller.add(this.content);
+ }
};
/**
@@ -11291,1092 +10770,1092 @@ Timeline.prototype.setGroups = function(groups) {
* When no maximum is found, max==null
*/
Timeline.prototype.getItemRange = function getItemRange() {
- // calculate min from start filed
- var itemsData = this.itemsData,
- min = null,
- max = null;
-
- if (itemsData) {
- // calculate the minimum value of the field 'start'
- var minItem = itemsData.min('start');
- min = minItem ? minItem.start.valueOf() : null;
-
- // calculate maximum value of fields 'start' and 'end'
- var maxStartItem = itemsData.max('start');
- if (maxStartItem) {
- max = maxStartItem.start.valueOf();
- }
- var maxEndItem = itemsData.max('end');
- if (maxEndItem) {
- if (max == null) {
- max = maxEndItem.end.valueOf();
- }
- else {
- max = Math.max(max, maxEndItem.end.valueOf());
- }
- }
+ // calculate min from start filed
+ var itemsData = this.itemsData,
+ min = null,
+ max = null;
+
+ if (itemsData) {
+ // calculate the minimum value of the field 'start'
+ var minItem = itemsData.min('start');
+ min = minItem ? minItem.start.valueOf() : null;
+
+ // calculate maximum value of fields 'start' and 'end'
+ var maxStartItem = itemsData.max('start');
+ if (maxStartItem) {
+ max = maxStartItem.start.valueOf();
+ }
+ var maxEndItem = itemsData.max('end');
+ if (maxEndItem) {
+ if (max == null) {
+ max = maxEndItem.end.valueOf();
+ }
+ else {
+ max = Math.max(max, maxEndItem.end.valueOf());
+ }
}
+ }
- return {
- min: (min != null) ? new Date(min) : null,
- max: (max != null) ? new Date(max) : null
- };
+ return {
+ min: (min != null) ? new Date(min) : null,
+ max: (max != null) ? new Date(max) : null
+ };
};
(function(exports) {
- /**
- * Parse a text source containing data in DOT language into a JSON object.
- * The object contains two lists: one with nodes and one with edges.
- *
- * DOT language reference: http://www.graphviz.org/doc/info/lang.html
- *
- * @param {String} data Text containing a graph in DOT-notation
- * @return {Object} graph An object containing two parameters:
- * {Object[]} nodes
- * {Object[]} edges
- */
- function parseDOT (data) {
- dot = data;
- return parseGraph();
- }
+ /**
+ * Parse a text source containing data in DOT language into a JSON object.
+ * The object contains two lists: one with nodes and one with edges.
+ *
+ * DOT language reference: http://www.graphviz.org/doc/info/lang.html
+ *
+ * @param {String} data Text containing a graph in DOT-notation
+ * @return {Object} graph An object containing two parameters:
+ * {Object[]} nodes
+ * {Object[]} edges
+ */
+ function parseDOT (data) {
+ dot = data;
+ return parseGraph();
+ }
- // token types enumeration
- var TOKENTYPE = {
- NULL : 0,
- DELIMITER : 1,
- IDENTIFIER: 2,
- UNKNOWN : 3
- };
+ // token types enumeration
+ var TOKENTYPE = {
+ NULL : 0,
+ DELIMITER : 1,
+ IDENTIFIER: 2,
+ UNKNOWN : 3
+ };
+
+ // map with all delimiters
+ var DELIMITERS = {
+ '{': true,
+ '}': true,
+ '[': true,
+ ']': true,
+ ';': true,
+ '=': true,
+ ',': true,
+
+ '->': true,
+ '--': true
+ };
+
+ var dot = ''; // current dot file
+ var index = 0; // current index in dot file
+ var c = ''; // current token character in expr
+ var token = ''; // current token
+ var tokenType = TOKENTYPE.NULL; // type of the token
+
+ /**
+ * Get the first character from the dot file.
+ * The character is stored into the char c. If the end of the dot file is
+ * reached, the function puts an empty string in c.
+ */
+ function first() {
+ index = 0;
+ c = dot.charAt(0);
+ }
- // map with all delimiters
- var DELIMITERS = {
- '{': true,
- '}': true,
- '[': true,
- ']': true,
- ';': true,
- '=': true,
- ',': true,
-
- '->': true,
- '--': true
- };
+ /**
+ * Get the next character from the dot file.
+ * The character is stored into the char c. If the end of the dot file is
+ * reached, the function puts an empty string in c.
+ */
+ function next() {
+ index++;
+ c = dot.charAt(index);
+ }
- var dot = ''; // current dot file
- var index = 0; // current index in dot file
- var c = ''; // current token character in expr
- var token = ''; // current token
- var tokenType = TOKENTYPE.NULL; // type of the token
+ /**
+ * Preview the next character from the dot file.
+ * @return {String} cNext
+ */
+ function nextPreview() {
+ return dot.charAt(index + 1);
+ }
- /**
- * Get the first character from the dot file.
- * The character is stored into the char c. If the end of the dot file is
- * reached, the function puts an empty string in c.
- */
- function first() {
- index = 0;
- c = dot.charAt(0);
- }
+ /**
+ * Test whether given character is alphabetic or numeric
+ * @param {String} c
+ * @return {Boolean} isAlphaNumeric
+ */
+ var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
+ function isAlphaNumeric(c) {
+ return regexAlphaNumeric.test(c);
+ }
- /**
- * Get the next character from the dot file.
- * The character is stored into the char c. If the end of the dot file is
- * reached, the function puts an empty string in c.
- */
- function next() {
- index++;
- c = dot.charAt(index);
+ /**
+ * Merge all properties of object b into object b
+ * @param {Object} a
+ * @param {Object} b
+ * @return {Object} a
+ */
+ function merge (a, b) {
+ if (!a) {
+ a = {};
}
- /**
- * Preview the next character from the dot file.
- * @return {String} cNext
- */
- function nextPreview() {
- return dot.charAt(index + 1);
+ if (b) {
+ for (var name in b) {
+ if (b.hasOwnProperty(name)) {
+ a[name] = b[name];
+ }
+ }
}
+ return a;
+ }
- /**
- * Test whether given character is alphabetic or numeric
- * @param {String} c
- * @return {Boolean} isAlphaNumeric
- */
- var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
- function isAlphaNumeric(c) {
- return regexAlphaNumeric.test(c);
+ /**
+ * Set a value in an object, where the provided parameter name can be a
+ * path with nested parameters. For example:
+ *
+ * var obj = {a: 2};
+ * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}}
+ *
+ * @param {Object} obj
+ * @param {String} path A parameter name or dot-separated parameter path,
+ * like "color.highlight.border".
+ * @param {*} value
+ */
+ function setValue(obj, path, value) {
+ var keys = path.split('.');
+ var o = obj;
+ while (keys.length) {
+ var key = keys.shift();
+ if (keys.length) {
+ // this isn't the end point
+ if (!o[key]) {
+ o[key] = {};
+ }
+ o = o[key];
+ }
+ else {
+ // this is the end point
+ o[key] = value;
+ }
}
+ }
- /**
- * Merge all properties of object b into object b
- * @param {Object} a
- * @param {Object} b
- * @return {Object} a
- */
- function merge (a, b) {
- if (!a) {
- a = {};
- }
-
- if (b) {
- for (var name in b) {
- if (b.hasOwnProperty(name)) {
- a[name] = b[name];
- }
- }
+ /**
+ * Add a node to a graph object. If there is already a node with
+ * the same id, their attributes will be merged.
+ * @param {Object} graph
+ * @param {Object} node
+ */
+ function addNode(graph, node) {
+ var i, len;
+ var current = null;
+
+ // find root graph (in case of subgraph)
+ var graphs = [graph]; // list with all graphs from current graph to root graph
+ var root = graph;
+ while (root.parent) {
+ graphs.push(root.parent);
+ root = root.parent;
+ }
+
+ // find existing node (at root level) by its id
+ if (root.nodes) {
+ for (i = 0, len = root.nodes.length; i < len; i++) {
+ if (node.id === root.nodes[i].id) {
+ current = root.nodes[i];
+ break;
}
- return a;
+ }
}
- /**
- * Set a value in an object, where the provided parameter name can be a
- * path with nested parameters. For example:
- *
- * var obj = {a: 2};
- * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}}
- *
- * @param {Object} obj
- * @param {String} path A parameter name or dot-separated parameter path,
- * like "color.highlight.border".
- * @param {*} value
- */
- function setValue(obj, path, value) {
- var keys = path.split('.');
- var o = obj;
- while (keys.length) {
- var key = keys.shift();
- if (keys.length) {
- // this isn't the end point
- if (!o[key]) {
- o[key] = {};
- }
- o = o[key];
- }
- else {
- // this is the end point
- o[key] = value;
- }
- }
+ if (!current) {
+ // this is a new node
+ current = {
+ id: node.id
+ };
+ if (graph.node) {
+ // clone default attributes
+ current.attr = merge(current.attr, graph.node);
+ }
}
- /**
- * Add a node to a graph object. If there is already a node with
- * the same id, their attributes will be merged.
- * @param {Object} graph
- * @param {Object} node
- */
- function addNode(graph, node) {
- var i, len;
- var current = null;
-
- // find root graph (in case of subgraph)
- var graphs = [graph]; // list with all graphs from current graph to root graph
- var root = graph;
- while (root.parent) {
- graphs.push(root.parent);
- root = root.parent;
- }
-
- // find existing node (at root level) by its id
- if (root.nodes) {
- for (i = 0, len = root.nodes.length; i < len; i++) {
- if (node.id === root.nodes[i].id) {
- current = root.nodes[i];
- break;
- }
- }
- }
-
- if (!current) {
- // this is a new node
- current = {
- id: node.id
- };
- if (graph.node) {
- // clone default attributes
- current.attr = merge(current.attr, graph.node);
- }
- }
+ // add node to this (sub)graph and all its parent graphs
+ for (i = graphs.length - 1; i >= 0; i--) {
+ var g = graphs[i];
- // add node to this (sub)graph and all its parent graphs
- for (i = graphs.length - 1; i >= 0; i--) {
- var g = graphs[i];
-
- if (!g.nodes) {
- g.nodes = [];
- }
- if (g.nodes.indexOf(current) == -1) {
- g.nodes.push(current);
- }
- }
-
- // merge attributes
- if (node.attr) {
- current.attr = merge(current.attr, node.attr);
- }
+ if (!g.nodes) {
+ g.nodes = [];
+ }
+ if (g.nodes.indexOf(current) == -1) {
+ g.nodes.push(current);
+ }
}
- /**
- * Add an edge to a graph object
- * @param {Object} graph
- * @param {Object} edge
- */
- function addEdge(graph, edge) {
- if (!graph.edges) {
- graph.edges = [];
- }
- graph.edges.push(edge);
- if (graph.edge) {
- var attr = merge({}, graph.edge); // clone default attributes
- edge.attr = merge(attr, edge.attr); // merge attributes
- }
+ // merge attributes
+ if (node.attr) {
+ current.attr = merge(current.attr, node.attr);
}
+ }
- /**
- * Create an edge to a graph object
- * @param {Object} graph
- * @param {String | Number | Object} from
- * @param {String | Number | Object} to
- * @param {String} type
- * @param {Object | null} attr
- * @return {Object} edge
- */
- function createEdge(graph, from, to, type, attr) {
- var edge = {
- from: from,
- to: to,
- type: type
- };
+ /**
+ * Add an edge to a graph object
+ * @param {Object} graph
+ * @param {Object} edge
+ */
+ function addEdge(graph, edge) {
+ if (!graph.edges) {
+ graph.edges = [];
+ }
+ graph.edges.push(edge);
+ if (graph.edge) {
+ var attr = merge({}, graph.edge); // clone default attributes
+ edge.attr = merge(attr, edge.attr); // merge attributes
+ }
+ }
- if (graph.edge) {
- edge.attr = merge({}, graph.edge); // clone default attributes
- }
- edge.attr = merge(edge.attr || {}, attr); // merge attributes
+ /**
+ * Create an edge to a graph object
+ * @param {Object} graph
+ * @param {String | Number | Object} from
+ * @param {String | Number | Object} to
+ * @param {String} type
+ * @param {Object | null} attr
+ * @return {Object} edge
+ */
+ function createEdge(graph, from, to, type, attr) {
+ var edge = {
+ from: from,
+ to: to,
+ type: type
+ };
- return edge;
+ if (graph.edge) {
+ edge.attr = merge({}, graph.edge); // clone default attributes
}
+ edge.attr = merge(edge.attr || {}, attr); // merge attributes
- /**
- * Get next token in the current dot file.
- * The token and token type are available as token and tokenType
- */
- function getToken() {
- tokenType = TOKENTYPE.NULL;
- token = '';
-
- // skip over whitespaces
- while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
- next();
- }
+ return edge;
+ }
- do {
- var isComment = false;
+ /**
+ * Get next token in the current dot file.
+ * The token and token type are available as token and tokenType
+ */
+ function getToken() {
+ tokenType = TOKENTYPE.NULL;
+ token = '';
- // skip comment
- if (c == '#') {
- // find the previous non-space character
- var i = index - 1;
- while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') {
- i--;
- }
- if (dot.charAt(i) == '\n' || dot.charAt(i) == '') {
- // the # is at the start of a line, this is indeed a line comment
- while (c != '' && c != '\n') {
- next();
- }
- isComment = true;
- }
- }
- if (c == '/' && nextPreview() == '/') {
- // skip line comment
- while (c != '' && c != '\n') {
- next();
- }
- isComment = true;
- }
- if (c == '/' && nextPreview() == '*') {
- // skip block comment
- while (c != '') {
- if (c == '*' && nextPreview() == '/') {
- // end of block comment found. skip these last two characters
- next();
- next();
- break;
- }
- else {
- next();
- }
- }
- isComment = true;
- }
+ // skip over whitespaces
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
+ next();
+ }
- // skip over whitespaces
- while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
- next();
- }
- }
- while (isComment);
+ do {
+ var isComment = false;
- // check for end of dot file
- if (c == '') {
- // token is still empty
- tokenType = TOKENTYPE.DELIMITER;
- return;
+ // skip comment
+ if (c == '#') {
+ // find the previous non-space character
+ var i = index - 1;
+ while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') {
+ i--;
}
-
- // check for delimiters consisting of 2 characters
- var c2 = c + nextPreview();
- if (DELIMITERS[c2]) {
- tokenType = TOKENTYPE.DELIMITER;
- token = c2;
- next();
+ if (dot.charAt(i) == '\n' || dot.charAt(i) == '') {
+ // the # is at the start of a line, this is indeed a line comment
+ while (c != '' && c != '\n') {
next();
- return;
+ }
+ isComment = true;
}
-
- // check for delimiters consisting of 1 character
- if (DELIMITERS[c]) {
- tokenType = TOKENTYPE.DELIMITER;
- token = c;
- next();
- return;
+ }
+ if (c == '/' && nextPreview() == '/') {
+ // skip line comment
+ while (c != '' && c != '\n') {
+ next();
}
-
- // check for an identifier (number or string)
- // TODO: more precise parsing of numbers/strings (and the port separator ':')
- if (isAlphaNumeric(c) || c == '-') {
- token += c;
+ isComment = true;
+ }
+ if (c == '/' && nextPreview() == '*') {
+ // skip block comment
+ while (c != '') {
+ if (c == '*' && nextPreview() == '/') {
+ // end of block comment found. skip these last two characters
next();
-
- while (isAlphaNumeric(c)) {
- token += c;
- next();
- }
- if (token == 'false') {
- token = false; // convert to boolean
- }
- else if (token == 'true') {
- token = true; // convert to boolean
- }
- else if (!isNaN(Number(token))) {
- token = Number(token); // convert to number
- }
- tokenType = TOKENTYPE.IDENTIFIER;
- return;
- }
-
- // check for a string enclosed by double quotes
- if (c == '"') {
next();
- while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) {
- token += c;
- if (c == '"') { // skip the escape character
- next();
- }
- next();
- }
- if (c != '"') {
- throw newSyntaxError('End of string " expected');
- }
+ break;
+ }
+ else {
next();
- tokenType = TOKENTYPE.IDENTIFIER;
- return;
+ }
}
+ isComment = true;
+ }
- // something unknown is found, wrong characters, a syntax error
- tokenType = TOKENTYPE.UNKNOWN;
- while (c != '') {
- token += c;
- next();
- }
- throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
+ // skip over whitespaces
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter
+ next();
+ }
}
+ while (isComment);
- /**
- * Parse a graph.
- * @returns {Object} graph
- */
- function parseGraph() {
- var graph = {};
-
- first();
- getToken();
-
- // optional strict keyword
- if (token == 'strict') {
- graph.strict = true;
- getToken();
- }
-
- // graph or digraph keyword
- if (token == 'graph' || token == 'digraph') {
- graph.type = token;
- getToken();
- }
-
- // optional graph id
- if (tokenType == TOKENTYPE.IDENTIFIER) {
- graph.id = token;
- getToken();
- }
-
- // open angle bracket
- if (token != '{') {
- throw newSyntaxError('Angle bracket { expected');
- }
- getToken();
-
- // statements
- parseStatements(graph);
+ // check for end of dot file
+ if (c == '') {
+ // token is still empty
+ tokenType = TOKENTYPE.DELIMITER;
+ return;
+ }
- // close angle bracket
- if (token != '}') {
- throw newSyntaxError('Angle bracket } expected');
- }
- getToken();
+ // check for delimiters consisting of 2 characters
+ var c2 = c + nextPreview();
+ if (DELIMITERS[c2]) {
+ tokenType = TOKENTYPE.DELIMITER;
+ token = c2;
+ next();
+ next();
+ return;
+ }
- // end of file
- if (token !== '') {
- throw newSyntaxError('End of file expected');
- }
- getToken();
+ // check for delimiters consisting of 1 character
+ if (DELIMITERS[c]) {
+ tokenType = TOKENTYPE.DELIMITER;
+ token = c;
+ next();
+ return;
+ }
- // remove temporary default properties
- delete graph.node;
- delete graph.edge;
- delete graph.graph;
+ // check for an identifier (number or string)
+ // TODO: more precise parsing of numbers/strings (and the port separator ':')
+ if (isAlphaNumeric(c) || c == '-') {
+ token += c;
+ next();
- return graph;
+ while (isAlphaNumeric(c)) {
+ token += c;
+ next();
+ }
+ if (token == 'false') {
+ token = false; // convert to boolean
+ }
+ else if (token == 'true') {
+ token = true; // convert to boolean
+ }
+ else if (!isNaN(Number(token))) {
+ token = Number(token); // convert to number
+ }
+ tokenType = TOKENTYPE.IDENTIFIER;
+ return;
}
- /**
- * Parse a list with statements.
- * @param {Object} graph
- */
- function parseStatements (graph) {
- while (token !== '' && token != '}') {
- parseStatement(graph);
- if (token == ';') {
- getToken();
- }
+ // check for a string enclosed by double quotes
+ if (c == '"') {
+ next();
+ while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) {
+ token += c;
+ if (c == '"') { // skip the escape character
+ next();
}
+ next();
+ }
+ if (c != '"') {
+ throw newSyntaxError('End of string " expected');
+ }
+ next();
+ tokenType = TOKENTYPE.IDENTIFIER;
+ return;
}
- /**
- * Parse a single statement. Can be a an attribute statement, node
- * statement, a series of node statements and edge statements, or a
- * parameter.
- * @param {Object} graph
- */
- function parseStatement(graph) {
- // parse subgraph
- var subgraph = parseSubgraph(graph);
- if (subgraph) {
- // edge statements
- parseEdge(graph, subgraph);
+ // something unknown is found, wrong characters, a syntax error
+ tokenType = TOKENTYPE.UNKNOWN;
+ while (c != '') {
+ token += c;
+ next();
+ }
+ throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
+ }
- return;
- }
+ /**
+ * Parse a graph.
+ * @returns {Object} graph
+ */
+ function parseGraph() {
+ var graph = {};
- // parse an attribute statement
- var attr = parseAttributeStatement(graph);
- if (attr) {
- return;
- }
+ first();
+ getToken();
- // parse node
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier expected');
- }
- var id = token; // id can be a string or a number
- getToken();
+ // optional strict keyword
+ if (token == 'strict') {
+ graph.strict = true;
+ getToken();
+ }
- if (token == '=') {
- // id statement
- getToken();
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier expected');
- }
- graph[id] = token;
- getToken();
- // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
- }
- else {
- parseNodeStatement(graph, id);
- }
+ // graph or digraph keyword
+ if (token == 'graph' || token == 'digraph') {
+ graph.type = token;
+ getToken();
}
- /**
- * Parse a subgraph
- * @param {Object} graph parent graph object
- * @return {Object | null} subgraph
- */
- function parseSubgraph (graph) {
- var subgraph = null;
-
- // optional subgraph keyword
- if (token == 'subgraph') {
- subgraph = {};
- subgraph.type = 'subgraph';
- getToken();
-
- // optional graph id
- if (tokenType == TOKENTYPE.IDENTIFIER) {
- subgraph.id = token;
- getToken();
- }
- }
+ // optional graph id
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
+ graph.id = token;
+ getToken();
+ }
- // open angle bracket
- if (token == '{') {
- getToken();
+ // open angle bracket
+ if (token != '{') {
+ throw newSyntaxError('Angle bracket { expected');
+ }
+ getToken();
- if (!subgraph) {
- subgraph = {};
- }
- subgraph.parent = graph;
- subgraph.node = graph.node;
- subgraph.edge = graph.edge;
- subgraph.graph = graph.graph;
+ // statements
+ parseStatements(graph);
- // statements
- parseStatements(subgraph);
+ // close angle bracket
+ if (token != '}') {
+ throw newSyntaxError('Angle bracket } expected');
+ }
+ getToken();
- // close angle bracket
- if (token != '}') {
- throw newSyntaxError('Angle bracket } expected');
- }
- getToken();
+ // end of file
+ if (token !== '') {
+ throw newSyntaxError('End of file expected');
+ }
+ getToken();
- // remove temporary default properties
- delete subgraph.node;
- delete subgraph.edge;
- delete subgraph.graph;
- delete subgraph.parent;
+ // remove temporary default properties
+ delete graph.node;
+ delete graph.edge;
+ delete graph.graph;
- // register at the parent graph
- if (!graph.subgraphs) {
- graph.subgraphs = [];
- }
- graph.subgraphs.push(subgraph);
- }
+ return graph;
+ }
- return subgraph;
+ /**
+ * Parse a list with statements.
+ * @param {Object} graph
+ */
+ function parseStatements (graph) {
+ while (token !== '' && token != '}') {
+ parseStatement(graph);
+ if (token == ';') {
+ getToken();
+ }
}
+ }
- /**
- * parse an attribute statement like "node [shape=circle fontSize=16]".
- * Available keywords are 'node', 'edge', 'graph'.
- * The previous list with default attributes will be replaced
- * @param {Object} graph
- * @returns {String | null} keyword Returns the name of the parsed attribute
- * (node, edge, graph), or null if nothing
- * is parsed.
- */
- function parseAttributeStatement (graph) {
- // attribute statements
- if (token == 'node') {
- getToken();
-
- // node attributes
- graph.node = parseAttributeList();
- return 'node';
- }
- else if (token == 'edge') {
- getToken();
+ /**
+ * Parse a single statement. Can be a an attribute statement, node
+ * statement, a series of node statements and edge statements, or a
+ * parameter.
+ * @param {Object} graph
+ */
+ function parseStatement(graph) {
+ // parse subgraph
+ var subgraph = parseSubgraph(graph);
+ if (subgraph) {
+ // edge statements
+ parseEdge(graph, subgraph);
- // edge attributes
- graph.edge = parseAttributeList();
- return 'edge';
- }
- else if (token == 'graph') {
- getToken();
-
- // graph attributes
- graph.graph = parseAttributeList();
- return 'graph';
- }
+ return;
+ }
- return null;
+ // parse an attribute statement
+ var attr = parseAttributeStatement(graph);
+ if (attr) {
+ return;
}
- /**
- * parse a node statement
- * @param {Object} graph
- * @param {String | Number} id
- */
- function parseNodeStatement(graph, id) {
- // node statement
- var node = {
- id: id
- };
- var attr = parseAttributeList();
- if (attr) {
- node.attr = attr;
- }
- addNode(graph, node);
+ // parse node
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Identifier expected');
+ }
+ var id = token; // id can be a string or a number
+ getToken();
- // edge statements
- parseEdge(graph, id);
+ if (token == '=') {
+ // id statement
+ getToken();
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Identifier expected');
+ }
+ graph[id] = token;
+ getToken();
+ // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
}
+ else {
+ parseNodeStatement(graph, id);
+ }
+ }
- /**
- * Parse an edge or a series of edges
- * @param {Object} graph
- * @param {String | Number} from Id of the from node
- */
- function parseEdge(graph, from) {
- while (token == '->' || token == '--') {
- var to;
- var type = token;
- getToken();
-
- var subgraph = parseSubgraph(graph);
- if (subgraph) {
- to = subgraph;
- }
- else {
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Identifier or subgraph expected');
- }
- to = token;
- addNode(graph, {
- id: to
- });
- getToken();
- }
+ /**
+ * Parse a subgraph
+ * @param {Object} graph parent graph object
+ * @return {Object | null} subgraph
+ */
+ function parseSubgraph (graph) {
+ var subgraph = null;
+
+ // optional subgraph keyword
+ if (token == 'subgraph') {
+ subgraph = {};
+ subgraph.type = 'subgraph';
+ getToken();
+
+ // optional graph id
+ if (tokenType == TOKENTYPE.IDENTIFIER) {
+ subgraph.id = token;
+ getToken();
+ }
+ }
- // parse edge attributes
- var attr = parseAttributeList();
+ // open angle bracket
+ if (token == '{') {
+ getToken();
- // create edge
- var edge = createEdge(graph, from, to, type, attr);
- addEdge(graph, edge);
+ if (!subgraph) {
+ subgraph = {};
+ }
+ subgraph.parent = graph;
+ subgraph.node = graph.node;
+ subgraph.edge = graph.edge;
+ subgraph.graph = graph.graph;
- from = to;
- }
- }
+ // statements
+ parseStatements(subgraph);
- /**
- * Parse a set with attributes,
- * for example [label="1.000", shape=solid]
- * @return {Object | null} attr
- */
- function parseAttributeList() {
- var attr = null;
-
- while (token == '[') {
- getToken();
- attr = {};
- while (token !== '' && token != ']') {
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Attribute name expected');
- }
- var name = token;
+ // close angle bracket
+ if (token != '}') {
+ throw newSyntaxError('Angle bracket } expected');
+ }
+ getToken();
- getToken();
- if (token != '=') {
- throw newSyntaxError('Equal sign = expected');
- }
- getToken();
+ // remove temporary default properties
+ delete subgraph.node;
+ delete subgraph.edge;
+ delete subgraph.graph;
+ delete subgraph.parent;
- if (tokenType != TOKENTYPE.IDENTIFIER) {
- throw newSyntaxError('Attribute value expected');
- }
- var value = token;
- setValue(attr, name, value); // name can be a path
+ // register at the parent graph
+ if (!graph.subgraphs) {
+ graph.subgraphs = [];
+ }
+ graph.subgraphs.push(subgraph);
+ }
- getToken();
- if (token ==',') {
- getToken();
- }
- }
+ return subgraph;
+ }
- if (token != ']') {
- throw newSyntaxError('Bracket ] expected');
- }
- getToken();
- }
+ /**
+ * parse an attribute statement like "node [shape=circle fontSize=16]".
+ * Available keywords are 'node', 'edge', 'graph'.
+ * The previous list with default attributes will be replaced
+ * @param {Object} graph
+ * @returns {String | null} keyword Returns the name of the parsed attribute
+ * (node, edge, graph), or null if nothing
+ * is parsed.
+ */
+ function parseAttributeStatement (graph) {
+ // attribute statements
+ if (token == 'node') {
+ getToken();
- return attr;
+ // node attributes
+ graph.node = parseAttributeList();
+ return 'node';
}
+ else if (token == 'edge') {
+ getToken();
- /**
- * Create a syntax error with extra information on current token and index.
- * @param {String} message
- * @returns {SyntaxError} err
- */
- function newSyntaxError(message) {
- return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
+ // edge attributes
+ graph.edge = parseAttributeList();
+ return 'edge';
}
+ else if (token == 'graph') {
+ getToken();
- /**
- * Chop off text after a maximum length
- * @param {String} text
- * @param {Number} maxLength
- * @returns {String}
- */
- function chop (text, maxLength) {
- return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...');
+ // graph attributes
+ graph.graph = parseAttributeList();
+ return 'graph';
}
- /**
- * Execute a function fn for each pair of elements in two arrays
- * @param {Array | *} array1
- * @param {Array | *} array2
- * @param {function} fn
- */
- function forEach2(array1, array2, fn) {
- if (array1 instanceof Array) {
- array1.forEach(function (elem1) {
- if (array2 instanceof Array) {
- array2.forEach(function (elem2) {
- fn(elem1, elem2);
- });
- }
- else {
- fn(elem1, array2);
- }
- });
- }
- else {
- if (array2 instanceof Array) {
- array2.forEach(function (elem2) {
- fn(array1, elem2);
- });
- }
- else {
- fn(array1, array2);
- }
- }
+ return null;
+ }
+
+ /**
+ * parse a node statement
+ * @param {Object} graph
+ * @param {String | Number} id
+ */
+ function parseNodeStatement(graph, id) {
+ // node statement
+ var node = {
+ id: id
+ };
+ var attr = parseAttributeList();
+ if (attr) {
+ node.attr = attr;
}
+ addNode(graph, node);
- /**
- * Convert a string containing a graph in DOT language into a map containing
- * with nodes and edges in the format of graph.
- * @param {String} data Text containing a graph in DOT-notation
- * @return {Object} graphData
- */
- function DOTToGraph (data) {
- // parse the DOT file
- var dotData = parseDOT(data);
- var graphData = {
- nodes: [],
- edges: [],
- options: {}
- };
+ // edge statements
+ parseEdge(graph, id);
+ }
- // copy the nodes
- if (dotData.nodes) {
- dotData.nodes.forEach(function (dotNode) {
- var graphNode = {
- id: dotNode.id,
- label: String(dotNode.label || dotNode.id)
- };
- merge(graphNode, dotNode.attr);
- if (graphNode.image) {
- graphNode.shape = 'image';
- }
- graphData.nodes.push(graphNode);
- });
+ /**
+ * Parse an edge or a series of edges
+ * @param {Object} graph
+ * @param {String | Number} from Id of the from node
+ */
+ function parseEdge(graph, from) {
+ while (token == '->' || token == '--') {
+ var to;
+ var type = token;
+ getToken();
+
+ var subgraph = parseSubgraph(graph);
+ if (subgraph) {
+ to = subgraph;
+ }
+ else {
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Identifier or subgraph expected');
}
+ to = token;
+ addNode(graph, {
+ id: to
+ });
+ getToken();
+ }
- // copy the edges
- if (dotData.edges) {
- /**
- * Convert an edge in DOT format to an edge with VisGraph format
- * @param {Object} dotEdge
- * @returns {Object} graphEdge
- */
- function convertEdge(dotEdge) {
- var graphEdge = {
- from: dotEdge.from,
- to: dotEdge.to
- };
- merge(graphEdge, dotEdge.attr);
- graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line';
- return graphEdge;
- }
-
- dotData.edges.forEach(function (dotEdge) {
- var from, to;
- if (dotEdge.from instanceof Object) {
- from = dotEdge.from.nodes;
- }
- else {
- from = {
- id: dotEdge.from
- }
- }
-
- if (dotEdge.to instanceof Object) {
- to = dotEdge.to.nodes;
- }
- else {
- to = {
- id: dotEdge.to
- }
- }
-
- if (dotEdge.from instanceof Object && dotEdge.from.edges) {
- dotEdge.from.edges.forEach(function (subEdge) {
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
- }
-
- forEach2(from, to, function (from, to) {
- var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
-
- if (dotEdge.to instanceof Object && dotEdge.to.edges) {
- dotEdge.to.edges.forEach(function (subEdge) {
- var graphEdge = convertEdge(subEdge);
- graphData.edges.push(graphEdge);
- });
- }
- });
- }
+ // parse edge attributes
+ var attr = parseAttributeList();
- // copy the options
- if (dotData.attr) {
- graphData.options = dotData.attr;
- }
+ // create edge
+ var edge = createEdge(graph, from, to, type, attr);
+ addEdge(graph, edge);
- return graphData;
+ from = to;
}
+ }
- // exports
- exports.parseDOT = parseDOT;
- exports.DOTToGraph = DOTToGraph;
-
-})(typeof util !== 'undefined' ? util : exports);
-
-/**
- * Canvas shapes used by the Graph
- */
-if (typeof CanvasRenderingContext2D !== 'undefined') {
-
- /**
- * Draw a circle shape
- */
- CanvasRenderingContext2D.prototype.circle = function(x, y, r) {
- this.beginPath();
- this.arc(x, y, r, 0, 2*Math.PI, false);
- };
-
- /**
- * Draw a square shape
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r size, width and height of the square
- */
- CanvasRenderingContext2D.prototype.square = function(x, y, r) {
- this.beginPath();
- this.rect(x - r, y - r, r * 2, r * 2);
- };
-
- /**
- * Draw a triangle shape
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius, half the length of the sides of the triangle
- */
- CanvasRenderingContext2D.prototype.triangle = function(x, y, r) {
- // http://en.wikipedia.org/wiki/Equilateral_triangle
- this.beginPath();
-
- var s = r * 2;
- var s2 = s / 2;
- var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
- var h = Math.sqrt(s * s - s2 * s2); // height
-
- this.moveTo(x, y - (h - ir));
- this.lineTo(x + s2, y + ir);
- this.lineTo(x - s2, y + ir);
- this.lineTo(x, y - (h - ir));
- this.closePath();
- };
+ /**
+ * Parse a set with attributes,
+ * for example [label="1.000", shape=solid]
+ * @return {Object | null} attr
+ */
+ function parseAttributeList() {
+ var attr = null;
+
+ while (token == '[') {
+ getToken();
+ attr = {};
+ while (token !== '' && token != ']') {
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Attribute name expected');
+ }
+ var name = token;
- /**
- * Draw a triangle shape in downward orientation
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius
- */
- CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) {
- // http://en.wikipedia.org/wiki/Equilateral_triangle
- this.beginPath();
-
- var s = r * 2;
- var s2 = s / 2;
- var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
- var h = Math.sqrt(s * s - s2 * s2); // height
-
- this.moveTo(x, y + (h - ir));
- this.lineTo(x + s2, y - ir);
- this.lineTo(x - s2, y - ir);
- this.lineTo(x, y + (h - ir));
- this.closePath();
- };
+ getToken();
+ if (token != '=') {
+ throw newSyntaxError('Equal sign = expected');
+ }
+ getToken();
- /**
- * Draw a star shape, a star with 5 points
- * @param {Number} x horizontal center
- * @param {Number} y vertical center
- * @param {Number} r radius, half the length of the sides of the triangle
- */
- CanvasRenderingContext2D.prototype.star = function(x, y, r) {
- // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
- this.beginPath();
+ if (tokenType != TOKENTYPE.IDENTIFIER) {
+ throw newSyntaxError('Attribute value expected');
+ }
+ var value = token;
+ setValue(attr, name, value); // name can be a path
- for (var n = 0; n < 10; n++) {
- var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5;
- this.lineTo(
- x + radius * Math.sin(n * 2 * Math.PI / 10),
- y - radius * Math.cos(n * 2 * Math.PI / 10)
- );
+ getToken();
+ if (token ==',') {
+ getToken();
}
+ }
- this.closePath();
- };
+ if (token != ']') {
+ throw newSyntaxError('Bracket ] expected');
+ }
+ getToken();
+ }
- /**
- * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
- */
- CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
- var r2d = Math.PI/180;
- if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x
- if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y
- this.beginPath();
- this.moveTo(x+r,y);
- this.lineTo(x+w-r,y);
- this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false);
- this.lineTo(x+w,y+h-r);
- this.arc(x+w-r,y+h-r,r,0,r2d*90,false);
- this.lineTo(x+r,y+h);
- this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false);
- this.lineTo(x,y+r);
- this.arc(x+r,y+r,r,r2d*180,r2d*270,false);
- };
+ return attr;
+ }
- /**
- * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
- */
- CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) {
- var kappa = .5522848,
- ox = (w / 2) * kappa, // control point offset horizontal
- oy = (h / 2) * kappa, // control point offset vertical
- xe = x + w, // x-end
- ye = y + h, // y-end
- xm = x + w / 2, // x-middle
- ym = y + h / 2; // y-middle
-
- this.beginPath();
- this.moveTo(x, ym);
- this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
- this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
- };
+ /**
+ * Create a syntax error with extra information on current token and index.
+ * @param {String} message
+ * @returns {SyntaxError} err
+ */
+ function newSyntaxError(message) {
+ return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
+ }
+ /**
+ * Chop off text after a maximum length
+ * @param {String} text
+ * @param {Number} maxLength
+ * @returns {String}
+ */
+ function chop (text, maxLength) {
+ return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...');
+ }
+ /**
+ * Execute a function fn for each pair of elements in two arrays
+ * @param {Array | *} array1
+ * @param {Array | *} array2
+ * @param {function} fn
+ */
+ function forEach2(array1, array2, fn) {
+ if (array1 instanceof Array) {
+ array1.forEach(function (elem1) {
+ if (array2 instanceof Array) {
+ array2.forEach(function (elem2) {
+ fn(elem1, elem2);
+ });
+ }
+ else {
+ fn(elem1, array2);
+ }
+ });
+ }
+ else {
+ if (array2 instanceof Array) {
+ array2.forEach(function (elem2) {
+ fn(array1, elem2);
+ });
+ }
+ else {
+ fn(array1, array2);
+ }
+ }
+ }
- /**
- * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
- */
- CanvasRenderingContext2D.prototype.database = function(x, y, w, h) {
- var f = 1/3;
- var wEllipse = w;
- var hEllipse = h * f;
+ /**
+ * Convert a string containing a graph in DOT language into a map containing
+ * with nodes and edges in the format of graph.
+ * @param {String} data Text containing a graph in DOT-notation
+ * @return {Object} graphData
+ */
+ function DOTToGraph (data) {
+ // parse the DOT file
+ var dotData = parseDOT(data);
+ var graphData = {
+ nodes: [],
+ edges: [],
+ options: {}
+ };
- var kappa = .5522848,
- ox = (wEllipse / 2) * kappa, // control point offset horizontal
- oy = (hEllipse / 2) * kappa, // control point offset vertical
- xe = x + wEllipse, // x-end
- ye = y + hEllipse, // y-end
- xm = x + wEllipse / 2, // x-middle
- ym = y + hEllipse / 2, // y-middle
- ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse
- yeb = y + h; // y-end, bottom ellipse
+ // copy the nodes
+ if (dotData.nodes) {
+ dotData.nodes.forEach(function (dotNode) {
+ var graphNode = {
+ id: dotNode.id,
+ label: String(dotNode.label || dotNode.id)
+ };
+ merge(graphNode, dotNode.attr);
+ if (graphNode.image) {
+ graphNode.shape = 'image';
+ }
+ graphData.nodes.push(graphNode);
+ });
+ }
+
+ // copy the edges
+ if (dotData.edges) {
+ /**
+ * Convert an edge in DOT format to an edge with VisGraph format
+ * @param {Object} dotEdge
+ * @returns {Object} graphEdge
+ */
+ function convertEdge(dotEdge) {
+ var graphEdge = {
+ from: dotEdge.from,
+ to: dotEdge.to
+ };
+ merge(graphEdge, dotEdge.attr);
+ graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line';
+ return graphEdge;
+ }
- this.beginPath();
- this.moveTo(xe, ym);
+ dotData.edges.forEach(function (dotEdge) {
+ var from, to;
+ if (dotEdge.from instanceof Object) {
+ from = dotEdge.from.nodes;
+ }
+ else {
+ from = {
+ id: dotEdge.from
+ }
+ }
- this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+ if (dotEdge.to instanceof Object) {
+ to = dotEdge.to.nodes;
+ }
+ else {
+ to = {
+ id: dotEdge.to
+ }
+ }
- this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+ if (dotEdge.from instanceof Object && dotEdge.from.edges) {
+ dotEdge.from.edges.forEach(function (subEdge) {
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
+ }
- this.lineTo(xe, ymb);
+ forEach2(from, to, function (from, to) {
+ var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
- this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
- this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
+ if (dotEdge.to instanceof Object && dotEdge.to.edges) {
+ dotEdge.to.edges.forEach(function (subEdge) {
+ var graphEdge = convertEdge(subEdge);
+ graphData.edges.push(graphEdge);
+ });
+ }
+ });
+ }
- this.lineTo(x, ym);
- };
+ // copy the options
+ if (dotData.attr) {
+ graphData.options = dotData.attr;
+ }
+ return graphData;
+ }
- /**
- * Draw an arrow point (no line)
- */
- CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) {
- // tail
- var xt = x - length * Math.cos(angle);
- var yt = y - length * Math.sin(angle);
-
- // inner tail
- // TODO: allow to customize different shapes
- var xi = x - length * 0.9 * Math.cos(angle);
- var yi = y - length * 0.9 * Math.sin(angle);
-
- // left
- var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
- var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);
-
- // right
- var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
- var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);
-
- this.beginPath();
- this.moveTo(x, y);
- this.lineTo(xl, yl);
- this.lineTo(xi, yi);
- this.lineTo(xr, yr);
- this.closePath();
- };
+ // exports
+ exports.parseDOT = parseDOT;
+ exports.DOTToGraph = DOTToGraph;
- /**
- * Sets up the dashedLine functionality for drawing
- * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
- * @author David Jordan
- * @date 2012-08-08
- */
- CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){
- if (!dashArray) dashArray=[10,5];
- if (dashLength==0) dashLength = 0.001; // Hack for Safari
- var dashCount = dashArray.length;
- this.moveTo(x, y);
- var dx = (x2-x), dy = (y2-y);
- var slope = dy/dx;
- var distRemaining = Math.sqrt( dx*dx + dy*dy );
- var dashIndex=0, draw=true;
- while (distRemaining>=0.1){
- var dashLength = dashArray[dashIndex++%dashCount];
- if (dashLength > distRemaining) dashLength = distRemaining;
- var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) );
- if (dx<0) xStep = -xStep;
- x += xStep;
- y += slope*xStep;
- this[draw ? 'lineTo' : 'moveTo'](x,y);
- distRemaining -= dashLength;
- draw = !draw;
- }
- };
+})(typeof util !== 'undefined' ? util : exports);
- // TODO: add diamond shape
+/**
+ * Canvas shapes used by the Graph
+ */
+if (typeof CanvasRenderingContext2D !== 'undefined') {
+
+ /**
+ * Draw a circle shape
+ */
+ CanvasRenderingContext2D.prototype.circle = function(x, y, r) {
+ this.beginPath();
+ this.arc(x, y, r, 0, 2*Math.PI, false);
+ };
+
+ /**
+ * Draw a square shape
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r size, width and height of the square
+ */
+ CanvasRenderingContext2D.prototype.square = function(x, y, r) {
+ this.beginPath();
+ this.rect(x - r, y - r, r * 2, r * 2);
+ };
+
+ /**
+ * Draw a triangle shape
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius, half the length of the sides of the triangle
+ */
+ CanvasRenderingContext2D.prototype.triangle = function(x, y, r) {
+ // http://en.wikipedia.org/wiki/Equilateral_triangle
+ this.beginPath();
+
+ var s = r * 2;
+ var s2 = s / 2;
+ var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
+ var h = Math.sqrt(s * s - s2 * s2); // height
+
+ this.moveTo(x, y - (h - ir));
+ this.lineTo(x + s2, y + ir);
+ this.lineTo(x - s2, y + ir);
+ this.lineTo(x, y - (h - ir));
+ this.closePath();
+ };
+
+ /**
+ * Draw a triangle shape in downward orientation
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius
+ */
+ CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) {
+ // http://en.wikipedia.org/wiki/Equilateral_triangle
+ this.beginPath();
+
+ var s = r * 2;
+ var s2 = s / 2;
+ var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
+ var h = Math.sqrt(s * s - s2 * s2); // height
+
+ this.moveTo(x, y + (h - ir));
+ this.lineTo(x + s2, y - ir);
+ this.lineTo(x - s2, y - ir);
+ this.lineTo(x, y + (h - ir));
+ this.closePath();
+ };
+
+ /**
+ * Draw a star shape, a star with 5 points
+ * @param {Number} x horizontal center
+ * @param {Number} y vertical center
+ * @param {Number} r radius, half the length of the sides of the triangle
+ */
+ CanvasRenderingContext2D.prototype.star = function(x, y, r) {
+ // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
+ this.beginPath();
+
+ for (var n = 0; n < 10; n++) {
+ var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5;
+ this.lineTo(
+ x + radius * Math.sin(n * 2 * Math.PI / 10),
+ y - radius * Math.cos(n * 2 * Math.PI / 10)
+ );
+ }
+
+ this.closePath();
+ };
+
+ /**
+ * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
+ */
+ CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
+ var r2d = Math.PI/180;
+ if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x
+ if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y
+ this.beginPath();
+ this.moveTo(x+r,y);
+ this.lineTo(x+w-r,y);
+ this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false);
+ this.lineTo(x+w,y+h-r);
+ this.arc(x+w-r,y+h-r,r,0,r2d*90,false);
+ this.lineTo(x+r,y+h);
+ this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false);
+ this.lineTo(x,y+r);
+ this.arc(x+r,y+r,r,r2d*180,r2d*270,false);
+ };
+
+ /**
+ * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ */
+ CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) {
+ var kappa = .5522848,
+ ox = (w / 2) * kappa, // control point offset horizontal
+ oy = (h / 2) * kappa, // control point offset vertical
+ xe = x + w, // x-end
+ ye = y + h, // y-end
+ xm = x + w / 2, // x-middle
+ ym = y + h / 2; // y-middle
+
+ this.beginPath();
+ this.moveTo(x, ym);
+ this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+ this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+ };
+
+
+
+ /**
+ * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ */
+ CanvasRenderingContext2D.prototype.database = function(x, y, w, h) {
+ var f = 1/3;
+ var wEllipse = w;
+ var hEllipse = h * f;
+
+ var kappa = .5522848,
+ ox = (wEllipse / 2) * kappa, // control point offset horizontal
+ oy = (hEllipse / 2) * kappa, // control point offset vertical
+ xe = x + wEllipse, // x-end
+ ye = y + hEllipse, // y-end
+ xm = x + wEllipse / 2, // x-middle
+ ym = y + hEllipse / 2, // y-middle
+ ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse
+ yeb = y + h; // y-end, bottom ellipse
+
+ this.beginPath();
+ this.moveTo(xe, ym);
+
+ this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+
+ this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+
+ this.lineTo(xe, ymb);
+
+ this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
+ this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
+
+ this.lineTo(x, ym);
+ };
+
+
+ /**
+ * Draw an arrow point (no line)
+ */
+ CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) {
+ // tail
+ var xt = x - length * Math.cos(angle);
+ var yt = y - length * Math.sin(angle);
+
+ // inner tail
+ // TODO: allow to customize different shapes
+ var xi = x - length * 0.9 * Math.cos(angle);
+ var yi = y - length * 0.9 * Math.sin(angle);
+
+ // left
+ var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
+ var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);
+
+ // right
+ var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
+ var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);
+
+ this.beginPath();
+ this.moveTo(x, y);
+ this.lineTo(xl, yl);
+ this.lineTo(xi, yi);
+ this.lineTo(xr, yr);
+ this.closePath();
+ };
+
+ /**
+ * Sets up the dashedLine functionality for drawing
+ * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
+ * @author David Jordan
+ * @date 2012-08-08
+ */
+ CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){
+ if (!dashArray) dashArray=[10,5];
+ if (dashLength==0) dashLength = 0.001; // Hack for Safari
+ var dashCount = dashArray.length;
+ this.moveTo(x, y);
+ var dx = (x2-x), dy = (y2-y);
+ var slope = dy/dx;
+ var distRemaining = Math.sqrt( dx*dx + dy*dy );
+ var dashIndex=0, draw=true;
+ while (distRemaining>=0.1){
+ var dashLength = dashArray[dashIndex++%dashCount];
+ if (dashLength > distRemaining) dashLength = distRemaining;
+ var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) );
+ if (dx<0) xStep = -xStep;
+ x += xStep;
+ y += slope*xStep;
+ this[draw ? 'lineTo' : 'moveTo'](x,y);
+ distRemaining -= dashLength;
+ draw = !draw;
+ }
+ };
+
+ // TODO: add diamond shape
}
/**
@@ -12404,43 +11883,43 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
* example for the color
*/
function Node(properties, imagelist, grouplist, constants) {
- this.selected = false;
+ this.selected = false;
- this.edges = []; // all edges connected to this node
- this.group = constants.nodes.group;
+ this.edges = []; // all edges connected to this node
+ this.group = constants.nodes.group;
- this.fontSize = constants.nodes.fontSize;
- this.fontFace = constants.nodes.fontFace;
- this.fontColor = constants.nodes.fontColor;
+ this.fontSize = constants.nodes.fontSize;
+ this.fontFace = constants.nodes.fontFace;
+ this.fontColor = constants.nodes.fontColor;
- this.color = constants.nodes.color;
+ this.color = constants.nodes.color;
- // set defaults for the properties
- this.id = undefined;
- this.shape = constants.nodes.shape;
- this.image = constants.nodes.image;
- this.x = 0;
- this.y = 0;
- this.xFixed = false;
- this.yFixed = false;
- this.radius = constants.nodes.radius;
- this.radiusFixed = false;
- this.radiusMin = constants.nodes.radiusMin;
- this.radiusMax = constants.nodes.radiusMax;
+ // set defaults for the properties
+ this.id = undefined;
+ this.shape = constants.nodes.shape;
+ this.image = constants.nodes.image;
+ this.x = 0;
+ this.y = 0;
+ this.xFixed = false;
+ this.yFixed = false;
+ this.radius = constants.nodes.radius;
+ this.radiusFixed = false;
+ this.radiusMin = constants.nodes.radiusMin;
+ this.radiusMax = constants.nodes.radiusMax;
- this.imagelist = imagelist;
- this.grouplist = grouplist;
+ this.imagelist = imagelist;
+ this.grouplist = grouplist;
- this.setProperties(properties, constants);
+ this.setProperties(properties, constants);
- // mass, force, velocity
- this.mass = 50; // kg (mass is adjusted for the number of connected edges)
- this.fx = 0.0; // external force x
- this.fy = 0.0; // external force y
- this.vx = 0.0; // velocity x
- this.vy = 0.0; // velocity y
- this.minForce = constants.minForce;
- this.damping = 0.9; // damping factor
+ // mass, force, velocity
+ this.mass = 50; // kg (mass is adjusted for the number of connected edges)
+ this.fx = 0.0; // external force x
+ this.fy = 0.0; // external force y
+ this.vx = 0.0; // velocity x
+ this.vy = 0.0; // velocity y
+ this.minForce = constants.minForce;
+ this.damping = 0.9; // damping factor
};
/**
@@ -12448,10 +11927,10 @@ function Node(properties, imagelist, grouplist, constants) {
* @param {Edge} edge
*/
Node.prototype.attachEdge = function(edge) {
- if (this.edges.indexOf(edge) == -1) {
- this.edges.push(edge);
- }
- this._updateMass();
+ if (this.edges.indexOf(edge) == -1) {
+ this.edges.push(edge);
+ }
+ this._updateMass();
};
/**
@@ -12459,11 +11938,11 @@ Node.prototype.attachEdge = function(edge) {
* @param {Edge} edge
*/
Node.prototype.detachEdge = function(edge) {
- var index = this.edges.indexOf(edge);
- if (index != -1) {
- this.edges.splice(index, 1);
- }
- this._updateMass();
+ var index = this.edges.indexOf(edge);
+ if (index != -1) {
+ this.edges.splice(index, 1);
+ }
+ this._updateMass();
};
/**
@@ -12472,7 +11951,7 @@ Node.prototype.detachEdge = function(edge) {
* @private
*/
Node.prototype._updateMass = function() {
- this.mass = 50 + 20 * this.edges.length; // kg
+ this.mass = 50 + 20 * this.edges.length; // kg
};
/**
@@ -12481,81 +11960,81 @@ Node.prototype._updateMass = function() {
* @param {Object} constants and object with default, global properties
*/
Node.prototype.setProperties = function(properties, constants) {
- if (!properties) {
- return;
- }
-
- // basic properties
- if (properties.id != undefined) {this.id = properties.id;}
- if (properties.label != undefined) {this.label = properties.label;}
- if (properties.title != undefined) {this.title = properties.title;}
- if (properties.group != undefined) {this.group = properties.group;}
- if (properties.x != undefined) {this.x = properties.x;}
- if (properties.y != undefined) {this.y = properties.y;}
- if (properties.value != undefined) {this.value = properties.value;}
+ if (!properties) {
+ return;
+ }
- if (this.id === undefined) {
- throw "Node must have an id";
- }
+ // basic properties
+ if (properties.id != undefined) {this.id = properties.id;}
+ if (properties.label != undefined) {this.label = properties.label;}
+ if (properties.title != undefined) {this.title = properties.title;}
+ if (properties.group != undefined) {this.group = properties.group;}
+ if (properties.x != undefined) {this.x = properties.x;}
+ if (properties.y != undefined) {this.y = properties.y;}
+ if (properties.value != undefined) {this.value = properties.value;}
+
+ if (this.id === undefined) {
+ throw "Node must have an id";
+ }
- // copy group properties
- if (this.group) {
- var groupObj = this.grouplist.get(this.group);
- for (var prop in groupObj) {
- if (groupObj.hasOwnProperty(prop)) {
- this[prop] = groupObj[prop];
- }
- }
+ // copy group properties
+ if (this.group) {
+ var groupObj = this.grouplist.get(this.group);
+ for (var prop in groupObj) {
+ if (groupObj.hasOwnProperty(prop)) {
+ this[prop] = groupObj[prop];
+ }
}
+ }
- // individual shape properties
- if (properties.shape != undefined) {this.shape = properties.shape;}
- if (properties.image != undefined) {this.image = properties.image;}
- if (properties.radius != undefined) {this.radius = properties.radius;}
- if (properties.color != undefined) {this.color = Node.parseColor(properties.color);}
+ // individual shape properties
+ if (properties.shape != undefined) {this.shape = properties.shape;}
+ if (properties.image != undefined) {this.image = properties.image;}
+ if (properties.radius != undefined) {this.radius = properties.radius;}
+ if (properties.color != undefined) {this.color = Node.parseColor(properties.color);}
- if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
- if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
- if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
+ if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
+ if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
+ if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
- if (this.image != undefined) {
- if (this.imagelist) {
- this.imageObj = this.imagelist.load(this.image);
- }
- else {
- throw "No imagelist provided";
- }
+ if (this.image != undefined) {
+ if (this.imagelist) {
+ this.imageObj = this.imagelist.load(this.image);
+ }
+ else {
+ throw "No imagelist provided";
}
+ }
- this.xFixed = this.xFixed || (properties.x != undefined);
- this.yFixed = this.yFixed || (properties.y != undefined);
- this.radiusFixed = this.radiusFixed || (properties.radius != undefined);
+ this.xFixed = this.xFixed || (properties.x != undefined);
+ this.yFixed = this.yFixed || (properties.y != undefined);
+ this.radiusFixed = this.radiusFixed || (properties.radius != undefined);
- if (this.shape == 'image') {
- this.radiusMin = constants.nodes.widthMin;
- this.radiusMax = constants.nodes.widthMax;
- }
+ if (this.shape == 'image') {
+ this.radiusMin = constants.nodes.widthMin;
+ this.radiusMax = constants.nodes.widthMax;
+ }
- // choose draw method depending on the shape
- switch (this.shape) {
- case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break;
- case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break;
- case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break;
- case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
- // TODO: add diamond shape
- case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
- case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
- case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
- case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
- case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break;
- case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break;
- case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break;
- default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
- }
+ // choose draw method depending on the shape
+ switch (this.shape) {
+ case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break;
+ case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break;
+ case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break;
+ case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
+ // TODO: add diamond shape
+ case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
+ case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
+ case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
+ case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
+ case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break;
+ case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break;
+ case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break;
+ default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
+ }
- // reset the size of the node, this can be changed
- this._reset();
+ // reset the size of the node, this can be changed
+ this._reset();
};
/**
@@ -12565,51 +12044,51 @@ Node.prototype.setProperties = function(properties, constants) {
* @return {Object} colorObject
*/
Node.parseColor = function(color) {
- var c;
- if (util.isString(color)) {
- c = {
- border: color,
- background: color,
- highlight: {
- border: color,
- background: color
- }
- };
- // TODO: automatically generate a nice highlight color
+ var c;
+ if (util.isString(color)) {
+ c = {
+ border: color,
+ background: color,
+ highlight: {
+ border: color,
+ background: color
+ }
+ };
+ // TODO: automatically generate a nice highlight color
+ }
+ else {
+ c = {};
+ c.background = color.background || 'white';
+ c.border = color.border || c.background;
+ if (util.isString(color.highlight)) {
+ c.highlight = {
+ border: color.highlight,
+ background: color.highlight
+ }
}
else {
- c = {};
- c.background = color.background || 'white';
- c.border = color.border || c.background;
- if (util.isString(color.highlight)) {
- c.highlight = {
- border: color.highlight,
- background: color.highlight
- }
- }
- else {
- c.highlight = {};
- c.highlight.background = color.highlight && color.highlight.background || c.background;
- c.highlight.border = color.highlight && color.highlight.border || c.border;
- }
+ c.highlight = {};
+ c.highlight.background = color.highlight && color.highlight.background || c.background;
+ c.highlight.border = color.highlight && color.highlight.border || c.border;
}
- return c;
+ }
+ return c;
};
/**
* select this node
*/
Node.prototype.select = function() {
- this.selected = true;
- this._reset();
+ this.selected = true;
+ this._reset();
};
/**
* unselect this node
*/
Node.prototype.unselect = function() {
- this.selected = false;
- this._reset();
+ this.selected = false;
+ this._reset();
};
/**
@@ -12617,8 +12096,8 @@ Node.prototype.unselect = function() {
* @private
*/
Node.prototype._reset = function() {
- this.width = undefined;
- this.height = undefined;
+ this.width = undefined;
+ this.height = undefined;
};
/**
@@ -12627,7 +12106,7 @@ Node.prototype._reset = function() {
* has been set.
*/
Node.prototype.getTitle = function() {
- return this.title;
+ return this.title;
};
/**
@@ -12637,46 +12116,46 @@ Node.prototype.getTitle = function() {
* @returns {number} distance Distance to the border in pixels
*/
Node.prototype.distanceToBorder = function (ctx, angle) {
- var borderWidth = 1;
+ var borderWidth = 1;
- if (!this.width) {
- this.resize(ctx);
- }
+ if (!this.width) {
+ this.resize(ctx);
+ }
- //noinspection FallthroughInSwitchStatementJS
- switch (this.shape) {
- case 'circle':
- case 'dot':
- return this.radius + borderWidth;
-
- case 'ellipse':
- var a = this.width / 2;
- var b = this.height / 2;
- var w = (Math.sin(angle) * a);
- var h = (Math.cos(angle) * b);
- return a * b / Math.sqrt(w * w + h * h);
-
- // TODO: implement distanceToBorder for database
- // TODO: implement distanceToBorder for triangle
- // TODO: implement distanceToBorder for triangleDown
-
- case 'box':
- case 'image':
- case 'text':
- default:
- if (this.width) {
- return Math.min(
- Math.abs(this.width / 2 / Math.cos(angle)),
- Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
- // TODO: reckon with border radius too in case of box
- }
- else {
- return 0;
- }
+ //noinspection FallthroughInSwitchStatementJS
+ switch (this.shape) {
+ case 'circle':
+ case 'dot':
+ return this.radius + borderWidth;
+
+ case 'ellipse':
+ var a = this.width / 2;
+ var b = this.height / 2;
+ var w = (Math.sin(angle) * a);
+ var h = (Math.cos(angle) * b);
+ return a * b / Math.sqrt(w * w + h * h);
+
+ // TODO: implement distanceToBorder for database
+ // TODO: implement distanceToBorder for triangle
+ // TODO: implement distanceToBorder for triangleDown
+
+ case 'box':
+ case 'image':
+ case 'text':
+ default:
+ if (this.width) {
+ return Math.min(
+ Math.abs(this.width / 2 / Math.cos(angle)),
+ Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
+ // TODO: reckon with border radius too in case of box
+ }
+ else {
+ return 0;
+ }
- }
+ }
- // TODO: implement calculation of distance to border for all shapes
+ // TODO: implement calculation of distance to border for all shapes
};
/**
@@ -12685,8 +12164,8 @@ Node.prototype.distanceToBorder = function (ctx, angle) {
* @param {number} fy Force in vertical direction
*/
Node.prototype._setForce = function(fx, fy) {
- this.fx = fx;
- this.fy = fy;
+ this.fx = fx;
+ this.fy = fy;
};
/**
@@ -12696,8 +12175,8 @@ Node.prototype._setForce = function(fx, fy) {
* @private
*/
Node.prototype._addForce = function(fx, fy) {
- this.fx += fx;
- this.fy += fy;
+ this.fx += fx;
+ this.fy += fy;
};
/**
@@ -12705,19 +12184,19 @@ Node.prototype._addForce = function(fx, fy) {
* @param {number} interval Time interval in seconds
*/
Node.prototype.discreteStep = function(interval) {
- if (!this.xFixed) {
- var dx = -this.damping * this.vx; // damping force
- var ax = (this.fx + dx) / this.mass; // acceleration
- this.vx += ax / interval; // velocity
- this.x += this.vx / interval; // position
- }
+ if (!this.xFixed) {
+ var dx = -this.damping * this.vx; // damping force
+ var ax = (this.fx + dx) / this.mass; // acceleration
+ this.vx += ax / interval; // velocity
+ this.x += this.vx / interval; // position
+ }
- if (!this.yFixed) {
- var dy = -this.damping * this.vy; // damping force
- var ay = (this.fy + dy) / this.mass; // acceleration
- this.vy += ay / interval; // velocity
- this.y += this.vy / interval; // position
- }
+ if (!this.yFixed) {
+ var dy = -this.damping * this.vy; // damping force
+ var ay = (this.fy + dy) / this.mass; // acceleration
+ this.vy += ay / interval; // velocity
+ this.y += this.vy / interval; // position
+ }
};
@@ -12726,7 +12205,7 @@ Node.prototype.discreteStep = function(interval) {
* @return {boolean} true if fixed, false if not
*/
Node.prototype.isFixed = function() {
- return (this.xFixed && this.yFixed);
+ return (this.xFixed && this.yFixed);
};
/**
@@ -12736,9 +12215,9 @@ Node.prototype.isFixed = function() {
*/
// TODO: replace this method with calculating the kinetic energy
Node.prototype.isMoving = function(vmin) {
- return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin ||
- (!this.xFixed && Math.abs(this.fx) > this.minForce) ||
- (!this.yFixed && Math.abs(this.fy) > this.minForce));
+ return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin ||
+ (!this.xFixed && Math.abs(this.fx) > this.minForce) ||
+ (!this.yFixed && Math.abs(this.fy) > this.minForce));
};
/**
@@ -12746,7 +12225,7 @@ Node.prototype.isMoving = function(vmin) {
* @return {boolean} selected True if node is selected, else false
*/
Node.prototype.isSelected = function() {
- return this.selected;
+ return this.selected;
};
/**
@@ -12754,7 +12233,7 @@ Node.prototype.isSelected = function() {
* @return {Number} value
*/
Node.prototype.getValue = function() {
- return this.value;
+ return this.value;
};
/**
@@ -12764,9 +12243,9 @@ Node.prototype.getValue = function() {
* @return {Number} value
*/
Node.prototype.getDistance = function(x, y) {
- var dx = this.x - x,
- dy = this.y - y;
- return Math.sqrt(dx * dx + dy * dy);
+ var dx = this.x - x,
+ dy = this.y - y;
+ return Math.sqrt(dx * dx + dy * dy);
};
@@ -12777,15 +12256,15 @@ Node.prototype.getDistance = function(x, y) {
* @param {Number} max
*/
Node.prototype.setValueRange = function(min, max) {
- if (!this.radiusFixed && this.value !== undefined) {
- if (max == min) {
- this.radius = (this.radiusMin + this.radiusMax) / 2;
- }
- else {
- var scale = (this.radiusMax - this.radiusMin) / (max - min);
- this.radius = (this.value - min) * scale + this.radiusMin;
- }
+ if (!this.radiusFixed && this.value !== undefined) {
+ if (max == min) {
+ this.radius = (this.radiusMin + this.radiusMax) / 2;
+ }
+ else {
+ var scale = (this.radiusMax - this.radiusMin) / (max - min);
+ this.radius = (this.value - min) * scale + this.radiusMin;
}
+ }
};
/**
@@ -12794,7 +12273,7 @@ Node.prototype.setValueRange = function(min, max) {
* @param {CanvasRenderingContext2D} ctx
*/
Node.prototype.draw = function(ctx) {
- throw "Draw method not initialized for node";
+ throw "Draw method not initialized for node";
};
/**
@@ -12803,7 +12282,7 @@ Node.prototype.draw = function(ctx) {
* @param {CanvasRenderingContext2D} ctx
*/
Node.prototype.resize = function(ctx) {
- throw "Resize method not initialized for node";
+ throw "Resize method not initialized for node";
};
/**
@@ -12812,258 +12291,258 @@ Node.prototype.resize = function(ctx) {
* @return {boolean} True if location is located on node
*/
Node.prototype.isOverlappingWith = function(obj) {
- return (this.left < obj.right &&
- this.left + this.width > obj.left &&
- this.top < obj.bottom &&
- this.top + this.height > obj.top);
+ return (this.left < obj.right &&
+ this.left + this.width > obj.left &&
+ this.top < obj.bottom &&
+ this.top + this.height > obj.top);
};
Node.prototype._resizeImage = function (ctx) {
- // TODO: pre calculate the image size
- if (!this.width) { // undefined or 0
- var width, height;
- if (this.value) {
- var scale = this.imageObj.height / this.imageObj.width;
- width = this.radius || this.imageObj.width;
- height = this.radius * scale || this.imageObj.height;
- }
- else {
- width = this.imageObj.width;
- height = this.imageObj.height;
- }
- this.width = width;
- this.height = height;
+ // TODO: pre calculate the image size
+ if (!this.width) { // undefined or 0
+ var width, height;
+ if (this.value) {
+ var scale = this.imageObj.height / this.imageObj.width;
+ width = this.radius || this.imageObj.width;
+ height = this.radius * scale || this.imageObj.height;
}
+ else {
+ width = this.imageObj.width;
+ height = this.imageObj.height;
+ }
+ this.width = width;
+ this.height = height;
+ }
};
Node.prototype._drawImage = function (ctx) {
- this._resizeImage(ctx);
+ this._resizeImage(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- var yLabel;
- if (this.imageObj) {
- ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
- yLabel = this.y + this.height / 2;
- }
- else {
- // image still loading... just draw the label for now
- yLabel = this.y;
- }
+ var yLabel;
+ if (this.imageObj) {
+ ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
+ yLabel = this.y + this.height / 2;
+ }
+ else {
+ // image still loading... just draw the label for now
+ yLabel = this.y;
+ }
- this._label(ctx, this.label, this.x, yLabel, undefined, "top");
+ this._label(ctx, this.label, this.x, yLabel, undefined, "top");
};
Node.prototype._resizeBox = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- this.width = textSize.width + 2 * margin;
- this.height = textSize.height + 2 * margin;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ this.width = textSize.width + 2 * margin;
+ this.height = textSize.height + 2 * margin;
+ }
};
Node.prototype._drawBox = function (ctx) {
- this._resizeBox(ctx);
+ this._resizeBox(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.roundRect(this.left, this.top, this.width, this.height, this.radius);
- ctx.fill();
- ctx.stroke();
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.roundRect(this.left, this.top, this.width, this.height, this.radius);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeDatabase = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- var size = textSize.width + 2 * margin;
- this.width = size;
- this.height = size;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ var size = textSize.width + 2 * margin;
+ this.width = size;
+ this.height = size;
+ }
};
Node.prototype._drawDatabase = function (ctx) {
- this._resizeDatabase(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
-
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height);
- ctx.fill();
- ctx.stroke();
+ this._resizeDatabase(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- this._label(ctx, this.label, this.x, this.y);
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height);
+ ctx.fill();
+ ctx.stroke();
+
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeCircle = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
- this.radius = diameter / 2;
-
- this.width = diameter;
- this.height = diameter;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
+ this.radius = diameter / 2;
+
+ this.width = diameter;
+ this.height = diameter;
+ }
};
Node.prototype._drawCircle = function (ctx) {
- this._resizeCircle(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
-
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.circle(this.x, this.y, this.radius);
- ctx.fill();
- ctx.stroke();
+ this._resizeCircle(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- this._label(ctx, this.label, this.x, this.y);
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.circle(this.x, this.y, this.radius);
+ ctx.fill();
+ ctx.stroke();
+
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._resizeEllipse = function (ctx) {
- if (!this.width) {
- var textSize = this.getTextSize(ctx);
+ if (!this.width) {
+ var textSize = this.getTextSize(ctx);
- this.width = textSize.width * 1.5;
- this.height = textSize.height * 2;
- if (this.width < this.height) {
- this.width = this.height;
- }
+ this.width = textSize.width * 1.5;
+ this.height = textSize.height * 2;
+ if (this.width < this.height) {
+ this.width = this.height;
}
+ }
};
Node.prototype._drawEllipse = function (ctx) {
- this._resizeEllipse(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
-
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx.ellipse(this.left, this.top, this.width, this.height);
- ctx.fill();
- ctx.stroke();
+ this._resizeEllipse(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
+
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.ellipse(this.left, this.top, this.width, this.height);
+ ctx.fill();
+ ctx.stroke();
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._drawDot = function (ctx) {
- this._drawShape(ctx, 'circle');
+ this._drawShape(ctx, 'circle');
};
Node.prototype._drawTriangle = function (ctx) {
- this._drawShape(ctx, 'triangle');
+ this._drawShape(ctx, 'triangle');
};
Node.prototype._drawTriangleDown = function (ctx) {
- this._drawShape(ctx, 'triangleDown');
+ this._drawShape(ctx, 'triangleDown');
};
Node.prototype._drawSquare = function (ctx) {
- this._drawShape(ctx, 'square');
+ this._drawShape(ctx, 'square');
};
Node.prototype._drawStar = function (ctx) {
- this._drawShape(ctx, 'star');
+ this._drawShape(ctx, 'star');
};
Node.prototype._resizeShape = function (ctx) {
- if (!this.width) {
- var size = 2 * this.radius;
- this.width = size;
- this.height = size;
- }
+ if (!this.width) {
+ var size = 2 * this.radius;
+ this.width = size;
+ this.height = size;
+ }
};
Node.prototype._drawShape = function (ctx, shape) {
- this._resizeShape(ctx);
+ this._resizeShape(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
- ctx.lineWidth = this.selected ? 2.0 : 1.0;
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
+ ctx.lineWidth = this.selected ? 2.0 : 1.0;
- ctx[shape](this.x, this.y, this.radius);
- ctx.fill();
- ctx.stroke();
+ ctx[shape](this.x, this.y, this.radius);
+ ctx.fill();
+ ctx.stroke();
- if (this.label) {
- this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top');
- }
+ if (this.label) {
+ this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top');
+ }
};
Node.prototype._resizeText = function (ctx) {
- if (!this.width) {
- var margin = 5;
- var textSize = this.getTextSize(ctx);
- this.width = textSize.width + 2 * margin;
- this.height = textSize.height + 2 * margin;
- }
+ if (!this.width) {
+ var margin = 5;
+ var textSize = this.getTextSize(ctx);
+ this.width = textSize.width + 2 * margin;
+ this.height = textSize.height + 2 * margin;
+ }
};
Node.prototype._drawText = function (ctx) {
- this._resizeText(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
+ this._resizeText(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
- this._label(ctx, this.label, this.x, this.y);
+ this._label(ctx, this.label, this.x, this.y);
};
Node.prototype._label = function (ctx, text, x, y, align, baseline) {
- if (text) {
- ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
- ctx.fillStyle = this.fontColor || "black";
- ctx.textAlign = align || "center";
- ctx.textBaseline = baseline || "middle";
+ if (text) {
+ ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
+ ctx.fillStyle = this.fontColor || "black";
+ ctx.textAlign = align || "center";
+ ctx.textBaseline = baseline || "middle";
- var lines = text.split('\n'),
- lineCount = lines.length,
- fontSize = (this.fontSize + 4),
- yLine = y + (1 - lineCount) / 2 * fontSize;
+ var lines = text.split('\n'),
+ lineCount = lines.length,
+ fontSize = (this.fontSize + 4),
+ yLine = y + (1 - lineCount) / 2 * fontSize;
- for (var i = 0; i < lineCount; i++) {
- ctx.fillText(lines[i], x, yLine);
- yLine += fontSize;
- }
+ for (var i = 0; i < lineCount; i++) {
+ ctx.fillText(lines[i], x, yLine);
+ yLine += fontSize;
}
+ }
};
Node.prototype.getTextSize = function(ctx) {
- if (this.label != undefined) {
- ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
-
- var lines = this.label.split('\n'),
- height = (this.fontSize + 4) * lines.length,
- width = 0;
+ if (this.label != undefined) {
+ ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace;
- for (var i = 0, iMax = lines.length; i < iMax; i++) {
- width = Math.max(width, ctx.measureText(lines[i]).width);
- }
+ var lines = this.label.split('\n'),
+ height = (this.fontSize + 4) * lines.length,
+ width = 0;
- return {"width": width, "height": height};
- }
- else {
- return {"width": 0, "height": 0};
+ for (var i = 0, iMax = lines.length; i < iMax; i++) {
+ width = Math.max(width, ctx.measureText(lines[i]).width);
}
+
+ return {"width": width, "height": height};
+ }
+ else {
+ return {"width": 0, "height": 0};
+ }
};
/**
@@ -13082,40 +12561,40 @@ Node.prototype.getTextSize = function(ctx) {
* example for the color
*/
function Edge (properties, graph, constants) {
- if (!graph) {
- throw "No graph provided";
- }
- this.graph = graph;
-
- // initialize constants
- this.widthMin = constants.edges.widthMin;
- this.widthMax = constants.edges.widthMax;
-
- // initialize variables
- this.id = undefined;
- this.fromId = undefined;
- this.toId = undefined;
- this.style = constants.edges.style;
- this.title = undefined;
- this.width = constants.edges.width;
- this.value = undefined;
- this.length = constants.edges.length;
-
- this.from = null; // a node
- this.to = null; // a node
- this.connected = false;
-
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
-
- this.stiffness = undefined; // depends on the length of the edge
- this.color = constants.edges.color;
- this.widthFixed = false;
- this.lengthFixed = false;
-
- this.setProperties(properties, constants);
+ if (!graph) {
+ throw "No graph provided";
+ }
+ this.graph = graph;
+
+ // initialize constants
+ this.widthMin = constants.edges.widthMin;
+ this.widthMax = constants.edges.widthMax;
+
+ // initialize variables
+ this.id = undefined;
+ this.fromId = undefined;
+ this.toId = undefined;
+ this.style = constants.edges.style;
+ this.title = undefined;
+ this.width = constants.edges.width;
+ this.value = undefined;
+ this.length = constants.edges.length;
+
+ this.from = null; // a node
+ this.to = null; // a node
+ this.connected = false;
+
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
+
+ this.stiffness = undefined; // depends on the length of the edge
+ this.color = constants.edges.color;
+ this.widthFixed = false;
+ this.lengthFixed = false;
+
+ this.setProperties(properties, constants);
}
/**
@@ -13124,95 +12603,95 @@ function Edge (properties, graph, constants) {
* @param {Object} constants and object with default, global properties
*/
Edge.prototype.setProperties = function(properties, constants) {
- if (!properties) {
- return;
- }
-
- if (properties.from != undefined) {this.fromId = properties.from;}
- if (properties.to != undefined) {this.toId = properties.to;}
+ if (!properties) {
+ return;
+ }
- if (properties.id != undefined) {this.id = properties.id;}
- if (properties.style != undefined) {this.style = properties.style;}
- if (properties.label != undefined) {this.label = properties.label;}
- if (this.label) {
- this.fontSize = constants.edges.fontSize;
- this.fontFace = constants.edges.fontFace;
- this.fontColor = constants.edges.fontColor;
- if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
- if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
- if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
- }
- if (properties.title != undefined) {this.title = properties.title;}
- if (properties.width != undefined) {this.width = properties.width;}
- if (properties.value != undefined) {this.value = properties.value;}
- if (properties.length != undefined) {this.length = properties.length;}
+ if (properties.from != undefined) {this.fromId = properties.from;}
+ if (properties.to != undefined) {this.toId = properties.to;}
+
+ if (properties.id != undefined) {this.id = properties.id;}
+ if (properties.style != undefined) {this.style = properties.style;}
+ if (properties.label != undefined) {this.label = properties.label;}
+ if (this.label) {
+ this.fontSize = constants.edges.fontSize;
+ this.fontFace = constants.edges.fontFace;
+ this.fontColor = constants.edges.fontColor;
+ if (properties.fontColor != undefined) {this.fontColor = properties.fontColor;}
+ if (properties.fontSize != undefined) {this.fontSize = properties.fontSize;}
+ if (properties.fontFace != undefined) {this.fontFace = properties.fontFace;}
+ }
+ if (properties.title != undefined) {this.title = properties.title;}
+ if (properties.width != undefined) {this.width = properties.width;}
+ if (properties.value != undefined) {this.value = properties.value;}
+ if (properties.length != undefined) {this.length = properties.length;}
+
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ if (properties.dash) {
+ if (properties.dash.length != undefined) {this.dash.length = properties.dash.length;}
+ if (properties.dash.gap != undefined) {this.dash.gap = properties.dash.gap;}
+ if (properties.dash.altLength != undefined) {this.dash.altLength = properties.dash.altLength;}
+ }
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- if (properties.dash) {
- if (properties.dash.length != undefined) {this.dash.length = properties.dash.length;}
- if (properties.dash.gap != undefined) {this.dash.gap = properties.dash.gap;}
- if (properties.dash.altLength != undefined) {this.dash.altLength = properties.dash.altLength;}
- }
-
- if (properties.color != undefined) {this.color = properties.color;}
+ if (properties.color != undefined) {this.color = properties.color;}
- // A node is connected when it has a from and to node.
- this.connect();
+ // A node is connected when it has a from and to node.
+ this.connect();
- this.widthFixed = this.widthFixed || (properties.width != undefined);
- this.lengthFixed = this.lengthFixed || (properties.length != undefined);
- this.stiffness = 1 / this.length;
+ this.widthFixed = this.widthFixed || (properties.width != undefined);
+ this.lengthFixed = this.lengthFixed || (properties.length != undefined);
+ this.stiffness = 1 / this.length;
- // set draw method based on style
- switch (this.style) {
- case 'line': this.draw = this._drawLine; break;
- case 'arrow': this.draw = this._drawArrow; break;
- case 'arrow-center': this.draw = this._drawArrowCenter; break;
- case 'dash-line': this.draw = this._drawDashLine; break;
- default: this.draw = this._drawLine; break;
- }
+ // set draw method based on style
+ switch (this.style) {
+ case 'line': this.draw = this._drawLine; break;
+ case 'arrow': this.draw = this._drawArrow; break;
+ case 'arrow-center': this.draw = this._drawArrowCenter; break;
+ case 'dash-line': this.draw = this._drawDashLine; break;
+ default: this.draw = this._drawLine; break;
+ }
};
/**
* Connect an edge to its nodes
*/
Edge.prototype.connect = function () {
- this.disconnect();
+ this.disconnect();
- this.from = this.graph.nodes[this.fromId] || null;
- this.to = this.graph.nodes[this.toId] || null;
- this.connected = (this.from && this.to);
+ this.from = this.graph.nodes[this.fromId] || null;
+ this.to = this.graph.nodes[this.toId] || null;
+ this.connected = (this.from && this.to);
- if (this.connected) {
- this.from.attachEdge(this);
- this.to.attachEdge(this);
+ if (this.connected) {
+ this.from.attachEdge(this);
+ this.to.attachEdge(this);
+ }
+ else {
+ if (this.from) {
+ this.from.detachEdge(this);
}
- else {
- if (this.from) {
- this.from.detachEdge(this);
- }
- if (this.to) {
- this.to.detachEdge(this);
- }
+ if (this.to) {
+ this.to.detachEdge(this);
}
+ }
};
/**
* Disconnect an edge from its nodes
*/
Edge.prototype.disconnect = function () {
- if (this.from) {
- this.from.detachEdge(this);
- this.from = null;
- }
- if (this.to) {
- this.to.detachEdge(this);
- this.to = null;
- }
+ if (this.from) {
+ this.from.detachEdge(this);
+ this.from = null;
+ }
+ if (this.to) {
+ this.to.detachEdge(this);
+ this.to = null;
+ }
- this.connected = false;
+ this.connected = false;
};
/**
@@ -13221,7 +12700,7 @@ Edge.prototype.disconnect = function () {
* has been set.
*/
Edge.prototype.getTitle = function() {
- return this.title;
+ return this.title;
};
@@ -13230,7 +12709,7 @@ Edge.prototype.getTitle = function() {
* @return {Number} value
*/
Edge.prototype.getValue = function() {
- return this.value;
+ return this.value;
};
/**
@@ -13240,10 +12719,10 @@ Edge.prototype.getValue = function() {
* @param {Number} max
*/
Edge.prototype.setValueRange = function(min, max) {
- if (!this.widthFixed && this.value !== undefined) {
- var factor = (this.widthMax - this.widthMin) / (max - min);
- this.width = (this.value - min) * factor + this.widthMin;
- }
+ if (!this.widthFixed && this.value !== undefined) {
+ var scale = (this.widthMax - this.widthMin) / (max - min);
+ this.width = (this.value - min) * scale + this.widthMin;
+ }
};
/**
@@ -13253,7 +12732,7 @@ Edge.prototype.setValueRange = function(min, max) {
* @param {CanvasRenderingContext2D} ctx
*/
Edge.prototype.draw = function(ctx) {
- throw "Method draw not initialized in edge";
+ throw "Method draw not initialized in edge";
};
/**
@@ -13262,19 +12741,19 @@ Edge.prototype.draw = function(ctx) {
* @return {boolean} True if location is located on the edge
*/
Edge.prototype.isOverlappingWith = function(obj) {
- var distMax = 10;
+ var distMax = 10;
- var xFrom = this.from.x;
- var yFrom = this.from.y;
- var xTo = this.to.x;
- var yTo = this.to.y;
- var xObj = obj.left;
- var yObj = obj.top;
+ var xFrom = this.from.x;
+ var yFrom = this.from.y;
+ var xTo = this.to.x;
+ var yTo = this.to.y;
+ var xObj = obj.left;
+ var yObj = obj.top;
- var dist = Edge._dist(xFrom, yFrom, xTo, yTo, xObj, yObj);
+ var dist = Edge._dist(xFrom, yFrom, xTo, yTo, xObj, yObj);
- return (dist < distMax);
+ return (dist < distMax);
};
@@ -13286,40 +12765,40 @@ Edge.prototype.isOverlappingWith = function(obj) {
* @private
*/
Edge.prototype._drawLine = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
- var point;
- if (this.from != this.to) {
- // draw line
- this._line(ctx);
+ var point;
+ if (this.from != this.to) {
+ // draw line
+ this._line(ctx);
- // draw label
- if (this.label) {
- point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // draw label
+ if (this.label) {
+ point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ var x, y;
+ var radius = this.length / 4;
+ var node = this.from;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
}
else {
- var x, y;
- var radius = this.length / 4;
- var node = this.from;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- }
- this._circle(ctx, x, y, radius);
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
+ x = node.x + radius;
+ y = node.y - node.height / 2;
}
+ this._circle(ctx, x, y, radius);
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
};
/**
@@ -13329,12 +12808,12 @@ Edge.prototype._drawLine = function(ctx) {
* @private
*/
Edge.prototype._getLineWidth = function() {
- if (this.from.selected || this.to.selected) {
- return Math.min(this.width * 2, this.widthMax);
- }
- else {
- return this.width;
- }
+ if (this.from.selected || this.to.selected) {
+ return Math.min(this.width * 2, this.widthMax);
+ }
+ else {
+ return this.width;
+ }
};
/**
@@ -13343,11 +12822,11 @@ Edge.prototype._getLineWidth = function() {
* @private
*/
Edge.prototype._line = function (ctx) {
- // draw a straight line
- ctx.beginPath();
- ctx.moveTo(this.from.x, this.from.y);
- ctx.lineTo(this.to.x, this.to.y);
- ctx.stroke();
+ // draw a straight line
+ ctx.beginPath();
+ ctx.moveTo(this.from.x, this.from.y);
+ ctx.lineTo(this.to.x, this.to.y);
+ ctx.stroke();
};
/**
@@ -13359,10 +12838,10 @@ Edge.prototype._line = function (ctx) {
* @private
*/
Edge.prototype._circle = function (ctx, x, y, radius) {
- // draw a circle
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
- ctx.stroke();
+ // draw a circle
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ ctx.stroke();
};
/**
@@ -13374,24 +12853,24 @@ Edge.prototype._circle = function (ctx, x, y, radius) {
* @private
*/
Edge.prototype._label = function (ctx, text, x, y) {
- if (text) {
- // TODO: cache the calculated size
- ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
- this.fontSize + "px " + this.fontFace;
- ctx.fillStyle = 'white';
- var width = ctx.measureText(text).width;
- var height = this.fontSize;
- var left = x - width / 2;
- var top = y - height / 2;
-
- ctx.fillRect(left, top, width, height);
-
- // draw text
- ctx.fillStyle = this.fontColor || "black";
- ctx.textAlign = "left";
- ctx.textBaseline = "top";
- ctx.fillText(text, left, top);
- }
+ if (text) {
+ // TODO: cache the calculated size
+ ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
+ this.fontSize + "px " + this.fontFace;
+ ctx.fillStyle = 'white';
+ var width = ctx.measureText(text).width;
+ var height = this.fontSize;
+ var left = x - width / 2;
+ var top = y - height / 2;
+
+ ctx.fillRect(left, top, width, height);
+
+ // draw text
+ ctx.fillStyle = this.fontColor || "black";
+ ctx.textAlign = "left";
+ ctx.textBaseline = "top";
+ ctx.fillText(text, left, top);
+ }
};
/**
@@ -13404,35 +12883,35 @@ Edge.prototype._label = function (ctx, text, x, y) {
* @private
*/
Edge.prototype._drawDashLine = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
-
- // draw dashed line
- ctx.beginPath();
- ctx.lineCap = 'round';
- if (this.dash.altLength != undefined) //If an alt dash value has been set add to the array this value
- {
- ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
- [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
- }
- else if (this.dash.length != undefined && this.dash.gap != undefined) //If a dash and gap value has been set add to the array this value
- {
- ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
- [this.dash.length,this.dash.gap]);
- }
- else //If all else fails draw a line
- {
- ctx.moveTo(this.from.x, this.from.y);
- ctx.lineTo(this.to.x, this.to.y);
- }
- ctx.stroke();
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ // draw dashed line
+ ctx.beginPath();
+ ctx.lineCap = 'round';
+ if (this.dash.altLength != undefined) //If an alt dash value has been set add to the array this value
+ {
+ ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
+ [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]);
+ }
+ else if (this.dash.length != undefined && this.dash.gap != undefined) //If a dash and gap value has been set add to the array this value
+ {
+ ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,
+ [this.dash.length,this.dash.gap]);
+ }
+ else //If all else fails draw a line
+ {
+ ctx.moveTo(this.from.x, this.from.y);
+ ctx.lineTo(this.to.x, this.to.y);
+ }
+ ctx.stroke();
- // draw label
- if (this.label) {
- var point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // draw label
+ if (this.label) {
+ var point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
};
/**
@@ -13442,10 +12921,10 @@ Edge.prototype._drawDashLine = function(ctx) {
* @private
*/
Edge.prototype._pointOnLine = function (percentage) {
- return {
- x: (1 - percentage) * this.from.x + percentage * this.to.x,
- y: (1 - percentage) * this.from.y + percentage * this.to.y
- }
+ return {
+ x: (1 - percentage) * this.from.x + percentage * this.to.x,
+ y: (1 - percentage) * this.from.y + percentage * this.to.y
+ }
};
/**
@@ -13458,11 +12937,11 @@ Edge.prototype._pointOnLine = function (percentage) {
* @private
*/
Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
- var angle = (percentage - 3/8) * 2 * Math.PI;
- return {
- x: x + radius * Math.cos(angle),
- y: y - radius * Math.sin(angle)
- }
+ var angle = (percentage - 3/8) * 2 * Math.PI;
+ return {
+ x: x + radius * Math.cos(angle),
+ y: y - radius * Math.sin(angle)
+ }
};
/**
@@ -13473,62 +12952,62 @@ Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
* @private
*/
Edge.prototype._drawArrowCenter = function(ctx) {
- var point;
- // set style
- ctx.strokeStyle = this.color;
- ctx.fillStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
-
- if (this.from != this.to) {
- // draw line
- this._line(ctx);
-
- // draw an arrow halfway the line
- var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
- var length = 10 + 5 * this.width; // TODO: make customizable?
- point = this._pointOnLine(0.5);
- ctx.arrow(point.x, point.y, angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ var point;
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.fillStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ if (this.from != this.to) {
+ // draw line
+ this._line(ctx);
+
+ // draw an arrow halfway the line
+ var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
+ var length = 10 + 5 * this.width; // TODO: make customizable?
+ point = this._pointOnLine(0.5);
+ ctx.arrow(point.x, point.y, angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ // draw circle
+ var x, y;
+ var radius = this.length / 4;
+ var node = this.from;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
}
else {
- // draw circle
- var x, y;
- var radius = this.length / 4;
- var node = this.from;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- }
- this._circle(ctx, x, y, radius);
+ x = node.x + radius;
+ y = node.y - node.height / 2;
+ }
+ this._circle(ctx, x, y, radius);
- // draw all arrows
- var angle = 0.2 * Math.PI;
- var length = 10 + 5 * this.width; // TODO: make customizable?
- point = this._pointOnCircle(x, y, radius, 0.5);
- ctx.arrow(point.x, point.y, angle, length);
- ctx.fill();
- ctx.stroke();
+ // draw all arrows
+ var angle = 0.2 * Math.PI;
+ var length = 10 + 5 * this.width; // TODO: make customizable?
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ ctx.arrow(point.x, point.y, angle, length);
+ ctx.fill();
+ ctx.stroke();
- // draw label
- if (this.label) {
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // draw label
+ if (this.label) {
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
}
+ }
};
@@ -13541,91 +13020,91 @@ Edge.prototype._drawArrowCenter = function(ctx) {
* @private
*/
Edge.prototype._drawArrow = function(ctx) {
- // set style
- ctx.strokeStyle = this.color;
- ctx.fillStyle = this.color;
- ctx.lineWidth = this._getLineWidth();
+ // set style
+ ctx.strokeStyle = this.color;
+ ctx.fillStyle = this.color;
+ ctx.lineWidth = this._getLineWidth();
+
+ // draw line
+ var angle, length;
+ if (this.from != this.to) {
+ // calculate length and angle of the line
+ angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
+ var dx = (this.to.x - this.from.x);
+ var dy = (this.to.y - this.from.y);
+ var lEdge = Math.sqrt(dx * dx + dy * dy);
+
+ var lFrom = this.from.distanceToBorder(ctx, angle + Math.PI);
+ var pFrom = (lEdge - lFrom) / lEdge;
+ var xFrom = (pFrom) * this.from.x + (1 - pFrom) * this.to.x;
+ var yFrom = (pFrom) * this.from.y + (1 - pFrom) * this.to.y;
+
+ var lTo = this.to.distanceToBorder(ctx, angle);
+ var pTo = (lEdge - lTo) / lEdge;
+ var xTo = (1 - pTo) * this.from.x + pTo * this.to.x;
+ var yTo = (1 - pTo) * this.from.y + pTo * this.to.y;
- // draw line
- var angle, length;
- if (this.from != this.to) {
- // calculate length and angle of the line
- angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
- var dx = (this.to.x - this.from.x);
- var dy = (this.to.y - this.from.y);
- var lEdge = Math.sqrt(dx * dx + dy * dy);
-
- var lFrom = this.from.distanceToBorder(ctx, angle + Math.PI);
- var pFrom = (lEdge - lFrom) / lEdge;
- var xFrom = (pFrom) * this.from.x + (1 - pFrom) * this.to.x;
- var yFrom = (pFrom) * this.from.y + (1 - pFrom) * this.to.y;
-
- var lTo = this.to.distanceToBorder(ctx, angle);
- var pTo = (lEdge - lTo) / lEdge;
- var xTo = (1 - pTo) * this.from.x + pTo * this.to.x;
- var yTo = (1 - pTo) * this.from.y + pTo * this.to.y;
-
- ctx.beginPath();
- ctx.moveTo(xFrom, yFrom);
- ctx.lineTo(xTo, yTo);
- ctx.stroke();
-
- // draw arrow at the end of the line
- length = 10 + 5 * this.width; // TODO: make customizable?
- ctx.arrow(xTo, yTo, angle, length);
- ctx.fill();
- ctx.stroke();
-
- // draw label
- if (this.label) {
- var point = this._pointOnLine(0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ ctx.beginPath();
+ ctx.moveTo(xFrom, yFrom);
+ ctx.lineTo(xTo, yTo);
+ ctx.stroke();
+
+ // draw arrow at the end of the line
+ length = 10 + 5 * this.width; // TODO: make customizable?
+ ctx.arrow(xTo, yTo, angle, length);
+ ctx.fill();
+ ctx.stroke();
+
+ // draw label
+ if (this.label) {
+ var point = this._pointOnLine(0.5);
+ this._label(ctx, this.label, point.x, point.y);
+ }
+ }
+ else {
+ // draw circle
+ var node = this.from;
+ var x, y, arrow;
+ var radius = this.length / 4;
+ if (!node.width) {
+ node.resize(ctx);
+ }
+ if (node.width > node.height) {
+ x = node.x + node.width / 2;
+ y = node.y - radius;
+ arrow = {
+ x: x,
+ y: node.y,
+ angle: 0.9 * Math.PI
+ };
}
else {
- // draw circle
- var node = this.from;
- var x, y, arrow;
- var radius = this.length / 4;
- if (!node.width) {
- node.resize(ctx);
- }
- if (node.width > node.height) {
- x = node.x + node.width / 2;
- y = node.y - radius;
- arrow = {
- x: x,
- y: node.y,
- angle: 0.9 * Math.PI
- };
- }
- else {
- x = node.x + radius;
- y = node.y - node.height / 2;
- arrow = {
- x: node.x,
- y: y,
- angle: 0.6 * Math.PI
- };
- }
- ctx.beginPath();
- // TODO: do not draw a circle, but an arc
- // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
- ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
- ctx.stroke();
+ x = node.x + radius;
+ y = node.y - node.height / 2;
+ arrow = {
+ x: node.x,
+ y: y,
+ angle: 0.6 * Math.PI
+ };
+ }
+ ctx.beginPath();
+ // TODO: do not draw a circle, but an arc
+ // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center
+ ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ ctx.stroke();
- // draw all arrows
- length = 10 + 5 * this.width; // TODO: make customizable?
- ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
- ctx.fill();
- ctx.stroke();
+ // draw all arrows
+ length = 10 + 5 * this.width; // TODO: make customizable?
+ ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
+ ctx.fill();
+ ctx.stroke();
- // draw label
- if (this.label) {
- point = this._pointOnCircle(x, y, radius, 0.5);
- this._label(ctx, this.label, point.x, point.y);
- }
+ // draw label
+ if (this.label) {
+ point = this._pointOnCircle(x, y, radius, 0.5);
+ this._label(ctx, this.label, point.x, point.y);
}
+ }
};
@@ -13643,30 +13122,30 @@ Edge.prototype._drawArrow = function(ctx) {
* @private
*/
Edge._dist = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
- var px = x2-x1,
- py = y2-y1,
- something = px*px + py*py,
- u = ((x3 - x1) * px + (y3 - y1) * py) / something;
+ var px = x2-x1,
+ py = y2-y1,
+ something = px*px + py*py,
+ u = ((x3 - x1) * px + (y3 - y1) * py) / something;
- if (u > 1) {
- u = 1;
- }
- else if (u < 0) {
- u = 0;
- }
+ if (u > 1) {
+ u = 1;
+ }
+ else if (u < 0) {
+ u = 0;
+ }
- var x = x1 + u * px,
- y = y1 + u * py,
- dx = x - x3,
- dy = y - y3;
+ var x = x1 + u * px,
+ y = y1 + u * py,
+ dx = x - x3,
+ dy = y - y3;
- //# Note: If the actual distance does not matter,
- //# if you only want to compare what this function
- //# returns to other results of this function, you
- //# can just return the squared distance instead
- //# (i.e. remove the sqrt) to gain a little performance
+ //# Note: If the actual distance does not matter,
+ //# if you only want to compare what this function
+ //# returns to other results of this function, you
+ //# can just return the squared distance instead
+ //# (i.e. remove the sqrt) to gain a little performance
- return Math.sqrt(dx*dx + dy*dy);
+ return Math.sqrt(dx*dx + dy*dy);
};
/**
@@ -13677,38 +13156,38 @@ Edge._dist = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
* @param {String} [text]
*/
function Popup(container, x, y, text) {
- if (container) {
- this.container = container;
- }
- else {
- this.container = document.body;
- }
- this.x = 0;
- this.y = 0;
- this.padding = 5;
+ if (container) {
+ this.container = container;
+ }
+ else {
+ this.container = document.body;
+ }
+ this.x = 0;
+ this.y = 0;
+ this.padding = 5;
- if (x !== undefined && y !== undefined ) {
- this.setPosition(x, y);
- }
- if (text !== undefined) {
- this.setText(text);
- }
+ if (x !== undefined && y !== undefined ) {
+ this.setPosition(x, y);
+ }
+ if (text !== undefined) {
+ this.setText(text);
+ }
- // create the frame
- this.frame = document.createElement("div");
- var style = this.frame.style;
- style.position = "absolute";
- style.visibility = "hidden";
- style.border = "1px solid #666";
- style.color = "black";
- style.padding = this.padding + "px";
- style.backgroundColor = "#FFFFC6";
- style.borderRadius = "3px";
- style.MozBorderRadius = "3px";
- style.WebkitBorderRadius = "3px";
- style.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)";
- style.whiteSpace = "nowrap";
- this.container.appendChild(this.frame);
+ // create the frame
+ this.frame = document.createElement("div");
+ var style = this.frame.style;
+ style.position = "absolute";
+ style.visibility = "hidden";
+ style.border = "1px solid #666";
+ style.color = "black";
+ style.padding = this.padding + "px";
+ style.backgroundColor = "#FFFFC6";
+ style.borderRadius = "3px";
+ style.MozBorderRadius = "3px";
+ style.WebkitBorderRadius = "3px";
+ style.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)";
+ style.whiteSpace = "nowrap";
+ this.container.appendChild(this.frame);
};
/**
@@ -13716,8 +13195,8 @@ function Popup(container, x, y, text) {
* @param {number} y Vertical position of the popup window
*/
Popup.prototype.setPosition = function(x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
+ this.x = parseInt(x);
+ this.y = parseInt(y);
};
/**
@@ -13725,7 +13204,7 @@ Popup.prototype.setPosition = function(x, y) {
* @param {string} text
*/
Popup.prototype.setText = function(text) {
- this.frame.innerHTML = text;
+ this.frame.innerHTML = text;
};
/**
@@ -13733,46 +13212,46 @@ Popup.prototype.setText = function(text) {
* @param {boolean} show Optional. Show or hide the window
*/
Popup.prototype.show = function (show) {
- if (show === undefined) {
- show = true;
- }
-
- if (show) {
- var height = this.frame.clientHeight;
- var width = this.frame.clientWidth;
- var maxHeight = this.frame.parentNode.clientHeight;
- var maxWidth = this.frame.parentNode.clientWidth;
+ if (show === undefined) {
+ show = true;
+ }
- var top = (this.y - height);
- if (top + height + this.padding > maxHeight) {
- top = maxHeight - height - this.padding;
- }
- if (top < this.padding) {
- top = this.padding;
- }
+ if (show) {
+ var height = this.frame.clientHeight;
+ var width = this.frame.clientWidth;
+ var maxHeight = this.frame.parentNode.clientHeight;
+ var maxWidth = this.frame.parentNode.clientWidth;
- var left = this.x;
- if (left + width + this.padding > maxWidth) {
- left = maxWidth - width - this.padding;
- }
- if (left < this.padding) {
- left = this.padding;
- }
+ var top = (this.y - height);
+ if (top + height + this.padding > maxHeight) {
+ top = maxHeight - height - this.padding;
+ }
+ if (top < this.padding) {
+ top = this.padding;
+ }
- this.frame.style.left = left + "px";
- this.frame.style.top = top + "px";
- this.frame.style.visibility = "visible";
+ var left = this.x;
+ if (left + width + this.padding > maxWidth) {
+ left = maxWidth - width - this.padding;
}
- else {
- this.hide();
+ if (left < this.padding) {
+ left = this.padding;
}
+
+ this.frame.style.left = left + "px";
+ this.frame.style.top = top + "px";
+ this.frame.style.visibility = "visible";
+ }
+ else {
+ this.hide();
+ }
};
/**
* Hide the popup window
*/
Popup.prototype.hide = function () {
- this.frame.style.visibility = "hidden";
+ this.frame.style.visibility = "hidden";
};
/**
@@ -13780,8 +13259,8 @@ Popup.prototype.hide = function () {
* This class can store groups and properties specific for groups.
*/
Groups = function () {
- this.clear();
- this.defaultIndex = 0;
+ this.clear();
+ this.defaultIndex = 0;
};
@@ -13789,16 +13268,16 @@ Groups = function () {
* default constants for group colors
*/
Groups.DEFAULT = [
- {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
- {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
- {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red
- {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green
- {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
- {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
- {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange
- {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
- {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
- {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint
+ {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
+ {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
+ {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red
+ {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green
+ {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
+ {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
+ {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange
+ {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
+ {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
+ {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint
];
@@ -13806,17 +13285,17 @@ Groups.DEFAULT = [
* Clear all groups
*/
Groups.prototype.clear = function () {
- this.groups = {};
- this.groups.length = function()
- {
- var i = 0;
- for ( var p in this ) {
- if (this.hasOwnProperty(p)) {
- i++;
- }
- }
- return i;
+ this.groups = {};
+ this.groups.length = function()
+ {
+ var i = 0;
+ for ( var p in this ) {
+ if (this.hasOwnProperty(p)) {
+ i++;
+ }
}
+ return i;
+ }
};
@@ -13827,18 +13306,18 @@ Groups.prototype.clear = function () {
* @return {Object} group The created group, containing all group properties
*/
Groups.prototype.get = function (groupname) {
- var group = this.groups[groupname];
-
- if (group == undefined) {
- // create new group
- var index = this.defaultIndex % Groups.DEFAULT.length;
- this.defaultIndex++;
- group = {};
- group.color = Groups.DEFAULT[index];
- this.groups[groupname] = group;
- }
+ var group = this.groups[groupname];
+
+ if (group == undefined) {
+ // create new group
+ var index = this.defaultIndex % Groups.DEFAULT.length;
+ this.defaultIndex++;
+ group = {};
+ group.color = Groups.DEFAULT[index];
+ this.groups[groupname] = group;
+ }
- return group;
+ return group;
};
/**
@@ -13849,11 +13328,11 @@ Groups.prototype.get = function (groupname) {
* @return {Object} group The created group object
*/
Groups.prototype.add = function (groupname, style) {
- this.groups[groupname] = style;
- if (style.color) {
- style.color = Node.parseColor(style.color);
- }
- return style;
+ this.groups[groupname] = style;
+ if (style.color) {
+ style.color = Node.parseColor(style.color);
+ }
+ return style;
};
/**
@@ -13861,9 +13340,9 @@ Groups.prototype.add = function (groupname, style) {
* This class loads images and keeps them stored.
*/
Images = function () {
- this.images = {};
+ this.images = {};
- this.callback = undefined;
+ this.callback = undefined;
};
/**
@@ -13872,7 +13351,7 @@ Images = function () {
* @param {function} callback
*/
Images.prototype.setOnloadCallback = function(callback) {
- this.callback = callback;
+ this.callback = callback;
};
/**
@@ -13881,27 +13360,27 @@ Images.prototype.setOnloadCallback = function(callback) {
* @return {Image} img The image object
*/
Images.prototype.load = function(url) {
- var img = this.images[url];
- if (img == undefined) {
- // create the image
- var images = this;
- img = new Image();
- this.images[url] = img;
- img.onload = function() {
- if (images.callback) {
- images.callback(this);
- }
- };
- img.src = url;
- }
+ var img = this.images[url];
+ if (img == undefined) {
+ // create the image
+ var images = this;
+ img = new Image();
+ this.images[url] = img;
+ img.onload = function() {
+ if (images.callback) {
+ images.callback(this);
+ }
+ };
+ img.src = url;
+ }
- return img;
+ return img;
};
/**
* @constructor Graph
* Create a graph visualization, displaying nodes and edges.
- *
+ *
* @param {Element} container The DOM element in which the Graph will
* be created. Normally a div element.
* @param {Object} data An object containing parameters
@@ -13910,125 +13389,125 @@ Images.prototype.load = function(url) {
* @param {Object} options Options
*/
function Graph (container, data, options) {
- // create variables and set default values
- this.containerElement = container;
- this.width = '100%';
- this.height = '100%';
- this.refreshRate = 50; // milliseconds
- this.stabilize = true; // stabilize before displaying the graph
- this.selectable = true;
-
- // set constant values
- this.constants = {
- nodes: {
- radiusMin: 5,
- radiusMax: 20,
- radius: 5,
- distance: 100, // px
- shape: 'ellipse',
- image: undefined,
- widthMin: 16, // px
- widthMax: 64, // px
- fontColor: 'black',
- fontSize: 14, // px
- //fontFace: verdana,
- fontFace: 'arial',
- color: {
- border: '#2B7CE9',
- background: '#97C2FC',
- highlight: {
- border: '#2B7CE9',
- background: '#D2E5FF'
- }
- },
- borderColor: '#2B7CE9',
- backgroundColor: '#97C2FC',
- highlightColor: '#D2E5FF',
- group: undefined
- },
- edges: {
- widthMin: 1,
- widthMax: 15,
- width: 1,
- style: 'line',
- color: '#343434',
- fontColor: '#343434',
- fontSize: 14, // px
- fontFace: 'arial',
- //distance: 100, //px
- length: 100, // px
- dash: {
- length: 10,
- gap: 5,
- altLength: undefined
- }
- },
- minForce: 0.05,
- minVelocity: 0.02, // px/s
- maxIterations: 1000 // maximum number of iteration to stabilize
- };
-
- var graph = this;
- this.nodes = {}; // object with Node objects
- this.edges = {}; // object with Edge objects
- // TODO: create a counter to keep track on the number of nodes having values
- // TODO: create a counter to keep track on the number of nodes currently moving
- // TODO: create a counter to keep track on the number of edges having values
-
- this.nodesData = null; // A DataSet or DataView
- this.edgesData = null; // A DataSet or DataView
-
- // create event listeners used to subscribe on the DataSets of the nodes and edges
- var me = this;
- this.nodesListeners = {
- 'add': function (event, params) {
- me._addNodes(params.items);
- me.start();
- },
- 'update': function (event, params) {
- me._updateNodes(params.items);
- me.start();
- },
- 'remove': function (event, params) {
- me._removeNodes(params.items);
- me.start();
- }
- };
- this.edgesListeners = {
- 'add': function (event, params) {
- me._addEdges(params.items);
- me.start();
- },
- 'update': function (event, params) {
- me._updateEdges(params.items);
- me.start();
- },
- 'remove': function (event, params) {
- me._removeEdges(params.items);
- me.start();
- }
- };
+ // create variables and set default values
+ this.containerElement = container;
+ this.width = '100%';
+ this.height = '100%';
+ this.refreshRate = 50; // milliseconds
+ this.stabilize = true; // stabilize before displaying the graph
+ this.selectable = true;
+
+ // set constant values
+ this.constants = {
+ nodes: {
+ radiusMin: 5,
+ radiusMax: 20,
+ radius: 5,
+ distance: 100, // px
+ shape: 'ellipse',
+ image: undefined,
+ widthMin: 16, // px
+ widthMax: 64, // px
+ fontColor: 'black',
+ fontSize: 14, // px
+ //fontFace: verdana,
+ fontFace: 'arial',
+ color: {
+ border: '#2B7CE9',
+ background: '#97C2FC',
+ highlight: {
+ border: '#2B7CE9',
+ background: '#D2E5FF'
+ }
+ },
+ borderColor: '#2B7CE9',
+ backgroundColor: '#97C2FC',
+ highlightColor: '#D2E5FF',
+ group: undefined
+ },
+ edges: {
+ widthMin: 1,
+ widthMax: 15,
+ width: 1,
+ style: 'line',
+ color: '#343434',
+ fontColor: '#343434',
+ fontSize: 14, // px
+ fontFace: 'arial',
+ //distance: 100, //px
+ length: 100, // px
+ dash: {
+ length: 10,
+ gap: 5,
+ altLength: undefined
+ }
+ },
+ minForce: 0.05,
+ minVelocity: 0.02, // px/s
+ maxIterations: 1000 // maximum number of iteration to stabilize
+ };
+
+ var graph = this;
+ this.nodes = {}; // object with Node objects
+ this.edges = {}; // object with Edge objects
+ // TODO: create a counter to keep track on the number of nodes having values
+ // TODO: create a counter to keep track on the number of nodes currently moving
+ // TODO: create a counter to keep track on the number of edges having values
+
+ this.nodesData = null; // A DataSet or DataView
+ this.edgesData = null; // A DataSet or DataView
+
+ // create event listeners used to subscribe on the DataSets of the nodes and edges
+ var me = this;
+ this.nodesListeners = {
+ 'add': function (event, params) {
+ me._addNodes(params.items);
+ me.start();
+ },
+ 'update': function (event, params) {
+ me._updateNodes(params.items);
+ me.start();
+ },
+ 'remove': function (event, params) {
+ me._removeNodes(params.items);
+ me.start();
+ }
+ };
+ this.edgesListeners = {
+ 'add': function (event, params) {
+ me._addEdges(params.items);
+ me.start();
+ },
+ 'update': function (event, params) {
+ me._updateEdges(params.items);
+ me.start();
+ },
+ 'remove': function (event, params) {
+ me._removeEdges(params.items);
+ me.start();
+ }
+ };
- this.groups = new Groups(); // object with groups
- this.images = new Images(); // object with images
- this.images.setOnloadCallback(function () {
- graph._redraw();
- });
+ this.groups = new Groups(); // object with groups
+ this.images = new Images(); // object with images
+ this.images.setOnloadCallback(function () {
+ graph._redraw();
+ });
- // properties of the data
- this.moving = false; // True if any of the nodes have an undefined position
+ // properties of the data
+ this.moving = false; // True if any of the nodes have an undefined position
- this.selection = [];
- this.timer = undefined;
+ this.selection = [];
+ this.timer = undefined;
- // create a frame and canvas
- this._create();
+ // create a frame and canvas
+ this._create();
- // apply options
- this.setOptions(options);
+ // apply options
+ this.setOptions(options);
- // draw data
- this.setData(data);
+ // draw data
+ this.setData(data);
}
/**
@@ -14041,33 +13520,33 @@ function Graph (container, data, options) {
* {Options} [options] Object with options
*/
Graph.prototype.setData = function(data) {
- if (data && data.dot && (data.nodes || data.edges)) {
- throw new SyntaxError('Data must contain either parameter "dot" or ' +
- ' parameter pair "nodes" and "edges", but not both.');
- }
+ if (data && data.dot && (data.nodes || data.edges)) {
+ throw new SyntaxError('Data must contain either parameter "dot" or ' +
+ ' parameter pair "nodes" and "edges", but not both.');
+ }
- // set options
- this.setOptions(data && data.options);
+ // set options
+ this.setOptions(data && data.options);
- // set all data
- if (data && data.dot) {
- // parse DOT file
- if(data && data.dot) {
- var dotData = vis.util.DOTToGraph(data.dot);
- this.setData(dotData);
- return;
- }
- }
- else {
- this._setNodes(data && data.nodes);
- this._setEdges(data && data.edges);
+ // set all data
+ if (data && data.dot) {
+ // parse DOT file
+ if(data && data.dot) {
+ var dotData = vis.util.DOTToGraph(data.dot);
+ this.setData(dotData);
+ return;
}
+ }
+ else {
+ this._setNodes(data && data.nodes);
+ this._setEdges(data && data.edges);
+ }
- // find a stable position or start animating to a stable position
- if (this.stabilize) {
- this._doStabilize();
- }
- this.start();
+ // find a stable position or start animating to a stable position
+ if (this.stabilize) {
+ this._doStabilize();
+ }
+ this.start();
};
/**
@@ -14075,77 +13554,77 @@ Graph.prototype.setData = function(data) {
* @param {Object} options
*/
Graph.prototype.setOptions = function (options) {
- if (options) {
- // retrieve parameter values
- if (options.width != undefined) {this.width = options.width;}
- if (options.height != undefined) {this.height = options.height;}
- if (options.stabilize != undefined) {this.stabilize = options.stabilize;}
- if (options.selectable != undefined) {this.selectable = options.selectable;}
-
- // TODO: work out these options and document them
- if (options.edges) {
- for (var prop in options.edges) {
- if (options.edges.hasOwnProperty(prop)) {
- this.constants.edges[prop] = options.edges[prop];
- }
- }
+ if (options) {
+ // retrieve parameter values
+ if (options.width != undefined) {this.width = options.width;}
+ if (options.height != undefined) {this.height = options.height;}
+ if (options.stabilize != undefined) {this.stabilize = options.stabilize;}
+ if (options.selectable != undefined) {this.selectable = options.selectable;}
+
+ // TODO: work out these options and document them
+ if (options.edges) {
+ for (var prop in options.edges) {
+ if (options.edges.hasOwnProperty(prop)) {
+ this.constants.edges[prop] = options.edges[prop];
+ }
+ }
- if (options.edges.length != undefined &&
- options.nodes && options.nodes.distance == undefined) {
- this.constants.edges.length = options.edges.length;
- this.constants.nodes.distance = options.edges.length * 1.25;
- }
+ if (options.edges.length != undefined &&
+ options.nodes && options.nodes.distance == undefined) {
+ this.constants.edges.length = options.edges.length;
+ this.constants.nodes.distance = options.edges.length * 1.25;
+ }
- if (!options.edges.fontColor) {
- this.constants.edges.fontColor = options.edges.color;
- }
+ if (!options.edges.fontColor) {
+ this.constants.edges.fontColor = options.edges.color;
+ }
- // Added to support dashed lines
- // David Jordan
- // 2012-08-08
- if (options.edges.dash) {
- if (options.edges.dash.length != undefined) {
- this.constants.edges.dash.length = options.edges.dash.length;
- }
- if (options.edges.dash.gap != undefined) {
- this.constants.edges.dash.gap = options.edges.dash.gap;
- }
- if (options.edges.dash.altLength != undefined) {
- this.constants.edges.dash.altLength = options.edges.dash.altLength;
- }
- }
+ // Added to support dashed lines
+ // David Jordan
+ // 2012-08-08
+ if (options.edges.dash) {
+ if (options.edges.dash.length != undefined) {
+ this.constants.edges.dash.length = options.edges.dash.length;
+ }
+ if (options.edges.dash.gap != undefined) {
+ this.constants.edges.dash.gap = options.edges.dash.gap;
}
+ if (options.edges.dash.altLength != undefined) {
+ this.constants.edges.dash.altLength = options.edges.dash.altLength;
+ }
+ }
+ }
- if (options.nodes) {
- for (prop in options.nodes) {
- if (options.nodes.hasOwnProperty(prop)) {
- this.constants.nodes[prop] = options.nodes[prop];
- }
- }
+ if (options.nodes) {
+ for (prop in options.nodes) {
+ if (options.nodes.hasOwnProperty(prop)) {
+ this.constants.nodes[prop] = options.nodes[prop];
+ }
+ }
- if (options.nodes.color) {
- this.constants.nodes.color = Node.parseColor(options.nodes.color);
- }
+ if (options.nodes.color) {
+ this.constants.nodes.color = Node.parseColor(options.nodes.color);
+ }
- /*
- if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin;
- if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax;
- */
- }
+ /*
+ if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin;
+ if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax;
+ */
+ }
- if (options.groups) {
- for (var groupname in options.groups) {
- if (options.groups.hasOwnProperty(groupname)) {
- var group = options.groups[groupname];
- this.groups.add(groupname, group);
- }
- }
+ if (options.groups) {
+ for (var groupname in options.groups) {
+ if (options.groups.hasOwnProperty(groupname)) {
+ var group = options.groups[groupname];
+ this.groups.add(groupname, group);
}
+ }
}
+ }
- this.setSize(this.width, this.height);
- this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
- this._setScale(1);
+ this.setSize(this.width, this.height);
+ this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
+ this._setScale(1);
};
/**
@@ -14155,7 +13634,7 @@ Graph.prototype.setOptions = function (options) {
* @private
*/
Graph.prototype._trigger = function (event, params) {
- events.trigger(this, event, params);
+ events.trigger(this, event, params);
};
@@ -14167,48 +13646,48 @@ Graph.prototype._trigger = function (event, params) {
* @private
*/
Graph.prototype._create = function () {
- // remove all elements from the container element.
- while (this.containerElement.hasChildNodes()) {
- this.containerElement.removeChild(this.containerElement.firstChild);
- }
-
- this.frame = document.createElement('div');
- this.frame.className = 'graph-frame';
- this.frame.style.position = 'relative';
- this.frame.style.overflow = 'hidden';
+ // remove all elements from the container element.
+ while (this.containerElement.hasChildNodes()) {
+ this.containerElement.removeChild(this.containerElement.firstChild);
+ }
- // create the graph canvas (HTML canvas element)
- this.frame.canvas = document.createElement( 'canvas' );
- this.frame.canvas.style.position = 'relative';
- this.frame.appendChild(this.frame.canvas);
- if (!this.frame.canvas.getContext) {
- var noCanvas = document.createElement( 'DIV' );
- noCanvas.style.color = 'red';
- noCanvas.style.fontWeight = 'bold' ;
- noCanvas.style.padding = '10px';
- noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
- this.frame.canvas.appendChild(noCanvas);
- }
+ this.frame = document.createElement('div');
+ this.frame.className = 'graph-frame';
+ this.frame.style.position = 'relative';
+ this.frame.style.overflow = 'hidden';
+
+ // create the graph canvas (HTML canvas element)
+ this.frame.canvas = document.createElement( 'canvas' );
+ this.frame.canvas.style.position = 'relative';
+ this.frame.appendChild(this.frame.canvas);
+ if (!this.frame.canvas.getContext) {
+ var noCanvas = document.createElement( 'DIV' );
+ noCanvas.style.color = 'red';
+ noCanvas.style.fontWeight = 'bold' ;
+ noCanvas.style.padding = '10px';
+ noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
+ this.frame.canvas.appendChild(noCanvas);
+ }
- var me = this;
- this.drag = {};
- this.pinch = {};
- this.hammer = Hammer(this.frame.canvas, {
- prevent_default: true
- });
- this.hammer.on('tap', me._onTap.bind(me) );
- this.hammer.on('hold', me._onHold.bind(me) );
- this.hammer.on('pinch', me._onPinch.bind(me) );
- this.hammer.on('touch', me._onTouch.bind(me) );
- this.hammer.on('dragstart', me._onDragStart.bind(me) );
- this.hammer.on('drag', me._onDrag.bind(me) );
- this.hammer.on('dragend', me._onDragEnd.bind(me) );
- this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
- this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
- this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
+ var me = this;
+ this.drag = {};
+ this.pinch = {};
+ this.hammer = Hammer(this.frame.canvas, {
+ prevent_default: true
+ });
+ this.hammer.on('tap', me._onTap.bind(me) );
+ this.hammer.on('hold', me._onHold.bind(me) );
+ this.hammer.on('pinch', me._onPinch.bind(me) );
+ this.hammer.on('touch', me._onTouch.bind(me) );
+ this.hammer.on('dragstart', me._onDragStart.bind(me) );
+ this.hammer.on('drag', me._onDrag.bind(me) );
+ this.hammer.on('dragend', me._onDragEnd.bind(me) );
+ this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
+ this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
+ this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
- // add the frame to the container element
- this.containerElement.appendChild(this.frame);
+ // add the frame to the container element
+ this.containerElement.appendChild(this.frame);
};
/**
@@ -14218,21 +13697,21 @@ Graph.prototype._create = function () {
* @private
*/
Graph.prototype._getNodeAt = function (pointer) {
- var x = this._canvasToX(pointer.x);
- var y = this._canvasToY(pointer.y);
-
- var obj = {
- left: x,
- top: y,
- right: x,
- bottom: y
- };
+ var x = this._canvasToX(pointer.x);
+ var y = this._canvasToY(pointer.y);
- // if there are overlapping nodes, select the last one, this is the
- // one which is drawn on top of the others
- var overlappingNodes = this._getNodesOverlappingWith(obj);
- return (overlappingNodes.length > 0) ?
- overlappingNodes[overlappingNodes.length - 1] : null;
+ var obj = {
+ left: x,
+ top: y,
+ right: x,
+ bottom: y
+ };
+
+ // if there are overlapping nodes, select the last one, this is the
+ // one which is drawn on top of the others
+ var overlappingNodes = this._getNodesOverlappingWith(obj);
+ return (overlappingNodes.length > 0) ?
+ overlappingNodes[overlappingNodes.length - 1] : null;
};
/**
@@ -14242,10 +13721,10 @@ Graph.prototype._getNodeAt = function (pointer) {
* @private
*/
Graph.prototype._getPointer = function (touch) {
- return {
- x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas),
- y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas)
- };
+ return {
+ x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas),
+ y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas)
+ };
};
/**
@@ -14254,9 +13733,9 @@ Graph.prototype._getPointer = function (touch) {
* @private
*/
Graph.prototype._onTouch = function (event) {
- this.drag.pointer = this._getPointer(event.gesture.touches[0]);
- this.drag.pinched = false;
- this.pinch.scale = this._getScale();
+ this.drag.pointer = this._getPointer(event.gesture.touches[0]);
+ this.drag.pinched = false;
+ this.pinch.scale = this._getScale();
};
/**
@@ -14264,44 +13743,44 @@ Graph.prototype._onTouch = function (event) {
* @private
*/
Graph.prototype._onDragStart = function () {
- var drag = this.drag;
-
- drag.selection = [];
- drag.translation = this._getTranslation();
- drag.nodeId = this._getNodeAt(drag.pointer);
- // note: drag.pointer is set in _onTouch to get the initial touch location
+ var drag = this.drag;
- var node = this.nodes[drag.nodeId];
- if (node) {
- // select the clicked node if not yet selected
- if (!node.isSelected()) {
- this._selectNodes([drag.nodeId]);
- }
-
- // create an array with the selected nodes and their original location and status
- var me = this;
- this.selection.forEach(function (id) {
- var node = me.nodes[id];
- if (node) {
- var s = {
- id: id,
- node: node,
-
- // store original x, y, xFixed and yFixed, make the node temporarily Fixed
- x: node.x,
- y: node.y,
- xFixed: node.xFixed,
- yFixed: node.yFixed
- };
-
- node.xFixed = true;
- node.yFixed = true;
-
- drag.selection.push(s);
- }
- });
+ drag.selection = [];
+ drag.translation = this._getTranslation();
+ drag.nodeId = this._getNodeAt(drag.pointer);
+ // note: drag.pointer is set in _onTouch to get the initial touch location
+ var node = this.nodes[drag.nodeId];
+ if (node) {
+ // select the clicked node if not yet selected
+ if (!node.isSelected()) {
+ this._selectNodes([drag.nodeId]);
}
+
+ // create an array with the selected nodes and their original location and status
+ var me = this;
+ this.selection.forEach(function (id) {
+ var node = me.nodes[id];
+ if (node) {
+ var s = {
+ id: id,
+ node: node,
+
+ // store original x, y, xFixed and yFixed, make the node temporarily Fixed
+ x: node.x,
+ y: node.y,
+ xFixed: node.xFixed,
+ yFixed: node.yFixed
+ };
+
+ node.xFixed = true;
+ node.yFixed = true;
+
+ drag.selection.push(s);
+ }
+ });
+
+ }
};
/**
@@ -14309,51 +13788,51 @@ Graph.prototype._onDragStart = function () {
* @private
*/
Graph.prototype._onDrag = function (event) {
- if (this.drag.pinched) {
- return;
- }
+ if (this.drag.pinched) {
+ return;
+ }
- var pointer = this._getPointer(event.gesture.touches[0]);
+ var pointer = this._getPointer(event.gesture.touches[0]);
- var me = this,
- drag = this.drag,
- selection = drag.selection;
- if (selection && selection.length) {
- // calculate delta's and new location
- var deltaX = pointer.x - drag.pointer.x,
- deltaY = pointer.y - drag.pointer.y;
+ var me = this,
+ drag = this.drag,
+ selection = drag.selection;
+ if (selection && selection.length) {
+ // calculate delta's and new location
+ var deltaX = pointer.x - drag.pointer.x,
+ deltaY = pointer.y - drag.pointer.y;
- // update position of all selected nodes
- selection.forEach(function (s) {
- var node = s.node;
+ // update position of all selected nodes
+ selection.forEach(function (s) {
+ var node = s.node;
- if (!s.xFixed) {
- node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
- }
+ if (!s.xFixed) {
+ node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
+ }
- if (!s.yFixed) {
- node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
- }
- });
+ if (!s.yFixed) {
+ node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
+ }
+ });
- // start animation if not yet running
- if (!this.moving) {
- this.moving = true;
- this.start();
- }
+ // start animation if not yet running
+ if (!this.moving) {
+ this.moving = true;
+ this.start();
}
- else {
- // move the graph
- var diffX = pointer.x - this.drag.pointer.x;
- var diffY = pointer.y - this.drag.pointer.y;
-
- this._setTranslation(
- this.drag.translation.x + diffX,
- this.drag.translation.y + diffY);
- this._redraw();
+ }
+ else {
+ // move the graph
+ var diffX = pointer.x - this.drag.pointer.x;
+ var diffY = pointer.y - this.drag.pointer.y;
+
+ this._setTranslation(
+ this.drag.translation.x + diffX,
+ this.drag.translation.y + diffY);
+ this._redraw();
- this.moved = true;
- }
+ this.moved = true;
+ }
};
/**
@@ -14361,14 +13840,14 @@ Graph.prototype._onDrag = function (event) {
* @private
*/
Graph.prototype._onDragEnd = function () {
- var selection = this.drag.selection;
- if (selection) {
- selection.forEach(function (s) {
- // restore original xFixed and yFixed
- s.node.xFixed = s.xFixed;
- s.node.yFixed = s.yFixed;
- });
- }
+ var selection = this.drag.selection;
+ if (selection) {
+ selection.forEach(function (s) {
+ // restore original xFixed and yFixed
+ s.node.xFixed = s.xFixed;
+ s.node.yFixed = s.yFixed;
+ });
+ }
};
/**
@@ -14376,23 +13855,23 @@ Graph.prototype._onDragEnd = function () {
* @private
*/
Graph.prototype._onTap = function (event) {
- var pointer = this._getPointer(event.gesture.touches[0]);
+ var pointer = this._getPointer(event.gesture.touches[0]);
- var nodeId = this._getNodeAt(pointer);
- var node = this.nodes[nodeId];
- if (node) {
- // select this node
- this._selectNodes([nodeId]);
+ var nodeId = this._getNodeAt(pointer);
+ var node = this.nodes[nodeId];
+ if (node) {
+ // select this node
+ this._selectNodes([nodeId]);
- if (!this.moving) {
- this._redraw();
- }
- }
- else {
- // remove selection
- this._unselectNodes();
- this._redraw();
+ if (!this.moving) {
+ this._redraw();
}
+ }
+ else {
+ // remove selection
+ this._unselectNodes();
+ this._redraw();
+ }
};
/**
@@ -14400,26 +13879,26 @@ Graph.prototype._onTap = function (event) {
* @private
*/
Graph.prototype._onHold = function (event) {
- var pointer = this._getPointer(event.gesture.touches[0]);
- var nodeId = this._getNodeAt(pointer);
- var node = this.nodes[nodeId];
- if (node) {
- if (!node.isSelected()) {
- // select this node, keep previous selection
- var append = true;
- this._selectNodes([nodeId], append);
- }
- else {
- this._unselectNodes([nodeId]);
- }
-
- if (!this.moving) {
- this._redraw();
- }
+ var pointer = this._getPointer(event.gesture.touches[0]);
+ var nodeId = this._getNodeAt(pointer);
+ var node = this.nodes[nodeId];
+ if (node) {
+ if (!node.isSelected()) {
+ // select this node, keep previous selection
+ var append = true;
+ this._selectNodes([nodeId], append);
}
else {
- // Do nothing
+ this._unselectNodes([nodeId]);
+ }
+
+ if (!this.moving) {
+ this._redraw();
}
+ }
+ else {
+ // Do nothing
+ }
};
/**
@@ -14428,16 +13907,16 @@ Graph.prototype._onHold = function (event) {
* @private
*/
Graph.prototype._onPinch = function (event) {
- var pointer = this._getPointer(event.gesture.center);
+ var pointer = this._getPointer(event.gesture.center);
- this.drag.pinched = true;
- if (!('scale' in this.pinch)) {
- this.pinch.scale = 1;
- }
+ this.drag.pinched = true;
+ if (!('scale' in this.pinch)) {
+ this.pinch.scale = 1;
+ }
- // TODO: enable moving while pinching?
- var scale = this.pinch.scale * event.gesture.scale;
- this._zoom(scale, pointer)
+ // TODO: enable moving while pinching?
+ var scale = this.pinch.scale * event.gesture.scale;
+ this._zoom(scale, pointer)
};
/**
@@ -14448,24 +13927,24 @@ Graph.prototype._onPinch = function (event) {
* @private
*/
Graph.prototype._zoom = function(scale, pointer) {
- var scaleOld = this._getScale();
- if (scale < 0.01) {
- scale = 0.01;
- }
- if (scale > 10) {
- scale = 10;
- }
+ var scaleOld = this._getScale();
+ if (scale < 0.01) {
+ scale = 0.01;
+ }
+ if (scale > 10) {
+ scale = 10;
+ }
- var translation = this._getTranslation();
- var scaleFrac = scale / scaleOld;
- var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
- var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
+ var translation = this._getTranslation();
+ var scaleFrac = scale / scaleOld;
+ var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
+ var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
- this._setScale(scale);
- this._setTranslation(tx, ty);
- this._redraw();
+ this._setScale(scale);
+ this._setTranslation(tx, ty);
+ this._redraw();
- return scale;
+ return scale;
};
/**
@@ -14476,45 +13955,45 @@ Graph.prototype._zoom = function(scale, pointer) {
* @private
*/
Graph.prototype._onMouseWheel = function(event) {
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta/120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail/3;
- }
+ // retrieve delta
+ var delta = 0;
+ if (event.wheelDelta) { /* IE/Opera. */
+ delta = event.wheelDelta/120;
+ } else if (event.detail) { /* Mozilla case. */
+ // In Mozilla, sign of delta is different than in IE.
+ // Also, delta is multiple of 3.
+ delta = -event.detail/3;
+ }
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- if (!('mouswheelScale' in this.pinch)) {
- this.pinch.mouswheelScale = 1;
- }
+ // If delta is nonzero, handle it.
+ // Basically, delta is now positive if wheel was scrolled up,
+ // and negative, if wheel was scrolled down.
+ if (delta) {
+ if (!('mouswheelScale' in this.pinch)) {
+ this.pinch.mouswheelScale = 1;
+ }
- // calculate the new scale
- var scale = this.pinch.mouswheelScale;
- var zoom = delta / 10;
- if (delta < 0) {
- zoom = zoom / (1 - zoom);
- }
- scale *= (1 + zoom);
+ // calculate the new scale
+ var scale = this.pinch.mouswheelScale;
+ var zoom = delta / 10;
+ if (delta < 0) {
+ zoom = zoom / (1 - zoom);
+ }
+ scale *= (1 + zoom);
- // calculate the pointer location
- var gesture = Hammer.event.collectEventData(this, 'scroll', event);
- var pointer = this._getPointer(gesture.center);
+ // calculate the pointer location
+ var gesture = Hammer.event.collectEventData(this, 'scroll', event);
+ var pointer = this._getPointer(gesture.center);
- // apply the new scale
- scale = this._zoom(scale, pointer);
+ // apply the new scale
+ scale = this._zoom(scale, pointer);
- // store the new, applied scale
- this.pinch.mouswheelScale = scale;
- }
+ // store the new, applied scale
+ this.pinch.mouswheelScale = scale;
+ }
- // Prevent default actions caused by mouse wheel.
- event.preventDefault();
+ // Prevent default actions caused by mouse wheel.
+ event.preventDefault();
};
@@ -14524,26 +14003,26 @@ Graph.prototype._onMouseWheel = function(event) {
* @private
*/
Graph.prototype._onMouseMoveTitle = function (event) {
- var gesture = Hammer.event.collectEventData(this, 'mousemove', event);
- var pointer = this._getPointer(gesture.center);
+ var gesture = Hammer.event.collectEventData(this, 'mousemove', event);
+ var pointer = this._getPointer(gesture.center);
- // check if the previously selected node is still selected
- if (this.popupNode) {
- this._checkHidePopup(pointer);
- }
+ // check if the previously selected node is still selected
+ if (this.popupNode) {
+ this._checkHidePopup(pointer);
+ }
- // start a timeout that will check if the mouse is positioned above
- // an element
- var me = this;
- var checkShow = function() {
- me._checkShowPopup(pointer);
- };
- if (this.popupTimer) {
- clearInterval(this.popupTimer); // stop any running timer
- }
- if (!this.leftButtonDown) {
- this.popupTimer = setTimeout(checkShow, 300);
- }
+ // start a timeout that will check if the mouse is positioned above
+ // an element
+ var me = this;
+ var checkShow = function() {
+ me._checkShowPopup(pointer);
+ };
+ if (this.popupTimer) {
+ clearInterval(this.popupTimer); // stop any running timer
+ }
+ if (!this.leftButtonDown) {
+ this.popupTimer = setTimeout(checkShow, 300);
+ }
};
/**
@@ -14555,66 +14034,66 @@ Graph.prototype._onMouseMoveTitle = function (event) {
* @private
*/
Graph.prototype._checkShowPopup = function (pointer) {
- var obj = {
- left: this._canvasToX(pointer.x),
- top: this._canvasToY(pointer.y),
- right: this._canvasToX(pointer.x),
- bottom: this._canvasToY(pointer.y)
- };
-
- var id;
- var lastPopupNode = this.popupNode;
-
- if (this.popupNode == undefined) {
- // search the nodes for overlap, select the top one in case of multiple nodes
- var nodes = this.nodes;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var node = nodes[id];
- if (node.getTitle() != undefined && node.isOverlappingWith(obj)) {
- this.popupNode = node;
- break;
- }
- }
+ var obj = {
+ left: this._canvasToX(pointer.x),
+ top: this._canvasToY(pointer.y),
+ right: this._canvasToX(pointer.x),
+ bottom: this._canvasToY(pointer.y)
+ };
+
+ var id;
+ var lastPopupNode = this.popupNode;
+
+ if (this.popupNode == undefined) {
+ // search the nodes for overlap, select the top one in case of multiple nodes
+ var nodes = this.nodes;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var node = nodes[id];
+ if (node.getTitle() != undefined && node.isOverlappingWith(obj)) {
+ this.popupNode = node;
+ break;
}
+ }
}
+ }
- if (this.popupNode == undefined) {
- // search the edges for overlap
- var edges = this.edges;
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected && (edge.getTitle() != undefined) &&
- edge.isOverlappingWith(obj)) {
- this.popupNode = edge;
- break;
- }
- }
+ if (this.popupNode == undefined) {
+ // search the edges for overlap
+ var edges = this.edges;
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected && (edge.getTitle() != undefined) &&
+ edge.isOverlappingWith(obj)) {
+ this.popupNode = edge;
+ break;
}
+ }
}
+ }
- if (this.popupNode) {
- // show popup message window
- if (this.popupNode != lastPopupNode) {
- var me = this;
- if (!me.popup) {
- me.popup = new Popup(me.frame);
- }
+ if (this.popupNode) {
+ // show popup message window
+ if (this.popupNode != lastPopupNode) {
+ var me = this;
+ if (!me.popup) {
+ me.popup = new Popup(me.frame);
+ }
- // adjust a small offset such that the mouse cursor is located in the
- // bottom left location of the popup, and you can easily move over the
- // popup area
- me.popup.setPosition(pointer.x - 3, pointer.y - 3);
- me.popup.setText(me.popupNode.getTitle());
- me.popup.show();
- }
+ // adjust a small offset such that the mouse cursor is located in the
+ // bottom left location of the popup, and you can easily move over the
+ // popup area
+ me.popup.setPosition(pointer.x - 3, pointer.y - 3);
+ me.popup.setText(me.popupNode.getTitle());
+ me.popup.show();
}
- else {
- if (this.popup) {
- this.popup.hide();
- }
+ }
+ else {
+ if (this.popup) {
+ this.popup.hide();
}
+ }
};
/**
@@ -14624,12 +14103,12 @@ Graph.prototype._checkShowPopup = function (pointer) {
* @private
*/
Graph.prototype._checkHidePopup = function (pointer) {
- if (!this.popupNode || !this._getNodeAt(pointer) ) {
- this.popupNode = undefined;
- if (this.popup) {
- this.popup.hide();
- }
+ if (!this.popupNode || !this._getNodeAt(pointer) ) {
+ this.popupNode = undefined;
+ if (this.popup) {
+ this.popup.hide();
}
+ }
};
/**
@@ -14643,43 +14122,43 @@ Graph.prototype._checkHidePopup = function (pointer) {
* @private
*/
Graph.prototype._unselectNodes = function(selection, triggerSelect) {
- var changed = false;
- var i, iMax, id;
-
- if (selection) {
- // remove provided selections
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- id = selection[i];
- this.nodes[id].unselect();
-
- var j = 0;
- while (j < this.selection.length) {
- if (this.selection[j] == id) {
- this.selection.splice(j, 1);
- changed = true;
- }
- else {
- j++;
- }
- }
+ var changed = false;
+ var i, iMax, id;
+
+ if (selection) {
+ // remove provided selections
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ id = selection[i];
+ this.nodes[id].unselect();
+
+ var j = 0;
+ while (j < this.selection.length) {
+ if (this.selection[j] == id) {
+ this.selection.splice(j, 1);
+ changed = true;
}
- }
- else if (this.selection && this.selection.length) {
- // remove all selections
- for (i = 0, iMax = this.selection.length; i < iMax; i++) {
- id = this.selection[i];
- this.nodes[id].unselect();
- changed = true;
+ else {
+ j++;
}
- this.selection = [];
+ }
}
-
- if (changed && (triggerSelect == true || triggerSelect == undefined)) {
- // fire the select event
- this._trigger('select');
+ }
+ else if (this.selection && this.selection.length) {
+ // remove all selections
+ for (i = 0, iMax = this.selection.length; i < iMax; i++) {
+ id = this.selection[i];
+ this.nodes[id].unselect();
+ changed = true;
}
+ this.selection = [];
+ }
- return changed;
+ if (changed && (triggerSelect == true || triggerSelect == undefined)) {
+ // fire the select event
+ this._trigger('select');
+ }
+
+ return changed;
};
/**
@@ -14691,51 +14170,51 @@ Graph.prototype._unselectNodes = function(selection, triggerSelect) {
* @private
*/
Graph.prototype._selectNodes = function(selection, append) {
- var changed = false;
- var i, iMax;
+ var changed = false;
+ var i, iMax;
- // TODO: the selectNodes method is a little messy, rework this
+ // TODO: the selectNodes method is a little messy, rework this
- // check if the current selection equals the desired selection
- var selectionAlreadyThere = true;
- if (selection.length != this.selection.length) {
+ // check if the current selection equals the desired selection
+ var selectionAlreadyThere = true;
+ if (selection.length != this.selection.length) {
+ selectionAlreadyThere = false;
+ }
+ else {
+ for (i = 0, iMax = Math.min(selection.length, this.selection.length); i < iMax; i++) {
+ if (selection[i] != this.selection[i]) {
selectionAlreadyThere = false;
+ break;
+ }
}
- else {
- for (i = 0, iMax = Math.min(selection.length, this.selection.length); i < iMax; i++) {
- if (selection[i] != this.selection[i]) {
- selectionAlreadyThere = false;
- break;
- }
- }
- }
- if (selectionAlreadyThere) {
- return changed;
- }
+ }
+ if (selectionAlreadyThere) {
+ return changed;
+ }
- if (append == undefined || append == false) {
- // first deselect any selected node
- var triggerSelect = false;
- changed = this._unselectNodes(undefined, triggerSelect);
- }
+ if (append == undefined || append == false) {
+ // first deselect any selected node
+ var triggerSelect = false;
+ changed = this._unselectNodes(undefined, triggerSelect);
+ }
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- // add each of the new selections, but only when they are not duplicate
- var id = selection[i];
- var isDuplicate = (this.selection.indexOf(id) != -1);
- if (!isDuplicate) {
- this.nodes[id].select();
- this.selection.push(id);
- changed = true;
- }
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ // add each of the new selections, but only when they are not duplicate
+ var id = selection[i];
+ var isDuplicate = (this.selection.indexOf(id) != -1);
+ if (!isDuplicate) {
+ this.nodes[id].select();
+ this.selection.push(id);
+ changed = true;
}
+ }
- if (changed) {
- // fire the select event
- this._trigger('select');
- }
+ if (changed) {
+ // fire the select event
+ this._trigger('select');
+ }
- return changed;
+ return changed;
};
/**
@@ -14745,18 +14224,18 @@ Graph.prototype._selectNodes = function(selection, append) {
* @private
*/
Graph.prototype._getNodesOverlappingWith = function (obj) {
- var nodes = this.nodes,
- overlappingNodes = [];
+ var nodes = this.nodes,
+ overlappingNodes = [];
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- if (nodes[id].isOverlappingWith(obj)) {
- overlappingNodes.push(id);
- }
- }
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ if (nodes[id].isOverlappingWith(obj)) {
+ overlappingNodes.push(id);
+ }
}
+ }
- return overlappingNodes;
+ return overlappingNodes;
};
/**
@@ -14765,7 +14244,7 @@ Graph.prototype._getNodesOverlappingWith = function (obj) {
* selected nodes.
*/
Graph.prototype.getSelection = function() {
- return this.selection.concat([]);
+ return this.selection.concat([]);
};
/**
@@ -14774,31 +14253,31 @@ Graph.prototype.getSelection = function() {
* selected nodes.
*/
Graph.prototype.setSelection = function(selection) {
- var i, iMax, id;
+ var i, iMax, id;
- if (!selection || (selection.length == undefined))
- throw 'Selection must be an array with ids';
+ if (!selection || (selection.length == undefined))
+ throw 'Selection must be an array with ids';
- // first unselect any selected node
- for (i = 0, iMax = this.selection.length; i < iMax; i++) {
- id = this.selection[i];
- this.nodes[id].unselect();
- }
+ // first unselect any selected node
+ for (i = 0, iMax = this.selection.length; i < iMax; i++) {
+ id = this.selection[i];
+ this.nodes[id].unselect();
+ }
- this.selection = [];
+ this.selection = [];
- for (i = 0, iMax = selection.length; i < iMax; i++) {
- id = selection[i];
+ for (i = 0, iMax = selection.length; i < iMax; i++) {
+ id = selection[i];
- var node = this.nodes[id];
- if (!node) {
- throw new RangeError('Node with id "' + id + '" not found');
- }
- node.select();
- this.selection.push(id);
+ var node = this.nodes[id];
+ if (!node) {
+ throw new RangeError('Node with id "' + id + '" not found');
}
+ node.select();
+ this.selection.push(id);
+ }
- this.redraw();
+ this.redraw();
};
/**
@@ -14806,16 +14285,16 @@ Graph.prototype.setSelection = function(selection) {
* @private
*/
Graph.prototype._updateSelection = function () {
- var i = 0;
- while (i < this.selection.length) {
- var id = this.selection[i];
- if (!this.nodes[id]) {
- this.selection.splice(i, 1);
- }
- else {
- i++;
- }
+ var i = 0;
+ while (i < this.selection.length) {
+ var id = this.selection[i];
+ if (!this.nodes[id]) {
+ this.selection.splice(i, 1);
+ }
+ else {
+ i++;
}
+ }
};
/**
@@ -14827,74 +14306,74 @@ Graph.prototype._updateSelection = function () {
* @private
*/
Graph.prototype._getConnectionCount = function(level) {
- if (level == undefined) {
- level = 1;
- }
-
- // get the nodes connected to given nodes
- function getConnectedNodes(nodes) {
- var connectedNodes = [];
-
- for (var j = 0, jMax = nodes.length; j < jMax; j++) {
- var node = nodes[j];
-
- // find all nodes connected to this node
- var edges = node.edges;
- for (var i = 0, iMax = edges.length; i < iMax; i++) {
- var edge = edges[i];
- var other = null;
-
- // check if connected
- if (edge.from == node)
- other = edge.to;
- else if (edge.to == node)
- other = edge.from;
-
- // check if the other node is not already in the list with nodes
- var k, kMax;
- if (other) {
- for (k = 0, kMax = nodes.length; k < kMax; k++) {
- if (nodes[k] == other) {
- other = null;
- break;
- }
- }
- }
- if (other) {
- for (k = 0, kMax = connectedNodes.length; k < kMax; k++) {
- if (connectedNodes[k] == other) {
- other = null;
- break;
- }
- }
- }
+ if (level == undefined) {
+ level = 1;
+ }
- if (other)
- connectedNodes.push(other);
- }
- }
+ // get the nodes connected to given nodes
+ function getConnectedNodes(nodes) {
+ var connectedNodes = [];
- return connectedNodes;
- }
+ for (var j = 0, jMax = nodes.length; j < jMax; j++) {
+ var node = nodes[j];
- var connections = [];
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var c = [nodes[id]];
- for (var l = 0; l < level; l++) {
- c = c.concat(getConnectedNodes(c));
+ // find all nodes connected to this node
+ var edges = node.edges;
+ for (var i = 0, iMax = edges.length; i < iMax; i++) {
+ var edge = edges[i];
+ var other = null;
+
+ // check if connected
+ if (edge.from == node)
+ other = edge.to;
+ else if (edge.to == node)
+ other = edge.from;
+
+ // check if the other node is not already in the list with nodes
+ var k, kMax;
+ if (other) {
+ for (k = 0, kMax = nodes.length; k < kMax; k++) {
+ if (nodes[k] == other) {
+ other = null;
+ break;
+ }
+ }
+ }
+ if (other) {
+ for (k = 0, kMax = connectedNodes.length; k < kMax; k++) {
+ if (connectedNodes[k] == other) {
+ other = null;
+ break;
}
- connections.push(c);
+ }
}
+
+ if (other)
+ connectedNodes.push(other);
+ }
}
- var hubs = [];
- for (var i = 0, len = connections.length; i < len; i++) {
- hubs.push(connections[i].length);
+ return connectedNodes;
+ }
+
+ var connections = [];
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var c = [nodes[id]];
+ for (var l = 0; l < level; l++) {
+ c = c.concat(getConnectedNodes(c));
+ }
+ connections.push(c);
}
+ }
+
+ var hubs = [];
+ for (var i = 0, len = connections.length; i < len; i++) {
+ hubs.push(connections[i].length);
+ }
- return hubs;
+ return hubs;
};
@@ -14906,14 +14385,14 @@ Graph.prototype._getConnectionCount = function(level) {
* or '30%')
*/
Graph.prototype.setSize = function(width, height) {
- this.frame.style.width = width;
- this.frame.style.height = height;
+ this.frame.style.width = width;
+ this.frame.style.height = height;
- this.frame.canvas.style.width = '100%';
- this.frame.canvas.style.height = '100%';
+ this.frame.canvas.style.width = '100%';
+ this.frame.canvas.style.height = '100%';
- this.frame.canvas.width = this.frame.canvas.clientWidth;
- this.frame.canvas.height = this.frame.canvas.clientHeight;
+ this.frame.canvas.width = this.frame.canvas.clientWidth;
+ this.frame.canvas.height = this.frame.canvas.clientHeight;
};
/**
@@ -14922,45 +14401,45 @@ Graph.prototype.setSize = function(width, height) {
* @private
*/
Graph.prototype._setNodes = function(nodes) {
- var oldNodesData = this.nodesData;
+ var oldNodesData = this.nodesData;
- if (nodes instanceof DataSet || nodes instanceof DataView) {
- this.nodesData = nodes;
- }
- else if (nodes instanceof Array) {
- this.nodesData = new DataSet();
- this.nodesData.add(nodes);
- }
- else if (!nodes) {
- this.nodesData = new DataSet();
- }
- else {
- throw new TypeError('Array or DataSet expected');
- }
+ if (nodes instanceof DataSet || nodes instanceof DataView) {
+ this.nodesData = nodes;
+ }
+ else if (nodes instanceof Array) {
+ this.nodesData = new DataSet();
+ this.nodesData.add(nodes);
+ }
+ else if (!nodes) {
+ this.nodesData = new DataSet();
+ }
+ else {
+ throw new TypeError('Array or DataSet expected');
+ }
- if (oldNodesData) {
- // unsubscribe from old dataset
- util.forEach(this.nodesListeners, function (callback, event) {
- oldNodesData.unsubscribe(event, callback);
- });
- }
+ if (oldNodesData) {
+ // unsubscribe from old dataset
+ util.forEach(this.nodesListeners, function (callback, event) {
+ oldNodesData.unsubscribe(event, callback);
+ });
+ }
- // remove drawn nodes
- this.nodes = {};
+ // remove drawn nodes
+ this.nodes = {};
- if (this.nodesData) {
- // subscribe to new dataset
- var me = this;
- util.forEach(this.nodesListeners, function (callback, event) {
- me.nodesData.subscribe(event, callback);
- });
+ if (this.nodesData) {
+ // subscribe to new dataset
+ var me = this;
+ util.forEach(this.nodesListeners, function (callback, event) {
+ me.nodesData.subscribe(event, callback);
+ });
- // draw all new nodes
- var ids = this.nodesData.getIds();
- this._addNodes(ids);
- }
+ // draw all new nodes
+ var ids = this.nodesData.getIds();
+ this._addNodes(ids);
+ }
- this._updateSelection();
+ this._updateSelection();
};
/**
@@ -14969,29 +14448,29 @@ Graph.prototype._setNodes = function(nodes) {
* @private
*/
Graph.prototype._addNodes = function(ids) {
- var id;
- for (var i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- var data = this.nodesData.get(id);
- var node = new Node(data, this.images, this.groups, this.constants);
- this.nodes[id] = node; // note: this may replace an existing node
-
- if (!node.isFixed()) {
- // TODO: position new nodes in a smarter way!
- var radius = this.constants.edges.length * 2;
- var count = ids.length;
- var angle = 2 * Math.PI * (i / count);
- node.x = radius * Math.cos(angle);
- node.y = radius * Math.sin(angle);
-
- // note: no not use node.isMoving() here, as that gives the current
- // velocity of the node, which is zero after creation of the node.
- this.moving = true;
- }
+ var id;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ id = ids[i];
+ var data = this.nodesData.get(id);
+ var node = new Node(data, this.images, this.groups, this.constants);
+ this.nodes[id] = node; // note: this may replace an existing node
+
+ if (!node.isFixed()) {
+ // TODO: position new nodes in a smarter way!
+ var radius = this.constants.edges.length * 2;
+ var count = ids.length;
+ var angle = 2 * Math.PI * (i / count);
+ node.x = radius * Math.cos(angle);
+ node.y = radius * Math.sin(angle);
+
+ // note: no not use node.isMoving() here, as that gives the current
+ // velocity of the node, which is zero after creation of the node.
+ this.moving = true;
}
+ }
- this._reconnectEdges();
- this._updateValueRange(this.nodes);
+ this._reconnectEdges();
+ this._updateValueRange(this.nodes);
};
/**
@@ -15000,29 +14479,29 @@ Graph.prototype._addNodes = function(ids) {
* @private
*/
Graph.prototype._updateNodes = function(ids) {
- var nodes = this.nodes,
- nodesData = this.nodesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- var node = nodes[id];
- var data = nodesData.get(id);
- if (node) {
- // update node
- node.setProperties(data, this.constants);
- }
- else {
- // create node
- node = new Node(properties, this.images, this.groups, this.constants);
- nodes[id] = node;
+ var nodes = this.nodes,
+ nodesData = this.nodesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ var node = nodes[id];
+ var data = nodesData.get(id);
+ if (node) {
+ // update node
+ node.setProperties(data, this.constants);
+ }
+ else {
+ // create node
+ node = new Node(properties, this.images, this.groups, this.constants);
+ nodes[id] = node;
- if (!node.isFixed()) {
- this.moving = true;
- }
- }
+ if (!node.isFixed()) {
+ this.moving = true;
+ }
}
+ }
- this._reconnectEdges();
- this._updateValueRange(nodes);
+ this._reconnectEdges();
+ this._updateValueRange(nodes);
};
/**
@@ -15031,15 +14510,15 @@ Graph.prototype._updateNodes = function(ids) {
* @private
*/
Graph.prototype._removeNodes = function(ids) {
- var nodes = this.nodes;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- delete nodes[id];
- }
+ var nodes = this.nodes;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ delete nodes[id];
+ }
- this._reconnectEdges();
- this._updateSelection();
- this._updateValueRange(nodes);
+ this._reconnectEdges();
+ this._updateSelection();
+ this._updateValueRange(nodes);
};
/**
@@ -15049,45 +14528,45 @@ Graph.prototype._removeNodes = function(ids) {
* @private
*/
Graph.prototype._setEdges = function(edges) {
- var oldEdgesData = this.edgesData;
+ var oldEdgesData = this.edgesData;
- if (edges instanceof DataSet || edges instanceof DataView) {
- this.edgesData = edges;
- }
- else if (edges instanceof Array) {
- this.edgesData = new DataSet();
- this.edgesData.add(edges);
- }
- else if (!edges) {
- this.edgesData = new DataSet();
- }
- else {
- throw new TypeError('Array or DataSet expected');
- }
+ if (edges instanceof DataSet || edges instanceof DataView) {
+ this.edgesData = edges;
+ }
+ else if (edges instanceof Array) {
+ this.edgesData = new DataSet();
+ this.edgesData.add(edges);
+ }
+ else if (!edges) {
+ this.edgesData = new DataSet();
+ }
+ else {
+ throw new TypeError('Array or DataSet expected');
+ }
- if (oldEdgesData) {
- // unsubscribe from old dataset
- util.forEach(this.edgesListeners, function (callback, event) {
- oldEdgesData.unsubscribe(event, callback);
- });
- }
+ if (oldEdgesData) {
+ // unsubscribe from old dataset
+ util.forEach(this.edgesListeners, function (callback, event) {
+ oldEdgesData.unsubscribe(event, callback);
+ });
+ }
- // remove drawn edges
- this.edges = {};
+ // remove drawn edges
+ this.edges = {};
- if (this.edgesData) {
- // subscribe to new dataset
- var me = this;
- util.forEach(this.edgesListeners, function (callback, event) {
- me.edgesData.subscribe(event, callback);
- });
+ if (this.edgesData) {
+ // subscribe to new dataset
+ var me = this;
+ util.forEach(this.edgesListeners, function (callback, event) {
+ me.edgesData.subscribe(event, callback);
+ });
- // draw all new nodes
- var ids = this.edgesData.getIds();
- this._addEdges(ids);
- }
+ // draw all new nodes
+ var ids = this.edgesData.getIds();
+ this._addEdges(ids);
+ }
- this._reconnectEdges();
+ this._reconnectEdges();
};
/**
@@ -15096,22 +14575,22 @@ Graph.prototype._setEdges = function(edges) {
* @private
*/
Graph.prototype._addEdges = function (ids) {
- var edges = this.edges,
- edgesData = this.edgesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
+ var edges = this.edges,
+ edgesData = this.edgesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
- var oldEdge = edges[id];
- if (oldEdge) {
- oldEdge.disconnect();
- }
-
- var data = edgesData.get(id);
- edges[id] = new Edge(data, this, this.constants);
+ var oldEdge = edges[id];
+ if (oldEdge) {
+ oldEdge.disconnect();
}
- this.moving = true;
- this._updateValueRange(edges);
+ var data = edgesData.get(id);
+ edges[id] = new Edge(data, this, this.constants);
+ }
+
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -15120,28 +14599,28 @@ Graph.prototype._addEdges = function (ids) {
* @private
*/
Graph.prototype._updateEdges = function (ids) {
- var edges = this.edges,
- edgesData = this.edgesData;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
-
- var data = edgesData.get(id);
- var edge = edges[id];
- if (edge) {
- // update edge
- edge.disconnect();
- edge.setProperties(data, this.constants);
- edge.connect();
- }
- else {
- // create edge
- edge = new Edge(data, this, this.constants);
- this.edges[id] = edge;
- }
+ var edges = this.edges,
+ edgesData = this.edgesData;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+
+ var data = edgesData.get(id);
+ var edge = edges[id];
+ if (edge) {
+ // update edge
+ edge.disconnect();
+ edge.setProperties(data, this.constants);
+ edge.connect();
+ }
+ else {
+ // create edge
+ edge = new Edge(data, this, this.constants);
+ this.edges[id] = edge;
}
+ }
- this.moving = true;
- this._updateValueRange(edges);
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -15150,18 +14629,18 @@ Graph.prototype._updateEdges = function (ids) {
* @private
*/
Graph.prototype._removeEdges = function (ids) {
- var edges = this.edges;
- for (var i = 0, len = ids.length; i < len; i++) {
- var id = ids[i];
- var edge = edges[id];
- if (edge) {
- edge.disconnect();
- delete edges[id];
- }
+ var edges = this.edges;
+ for (var i = 0, len = ids.length; i < len; i++) {
+ var id = ids[i];
+ var edge = edges[id];
+ if (edge) {
+ edge.disconnect();
+ delete edges[id];
}
+ }
- this.moving = true;
- this._updateValueRange(edges);
+ this.moving = true;
+ this._updateValueRange(edges);
};
/**
@@ -15169,23 +14648,23 @@ Graph.prototype._removeEdges = function (ids) {
* @private
*/
Graph.prototype._reconnectEdges = function() {
- var id,
- nodes = this.nodes,
- edges = this.edges;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- nodes[id].edges = [];
- }
+ var id,
+ nodes = this.nodes,
+ edges = this.edges;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ nodes[id].edges = [];
}
+ }
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- edge.from = null;
- edge.to = null;
- edge.connect();
- }
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ edge.from = null;
+ edge.to = null;
+ edge.connect();
}
+ }
};
/**
@@ -15197,29 +14676,29 @@ Graph.prototype._reconnectEdges = function() {
* @private
*/
Graph.prototype._updateValueRange = function(obj) {
- var id;
-
- // determine the range of the objects
- var valueMin = undefined;
- var valueMax = undefined;
- for (id in obj) {
- if (obj.hasOwnProperty(id)) {
- var value = obj[id].getValue();
- if (value !== undefined) {
- valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
- valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
- }
- }
+ var id;
+
+ // determine the range of the objects
+ var valueMin = undefined;
+ var valueMax = undefined;
+ for (id in obj) {
+ if (obj.hasOwnProperty(id)) {
+ var value = obj[id].getValue();
+ if (value !== undefined) {
+ valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
+ valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
+ }
}
+ }
- // adjust the range of all objects
- if (valueMin !== undefined && valueMax !== undefined) {
- for (id in obj) {
- if (obj.hasOwnProperty(id)) {
- obj[id].setValueRange(valueMin, valueMax);
- }
- }
+ // adjust the range of all objects
+ if (valueMin !== undefined && valueMax !== undefined) {
+ for (id in obj) {
+ if (obj.hasOwnProperty(id)) {
+ obj[id].setValueRange(valueMin, valueMax);
+ }
}
+ }
};
/**
@@ -15227,9 +14706,9 @@ Graph.prototype._updateValueRange = function(obj) {
* chart will be resized too.
*/
Graph.prototype.redraw = function() {
- this.setSize(this.width, this.height);
+ this.setSize(this.width, this.height);
- this._redraw();
+ this._redraw();
};
/**
@@ -15237,23 +14716,23 @@ Graph.prototype.redraw = function() {
* @private
*/
Graph.prototype._redraw = function() {
- var ctx = this.frame.canvas.getContext('2d');
+ var ctx = this.frame.canvas.getContext('2d');
- // clear the canvas
- var w = this.frame.canvas.width;
- var h = this.frame.canvas.height;
- ctx.clearRect(0, 0, w, h);
+ // clear the canvas
+ var w = this.frame.canvas.width;
+ var h = this.frame.canvas.height;
+ ctx.clearRect(0, 0, w, h);
- // set scaling and translation
- ctx.save();
- ctx.translate(this.translation.x, this.translation.y);
- ctx.scale(this.scale, this.scale);
+ // set scaling and translation
+ ctx.save();
+ ctx.translate(this.translation.x, this.translation.y);
+ ctx.scale(this.scale, this.scale);
- this._drawEdges(ctx);
- this._drawNodes(ctx);
+ this._drawEdges(ctx);
+ this._drawNodes(ctx);
- // restore original scaling and translation
- ctx.restore();
+ // restore original scaling and translation
+ ctx.restore();
};
/**
@@ -15263,19 +14742,19 @@ Graph.prototype._redraw = function() {
* @private
*/
Graph.prototype._setTranslation = function(offsetX, offsetY) {
- if (this.translation === undefined) {
- this.translation = {
- x: 0,
- y: 0
- };
- }
+ if (this.translation === undefined) {
+ this.translation = {
+ x: 0,
+ y: 0
+ };
+ }
- if (offsetX !== undefined) {
- this.translation.x = offsetX;
- }
- if (offsetY !== undefined) {
- this.translation.y = offsetY;
- }
+ if (offsetX !== undefined) {
+ this.translation.x = offsetX;
+ }
+ if (offsetY !== undefined) {
+ this.translation.y = offsetY;
+ }
};
/**
@@ -15284,10 +14763,10 @@ Graph.prototype._setTranslation = function(offsetX, offsetY) {
* @private
*/
Graph.prototype._getTranslation = function() {
- return {
- x: this.translation.x,
- y: this.translation.y
- };
+ return {
+ x: this.translation.x,
+ y: this.translation.y
+ };
};
/**
@@ -15296,7 +14775,7 @@ Graph.prototype._getTranslation = function() {
* @private
*/
Graph.prototype._setScale = function(scale) {
- this.scale = scale;
+ this.scale = scale;
};
/**
* Get the current scale of the graph
@@ -15304,7 +14783,7 @@ Graph.prototype._setScale = function(scale) {
* @private
*/
Graph.prototype._getScale = function() {
- return this.scale;
+ return this.scale;
};
/**
@@ -15314,7 +14793,7 @@ Graph.prototype._getScale = function() {
* @private
*/
Graph.prototype._canvasToX = function(x) {
- return (x - this.translation.x) / this.scale;
+ return (x - this.translation.x) / this.scale;
};
/**
@@ -15324,7 +14803,7 @@ Graph.prototype._canvasToX = function(x) {
* @private
*/
Graph.prototype._xToCanvas = function(x) {
- return x * this.scale + this.translation.x;
+ return x * this.scale + this.translation.x;
};
/**
@@ -15334,7 +14813,7 @@ Graph.prototype._xToCanvas = function(x) {
* @private
*/
Graph.prototype._canvasToY = function(y) {
- return (y - this.translation.y) / this.scale;
+ return (y - this.translation.y) / this.scale;
};
/**
@@ -15344,7 +14823,7 @@ Graph.prototype._canvasToY = function(y) {
* @private
*/
Graph.prototype._yToCanvas = function(y) {
- return y * this.scale + this.translation.y ;
+ return y * this.scale + this.translation.y ;
};
/**
@@ -15354,24 +14833,24 @@ Graph.prototype._yToCanvas = function(y) {
* @private
*/
Graph.prototype._drawNodes = function(ctx) {
- // first draw the unselected nodes
- var nodes = this.nodes;
- var selected = [];
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- if (nodes[id].isSelected()) {
- selected.push(id);
- }
- else {
- nodes[id].draw(ctx);
- }
- }
+ // first draw the unselected nodes
+ var nodes = this.nodes;
+ var selected = [];
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ if (nodes[id].isSelected()) {
+ selected.push(id);
+ }
+ else {
+ nodes[id].draw(ctx);
+ }
}
+ }
- // draw the selected nodes on top
- for (var s = 0, sMax = selected.length; s < sMax; s++) {
- nodes[selected[s]].draw(ctx);
- }
+ // draw the selected nodes on top
+ for (var s = 0, sMax = selected.length; s < sMax; s++) {
+ nodes[selected[s]].draw(ctx);
+ }
};
/**
@@ -15381,15 +14860,15 @@ Graph.prototype._drawNodes = function(ctx) {
* @private
*/
Graph.prototype._drawEdges = function(ctx) {
- var edges = this.edges;
- for (var id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected) {
- edges[id].draw(ctx);
- }
- }
+ var edges = this.edges;
+ for (var id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected) {
+ edges[id].draw(ctx);
+ }
}
+ }
};
/**
@@ -15397,22 +14876,22 @@ Graph.prototype._drawEdges = function(ctx) {
* @private
*/
Graph.prototype._doStabilize = function() {
- var start = new Date();
-
- // find stable position
- var count = 0;
- var vmin = this.constants.minVelocity;
- var stable = false;
- while (!stable && count < this.constants.maxIterations) {
- this._calculateForces();
- this._discreteStepNodes();
- stable = !this._isMoving(vmin);
- count++;
- }
+ var start = new Date();
+
+ // find stable position
+ var count = 0;
+ var vmin = this.constants.minVelocity;
+ var stable = false;
+ while (!stable && count < this.constants.maxIterations) {
+ this._calculateForces();
+ this._discreteStepNodes();
+ stable = !this._isMoving(vmin);
+ count++;
+ }
- var end = new Date();
+ var end = new Date();
- // console.log('Stabilized in ' + (end-start) + ' ms, ' + count + ' iterations' ); // TODO: cleanup
+ // console.log('Stabilized in ' + (end-start) + ' ms, ' + count + ' iterations' ); // TODO: cleanup
};
/**
@@ -15421,149 +14900,149 @@ Graph.prototype._doStabilize = function() {
* @private
*/
Graph.prototype._calculateForces = function() {
- // create a local edge to the nodes and edges, that is faster
- var id, dx, dy, angle, distance, fx, fy,
- repulsingForce, springForce, length, edgeLength,
- nodes = this.nodes,
- edges = this.edges;
-
- // gravity, add a small constant force to pull the nodes towards the center of
- // the graph
- // Also, the forces are reset to zero in this loop by using _setForce instead
- // of _addForce
- var gravity = 0.01,
- gx = this.frame.canvas.clientWidth / 2,
- gy = this.frame.canvas.clientHeight / 2;
- for (id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var node = nodes[id];
- dx = gx - node.x;
- dy = gy - node.y;
- angle = Math.atan2(dy, dx);
- fx = Math.cos(angle) * gravity;
- fy = Math.sin(angle) * gravity;
-
- node._setForce(fx, fy);
- }
- }
-
- // repulsing forces between nodes
- var minimumDistance = this.constants.nodes.distance,
- steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
-
- for (var id1 in nodes) {
- if (nodes.hasOwnProperty(id1)) {
- var node1 = nodes[id1];
- for (var id2 in nodes) {
- if (nodes.hasOwnProperty(id2)) {
- var node2 = nodes[id2];
- // calculate normally distributed force
- dx = node2.x - node1.x;
- dy = node2.y - node1.y;
- distance = Math.sqrt(dx * dx + dy * dy);
- angle = Math.atan2(dy, dx);
-
- // TODO: correct factor for repulsing force
- //repulsingForce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingForce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- repulsingForce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)); // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingForce;
- fy = Math.sin(angle) * repulsingForce;
-
- node1._addForce(-fx, -fy);
- node2._addForce(fx, fy);
- }
- }
- }
+ // create a local edge to the nodes and edges, that is faster
+ var id, dx, dy, angle, distance, fx, fy,
+ repulsingForce, springForce, length, edgeLength,
+ nodes = this.nodes,
+ edges = this.edges;
+
+ // gravity, add a small constant force to pull the nodes towards the center of
+ // the graph
+ // Also, the forces are reset to zero in this loop by using _setForce instead
+ // of _addForce
+ var gravity = 0.01,
+ gx = this.frame.canvas.clientWidth / 2,
+ gy = this.frame.canvas.clientHeight / 2;
+ for (id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ var node = nodes[id];
+ dx = gx - node.x;
+ dy = gy - node.y;
+ angle = Math.atan2(dy, dx);
+ fx = Math.cos(angle) * gravity;
+ fy = Math.sin(angle) * gravity;
+
+ node._setForce(fx, fy);
}
+ }
- /* TODO: re-implement repulsion of edges
- for (var n = 0; n < nodes.length; n++) {
- for (var l = 0; l < edges.length; l++) {
- var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
- ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
-
- // calculate normally distributed force
- dx = nodes[n].x - lx,
- dy = nodes[n].y - ly,
- distance = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(dy, dx),
-
-
- // TODO: correct factor for repulsing force
- //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
- repulsingforce = 1 / (1 + Math.exp((distance / (minimumDistance / 2) - 1) * steepness)), // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingforce,
- fy = Math.sin(angle) * repulsingforce;
- nodes[n]._addForce(fx, fy);
- edges[l].from._addForce(-fx/2,-fy/2);
- edges[l].to._addForce(-fx/2,-fy/2);
- }
+ // repulsing forces between nodes
+ var minimumDistance = this.constants.nodes.distance,
+ steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
+
+ for (var id1 in nodes) {
+ if (nodes.hasOwnProperty(id1)) {
+ var node1 = nodes[id1];
+ for (var id2 in nodes) {
+ if (nodes.hasOwnProperty(id2)) {
+ var node2 = nodes[id2];
+ // calculate normally distributed force
+ dx = node2.x - node1.x;
+ dy = node2.y - node1.y;
+ distance = Math.sqrt(dx * dx + dy * dy);
+ angle = Math.atan2(dy, dx);
+
+ // TODO: correct factor for repulsing force
+ //repulsingForce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingForce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ repulsingForce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)); // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingForce;
+ fy = Math.sin(angle) * repulsingForce;
+
+ node1._addForce(-fx, -fy);
+ node2._addForce(fx, fy);
+ }
+ }
}
- */
+ }
- // forces caused by the edges, modelled as springs
- for (id in edges) {
- if (edges.hasOwnProperty(id)) {
- var edge = edges[id];
- if (edge.connected) {
- dx = (edge.to.x - edge.from.x);
- dy = (edge.to.y - edge.from.y);
- //edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
- //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
- //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
- edgeLength = edge.length;
- length = Math.sqrt(dx * dx + dy * dy);
- angle = Math.atan2(dy, dx);
-
- springForce = edge.stiffness * (edgeLength - length);
-
- fx = Math.cos(angle) * springForce;
- fy = Math.sin(angle) * springForce;
-
- edge.from._addForce(-fx, -fy);
- edge.to._addForce(fx, fy);
- }
- }
+ /* TODO: re-implement repulsion of edges
+ for (var n = 0; n < nodes.length; n++) {
+ for (var l = 0; l < edges.length; l++) {
+ var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
+ ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
+
+ // calculate normally distributed force
+ dx = nodes[n].x - lx,
+ dy = nodes[n].y - ly,
+ distance = Math.sqrt(dx * dx + dy * dy),
+ angle = Math.atan2(dy, dx),
+
+
+ // TODO: correct factor for repulsing force
+ //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
+ repulsingforce = 1 / (1 + Math.exp((distance / (minimumDistance / 2) - 1) * steepness)), // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingforce,
+ fy = Math.sin(angle) * repulsingforce;
+ nodes[n]._addForce(fx, fy);
+ edges[l].from._addForce(-fx/2,-fy/2);
+ edges[l].to._addForce(-fx/2,-fy/2);
+ }
+ }
+ */
+
+ // forces caused by the edges, modelled as springs
+ for (id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ var edge = edges[id];
+ if (edge.connected) {
+ dx = (edge.to.x - edge.from.x);
+ dy = (edge.to.y - edge.from.y);
+ //edgeLength = (edge.from.width + edge.from.height + edge.to.width + edge.to.height)/2 || edge.length; // TODO: dmin
+ //edgeLength = (edge.from.width + edge.to.width)/2 || edge.length; // TODO: dmin
+ //edgeLength = 20 + ((edge.from.width + edge.to.width) || 0) / 2;
+ edgeLength = edge.length;
+ length = Math.sqrt(dx * dx + dy * dy);
+ angle = Math.atan2(dy, dx);
+
+ springForce = edge.stiffness * (edgeLength - length);
+
+ fx = Math.cos(angle) * springForce;
+ fy = Math.sin(angle) * springForce;
+
+ edge.from._addForce(-fx, -fy);
+ edge.to._addForce(fx, fy);
+ }
}
+ }
- /* TODO: re-implement repulsion of edges
- // repulsing forces between edges
- var minimumDistance = this.constants.edges.distance,
- steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
- for (var l = 0; l < edges.length; l++) {
- //Keep distance from other edge centers
- for (var l2 = l + 1; l2 < this.edges.length; l2++) {
- //var dmin = (nodes[n].width + nodes[n].height + nodes[n2].width + nodes[n2].height) / 1 || minimumDistance, // TODO: dmin
- //var dmin = (nodes[n].width + nodes[n2].width)/2 || minimumDistance, // TODO: dmin
- //dmin = 40 + ((nodes[n].width/2 + nodes[n2].width/2) || 0),
- var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
- ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
- l2x = edges[l2].from.x+(edges[l2].to.x - edges[l2].from.x)/2,
- l2y = edges[l2].from.y+(edges[l2].to.y - edges[l2].from.y)/2,
-
- // calculate normally distributed force
- dx = l2x - lx,
- dy = l2y - ly,
- distance = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(dy, dx),
-
-
- // TODO: correct factor for repulsing force
- //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
- //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
- repulsingforce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)), // TODO: customize the repulsing force
- fx = Math.cos(angle) * repulsingforce,
- fy = Math.sin(angle) * repulsingforce;
-
- edges[l].from._addForce(-fx, -fy);
- edges[l].to._addForce(-fx, -fy);
- edges[l2].from._addForce(fx, fy);
- edges[l2].to._addForce(fx, fy);
- }
- }
- */
+ /* TODO: re-implement repulsion of edges
+ // repulsing forces between edges
+ var minimumDistance = this.constants.edges.distance,
+ steepness = 10; // higher value gives steeper slope of the force around the given minimumDistance
+ for (var l = 0; l < edges.length; l++) {
+ //Keep distance from other edge centers
+ for (var l2 = l + 1; l2 < this.edges.length; l2++) {
+ //var dmin = (nodes[n].width + nodes[n].height + nodes[n2].width + nodes[n2].height) / 1 || minimumDistance, // TODO: dmin
+ //var dmin = (nodes[n].width + nodes[n2].width)/2 || minimumDistance, // TODO: dmin
+ //dmin = 40 + ((nodes[n].width/2 + nodes[n2].width/2) || 0),
+ var lx = edges[l].from.x+(edges[l].to.x - edges[l].from.x)/2,
+ ly = edges[l].from.y+(edges[l].to.y - edges[l].from.y)/2,
+ l2x = edges[l2].from.x+(edges[l2].to.x - edges[l2].from.x)/2,
+ l2y = edges[l2].from.y+(edges[l2].to.y - edges[l2].from.y)/2,
+
+ // calculate normally distributed force
+ dx = l2x - lx,
+ dy = l2y - ly,
+ distance = Math.sqrt(dx * dx + dy * dy),
+ angle = Math.atan2(dy, dx),
+
+
+ // TODO: correct factor for repulsing force
+ //var repulsingforce = 2 * Math.exp(-5 * (distance * distance) / (dmin * dmin) ); // TODO: customize the repulsing force
+ //repulsingforce = Math.exp(-1 * (distance * distance) / (dmin * dmin) ), // TODO: customize the repulsing force
+ repulsingforce = 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)), // TODO: customize the repulsing force
+ fx = Math.cos(angle) * repulsingforce,
+ fy = Math.sin(angle) * repulsingforce;
+
+ edges[l].from._addForce(-fx, -fy);
+ edges[l].to._addForce(-fx, -fy);
+ edges[l2].from._addForce(fx, fy);
+ edges[l2].to._addForce(fx, fy);
+ }
+ }
+ */
};
@@ -15574,14 +15053,14 @@ Graph.prototype._calculateForces = function() {
* @private
*/
Graph.prototype._isMoving = function(vmin) {
- // TODO: ismoving does not work well: should check the kinetic energy, not its velocity
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) {
- return true;
- }
+ // TODO: ismoving does not work well: should check the kinetic energy, not its velocity
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) {
+ return true;
}
- return false;
+ }
+ return false;
};
@@ -15590,124 +15069,124 @@ Graph.prototype._isMoving = function(vmin) {
* @private
*/
Graph.prototype._discreteStepNodes = function() {
- var interval = this.refreshRate / 1000.0; // in seconds
- var nodes = this.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- nodes[id].discreteStep(interval);
- }
+ var interval = this.refreshRate / 1000.0; // in seconds
+ var nodes = this.nodes;
+ for (var id in nodes) {
+ if (nodes.hasOwnProperty(id)) {
+ nodes[id].discreteStep(interval);
}
+ }
};
/**
* Start animating nodes and edges
*/
Graph.prototype.start = function() {
- if (this.moving) {
- this._calculateForces();
- this._discreteStepNodes();
+ if (this.moving) {
+ this._calculateForces();
+ this._discreteStepNodes();
- var vmin = this.constants.minVelocity;
- this.moving = this._isMoving(vmin);
- }
+ var vmin = this.constants.minVelocity;
+ this.moving = this._isMoving(vmin);
+ }
- if (this.moving) {
- // start animation. only start timer if it is not already running
- if (!this.timer) {
- var graph = this;
- this.timer = window.setTimeout(function () {
- graph.timer = undefined;
- graph.start();
- graph._redraw();
- }, this.refreshRate);
- }
- }
- else {
- this._redraw();
+ if (this.moving) {
+ // start animation. only start timer if it is not already running
+ if (!this.timer) {
+ var graph = this;
+ this.timer = window.setTimeout(function () {
+ graph.timer = undefined;
+ graph.start();
+ graph._redraw();
+ }, this.refreshRate);
}
+ }
+ else {
+ this._redraw();
+ }
};
/**
* Stop animating nodes and edges.
*/
Graph.prototype.stop = function () {
- if (this.timer) {
- window.clearInterval(this.timer);
- this.timer = undefined;
- }
+ if (this.timer) {
+ window.clearInterval(this.timer);
+ this.timer = undefined;
+ }
};
/**
* vis.js module exports
*/
var vis = {
- util: util,
- events: events,
-
- Controller: Controller,
- DataSet: DataSet,
- DataView: DataView,
- Range: Range,
- Stack: Stack,
- TimeStep: TimeStep,
- EventBus: EventBus,
-
- components: {
- items: {
- Item: Item,
- ItemBox: ItemBox,
- ItemPoint: ItemPoint,
- ItemRange: ItemRange
- },
-
- Component: Component,
- Panel: Panel,
- RootPanel: RootPanel,
- ItemSet: ItemSet,
- TimeAxis: TimeAxis
+ util: util,
+ events: events,
+
+ Controller: Controller,
+ DataSet: DataSet,
+ DataView: DataView,
+ Range: Range,
+ Stack: Stack,
+ TimeStep: TimeStep,
+ EventBus: EventBus,
+
+ components: {
+ items: {
+ Item: Item,
+ ItemBox: ItemBox,
+ ItemPoint: ItemPoint,
+ ItemRange: ItemRange
},
- graph: {
- Node: Node,
- Edge: Edge,
- Popup: Popup,
- Groups: Groups,
- Images: Images
- },
+ Component: Component,
+ Panel: Panel,
+ RootPanel: RootPanel,
+ ItemSet: ItemSet,
+ TimeAxis: TimeAxis
+ },
- Timeline: Timeline,
- Graph: Graph
+ graph: {
+ Node: Node,
+ Edge: Edge,
+ Popup: Popup,
+ Groups: Groups,
+ Images: Images
+ },
+
+ Timeline: Timeline,
+ Graph: Graph
};
/**
* CommonJS module exports
*/
if (typeof exports !== 'undefined') {
- exports = vis;
+ exports = vis;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = vis;
+ module.exports = vis;
}
/**
* AMD module exports
*/
if (typeof(define) === 'function') {
- define(function () {
- return vis;
- });
+ define(function () {
+ return vis;
+ });
}
/**
* Window exports
*/
if (typeof window !== 'undefined') {
- // attach the module to the window, load as a regular javascript file
- window['vis'] = vis;
+ // attach the module to the window, load as a regular javascript file
+ window['vis'] = vis;
}
// inject css
-util.loadCss("/* vis.js stylesheet */\n.vis.timeline {\n}\n\n\n.vis.timeline.rootpanel {\n position: relative;\n overflow: hidden;\n\n border: 1px solid #bfbfbf;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n.vis.timeline .panel {\n position: absolute;\n overflow: hidden;\n}\n\n\n.vis.timeline .groupset {\n position: absolute;\n padding: 0;\n margin: 0;\n}\n\n.vis.timeline .labels {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n padding: 0;\n margin: 0;\n\n border-right: 1px solid #bfbfbf;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.vis.timeline .labels .label {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n border-bottom: 1px solid #bfbfbf;\n color: #4d4d4d;\n}\n\n.vis.timeline .labels .label .inner {\n display: inline-block;\n padding: 5px;\n}\n\n\n.vis.timeline .itemset {\n position: absolute;\n padding: 0;\n margin: 0;\n overflow: hidden;\n}\n\n.vis.timeline .background {\n}\n\n.vis.timeline .foreground {\n}\n\n.vis.timeline .itemset-axis {\n position: absolute;\n}\n\n.vis.timeline .groupset .itemset-axis {\n border-top: 1px solid #bfbfbf;\n}\n\n/* TODO: with orientation=='bottom', this will more or less overlap with timeline axis\n.vis.timeline .groupset .itemset-axis:last-child {\n border-top: none;\n}\n*/\n\n\n.vis.timeline .item {\n position: absolute;\n color: #1A1A1A;\n border-color: #97B0F8;\n background-color: #D5DDF6;\n display: inline-block;\n}\n\n.vis.timeline .item.selected {\n border-color: #FFC200;\n background-color: #FFF785;\n z-index: 999;\n}\n\n.vis.timeline .item.cluster {\n /* TODO: use another color or pattern? */\n background: #97B0F8 url('img/cluster_bg.png');\n color: white;\n}\n.vis.timeline .item.cluster.point {\n border-color: #D5DDF6;\n}\n\n.vis.timeline .item.box {\n text-align: center;\n border-style: solid;\n border-width: 1px;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.point {\n background: none;\n}\n\n.vis.timeline .dot {\n border: 5px solid #97B0F8;\n position: absolute;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range {\n overflow: hidden;\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.rangeoverflow {\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range .drag-left, .vis.timeline .item.rangeoverflow .drag-left {\n cursor: w-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .drag-right, .vis.timeline .item.rangeoverflow .drag-right {\n cursor: e-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .content, .vis.timeline .item.rangeoverflow .content {\n position: relative;\n display: inline-block;\n}\n\n.vis.timeline .item.line {\n position: absolute;\n width: 0;\n border-left-width: 1px;\n border-left-style: solid;\n}\n\n.vis.timeline .item .content {\n margin: 5px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.vis.timeline .axis {\n position: relative;\n}\n\n.vis.timeline .axis .text {\n position: absolute;\n color: #4d4d4d;\n padding: 3px;\n white-space: nowrap;\n}\n\n.vis.timeline .axis .text.measure {\n position: absolute;\n padding-left: 0;\n padding-right: 0;\n margin-left: 0;\n margin-right: 0;\n visibility: hidden;\n}\n\n.vis.timeline .axis .grid.vertical {\n position: absolute;\n width: 0;\n border-right: 1px solid;\n}\n\n.vis.timeline .axis .grid.horizontal {\n position: absolute;\n left: 0;\n width: 100%;\n height: 0;\n border-bottom: 1px solid;\n}\n\n.vis.timeline .axis .grid.minor {\n border-color: #e5e5e5;\n}\n\n.vis.timeline .axis .grid.major {\n border-color: #bfbfbf;\n}\n\n.vis.timeline .currenttime {\n background-color: #FF7F6E;\n width: 2px;\n z-index: 9;\n}\n.vis.timeline .customtime {\n background-color: #6E94FF;\n width: 2px;\n cursor: move;\n z-index: 9; \n}\n");
+util.loadCss("/* vis.js stylesheet */\n.vis.timeline {\n}\n\n\n.vis.timeline.rootpanel {\n position: relative;\n overflow: hidden;\n\n border: 1px solid #bfbfbf;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n.vis.timeline .panel {\n position: absolute;\n overflow: hidden;\n}\n\n\n.vis.timeline .groupset {\n position: absolute;\n padding: 0;\n margin: 0;\n}\n\n.vis.timeline .labels {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n padding: 0;\n margin: 0;\n\n border-right: 1px solid #bfbfbf;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.vis.timeline .labels .label-set {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n overflow: hidden;\n\n border-top: none;\n border-bottom: 1px solid #bfbfbf;\n}\n\n.vis.timeline .labels .label-set .label {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n color: #4d4d4d;\n}\n\n.vis.timeline.top .labels .label-set .label,\n.vis.timeline.top .groupset .itemset-axis {\n border-top: 1px solid #bfbfbf;\n border-bottom: none;\n}\n\n.vis.timeline.bottom .labels .label-set .label,\n.vis.timeline.bottom .groupset .itemset-axis {\n border-top: none;\n border-bottom: 1px solid #bfbfbf;\n}\n\n.vis.timeline .labels .label-set .label .inner {\n display: inline-block;\n padding: 5px;\n}\n\n\n.vis.timeline .itemset {\n position: absolute;\n padding: 0;\n margin: 0;\n overflow: hidden;\n}\n\n.vis.timeline .background {\n}\n\n.vis.timeline .foreground {\n}\n\n.vis.timeline .itemset-axis {\n position: absolute;\n}\n\n\n.vis.timeline .item {\n position: absolute;\n color: #1A1A1A;\n border-color: #97B0F8;\n background-color: #D5DDF6;\n display: inline-block;\n}\n\n.vis.timeline .item.selected {\n border-color: #FFC200;\n background-color: #FFF785;\n z-index: 999;\n}\n\n.vis.timeline .item.cluster {\n /* TODO: use another color or pattern? */\n background: #97B0F8 url('img/cluster_bg.png');\n color: white;\n}\n.vis.timeline .item.cluster.point {\n border-color: #D5DDF6;\n}\n\n.vis.timeline .item.box {\n text-align: center;\n border-style: solid;\n border-width: 1px;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.point {\n background: none;\n}\n\n.vis.timeline .dot {\n border: 5px solid #97B0F8;\n position: absolute;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range {\n overflow: hidden;\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.rangeoverflow {\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range .drag-left, .vis.timeline .item.rangeoverflow .drag-left {\n cursor: w-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .drag-right, .vis.timeline .item.rangeoverflow .drag-right {\n cursor: e-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .content, .vis.timeline .item.rangeoverflow .content {\n position: relative;\n display: inline-block;\n}\n\n.vis.timeline .item.line {\n position: absolute;\n width: 0;\n border-left-width: 1px;\n border-left-style: solid;\n}\n\n.vis.timeline .item .content {\n margin: 5px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.vis.timeline .axis {\n position: relative;\n}\n\n.vis.timeline .axis .text {\n position: absolute;\n color: #4d4d4d;\n padding: 3px;\n white-space: nowrap;\n}\n\n.vis.timeline .axis .text.measure {\n position: absolute;\n padding-left: 0;\n padding-right: 0;\n margin-left: 0;\n margin-right: 0;\n visibility: hidden;\n}\n\n.vis.timeline .axis .grid.vertical {\n position: absolute;\n width: 0;\n border-right: 1px solid;\n}\n\n.vis.timeline .axis .grid.horizontal {\n position: absolute;\n left: 0;\n width: 100%;\n height: 0;\n border-bottom: 1px solid;\n}\n\n.vis.timeline .axis .grid.minor {\n border-color: #e5e5e5;\n}\n\n.vis.timeline .axis .grid.major {\n border-color: #bfbfbf;\n}\n\n.vis.timeline .currenttime {\n background-color: #FF7F6E;\n width: 2px;\n z-index: 9;\n}\n.vis.timeline .customtime {\n background-color: #6E94FF;\n width: 2px;\n cursor: move;\n z-index: 9;\n}\n");
},{"hammerjs":1,"moment":2}]},{},[3])
(3)
diff --git a/vis.min.js b/vis.min.js
index f58a6d0e..843cd31c 100644
--- a/vis.min.js
+++ b/vis.min.js
@@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library.
*
* @version 0.3.0-SNAPSHOT
- * @date 2013-10-30
+ * @date 2014-01-03
*
* @license
* Copyright (C) 2011-2013 Almende B.V, http://almende.com
@@ -22,9 +22,8 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-!function(t){"object"==typeof exports?module.exports=t():"function"==typeof define&&define.amd?define(t):"undefined"!=typeof window?window.vis=t():"undefined"!=typeof global?global.vis=t():"undefined"!=typeof self&&(self.vis=t())}(function(){var t;return function e(t,i,n){function s(r,a){if(!i[r]){if(!t[r]){var h="function"==typeof require&&require;if(!a&&h)return h(r,!0);if(o)return o(r,!0);throw new Error("Cannot find module '"+r+"'")}var d=i[r]={exports:{}};t[r][0].call(d.exports,function(e){var i=t[r][1][e];return s(i?i:e)},d,d.exports,e,t,i,n)}return i[r].exports}for(var o="function"==typeof require&&require,r=0;r0&&e==s.EVENT_END?e=s.EVENT_MOVE:l||(e=s.EVENT_END),l||null===o?o=h:h=o,i.call(s.detection,n.collectEventData(t,e,h)),s.HAS_POINTEREVENTS&&e==s.EVENT_END&&(l=s.PointerEvent.updatePointer(e,h))),l||(o=null,r=!1,a=!1,s.PointerEvent.reset())}})},determineEventTypes:function(){var t;t=s.HAS_POINTEREVENTS?s.PointerEvent.getEvents():s.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],s.EVENT_TYPES[s.EVENT_START]=t[0],s.EVENT_TYPES[s.EVENT_MOVE]=t[1],s.EVENT_TYPES[s.EVENT_END]=t[2]},getTouchList:function(t){return s.HAS_POINTEREVENTS?s.PointerEvent.getTouchList():t.touches?t.touches:[{identifier:1,pageX:t.pageX,pageY:t.pageY,target:t.target}]},collectEventData:function(t,e,i){var n=this.getTouchList(i,e),o=s.POINTER_TOUCH;return(i.type.match(/mouse/)||s.PointerEvent.matchType(s.POINTER_MOUSE,i))&&(o=s.POINTER_MOUSE),{center:s.utils.getCenter(n),timeStamp:(new Date).getTime(),target:i.target,touches:n,eventType:e,pointerType:o,srcEvent:i,preventDefault:function(){this.srcEvent.preventManipulation&&this.srcEvent.preventManipulation(),this.srcEvent.preventDefault&&this.srcEvent.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return s.detection.stopDetect()}}}},s.PointerEvent={pointers:{},getTouchList:function(){var t=this,e=[];return Object.keys(t.pointers).sort().forEach(function(i){e.push(t.pointers[i])}),e},updatePointer:function(t,e){return t==s.EVENT_END?this.pointers={}:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e),Object.keys(this.pointers).length},matchType:function(t,e){if(!e.pointerType)return!1;var i={};return i[s.POINTER_MOUSE]=e.pointerType==e.MSPOINTER_TYPE_MOUSE||e.pointerType==s.POINTER_MOUSE,i[s.POINTER_TOUCH]=e.pointerType==e.MSPOINTER_TYPE_TOUCH||e.pointerType==s.POINTER_TOUCH,i[s.POINTER_PEN]=e.pointerType==e.MSPOINTER_TYPE_PEN||e.pointerType==s.POINTER_PEN,i[t]},getEvents:function(){return["pointerdown MSPointerDown","pointermove MSPointerMove","pointerup pointercancel MSPointerUp MSPointerCancel"]},reset:function(){this.pointers={}}},s.utils={extend:function(t,e,n){for(var s in e)t[s]!==i&&n||(t[s]=e[s]);return t},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){for(var e=[],i=[],n=0,s=t.length;s>n;n++)e.push(t[n].pageX),i.push(t[n].pageY);return{pageX:(Math.min.apply(Math,e)+Math.max.apply(Math,e))/2,pageY:(Math.min.apply(Math,i)+Math.max.apply(Math,i))/2}},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.pageY-t.pageY,n=e.pageX-t.pageX;return 180*Math.atan2(i,n)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.pageX-e.pageX),n=Math.abs(t.pageY-e.pageY);return i>=n?t.pageX-e.pageX>0?s.DIRECTION_LEFT:s.DIRECTION_RIGHT:t.pageY-e.pageY>0?s.DIRECTION_UP:s.DIRECTION_DOWN},getDistance:function(t,e){var i=e.pageX-t.pageX,n=e.pageY-t.pageY;return Math.sqrt(i*i+n*n)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==s.DIRECTION_UP||t==s.DIRECTION_DOWN},stopDefaultBrowserBehavior:function(t,e){var i,n=["webkit","khtml","moz","ms","o",""];if(e&&t.style){for(var s=0;si;i++){var o=this.gestures[i];if(!this.stopped&&e[o.name]!==!1&&o.handler.call(o,t,this.current.inst)===!1){this.stopDetect();break}}return this.current&&(this.current.lastEvent=t),t.eventType==s.EVENT_END&&!t.touches.length-1&&this.stopDetect(),t}},stopDetect:function(){this.previous=s.utils.extend({},this.current),this.current=null,this.stopped=!0},extendEventData:function(t){var e=this.current.startEvent;if(e&&(t.touches.length!=e.touches.length||t.touches===e.touches)){e.touches=[];for(var i=0,n=t.touches.length;n>i;i++)e.touches.push(s.utils.extend({},t.touches[i]))}var o=t.timeStamp-e.timeStamp,r=t.center.pageX-e.center.pageX,a=t.center.pageY-e.center.pageY,h=s.utils.getVelocity(o,r,a);return s.utils.extend(t,{deltaTime:o,deltaX:r,deltaY:a,velocityX:h.x,velocityY:h.y,distance:s.utils.getDistance(e.center,t.center),angle:s.utils.getAngle(e.center,t.center),direction:s.utils.getDirection(e.center,t.center),scale:s.utils.getScale(e.touches,t.touches),rotation:s.utils.getRotation(e.touches,t.touches),startEvent:e}),t},register:function(t){var e=t.defaults||{};return e[t.name]===i&&(e[t.name]=!0),s.utils.extend(s.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}},s.gestures=s.gestures||{},s.gestures.Hold={name:"hold",index:10,defaults:{hold_timeout:500,hold_threshold:1},timer:null,handler:function(t,e){switch(t.eventType){case s.EVENT_START:clearTimeout(this.timer),s.detection.current.name=this.name,this.timer=setTimeout(function(){"hold"==s.detection.current.name&&e.trigger("hold",t)},e.options.hold_timeout);break;case s.EVENT_MOVE:t.distance>e.options.hold_threshold&&clearTimeout(this.timer);break;case s.EVENT_END:clearTimeout(this.timer)}}},s.gestures.Tap={name:"tap",index:100,defaults:{tap_max_touchtime:250,tap_max_distance:10,tap_always:!0,doubletap_distance:20,doubletap_interval:300},handler:function(t,e){if(t.eventType==s.EVENT_END){var i=s.detection.previous,n=!1;if(t.deltaTime>e.options.tap_max_touchtime||t.distance>e.options.tap_max_distance)return;i&&"tap"==i.name&&t.timeStamp-i.lastEvent.timeStamp0&&t.touches.length>e.options.swipe_max_touches)return;(t.velocityX>e.options.swipe_velocity||t.velocityY>e.options.swipe_velocity)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},s.gestures.Drag={name:"drag",index:50,defaults:{drag_min_distance:10,drag_max_touches:1,drag_block_horizontal:!1,drag_block_vertical:!1,drag_lock_to_axis:!1,drag_lock_min_distance:25},triggered:!1,handler:function(t,e){if(s.detection.current.name!=this.name&&this.triggered)return e.trigger(this.name+"end",t),this.triggered=!1,void 0;if(!(e.options.drag_max_touches>0&&t.touches.length>e.options.drag_max_touches))switch(t.eventType){case s.EVENT_START:this.triggered=!1;break;case s.EVENT_MOVE:if(t.distancee.options.transform_min_rotation&&e.trigger("rotate",t),i>e.options.transform_min_scale&&(e.trigger("pinch",t),e.trigger("pinch"+(t.scale<1?"in":"out"),t));break;case s.EVENT_END:this.triggered&&e.trigger(this.name+"end",t),this.triggered=!1}}},s.gestures.Touch={name:"touch",index:-1/0,defaults:{prevent_default:!1,prevent_mouseevents:!1},handler:function(t,e){return e.options.prevent_mouseevents&&t.pointerType==s.POINTER_MOUSE?(t.stopDetect(),void 0):(e.options.prevent_default&&t.preventDefault(),t.eventType==s.EVENT_START&&e.trigger(this.name,t),void 0)}},s.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==s.EVENT_END&&e.trigger(this.name,t)}},"object"==typeof e&&"object"==typeof e.exports?e.exports=s:(t.Hammer=s,"function"==typeof t.define&&t.define.amd&&t.define("hammer",[],function(){return s}))}(this)},{}],2:[function(e,i){(function(n){function s(t,e){return function(i){return p(t.call(this,i),e)}}function o(t,e){return function(i){return this.lang().ordinal(t.call(this,i),e)}}function r(){}function a(t){T(t),d(this,t)}function h(t){var e=v(t),i=e.year||0,n=e.month||0,s=e.week||0,o=e.day||0,r=e.hour||0,a=e.minute||0,h=e.second||0,d=e.millisecond||0;this._input=t,this._milliseconds=+d+1e3*h+6e4*a+36e5*r,this._days=+o+7*s,this._months=+n+12*i,this._data={},this._bubble()}function d(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return e.hasOwnProperty("toString")&&(t.toString=e.toString),e.hasOwnProperty("valueOf")&&(t.valueOf=e.valueOf),t}function l(t){return 0>t?Math.ceil(t):Math.floor(t)}function p(t,e){for(var i=t+"";i.lengthn;n++)(i&&t[n]!==e[n]||!i&&w(t[n])!==w(e[n]))&&r++;return r+o}function g(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=ze[t]||He[e]||e}return t}function v(t){var e,i,n={};for(i in t)t.hasOwnProperty(i)&&(e=g(i),e&&(n[e]=t[i]));return n}function y(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}se[t]=function(s,o){var r,a,h=se.fn._lang[t],d=[];if("number"==typeof s&&(o=s,s=n),a=function(t){var e=se().utc().set(i,t);return h.call(se.fn._lang,e,s||"")},null!=o)return a(o);for(r=0;e>r;r++)d.push(a(r));return d}}function w(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function _(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function b(t){return E(t)?366:365}function E(t){return 0===t%4&&0!==t%100||0===t%400}function T(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[de]<0||t._a[de]>11?de:t._a[le]<1||t._a[le]>_(t._a[he],t._a[de])?le:t._a[pe]<0||t._a[pe]>23?pe:t._a[ue]<0||t._a[ue]>59?ue:t._a[ce]<0||t._a[ce]>59?ce:t._a[fe]<0||t._a[fe]>999?fe:-1,t._pf._overflowDayOfYear&&(he>e||e>le)&&(e=le),t._pf.overflow=e)}function x(t){t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function S(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length)),t._isValid}function M(t){return t?t.toLowerCase().replace("_","-"):t}function D(t,e){return e.abbr=t,me[t]||(me[t]=new r),me[t].set(e),me[t]}function C(t){delete me[t]}function O(t){var i,n,s,o,r=0,a=function(t){if(!me[t]&&ge)try{e("./lang/"+t)}catch(i){}return me[t]};if(!t)return se.fn._lang;if(!c(t)){if(n=a(t))return n;t=[t]}for(;r0;){if(n=a(o.slice(0,i).join("-")))return n;if(s&&s.length>=i&&m(o,s,!0)>=i-1)break;i--}r++}return se.fn._lang}function N(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function L(t){var e,i,n=t.match(_e);for(e=0,i=n.length;i>e;e++)n[e]=Ve[n[e]]?Ve[n[e]]:N(n[e]);return function(s){var o="";for(e=0;i>e;e++)o+=n[e]instanceof Function?n[e].call(s,t):n[e];return o}}function k(t,e){return t.isValid()?(e=I(e,t.lang()),Ue[e]||(Ue[e]=L(e)),Ue[e](t)):t.lang().invalidDate()}function I(t,e){function i(t){return e.longDateFormat(t)||t}var n=5;for(be.lastIndex=0;n>=0&&be.test(t);)t=t.replace(be,i),be.lastIndex=0,n-=1;return t}function A(t,e){var i;switch(t){case"DDDD":return xe;case"YYYY":case"GGGG":case"gggg":return Se;case"YYYYY":case"GGGGG":case"ggggg":return Me;case"S":case"SS":case"SSS":case"DDD":return Te;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ce;case"a":case"A":return O(e._l)._meridiemParse;case"X":return Le;case"Z":case"ZZ":return Oe;case"T":return Ne;case"SSSS":return De;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"ww":case"W":case"WW":case"e":case"E":return Ee;default:return i=new RegExp(W(U(t.replace("\\","")),"i"))}}function P(t){var e=(Oe.exec(t)||[])[0],i=(e+"").match(Fe)||["-",0,0],n=+(60*i[1])+w(i[2]);return"+"===i[0]?-n:n}function F(t,e,i){var n,s=i._a;switch(t){case"M":case"MM":null!=e&&(s[de]=w(e)-1);break;case"MMM":case"MMMM":n=O(i._l).monthsParse(e),null!=n?s[de]=n:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(s[le]=w(e));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=w(e));break;case"YY":s[he]=w(e)+(w(e)>68?1900:2e3);break;case"YYYY":case"YYYYY":s[he]=w(e);break;case"a":case"A":i._isPm=O(i._l).isPM(e);break;case"H":case"HH":case"h":case"hh":s[pe]=w(e);break;case"m":case"mm":s[ue]=w(e);break;case"s":case"ss":s[ce]=w(e);break;case"S":case"SS":case"SSS":case"SSSS":s[fe]=w(1e3*("0."+e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=P(e);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":t=t.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=e)}}function Y(t){var e,i,n,s,o,r,a,h,d,l,p=[];if(!t._d){for(n=z(t),t._w&&null==t._a[le]&&null==t._a[de]&&(o=function(e){return e?e.length<3?parseInt(e,10)>68?"19"+e:"20"+e:e:null==t._a[he]?se().weekYear():t._a[he]},r=t._w,null!=r.GG||null!=r.W||null!=r.E?a=J(o(r.GG),r.W||1,r.E,4,1):(h=O(t._l),d=null!=r.d?q(r.d,h):null!=r.e?parseInt(r.e,10)+h._week.dow:0,l=parseInt(r.w,10)||1,null!=r.d&&db(s)&&(t._pf._overflowDayOfYear=!0),i=X(s,0,t._dayOfYear),t._a[de]=i.getUTCMonth(),t._a[le]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=p[e]=n[e];for(;7>e;e++)t._a[e]=p[e]=null==t._a[e]?2===e?1:0:t._a[e];p[pe]+=w((t._tzm||0)/60),p[ue]+=w((t._tzm||0)%60),t._d=(t._useUTC?X:B).apply(null,p)}}function R(t){var e;t._d||(e=v(t._i),t._a=[e.year,e.month,e.day,e.hour,e.minute,e.second,e.millisecond],Y(t))}function z(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function H(t){t._a=[],t._pf.empty=!0;var e,i,n,s,o,r=O(t._l),a=""+t._i,h=a.length,d=0;for(n=I(t._f,r).match(_e)||[],e=0;e0&&t._pf.unusedInput.push(o),a=a.slice(a.indexOf(i)+i.length),d+=i.length),Ve[s]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(s),F(s,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(s);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._isPm&&t._a[pe]<12&&(t._a[pe]+=12),t._isPm===!1&&12===t._a[pe]&&(t._a[pe]=0),Y(t),T(t)}function U(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,n,s){return e||i||n||s})}function W(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function j(t){var e,i,n,s,o;if(0===t._f.length)return t._pf.invalidFormat=!0,t._d=new Date(0/0),void 0;for(s=0;so)&&(n=o,i=e));d(t,i||e)}function V(t){var e,i=t._i,n=ke.exec(i);if(n){for(t._pf.iso=!0,e=4;e>0;e--)if(n[e]){t._f=Ae[e-1]+(n[6]||" ");break}for(e=0;4>e;e++)if(Pe[e][1].exec(i)){t._f+=Pe[e][0];break}Oe.exec(i)&&(t._f+="Z"),H(t)}else t._d=new Date(i)}function G(t){var e=t._i,i=ve.exec(e);e===n?t._d=new Date:i?t._d=new Date(+i[1]):"string"==typeof e?V(t):c(e)?(t._a=e.slice(0),Y(t)):f(e)?t._d=new Date(+e):"object"==typeof e?R(t):t._d=new Date(e)}function B(t,e,i,n,s,o,r){var a=new Date(t,e,i,n,s,o,r);return 1970>t&&a.setFullYear(t),a}function X(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function q(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function Z(t,e,i,n,s){return s.relativeTime(e||1,!!i,t,n)}function K(t,e,i){var n=ae(Math.abs(t)/1e3),s=ae(n/60),o=ae(s/60),r=ae(o/24),a=ae(r/365),h=45>n&&["s",n]||1===s&&["m"]||45>s&&["mm",s]||1===o&&["h"]||22>o&&["hh",o]||1===r&&["d"]||25>=r&&["dd",r]||45>=r&&["M"]||345>r&&["MM",ae(r/30)]||1===a&&["y"]||["yy",a];return h[2]=e,h[3]=t>0,h[4]=i,Z.apply({},h)}function $(t,e,i){var n,s=i-e,o=i-t.day();return o>s&&(o-=7),s-7>o&&(o+=7),n=se(t).add("d",o),{week:Math.ceil(n.dayOfYear()/7),year:n.year()}}function J(t,e,i,n,s){var o,r,a=new Date(Date.UTC(t,0)).getUTCDay();return i=null!=i?i:s,o=s-a+(a>n?7:0),r=7*(e-1)+(i-s)+o+1,{year:r>0?t:t-1,dayOfYear:r>0?r:b(t-1)+r}}function Q(t){var e=t._i,i=t._f;return"undefined"==typeof t._pf&&x(t),null===e?se.invalid({nullInput:!0}):("string"==typeof e&&(t._i=e=O().preparse(e)),se.isMoment(e)?(t=d({},e),t._d=new Date(+e._d)):i?c(i)?j(t):H(t):G(t),new a(t))}function te(t,e){se.fn[t]=se.fn[t+"s"]=function(t){var i=this._isUTC?"UTC":"";return null!=t?(this._d["set"+i+e](t),se.updateOffset(this),this):this._d["get"+i+e]()}}function ee(t){se.duration.fn[t]=function(){return this._data[t]}}function ie(t,e){se.duration.fn["as"+t]=function(){return+this/e}}function ne(t){var e=!1,i=se;"undefined"==typeof ender&&(this.moment=t?function(){return!e&&console&&console.warn&&(e=!0,console.warn("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.")),i.apply(null,arguments)}:se)}for(var se,oe,re="2.4.0",ae=Math.round,he=0,de=1,le=2,pe=3,ue=4,ce=5,fe=6,me={},ge="undefined"!=typeof i&&i.exports,ve=/^\/?Date\((\-?\d+)/i,ye=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,we=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,_e=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,be=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,Ee=/\d\d?/,Te=/\d{1,3}/,xe=/\d{3}/,Se=/\d{1,4}/,Me=/[+\-]?\d{1,6}/,De=/\d+/,Ce=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Oe=/Z|[\+\-]\d\d:?\d\d/i,Ne=/T/i,Le=/[\+\-]?\d+(\.\d{1,3})?/,ke=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d:?\d\d|Z)?)?$/,Ie="YYYY-MM-DDTHH:mm:ssZ",Ae=["YYYY-MM-DD","GGGG-[W]WW","GGGG-[W]WW-E","YYYY-DDD"],Pe=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Fe=/([\+\-]|\d\d)/gi,Ye="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),Re={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},ze={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},He={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},Ue={},We="DDD w W M D d".split(" "),je="M D H h m s w W".split(" "),Ve={M:function(){return this.month()+1},MMM:function(t){return this.lang().monthsShort(this,t)},MMMM:function(t){return this.lang().months(this,t)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(t){return this.lang().weekdaysMin(this,t)},ddd:function(t){return this.lang().weekdaysShort(this,t)},dddd:function(t){return this.lang().weekdays(this,t)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return p(this.year()%100,2)},YYYY:function(){return p(this.year(),4)},YYYYY:function(){return p(this.year(),5)},gg:function(){return p(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return p(this.weekYear(),5)},GG:function(){return p(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return p(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return w(this.milliseconds()/100)},SS:function(){return p(w(this.milliseconds()/10),2)},SSS:function(){return p(this.milliseconds(),3)},SSSS:function(){return p(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+p(w(t/60),2)+":"+p(w(t)%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+p(w(10*t/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}},Ge=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];We.length;)oe=We.pop(),Ve[oe+"o"]=o(Ve[oe],oe);for(;je.length;)oe=je.pop(),Ve[oe+oe]=s(Ve[oe],2);for(Ve.DDDD=s(Ve.DDD,3),d(r.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,i,n;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(i=se.utc([2e3,e]),n="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[e]=new RegExp(n.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,n;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=se([2e3,1]).day(e),n="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(n.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var i=this._calendar[t];return"function"==typeof i?i.apply(e):i},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,n){var s=this._relativeTime[i];return"function"==typeof s?s(t,e,i,n):s.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return $(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),se=function(t,e,i,s){return"boolean"==typeof i&&(s=i,i=n),Q({_i:t,_f:e,_l:i,_strict:s,_isUTC:!1})},se.utc=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o=Q({_useUTC:!0,_isUTC:!0,_l:i,_i:t,_f:e,_strict:s}).utc()},se.unix=function(t){return se(1e3*t)},se.duration=function(t,e){var i,n,s,o=se.isDuration(t),r="number"==typeof t,a=o?t._input:r?{}:t,d=null;return r?e?a[e]=t:a.milliseconds=t:(d=ye.exec(t))?(i="-"===d[1]?-1:1,a={y:0,d:w(d[le])*i,h:w(d[pe])*i,m:w(d[ue])*i,s:w(d[ce])*i,ms:w(d[fe])*i}):(d=we.exec(t))&&(i="-"===d[1]?-1:1,s=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},a={y:s(d[2]),M:s(d[3]),d:s(d[4]),h:s(d[5]),m:s(d[6]),s:s(d[7]),w:s(d[8])}),n=new h(a),o&&t.hasOwnProperty("_lang")&&(n._lang=t._lang),n},se.version=re,se.defaultFormat=Ie,se.updateOffset=function(){},se.lang=function(t,e){var i;return t?(e?D(M(t),e):null===e?(C(t),t="en"):me[t]||O(t),i=se.duration.fn._lang=se.fn._lang=O(t),i._abbr):se.fn._lang._abbr},se.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),O(t)},se.isMoment=function(t){return t instanceof a},se.isDuration=function(t){return t instanceof h},oe=Ge.length-1;oe>=0;--oe)y(Ge[oe]);for(se.normalizeUnits=function(t){return g(t)},se.invalid=function(t){var e=se.utc(0/0);return null!=t?d(e._pf,t):e._pf.userInvalidated=!0,e},se.parseZone=function(t){return se(t).parseZone()},d(se.fn=a.prototype,{clone:function(){return se(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return k(se(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var t=this;return[t.year(),t.month(),t.date(),t.hours(),t.minutes(),t.seconds(),t.milliseconds()]},isValid:function(){return S(this)},isDSTShifted:function(){return this._a?this.isValid()&&m(this._a,(this._isUTC?se.utc(this._a):se(this._a)).toArray())>0:!1},parsingFlags:function(){return d({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=k(this,t||se.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var i;return i="string"==typeof t?se.duration(+e,t):se.duration(t,e),u(this,i,1),this},subtract:function(t,e){var i;return i="string"==typeof t?se.duration(+e,t):se.duration(t,e),u(this,i,-1),this},diff:function(t,e,i){var n,s,o=this._isUTC?se(t).zone(this._offset||0):se(t).local(),r=6e4*(this.zone()-o.zone());return e=g(e),"year"===e||"month"===e?(n=432e5*(this.daysInMonth()+o.daysInMonth()),s=12*(this.year()-o.year())+(this.month()-o.month()),s+=(this-se(this).startOf("month")-(o-se(o).startOf("month")))/n,s-=6e4*(this.zone()-se(this).startOf("month").zone()-(o.zone()-se(o).startOf("month").zone()))/n,"year"===e&&(s/=12)):(n=this-o,s="second"===e?n/1e3:"minute"===e?n/6e4:"hour"===e?n/36e5:"day"===e?(n-r)/864e5:"week"===e?(n-r)/6048e5:n),i?s:l(s)
-},from:function(t,e){return se.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(se(),t)},calendar:function(){var t=this.diff(se().zone(this.zone()).startOf("day"),"days",!0),e=-6>t?"sameElse":-1>t?"lastWeek":0>t?"lastDay":1>t?"sameDay":2>t?"nextDay":7>t?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){return E(this.year())},isDST:function(){return this.zone()+se(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+se(t).startOf(e)},isSame:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)===+se(t).startOf(e)},min:function(t){return t=se.apply(null,arguments),this>t?this:t},max:function(t){return t=se.apply(null,arguments),t>this?this:t},zone:function(t){var e=this._offset||0;return null==t?this._isUTC?e:this._d.getTimezoneOffset():("string"==typeof t&&(t=P(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,e!==t&&u(this,se.duration(e-t,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(t){return t=t?se(t).zone():0,0===(this.zone()-t)%60},daysInMonth:function(){return _(this.year(),this.month())},dayOfYear:function(t){var e=ae((se(this).startOf("day")-se(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},weekYear:function(t){var e=$(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=$(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=$(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this.day()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},get:function(t){return t=g(t),this[t]()},set:function(t,e){return t=g(t),"function"==typeof this[t]&&this[t](e),this},lang:function(t){return t===n?this._lang:(this._lang=O(t),this)}}),oe=0;oei;++i)t.call(e||this,this[i],i,this)}),Array.prototype.map||(Array.prototype.map=function(t,e){var i,n,s;if(null==this)throw new TypeError(" this is null or not defined");var o=Object(this),r=o.length>>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(e&&(i=e),n=new Array(r),s=0;r>s;){var a,h;s in o&&(a=o[s],h=t.call(i,a,s,o),n[s]=h),s++}return n}),Array.prototype.filter||(Array.prototype.filter=function(t){"use strict";if(null==this)throw new TypeError;var e=Object(this),i=e.length>>>0;if("function"!=typeof t)throw new TypeError;for(var n=[],s=arguments[1],o=0;i>o;o++)if(o in e){var r=e[o];t.call(s,r,o,e)&&n.push(r)}return n}),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!{toString:null}.propertyIsEnumerable("toString"),i=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],n=i.length;return function(s){if("object"!=typeof s&&"function"!=typeof s||null===s)throw new TypeError("Object.keys called on non-object");var o=[];for(var r in s)t.call(s,r)&&o.push(r);if(e)for(var a=0;n>a;a++)t.call(s,i[a])&&o.push(i[a]);return o}}()),Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},s=function(){return i.apply(this instanceof n&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,s.prototype=new n,s}),Object.create||(Object.create=function(t){function e(){}if(arguments.length>1)throw new Error("Object.create implementation only accepts the first parameter.");return e.prototype=t,new e}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},s=function(){return i.apply(this instanceof n&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,s.prototype=new n,s});var k={};k.isNumber=function(t){return t instanceof Number||"number"==typeof t},k.isString=function(t){return t instanceof String||"string"==typeof t},k.isDate=function(t){if(t instanceof Date)return!0;if(k.isString(t)){var e=I.exec(t);if(e)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},k.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},k.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},k.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var n=arguments[e];for(var s in n)n.hasOwnProperty(s)&&void 0!==n[s]&&(t[s]=n[s])}return t},k.convert=function(t,e){var i;if(void 0===t)return void 0;if(null===t)return null;if(!e)return t;if("string"!=typeof e&&!(e instanceof String))throw new Error("Type must be a string");switch(e){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return String(t);case"Date":if(k.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(N.isMoment(t))return new Date(t.valueOf());if(k.isString(t))return i=I.exec(t),i?new Date(Number(i[1])):N(t).toDate();throw new Error("Cannot convert object of type "+k.getType(t)+" to type Date");case"Moment":if(k.isNumber(t))return N(t);if(t instanceof Date)return N(t.valueOf());if(N.isMoment(t))return N(t);if(k.isString(t))return i=I.exec(t),i?N(Number(i[1])):N(t);throw new Error("Cannot convert object of type "+k.getType(t)+" to type Date");case"ISODate":if(k.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(N.isMoment(t))return t.toDate().toISOString();if(k.isString(t))return i=I.exec(t),i?new Date(Number(i[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+k.getType(t)+" to type ISODate");case"ASPDate":if(k.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(k.isString(t)){i=I.exec(t);var n;return n=i?new Date(Number(i[1])).valueOf():new Date(t).valueOf(),"/Date("+n+")/"}throw new Error("Cannot convert object of type "+k.getType(t)+" to type ASPDate");default:throw new Error("Cannot convert object of type "+k.getType(t)+' to type "'+e+'"')}};var I=/^\/?Date\((\-?\d+)/i;k.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":t instanceof Array?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},k.getAbsoluteLeft=function(t){for(var e=document.documentElement,i=document.body,n=t.offsetLeft,s=t.offsetParent;null!=s&&s!=i&&s!=e;)n+=s.offsetLeft,n-=s.scrollLeft,s=s.offsetParent;return n},k.getAbsoluteTop=function(t){for(var e=document.documentElement,i=document.body,n=t.offsetTop,s=t.offsetParent;null!=s&&s!=i&&s!=e;)n+=s.offsetTop,n-=s.scrollTop,s=s.offsetParent;return n},k.getPageY=function(t){if("pageY"in t)return t.pageY;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientY:t.clientY;var i=document.documentElement,n=document.body;return e+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)},k.getPageX=function(t){if("pageY"in t)return t.pageX;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientX:t.clientX;var i=document.documentElement,n=document.body;return e+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0)},k.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},k.removeClassName=function(t,e){var i=t.className.split(" "),n=i.indexOf(e);-1!=n&&(i.splice(n,1),t.className=i.join(" "))},k.forEach=function(t,e){var i,n;if(t instanceof Array)for(i=0,n=t.length;n>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},k.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},k.addEventListener=function(t,e,i,n){t.addEventListener?(void 0===n&&(n=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,n)):t.attachEvent("on"+e,i)},k.removeEventListener=function(t,e,i,n){t.removeEventListener?(void 0===n&&(n=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,n)):t.detachEvent("on"+e,i)},k.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},k.stopPropagation=function(t){t||(t=window.event),t.stopPropagation?t.stopPropagation():t.cancelBubble=!0},k.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},k.option={},k.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},k.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},k.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},k.option.asSize=function(t,e){return"function"==typeof t&&(t=t()),k.isString(t)?t:k.isNumber(t)?t+"px":e||null},k.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},k.loadCss=function(t){if("undefined"!=typeof document){var e=document.createElement("style");e.type="text/css",e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),document.getElementsByTagName("head")[0].appendChild(e)}};var A={listeners:[],indexOf:function(t){for(var e=this.listeners,i=0,n=this.listeners.length;n>i;i++){var s=e[i];if(s&&s.object==t)return i}return-1},addListener:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];s||(s={object:t,events:{}},this.listeners.push(s));var o=s.events[e];o||(o=[],s.events[e]=o),-1==o.indexOf(i)&&o.push(i)},removeListener:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];if(s){var o=s.events[e];o&&(n=o.indexOf(i),-1!=n&&o.splice(n,1),0==o.length&&delete s.events[e]);var r=0,a=s.events;for(var h in a)a.hasOwnProperty(h)&&r++;0==r&&delete this.listeners[n]}},removeAllListeners:function(){this.listeners=[]},trigger:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];if(s){var o=s.events[e];if(o)for(var r=0,a=o.length;a>r;r++)o[r](i)}}};s.prototype.on=function(t,e,i){var n=t instanceof RegExp?t:new RegExp(t.replace("*","\\w+")),s={id:k.randomUUID(),event:t,regexp:n,callback:"function"==typeof e?e:null,target:i};return this.subscriptions.push(s),s.id},s.prototype.off=function(t){for(var e=0;eo;o++)i=s._addItem(t[o]),n.push(i);else if(k.isDataTable(t))for(var a=this._getColumnNames(t),h=0,d=t.getNumberOfRows();d>h;h++){for(var l={},p=0,u=a.length;u>p;p++){var c=a[p];l[c]=t.getValue(h,p)}i=s._addItem(l),n.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=s._addItem(t),n.push(i)}return n.length&&this._trigger("add",{items:n},e),n},o.prototype.update=function(t,e){var i=[],n=[],s=this,o=s.fieldId,r=function(t){var e=t[o];s.data[e]?(e=s._updateItem(t),n.push(e)):(e=s._addItem(t),i.push(e))};if(t instanceof Array)for(var a=0,h=t.length;h>a;a++)r(t[a]);else if(k.isDataTable(t))for(var d=this._getColumnNames(t),l=0,p=t.getNumberOfRows();p>l;l++){for(var u={},c=0,f=d.length;f>c;c++){var m=d[c];u[m]=t.getValue(l,c)}r(u)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");r(t)}return i.length&&this._trigger("add",{items:i},e),n.length&&this._trigger("update",{items:n},e),i.concat(n)},o.prototype.get=function(){var t,e,i,n,s=this,o=k.getType(arguments[0]);"String"==o||"Number"==o?(t=arguments[0],i=arguments[1],n=arguments[2]):"Array"==o?(e=arguments[0],i=arguments[1],n=arguments[2]):(i=arguments[0],n=arguments[1]);var r;if(i&&i.type){if(r="DataTable"==i.type?"DataTable":"Array",n&&r!=k.getType(n))throw new Error('Type of parameter "data" ('+k.getType(n)+") "+"does not correspond with specified options.type ("+i.type+")");if("DataTable"==r&&!k.isDataTable(n))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else r=n?"DataTable"==k.getType(n)?"DataTable":"Array":"Array";var a,h,d,l,p=i&&i.convert||this.options.convert,u=i&&i.filter,c=[];if(void 0!=t)a=s._getItem(t,p),u&&!u(a)&&(a=null);else if(void 0!=e)for(d=0,l=e.length;l>d;d++)a=s._getItem(e[d],p),(!u||u(a))&&c.push(a);else for(h in this.data)this.data.hasOwnProperty(h)&&(a=s._getItem(h,p),(!u||u(a))&&c.push(a));if(i&&i.order&&void 0==t&&this._sort(c,i.order),i&&i.fields){var f=i.fields;if(void 0!=t)a=this._filterFields(a,f);else for(d=0,l=c.length;l>d;d++)c[d]=this._filterFields(c[d],f)}if("DataTable"==r){var m=this._getColumnNames(n);if(void 0!=t)s._appendRow(n,m,a);else for(d=0,l=c.length;l>d;d++)s._appendRow(n,m,c[d]);return n}if(void 0!=t)return a;if(n){for(d=0,l=c.length;l>d;d++)n.push(c[d]);return n}return c},o.prototype.getIds=function(t){var e,i,n,s,o,r=this.data,a=t&&t.filter,h=t&&t.order,d=t&&t.convert||this.options.convert,l=[];if(a)if(h){o=[];for(n in r)r.hasOwnProperty(n)&&(s=this._getItem(n,d),a(s)&&o.push(s));for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(n in r)r.hasOwnProperty(n)&&(s=this._getItem(n,d),a(s)&&l.push(s[this.fieldId]));else if(h){o=[];for(n in r)r.hasOwnProperty(n)&&o.push(r[n]);for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(n in r)r.hasOwnProperty(n)&&(s=r[n],l.push(s[this.fieldId]));return l},o.prototype.forEach=function(t,e){var i,n,s=e&&e.filter,o=e&&e.convert||this.options.convert,r=this.data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],n=i[this.fieldId],t(i,n);else for(n in r)r.hasOwnProperty(n)&&(i=this._getItem(n,o),(!s||s(i))&&t(i,n))},o.prototype.map=function(t,e){var i,n=e&&e.filter,s=e&&e.convert||this.options.convert,o=[],r=this.data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,s),(!n||n(i))&&o.push(t(i,a)));return e&&e.order&&this._sort(o,e.order),o},o.prototype._filterFields=function(t,e){var i={};for(var n in t)t.hasOwnProperty(n)&&-1!=e.indexOf(n)&&(i[n]=t[n]);return i},o.prototype._sort=function(t,e){if(k.isString(e)){var i=e;t.sort(function(t,e){var n=t[i],s=e[i];return n>s?1:s>n?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},o.prototype.remove=function(t,e){var i,n,s,o=[];if(t instanceof Array)for(i=0,n=t.length;n>i;i++)s=this._remove(t[i]),null!=s&&o.push(s);else s=this._remove(t),null!=s&&o.push(s);return o.length&&this._trigger("remove",{items:o},e),o},o.prototype._remove=function(t){if(k.isNumber(t)||k.isString(t)){if(this.data[t])return delete this.data[t],delete this.internalIds[t],t}else if(t instanceof Object){var e=t[this.fieldId];if(e&&this.data[e])return delete this.data[e],delete this.internalIds[e],e}return null},o.prototype.clear=function(t){var e=Object.keys(this.data);return this.data={},this.internalIds={},this._trigger("remove",{items:e},t),e},o.prototype.max=function(t){var e=this.data,i=null,n=null;for(var s in e)if(e.hasOwnProperty(s)){var o=e[s],r=o[t];null!=r&&(!i||r>n)&&(i=o,n=r)}return i},o.prototype.min=function(t){var e=this.data,i=null,n=null;for(var s in e)if(e.hasOwnProperty(s)){var o=e[s],r=o[t];null!=r&&(!i||n>r)&&(i=o,n=r)}return i},o.prototype.distinct=function(t){var e=this.data,i=[],n=this.options.convert[t],s=0;for(var o in e)if(e.hasOwnProperty(o)){for(var r=e[o],a=k.convert(r[t],n),h=!1,d=0;s>d;d++)if(i[d]==a){h=!0;break}h||(i[s]=a,s++)}return i},o.prototype._addItem=function(t){var e=t[this.fieldId];if(void 0!=e){if(this.data[e])throw new Error("Cannot add item: item with id "+e+" already exists")}else e=k.randomUUID(),t[this.fieldId]=e,this.internalIds[e]=t;var i={};for(var n in t)if(t.hasOwnProperty(n)){var s=this.convert[n];i[n]=k.convert(t[n],s)}return this.data[e]=i,e},o.prototype._getItem=function(t,e){var i,n,s=this.data[t];if(!s)return null;var o={},r=this.fieldId,a=this.internalIds;if(e)for(i in s)s.hasOwnProperty(i)&&(n=s[i],i==r&&n in a||(o[i]=k.convert(n,e[i])));else for(i in s)s.hasOwnProperty(i)&&(n=s[i],i==r&&n in a||(o[i]=n));return o},o.prototype._updateItem=function(t){var e=t[this.fieldId];if(void 0==e)throw new Error("Cannot update item: item has no id (item: "+JSON.stringify(t)+")");var i=this.data[e];if(!i)throw new Error("Cannot update item: no item with id "+e+" found");for(var n in t)if(t.hasOwnProperty(n)){var s=this.convert[n];i[n]=k.convert(t[n],s)}return e},o.prototype._getColumnNames=function(t){for(var e=[],i=0,n=t.getNumberOfColumns();n>i;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},o.prototype._appendRow=function(t,e,i){for(var n=t.addRow(),s=0,o=e.length;o>s;s++){var r=e[s];t.setValue(n,s,i[r])}},r.prototype.setData=function(t){var e,i,n;if(this.data){this.data.unsubscribe&&this.data.unsubscribe("*",this.listener),e=[];for(var s in this.ids)this.ids.hasOwnProperty(s)&&e.push(s);this.ids={},this._trigger("remove",{items:e})}if(this.data=t,this.data){for(this.fieldId=this.options.fieldId||this.data&&this.data.options&&this.data.options.fieldId||"id",e=this.data.getIds({filter:this.options&&this.options.filter}),i=0,n=e.length;n>i;i++)s=e[i],this.ids[s]=!0;this._trigger("add",{items:e}),this.data.subscribe&&this.data.subscribe("*",this.listener)}},r.prototype.get=function(){var t,e,i,n=this,s=k.getType(arguments[0]);"String"==s||"Number"==s||"Array"==s?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var o=k.extend({},this.options,e);this.options.filter&&e&&e.filter&&(o.filter=function(t){return n.options.filter(t)&&e.filter(t)});var r=[];return void 0!=t&&r.push(t),r.push(o),r.push(i),this.data&&this.data.get.apply(this.data,r)},r.prototype.getIds=function(t){var e;if(this.data){var i,n=this.options.filter;i=t&&t.filter?n?function(e){return n(e)&&t.filter(e)}:t.filter:n,e=this.data.getIds({filter:i,order:t&&t.order})}else e=[];return e},r.prototype._onEvent=function(t,e,i){var n,s,o,r,a=e&&e.items,h=this.data,d=[],l=[],p=[];
-if(a&&h){switch(t){case"add":for(n=0,s=a.length;s>n;n++)o=a[n],r=this.get(o),r&&(this.ids[o]=!0,d.push(o));break;case"update":for(n=0,s=a.length;s>n;n++)o=a[n],r=this.get(o),r?this.ids[o]?l.push(o):(this.ids[o]=!0,d.push(o)):this.ids[o]&&(delete this.ids[o],p.push(o));break;case"remove":for(n=0,s=a.length;s>n;n++)o=a[n],this.ids[o]&&(delete this.ids[o],p.push(o))}d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),p.length&&this._trigger("remove",{items:p},i)}},r.prototype.subscribe=o.prototype.subscribe,r.prototype.unsubscribe=o.prototype.unsubscribe,r.prototype._trigger=o.prototype._trigger,TimeStep=function(t,e,i){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale=TimeStep.SCALE.DAY,this.step=1,this.setRange(t,e,i)},TimeStep.SCALE={MILLISECOND:1,SECOND:2,MINUTE:3,HOUR:4,DAY:5,WEEKDAY:6,MONTH:7,YEAR:8},TimeStep.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},TimeStep.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},TimeStep.prototype.roundToMinor=function(){switch(this.scale){case TimeStep.SCALE.YEAR:this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case TimeStep.SCALE.MONTH:this.current.setDate(1);case TimeStep.SCALE.DAY:case TimeStep.SCALE.WEEKDAY:this.current.setHours(0);case TimeStep.SCALE.HOUR:this.current.setMinutes(0);case TimeStep.SCALE.MINUTE:this.current.setSeconds(0);case TimeStep.SCALE.SECOND:this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},TimeStep.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},TimeStep.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current=new Date(this.current.valueOf()+1e3*this.step);break;case TimeStep.SCALE.MINUTE:this.current=new Date(this.current.valueOf()+60*1e3*this.step);break;case TimeStep.SCALE.HOUR:this.current=new Date(this.current.valueOf()+60*60*1e3*this.step);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()+this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()+this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()+this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.getMilliseconds()0&&(this.step=e),this.autoScale=!1},TimeStep.prototype.setAutoScale=function(t){this.autoScale=t},TimeStep.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,n=864e5,s=36e5,o=6e4,r=1e3,a=1;1e3*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1e3),500*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=500),100*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=100),50*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=50),10*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=10),5*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=5),e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1),3*i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=3),i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=1),5*n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=5),2*n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=2),n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=1),n/2>t&&(this.scale=TimeStep.SCALE.WEEKDAY,this.step=1),4*s>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=4),s>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=1),15*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=15),10*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=10),5*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=5),o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=1),15*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=15),10*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=10),5*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=5),r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=1),200*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=200),100*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=100),50*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=50),10*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=10),5*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=5),a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=1)}},TimeStep.prototype.snap=function(t){if(this.scale==TimeStep.SCALE.YEAR){var e=t.getFullYear()+Math.round(t.getMonth()/12);t.setFullYear(Math.round(e/this.step)*this.step),t.setMonth(0),t.setDate(0),t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.MONTH)t.getDate()>15?(t.setDate(1),t.setMonth(t.getMonth()+1)):t.setDate(1),t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0);else if(this.scale==TimeStep.SCALE.DAY||this.scale==TimeStep.SCALE.WEEKDAY){switch(this.step){case 5:case 2:t.setHours(24*Math.round(t.getHours()/24));break;default:t.setHours(12*Math.round(t.getHours()/12))}t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.HOUR){switch(this.step){case 4:t.setMinutes(60*Math.round(t.getMinutes()/60));break;default:t.setMinutes(30*Math.round(t.getMinutes()/30))}t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.MINUTE){switch(this.step){case 15:case 10:t.setMinutes(5*Math.round(t.getMinutes()/5)),t.setSeconds(0);break;case 5:t.setSeconds(60*Math.round(t.getSeconds()/60));break;default:t.setSeconds(30*Math.round(t.getSeconds()/30))}t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.SECOND)switch(this.step){case 15:case 10:t.setSeconds(5*Math.round(t.getSeconds()/5)),t.setMilliseconds(0);break;case 5:t.setMilliseconds(1e3*Math.round(t.getMilliseconds()/1e3));break;default:t.setMilliseconds(500*Math.round(t.getMilliseconds()/500))}else if(this.scale==TimeStep.SCALE.MILLISECOND){var i=this.step>5?this.step/2:1;t.setMilliseconds(Math.round(t.getMilliseconds()/i)*i)}},TimeStep.prototype.isMajor=function(){switch(this.scale){case TimeStep.SCALE.MILLISECOND:return 0==this.current.getMilliseconds();case TimeStep.SCALE.SECOND:return 0==this.current.getSeconds();case TimeStep.SCALE.MINUTE:return 0==this.current.getHours()&&0==this.current.getMinutes();case TimeStep.SCALE.HOUR:return 0==this.current.getHours();case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return 1==this.current.getDate();case TimeStep.SCALE.MONTH:return 0==this.current.getMonth();case TimeStep.SCALE.YEAR:return!1;default:return!1}},TimeStep.prototype.getLabelMinor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return N(t).format("SSS");case TimeStep.SCALE.SECOND:return N(t).format("s");case TimeStep.SCALE.MINUTE:return N(t).format("HH:mm");case TimeStep.SCALE.HOUR:return N(t).format("HH:mm");case TimeStep.SCALE.WEEKDAY:return N(t).format("ddd D");case TimeStep.SCALE.DAY:return N(t).format("D");case TimeStep.SCALE.MONTH:return N(t).format("MMM");case TimeStep.SCALE.YEAR:return N(t).format("YYYY");default:return""}},TimeStep.prototype.getLabelMajor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return N(t).format("HH:mm:ss");case TimeStep.SCALE.SECOND:return N(t).format("D MMMM HH:mm");case TimeStep.SCALE.MINUTE:case TimeStep.SCALE.HOUR:return N(t).format("ddd D MMMM");case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return N(t).format("MMMM YYYY");case TimeStep.SCALE.MONTH:return N(t).format("YYYY");case TimeStep.SCALE.YEAR:return"";default:return""}},a.prototype.setOptions=function(t){k.extend(this.options,t)},a.prototype.update=function(){this._order(),this._stack()},a.prototype._order=function(){var t=this.parent.items;if(!t)throw new Error("Cannot stack items: parent does not contain items");var e=[],i=0;k.forEach(t,function(t){t.visible&&(e[i]=t,i++)});var n=this.options.order||this.defaultOptions.order;if("function"!=typeof n)throw new Error("Option order must be a function");e.sort(n),this.ordered=e},a.prototype._stack=function(){var t,e,i,n=this.ordered,s=this.options,o=s.orientation||this.defaultOptions.orientation,r="top"==o;for(i=s.margin&&void 0!==s.margin.item?s.margin.item:this.defaultOptions.margin.item,t=0,e=n.length;e>t;t++){var a=n[t],h=null;do h=this.checkOverlap(n,t,0,t-1,i),null!=h&&(a.top=r?h.top+h.height+i:h.top-a.height-i);while(h)}},a.prototype.checkOverlap=function(t,e,i,n,s){for(var o=this.collision,r=t[e],a=n;a>=i;a--){var h=t[a];if(o(r,h,s)&&a!=e)return h}return null},a.prototype.collision=function(t,e,i){return t.left-ie.left&&t.top-ie.top},h.prototype.setOptions=function(t){k.extend(this.options,t),null!==this.start&&null!==this.end&&this.setRange(this.start,this.end)},h.prototype.subscribe=function(t,e,i){var n,s=this;if("horizontal"!=i&&"vertical"!=i)throw new TypeError('Unknown direction "'+i+'". '+'Choose "horizontal" or "vertical".');if("move"==e)n={component:t,event:e,direction:i,callback:function(t){s._onMouseDown(t,n)},params:{}},t.on("mousedown",n.callback),s.listeners.push(n);else{if("zoom"!=e)throw new TypeError('Unknown event "'+e+'". '+'Choose "move" or "zoom".');n={component:t,event:e,direction:i,callback:function(t){s._onMouseWheel(t,n)},params:{}},t.on("mousewheel",n.callback),s.listeners.push(n)}},h.prototype.on=function(t,e){A.addListener(this,t,e)},h.prototype._trigger=function(t){A.trigger(this,t,{start:this.start,end:this.end})},h.prototype.setRange=function(t,e){var i=this._applyRange(t,e);i&&(this._trigger("rangechange"),this._trigger("rangechanged"))},h.prototype._applyRange=function(t,e){var i,n=null!=t?k.convert(t,"Number"):this.start,s=null!=e?k.convert(e,"Number"):this.end,o=null!=this.options.max?k.convert(this.options.max,"Date").valueOf():null,r=null!=this.options.min?k.convert(this.options.min,"Date").valueOf():null;if(isNaN(n)||null===n)throw new Error('Invalid start "'+t+'"');if(isNaN(s)||null===s)throw new Error('Invalid end "'+e+'"');if(n>s&&(s=n),null!==r&&r>n&&(i=r-n,n+=i,s+=i,null!=o&&s>o&&(s=o)),null!==o&&s>o&&(i=s-o,n-=i,s-=i,null!=r&&r>n&&(n=r)),null!==this.options.zoomMin){var a=parseFloat(this.options.zoomMin);0>a&&(a=0),a>s-n&&(this.end-this.start===a?(n=this.start,s=this.end):(i=a-(s-n),n-=i/2,s+=i/2))}if(null!==this.options.zoomMax){var h=parseFloat(this.options.zoomMax);0>h&&(h=0),s-n>h&&(this.end-this.start===h?(n=this.start,s=this.end):(i=s-n-h,n+=i/2,s-=i/2))}var d=this.start!=n||this.end!=s;return this.start=n,this.end=s,d},h.prototype.getRange=function(){return{start:this.start,end:this.end}},h.prototype.conversion=function(t){return this.start,this.end,h.conversion(this.start,this.end,t)},h.conversion=function(t,e,i){return 0!=i&&0!=e-t?{offset:t,factor:i/(e-t)}:{offset:0,factor:1}},h.prototype._onMouseDown=function(t,e){t=t||window.event;var i=e.params,n=t.which?1==t.which:1==t.button;if(n){i.mouseX=k.getPageX(t),i.mouseY=k.getPageY(t),i.previousLeft=0,i.previousOffset=0,i.moved=!1,i.start=this.start,i.end=this.end;var s=e.component.frame;s&&(s.style.cursor="move");var o=this;i.onMouseMove||(i.onMouseMove=function(t){o._onMouseMove(t,e)},k.addEventListener(document,"mousemove",i.onMouseMove)),i.onMouseUp||(i.onMouseUp=function(t){o._onMouseUp(t,e)},k.addEventListener(document,"mouseup",i.onMouseUp)),k.preventDefault(t)}},h.prototype._onMouseMove=function(t,e){t=t||window.event;var i=e.params,n=k.getPageX(t),s=k.getPageY(t);void 0==i.mouseX&&(i.mouseX=n),void 0==i.mouseY&&(i.mouseY=s);var o=n-i.mouseX,r=s-i.mouseY,a="horizontal"==e.direction?o:r;Math.abs(a)>=1&&(i.moved=!0);var h=i.end-i.start,d="horizontal"==e.direction?e.component.width:e.component.height,l=-a/d*h;this._applyRange(i.start+l,i.end+l),this._trigger("rangechange"),k.preventDefault(t)},h.prototype._onMouseUp=function(t,e){t=t||window.event;var i=e.params;e.component.frame&&(e.component.frame.style.cursor="auto"),i.onMouseMove&&(k.removeEventListener(document,"mousemove",i.onMouseMove),i.onMouseMove=null),i.onMouseUp&&(k.removeEventListener(document,"mouseup",i.onMouseUp),i.onMouseUp=null),i.moved&&this._trigger("rangechanged")},h.prototype._onMouseWheel=function(t,e){t=t||window.event;var i=0;if(t.wheelDelta?i=t.wheelDelta/120:t.detail&&(i=-t.detail/3),i){var n=this,s=function(){var s=i/5,o=null,r=e.component.frame;if(r){var a,h;if("horizontal"==e.direction){a=e.component.width,h=n.conversion(a);var d=k.getAbsoluteLeft(r),l=k.getPageX(t);o=(l-d)/h.factor+h.offset}else{a=e.component.height,h=n.conversion(a);var p=k.getAbsoluteTop(r),u=k.getPageY(t);o=(p+a-u-p)/h.factor+h.offset}}n.zoom(s,o)};s()}k.preventDefault(t)},h.prototype.zoom=function(t,e){null==e&&(e=(this.start+this.end)/2),t>=1&&(t=.9),-1>=t&&(t=-.9),0>t&&(t/=1+t);var i=this.start-e,n=this.end-e,s=this.start-i*t,o=this.end-n*t;this.setRange(s,o)},h.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,n=this.end+e*t;this.start=i,this.end=n},h.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,n=this.start-i,s=this.end-i;this.setRange(n,s)},d.prototype.add=function(t){if(void 0==t.id)throw new Error("Component has no field id");if(!(t instanceof l||t instanceof d))throw new TypeError("Component must be an instance of prototype Component or Controller");t.controller=this,this.components[t.id]=t},d.prototype.remove=function(t){var e;for(e in this.components)if(this.components.hasOwnProperty(e)&&(e==t||this.components[e]==t))break;e&&delete this.components[e]},d.prototype.requestReflow=function(t){if(t)this.reflow();else if(!this.reflowTimer){var e=this;this.reflowTimer=setTimeout(function(){e.reflowTimer=void 0,e.reflow()},0)}},d.prototype.requestRepaint=function(t){if(t)this.repaint();else if(!this.repaintTimer){var e=this;this.repaintTimer=setTimeout(function(){e.repaintTimer=void 0,e.repaint()},0)}},d.prototype.repaint=function F(){function F(i,n){n in e||(i.depends&&i.depends.forEach(function(t){F(t,t.id)}),i.parent&&F(i.parent,i.parent.id),t=i.repaint()||t,e[n]=!0)}var t=!1;this.repaintTimer&&(clearTimeout(this.repaintTimer),this.repaintTimer=void 0);var e={};k.forEach(this.components,F),t&&this.reflow()},d.prototype.reflow=function Y(){function Y(i,n){n in e||(i.depends&&i.depends.forEach(function(t){Y(t,t.id)}),i.parent&&Y(i.parent,i.parent.id),t=i.reflow()||t,e[n]=!0)}var t=!1;this.reflowTimer&&(clearTimeout(this.reflowTimer),this.reflowTimer=void 0);var e={};k.forEach(this.components,Y),t&&this.repaint()},l.prototype.setOptions=function(t){t&&(k.extend(this.options,t),this.controller&&(this.requestRepaint(),this.requestReflow()))},l.prototype.getOption=function(t){var e;return this.options&&(e=this.options[t]),void 0===e&&this.defaultOptions&&(e=this.defaultOptions[t]),e},l.prototype.getContainer=function(){return null},l.prototype.getFrame=function(){return this.frame},l.prototype.repaint=function(){return!1},l.prototype.reflow=function(){return!1},l.prototype.hide=function(){return this.frame&&this.frame.parentNode?(this.frame.parentNode.removeChild(this.frame),!0):!1},l.prototype.show=function(){return this.frame&&this.frame.parentNode?!1:this.repaint()},l.prototype.requestRepaint=function(){if(!this.controller)throw new Error("Cannot request a repaint: no controller configured");this.controller.requestRepaint()},l.prototype.requestReflow=function(){if(!this.controller)throw new Error("Cannot request a reflow: no controller configured");this.controller.requestReflow()},p.prototype=new l,p.prototype.setOptions=l.prototype.setOptions,p.prototype.getContainer=function(){return this.frame},p.prototype.repaint=function(){var t=0,e=k.updateProperty,i=k.option.asSize,n=this.options,s=this.frame;if(!s){s=document.createElement("div"),s.className="panel";var o=n.className;o&&("function"==typeof o?k.addClassName(s,String(o())):k.addClassName(s,String(o))),this.frame=s,t+=1}if(!s.parentNode){if(!this.parent)throw new Error("Cannot repaint panel: no parent attached");var r=this.parent.getContainer();if(!r)throw new Error("Cannot repaint panel: parent has no container element");r.appendChild(s),t+=1}return t+=e(s.style,"top",i(n.top,"0px")),t+=e(s.style,"left",i(n.left,"0px")),t+=e(s.style,"width",i(n.width,"100%")),t+=e(s.style,"height",i(n.height,"100%")),t>0},p.prototype.reflow=function(){var t=0,e=k.updateProperty,i=this.frame;return i?(t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft),t+=e(this,"width",i.offsetWidth),t+=e(this,"height",i.offsetHeight)):t+=1,t>0},u.prototype=new p,u.prototype.setOptions=l.prototype.setOptions,u.prototype.repaint=function(){var t=0,e=k.updateProperty,i=k.option.asSize,n=this.options,s=this.frame;if(!s){s=document.createElement("div"),s.className="vis timeline rootpanel";var o=n.className;o&&k.addClassName(s,k.option.asString(o)),this.frame=s,t+=1}if(!s.parentNode){if(!this.container)throw new Error("Cannot repaint root panel: no container attached");this.container.appendChild(s),t+=1}return t+=e(s.style,"top",i(n.top,"0px")),t+=e(s.style,"left",i(n.left,"0px")),t+=e(s.style,"width",i(n.width,"100%")),t+=e(s.style,"height",i(n.height,"100%")),this._updateEventEmitters(),this._updateWatch(),t>0},u.prototype.reflow=function(){var t=0,e=k.updateProperty,i=this.frame;return i?(t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft),t+=e(this,"width",i.offsetWidth),t+=e(this,"height",i.offsetHeight)):t+=1,t>0},u.prototype._updateWatch=function(){var t=this.getOption("autoResize");t?this._watch():this._unwatch()},u.prototype._watch=function(){var t=this;this._unwatch();var e=function(){var e=t.getOption("autoResize");return e?(t.frame&&(t.frame.clientWidth!=t.width||t.frame.clientHeight!=t.height)&&t.requestReflow(),void 0):(t._unwatch(),void 0)};k.addEventListener(window,"resize",e),this.watchTimer=setInterval(e,1e3)},u.prototype._unwatch=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0)},u.prototype.on=function(t,e){var i=this.listeners[t];i||(i=[],this.listeners[t]=i),i.push(e),this._updateEventEmitters()},u.prototype._updateEventEmitters=function(){if(this.listeners){var t=this;k.forEach(this.listeners,function(e,i){if(t.emitters||(t.emitters={}),!(i in t.emitters)){var n=t.frame;if(n){var s=function(t){e.forEach(function(e){e(t)})};t.emitters[i]=s,k.addEventListener(n,i,s)}}})}},c.prototype=new l,c.prototype.setOptions=l.prototype.setOptions,c.prototype.setRange=function(t){if(!(t instanceof h||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},c.prototype.toTime=function(t){var e=this.conversion;return new Date(t/e.factor+e.offset)},c.prototype.toScreen=function(t){var e=this.conversion;return(t.valueOf()-e.offset)*e.factor},c.prototype.repaint=function(){var t=0,e=k.updateProperty,i=k.option.asSize,n=this.options,s=this.getOption("orientation"),o=this.props,r=this.step,a=this.frame;if(a||(a=document.createElement("div"),this.frame=a,t+=1),a.className="axis "+s,!a.parentNode){if(!this.parent)throw new Error("Cannot repaint time axis: no parent attached");var h=this.parent.getContainer();if(!h)throw new Error("Cannot repaint time axis: parent has no container element");h.appendChild(a),t+=1}var d=a.parentNode;if(d){var l=a.nextSibling;d.removeChild(a);var p="bottom"==s&&this.props.parentHeight&&this.height?this.props.parentHeight-this.height+"px":"0px";if(t+=e(a.style,"top",i(n.top,p)),t+=e(a.style,"left",i(n.left,"0px")),t+=e(a.style,"width",i(n.width,"100%")),t+=e(a.style,"height",i(n.height,this.height+"px")),this._repaintMeasureChars(),this.step){this._repaintStart(),r.first();for(var u=void 0,c=0;r.hasNext()&&1e3>c;){c++;var f=r.getCurrent(),m=this.toScreen(f),g=r.isMajor();this.getOption("showMinorLabels")&&this._repaintMinorText(m,r.getLabelMinor()),g&&this.getOption("showMajorLabels")?(m>0&&(void 0==u&&(u=m),this._repaintMajorText(m,r.getLabelMajor())),this._repaintMajorLine(m)):this._repaintMinorLine(m),r.next()}if(this.getOption("showMajorLabels")){var v=this.toTime(0),y=r.getLabelMajor(v),w=y.length*(o.majorCharWidth||10)+10;(void 0==u||u>w)&&this._repaintMajorText(0,y)}this._repaintEnd()}this._repaintLine(),l?d.insertBefore(a,l):d.appendChild(a)}return t>0},c.prototype._repaintStart=function(){var t=this.dom,e=t.redundant;e.majorLines=t.majorLines,e.majorTexts=t.majorTexts,e.minorLines=t.minorLines,e.minorTexts=t.minorTexts,t.majorLines=[],t.majorTexts=[],t.minorLines=[],t.minorTexts=[]},c.prototype._repaintEnd=function(){k.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},c.prototype._repaintMinorText=function(t,e){var i=this.dom.redundant.minorTexts.shift();if(!i){var n=document.createTextNode("");i=document.createElement("div"),i.appendChild(n),i.className="text minor",this.frame.appendChild(i)}this.dom.minorTexts.push(i),i.childNodes[0].nodeValue=e,i.style.left=t+"px",i.style.top=this.props.minorLabelTop+"px"},c.prototype._repaintMajorText=function(t,e){var i=this.dom.redundant.majorTexts.shift();if(!i){var n=document.createTextNode(e);i=document.createElement("div"),i.className="text major",i.appendChild(n),this.frame.appendChild(i)}this.dom.majorTexts.push(i),i.childNodes[0].nodeValue=e,i.style.top=this.props.majorLabelTop+"px",i.style.left=t+"px"},c.prototype._repaintMinorLine=function(t){var e=this.dom.redundant.minorLines.shift();e||(e=document.createElement("div"),e.className="grid vertical minor",this.frame.appendChild(e)),this.dom.minorLines.push(e);var i=this.props;e.style.top=i.minorLineTop+"px",e.style.height=i.minorLineHeight+"px",e.style.left=t-i.minorLineWidth/2+"px"},c.prototype._repaintMajorLine=function(t){var e=this.dom.redundant.majorLines.shift();e||(e=document.createElement("DIV"),e.className="grid vertical major",this.frame.appendChild(e)),this.dom.majorLines.push(e);var i=this.props;e.style.top=i.majorLineTop+"px",e.style.left=t-i.majorLineWidth/2+"px",e.style.height=i.majorLineHeight+"px"},c.prototype._repaintLine=function(){var t=this.dom.line,e=this.frame;this.options,this.getOption("showMinorLabels")||this.getOption("showMajorLabels")?(t?(e.removeChild(t),e.appendChild(t)):(t=document.createElement("div"),t.className="grid horizontal major",e.appendChild(t),this.dom.line=t),t.style.top=this.props.lineTop+"px"):t&&axis.parentElement&&(e.removeChild(axis.line),delete this.dom.line)},c.prototype._repaintMeasureChars=function(){var t,e=this.dom;if(!e.measureCharMinor){t=document.createTextNode("0");var i=document.createElement("DIV");i.className="text minor measure",i.appendChild(t),this.frame.appendChild(i),e.measureCharMinor=i}if(!e.measureCharMajor){t=document.createTextNode("0");var n=document.createElement("DIV");n.className="text major measure",n.appendChild(t),this.frame.appendChild(n),e.measureCharMajor=n}},c.prototype.reflow=function(){var t=0,e=k.updateProperty,i=this.frame,n=this.range;if(!n)throw new Error("Cannot repaint time axis: no range configured");if(i){t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft);var s=this.props,o=this.getOption("showMinorLabels"),r=this.getOption("showMajorLabels"),a=this.dom.measureCharMinor,h=this.dom.measureCharMajor;a&&(s.minorCharHeight=a.clientHeight,s.minorCharWidth=a.clientWidth),h&&(s.majorCharHeight=h.clientHeight,s.majorCharWidth=h.clientWidth);var d=i.parentNode?i.parentNode.offsetHeight:0;switch(d!=s.parentHeight&&(s.parentHeight=d,t+=1),this.getOption("orientation")){case"bottom":s.minorLabelHeight=o?s.minorCharHeight:0,s.majorLabelHeight=r?s.majorCharHeight:0,s.minorLabelTop=0,s.majorLabelTop=s.minorLabelTop+s.minorLabelHeight,s.minorLineTop=-this.top,s.minorLineHeight=Math.max(this.top+s.majorLabelHeight,0),s.minorLineWidth=1,s.majorLineTop=-this.top,s.majorLineHeight=Math.max(this.top+s.minorLabelHeight+s.majorLabelHeight,0),s.majorLineWidth=1,s.lineTop=0;break;case"top":s.minorLabelHeight=o?s.minorCharHeight:0,s.majorLabelHeight=r?s.majorCharHeight:0,s.majorLabelTop=0,s.minorLabelTop=s.majorLabelTop+s.majorLabelHeight,s.minorLineTop=s.minorLabelTop,s.minorLineHeight=Math.max(d-s.majorLabelHeight-this.top),s.minorLineWidth=1,s.majorLineTop=0,s.majorLineHeight=Math.max(d-this.top),s.majorLineWidth=1,s.lineTop=s.majorLabelHeight+s.minorLabelHeight;break;default:throw new Error('Unkown orientation "'+this.getOption("orientation")+'"')}var l=s.minorLabelHeight+s.majorLabelHeight;t+=e(this,"width",i.offsetWidth),t+=e(this,"height",l),this._updateConversion();var p=k.convert(n.start,"Number"),u=k.convert(n.end,"Number"),c=this.toTime(5*(s.minorCharWidth||10)).valueOf()-this.toTime(0).valueOf();this.step=new TimeStep(new Date(p),new Date(u),c),t+=e(s.range,"start",p),t+=e(s.range,"end",u),t+=e(s.range,"minimumStep",c.valueOf())}return t>0},c.prototype._updateConversion=function(){var t=this.range;if(!t)throw new Error("No range configured");this.conversion=t.conversion?t.conversion(this.width):h.conversion(t.start,t.end,this.width)},f.prototype=new l,f.prototype.setOptions=l.prototype.setOptions,f.prototype.getContainer=function(){return this.frame},f.prototype.repaint=function(){var t=this.frame,e=this.parent,i=e.parent.getContainer();if(!e)throw new Error("Cannot repaint bar: no parent attached");if(!i)throw new Error("Cannot repaint bar: parent has no container element");if(!this.getOption("showCurrentTime"))return t&&(i.removeChild(t),delete this.frame),void 0;t||(t=document.createElement("div"),t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",i.appendChild(t),this.frame=t),e.conversion||e._updateConversion();var n=new Date,s=e.toScreen(n);t.style.left=s+"px",t.title="Current time: "+n,void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer);var o=this,r=1/e.conversion.factor/2;return 30>r&&(r=30),this.currentTimeTimer=setTimeout(function(){o.repaint()},r),!1},m.prototype=new l,m.prototype.setOptions=l.prototype.setOptions,m.prototype.getContainer=function(){return this.frame},m.prototype.repaint=function(){var t=this.frame,e=this.parent,i=e.parent.getContainer();if(!e)throw new Error("Cannot repaint bar: no parent attached");if(!i)throw new Error("Cannot repaint bar: parent has no container element");if(!this.getOption("showCustomTime"))return t&&(i.removeChild(t),delete this.frame),void 0;if(!t){t=document.createElement("div"),t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",i.appendChild(t);var n=document.createElement("div");n.style.position="relative",n.style.top="0px",n.style.left="-10px",n.style.height="100%",n.style.width="20px",t.appendChild(n),this.frame=t,this.subscribe(this,"movetime")}e.conversion||e._updateConversion();var s=e.toScreen(this.customTime);return t.style.left=s+"px",t.title="Time: "+this.customTime,!1},m.prototype._setCustomTime=function(t){this.customTime=new Date(t.valueOf()),this.repaint()},m.prototype._getCustomTime=function(){return new Date(this.customTime.valueOf())},m.prototype.subscribe=function(t,e){var i=this,n={component:t,event:e,callback:function(t){i._onMouseDown(t,n)},params:{}};t.on("mousedown",n.callback),i.listeners.push(n)},m.prototype.on=function(t,e){var i=this.frame;if(!i)throw new Error("Cannot add event listener: no parent attached");A.addListener(this,t,e),k.addEventListener(i,t,e)},m.prototype._onMouseDown=function(t,e){t=t||window.event;var i=e.params,n=t.which?1==t.which:1==t.button;if(n){i.mouseX=k.getPageX(t),i.moved=!1,i.customTime=this.customTime;var s=this;i.onMouseMove||(i.onMouseMove=function(t){s._onMouseMove(t,e)},k.addEventListener(document,"mousemove",i.onMouseMove)),i.onMouseUp||(i.onMouseUp=function(t){s._onMouseUp(t,e)},k.addEventListener(document,"mouseup",i.onMouseUp)),k.stopPropagation(t),k.preventDefault(t)}},m.prototype._onMouseMove=function(t,e){t=t||window.event;var i=e.params,n=this.parent,s=k.getPageX(t);void 0===i.mouseX&&(i.mouseX=s);var o=s-i.mouseX;Math.abs(o)>=1&&(i.moved=!0);var r=n.toScreen(i.customTime),a=r+o,h=n.toTime(a);this._setCustomTime(h),A.trigger(this,"timechange",{customTime:this.customTime}),k.preventDefault(t)},m.prototype._onMouseUp=function(t,e){t=t||window.event;var i=e.params;i.onMouseMove&&(k.removeEventListener(document,"mousemove",i.onMouseMove),i.onMouseMove=null),i.onMouseUp&&(k.removeEventListener(document,"mouseup",i.onMouseUp),i.onMouseUp=null),i.moved&&A.trigger(this,"timechanged",{customTime:this.customTime})},g.prototype=new p,g.types={box:y,range:_,rangeoverflow:b,point:w},g.prototype.setOptions=l.prototype.setOptions,g.prototype.setRange=function(t){if(!(t instanceof h||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},g.prototype.repaint=function(){var t=0,e=k.updateProperty,i=k.option.asSize,n=this.options,s=this.getOption("orientation"),o=this.defaultOptions,r=this.frame;if(!r){r=document.createElement("div"),r.className="itemset";var a=n.className;a&&k.addClassName(r,k.option.asString(a));var h=document.createElement("div");h.className="background",r.appendChild(h),this.dom.background=h;var d=document.createElement("div");d.className="foreground",r.appendChild(d),this.dom.foreground=d;var l=document.createElement("div");l.className="itemset-axis",this.dom.axis=l,this.frame=r,t+=1}if(!this.parent)throw new Error("Cannot repaint itemset: no parent attached");var p=this.parent.getContainer();if(!p)throw new Error("Cannot repaint itemset: parent has no container element");
-r.parentNode||(p.appendChild(r),t+=1),this.dom.axis.parentNode||(p.appendChild(this.dom.axis),t+=1),t+=e(r.style,"left",i(n.left,"0px")),t+=e(r.style,"top",i(n.top,"0px")),t+=e(r.style,"width",i(n.width,"100%")),t+=e(r.style,"height",i(n.height,this.height+"px")),t+=e(this.dom.axis.style,"left",i(n.left,"0px")),t+=e(this.dom.axis.style,"width",i(n.width,"100%")),t+="bottom"==s?e(this.dom.axis.style,"top",this.height+this.top+"px"):e(this.dom.axis.style,"top",this.top+"px"),this._updateConversion();var u=this,c=this.queue,f=this.itemsData,m=this.items,v={};return Object.keys(c).forEach(function(e){var i=c[e],s=m[e];switch(i){case"add":case"update":var r=f&&f.get(e,v);if(r){var a=r.type||r.start&&r.end&&"range"||n.type||"box",h=g.types[a];if(s&&(h&&s instanceof h?(s.data=r,t++):(t+=s.hide(),s=null)),!s){if(!h)throw new TypeError('Unknown item type "'+a+'"');s=new h(u,r,n,o),t++}s.repaint(),m[e]=s}delete c[e];break;case"remove":s&&(t+=s.hide()),delete m[e],delete c[e];break;default:console.log('Error: unknown action "'+i+'"')}}),k.forEach(this.items,function(e){e.visible?(t+=e.show(),e.reposition()):t+=e.hide()}),t>0},g.prototype.getForeground=function(){return this.dom.foreground},g.prototype.getBackground=function(){return this.dom.background},g.prototype.getAxis=function(){return this.dom.axis},g.prototype.reflow=function(){var t=0,e=this.options,i=e.margin&&e.margin.axis||this.defaultOptions.margin.axis,n=e.margin&&e.margin.item||this.defaultOptions.margin.item,s=k.updateProperty,o=k.option.asNumber,r=k.option.asSize,a=this.frame;if(a){this._updateConversion(),k.forEach(this.items,function(e){t+=e.reflow()}),this.stack.update();var h,d=o(e.maxHeight),l=null!=r(e.height);if(l)h=a.offsetHeight;else{var p=this.stack.ordered;if(p.length){var u=p[0].top,c=p[0].top+p[0].height;k.forEach(p,function(t){u=Math.min(u,t.top),c=Math.max(c,t.top+t.height)}),h=c-u+i+n}else h=i+n}null!=d&&(h=Math.min(h,d)),t+=s(this,"height",h),t+=s(this,"top",a.offsetTop),t+=s(this,"left",a.offsetLeft),t+=s(this,"width",a.offsetWidth)}else t+=1;return t>0},g.prototype.hide=function(){var t=!1;return this.frame&&this.frame.parentNode&&(this.frame.parentNode.removeChild(this.frame),t=!0),this.dom.axis&&this.dom.axis.parentNode&&(this.dom.axis.parentNode.removeChild(this.dom.axis),t=!0),t},g.prototype.setItems=function(t){var e,i=this,n=this.itemsData;if(t){if(!(t instanceof o||t instanceof r))throw new TypeError("Data must be an instance of DataSet");this.itemsData=t}else this.itemsData=null;if(n&&(k.forEach(this.listeners,function(t,e){n.unsubscribe(e,t)}),e=n.getIds(),this._onRemove(e)),this.itemsData){var s=this.id;k.forEach(this.listeners,function(t,e){i.itemsData.subscribe(e,t,s)}),e=this.itemsData.getIds(),this._onAdd(e)}},g.prototype.getItems=function(){return this.itemsData},g.prototype._onUpdate=function(t){this._toQueue("update",t)},g.prototype._onAdd=function(t){this._toQueue("add",t)},g.prototype._onRemove=function(t){this._toQueue("remove",t)},g.prototype._toQueue=function(t,e){var i=this.queue;e.forEach(function(e){i[e]=t}),this.controller&&this.requestRepaint()},g.prototype._updateConversion=function(){var t=this.range;if(!t)throw new Error("No range configured");this.conversion=t.conversion?t.conversion(this.width):h.conversion(t.start,t.end,this.width)},g.prototype.toTime=function(t){var e=this.conversion;return new Date(t/e.factor+e.offset)},g.prototype.toScreen=function(t){var e=this.conversion;return(t.valueOf()-e.offset)*e.factor},v.prototype.select=function(){this.selected=!0},v.prototype.unselect=function(){this.selected=!1},v.prototype.show=function(){return!1},v.prototype.hide=function(){return!1},v.prototype.repaint=function(){return!1},v.prototype.reflow=function(){return!1},v.prototype.getWidth=function(){return this.width},y.prototype=new v(null,null),y.prototype.select=function(){this.selected=!0},y.prototype.unselect=function(){this.selected=!1},y.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw new Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw new Error("Cannot repaint time axis: parent has no foreground container element");var n=this.parent.getBackground();if(!n)throw new Error("Cannot repaint time axis: parent has no background container element");var s=this.parent.getAxis();if(!n)throw new Error("Cannot repaint time axis: parent has no axis container element");if(e.box.parentNode||(i.appendChild(e.box),t=!0),e.line.parentNode||(n.appendChild(e.line),t=!0),e.dot.parentNode||(s.appendChild(e.dot),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw new Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var o=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");this.className!=o&&(this.className=o,e.box.className="item box"+o,e.line.className="item line"+o,e.dot.className="item dot"+o,t=!0)}return t},y.prototype.show=function(){return this.dom&&this.dom.box.parentNode?!1:this.repaint()},y.prototype.hide=function(){var t=!1,e=this.dom;return e&&(e.box.parentNode&&(e.box.parentNode.removeChild(e.box),t=!0),e.line.parentNode&&e.line.parentNode.removeChild(e.line),e.dot.parentNode&&e.dot.parentNode.removeChild(e.dot)),t},y.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l,p,u=0;if(void 0==this.data.start)throw new Error('Property "start" missing in item '+this.data.id);if(l=this.data,p=this.parent&&this.parent.range,l&&p){var c=p.end-p.start;this.visible=l.start>p.start-c&&l.start0},y.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.box=document.createElement("DIV"),t.content=document.createElement("DIV"),t.content.className="content",t.box.appendChild(t.content),t.line=document.createElement("DIV"),t.line.className="line",t.dot=document.createElement("DIV"),t.dot.className="dot")},y.prototype.reposition=function(){var t=this.dom,e=this.props,i=this.options.orientation||this.defaultOptions.orientation;if(t){var n=t.box,s=t.line,o=t.dot;n.style.left=this.left+"px",n.style.top=this.top+"px",s.style.left=e.line.left+"px","top"==i?(s.style.top="0px",s.style.height=this.top+"px"):(s.style.top=this.top+this.height+"px",s.style.height=Math.max(this.parent.height-this.top-this.height+this.props.dot.height/2,0)+"px"),o.style.left=e.dot.left+"px",o.style.top=e.dot.top+"px"}},w.prototype=new v(null,null),w.prototype.select=function(){this.selected=!0},w.prototype.unselect=function(){this.selected=!1},w.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw new Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw new Error("Cannot repaint time axis: parent has no foreground container element");if(e.point.parentNode||(i.appendChild(e.point),i.appendChild(e.point),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw new Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");this.className!=n&&(this.className=n,e.point.className="item point"+n,t=!0)}return t},w.prototype.show=function(){return this.dom&&this.dom.point.parentNode?!1:this.repaint()},w.prototype.hide=function(){var t=!1,e=this.dom;return e&&e.point.parentNode&&(e.point.parentNode.removeChild(e.point),t=!0),t},w.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l=0;if(void 0==this.data.start)throw new Error('Property "start" missing in item '+this.data.id);if(h=this.data,d=this.parent&&this.parent.range,h&&d){var p=d.end-d.start;this.visible=h.start>d.start-p&&h.start0},w.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.point=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.point.appendChild(t.content),t.dot=document.createElement("div"),t.dot.className="dot",t.point.appendChild(t.dot))},w.prototype.reposition=function(){var t=this.dom,e=this.props;t&&(t.point.style.top=this.top+"px",t.point.style.left=this.left+"px",t.content.style.marginLeft=e.content.marginLeft+"px",t.dot.style.top=e.dot.top+"px")},_.prototype=new v(null,null),_.prototype.select=function(){this.selected=!0},_.prototype.unselect=function(){this.selected=!1},_.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw new Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw new Error("Cannot repaint time axis: parent has no foreground container element");if(e.box.parentNode||(i.appendChild(e.box),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw new Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=this.data.className?" "+this.data.className:"";this.className!=n&&(this.className=n,e.box.className="item range"+n,t=!0)}return t},_.prototype.show=function(){return this.dom&&this.dom.box.parentNode?!1:this.repaint()},_.prototype.hide=function(){var t=!1,e=this.dom;return e&&e.box.parentNode&&(e.box.parentNode.removeChild(e.box),t=!0),t},_.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l,p,u,c,f,m,g=0;if(void 0==this.data.start)throw new Error('Property "start" missing in item '+this.data.id);if(void 0==this.data.end)throw new Error('Property "end" missing in item '+this.data.id);return h=this.data,d=this.parent&&this.parent.range,this.visible=h&&d?h.startd.start:!1,this.visible&&(t=this.dom,t?(e=this.props,i=this.options,o=this.parent,r=o.toScreen(this.data.start),a=o.toScreen(this.data.end),l=k.updateProperty,p=t.box,u=o.width,f=i.orientation||this.defaultOptions.orientation,n=i.margin&&i.margin.axis||this.defaultOptions.margin.axis,s=i.padding||this.defaultOptions.padding,g+=l(e.content,"width",t.content.offsetWidth),g+=l(this,"height",p.offsetHeight),-u>r&&(r=-u),a>2*u&&(a=2*u),c=0>r?Math.min(-r,a-r-e.content.width-2*s):0,g+=l(e.content,"left",c),"top"==f?(m=n,g+=l(this,"top",m)):(m=o.height-this.height-n,g+=l(this,"top",m)),g+=l(this,"left",r),g+=l(this,"width",Math.max(a-r,1))):g+=1),g>0},_.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content))},_.prototype.reposition=function(){var t=this.dom,e=this.props;t&&(t.box.style.top=this.top+"px",t.box.style.left=this.left+"px",t.box.style.width=this.width+"px",t.content.style.left=e.content.left+"px")},b.prototype=new _(null,null),b.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw new Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw new Error("Cannot repaint time axis: parent has no foreground container element");if(e.box.parentNode||(i.appendChild(e.box),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw new Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=this.data.className?" "+this.data.className:"";this.className!=n&&(this.className=n,e.box.className="item rangeoverflow"+n,t=!0)}return t},b.prototype.getWidth=function(){return void 0!==this.props.content&&this.width0},T.prototype=new p,T.prototype.setOptions=l.prototype.setOptions,T.prototype.setRange=function(){},T.prototype.setItems=function(t){this.itemsData=t;for(var e in this.groups)if(this.groups.hasOwnProperty(e)){var i=this.groups[e];i.setItems(t)}},T.prototype.getItems=function(){return this.itemsData},T.prototype.setRange=function(t){this.range=t},T.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(k.forEach(this.listeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this._onRemove(e)),t?t instanceof o?this.groupsData=t:(this.groupsData=new o({convert:{start:"Date",end:"Date"}}),this.groupsData.add(t)):this.groupsData=null,this.groupsData){var n=this.id;k.forEach(this.listeners,function(t,e){i.groupsData.subscribe(e,t,n)}),e=this.groupsData.getIds(),this._onAdd(e)}},T.prototype.getGroups=function(){return this.groupsData},T.prototype.repaint=function(){var t,e,i,n,s=0,o=k.updateProperty,r=k.option.asSize,a=k.option.asElement,h=this.options,d=this.dom.frame,l=this.dom.labels;if(!this.parent)throw new Error("Cannot repaint groupset: no parent attached");var p=this.parent.getContainer();if(!p)throw new Error("Cannot repaint groupset: parent has no container element");if(!d){d=document.createElement("div"),d.className="groupset",this.dom.frame=d;var u=h.className;u&&k.addClassName(d,k.option.asString(u)),s+=1}d.parentNode||(p.appendChild(d),s+=1);var c=a(h.labelContainer);if(!c)throw new Error('Cannot repaint groupset: option "labelContainer" not defined');l||(l=document.createElement("div"),l.className="labels",this.dom.labels=l),l.parentNode&&l.parentNode==c||(l.parentNode&&l.parentNode.removeChild(l.parentNode),c.appendChild(l)),s+=o(d.style,"height",r(h.height,this.height+"px")),s+=o(d.style,"top",r(h.top,"0px")),s+=o(d.style,"left",r(h.left,"0px")),s+=o(d.style,"width",r(h.width,"100%")),s+=o(l.style,"top",r(h.top,"0px"));var f=this,m=this.queue,g=this.groups,v=this.groupsData,y=Object.keys(m);if(y.length){y.forEach(function(t){var e=m[t],i=g[t];switch(e){case"add":case"update":if(!i){var n=Object.create(f.options);i=new E(f,t,n),i.setItems(f.itemsData),g[t]=i,f.controller.add(i)}i.data=v.get(t),delete m[t];break;case"remove":i&&(i.setItems(),delete g[t],f.controller.remove(i)),delete m[t];break;default:console.log('Error: unknown action "'+e+'"')}});var w=this.groupsData.getIds({order:this.options.groupsOrder});for(t=0;t0},T.prototype._createLabel=function(t){var e=this.groups[t],i=document.createElement("div");i.className="label";var n=document.createElement("div");n.className="inner",i.appendChild(n);var s=e.data&&e.data.content;s instanceof Element?n.appendChild(s):void 0!=s&&(n.innerHTML=s);var o=e.data&&e.data.className;return o&&k.addClassName(i,o),e.label=i,i},T.prototype.getContainer=function(){return this.dom.frame},T.prototype.getLabelsWidth=function(){return this.props.labels.width},T.prototype.reflow=function(){var t,e,i=0,n=this.options,s=k.updateProperty,o=k.option.asNumber,r=k.option.asSize,a=this.dom.frame;if(a){var h,d=o(n.maxHeight),l=null!=r(n.height);if(l)h=a.offsetHeight;else{h=0;for(t in this.groups)this.groups.hasOwnProperty(t)&&(e=this.groups[t],h+=e.height)}null!=d&&(h=Math.min(h,d)),i+=s(this,"height",h),i+=s(this,"top",a.offsetTop),i+=s(this,"left",a.offsetLeft),i+=s(this,"width",a.offsetWidth)}var p=0;for(t in this.groups)if(this.groups.hasOwnProperty(t)){e=this.groups[t];var u=e.props&&e.props.label&&e.props.label.width||0;p=Math.max(p,u)}return i+=s(this.props.labels,"width",p),i>0},T.prototype.hide=function(){return this.dom.frame&&this.dom.frame.parentNode?(this.dom.frame.parentNode.removeChild(this.dom.frame),!0):!1},T.prototype.show=function(){return this.dom.frame&&this.dom.frame.parentNode?!1:this.repaint()},T.prototype._onUpdate=function(t){this._toQueue(t,"update")},T.prototype._onAdd=function(t){this._toQueue(t,"add")},T.prototype._onRemove=function(t){this._toQueue(t,"remove")},T.prototype._toQueue=function(t,e){var i=this.queue;t.forEach(function(t){i[t]=e}),this.controller&&this.requestRepaint()},x.prototype.setOptions=function(t){k.extend(this.options,t),this.range.setRange(),this.controller.reflow(),this.controller.repaint()},x.prototype.setCustomTime=function(t){this.customtime._setCustomTime(t)},x.prototype.getCustomTime=function(){return new Date(this.customtime.customTime.valueOf())},x.prototype.setItems=function(t){var e,i=null==this.itemsData;if(t?t instanceof o&&(e=t):e=null,t instanceof o||(e=new o({convert:{start:"Date",end:"Date"}}),e.add(t)),this.itemsData=e,this.content.setItems(e),i&&(void 0==this.options.start||void 0==this.options.end)){var n=this.getItemRange(),s=n.min,r=n.max;if(null!=s&&null!=r){var a=r.valueOf()-s.valueOf();0>=a&&(a=864e5),s=new Date(s.valueOf()-.05*a),r=new Date(r.valueOf()+.05*a)}void 0!=this.options.start&&(s=k.convert(this.options.start,"Date")),void 0!=this.options.end&&(r=k.convert(this.options.end,"Date")),(null!=s||null!=r)&&this.range.setRange(s,r)}},x.prototype.setGroups=function(t){var e=this;this.groupsData=t;var i=this.groupsData?T:g;if(!(this.content instanceof i)){this.content&&(this.content.hide(),this.content.setItems&&this.content.setItems(),this.content.setGroups&&this.content.setGroups(),this.controller.remove(this.content));var n=Object.create(this.options);k.extend(n,{top:function(){return"top"==e.options.orientation?e.timeaxis.height:e.itemPanel.height-e.timeaxis.height-e.content.height},left:null,width:"100%",height:function(){return e.options.height?e.itemPanel.height-e.timeaxis.height:null},maxHeight:function(){if(e.options.maxHeight){if(!k.isNumber(e.options.maxHeight))throw new TypeError("Number expected for property maxHeight");return e.options.maxHeight-e.timeaxis.height}return null},labelContainer:function(){return e.labelPanel.getContainer()}}),this.content=new i(this.itemPanel,[this.timeaxis],n),this.content.setRange&&this.content.setRange(this.range),this.content.setItems&&this.content.setItems(this.itemsData),this.content.setGroups&&this.content.setGroups(this.groupsData),this.controller.add(this.content)}},x.prototype.getItemRange=function(){var t=this.itemsData,e=null,i=null;if(t){var n=t.min("start");e=n?n.start.valueOf():null;var s=t.max("start");s&&(i=s.start.valueOf());var o=t.max("end");o&&(i=null==i?o.end.valueOf():Math.max(i,o.end.valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},function(t){function e(t){return M=t,u()}function i(){D=0,C=M.charAt(0)}function n(){D++,C=M.charAt(D)}function s(){return M.charAt(D+1)}function o(t){return L.test(t)}function r(t,e){if(t||(t={}),e)for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function a(t,e,i){for(var n=e.split("."),s=t;n.length;){var o=n.shift();n.length?(s[o]||(s[o]={}),s=s[o]):s[o]=i}}function h(t,e){for(var i,n,s=null,o=[t],a=t;a.parent;)o.push(a.parent),a=a.parent;if(a.nodes)for(i=0,n=a.nodes.length;n>i;i++)if(e.id===a.nodes[i].id){s=a.nodes[i];break}for(s||(s={id:e.id},t.node&&(s.attr=r(s.attr,t.node))),i=o.length-1;i>=0;i--){var h=o[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(s)&&h.nodes.push(s)}e.attr&&(s.attr=r(s.attr,e.attr))}function d(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=r({},t.edge);e.attr=r(i,e.attr)}}function l(t,e,i,n,s){var o={from:e,to:i,type:n};return t.edge&&(o.attr=r({},t.edge)),o.attr=r(o.attr||{},s),o}function p(){for(N=x.NULL,O="";" "==C||" "==C||"\n"==C||"\r"==C;)n();do{var t=!1;if("#"==C){for(var e=D-1;" "==M.charAt(e)||" "==M.charAt(e);)e--;if("\n"==M.charAt(e)||""==M.charAt(e)){for(;""!=C&&"\n"!=C;)n();t=!0}}if("/"==C&&"/"==s()){for(;""!=C&&"\n"!=C;)n();t=!0}if("/"==C&&"*"==s()){for(;""!=C;){if("*"==C&&"/"==s()){n(),n();break}n()}t=!0}for(;" "==C||" "==C||"\n"==C||"\r"==C;)n()}while(t);if(""==C)return N=x.DELIMITER,void 0;var i=C+s();if(S[i])return N=x.DELIMITER,O=i,n(),n(),void 0;if(S[C])return N=x.DELIMITER,O=C,n(),void 0;if(o(C)||"-"==C){for(O+=C,n();o(C);)O+=C,n();return"false"==O?O=!1:"true"==O?O=!0:isNaN(Number(O))||(O=Number(O)),N=x.IDENTIFIER,void 0}if('"'==C){for(n();""!=C&&('"'!=C||'"'==C&&'"'==s());)O+=C,'"'==C&&n(),n();if('"'!=C)throw _('End of string " expected');return n(),N=x.IDENTIFIER,void 0}for(N=x.UNKNOWN;""!=C;)O+=C,n();throw new SyntaxError('Syntax error in part "'+b(O,30)+'"')}function u(){var t={};if(i(),p(),"strict"==O&&(t.strict=!0,p()),("graph"==O||"digraph"==O)&&(t.type=O,p()),N==x.IDENTIFIER&&(t.id=O,p()),"{"!=O)throw _("Angle bracket { expected");if(p(),c(t),"}"!=O)throw _("Angle bracket } expected");if(p(),""!==O)throw _("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function c(t){for(;""!==O&&"}"!=O;)f(t),";"==O&&p()}function f(t){var e=m(t);if(e)return y(t,e),void 0;var i=g(t);if(!i){if(N!=x.IDENTIFIER)throw _("Identifier expected");var n=O;if(p(),"="==O){if(p(),N!=x.IDENTIFIER)throw _("Identifier expected");t[n]=O,p()}else v(t,n)}}function m(t){var e=null;if("subgraph"==O&&(e={},e.type="subgraph",p(),N==x.IDENTIFIER&&(e.id=O,p())),"{"==O){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,c(e),"}"!=O)throw _("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function g(t){return"node"==O?(p(),t.node=w(),"node"):"edge"==O?(p(),t.edge=w(),"edge"):"graph"==O?(p(),t.graph=w(),"graph"):null}function v(t,e){var i={id:e},n=w();n&&(i.attr=n),h(t,i),y(t,e)}function y(t,e){for(;"->"==O||"--"==O;){var i,n=O;p();var s=m(t);if(s)i=s;else{if(N!=x.IDENTIFIER)throw _("Identifier or subgraph expected");i=O,h(t,{id:i}),p()}var o=w(),r=l(t,e,i,n,o);d(t,r),e=i}}function w(){for(var t=null;"["==O;){for(p(),t={};""!==O&&"]"!=O;){if(N!=x.IDENTIFIER)throw _("Attribute name expected");var e=O;if(p(),"="!=O)throw _("Equal sign = expected");if(p(),N!=x.IDENTIFIER)throw _("Attribute value expected");var i=O;a(t,e,i),p(),","==O&&p()}if("]"!=O)throw _("Bracket ] expected");p()}return t}function _(t){return new SyntaxError(t+', got "'+b(O,30)+'" (char '+D+")")}function b(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function E(t,e,i){t instanceof Array?t.forEach(function(t){e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}):e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}function T(t){function i(t){var e={from:t.from,to:t.to};return r(e,t.attr),e.style="->"==t.type?"arrow":"line",e}var n=e(t),s={nodes:[],edges:[],options:{}};return n.nodes&&n.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};r(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),n.edges&&n.edges.forEach(function(t){var e,n;e=t.from instanceof Object?t.from.nodes:{id:t.from},n=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=i(t);s.edges.push(e)}),E(e,n,function(e,n){var o=l(s,e.id,n.id,t.type,t.attr),r=i(o);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=i(t);s.edges.push(e)})}),n.attr&&(s.options=n.attr),s}var x={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},S={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},M="",D=0,C="",O="",N=x.NULL,L=/[a-zA-Z_0-9.:#]/;t.parseDOT=e,t.DOTToGraph=T}("undefined"!=typeof k?k:n),"undefined"!=typeof CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.circle=function(t,e,i){this.beginPath(),this.arc(t,e,i,0,2*Math.PI,!1)},CanvasRenderingContext2D.prototype.square=function(t,e,i){this.beginPath(),this.rect(t-i,e-i,2*i,2*i)},CanvasRenderingContext2D.prototype.triangle=function(t,e,i){this.beginPath();var n=2*i,s=n/2,o=Math.sqrt(3)/6*n,r=Math.sqrt(n*n-s*s);this.moveTo(t,e-(r-o)),this.lineTo(t+s,e+o),this.lineTo(t-s,e+o),this.lineTo(t,e-(r-o)),this.closePath()},CanvasRenderingContext2D.prototype.triangleDown=function(t,e,i){this.beginPath();var n=2*i,s=n/2,o=Math.sqrt(3)/6*n,r=Math.sqrt(n*n-s*s);this.moveTo(t,e+(r-o)),this.lineTo(t+s,e-o),this.lineTo(t-s,e-o),this.lineTo(t,e+(r-o)),this.closePath()},CanvasRenderingContext2D.prototype.star=function(t,e,i){this.beginPath();for(var n=0;10>n;n++){var s=0===n%2?1.3*i:.5*i;this.lineTo(t+s*Math.sin(2*n*Math.PI/10),e-s*Math.cos(2*n*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,n,s){var o=Math.PI/180;0>i-2*s&&(s=i/2),0>n-2*s&&(s=n/2),this.beginPath(),this.moveTo(t+s,e),this.lineTo(t+i-s,e),this.arc(t+i-s,e+s,s,270*o,360*o,!1),this.lineTo(t+i,e+n-s),this.arc(t+i-s,e+n-s,s,0,90*o,!1),this.lineTo(t+s,e+n),this.arc(t+s,e+n-s,s,90*o,180*o,!1),this.lineTo(t,e+s),this.arc(t+s,e+s,s,180*o,270*o,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,n){var s=.5522848,o=i/2*s,r=n/2*s,a=t+i,h=e+n,d=t+i/2,l=e+n/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-o,e,d,e),this.bezierCurveTo(d+o,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+o,h,d,h),this.bezierCurveTo(d-o,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,n){var s=1/3,o=i,r=n*s,a=.5522848,h=o/2*a,d=r/2*a,l=t+o,p=e+r,u=t+o/2,c=e+r/2,f=e+(n-r/2),m=e+n;this.beginPath(),this.moveTo(l,c),this.bezierCurveTo(l,c+d,u+h,p,u,p),this.bezierCurveTo(u-h,p,t,c+d,t,c),this.bezierCurveTo(t,c-d,u-h,e,u,e),this.bezierCurveTo(u+h,e,l,c-d,l,c),this.lineTo(l,f),this.bezierCurveTo(l,f+d,u+h,m,u,m),this.bezierCurveTo(u-h,m,t,f+d,t,f),this.lineTo(t,c)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,n){var s=t-n*Math.cos(i),o=e-n*Math.sin(i),r=t-.9*n*Math.cos(i),a=e-.9*n*Math.sin(i),h=s+n/3*Math.cos(i+.5*Math.PI),d=o+n/3*Math.sin(i+.5*Math.PI),l=s+n/3*Math.cos(i-.5*Math.PI),p=o+n/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,p),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,n,s){s||(s=[10,5]),0==u&&(u=.001);var o=s.length;this.moveTo(t,e);for(var r=i-t,a=n-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,p=!0;d>=.1;){var u=s[l++%o];u>d&&(u=d);var c=Math.sqrt(u*u/(1+h*h));0>r&&(c=-c),t+=c,e+=h*c,this[p?"lineTo":"moveTo"](t,e),d-=u,p=!p}}),S.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),this._updateMass()},S.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),this._updateMass()},S.prototype._updateMass=function(){this.mass=50+20*this.edges.length},S.prototype.setProperties=function(t,e){if(t){if(void 0!=t.id&&(this.id=t.id),void 0!=t.label&&(this.label=t.label),void 0!=t.title&&(this.title=t.title),void 0!=t.group&&(this.group=t.group),void 0!=t.x&&(this.x=t.x),void 0!=t.y&&(this.y=t.y),void 0!=t.value&&(this.value=t.value),void 0===this.id)throw"Node must have an id";if(this.group){var i=this.grouplist.get(this.group);for(var n in i)i.hasOwnProperty(n)&&(this[n]=i[n])}if(void 0!=t.shape&&(this.shape=t.shape),void 0!=t.image&&(this.image=t.image),void 0!=t.radius&&(this.radius=t.radius),void 0!=t.color&&(this.color=S.parseColor(t.color)),void 0!=t.fontColor&&(this.fontColor=t.fontColor),void 0!=t.fontSize&&(this.fontSize=t.fontSize),void 0!=t.fontFace&&(this.fontFace=t.fontFace),void 0!=this.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.image)}switch(this.xFixed=this.xFixed||void 0!=t.x,this.yFixed=this.yFixed||void 0!=t.y,this.radiusFixed=this.radiusFixed||void 0!=t.radius,"image"==this.shape&&(this.radiusMin=e.nodes.widthMin,this.radiusMax=e.nodes.widthMax),this.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},S.parseColor=function(t){var e;return k.isString(t)?e={border:t,background:t,highlight:{border:t,background:t}}:(e={},e.background=t.background||"white",e.border=t.border||e.background,k.isString(t.highlight)?e.highlight={border:t.highlight,background:t.highlight}:(e.highlight={},e.highlight.background=t.highlight&&t.highlight.background||e.background,e.highlight.border=t.highlight&&t.highlight.border||e.border)),e},S.prototype.select=function(){this.selected=!0,this._reset()},S.prototype.unselect=function(){this.selected=!1,this._reset()},S.prototype._reset=function(){this.width=void 0,this.height=void 0},S.prototype.getTitle=function(){return this.title},S.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.shape){case"circle":case"dot":return this.radius+i;case"ellipse":var n=this.width/2,s=this.height/2,o=Math.sin(e)*n,r=Math.cos(e)*s;
-return n*s/Math.sqrt(o*o+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},S.prototype._setForce=function(t,e){this.fx=t,this.fy=e},S.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},S.prototype.discreteStep=function(t){if(!this.xFixed){var e=-this.damping*this.vx,i=(this.fx+e)/this.mass;this.vx+=i/t,this.x+=this.vx/t}if(!this.yFixed){var n=-this.damping*this.vy,s=(this.fy+n)/this.mass;this.vy+=s/t,this.y+=this.vy/t}},S.prototype.isFixed=function(){return this.xFixed&&this.yFixed},S.prototype.isMoving=function(t){return Math.abs(this.vx)>t||Math.abs(this.vy)>t||!this.xFixed&&Math.abs(this.fx)>this.minForce||!this.yFixed&&Math.abs(this.fy)>this.minForce},S.prototype.isSelected=function(){return this.selected},S.prototype.getValue=function(){return this.value},S.prototype.getDistance=function(t,e){var i=this.x-t,n=this.y-e;return Math.sqrt(i*i+n*n)},S.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.radius=(this.radiusMin+this.radiusMax)/2;else{var i=(this.radiusMax-this.radiusMin)/(e-t);this.radius=(this.value-t)*i+this.radiusMin}},S.prototype.draw=function(){throw"Draw method not initialized for node"},S.prototype.resize=function(){throw"Resize method not initialized for node"},S.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},S.prototype._resizeImage=function(){if(!this.width){var t,e;if(this.value){var i=this.imageObj.height/this.imageObj.width;t=this.radius||this.imageObj.width,e=this.radius*i||this.imageObj.height}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e}},S.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e;this.imageObj?(t.drawImage(this.imageObj,this.left,this.top,this.width,this.height),e=this.y+this.height/2):e=this.y,this._label(t,this.label,this.x,e,void 0,"top")},S.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e}},S.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.roundRect(this.left,this.top,this.width,this.height,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},S.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),n=i.width+2*e;this.width=n,this.height=n}},S.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},S.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),n=Math.max(i.width,i.height)+2*e;this.radius=n/2,this.width=n,this.height=n}},S.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.circle(this.x,this.y,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},S.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.widthl;l++)t.fillText(r[l],i,d),d+=h}},S.prototype.getTextSize=function(t){if(void 0!=this.label){t.font=(this.selected?"bold ":"")+this.fontSize+"px "+this.fontFace;for(var e=this.label.split("\n"),i=(this.fontSize+4)*e.length,n=0,s=0,o=e.length;o>s;s++)n=Math.max(n,t.measureText(e[s]).width);return{width:n,height:i}}return{width:0,height:0}},M.prototype.setProperties=function(t,e){if(t)switch(void 0!=t.from&&(this.fromId=t.from),void 0!=t.to&&(this.toId=t.to),void 0!=t.id&&(this.id=t.id),void 0!=t.style&&(this.style=t.style),void 0!=t.label&&(this.label=t.label),this.label&&(this.fontSize=e.edges.fontSize,this.fontFace=e.edges.fontFace,this.fontColor=e.edges.fontColor,void 0!=t.fontColor&&(this.fontColor=t.fontColor),void 0!=t.fontSize&&(this.fontSize=t.fontSize),void 0!=t.fontFace&&(this.fontFace=t.fontFace)),void 0!=t.title&&(this.title=t.title),void 0!=t.width&&(this.width=t.width),void 0!=t.value&&(this.value=t.value),void 0!=t.length&&(this.length=t.length),t.dash&&(void 0!=t.dash.length&&(this.dash.length=t.dash.length),void 0!=t.dash.gap&&(this.dash.gap=t.dash.gap),void 0!=t.dash.altLength&&(this.dash.altLength=t.dash.altLength)),void 0!=t.color&&(this.color=t.color),this.connect(),this.widthFixed=this.widthFixed||void 0!=t.width,this.lengthFixed=this.lengthFixed||void 0!=t.length,this.stiffness=1/this.length,this.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter;break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}},M.prototype.connect=function(){this.disconnect(),this.from=this.graph.nodes[this.fromId]||null,this.to=this.graph.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this))},M.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},M.prototype.getTitle=function(){return this.title},M.prototype.getValue=function(){return this.value},M.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.widthMax-this.widthMin)/(e-t);this.width=(this.value-t)*i+this.widthMin}},M.prototype.draw=function(){throw"Method draw not initialized in edge"},M.prototype.isOverlappingWith=function(t){var e=10,i=this.from.x,n=this.from.y,s=this.to.x,o=this.to.y,r=t.left,a=t.top,h=M._dist(i,n,s,o,r,a);return e>h},M.prototype._drawLine=function(t){t.strokeStyle=this.color,t.lineWidth=this._getLineWidth();var e;if(this.from!=this.to)this._line(t),this.label&&(e=this._pointOnLine(.5),this._label(t,this.label,e.x,e.y));else{var i,n,s=this.length/4,o=this.from;o.width||o.resize(t),o.width>o.height?(i=o.x+o.width/2,n=o.y-s):(i=o.x+s,n=o.y-o.height/2),this._circle(t,i,n,s),e=this._pointOnCircle(i,n,s,.5),this._label(t,this.label,e.x,e.y)}},M.prototype._getLineWidth=function(){return this.from.selected||this.to.selected?Math.min(2*this.width,this.widthMax):this.width},M.prototype._line=function(t){t.beginPath(),t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y),t.stroke()},M.prototype._circle=function(t,e,i,n){t.beginPath(),t.arc(e,i,n,0,2*Math.PI,!1),t.stroke()},M.prototype._label=function(t,e,i,n){if(e){t.font=(this.from.selected||this.to.selected?"bold ":"")+this.fontSize+"px "+this.fontFace,t.fillStyle="white";var s=t.measureText(e).width,o=this.fontSize,r=i-s/2,a=n-o/2;t.fillRect(r,a,s,o),t.fillStyle=this.fontColor||"black",t.textAlign="left",t.textBaseline="top",t.fillText(e,r,a)}},M.prototype._drawDashLine=function(t){if(t.strokeStyle=this.color,t.lineWidth=this._getLineWidth(),t.beginPath(),t.lineCap="round",void 0!=this.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]):void 0!=this.dash.length&&void 0!=this.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke(),this.label){var e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}},M.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},M.prototype._pointOnCircle=function(t,e,i,n){var s=2*(n-3/8)*Math.PI;return{x:t+i*Math.cos(s),y:e-i*Math.sin(s)}},M.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this.color,t.fillStyle=this.color,t.lineWidth=this._getLineWidth(),this.from!=this.to){this._line(t);var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),n=10+5*this.width;e=this._pointOnLine(.5),t.arrow(e.x,e.y,i,n),t.fill(),t.stroke(),this.label&&(e=this._pointOnLine(.5),this._label(t,this.label,e.x,e.y))}else{var s,o,r=this.length/4,a=this.from;a.width||a.resize(t),a.width>a.height?(s=a.x+a.width/2,o=a.y-r):(s=a.x+r,o=a.y-a.height/2),this._circle(t,s,o,r);var i=.2*Math.PI,n=10+5*this.width;e=this._pointOnCircle(s,o,r,.5),t.arrow(e.x,e.y,i,n),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(s,o,r,.5),this._label(t,this.label,e.x,e.y))}},M.prototype._drawArrow=function(t){t.strokeStyle=this.color,t.fillStyle=this.color,t.lineWidth=this._getLineWidth();var e,i;if(this.from!=this.to){e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var n=this.to.x-this.from.x,s=this.to.y-this.from.y,o=Math.sqrt(n*n+s*s),r=this.from.distanceToBorder(t,e+Math.PI),a=(o-r)/o,h=a*this.from.x+(1-a)*this.to.x,d=a*this.from.y+(1-a)*this.to.y,l=this.to.distanceToBorder(t,e),p=(o-l)/o,u=(1-p)*this.from.x+p*this.to.x,c=(1-p)*this.from.y+p*this.to.y;if(t.beginPath(),t.moveTo(h,d),t.lineTo(u,c),t.stroke(),i=10+5*this.width,t.arrow(u,c,e,i),t.fill(),t.stroke(),this.label){var f=this._pointOnLine(.5);this._label(t,this.label,f.x,f.y)}}else{var m,g,v,y=this.from,w=this.length/4;y.width||y.resize(t),y.width>y.height?(m=y.x+y.width/2,g=y.y-w,v={x:m,y:y.y,angle:.9*Math.PI}):(m=y.x+w,g=y.y-y.height/2,v={x:y.x,y:g,angle:.6*Math.PI}),t.beginPath(),t.arc(m,g,w,0,2*Math.PI,!1),t.stroke(),i=10+5*this.width,t.arrow(v.x,v.y,v.angle,i),t.fill(),t.stroke(),this.label&&(f=this._pointOnCircle(m,g,w,.5),this._label(t,this.label,f.x,f.y))}},M._dist=function(t,e,i,n,s,o){var r=i-t,a=n-e,h=r*r+a*a,d=((s-t)*r+(o-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,p=e+d*a,u=l-s,c=p-o;return Math.sqrt(u*u+c*c)},D.prototype.setPosition=function(t,e){this.x=parseInt(t),this.y=parseInt(e)},D.prototype.setText=function(t){this.frame.innerHTML=t},D.prototype.show=function(t){if(void 0===t&&(t=!0),t){var e=this.frame.clientHeight,i=this.frame.clientWidth,n=this.frame.parentNode.clientHeight,s=this.frame.parentNode.clientWidth,o=this.y-e;o+e+this.padding>n&&(o=n-e-this.padding),os&&(r=s-i-this.padding),r0?s[s.length-1]:null},C.prototype._getPointer=function(t){return{x:t.pageX-P.util.getAbsoluteLeft(this.frame.canvas),y:t.pageY-P.util.getAbsoluteTop(this.frame.canvas)}},C.prototype._onTouch=function(t){this.drag.pointer=this._getPointer(t.gesture.touches[0]),this.drag.pinched=!1,this.pinch.scale=this._getScale()},C.prototype._onDragStart=function(){var t=this.drag;t.selection=[],t.translation=this._getTranslation(),t.nodeId=this._getNodeAt(t.pointer);var e=this.nodes[t.nodeId];if(e){e.isSelected()||this._selectNodes([t.nodeId]);var i=this;this.selection.forEach(function(e){var n=i.nodes[e];if(n){var s={id:e,node:n,x:n.x,y:n.y,xFixed:n.xFixed,yFixed:n.yFixed};n.xFixed=!0,n.yFixed=!0,t.selection.push(s)}})}},C.prototype._onDrag=function(t){if(!this.drag.pinched){var e=this._getPointer(t.gesture.touches[0]),i=this,n=this.drag,s=n.selection;if(s&&s.length){var o=e.x-n.pointer.x,r=e.y-n.pointer.y;s.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._canvasToX(i._xToCanvas(t.x)+o)),t.yFixed||(e.y=i._canvasToY(i._yToCanvas(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else{var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw(),this.moved=!0}}},C.prototype._onDragEnd=function(){var t=this.drag.selection;t&&t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed})},C.prototype._onTap=function(t){var e=this._getPointer(t.gesture.touches[0]),i=this._getNodeAt(e),n=this.nodes[i];n?(this._selectNodes([i]),this.moving||this._redraw()):(this._unselectNodes(),this._redraw())},C.prototype._onHold=function(t){var e=this._getPointer(t.gesture.touches[0]),i=this._getNodeAt(e),n=this.nodes[i];if(n){if(n.isSelected())this._unselectNodes([i]);else{var s=!0;this._selectNodes([i],s)}this.moving||this._redraw()}},C.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},C.prototype._zoom=function(t,e){var i=this._getScale();.01>t&&(t=.01),t>10&&(t=10);var n=this._getTranslation(),s=t/i,o=(1-s)*e.x+n.x*s,r=(1-s)*e.y+n.y*s;return this._setScale(t),this._setTranslation(o,r),this._redraw(),t},C.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){"mouswheelScale"in this.pinch||(this.pinch.mouswheelScale=1);var i=this.pinch.mouswheelScale,n=e/10;0>e&&(n/=1-n),i*=1+n;var s=O.event.collectEventData(this,"scroll",t),o=this._getPointer(s.center);i=this._zoom(i,o),this.pinch.mouswheelScale=i}t.preventDefault()},C.prototype._onMouseMoveTitle=function(t){var e=O.event.collectEventData(this,"mousemove",t),i=this._getPointer(e.center);this.popupNode&&this._checkHidePopup(i);var n=this,s=function(){n._checkShowPopup(i)};this.popupTimer&&clearInterval(this.popupTimer),this.leftButtonDown||(this.popupTimer=setTimeout(s,300))},C.prototype._checkShowPopup=function(t){var e,i={left:this._canvasToX(t.x),top:this._canvasToY(t.y),right:this._canvasToX(t.x),bottom:this._canvasToY(t.y)},n=this.popupNode;if(void 0==this.popupNode){var s=this.nodes;for(e in s)if(s.hasOwnProperty(e)){var o=s[e];if(void 0!=o.getTitle()&&o.isOverlappingWith(i)){this.popupNode=o;break}}}if(void 0==this.popupNode){var r=this.edges;for(e in r)if(r.hasOwnProperty(e)){var a=r[e];if(a.connected&&void 0!=a.getTitle()&&a.isOverlappingWith(i)){this.popupNode=a;break}}}if(this.popupNode){if(this.popupNode!=n){var h=this;h.popup||(h.popup=new D(h.frame)),h.popup.setPosition(t.x-3,t.y-3),h.popup.setText(h.popupNode.getTitle()),h.popup.show()}}else this.popup&&this.popup.hide()},C.prototype._checkHidePopup=function(t){this.popupNode&&this._getNodeAt(t)||(this.popupNode=void 0,this.popup&&this.popup.hide())},C.prototype._unselectNodes=function(t,e){var i,n,s,o=!1;if(t)for(i=0,n=t.length;n>i;i++){s=t[i],this.nodes[s].unselect();for(var r=0;ri;i++)s=this.selection[i],this.nodes[s].unselect(),o=!0;this.selection=[]}return!o||1!=e&&void 0!=e||this._trigger("select"),o},C.prototype._selectNodes=function(t,e){var i,n,s=!1,o=!0;if(t.length!=this.selection.length)o=!1;else for(i=0,n=Math.min(t.length,this.selection.length);n>i;i++)if(t[i]!=this.selection[i]){o=!1;break}if(o)return s;if(void 0==e||0==e){var r=!1;s=this._unselectNodes(void 0,r)}for(i=0,n=t.length;n>i;i++){var a=t[i],h=-1!=this.selection.indexOf(a);h||(this.nodes[a].select(),this.selection.push(a),s=!0)}return s&&this._trigger("select"),s},C.prototype._getNodesOverlappingWith=function(t){var e=this.nodes,i=[];for(var n in e)e.hasOwnProperty(n)&&e[n].isOverlappingWith(t)&&i.push(n);return i},C.prototype.getSelection=function(){return this.selection.concat([])},C.prototype.setSelection=function(t){var e,i,n;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(e=0,i=this.selection.length;i>e;e++)n=this.selection[e],this.nodes[n].unselect();for(this.selection=[],e=0,i=t.length;i>e;e++){n=t[e];var s=this.nodes[n];if(!s)throw new RangeError('Node with id "'+n+'" not found');s.select(),this.selection.push(n)}this.redraw()},C.prototype._updateSelection=function(){for(var t=0;ti;i++)for(var s=t[i],o=s.edges,r=0,a=o.length;a>r;r++){var h=o[r],d=null;h.from==s?d=h.to:h.to==s&&(d=h.from);var l,p;if(d)for(l=0,p=t.length;p>l;l++)if(t[l]==d){d=null;break}if(d)for(l=0,p=e.length;p>l;l++)if(e[l]==d){d=null;break}d&&e.push(d)}return e}void 0==t&&(t=1);var i=[],n=this.nodes;for(var s in n)if(n.hasOwnProperty(s)){for(var o=[n[s]],r=0;t>r;r++)o=o.concat(e(o));i.push(o)}for(var a=[],h=0,d=i.length;d>h;h++)a.push(i[h].length);return a},C.prototype.setSize=function(t,e){this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth,this.frame.canvas.height=this.frame.canvas.clientHeight},C.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof o||t instanceof r)this.nodesData=t;else if(t instanceof Array)this.nodesData=new o,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new o}if(e&&k.forEach(this.nodesListeners,function(t,i){e.unsubscribe(i,t)}),this.nodes={},this.nodesData){var i=this;k.forEach(this.nodesListeners,function(t,e){i.nodesData.subscribe(e,t)});var n=this.nodesData.getIds();this._addNodes(n)}this._updateSelection()},C.prototype._addNodes=function(t){for(var e,i=0,n=t.length;n>i;i++){e=t[i];var s=this.nodesData.get(e),o=new S(s,this.images,this.groups,this.constants);if(this.nodes[e]=o,!o.isFixed()){var r=2*this.constants.edges.length,a=t.length,h=2*Math.PI*(i/a);o.x=r*Math.cos(h),o.y=r*Math.sin(h),this.moving=!0}}this._reconnectEdges(),this._updateValueRange(this.nodes)},C.prototype._updateNodes=function(t){for(var e=this.nodes,i=this.nodesData,n=0,s=t.length;s>n;n++){var o=t[n],r=e[o],a=i.get(o);r?r.setProperties(a,this.constants):(r=new S(properties,this.images,this.groups,this.constants),e[o]=r,r.isFixed()||(this.moving=!0))}this._reconnectEdges(),this._updateValueRange(e)},C.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,n=t.length;n>i;i++){var s=t[i];delete e[s]}this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},C.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof o||t instanceof r)this.edgesData=t;else if(t instanceof Array)this.edgesData=new o,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new o}if(e&&k.forEach(this.edgesListeners,function(t,i){e.unsubscribe(i,t)}),this.edges={},this.edgesData){var i=this;k.forEach(this.edgesListeners,function(t,e){i.edgesData.subscribe(e,t)});var n=this.edgesData.getIds();this._addEdges(n)}this._reconnectEdges()},C.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,n=0,s=t.length;s>n;n++){var o=t[n],r=e[o];r&&r.disconnect();var a=i.get(o);e[o]=new M(a,this,this.constants)}this.moving=!0,this._updateValueRange(e)},C.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,n=0,s=t.length;s>n;n++){var o=t[n],r=i.get(o),a=e[o];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new M(r,this,this.constants),this.edges[o]=a)}this.moving=!0,this._updateValueRange(e)},C.prototype._removeEdges=function(t){for(var e=this.edges,i=0,n=t.length;n>i;i++){var s=t[i],o=e[s];o&&(o.disconnect(),delete e[s])}this.moving=!0,this._updateValueRange(e)},C.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[]);for(t in i)if(i.hasOwnProperty(t)){var n=i[t];n.from=null,n.to=null,n.connect()}},C.prototype._updateValueRange=function(t){var e,i=void 0,n=void 0;for(e in t)if(t.hasOwnProperty(e)){var s=t[e].getValue();void 0!==s&&(i=void 0===i?s:Math.min(s,i),n=void 0===n?s:Math.max(s,n))}if(void 0!==i&&void 0!==n)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,n)},C.prototype.redraw=function(){this.setSize(this.width,this.height),this._redraw()},C.prototype._redraw=function(){var t=this.frame.canvas.getContext("2d"),e=this.frame.canvas.width,i=this.frame.canvas.height;t.clearRect(0,0,e,i),t.save(),t.translate(this.translation.x,this.translation.y),t.scale(this.scale,this.scale),this._drawEdges(t),this._drawNodes(t),t.restore()},C.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e)},C.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},C.prototype._setScale=function(t){this.scale=t},C.prototype._getScale=function(){return this.scale},C.prototype._canvasToX=function(t){return(t-this.translation.x)/this.scale},C.prototype._xToCanvas=function(t){return t*this.scale+this.translation.x},C.prototype._canvasToY=function(t){return(t-this.translation.y)/this.scale},C.prototype._yToCanvas=function(t){return t*this.scale+this.translation.y},C.prototype._drawNodes=function(t){var e=this.nodes,i=[];for(var n in e)e.hasOwnProperty(n)&&(e[n].isSelected()?i.push(n):e[n].draw(t));for(var s=0,o=i.length;o>s;s++)e[i[s]].draw(t)},C.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var n=e[i];n.connected&&e[i].draw(t)}},C.prototype._doStabilize=function(){new Date;for(var t=0,e=this.constants.minVelocity,i=!1;!i&&tr;r++)s(n[r]);return s}({1:[function(t,e){(function(t,i){"use strict";function n(){if(!s.READY){s.event.determineEventTypes();for(var t in s.gestures)s.gestures.hasOwnProperty(t)&&s.detection.register(s.gestures[t]);s.event.onTouch(s.DOCUMENT,s.EVENT_MOVE,s.detection.detect),s.event.onTouch(s.DOCUMENT,s.EVENT_END,s.detection.detect),s.READY=!0}}var s=function(t,e){return new s.Instance(t,e||{})};s.defaults={stop_browser_behavior:{userSelect:"none",touchAction:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},s.HAS_POINTEREVENTS=navigator.pointerEnabled||navigator.msPointerEnabled,s.HAS_TOUCHEVENTS="ontouchstart"in t,s.MOBILE_REGEX=/mobile|tablet|ip(ad|hone|od)|android/i,s.NO_MOUSEEVENTS=s.HAS_TOUCHEVENTS&&navigator.userAgent.match(s.MOBILE_REGEX),s.EVENT_TYPES={},s.DIRECTION_DOWN="down",s.DIRECTION_LEFT="left",s.DIRECTION_UP="up",s.DIRECTION_RIGHT="right",s.POINTER_MOUSE="mouse",s.POINTER_TOUCH="touch",s.POINTER_PEN="pen",s.EVENT_START="start",s.EVENT_MOVE="move",s.EVENT_END="end",s.DOCUMENT=document,s.plugins={},s.READY=!1,s.Instance=function(t,e){var i=this;return n(),this.element=t,this.enabled=!0,this.options=s.utils.extend(s.utils.extend({},s.defaults),e||{}),this.options.stop_browser_behavior&&s.utils.stopDefaultBrowserBehavior(this.element,this.options.stop_browser_behavior),s.event.onTouch(t,s.EVENT_START,function(t){i.enabled&&s.detection.startDetect(i,t)}),this},s.Instance.prototype={on:function(t,e){for(var i=t.split(" "),n=0;i.length>n;n++)this.element.addEventListener(i[n],e,!1);return this},off:function(t,e){for(var i=t.split(" "),n=0;i.length>n;n++)this.element.removeEventListener(i[n],e,!1);return this},trigger:function(t,e){var i=s.DOCUMENT.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=e;var n=this.element;return s.utils.hasParent(e.target,n)&&(n=e.target),n.dispatchEvent(i),this},enable:function(t){return this.enabled=t,this}};var o=null,r=!1,a=!1;s.event={bindDom:function(t,e,i){for(var n=e.split(" "),s=0;n.length>s;s++)t.addEventListener(n[s],i,!1)},onTouch:function(t,e,i){var n=this;this.bindDom(t,s.EVENT_TYPES[e],function(h){var d=h.type.toLowerCase();if(!d.match(/mouse/)||!a){(d.match(/touch/)||d.match(/pointerdown/)||d.match(/mouse/)&&1===h.which)&&(r=!0),d.match(/touch|pointer/)&&(a=!0);var l=0;r&&(s.HAS_POINTEREVENTS&&e!=s.EVENT_END?l=s.PointerEvent.updatePointer(e,h):d.match(/touch/)?l=h.touches.length:a||(l=d.match(/up/)?0:1),l>0&&e==s.EVENT_END?e=s.EVENT_MOVE:l||(e=s.EVENT_END),l||null===o?o=h:h=o,i.call(s.detection,n.collectEventData(t,e,h)),s.HAS_POINTEREVENTS&&e==s.EVENT_END&&(l=s.PointerEvent.updatePointer(e,h))),l||(o=null,r=!1,a=!1,s.PointerEvent.reset())}})},determineEventTypes:function(){var t;t=s.HAS_POINTEREVENTS?s.PointerEvent.getEvents():s.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],s.EVENT_TYPES[s.EVENT_START]=t[0],s.EVENT_TYPES[s.EVENT_MOVE]=t[1],s.EVENT_TYPES[s.EVENT_END]=t[2]},getTouchList:function(t){return s.HAS_POINTEREVENTS?s.PointerEvent.getTouchList():t.touches?t.touches:[{identifier:1,pageX:t.pageX,pageY:t.pageY,target:t.target}]},collectEventData:function(t,e,i){var n=this.getTouchList(i,e),o=s.POINTER_TOUCH;return(i.type.match(/mouse/)||s.PointerEvent.matchType(s.POINTER_MOUSE,i))&&(o=s.POINTER_MOUSE),{center:s.utils.getCenter(n),timeStamp:(new Date).getTime(),target:i.target,touches:n,eventType:e,pointerType:o,srcEvent:i,preventDefault:function(){this.srcEvent.preventManipulation&&this.srcEvent.preventManipulation(),this.srcEvent.preventDefault&&this.srcEvent.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return s.detection.stopDetect()}}}},s.PointerEvent={pointers:{},getTouchList:function(){var t=this,e=[];return Object.keys(t.pointers).sort().forEach(function(i){e.push(t.pointers[i])}),e},updatePointer:function(t,e){return t==s.EVENT_END?this.pointers={}:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e),Object.keys(this.pointers).length},matchType:function(t,e){if(!e.pointerType)return!1;var i={};return i[s.POINTER_MOUSE]=e.pointerType==e.MSPOINTER_TYPE_MOUSE||e.pointerType==s.POINTER_MOUSE,i[s.POINTER_TOUCH]=e.pointerType==e.MSPOINTER_TYPE_TOUCH||e.pointerType==s.POINTER_TOUCH,i[s.POINTER_PEN]=e.pointerType==e.MSPOINTER_TYPE_PEN||e.pointerType==s.POINTER_PEN,i[t]},getEvents:function(){return["pointerdown MSPointerDown","pointermove MSPointerMove","pointerup pointercancel MSPointerUp MSPointerCancel"]},reset:function(){this.pointers={}}},s.utils={extend:function(t,e,n){for(var s in e)t[s]!==i&&n||(t[s]=e[s]);return t},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){for(var e=[],i=[],n=0,s=t.length;s>n;n++)e.push(t[n].pageX),i.push(t[n].pageY);return{pageX:(Math.min.apply(Math,e)+Math.max.apply(Math,e))/2,pageY:(Math.min.apply(Math,i)+Math.max.apply(Math,i))/2}},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.pageY-t.pageY,n=e.pageX-t.pageX;return 180*Math.atan2(i,n)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.pageX-e.pageX),n=Math.abs(t.pageY-e.pageY);return i>=n?t.pageX-e.pageX>0?s.DIRECTION_LEFT:s.DIRECTION_RIGHT:t.pageY-e.pageY>0?s.DIRECTION_UP:s.DIRECTION_DOWN},getDistance:function(t,e){var i=e.pageX-t.pageX,n=e.pageY-t.pageY;return Math.sqrt(i*i+n*n)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==s.DIRECTION_UP||t==s.DIRECTION_DOWN},stopDefaultBrowserBehavior:function(t,e){var i,n=["webkit","khtml","moz","ms","o",""];if(e&&t.style){for(var s=0;n.length>s;s++)for(var o in e)e.hasOwnProperty(o)&&(i=o,n[s]&&(i=n[s]+i.substring(0,1).toUpperCase()+i.substring(1)),t.style[i]=e[o]);"none"==e.userSelect&&(t.onselectstart=function(){return!1})}}},s.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,e){this.current||(this.stopped=!1,this.current={inst:t,startEvent:s.utils.extend({},e),lastEvent:!1,name:""},this.detect(e))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);for(var e=this.current.inst.options,i=0,n=this.gestures.length;n>i;i++){var o=this.gestures[i];if(!this.stopped&&e[o.name]!==!1&&o.handler.call(o,t,this.current.inst)===!1){this.stopDetect();break}}return this.current&&(this.current.lastEvent=t),t.eventType==s.EVENT_END&&!t.touches.length-1&&this.stopDetect(),t}},stopDetect:function(){this.previous=s.utils.extend({},this.current),this.current=null,this.stopped=!0},extendEventData:function(t){var e=this.current.startEvent;if(e&&(t.touches.length!=e.touches.length||t.touches===e.touches)){e.touches=[];for(var i=0,n=t.touches.length;n>i;i++)e.touches.push(s.utils.extend({},t.touches[i]))}var o=t.timeStamp-e.timeStamp,r=t.center.pageX-e.center.pageX,a=t.center.pageY-e.center.pageY,h=s.utils.getVelocity(o,r,a);return s.utils.extend(t,{deltaTime:o,deltaX:r,deltaY:a,velocityX:h.x,velocityY:h.y,distance:s.utils.getDistance(e.center,t.center),angle:s.utils.getAngle(e.center,t.center),direction:s.utils.getDirection(e.center,t.center),scale:s.utils.getScale(e.touches,t.touches),rotation:s.utils.getRotation(e.touches,t.touches),startEvent:e}),t},register:function(t){var e=t.defaults||{};return e[t.name]===i&&(e[t.name]=!0),s.utils.extend(s.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}},s.gestures=s.gestures||{},s.gestures.Hold={name:"hold",index:10,defaults:{hold_timeout:500,hold_threshold:1},timer:null,handler:function(t,e){switch(t.eventType){case s.EVENT_START:clearTimeout(this.timer),s.detection.current.name=this.name,this.timer=setTimeout(function(){"hold"==s.detection.current.name&&e.trigger("hold",t)},e.options.hold_timeout);break;case s.EVENT_MOVE:t.distance>e.options.hold_threshold&&clearTimeout(this.timer);break;case s.EVENT_END:clearTimeout(this.timer)}}},s.gestures.Tap={name:"tap",index:100,defaults:{tap_max_touchtime:250,tap_max_distance:10,tap_always:!0,doubletap_distance:20,doubletap_interval:300},handler:function(t,e){if(t.eventType==s.EVENT_END){var i=s.detection.previous,n=!1;if(t.deltaTime>e.options.tap_max_touchtime||t.distance>e.options.tap_max_distance)return;i&&"tap"==i.name&&t.timeStamp-i.lastEvent.timeStamp0&&t.touches.length>e.options.swipe_max_touches)return;(t.velocityX>e.options.swipe_velocity||t.velocityY>e.options.swipe_velocity)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},s.gestures.Drag={name:"drag",index:50,defaults:{drag_min_distance:10,drag_max_touches:1,drag_block_horizontal:!1,drag_block_vertical:!1,drag_lock_to_axis:!1,drag_lock_min_distance:25},triggered:!1,handler:function(t,e){if(s.detection.current.name!=this.name&&this.triggered)return e.trigger(this.name+"end",t),this.triggered=!1,i;if(!(e.options.drag_max_touches>0&&t.touches.length>e.options.drag_max_touches))switch(t.eventType){case s.EVENT_START:this.triggered=!1;break;case s.EVENT_MOVE:if(t.distancet.deltaY?s.DIRECTION_UP:s.DIRECTION_DOWN:0>t.deltaX?s.DIRECTION_LEFT:s.DIRECTION_RIGHT),this.triggered||(e.trigger(this.name+"start",t),this.triggered=!0),e.trigger(this.name,t),e.trigger(this.name+t.direction,t),(e.options.drag_block_vertical&&s.utils.isVertical(t.direction)||e.options.drag_block_horizontal&&!s.utils.isVertical(t.direction))&&t.preventDefault();break;case s.EVENT_END:this.triggered&&e.trigger(this.name+"end",t),this.triggered=!1}}},s.gestures.Transform={name:"transform",index:45,defaults:{transform_min_scale:.01,transform_min_rotation:1,transform_always_block:!1},triggered:!1,handler:function(t,e){if(s.detection.current.name!=this.name&&this.triggered)return e.trigger(this.name+"end",t),this.triggered=!1,i;if(!(2>t.touches.length))switch(e.options.transform_always_block&&t.preventDefault(),t.eventType){case s.EVENT_START:this.triggered=!1;break;case s.EVENT_MOVE:var n=Math.abs(1-t.scale),o=Math.abs(t.rotation);if(e.options.transform_min_scale>n&&e.options.transform_min_rotation>o)return;s.detection.current.name=this.name,this.triggered||(e.trigger(this.name+"start",t),this.triggered=!0),e.trigger(this.name,t),o>e.options.transform_min_rotation&&e.trigger("rotate",t),n>e.options.transform_min_scale&&(e.trigger("pinch",t),e.trigger("pinch"+(1>t.scale?"in":"out"),t));break;case s.EVENT_END:this.triggered&&e.trigger(this.name+"end",t),this.triggered=!1}}},s.gestures.Touch={name:"touch",index:-1/0,defaults:{prevent_default:!1,prevent_mouseevents:!1},handler:function(t,e){return e.options.prevent_mouseevents&&t.pointerType==s.POINTER_MOUSE?(t.stopDetect(),i):(e.options.prevent_default&&t.preventDefault(),t.eventType==s.EVENT_START&&e.trigger(this.name,t),i)}},s.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==s.EVENT_END&&e.trigger(this.name,t)}},"object"==typeof e&&"object"==typeof e.exports?e.exports=s:(t.Hammer=s,"function"==typeof t.define&&t.define.amd&&t.define("hammer",[],function(){return s}))})(this)},{}],2:[function(e,i){(function(n){function s(t,e){return function(i){return p(t.call(this,i),e)}}function o(t,e){return function(i){return this.lang().ordinal(t.call(this,i),e)}}function r(){}function a(t){d(this,t)}function h(t){var e=t.years||t.year||t.y||0,i=t.months||t.month||t.M||0,n=t.weeks||t.week||t.w||0,s=t.days||t.day||t.d||0,o=t.hours||t.hour||t.h||0,r=t.minutes||t.minute||t.m||0,a=t.seconds||t.second||t.s||0,h=t.milliseconds||t.millisecond||t.ms||0;this._input=t,this._milliseconds=+h+1e3*a+6e4*r+36e5*o,this._days=+s+7*n,this._months=+i+12*e,this._data={},this._bubble()}function d(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function l(t){return 0>t?Math.ceil(t):Math.floor(t)}function p(t,e){for(var i=t+"";e>i.length;)i="0"+i;return i}function c(t,e,i,n){var s,o,r=e._milliseconds,a=e._days,h=e._months;r&&t._d.setTime(+t._d+r*i),(a||h)&&(s=t.minute(),o=t.hour()),a&&t.date(t.date()+a*i),h&&t.month(t.month()+h*i),r&&!n&&H.updateOffset(t),(a||h)&&(t.minute(s),t.hour(o))}function u(t){return"[object Array]"===Object.prototype.toString.call(t)}function f(t,e){var i,n=Math.min(t.length,e.length),s=Math.abs(t.length-e.length),o=0;for(i=0;n>i;i++)~~t[i]!==~~e[i]&&o++;return o+s}function m(t){return t?pe[t]||t.toLowerCase().replace(/(.)s$/,"$1"):t}function g(t,e){return e.abbr=t,V[t]||(V[t]=new r),V[t].set(e),V[t]}function v(t){delete V[t]}function y(t){if(!t)return H.fn._lang;if(!V[t]&&B)try{e("./lang/"+t)}catch(i){return H.fn._lang}return V[t]||H.fn._lang}function b(t){return t.match(/\[.*\]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function w(t){var e,i,n=t.match(X);for(e=0,i=n.length;i>e;e++)n[e]=me[n[e]]?me[n[e]]:b(n[e]);return function(s){var o="";for(e=0;i>e;e++)o+=n[e]instanceof Function?n[e].call(s,t):n[e];return o}}function _(t,e){return e=E(e,t.lang()),ce[e]||(ce[e]=w(e)),ce[e](t)}function E(t,e){function i(t){return e.longDateFormat(t)||t}for(var n=5;n--&&(Z.lastIndex=0,Z.test(t));)t=t.replace(Z,i);return t}function T(t,e){switch(t){case"DDDD":return Q;case"YYYY":return $;case"YYYYY":return te;case"S":case"SS":case"SSS":case"DDD":return J;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return ee;case"a":case"A":return y(e._l)._meridiemParse;case"X":return se;case"Z":case"ZZ":return ie;case"T":return ne;case"MM":case"DD":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return K;default:return RegExp(t.replace("\\",""))}}function x(t){var e=(ie.exec(t)||[])[0],i=(e+"").match(he)||["-",0,0],n=+(60*i[1])+~~i[2];return"+"===i[0]?-n:n}function S(t,e,i){var n,s=i._a;switch(t){case"M":case"MM":null!=e&&(s[1]=~~e-1);break;case"MMM":case"MMMM":n=y(i._l).monthsParse(e),null!=n?s[1]=n:i._isValid=!1;break;case"D":case"DD":null!=e&&(s[2]=~~e);break;case"DDD":case"DDDD":null!=e&&(s[1]=0,s[2]=~~e);break;case"YY":s[0]=~~e+(~~e>68?1900:2e3);break;case"YYYY":case"YYYYY":s[0]=~~e;break;case"a":case"A":i._isPm=y(i._l).isPM(e);break;case"H":case"HH":case"h":case"hh":s[3]=~~e;break;case"m":case"mm":s[4]=~~e;break;case"s":case"ss":s[5]=~~e;break;case"S":case"SS":case"SSS":s[6]=~~(1e3*("0."+e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=x(e)}null==e&&(i._isValid=!1)}function D(t){var e,i,n,s=[];if(!t._d){for(n=C(t),e=0;3>e&&null==t._a[e];++e)t._a[e]=s[e]=n[e];for(;7>e;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];s[3]+=~~((t._tzm||0)/60),s[4]+=~~((t._tzm||0)%60),i=new Date(0),t._useUTC?(i.setUTCFullYear(s[0],s[1],s[2]),i.setUTCHours(s[3],s[4],s[5],s[6])):(i.setFullYear(s[0],s[1],s[2]),i.setHours(s[3],s[4],s[5],s[6])),t._d=i}}function M(t){var e=t._i;t._d||(t._a=[e.years||e.year||e.y,e.months||e.month||e.M,e.days||e.day||e.d,e.hours||e.hour||e.h,e.minutes||e.minute||e.m,e.seconds||e.second||e.s,e.milliseconds||e.millisecond||e.ms],D(t))}function C(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function O(t){var e,i,n,s=y(t._l),o=""+t._i;for(n=E(t._f,s).match(X),t._a=[],e=0;n.length>e;e++)i=(T(n[e],t).exec(o)||[])[0],i&&(o=o.slice(o.indexOf(i)+i.length)),me[n[e]]&&S(n[e],i,t);o&&(t._il=o),t._isPm&&12>t._a[3]&&(t._a[3]+=12),t._isPm===!1&&12===t._a[3]&&(t._a[3]=0),D(t)}function N(t){var e,i,n,s,o,r=99;for(s=0;t._f.length>s;s++)e=d({},t),e._f=t._f[s],O(e),i=new a(e),o=f(e._a,i.toArray()),i._il&&(o+=i._il.length),r>o&&(r=o,n=i);d(t,n)}function L(t){var e,i=t._i,n=oe.exec(i);if(n){for(t._f="YYYY-MM-DD"+(n[2]||" "),e=0;4>e;e++)if(ae[e][1].exec(i)){t._f+=ae[e][0];break}ie.exec(i)&&(t._f+=" Z"),O(t)}else t._d=new Date(i)}function I(t){var e=t._i,i=q.exec(e);e===n?t._d=new Date:i?t._d=new Date(+i[1]):"string"==typeof e?L(t):u(e)?(t._a=e.slice(0),D(t)):e instanceof Date?t._d=new Date(+e):"object"==typeof e?M(t):t._d=new Date(e)}function k(t,e,i,n,s){return s.relativeTime(e||1,!!i,t,n)}function A(t,e,i){var n=W(Math.abs(t)/1e3),s=W(n/60),o=W(s/60),r=W(o/24),a=W(r/365),h=45>n&&["s",n]||1===s&&["m"]||45>s&&["mm",s]||1===o&&["h"]||22>o&&["hh",o]||1===r&&["d"]||25>=r&&["dd",r]||45>=r&&["M"]||345>r&&["MM",W(r/30)]||1===a&&["y"]||["yy",a];return h[2]=e,h[3]=t>0,h[4]=i,k.apply({},h)}function P(t,e,i){var n,s=i-e,o=i-t.day();return o>s&&(o-=7),s-7>o&&(o+=7),n=H(t).add("d",o),{week:Math.ceil(n.dayOfYear()/7),year:n.year()}}function F(t){var e=t._i,i=t._f;return null===e||""===e?null:("string"==typeof e&&(t._i=e=y().preparse(e)),H.isMoment(e)?(t=d({},e),t._d=new Date(+e._d)):i?u(i)?N(t):O(t):I(t),new a(t))}function Y(t,e){H.fn[t]=H.fn[t+"s"]=function(t){var i=this._isUTC?"UTC":"";return null!=t?(this._d["set"+i+e](t),H.updateOffset(this),this):this._d["get"+i+e]()}}function R(t){H.duration.fn[t]=function(){return this._data[t]}}function z(t,e){H.duration.fn["as"+t]=function(){return+this/e}}for(var H,U,j="2.2.1",W=Math.round,V={},B=i!==n&&i.exports,q=/^\/?Date\((\-?\d+)/i,G=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,X=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,Z=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,K=/\d\d?/,J=/\d{1,3}/,Q=/\d{3}/,$=/\d{1,4}/,te=/[+\-]?\d{1,6}/,ee=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,ie=/Z|[\+\-]\d\d:?\d\d/i,ne=/T/i,se=/[\+\-]?\d+(\.\d{1,3})?/,oe=/^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,re="YYYY-MM-DDTHH:mm:ssZ",ae=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],he=/([\+\-]|\d\d)/gi,de="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),le={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},pe={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",w:"week",W:"isoweek",M:"month",y:"year"},ce={},ue="DDD w W M D d".split(" "),fe="M D H h m s w W".split(" "),me={M:function(){return this.month()+1},MMM:function(t){return this.lang().monthsShort(this,t)},MMMM:function(t){return this.lang().months(this,t)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(t){return this.lang().weekdaysMin(this,t)},ddd:function(t){return this.lang().weekdaysShort(this,t)},dddd:function(t){return this.lang().weekdays(this,t)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return p(this.year()%100,2)},YYYY:function(){return p(this.year(),4)},YYYYY:function(){return p(this.year(),5)},gg:function(){return p(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return p(this.weekYear(),5)},GG:function(){return p(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return p(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return~~(this.milliseconds()/100)},SS:function(){return p(~~(this.milliseconds()/10),2)},SSS:function(){return p(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+p(~~(t/60),2)+":"+p(~~t%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+p(~~(10*t/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}};ue.length;)U=ue.pop(),me[U+"o"]=o(me[U],U);for(;fe.length;)U=fe.pop(),me[U+U]=s(me[U],2);for(me.DDDD=s(me.DDD,3),d(r.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,i,n;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(i=H.utc([2e3,e]),n="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[e]=RegExp(n.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,n;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=H([2e3,1]).day(e),n="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=RegExp(n.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var i=this._calendar[t];return"function"==typeof i?i.apply(e):i},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,n){var s=this._relativeTime[i];return"function"==typeof s?s(t,e,i,n):s.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return P(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6}}),H=function(t,e,i){return F({_i:t,_f:e,_l:i,_isUTC:!1})},H.utc=function(t,e,i){return F({_useUTC:!0,_isUTC:!0,_l:i,_i:t,_f:e}).utc()},H.unix=function(t){return H(1e3*t)},H.duration=function(t,e){var i,n,s=H.isDuration(t),o="number"==typeof t,r=s?t._input:o?{}:t,a=G.exec(t);return o?e?r[e]=t:r.milliseconds=t:a&&(i="-"===a[1]?-1:1,r={y:0,d:~~a[2]*i,h:~~a[3]*i,m:~~a[4]*i,s:~~a[5]*i,ms:~~a[6]*i}),n=new h(r),s&&t.hasOwnProperty("_lang")&&(n._lang=t._lang),n},H.version=j,H.defaultFormat=re,H.updateOffset=function(){},H.lang=function(t,e){return t?(t=t.toLowerCase(),t=t.replace("_","-"),e?g(t,e):null===e?(v(t),t="en"):V[t]||y(t),H.duration.fn._lang=H.fn._lang=y(t),n):H.fn._lang._abbr},H.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),y(t)},H.isMoment=function(t){return t instanceof a},H.isDuration=function(t){return t instanceof h},d(H.fn=a.prototype,{clone:function(){return H(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return _(H(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var t=this;return[t.year(),t.month(),t.date(),t.hours(),t.minutes(),t.seconds(),t.milliseconds()]},isValid:function(){return null==this._isValid&&(this._isValid=this._a?!f(this._a,(this._isUTC?H.utc(this._a):H(this._a)).toArray()):!isNaN(this._d.getTime())),!!this._isValid},invalidAt:function(){var t,e=this._a,i=(this._isUTC?H.utc(this._a):H(this._a)).toArray();for(t=6;t>=0&&e[t]===i[t];--t);return t},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=_(this,t||H.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var i;return i="string"==typeof t?H.duration(+e,t):H.duration(t,e),c(this,i,1),this},subtract:function(t,e){var i;return i="string"==typeof t?H.duration(+e,t):H.duration(t,e),c(this,i,-1),this},diff:function(t,e,i){var n,s,o=this._isUTC?H(t).zone(this._offset||0):H(t).local(),r=6e4*(this.zone()-o.zone());return e=m(e),"year"===e||"month"===e?(n=432e5*(this.daysInMonth()+o.daysInMonth()),s=12*(this.year()-o.year())+(this.month()-o.month()),s+=(this-H(this).startOf("month")-(o-H(o).startOf("month")))/n,s-=6e4*(this.zone()-H(this).startOf("month").zone()-(o.zone()-H(o).startOf("month").zone()))/n,"year"===e&&(s/=12)):(n=this-o,s="second"===e?n/1e3:"minute"===e?n/6e4:"hour"===e?n/36e5:"day"===e?(n-r)/864e5:"week"===e?(n-r)/6048e5:n),i?s:l(s)},from:function(t,e){return H.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(H(),t)},calendar:function(){var t=this.diff(H().zone(this.zone()).startOf("day"),"days",!0),e=-6>t?"sameElse":-1>t?"lastWeek":0>t?"lastDay":1>t?"sameDay":2>t?"nextDay":7>t?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){var t=this.year();return 0===t%4&&0!==t%100||0===t%400},isDST:function(){return this.zone()+H(t).startOf(e)},isBefore:function(t,e){return e=e!==n?e:"millisecond",+this.clone().startOf(e)<+H(t).startOf(e)},isSame:function(t,e){return e=e!==n?e:"millisecond",+this.clone().startOf(e)===+H(t).startOf(e)},min:function(t){return t=H.apply(null,arguments),this>t?this:t},max:function(t){return t=H.apply(null,arguments),t>this?this:t},zone:function(t){var e=this._offset||0;return null==t?this._isUTC?e:this._d.getTimezoneOffset():("string"==typeof t&&(t=x(t)),16>Math.abs(t)&&(t=60*t),this._offset=t,this._isUTC=!0,e!==t&&c(this,H.duration(e-t,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},hasAlignedHourOffset:function(t){return t=t?H(t).zone():0,0===(this.zone()-t)%60},daysInMonth:function(){return H.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(t){var e=W((H(this).startOf("day")-H(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},weekYear:function(t){var e=P(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=P(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=P(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this._d.getDay()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},get:function(t){return t=m(t),this[t.toLowerCase()]()},set:function(t,e){t=m(t),this[t.toLowerCase()](e)},lang:function(t){return t===n?this._lang:(this._lang=y(t),this)}}),U=0;de.length>U;U++)Y(de[U].toLowerCase().replace(/s$/,""),de[U]);Y("year","FullYear"),H.fn.days=H.fn.day,H.fn.months=H.fn.month,H.fn.weeks=H.fn.week,H.fn.isoWeeks=H.fn.isoWeek,H.fn.toJSON=H.fn.toISOString,d(H.duration.fn=h.prototype,{_bubble:function(){var t,e,i,n,s=this._milliseconds,o=this._days,r=this._months,a=this._data;a.milliseconds=s%1e3,t=l(s/1e3),a.seconds=t%60,e=l(t/60),a.minutes=e%60,i=l(e/60),a.hours=i%24,o+=l(i/24),a.days=o%30,r+=l(o/30),a.months=r%12,n=l(r/12),a.years=n},weeks:function(){return l(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+2592e6*(this._months%12)+31536e6*~~(this._months/12)},humanize:function(t){var e=+this,i=A(e,!t,this.lang());return t&&(i=this.lang().pastFuture(e,i)),this.lang().postformat(i)},add:function(t,e){var i=H.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=H.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=m(t),this[t.toLowerCase()+"s"]()},as:function(t){return t=m(t),this["as"+t.charAt(0).toUpperCase()+t.slice(1)+"s"]()},lang:H.fn.lang});for(U in le)le.hasOwnProperty(U)&&(z(U,le[U]),R(U.toLowerCase()));z("Weeks",6048e5),H.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},H.lang("en",{ordinal:function(t){var e=t%10,i=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),B&&(i.exports=H),"undefined"==typeof ender&&(this.moment=H),"function"==typeof t&&t.amd&&t("moment",[],function(){return H})}).call(this)},{}],3:[function(e,i,n){function s(){this.subscriptions=[]}function o(t){if(this.id=A.randomUUID(),this.options=t||{},this.data={},this.fieldId=this.options.fieldId||"id",this.convert={},this.options.convert)for(var e in this.options.convert)if(this.options.convert.hasOwnProperty(e)){var i=this.options.convert[e];this.convert[e]="Date"==i||"ISODate"==i||"ASPDate"==i?"Date":i
+}this.subscribers={},this.internalIds={}}function r(t,e){this.id=A.randomUUID(),this.data=null,this.ids={},this.options=e||{},this.fieldId="id",this.subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}function a(t,e){this.parent=t,this.options=e||{},this.defaultOptions={order:function(t,e){if(t instanceof E){if(e instanceof E){var i=t.data.end-t.data.start,n=e.data.end-e.data.start;return i-n||t.data.start-e.data.start}return-1}return e instanceof E?1:t.data.start-e.data.start},margin:{item:10}},this.ordered=[]}function h(t){this.id=A.randomUUID(),this.start=null,this.end=null,this.options=t||{},this.setOptions(t)}function d(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". '+'Choose "horizontal" or "vertical".')}function l(t,e){return{x:t.pageX-R.util.getAbsoluteLeft(e),y:t.pageY-R.util.getAbsoluteTop(e)}}function p(){this.id=A.randomUUID(),this.components={},this.repaintTimer=void 0,this.reflowTimer=void 0}function c(){this.id=null,this.parent=null,this.depends=null,this.controller=null,this.options=null,this.frame=null,this.top=0,this.left=0,this.width=0,this.height=0}function u(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.options=i||{}}function f(t,e){this.id=A.randomUUID(),this.container=t,this.options=e||{},this.defaultOptions={autoResize:!0},this.listeners={}}function m(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.dom={majorLines:[],majorTexts:[],minorLines:[],minorTexts:[],redundant:{majorLines:[],majorTexts:[],minorLines:[],minorTexts:[]}},this.props={range:{start:0,end:0,minimumStep:0},lineTop:0},this.options=i||{},this.defaultOptions={orientation:"bottom",showMinorLabels:!0,showMajorLabels:!0},this.conversion=null,this.range=null}function g(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.options=i||{},this.defaultOptions={showCurrentTime:!1}}function v(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.options=i||{},this.defaultOptions={showCustomTime:!1},this.listeners=[],this.customTime=new Date}function y(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.options=i||{},this.defaultOptions={type:"box",align:"center",orientation:"bottom",margin:{axis:20,item:10},padding:5},this.dom={};var n=this;this.itemsData=null,this.range=null,this.listeners={add:function(t,e,i){i!=n.id&&n._onAdd(e.items)},update:function(t,e,i){i!=n.id&&n._onUpdate(e.items)},remove:function(t,e,i){i!=n.id&&n._onRemove(e.items)}},this.items={},this.queue={},this.stack=new a(this,Object.create(this.options)),this.conversion=null}function b(t,e,i,n){this.parent=t,this.data=e,this.dom=null,this.options=i||{},this.defaultOptions=n||{},this.selected=!1,this.visible=!1,this.top=0,this.left=0,this.width=0,this.height=0}function w(t,e,i,n){this.props={dot:{left:0,top:0,width:0,height:0},line:{top:0,left:0,width:0,height:0}},b.call(this,t,e,i,n)}function _(t,e,i,n){this.props={dot:{top:0,width:0,height:0},content:{height:0,marginLeft:0}},b.call(this,t,e,i,n)}function E(t,e,i,n){this.props={content:{left:0,width:0}},b.call(this,t,e,i,n)}function T(t,e,i,n){this.props={content:{left:0,width:0}},E.call(this,t,e,i,n)}function x(t,e,i){this.id=A.randomUUID(),this.parent=t,this.groupId=e,this.itemset=null,this.options=i||{},this.options.top=0,this.props={label:{width:0,height:0}},this.top=0,this.left=0,this.width=0,this.height=0}function S(t,e,i){this.id=A.randomUUID(),this.parent=t,this.depends=e,this.options=i||{},this.range=null,this.itemsData=null,this.groupsData=null,this.groups={},this.dom={},this.props={labels:{width:0}},this.queue={};var n=this;this.listeners={add:function(t,e){n._onAdd(e.items)},update:function(t,e){n._onUpdate(e.items)},remove:function(t,e){n._onRemove(e.items)}}}function D(t,e,i){var n=this,s=I().hours(0).minutes(0).seconds(0).milliseconds(0);if(this.options={orientation:"bottom",min:null,max:null,zoomMin:10,zoomMax:31536e10,showMinorLabels:!0,showMajorLabels:!0,showCurrentTime:!1,showCustomTime:!1,autoResize:!1},this.controller=new p,!t)throw Error("No container element provided");var o=Object.create(this.options);o.height=function(){return n.options.height?n.options.height:n.timeaxis.height+n.content.height+"px"},this.rootPanel=new f(t,o),this.controller.add(this.rootPanel);var r=Object.create(this.options);r.left=function(){return n.labelPanel.width},r.width=function(){return n.rootPanel.width-n.labelPanel.width},r.top=null,r.height=null,this.itemPanel=new u(this.rootPanel,[],r),this.controller.add(this.itemPanel);var a=Object.create(this.options);a.top=null,a.left=null,a.height=null,a.width=function(){return n.content&&"function"==typeof n.content.getLabelsWidth?n.content.getLabelsWidth():0},this.labelPanel=new u(this.rootPanel,[],a),this.controller.add(this.labelPanel);var d=Object.create(this.options);this.range=new h(d),this.range.setRange(s.clone().add("days",-3).valueOf(),s.clone().add("days",4).valueOf()),this.range.subscribe(this.rootPanel,"move","horizontal"),this.range.subscribe(this.rootPanel,"zoom","horizontal"),this.range.on("rangechange",function(){var t=!0;n.controller.requestReflow(t)}),this.range.on("rangechanged",function(){var t=!0;n.controller.requestReflow(t)});var l=Object.create(o);l.range=this.range,l.left=null,l.top=null,l.width="100%",l.height=null,this.timeaxis=new m(this.itemPanel,[],l),this.timeaxis.setRange(this.range),this.controller.add(this.timeaxis),this.currenttime=new g(this.timeaxis,[],o),this.controller.add(this.currenttime),this.customtime=new v(this.timeaxis,[],o),this.controller.add(this.customtime),this.setGroups(null),this.itemsData=null,this.groupsData=null,i&&this.setOptions(i),e&&this.setItems(e)}function M(t,e,i,n){this.selected=!1,this.edges=[],this.group=n.nodes.group,this.fontSize=n.nodes.fontSize,this.fontFace=n.nodes.fontFace,this.fontColor=n.nodes.fontColor,this.color=n.nodes.color,this.id=void 0,this.shape=n.nodes.shape,this.image=n.nodes.image,this.x=0,this.y=0,this.xFixed=!1,this.yFixed=!1,this.radius=n.nodes.radius,this.radiusFixed=!1,this.radiusMin=n.nodes.radiusMin,this.radiusMax=n.nodes.radiusMax,this.imagelist=e,this.grouplist=i,this.setProperties(t,n),this.mass=50,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.minForce=n.minForce,this.damping=.9}function C(t,e,i){if(!e)throw"No graph provided";this.graph=e,this.widthMin=i.edges.widthMin,this.widthMax=i.edges.widthMax,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.style=i.edges.style,this.title=void 0,this.width=i.edges.width,this.value=void 0,this.length=i.edges.length,this.from=null,this.to=null,this.connected=!1,this.dash=A.extend({},i.edges.dash),this.stiffness=void 0,this.color=i.edges.color,this.widthFixed=!1,this.lengthFixed=!1,this.setProperties(t,i)}function O(t,e,i,n){this.container=t?t:document.body,this.x=0,this.y=0,this.padding=5,void 0!==e&&void 0!==i&&this.setPosition(e,i),void 0!==n&&this.setText(n),this.frame=document.createElement("div");var s=this.frame.style;s.position="absolute",s.visibility="hidden",s.border="1px solid #666",s.color="black",s.padding=this.padding+"px",s.backgroundColor="#FFFFC6",s.borderRadius="3px",s.MozBorderRadius="3px",s.WebkitBorderRadius="3px",s.boxShadow="3px 3px 10px rgba(128, 128, 128, 0.5)",s.whiteSpace="nowrap",this.container.appendChild(this.frame)}function N(t,e,i){this.containerElement=t,this.width="100%",this.height="100%",this.refreshRate=50,this.stabilize=!0,this.selectable=!0,this.constants={nodes:{radiusMin:5,radiusMax:20,radius:5,distance:100,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fontColor:"black",fontSize:14,fontFace:"arial",color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"}},borderColor:"#2B7CE9",backgroundColor:"#97C2FC",highlightColor:"#D2E5FF",group:void 0},edges:{widthMin:1,widthMax:15,width:1,style:"line",color:"#343434",fontColor:"#343434",fontSize:14,fontFace:"arial",length:100,dash:{length:10,gap:5,altLength:void 0}},minForce:.05,minVelocity:.02,maxIterations:1e3};var n=this;this.nodes={},this.edges={},this.nodesData=null,this.edgesData=null;var s=this;this.nodesListeners={add:function(t,e){s._addNodes(e.items),s.start()},update:function(t,e){s._updateNodes(e.items),s.start()},remove:function(t,e){s._removeNodes(e.items),s.start()}},this.edgesListeners={add:function(t,e){s._addEdges(e.items),s.start()},update:function(t,e){s._updateEdges(e.items),s.start()},remove:function(t,e){s._removeEdges(e.items),s.start()}},this.groups=new Groups,this.images=new Images,this.images.setOnloadCallback(function(){n._redraw()}),this.moving=!1,this.selection=[],this.timer=void 0,this._create(),this.setOptions(i),this.setData(e)}var L,I="undefined"!=typeof window&&window.moment||e("moment");if(L="undefined"!=typeof window?window.Hammer||e("hammerjs"):function(){throw Error("hammer.js is only available in a browser, not in node.js.")},!Array.prototype.indexOf){Array.prototype.indexOf=function(t){for(var e=0;this.length>e;e++)if(this[e]==t)return e;return-1};try{console.log("Warning: Ancient browser detected. Please update your browser")}catch(k){}}Array.prototype.forEach||(Array.prototype.forEach=function(t,e){for(var i=0,n=this.length;n>i;++i)t.call(e||this,this[i],i,this)}),Array.prototype.map||(Array.prototype.map=function(t,e){var i,n,s;if(null==this)throw new TypeError(" this is null or not defined");var o=Object(this),r=o.length>>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(e&&(i=e),n=Array(r),s=0;r>s;){var a,h;s in o&&(a=o[s],h=t.call(i,a,s,o),n[s]=h),s++}return n}),Array.prototype.filter||(Array.prototype.filter=function(t){"use strict";if(null==this)throw new TypeError;var e=Object(this),i=e.length>>>0;if("function"!=typeof t)throw new TypeError;for(var n=[],s=arguments[1],o=0;i>o;o++)if(o in e){var r=e[o];t.call(s,r,o,e)&&n.push(r)}return n}),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!{toString:null}.propertyIsEnumerable("toString"),i=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],n=i.length;return function(s){if("object"!=typeof s&&"function"!=typeof s||null===s)throw new TypeError("Object.keys called on non-object");var o=[];for(var r in s)t.call(s,r)&&o.push(r);if(e)for(var a=0;n>a;a++)t.call(s,i[a])&&o.push(i[a]);return o}}()),Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},s=function(){return i.apply(this instanceof n&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,s.prototype=new n,s}),Object.create||(Object.create=function(t){function e(){}if(arguments.length>1)throw Error("Object.create implementation only accepts the first parameter.");return e.prototype=t,new e}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},s=function(){return i.apply(this instanceof n&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,s.prototype=new n,s});var A={};A.isNumber=function(t){return t instanceof Number||"number"==typeof t},A.isString=function(t){return t instanceof String||"string"==typeof t},A.isDate=function(t){if(t instanceof Date)return!0;if(A.isString(t)){var e=P.exec(t);if(e)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},A.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},A.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},A.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var n=arguments[e];for(var s in n)n.hasOwnProperty(s)&&void 0!==n[s]&&(t[s]=n[s])}return t},A.convert=function(t,e){var i;if(void 0===t)return void 0;if(null===t)return null;if(!e)return t;if("string"!=typeof e&&!(e instanceof String))throw Error("Type must be a string");switch(e){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return t+"";case"Date":if(A.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(I.isMoment(t))return new Date(t.valueOf());if(A.isString(t))return i=P.exec(t),i?new Date(Number(i[1])):I(t).toDate();throw Error("Cannot convert object of type "+A.getType(t)+" to type Date");case"Moment":if(A.isNumber(t))return I(t);if(t instanceof Date)return I(t.valueOf());if(I.isMoment(t))return I(t);if(A.isString(t))return i=P.exec(t),i?I(Number(i[1])):I(t);throw Error("Cannot convert object of type "+A.getType(t)+" to type Date");case"ISODate":if(A.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(I.isMoment(t))return t.toDate().toISOString();if(A.isString(t))return i=P.exec(t),i?new Date(Number(i[1])).toISOString():new Date(t).toISOString();throw Error("Cannot convert object of type "+A.getType(t)+" to type ISODate");case"ASPDate":if(A.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(A.isString(t)){i=P.exec(t);var n;return n=i?new Date(Number(i[1])).valueOf():new Date(t).valueOf(),"/Date("+n+")/"}throw Error("Cannot convert object of type "+A.getType(t)+" to type ASPDate");default:throw Error("Cannot convert object of type "+A.getType(t)+' to type "'+e+'"')}};var P=/^\/?Date\((\-?\d+)/i;A.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":t instanceof Array?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},A.getAbsoluteLeft=function(t){for(var e=document.documentElement,i=document.body,n=t.offsetLeft,s=t.offsetParent;null!=s&&s!=i&&s!=e;)n+=s.offsetLeft,n-=s.scrollLeft,s=s.offsetParent;return n},A.getAbsoluteTop=function(t){for(var e=document.documentElement,i=document.body,n=t.offsetTop,s=t.offsetParent;null!=s&&s!=i&&s!=e;)n+=s.offsetTop,n-=s.scrollTop,s=s.offsetParent;return n},A.getPageY=function(t){if("pageY"in t)return t.pageY;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientY:t.clientY;var i=document.documentElement,n=document.body;return e+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)},A.getPageX=function(t){if("pageY"in t)return t.pageX;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientX:t.clientX;var i=document.documentElement,n=document.body;return e+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0)},A.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},A.removeClassName=function(t,e){var i=t.className.split(" "),n=i.indexOf(e);-1!=n&&(i.splice(n,1),t.className=i.join(" "))},A.forEach=function(t,e){var i,n;if(t instanceof Array)for(i=0,n=t.length;n>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},A.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},A.addEventListener=function(t,e,i,n){t.addEventListener?(void 0===n&&(n=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,n)):t.attachEvent("on"+e,i)},A.removeEventListener=function(t,e,i,n){t.removeEventListener?(void 0===n&&(n=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,n)):t.detachEvent("on"+e,i)},A.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},A.stopPropagation=function(t){t||(t=window.event),t.stopPropagation?t.stopPropagation():t.cancelBubble=!0},A.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},A.option={},A.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},A.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},A.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?t+"":e||null},A.option.asSize=function(t,e){return"function"==typeof t&&(t=t()),A.isString(t)?t:A.isNumber(t)?t+"px":e||null},A.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},A.loadCss=function(t){if("undefined"!=typeof document){var e=document.createElement("style");e.type="text/css",e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),document.getElementsByTagName("head")[0].appendChild(e)}};var F={listeners:[],indexOf:function(t){for(var e=this.listeners,i=0,n=this.listeners.length;n>i;i++){var s=e[i];if(s&&s.object==t)return i}return-1},addListener:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];s||(s={object:t,events:{}},this.listeners.push(s));var o=s.events[e];o||(o=[],s.events[e]=o),-1==o.indexOf(i)&&o.push(i)},removeListener:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];if(s){var o=s.events[e];o&&(n=o.indexOf(i),-1!=n&&o.splice(n,1),0==o.length&&delete s.events[e]);var r=0,a=s.events;for(var h in a)a.hasOwnProperty(h)&&r++;0==r&&delete this.listeners[n]}},removeAllListeners:function(){this.listeners=[]},trigger:function(t,e,i){var n=this.indexOf(t),s=this.listeners[n];if(s){var o=s.events[e];if(o)for(var r=0,a=o.length;a>r;r++)o[r](i)}}};s.prototype.on=function(t,e,i){var n=t instanceof RegExp?t:RegExp(t.replace("*","\\w+")),s={id:A.randomUUID(),event:t,regexp:n,callback:"function"==typeof e?e:null,target:i};return this.subscriptions.push(s),s.id},s.prototype.off=function(t){for(var e=0;this.subscriptions.length>e;){var i=this.subscriptions[e],n=!0;if(t instanceof Object)for(var s in t)t.hasOwnProperty(s)&&t[s]!==i[s]&&(n=!1);else n=i.id==t;n?this.subscriptions.splice(e,1):e++}},s.prototype.emit=function(t,e,i){for(var n=0;this.subscriptions.length>n;n++){var s=this.subscriptions[n];s.regexp.test(t)&&s.callback&&s.callback(t,e,i)}},o.prototype.subscribe=function(t,e){var i=this.subscribers[t];i||(i=[],this.subscribers[t]=i),i.push({callback:e})},o.prototype.unsubscribe=function(t,e){var i=this.subscribers[t];i&&(this.subscribers[t]=i.filter(function(t){return t.callback!=e}))},o.prototype._trigger=function(t,e,i){if("*"==t)throw Error("Cannot trigger event *");var n=[];t in this.subscribers&&(n=n.concat(this.subscribers[t])),"*"in this.subscribers&&(n=n.concat(this.subscribers["*"]));for(var s=0;n.length>s;s++){var o=n[s];o.callback&&o.callback(t,e,i||null)}},o.prototype.add=function(t,e){var i,n=[],s=this;if(t instanceof Array)for(var o=0,r=t.length;r>o;o++)i=s._addItem(t[o]),n.push(i);else if(A.isDataTable(t))for(var a=this._getColumnNames(t),h=0,d=t.getNumberOfRows();d>h;h++){for(var l={},p=0,c=a.length;c>p;p++){var u=a[p];l[u]=t.getValue(h,p)}i=s._addItem(l),n.push(i)}else{if(!(t instanceof Object))throw Error("Unknown dataType");i=s._addItem(t),n.push(i)}return n.length&&this._trigger("add",{items:n},e),n},o.prototype.update=function(t,e){var i=[],n=[],s=this,o=s.fieldId,r=function(t){var e=t[o];s.data[e]?(e=s._updateItem(t),n.push(e)):(e=s._addItem(t),i.push(e))};if(t instanceof Array)for(var a=0,h=t.length;h>a;a++)r(t[a]);else if(A.isDataTable(t))for(var d=this._getColumnNames(t),l=0,p=t.getNumberOfRows();p>l;l++){for(var c={},u=0,f=d.length;f>u;u++){var m=d[u];c[m]=t.getValue(l,u)}r(c)}else{if(!(t instanceof Object))throw Error("Unknown dataType");r(t)}return i.length&&this._trigger("add",{items:i},e),n.length&&this._trigger("update",{items:n},e),i.concat(n)},o.prototype.get=function(){var t,e,i,n,s=this,o=A.getType(arguments[0]);"String"==o||"Number"==o?(t=arguments[0],i=arguments[1],n=arguments[2]):"Array"==o?(e=arguments[0],i=arguments[1],n=arguments[2]):(i=arguments[0],n=arguments[1]);var r;if(i&&i.type){if(r="DataTable"==i.type?"DataTable":"Array",n&&r!=A.getType(n))throw Error('Type of parameter "data" ('+A.getType(n)+") "+"does not correspond with specified options.type ("+i.type+")");if("DataTable"==r&&!A.isDataTable(n))throw Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else r=n?"DataTable"==A.getType(n)?"DataTable":"Array":"Array";var a,h,d,l,p=i&&i.convert||this.options.convert,c=i&&i.filter,u=[];if(void 0!=t)a=s._getItem(t,p),c&&!c(a)&&(a=null);else if(void 0!=e)for(d=0,l=e.length;l>d;d++)a=s._getItem(e[d],p),(!c||c(a))&&u.push(a);else for(h in this.data)this.data.hasOwnProperty(h)&&(a=s._getItem(h,p),(!c||c(a))&&u.push(a));if(i&&i.order&&void 0==t&&this._sort(u,i.order),i&&i.fields){var f=i.fields;if(void 0!=t)a=this._filterFields(a,f);else for(d=0,l=u.length;l>d;d++)u[d]=this._filterFields(u[d],f)}if("DataTable"==r){var m=this._getColumnNames(n);if(void 0!=t)s._appendRow(n,m,a);else for(d=0,l=u.length;l>d;d++)s._appendRow(n,m,u[d]);return n}if(void 0!=t)return a;if(n){for(d=0,l=u.length;l>d;d++)n.push(u[d]);return n}return u},o.prototype.getIds=function(t){var e,i,n,s,o,r=this.data,a=t&&t.filter,h=t&&t.order,d=t&&t.convert||this.options.convert,l=[];if(a)if(h){o=[];for(n in r)r.hasOwnProperty(n)&&(s=this._getItem(n,d),a(s)&&o.push(s));for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(n in r)r.hasOwnProperty(n)&&(s=this._getItem(n,d),a(s)&&l.push(s[this.fieldId]));else if(h){o=[];for(n in r)r.hasOwnProperty(n)&&o.push(r[n]);for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(n in r)r.hasOwnProperty(n)&&(s=r[n],l.push(s[this.fieldId]));return l},o.prototype.forEach=function(t,e){var i,n,s=e&&e.filter,o=e&&e.convert||this.options.convert,r=this.data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],n=i[this.fieldId],t(i,n);else for(n in r)r.hasOwnProperty(n)&&(i=this._getItem(n,o),(!s||s(i))&&t(i,n))},o.prototype.map=function(t,e){var i,n=e&&e.filter,s=e&&e.convert||this.options.convert,o=[],r=this.data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,s),(!n||n(i))&&o.push(t(i,a)));return e&&e.order&&this._sort(o,e.order),o},o.prototype._filterFields=function(t,e){var i={};for(var n in t)t.hasOwnProperty(n)&&-1!=e.indexOf(n)&&(i[n]=t[n]);return i},o.prototype._sort=function(t,e){if(A.isString(e)){var i=e;t.sort(function(t,e){var n=t[i],s=e[i];return n>s?1:s>n?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},o.prototype.remove=function(t,e){var i,n,s,o=[];if(t instanceof Array)for(i=0,n=t.length;n>i;i++)s=this._remove(t[i]),null!=s&&o.push(s);else s=this._remove(t),null!=s&&o.push(s);return o.length&&this._trigger("remove",{items:o},e),o},o.prototype._remove=function(t){if(A.isNumber(t)||A.isString(t)){if(this.data[t])return delete this.data[t],delete this.internalIds[t],t}else if(t instanceof Object){var e=t[this.fieldId];if(e&&this.data[e])return delete this.data[e],delete this.internalIds[e],e}return null},o.prototype.clear=function(t){var e=Object.keys(this.data);return this.data={},this.internalIds={},this._trigger("remove",{items:e},t),e},o.prototype.max=function(t){var e=this.data,i=null,n=null;for(var s in e)if(e.hasOwnProperty(s)){var o=e[s],r=o[t];null!=r&&(!i||r>n)&&(i=o,n=r)}return i},o.prototype.min=function(t){var e=this.data,i=null,n=null;for(var s in e)if(e.hasOwnProperty(s)){var o=e[s],r=o[t];null!=r&&(!i||n>r)&&(i=o,n=r)}return i},o.prototype.distinct=function(t){var e=this.data,i=[],n=this.options.convert[t],s=0;for(var o in e)if(e.hasOwnProperty(o)){for(var r=e[o],a=A.convert(r[t],n),h=!1,d=0;s>d;d++)if(i[d]==a){h=!0;break}h||(i[s]=a,s++)}return i},o.prototype._addItem=function(t){var e=t[this.fieldId];if(void 0!=e){if(this.data[e])throw Error("Cannot add item: item with id "+e+" already exists")}else e=A.randomUUID(),t[this.fieldId]=e,this.internalIds[e]=t;var i={};for(var n in t)if(t.hasOwnProperty(n)){var s=this.convert[n];i[n]=A.convert(t[n],s)}return this.data[e]=i,e},o.prototype._getItem=function(t,e){var i,n,s=this.data[t];if(!s)return null;var o={},r=this.fieldId,a=this.internalIds;if(e)for(i in s)s.hasOwnProperty(i)&&(n=s[i],i==r&&n in a||(o[i]=A.convert(n,e[i])));else for(i in s)s.hasOwnProperty(i)&&(n=s[i],i==r&&n in a||(o[i]=n));return o},o.prototype._updateItem=function(t){var e=t[this.fieldId];if(void 0==e)throw Error("Cannot update item: item has no id (item: "+JSON.stringify(t)+")");var i=this.data[e];if(!i)throw Error("Cannot update item: no item with id "+e+" found");for(var n in t)if(t.hasOwnProperty(n)){var s=this.convert[n];i[n]=A.convert(t[n],s)}return e},o.prototype._getColumnNames=function(t){for(var e=[],i=0,n=t.getNumberOfColumns();n>i;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},o.prototype._appendRow=function(t,e,i){for(var n=t.addRow(),s=0,o=e.length;o>s;s++){var r=e[s];t.setValue(n,s,i[r])}},r.prototype.setData=function(t){var e,i,n;if(this.data){this.data.unsubscribe&&this.data.unsubscribe("*",this.listener),e=[];for(var s in this.ids)this.ids.hasOwnProperty(s)&&e.push(s);this.ids={},this._trigger("remove",{items:e})}if(this.data=t,this.data){for(this.fieldId=this.options.fieldId||this.data&&this.data.options&&this.data.options.fieldId||"id",e=this.data.getIds({filter:this.options&&this.options.filter}),i=0,n=e.length;n>i;i++)s=e[i],this.ids[s]=!0;this._trigger("add",{items:e}),this.data.subscribe&&this.data.subscribe("*",this.listener)}},r.prototype.get=function(){var t,e,i,n=this,s=A.getType(arguments[0]);"String"==s||"Number"==s||"Array"==s?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var o=A.extend({},this.options,e);this.options.filter&&e&&e.filter&&(o.filter=function(t){return n.options.filter(t)&&e.filter(t)});var r=[];return void 0!=t&&r.push(t),r.push(o),r.push(i),this.data&&this.data.get.apply(this.data,r)},r.prototype.getIds=function(t){var e;if(this.data){var i,n=this.options.filter;i=t&&t.filter?n?function(e){return n(e)&&t.filter(e)}:t.filter:n,e=this.data.getIds({filter:i,order:t&&t.order})}else e=[];return e},r.prototype._onEvent=function(t,e,i){var n,s,o,r,a=e&&e.items,h=this.data,d=[],l=[],p=[];if(a&&h){switch(t){case"add":for(n=0,s=a.length;s>n;n++)o=a[n],r=this.get(o),r&&(this.ids[o]=!0,d.push(o));break;case"update":for(n=0,s=a.length;s>n;n++)o=a[n],r=this.get(o),r?this.ids[o]?l.push(o):(this.ids[o]=!0,d.push(o)):this.ids[o]&&(delete this.ids[o],p.push(o));break;case"remove":for(n=0,s=a.length;s>n;n++)o=a[n],this.ids[o]&&(delete this.ids[o],p.push(o))}d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),p.length&&this._trigger("remove",{items:p},i)}},r.prototype.subscribe=o.prototype.subscribe,r.prototype.unsubscribe=o.prototype.unsubscribe,r.prototype._trigger=o.prototype._trigger,TimeStep=function(t,e,i){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale=TimeStep.SCALE.DAY,this.step=1,this.setRange(t,e,i)},TimeStep.SCALE={MILLISECOND:1,SECOND:2,MINUTE:3,HOUR:4,DAY:5,WEEKDAY:6,MONTH:7,YEAR:8},TimeStep.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},TimeStep.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},TimeStep.prototype.roundToMinor=function(){switch(this.scale){case TimeStep.SCALE.YEAR:this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case TimeStep.SCALE.MONTH:this.current.setDate(1);case TimeStep.SCALE.DAY:case TimeStep.SCALE.WEEKDAY:this.current.setHours(0);case TimeStep.SCALE.HOUR:this.current.setMinutes(0);case TimeStep.SCALE.MINUTE:this.current.setSeconds(0);case TimeStep.SCALE.SECOND:this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step);break;default:}},TimeStep.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},TimeStep.prototype.next=function(){var t=this.current.valueOf();if(6>this.current.getMonth())switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current=new Date(this.current.valueOf()+1e3*this.step);break;case TimeStep.SCALE.MINUTE:this.current=new Date(this.current.valueOf()+60*1e3*this.step);break;case TimeStep.SCALE.HOUR:this.current=new Date(this.current.valueOf()+60*60*1e3*this.step);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step);break;default:}else switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()+this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()+this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()+this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step);break;default:}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.getMilliseconds()0&&(this.step=e),this.autoScale=!1},TimeStep.prototype.setAutoScale=function(t){this.autoScale=t},TimeStep.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,n=864e5,s=36e5,o=6e4,r=1e3,a=1;1e3*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1e3),500*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=500),100*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=100),50*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=50),10*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=10),5*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=5),e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1),3*i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=3),i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=1),5*n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=5),2*n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=2),n>t&&(this.scale=TimeStep.SCALE.DAY,this.step=1),n/2>t&&(this.scale=TimeStep.SCALE.WEEKDAY,this.step=1),4*s>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=4),s>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=1),15*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=15),10*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=10),5*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=5),o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=1),15*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=15),10*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=10),5*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=5),r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=1),200*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=200),100*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=100),50*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=50),10*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=10),5*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=5),a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=1)
+}},TimeStep.prototype.snap=function(t){if(this.scale==TimeStep.SCALE.YEAR){var e=t.getFullYear()+Math.round(t.getMonth()/12);t.setFullYear(Math.round(e/this.step)*this.step),t.setMonth(0),t.setDate(0),t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.MONTH)t.getDate()>15?(t.setDate(1),t.setMonth(t.getMonth()+1)):t.setDate(1),t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0);else if(this.scale==TimeStep.SCALE.DAY||this.scale==TimeStep.SCALE.WEEKDAY){switch(this.step){case 5:case 2:t.setHours(24*Math.round(t.getHours()/24));break;default:t.setHours(12*Math.round(t.getHours()/12))}t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.HOUR){switch(this.step){case 4:t.setMinutes(60*Math.round(t.getMinutes()/60));break;default:t.setMinutes(30*Math.round(t.getMinutes()/30))}t.setSeconds(0),t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.MINUTE){switch(this.step){case 15:case 10:t.setMinutes(5*Math.round(t.getMinutes()/5)),t.setSeconds(0);break;case 5:t.setSeconds(60*Math.round(t.getSeconds()/60));break;default:t.setSeconds(30*Math.round(t.getSeconds()/30))}t.setMilliseconds(0)}else if(this.scale==TimeStep.SCALE.SECOND)switch(this.step){case 15:case 10:t.setSeconds(5*Math.round(t.getSeconds()/5)),t.setMilliseconds(0);break;case 5:t.setMilliseconds(1e3*Math.round(t.getMilliseconds()/1e3));break;default:t.setMilliseconds(500*Math.round(t.getMilliseconds()/500))}else if(this.scale==TimeStep.SCALE.MILLISECOND){var i=this.step>5?this.step/2:1;t.setMilliseconds(Math.round(t.getMilliseconds()/i)*i)}},TimeStep.prototype.isMajor=function(){switch(this.scale){case TimeStep.SCALE.MILLISECOND:return 0==this.current.getMilliseconds();case TimeStep.SCALE.SECOND:return 0==this.current.getSeconds();case TimeStep.SCALE.MINUTE:return 0==this.current.getHours()&&0==this.current.getMinutes();case TimeStep.SCALE.HOUR:return 0==this.current.getHours();case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return 1==this.current.getDate();case TimeStep.SCALE.MONTH:return 0==this.current.getMonth();case TimeStep.SCALE.YEAR:return!1;default:return!1}},TimeStep.prototype.getLabelMinor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return I(t).format("SSS");case TimeStep.SCALE.SECOND:return I(t).format("s");case TimeStep.SCALE.MINUTE:return I(t).format("HH:mm");case TimeStep.SCALE.HOUR:return I(t).format("HH:mm");case TimeStep.SCALE.WEEKDAY:return I(t).format("ddd D");case TimeStep.SCALE.DAY:return I(t).format("D");case TimeStep.SCALE.MONTH:return I(t).format("MMM");case TimeStep.SCALE.YEAR:return I(t).format("YYYY");default:return""}},TimeStep.prototype.getLabelMajor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return I(t).format("HH:mm:ss");case TimeStep.SCALE.SECOND:return I(t).format("D MMMM HH:mm");case TimeStep.SCALE.MINUTE:case TimeStep.SCALE.HOUR:return I(t).format("ddd D MMMM");case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return I(t).format("MMMM YYYY");case TimeStep.SCALE.MONTH:return I(t).format("YYYY");case TimeStep.SCALE.YEAR:return"";default:return""}},a.prototype.setOptions=function(t){A.extend(this.options,t)},a.prototype.update=function(){this._order(),this._stack()},a.prototype._order=function(){var t=this.parent.items;if(!t)throw Error("Cannot stack items: parent does not contain items");var e=[],i=0;A.forEach(t,function(t){t.visible&&(e[i]=t,i++)});var n=this.options.order||this.defaultOptions.order;if("function"!=typeof n)throw Error("Option order must be a function");e.sort(n),this.ordered=e},a.prototype._stack=function(){var t,e,i,n=this.ordered,s=this.options,o=s.orientation||this.defaultOptions.orientation,r="top"==o;for(i=s.margin&&void 0!==s.margin.item?s.margin.item:this.defaultOptions.margin.item,t=0,e=n.length;e>t;t++){var a=n[t],h=null;do h=this.checkOverlap(n,t,0,t-1,i),null!=h&&(a.top=r?h.top+h.height+i:h.top-a.height-i);while(h)}},a.prototype.checkOverlap=function(t,e,i,n,s){for(var o=this.collision,r=t[e],a=n;a>=i;a--){var h=t[a];if(o(r,h,s)&&a!=e)return h}return null},a.prototype.collision=function(t,e,i){return t.left-ie.left&&t.top-ie.top},h.prototype.setOptions=function(t){A.extend(this.options,t),null!==this.start&&null!==this.end&&this.setRange(this.start,this.end)},h.prototype.subscribe=function(t,e,i){function n(e){s._onMouseWheel(e,t,i)}var s=this;if("move"==e)t.on("dragstart",function(e){s._onDragStart(e,t)}),t.on("drag",function(e){s._onDrag(e,t,i)}),t.on("dragend",function(e){s._onDragEnd(e,t)});else{if("zoom"!=e)throw new TypeError('Unknown event "'+e+'". '+'Choose "move" or "zoom".');t.on("mousewheel",n),t.on("DOMMouseScroll",n),t.on("touch",function(){s._onTouch()}),t.on("pinch",function(e){s._onPinch(e,t,i)})}},h.prototype.on=function(t,e){F.addListener(this,t,e)},h.prototype._trigger=function(t){F.trigger(this,t,{start:this.start,end:this.end})},h.prototype.setRange=function(t,e){var i=this._applyRange(t,e);i&&(this._trigger("rangechange"),this._trigger("rangechanged"))},h.prototype._applyRange=function(t,e){var i,n=null!=t?A.convert(t,"Number"):this.start,s=null!=e?A.convert(e,"Number"):this.end,o=null!=this.options.max?A.convert(this.options.max,"Date").valueOf():null,r=null!=this.options.min?A.convert(this.options.min,"Date").valueOf():null;if(isNaN(n)||null===n)throw Error('Invalid start "'+t+'"');if(isNaN(s)||null===s)throw Error('Invalid end "'+e+'"');if(n>s&&(s=n),null!==r&&r>n&&(i=r-n,n+=i,s+=i,null!=o&&s>o&&(s=o)),null!==o&&s>o&&(i=s-o,n-=i,s-=i,null!=r&&r>n&&(n=r)),null!==this.options.zoomMin){var a=parseFloat(this.options.zoomMin);0>a&&(a=0),a>s-n&&(this.end-this.start===a?(n=this.start,s=this.end):(i=a-(s-n),n-=i/2,s+=i/2))}if(null!==this.options.zoomMax){var h=parseFloat(this.options.zoomMax);0>h&&(h=0),s-n>h&&(this.end-this.start===h?(n=this.start,s=this.end):(i=s-n-h,n+=i/2,s-=i/2))}var d=this.start!=n||this.end!=s;return this.start=n,this.end=s,d},h.prototype.getRange=function(){return{start:this.start,end:this.end}},h.prototype.conversion=function(t){return h.conversion(this.start,this.end,t)},h.conversion=function(t,e,i){return 0!=i&&0!=e-t?{offset:t,scale:i/(e-t)}:{offset:0,scale:1}};var Y={};h.prototype._onDragStart=function(t,e){if(!Y.pinching){Y.start=this.start,Y.end=this.end;var i=e.frame;i&&(i.style.cursor="move")}},h.prototype._onDrag=function(t,e,i){if(d(i),!Y.pinching){var n="horizontal"==i?t.gesture.deltaX:t.gesture.deltaY,s=Y.end-Y.start,o="horizontal"==i?e.width:e.height,r=-n/o*s;this._applyRange(Y.start+r,Y.end+r),this._trigger("rangechange")}},h.prototype._onDragEnd=function(t,e){Y.pinching||(e.frame&&(e.frame.style.cursor="auto"),this._trigger("rangechanged"))},h.prototype._onMouseWheel=function(t,e,i){d(i);var n=0;if(t.wheelDelta?n=t.wheelDelta/120:t.detail&&(n=-t.detail/3),n){var s;s=0>n?1-n/5:1/(1+n/5);var o=L.event.collectEventData(this,"scroll",t),r=l(o.touches[0],e.frame),a=this._pointerToDate(e,i,r);this.zoom(s,a)}A.preventDefault(t)},h.prototype._onTouch=function(){Y.start=this.start,Y.end=this.end,Y.pinching=!1,Y.center=null},h.prototype._onPinch=function(t,e,i){if(Y.pinching=!0,t.gesture.touches.length>1){Y.center||(Y.center=l(t.gesture.center,e.frame));var n=1/t.gesture.scale,s=this._pointerToDate(e,i,Y.center),o=l(t.gesture.center,e.frame);this._pointerToDate(e,i,o);var r=parseInt(s+(Y.start-s)*n),a=parseInt(s+(Y.end-s)*n);this.setRange(r,a)}},h.prototype._pointerToDate=function(t,e,i){var n;if("horizontal"==e){var s=t.width;return n=this.conversion(s),i.x/n.scale+n.offset}var o=t.height;return n=this.conversion(o),i.y/n.scale+n.offset},h.prototype.zoom=function(t,e){null==e&&(e=(this.start+this.end)/2);var i=e+(this.start-e)*t,n=e+(this.end-e)*t;this.setRange(i,n)},h.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,n=this.end+e*t;this.start=i,this.end=n},h.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,n=this.start-i,s=this.end-i;this.setRange(n,s)},p.prototype.add=function(t){if(void 0==t.id)throw Error("Component has no field id");if(!(t instanceof c||t instanceof p))throw new TypeError("Component must be an instance of prototype Component or Controller");t.controller=this,this.components[t.id]=t},p.prototype.remove=function(t){var e;for(e in this.components)if(this.components.hasOwnProperty(e)&&(e==t||this.components[e]==t))break;e&&delete this.components[e]},p.prototype.requestReflow=function(t){if(t)this.reflow();else if(!this.reflowTimer){var e=this;this.reflowTimer=setTimeout(function(){e.reflowTimer=void 0,e.reflow()},0)}},p.prototype.requestRepaint=function(t){if(t)this.repaint();else if(!this.repaintTimer){var e=this;this.repaintTimer=setTimeout(function(){e.repaintTimer=void 0,e.repaint()},0)}},p.prototype.repaint=function(){function t(n,s){s in i||(n.depends&&n.depends.forEach(function(e){t(e,e.id)}),n.parent&&t(n.parent,n.parent.id),e=n.repaint()||e,i[s]=!0)}var e=!1;this.repaintTimer&&(clearTimeout(this.repaintTimer),this.repaintTimer=void 0);var i={};A.forEach(this.components,t),e&&this.reflow()},p.prototype.reflow=function(){function t(n,s){s in i||(n.depends&&n.depends.forEach(function(e){t(e,e.id)}),n.parent&&t(n.parent,n.parent.id),e=n.reflow()||e,i[s]=!0)}var e=!1;this.reflowTimer&&(clearTimeout(this.reflowTimer),this.reflowTimer=void 0);var i={};A.forEach(this.components,t),e&&this.repaint()},c.prototype.setOptions=function(t){t&&(A.extend(this.options,t),this.controller&&(this.requestRepaint(),this.requestReflow()))},c.prototype.getOption=function(t){var e;return this.options&&(e=this.options[t]),void 0===e&&this.defaultOptions&&(e=this.defaultOptions[t]),e},c.prototype.getContainer=function(){return null},c.prototype.getFrame=function(){return this.frame},c.prototype.repaint=function(){return!1},c.prototype.reflow=function(){return!1},c.prototype.hide=function(){return this.frame&&this.frame.parentNode?(this.frame.parentNode.removeChild(this.frame),!0):!1},c.prototype.show=function(){return this.frame&&this.frame.parentNode?!1:this.repaint()},c.prototype.requestRepaint=function(){if(!this.controller)throw Error("Cannot request a repaint: no controller configured");this.controller.requestRepaint()},c.prototype.requestReflow=function(){if(!this.controller)throw Error("Cannot request a reflow: no controller configured");this.controller.requestReflow()},u.prototype=new c,u.prototype.setOptions=c.prototype.setOptions,u.prototype.getContainer=function(){return this.frame},u.prototype.repaint=function(){var t=0,e=A.updateProperty,i=A.option.asSize,n=this.options,s=this.frame;if(!s){s=document.createElement("div"),s.className="panel";var o=n.className;o&&("function"==typeof o?A.addClassName(s,o()+""):A.addClassName(s,o+"")),this.frame=s,t+=1}if(!s.parentNode){if(!this.parent)throw Error("Cannot repaint panel: no parent attached");var r=this.parent.getContainer();if(!r)throw Error("Cannot repaint panel: parent has no container element");r.appendChild(s),t+=1}return t+=e(s.style,"top",i(n.top,"0px")),t+=e(s.style,"left",i(n.left,"0px")),t+=e(s.style,"width",i(n.width,"100%")),t+=e(s.style,"height",i(n.height,"100%")),t>0},u.prototype.reflow=function(){var t=0,e=A.updateProperty,i=this.frame;return i?(t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft),t+=e(this,"width",i.offsetWidth),t+=e(this,"height",i.offsetHeight)):t+=1,t>0},f.prototype=new u,f.prototype.setOptions=c.prototype.setOptions,f.prototype.repaint=function(){var t=0,e=A.updateProperty,i=A.option.asSize,n=this.options,s=this.frame;if(s||(s=document.createElement("div"),this.frame=s,t+=1),!s.parentNode){if(!this.container)throw Error("Cannot repaint root panel: no container attached");this.container.appendChild(s),t+=1}s.className="vis timeline rootpanel "+n.orientation;var o=n.className;return o&&A.addClassName(s,A.option.asString(o)),t+=e(s.style,"top",i(n.top,"0px")),t+=e(s.style,"left",i(n.left,"0px")),t+=e(s.style,"width",i(n.width,"100%")),t+=e(s.style,"height",i(n.height,"100%")),this._updateEventEmitters(),this._updateWatch(),t>0},f.prototype.reflow=function(){var t=0,e=A.updateProperty,i=this.frame;return i?(t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft),t+=e(this,"width",i.offsetWidth),t+=e(this,"height",i.offsetHeight)):t+=1,t>0},f.prototype._updateWatch=function(){var t=this.getOption("autoResize");t?this._watch():this._unwatch()},f.prototype._watch=function(){var t=this;this._unwatch();var e=function(){var e=t.getOption("autoResize");return e?(t.frame&&(t.frame.clientWidth!=t.width||t.frame.clientHeight!=t.height)&&t.requestReflow(),void 0):(t._unwatch(),void 0)};A.addEventListener(window,"resize",e),this.watchTimer=setInterval(e,1e3)},f.prototype._unwatch=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0)},f.prototype.on=function(t,e){var i=this.listeners[t];i||(i=[],this.listeners[t]=i),i.push(e),this._updateEventEmitters()},f.prototype._updateEventEmitters=function(){if(this.listeners){var t=this;A.forEach(this.listeners,function(e,i){if(t.emitters||(t.emitters={}),!(i in t.emitters)){var n=t.frame;if(n){var s=function(t){e.forEach(function(e){e(t)})};t.emitters[i]=s,t.hammer||(t.hammer=L(n,{prevent_default:!0})),t.hammer.on(i,s)}}})}},m.prototype=new c,m.prototype.setOptions=c.prototype.setOptions,m.prototype.setRange=function(t){if(!(t instanceof h||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},m.prototype.toTime=function(t){var e=this.conversion;return new Date(t/e.scale+e.offset)},m.prototype.toScreen=function(t){var e=this.conversion;return(t.valueOf()-e.offset)*e.scale},m.prototype.repaint=function(){var t=0,e=A.updateProperty,i=A.option.asSize,n=this.options,s=this.getOption("orientation"),o=this.props,r=this.step,a=this.frame;if(a||(a=document.createElement("div"),this.frame=a,t+=1),a.className="axis",!a.parentNode){if(!this.parent)throw Error("Cannot repaint time axis: no parent attached");var h=this.parent.getContainer();if(!h)throw Error("Cannot repaint time axis: parent has no container element");h.appendChild(a),t+=1}var d=a.parentNode;if(d){var l=a.nextSibling;d.removeChild(a);var p="bottom"==s&&this.props.parentHeight&&this.height?this.props.parentHeight-this.height+"px":"0px";if(t+=e(a.style,"top",i(n.top,p)),t+=e(a.style,"left",i(n.left,"0px")),t+=e(a.style,"width",i(n.width,"100%")),t+=e(a.style,"height",i(n.height,this.height+"px")),this._repaintMeasureChars(),this.step){this._repaintStart(),r.first();for(var c=void 0,u=0;r.hasNext()&&1e3>u;){u++;var f=r.getCurrent(),m=this.toScreen(f),g=r.isMajor();this.getOption("showMinorLabels")&&this._repaintMinorText(m,r.getLabelMinor()),g&&this.getOption("showMajorLabels")?(m>0&&(void 0==c&&(c=m),this._repaintMajorText(m,r.getLabelMajor())),this._repaintMajorLine(m)):this._repaintMinorLine(m),r.next()}if(this.getOption("showMajorLabels")){var v=this.toTime(0),y=r.getLabelMajor(v),b=y.length*(o.majorCharWidth||10)+10;(void 0==c||c>b)&&this._repaintMajorText(0,y)}this._repaintEnd()}this._repaintLine(),l?d.insertBefore(a,l):d.appendChild(a)}return t>0},m.prototype._repaintStart=function(){var t=this.dom,e=t.redundant;e.majorLines=t.majorLines,e.majorTexts=t.majorTexts,e.minorLines=t.minorLines,e.minorTexts=t.minorTexts,t.majorLines=[],t.majorTexts=[],t.minorLines=[],t.minorTexts=[]},m.prototype._repaintEnd=function(){A.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},m.prototype._repaintMinorText=function(t,e){var i=this.dom.redundant.minorTexts.shift();if(!i){var n=document.createTextNode("");i=document.createElement("div"),i.appendChild(n),i.className="text minor",this.frame.appendChild(i)}this.dom.minorTexts.push(i),i.childNodes[0].nodeValue=e,i.style.left=t+"px",i.style.top=this.props.minorLabelTop+"px"},m.prototype._repaintMajorText=function(t,e){var i=this.dom.redundant.majorTexts.shift();if(!i){var n=document.createTextNode(e);i=document.createElement("div"),i.className="text major",i.appendChild(n),this.frame.appendChild(i)}this.dom.majorTexts.push(i),i.childNodes[0].nodeValue=e,i.style.top=this.props.majorLabelTop+"px",i.style.left=t+"px"},m.prototype._repaintMinorLine=function(t){var e=this.dom.redundant.minorLines.shift();e||(e=document.createElement("div"),e.className="grid vertical minor",this.frame.appendChild(e)),this.dom.minorLines.push(e);var i=this.props;e.style.top=i.minorLineTop+"px",e.style.height=i.minorLineHeight+"px",e.style.left=t-i.minorLineWidth/2+"px"},m.prototype._repaintMajorLine=function(t){var e=this.dom.redundant.majorLines.shift();e||(e=document.createElement("DIV"),e.className="grid vertical major",this.frame.appendChild(e)),this.dom.majorLines.push(e);var i=this.props;e.style.top=i.majorLineTop+"px",e.style.left=t-i.majorLineWidth/2+"px",e.style.height=i.majorLineHeight+"px"},m.prototype._repaintLine=function(){var t=this.dom.line,e=this.frame;this.options,this.getOption("showMinorLabels")||this.getOption("showMajorLabels")?(t?(e.removeChild(t),e.appendChild(t)):(t=document.createElement("div"),t.className="grid horizontal major",e.appendChild(t),this.dom.line=t),t.style.top=this.props.lineTop+"px"):t&&t.parentElement&&(e.removeChild(t.line),delete this.dom.line)},m.prototype._repaintMeasureChars=function(){var t,e=this.dom;if(!e.measureCharMinor){t=document.createTextNode("0");var i=document.createElement("DIV");i.className="text minor measure",i.appendChild(t),this.frame.appendChild(i),e.measureCharMinor=i}if(!e.measureCharMajor){t=document.createTextNode("0");var n=document.createElement("DIV");n.className="text major measure",n.appendChild(t),this.frame.appendChild(n),e.measureCharMajor=n}},m.prototype.reflow=function(){var t=0,e=A.updateProperty,i=this.frame,n=this.range;if(!n)throw Error("Cannot repaint time axis: no range configured");if(i){t+=e(this,"top",i.offsetTop),t+=e(this,"left",i.offsetLeft);var s=this.props,o=this.getOption("showMinorLabels"),r=this.getOption("showMajorLabels"),a=this.dom.measureCharMinor,h=this.dom.measureCharMajor;a&&(s.minorCharHeight=a.clientHeight,s.minorCharWidth=a.clientWidth),h&&(s.majorCharHeight=h.clientHeight,s.majorCharWidth=h.clientWidth);var d=i.parentNode?i.parentNode.offsetHeight:0;switch(d!=s.parentHeight&&(s.parentHeight=d,t+=1),this.getOption("orientation")){case"bottom":s.minorLabelHeight=o?s.minorCharHeight:0,s.majorLabelHeight=r?s.majorCharHeight:0,s.minorLabelTop=0,s.majorLabelTop=s.minorLabelTop+s.minorLabelHeight,s.minorLineTop=-this.top,s.minorLineHeight=Math.max(this.top+s.majorLabelHeight,0),s.minorLineWidth=1,s.majorLineTop=-this.top,s.majorLineHeight=Math.max(this.top+s.minorLabelHeight+s.majorLabelHeight,0),s.majorLineWidth=1,s.lineTop=0;break;case"top":s.minorLabelHeight=o?s.minorCharHeight:0,s.majorLabelHeight=r?s.majorCharHeight:0,s.majorLabelTop=0,s.minorLabelTop=s.majorLabelTop+s.majorLabelHeight,s.minorLineTop=s.minorLabelTop,s.minorLineHeight=Math.max(d-s.majorLabelHeight-this.top),s.minorLineWidth=1,s.majorLineTop=0,s.majorLineHeight=Math.max(d-this.top),s.majorLineWidth=1,s.lineTop=s.majorLabelHeight+s.minorLabelHeight;break;default:throw Error('Unkown orientation "'+this.getOption("orientation")+'"')}var l=s.minorLabelHeight+s.majorLabelHeight;t+=e(this,"width",i.offsetWidth),t+=e(this,"height",l),this._updateConversion();var p=A.convert(n.start,"Number"),c=A.convert(n.end,"Number"),u=this.toTime(5*(s.minorCharWidth||10)).valueOf()-this.toTime(0).valueOf();this.step=new TimeStep(new Date(p),new Date(c),u),t+=e(s.range,"start",p),t+=e(s.range,"end",c),t+=e(s.range,"minimumStep",u.valueOf())}return t>0},m.prototype._updateConversion=function(){var t=this.range;if(!t)throw Error("No range configured");this.conversion=t.conversion?t.conversion(this.width):h.conversion(t.start,t.end,this.width)},g.prototype=new c,g.prototype.setOptions=c.prototype.setOptions,g.prototype.getContainer=function(){return this.frame},g.prototype.repaint=function(){var t=this.frame,e=this.parent,i=e.parent.getContainer();if(!e)throw Error("Cannot repaint bar: no parent attached");if(!i)throw Error("Cannot repaint bar: parent has no container element");if(!this.getOption("showCurrentTime"))return t&&(i.removeChild(t),delete this.frame),void 0;t||(t=document.createElement("div"),t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",i.appendChild(t),this.frame=t),e.conversion||e._updateConversion();var n=new Date,s=e.toScreen(n);t.style.left=s+"px",t.title="Current time: "+n,void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer);var o=this,r=1/e.conversion.scale/2;return 30>r&&(r=30),this.currentTimeTimer=setTimeout(function(){o.repaint()},r),!1},v.prototype=new c,v.prototype.setOptions=c.prototype.setOptions,v.prototype.getContainer=function(){return this.frame},v.prototype.repaint=function(){var t=this.frame,e=this.parent,i=e.parent.getContainer();if(!e)throw Error("Cannot repaint bar: no parent attached");if(!i)throw Error("Cannot repaint bar: parent has no container element");if(!this.getOption("showCustomTime"))return t&&(i.removeChild(t),delete this.frame),void 0;if(!t){t=document.createElement("div"),t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",i.appendChild(t);var n=document.createElement("div");n.style.position="relative",n.style.top="0px",n.style.left="-10px",n.style.height="100%",n.style.width="20px",t.appendChild(n),this.frame=t,this.subscribe(this,"movetime")}e.conversion||e._updateConversion();var s=e.toScreen(this.customTime);return t.style.left=s+"px",t.title="Time: "+this.customTime,!1},v.prototype._setCustomTime=function(t){this.customTime=new Date(t.valueOf()),this.repaint()},v.prototype._getCustomTime=function(){return new Date(this.customTime.valueOf())},v.prototype.subscribe=function(t,e){var i=this,n={component:t,event:e,callback:function(t){i._onMouseDown(t,n)},params:{}};t.on("mousedown",n.callback),i.listeners.push(n)},v.prototype.on=function(t,e){var i=this.frame;if(!i)throw Error("Cannot add event listener: no parent attached");F.addListener(this,t,e),A.addEventListener(i,t,e)},v.prototype._onMouseDown=function(t,e){t=t||window.event;var i=e.params,n=t.which?1==t.which:1==t.button;if(n){i.mouseX=A.getPageX(t),i.moved=!1,i.customTime=this.customTime;var s=this;i.onMouseMove||(i.onMouseMove=function(t){s._onMouseMove(t,e)},A.addEventListener(document,"mousemove",i.onMouseMove)),i.onMouseUp||(i.onMouseUp=function(t){s._onMouseUp(t,e)},A.addEventListener(document,"mouseup",i.onMouseUp)),A.stopPropagation(t),A.preventDefault(t)}},v.prototype._onMouseMove=function(t,e){t=t||window.event;var i=e.params,n=this.parent,s=A.getPageX(t);void 0===i.mouseX&&(i.mouseX=s);var o=s-i.mouseX;Math.abs(o)>=1&&(i.moved=!0);var r=n.toScreen(i.customTime),a=r+o,h=n.toTime(a);this._setCustomTime(h),F.trigger(this,"timechange",{customTime:this.customTime}),A.preventDefault(t)},v.prototype._onMouseUp=function(t,e){t=t||window.event;var i=e.params;i.onMouseMove&&(A.removeEventListener(document,"mousemove",i.onMouseMove),i.onMouseMove=null),i.onMouseUp&&(A.removeEventListener(document,"mouseup",i.onMouseUp),i.onMouseUp=null),i.moved&&F.trigger(this,"timechanged",{customTime:this.customTime})},y.prototype=new u,y.types={box:w,range:E,rangeoverflow:T,point:_},y.prototype.setOptions=c.prototype.setOptions,y.prototype.setRange=function(t){if(!(t instanceof h||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},y.prototype.repaint=function(){var t=0,e=A.updateProperty,i=A.option.asSize,n=this.options,s=this.getOption("orientation"),o=this.defaultOptions,r=this.frame;if(!r){r=document.createElement("div"),r.className="itemset";var a=n.className;a&&A.addClassName(r,A.option.asString(a));var h=document.createElement("div");h.className="background",r.appendChild(h),this.dom.background=h;var d=document.createElement("div");d.className="foreground",r.appendChild(d),this.dom.foreground=d;var l=document.createElement("div");l.className="itemset-axis",this.dom.axis=l,this.frame=r,t+=1}if(!this.parent)throw Error("Cannot repaint itemset: no parent attached");var p=this.parent.getContainer();if(!p)throw Error("Cannot repaint itemset: parent has no container element");r.parentNode||(p.appendChild(r),t+=1),this.dom.axis.parentNode||(p.appendChild(this.dom.axis),t+=1),t+=e(r.style,"left",i(n.left,"0px")),t+=e(r.style,"top",i(n.top,"0px")),t+=e(r.style,"width",i(n.width,"100%")),t+=e(r.style,"height",i(n.height,this.height+"px")),t+=e(this.dom.axis.style,"left",i(n.left,"0px")),t+=e(this.dom.axis.style,"width",i(n.width,"100%")),t+="bottom"==s?e(this.dom.axis.style,"top",this.height+this.top+"px"):e(this.dom.axis.style,"top",this.top+"px"),this._updateConversion();var c=this,u=this.queue,f=this.itemsData,m=this.items,g={};return Object.keys(u).forEach(function(e){var i=u[e],s=m[e];switch(i){case"add":case"update":var r=f&&f.get(e,g);if(r){var a=r.type||r.start&&r.end&&"range"||n.type||"box",h=y.types[a];if(s&&(h&&s instanceof h?(s.data=r,t++):(t+=s.hide(),s=null)),!s){if(!h)throw new TypeError('Unknown item type "'+a+'"');s=new h(c,r,n,o),t++}s.repaint(),m[e]=s}delete u[e];break;case"remove":s&&(t+=s.hide()),delete m[e],delete u[e];break;default:console.log('Error: unknown action "'+i+'"')}}),A.forEach(this.items,function(e){e.visible?(t+=e.show(),e.reposition()):t+=e.hide()}),t>0},y.prototype.getForeground=function(){return this.dom.foreground},y.prototype.getBackground=function(){return this.dom.background},y.prototype.getAxis=function(){return this.dom.axis},y.prototype.reflow=function(){var t=0,e=this.options,i=e.margin&&e.margin.axis||this.defaultOptions.margin.axis,n=e.margin&&e.margin.item||this.defaultOptions.margin.item,s=A.updateProperty,o=A.option.asNumber,r=A.option.asSize,a=this.frame;if(a){this._updateConversion(),A.forEach(this.items,function(e){t+=e.reflow()}),this.stack.update();var h,d=o(e.maxHeight),l=null!=r(e.height);if(l)h=a.offsetHeight;else{var p=this.stack.ordered;if(p.length){var c=p[0].top,u=p[0].top+p[0].height;A.forEach(p,function(t){c=Math.min(c,t.top),u=Math.max(u,t.top+t.height)}),h=u-c+i+n}else h=i+n}null!=d&&(h=Math.min(h,d)),t+=s(this,"height",h),t+=s(this,"top",a.offsetTop),t+=s(this,"left",a.offsetLeft),t+=s(this,"width",a.offsetWidth)}else t+=1;return t>0},y.prototype.hide=function(){var t=!1;return this.frame&&this.frame.parentNode&&(this.frame.parentNode.removeChild(this.frame),t=!0),this.dom.axis&&this.dom.axis.parentNode&&(this.dom.axis.parentNode.removeChild(this.dom.axis),t=!0),t},y.prototype.setItems=function(t){var e,i=this,n=this.itemsData;if(t){if(!(t instanceof o||t instanceof r))throw new TypeError("Data must be an instance of DataSet");this.itemsData=t}else this.itemsData=null;if(n&&(A.forEach(this.listeners,function(t,e){n.unsubscribe(e,t)}),e=n.getIds(),this._onRemove(e)),this.itemsData){var s=this.id;A.forEach(this.listeners,function(t,e){i.itemsData.subscribe(e,t,s)}),e=this.itemsData.getIds(),this._onAdd(e)}},y.prototype.getItems=function(){return this.itemsData},y.prototype._onUpdate=function(t){this._toQueue("update",t)},y.prototype._onAdd=function(t){this._toQueue("add",t)},y.prototype._onRemove=function(t){this._toQueue("remove",t)},y.prototype._toQueue=function(t,e){var i=this.queue;e.forEach(function(e){i[e]=t}),this.controller&&this.requestRepaint()},y.prototype._updateConversion=function(){var t=this.range;if(!t)throw Error("No range configured");this.conversion=t.conversion?t.conversion(this.width):h.conversion(t.start,t.end,this.width)},y.prototype.toTime=function(t){var e=this.conversion;return new Date(t/e.scale+e.offset)},y.prototype.toScreen=function(t){var e=this.conversion;return(t.valueOf()-e.offset)*e.scale},b.prototype.select=function(){this.selected=!0},b.prototype.unselect=function(){this.selected=!1},b.prototype.show=function(){return!1},b.prototype.hide=function(){return!1},b.prototype.repaint=function(){return!1},b.prototype.reflow=function(){return!1},b.prototype.getWidth=function(){return this.width},w.prototype=new b(null,null),w.prototype.select=function(){this.selected=!0},w.prototype.unselect=function(){this.selected=!1},w.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw Error("Cannot repaint item: no parent attached");if(!e.box.parentNode){var i=this.parent.getForeground();if(!i)throw Error("Cannot repaint time axis: parent has no foreground container element");i.appendChild(e.box),t=!0}if(!e.line.parentNode){var n=this.parent.getBackground();if(!n)throw Error("Cannot repaint time axis: parent has no background container element");n.appendChild(e.line),t=!0}if(!e.dot.parentNode){var s=this.parent.getAxis();if(!n)throw Error("Cannot repaint time axis: parent has no axis container element");s.appendChild(e.dot),t=!0}if(this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var o=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");this.className!=o&&(this.className=o,e.box.className="item box"+o,e.line.className="item line"+o,e.dot.className="item dot"+o,t=!0)}return t},w.prototype.show=function(){return this.dom&&this.dom.box.parentNode?!1:this.repaint()},w.prototype.hide=function(){var t=!1,e=this.dom;return e&&(e.box.parentNode&&(e.box.parentNode.removeChild(e.box),t=!0),e.line.parentNode&&e.line.parentNode.removeChild(e.line),e.dot.parentNode&&e.dot.parentNode.removeChild(e.dot)),t},w.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l,p,c=0;if(void 0==this.data.start)throw Error('Property "start" missing in item '+this.data.id);if(l=this.data,p=this.parent&&this.parent.range,l&&p){var u=p.end-p.start;this.visible=l.start>p.start-u&&l.start0},w.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.box=document.createElement("DIV"),t.content=document.createElement("DIV"),t.content.className="content",t.box.appendChild(t.content),t.line=document.createElement("DIV"),t.line.className="line",t.dot=document.createElement("DIV"),t.dot.className="dot")},w.prototype.reposition=function(){var t=this.dom,e=this.props,i=this.options.orientation||this.defaultOptions.orientation;if(t){var n=t.box,s=t.line,o=t.dot;n.style.left=this.left+"px",n.style.top=this.top+"px",s.style.left=e.line.left+"px","top"==i?(s.style.top="0px",s.style.height=this.top+"px"):(s.style.top=this.top+this.height+"px",s.style.height=Math.max(this.parent.height-this.top-this.height+this.props.dot.height/2,0)+"px"),o.style.left=e.dot.left+"px",o.style.top=e.dot.top+"px"}},_.prototype=new b(null,null),_.prototype.select=function(){this.selected=!0},_.prototype.unselect=function(){this.selected=!1},_.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw Error("Cannot repaint item: no parent attached");
+var i=this.parent.getForeground();if(!i)throw Error("Cannot repaint time axis: parent has no foreground container element");if(e.point.parentNode||(i.appendChild(e.point),i.appendChild(e.point),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");this.className!=n&&(this.className=n,e.point.className="item point"+n,t=!0)}return t},_.prototype.show=function(){return this.dom&&this.dom.point.parentNode?!1:this.repaint()},_.prototype.hide=function(){var t=!1,e=this.dom;return e&&e.point.parentNode&&(e.point.parentNode.removeChild(e.point),t=!0),t},_.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l=0;if(void 0==this.data.start)throw Error('Property "start" missing in item '+this.data.id);if(h=this.data,d=this.parent&&this.parent.range,h&&d){var p=d.end-d.start;this.visible=h.start>d.start-p&&h.start0},_.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.point=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.point.appendChild(t.content),t.dot=document.createElement("div"),t.dot.className="dot",t.point.appendChild(t.dot))},_.prototype.reposition=function(){var t=this.dom,e=this.props;t&&(t.point.style.top=this.top+"px",t.point.style.left=this.left+"px",t.content.style.marginLeft=e.content.marginLeft+"px",t.dot.style.top=e.dot.top+"px")},E.prototype=new b(null,null),E.prototype.select=function(){this.selected=!0},E.prototype.unselect=function(){this.selected=!1},E.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw Error("Cannot repaint time axis: parent has no foreground container element");if(e.box.parentNode||(i.appendChild(e.box),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=this.data.className?" "+this.data.className:"";this.className!=n&&(this.className=n,e.box.className="item range"+n,t=!0)}return t},E.prototype.show=function(){return this.dom&&this.dom.box.parentNode?!1:this.repaint()},E.prototype.hide=function(){var t=!1,e=this.dom;return e&&e.box.parentNode&&(e.box.parentNode.removeChild(e.box),t=!0),t},E.prototype.reflow=function(){var t,e,i,n,s,o,r,a,h,d,l,p,c,u,f,m,g=0;if(void 0==this.data.start)throw Error('Property "start" missing in item '+this.data.id);if(void 0==this.data.end)throw Error('Property "end" missing in item '+this.data.id);return h=this.data,d=this.parent&&this.parent.range,this.visible=h&&d?h.startd.start:!1,this.visible&&(t=this.dom,t?(e=this.props,i=this.options,o=this.parent,r=o.toScreen(this.data.start),a=o.toScreen(this.data.end),l=A.updateProperty,p=t.box,c=o.width,f=i.orientation||this.defaultOptions.orientation,n=i.margin&&i.margin.axis||this.defaultOptions.margin.axis,s=i.padding||this.defaultOptions.padding,g+=l(e.content,"width",t.content.offsetWidth),g+=l(this,"height",p.offsetHeight),-c>r&&(r=-c),a>2*c&&(a=2*c),u=0>r?Math.min(-r,a-r-e.content.width-2*s):0,g+=l(e.content,"left",u),"top"==f?(m=n,g+=l(this,"top",m)):(m=o.height-this.height-n,g+=l(this,"top",m)),g+=l(this,"left",r),g+=l(this,"width",Math.max(a-r,1))):g+=1),g>0},E.prototype._create=function(){var t=this.dom;t||(this.dom=t={},t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content))},E.prototype.reposition=function(){var t=this.dom,e=this.props;t&&(t.box.style.top=this.top+"px",t.box.style.left=this.left+"px",t.box.style.width=this.width+"px",t.content.style.left=e.content.left+"px")},T.prototype=new E(null,null),T.prototype.repaint=function(){var t=!1,e=this.dom;if(e||(this._create(),e=this.dom,t=!0),e){if(!this.parent)throw Error("Cannot repaint item: no parent attached");var i=this.parent.getForeground();if(!i)throw Error("Cannot repaint time axis: parent has no foreground container element");if(e.box.parentNode||(i.appendChild(e.box),t=!0),this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)e.content.innerHTML="",e.content.appendChild(this.content);else{if(void 0==this.data.content)throw Error('Property "content" missing in item '+this.data.id);e.content.innerHTML=this.content}t=!0}var n=this.data.className?" "+this.data.className:"";this.className!=n&&(this.className=n,e.box.className="item rangeoverflow"+n,t=!0)}return t},T.prototype.getWidth=function(){return void 0!==this.props.content&&this.width0},S.prototype=new u,S.prototype.setOptions=c.prototype.setOptions,S.prototype.setRange=function(){},S.prototype.setItems=function(t){this.itemsData=t;for(var e in this.groups)if(this.groups.hasOwnProperty(e)){var i=this.groups[e];i.setItems(t)}},S.prototype.getItems=function(){return this.itemsData},S.prototype.setRange=function(t){this.range=t},S.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(A.forEach(this.listeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this._onRemove(e)),t?t instanceof o?this.groupsData=t:(this.groupsData=new o({convert:{start:"Date",end:"Date"}}),this.groupsData.add(t)):this.groupsData=null,this.groupsData){var n=this.id;A.forEach(this.listeners,function(t,e){i.groupsData.subscribe(e,t,n)}),e=this.groupsData.getIds(),this._onAdd(e)}},S.prototype.getGroups=function(){return this.groupsData},S.prototype.repaint=function(){var t,e,i,n,s=0,o=A.updateProperty,r=A.option.asSize,a=A.option.asElement,h=this.options,d=this.dom.frame,l=this.dom.labels,p=this.dom.labelSet;if(!this.parent)throw Error("Cannot repaint groupset: no parent attached");var c=this.parent.getContainer();if(!c)throw Error("Cannot repaint groupset: parent has no container element");if(!d){d=document.createElement("div"),d.className="groupset",this.dom.frame=d;var u=h.className;u&&A.addClassName(d,A.option.asString(u)),s+=1}d.parentNode||(c.appendChild(d),s+=1);var f=a(h.labelContainer);if(!f)throw Error('Cannot repaint groupset: option "labelContainer" not defined');l||(l=document.createElement("div"),l.className="labels",this.dom.labels=l),p||(p=document.createElement("div"),p.className="label-set",l.appendChild(p),this.dom.labelSet=p),l.parentNode&&l.parentNode==f||(l.parentNode&&l.parentNode.removeChild(l.parentNode),f.appendChild(l)),s+=o(d.style,"height",r(h.height,this.height+"px")),s+=o(d.style,"top",r(h.top,"0px")),s+=o(d.style,"left",r(h.left,"0px")),s+=o(d.style,"width",r(h.width,"100%")),s+=o(p.style,"top",r(h.top,"0px")),s+=o(p.style,"height",r(h.height,this.height+"px"));var m=this,g=this.queue,v=this.groups,y=this.groupsData,b=Object.keys(g);if(b.length){b.forEach(function(t){var e=g[t],i=v[t];switch(e){case"add":case"update":if(!i){var n=Object.create(m.options);A.extend(n,{height:null,maxHeight:null}),i=new x(m,t,n),i.setItems(m.itemsData),v[t]=i,m.controller.add(i)}i.data=y.get(t),delete g[t];break;case"remove":i&&(i.setItems(),delete v[t],m.controller.remove(i)),delete g[t];break;default:console.log('Error: unknown action "'+e+'"')}});var w=this.groupsData.getIds({order:this.options.groupOrder});for(t=0;w.length>t;t++)(function(t,e){var i=0;e&&(i=function(){return e.top+e.height}),t.setOptions({top:i})})(v[w[t]],v[w[t-1]]);for(;p.firstChild;)p.removeChild(p.firstChild);for(t=0;w.length>t;t++)e=w[t],n=this._createLabel(e),p.appendChild(n);s++}for(e in v)v.hasOwnProperty(e)&&(i=v[e],n=i.label,n&&(n.style.top=i.top+"px",n.style.height=i.height+"px"));return s>0},S.prototype._createLabel=function(t){var e=this.groups[t],i=document.createElement("div");i.className="label";var n=document.createElement("div");n.className="inner",i.appendChild(n);var s=e.data&&e.data.content;s instanceof Element?n.appendChild(s):void 0!=s&&(n.innerHTML=s);var o=e.data&&e.data.className;return o&&A.addClassName(i,o),e.label=i,i},S.prototype.getContainer=function(){return this.dom.frame},S.prototype.getLabelsWidth=function(){return this.props.labels.width},S.prototype.reflow=function(){var t,e,i=0,n=this.options,s=A.updateProperty,o=A.option.asNumber,r=A.option.asSize,a=this.dom.frame;if(a){var h,d=o(n.maxHeight),l=null!=r(n.height);if(l)h=a.offsetHeight;else{h=0;for(t in this.groups)this.groups.hasOwnProperty(t)&&(e=this.groups[t],h+=e.height)}null!=d&&(h=Math.min(h,d)),i+=s(this,"height",h),i+=s(this,"top",a.offsetTop),i+=s(this,"left",a.offsetLeft),i+=s(this,"width",a.offsetWidth)}var p=0;for(t in this.groups)if(this.groups.hasOwnProperty(t)){e=this.groups[t];var c=e.props&&e.props.label&&e.props.label.width||0;p=Math.max(p,c)}return i+=s(this.props.labels,"width",p),i>0},S.prototype.hide=function(){return this.dom.frame&&this.dom.frame.parentNode?(this.dom.frame.parentNode.removeChild(this.dom.frame),!0):!1},S.prototype.show=function(){return this.dom.frame&&this.dom.frame.parentNode?!1:this.repaint()},S.prototype._onUpdate=function(t){this._toQueue(t,"update")},S.prototype._onAdd=function(t){this._toQueue(t,"add")},S.prototype._onRemove=function(t){this._toQueue(t,"remove")},S.prototype._toQueue=function(t,e){var i=this.queue;t.forEach(function(t){i[t]=e}),this.controller&&this.requestRepaint()},D.prototype.setOptions=function(t){A.extend(this.options,t),this.range.setRange(),this.controller.reflow(),this.controller.repaint()},D.prototype.setCustomTime=function(t){this.customtime._setCustomTime(t)},D.prototype.getCustomTime=function(){return new Date(this.customtime.customTime.valueOf())},D.prototype.setItems=function(t){var e,i=null==this.itemsData;if(t?t instanceof o&&(e=t):e=null,t instanceof o||(e=new o({convert:{start:"Date",end:"Date"}}),e.add(t)),this.itemsData=e,this.content.setItems(e),i&&(void 0==this.options.start||void 0==this.options.end)){var n=this.getItemRange(),s=n.min,r=n.max;if(null!=s&&null!=r){var a=r.valueOf()-s.valueOf();0>=a&&(a=864e5),s=new Date(s.valueOf()-.05*a),r=new Date(r.valueOf()+.05*a)}void 0!=this.options.start&&(s=A.convert(this.options.start,"Date")),void 0!=this.options.end&&(r=A.convert(this.options.end,"Date")),(null!=s||null!=r)&&this.range.setRange(s,r)}},D.prototype.setGroups=function(t){var e=this;this.groupsData=t;var i=this.groupsData?S:y;if(!(this.content instanceof i)){this.content&&(this.content.hide(),this.content.setItems&&this.content.setItems(),this.content.setGroups&&this.content.setGroups(),this.controller.remove(this.content));var n=Object.create(this.options);A.extend(n,{top:function(){return"top"==e.options.orientation?e.timeaxis.height:e.itemPanel.height-e.timeaxis.height-e.content.height},left:null,width:"100%",height:function(){return e.options.height?e.itemPanel.height-e.timeaxis.height:null},maxHeight:function(){if(e.options.maxHeight){if(!A.isNumber(e.options.maxHeight))throw new TypeError("Number expected for property maxHeight");return e.options.maxHeight-e.timeaxis.height}return null},labelContainer:function(){return e.labelPanel.getContainer()}}),this.content=new i(this.itemPanel,[this.timeaxis],n),this.content.setRange&&this.content.setRange(this.range),this.content.setItems&&this.content.setItems(this.itemsData),this.content.setGroups&&this.content.setGroups(this.groupsData),this.controller.add(this.content)}},D.prototype.getItemRange=function(){var t=this.itemsData,e=null,i=null;if(t){var n=t.min("start");e=n?n.start.valueOf():null;var s=t.max("start");s&&(i=s.start.valueOf());var o=t.max("end");o&&(i=null==i?o.end.valueOf():Math.max(i,o.end.valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},function(t){function e(t){return D=t,c()}function i(){M=0,C=D.charAt(0)}function n(){M++,C=D.charAt(M)}function s(){return D.charAt(M+1)}function o(t){return L.test(t)}function r(t,e){if(t||(t={}),e)for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function a(t,e,i){for(var n=e.split("."),s=t;n.length;){var o=n.shift();n.length?(s[o]||(s[o]={}),s=s[o]):s[o]=i}}function h(t,e){for(var i,n,s=null,o=[t],a=t;a.parent;)o.push(a.parent),a=a.parent;if(a.nodes)for(i=0,n=a.nodes.length;n>i;i++)if(e.id===a.nodes[i].id){s=a.nodes[i];break}for(s||(s={id:e.id},t.node&&(s.attr=r(s.attr,t.node))),i=o.length-1;i>=0;i--){var h=o[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(s)&&h.nodes.push(s)}e.attr&&(s.attr=r(s.attr,e.attr))}function d(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=r({},t.edge);e.attr=r(i,e.attr)}}function l(t,e,i,n,s){var o={from:e,to:i,type:n};return t.edge&&(o.attr=r({},t.edge)),o.attr=r(o.attr||{},s),o}function p(){for(N=x.NULL,O="";" "==C||" "==C||"\n"==C||"\r"==C;)n();do{var t=!1;if("#"==C){for(var e=M-1;" "==D.charAt(e)||" "==D.charAt(e);)e--;if("\n"==D.charAt(e)||""==D.charAt(e)){for(;""!=C&&"\n"!=C;)n();t=!0}}if("/"==C&&"/"==s()){for(;""!=C&&"\n"!=C;)n();t=!0}if("/"==C&&"*"==s()){for(;""!=C;){if("*"==C&&"/"==s()){n(),n();break}n()}t=!0}for(;" "==C||" "==C||"\n"==C||"\r"==C;)n()}while(t);if(""==C)return N=x.DELIMITER,void 0;var i=C+s();if(S[i])return N=x.DELIMITER,O=i,n(),n(),void 0;if(S[C])return N=x.DELIMITER,O=C,n(),void 0;if(o(C)||"-"==C){for(O+=C,n();o(C);)O+=C,n();return"false"==O?O=!1:"true"==O?O=!0:isNaN(Number(O))||(O=Number(O)),N=x.IDENTIFIER,void 0}if('"'==C){for(n();""!=C&&('"'!=C||'"'==C&&'"'==s());)O+=C,'"'==C&&n(),n();if('"'!=C)throw w('End of string " expected');return n(),N=x.IDENTIFIER,void 0}for(N=x.UNKNOWN;""!=C;)O+=C,n();throw new SyntaxError('Syntax error in part "'+_(O,30)+'"')}function c(){var t={};if(i(),p(),"strict"==O&&(t.strict=!0,p()),("graph"==O||"digraph"==O)&&(t.type=O,p()),N==x.IDENTIFIER&&(t.id=O,p()),"{"!=O)throw w("Angle bracket { expected");if(p(),u(t),"}"!=O)throw w("Angle bracket } expected");if(p(),""!==O)throw w("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function u(t){for(;""!==O&&"}"!=O;)f(t),";"==O&&p()}function f(t){var e=m(t);if(e)return y(t,e),void 0;var i=g(t);if(!i){if(N!=x.IDENTIFIER)throw w("Identifier expected");var n=O;if(p(),"="==O){if(p(),N!=x.IDENTIFIER)throw w("Identifier expected");t[n]=O,p()}else v(t,n)}}function m(t){var e=null;if("subgraph"==O&&(e={},e.type="subgraph",p(),N==x.IDENTIFIER&&(e.id=O,p())),"{"==O){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,u(e),"}"!=O)throw w("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function g(t){return"node"==O?(p(),t.node=b(),"node"):"edge"==O?(p(),t.edge=b(),"edge"):"graph"==O?(p(),t.graph=b(),"graph"):null}function v(t,e){var i={id:e},n=b();n&&(i.attr=n),h(t,i),y(t,e)}function y(t,e){for(;"->"==O||"--"==O;){var i,n=O;p();var s=m(t);if(s)i=s;else{if(N!=x.IDENTIFIER)throw w("Identifier or subgraph expected");i=O,h(t,{id:i}),p()}var o=b(),r=l(t,e,i,n,o);d(t,r),e=i}}function b(){for(var t=null;"["==O;){for(p(),t={};""!==O&&"]"!=O;){if(N!=x.IDENTIFIER)throw w("Attribute name expected");var e=O;if(p(),"="!=O)throw w("Equal sign = expected");if(p(),N!=x.IDENTIFIER)throw w("Attribute value expected");var i=O;a(t,e,i),p(),","==O&&p()}if("]"!=O)throw w("Bracket ] expected");p()}return t}function w(t){return new SyntaxError(t+', got "'+_(O,30)+'" (char '+M+")")}function _(t,e){return e>=t.length?t:t.substr(0,27)+"..."}function E(t,e,i){t instanceof Array?t.forEach(function(t){e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}):e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}function T(t){function i(t){var e={from:t.from,to:t.to};return r(e,t.attr),e.style="->"==t.type?"arrow":"line",e}var n=e(t),s={nodes:[],edges:[],options:{}};return n.nodes&&n.nodes.forEach(function(t){var e={id:t.id,label:(t.label||t.id)+""};r(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),n.edges&&n.edges.forEach(function(t){var e,n;e=t.from instanceof Object?t.from.nodes:{id:t.from},n=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=i(t);s.edges.push(e)}),E(e,n,function(e,n){var o=l(s,e.id,n.id,t.type,t.attr),r=i(o);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=i(t);s.edges.push(e)})}),n.attr&&(s.options=n.attr),s}var x={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},S={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},D="",M=0,C="",O="",N=x.NULL,L=/[a-zA-Z_0-9.:#]/;t.parseDOT=e,t.DOTToGraph=T}(A!==void 0?A:n),"undefined"!=typeof CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.circle=function(t,e,i){this.beginPath(),this.arc(t,e,i,0,2*Math.PI,!1)},CanvasRenderingContext2D.prototype.square=function(t,e,i){this.beginPath(),this.rect(t-i,e-i,2*i,2*i)},CanvasRenderingContext2D.prototype.triangle=function(t,e,i){this.beginPath();var n=2*i,s=n/2,o=Math.sqrt(3)/6*n,r=Math.sqrt(n*n-s*s);this.moveTo(t,e-(r-o)),this.lineTo(t+s,e+o),this.lineTo(t-s,e+o),this.lineTo(t,e-(r-o)),this.closePath()},CanvasRenderingContext2D.prototype.triangleDown=function(t,e,i){this.beginPath();var n=2*i,s=n/2,o=Math.sqrt(3)/6*n,r=Math.sqrt(n*n-s*s);this.moveTo(t,e+(r-o)),this.lineTo(t+s,e-o),this.lineTo(t-s,e-o),this.lineTo(t,e+(r-o)),this.closePath()},CanvasRenderingContext2D.prototype.star=function(t,e,i){this.beginPath();for(var n=0;10>n;n++){var s=0===n%2?1.3*i:.5*i;this.lineTo(t+s*Math.sin(2*n*Math.PI/10),e-s*Math.cos(2*n*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,n,s){var o=Math.PI/180;0>i-2*s&&(s=i/2),0>n-2*s&&(s=n/2),this.beginPath(),this.moveTo(t+s,e),this.lineTo(t+i-s,e),this.arc(t+i-s,e+s,s,270*o,360*o,!1),this.lineTo(t+i,e+n-s),this.arc(t+i-s,e+n-s,s,0,90*o,!1),this.lineTo(t+s,e+n),this.arc(t+s,e+n-s,s,90*o,180*o,!1),this.lineTo(t,e+s),this.arc(t+s,e+s,s,180*o,270*o,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,n){var s=.5522848,o=i/2*s,r=n/2*s,a=t+i,h=e+n,d=t+i/2,l=e+n/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-o,e,d,e),this.bezierCurveTo(d+o,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+o,h,d,h),this.bezierCurveTo(d-o,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,n){var s=1/3,o=i,r=n*s,a=.5522848,h=o/2*a,d=r/2*a,l=t+o,p=e+r,c=t+o/2,u=e+r/2,f=e+(n-r/2),m=e+n;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,c+h,p,c,p),this.bezierCurveTo(c-h,p,t,u+d,t,u),this.bezierCurveTo(t,u-d,c-h,e,c,e),this.bezierCurveTo(c+h,e,l,u-d,l,u),this.lineTo(l,f),this.bezierCurveTo(l,f+d,c+h,m,c,m),this.bezierCurveTo(c-h,m,t,f+d,t,f),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,n){var s=t-n*Math.cos(i),o=e-n*Math.sin(i),r=t-.9*n*Math.cos(i),a=e-.9*n*Math.sin(i),h=s+n/3*Math.cos(i+.5*Math.PI),d=o+n/3*Math.sin(i+.5*Math.PI),l=s+n/3*Math.cos(i-.5*Math.PI),p=o+n/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,p),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,n,s){s||(s=[10,5]),0==c&&(c=.001);var o=s.length;this.moveTo(t,e);for(var r=i-t,a=n-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,p=!0;d>=.1;){var c=s[l++%o];c>d&&(c=d);var u=Math.sqrt(c*c/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[p?"lineTo":"moveTo"](t,e),d-=c,p=!p}}),M.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),this._updateMass()},M.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),this._updateMass()},M.prototype._updateMass=function(){this.mass=50+20*this.edges.length},M.prototype.setProperties=function(t,e){if(t){if(void 0!=t.id&&(this.id=t.id),void 0!=t.label&&(this.label=t.label),void 0!=t.title&&(this.title=t.title),void 0!=t.group&&(this.group=t.group),void 0!=t.x&&(this.x=t.x),void 0!=t.y&&(this.y=t.y),void 0!=t.value&&(this.value=t.value),void 0===this.id)throw"Node must have an id";if(this.group){var i=this.grouplist.get(this.group);for(var n in i)i.hasOwnProperty(n)&&(this[n]=i[n])}if(void 0!=t.shape&&(this.shape=t.shape),void 0!=t.image&&(this.image=t.image),void 0!=t.radius&&(this.radius=t.radius),void 0!=t.color&&(this.color=M.parseColor(t.color)),void 0!=t.fontColor&&(this.fontColor=t.fontColor),void 0!=t.fontSize&&(this.fontSize=t.fontSize),void 0!=t.fontFace&&(this.fontFace=t.fontFace),void 0!=this.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.image)}switch(this.xFixed=this.xFixed||void 0!=t.x,this.yFixed=this.yFixed||void 0!=t.y,this.radiusFixed=this.radiusFixed||void 0!=t.radius,"image"==this.shape&&(this.radiusMin=e.nodes.widthMin,this.radiusMax=e.nodes.widthMax),this.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},M.parseColor=function(t){var e;return A.isString(t)?e={border:t,background:t,highlight:{border:t,background:t}}:(e={},e.background=t.background||"white",e.border=t.border||e.background,A.isString(t.highlight)?e.highlight={border:t.highlight,background:t.highlight}:(e.highlight={},e.highlight.background=t.highlight&&t.highlight.background||e.background,e.highlight.border=t.highlight&&t.highlight.border||e.border)),e},M.prototype.select=function(){this.selected=!0,this._reset()},M.prototype.unselect=function(){this.selected=!1,this._reset()},M.prototype._reset=function(){this.width=void 0,this.height=void 0},M.prototype.getTitle=function(){return this.title},M.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.shape){case"circle":case"dot":return this.radius+i;case"ellipse":var n=this.width/2,s=this.height/2,o=Math.sin(e)*n,r=Math.cos(e)*s;return n*s/Math.sqrt(o*o+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},M.prototype._setForce=function(t,e){this.fx=t,this.fy=e},M.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},M.prototype.discreteStep=function(t){if(!this.xFixed){var e=-this.damping*this.vx,i=(this.fx+e)/this.mass;this.vx+=i/t,this.x+=this.vx/t}if(!this.yFixed){var n=-this.damping*this.vy,s=(this.fy+n)/this.mass;this.vy+=s/t,this.y+=this.vy/t}},M.prototype.isFixed=function(){return this.xFixed&&this.yFixed},M.prototype.isMoving=function(t){return Math.abs(this.vx)>t||Math.abs(this.vy)>t||!this.xFixed&&Math.abs(this.fx)>this.minForce||!this.yFixed&&Math.abs(this.fy)>this.minForce},M.prototype.isSelected=function(){return this.selected},M.prototype.getValue=function(){return this.value},M.prototype.getDistance=function(t,e){var i=this.x-t,n=this.y-e;return Math.sqrt(i*i+n*n)},M.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.radius=(this.radiusMin+this.radiusMax)/2;else{var i=(this.radiusMax-this.radiusMin)/(e-t);this.radius=(this.value-t)*i+this.radiusMin}},M.prototype.draw=function(){throw"Draw method not initialized for node"},M.prototype.resize=function(){throw"Resize method not initialized for node"},M.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},M.prototype._resizeImage=function(){if(!this.width){var t,e;if(this.value){var i=this.imageObj.height/this.imageObj.width;t=this.radius||this.imageObj.width,e=this.radius*i||this.imageObj.height}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e}},M.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e;this.imageObj?(t.drawImage(this.imageObj,this.left,this.top,this.width,this.height),e=this.y+this.height/2):e=this.y,this._label(t,this.label,this.x,e,void 0,"top")},M.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e}},M.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.roundRect(this.left,this.top,this.width,this.height,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},M.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),n=i.width+2*e;this.width=n,this.height=n}},M.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},M.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),n=Math.max(i.width,i.height)+2*e;this.radius=n/2,this.width=n,this.height=n}},M.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,t.strokeStyle=this.selected?this.color.highlight.border:this.color.border,t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.lineWidth=this.selected?2:1,t.circle(this.x,this.y,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},M.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.widthl;l++)t.fillText(r[l],i,d),d+=h}},M.prototype.getTextSize=function(t){if(void 0!=this.label){t.font=(this.selected?"bold ":"")+this.fontSize+"px "+this.fontFace;for(var e=this.label.split("\n"),i=(this.fontSize+4)*e.length,n=0,s=0,o=e.length;o>s;s++)n=Math.max(n,t.measureText(e[s]).width);return{width:n,height:i}}return{width:0,height:0}},C.prototype.setProperties=function(t,e){if(t)switch(void 0!=t.from&&(this.fromId=t.from),void 0!=t.to&&(this.toId=t.to),void 0!=t.id&&(this.id=t.id),void 0!=t.style&&(this.style=t.style),void 0!=t.label&&(this.label=t.label),this.label&&(this.fontSize=e.edges.fontSize,this.fontFace=e.edges.fontFace,this.fontColor=e.edges.fontColor,void 0!=t.fontColor&&(this.fontColor=t.fontColor),void 0!=t.fontSize&&(this.fontSize=t.fontSize),void 0!=t.fontFace&&(this.fontFace=t.fontFace)),void 0!=t.title&&(this.title=t.title),void 0!=t.width&&(this.width=t.width),void 0!=t.value&&(this.value=t.value),void 0!=t.length&&(this.length=t.length),t.dash&&(void 0!=t.dash.length&&(this.dash.length=t.dash.length),void 0!=t.dash.gap&&(this.dash.gap=t.dash.gap),void 0!=t.dash.altLength&&(this.dash.altLength=t.dash.altLength)),void 0!=t.color&&(this.color=t.color),this.connect(),this.widthFixed=this.widthFixed||void 0!=t.width,this.lengthFixed=this.lengthFixed||void 0!=t.length,this.stiffness=1/this.length,this.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter;break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}},C.prototype.connect=function(){this.disconnect(),this.from=this.graph.nodes[this.fromId]||null,this.to=this.graph.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this))
+},C.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},C.prototype.getTitle=function(){return this.title},C.prototype.getValue=function(){return this.value},C.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.widthMax-this.widthMin)/(e-t);this.width=(this.value-t)*i+this.widthMin}},C.prototype.draw=function(){throw"Method draw not initialized in edge"},C.prototype.isOverlappingWith=function(t){var e=10,i=this.from.x,n=this.from.y,s=this.to.x,o=this.to.y,r=t.left,a=t.top,h=C._dist(i,n,s,o,r,a);return e>h},C.prototype._drawLine=function(t){t.strokeStyle=this.color,t.lineWidth=this._getLineWidth();var e;if(this.from!=this.to)this._line(t),this.label&&(e=this._pointOnLine(.5),this._label(t,this.label,e.x,e.y));else{var i,n,s=this.length/4,o=this.from;o.width||o.resize(t),o.width>o.height?(i=o.x+o.width/2,n=o.y-s):(i=o.x+s,n=o.y-o.height/2),this._circle(t,i,n,s),e=this._pointOnCircle(i,n,s,.5),this._label(t,this.label,e.x,e.y)}},C.prototype._getLineWidth=function(){return this.from.selected||this.to.selected?Math.min(2*this.width,this.widthMax):this.width},C.prototype._line=function(t){t.beginPath(),t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y),t.stroke()},C.prototype._circle=function(t,e,i,n){t.beginPath(),t.arc(e,i,n,0,2*Math.PI,!1),t.stroke()},C.prototype._label=function(t,e,i,n){if(e){t.font=(this.from.selected||this.to.selected?"bold ":"")+this.fontSize+"px "+this.fontFace,t.fillStyle="white";var s=t.measureText(e).width,o=this.fontSize,r=i-s/2,a=n-o/2;t.fillRect(r,a,s,o),t.fillStyle=this.fontColor||"black",t.textAlign="left",t.textBaseline="top",t.fillText(e,r,a)}},C.prototype._drawDashLine=function(t){if(t.strokeStyle=this.color,t.lineWidth=this._getLineWidth(),t.beginPath(),t.lineCap="round",void 0!=this.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]):void 0!=this.dash.length&&void 0!=this.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke(),this.label){var e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}},C.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},C.prototype._pointOnCircle=function(t,e,i,n){var s=2*(n-3/8)*Math.PI;return{x:t+i*Math.cos(s),y:e-i*Math.sin(s)}},C.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this.color,t.fillStyle=this.color,t.lineWidth=this._getLineWidth(),this.from!=this.to){this._line(t);var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),n=10+5*this.width;e=this._pointOnLine(.5),t.arrow(e.x,e.y,i,n),t.fill(),t.stroke(),this.label&&(e=this._pointOnLine(.5),this._label(t,this.label,e.x,e.y))}else{var s,o,r=this.length/4,a=this.from;a.width||a.resize(t),a.width>a.height?(s=a.x+a.width/2,o=a.y-r):(s=a.x+r,o=a.y-a.height/2),this._circle(t,s,o,r);var i=.2*Math.PI,n=10+5*this.width;e=this._pointOnCircle(s,o,r,.5),t.arrow(e.x,e.y,i,n),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(s,o,r,.5),this._label(t,this.label,e.x,e.y))}},C.prototype._drawArrow=function(t){t.strokeStyle=this.color,t.fillStyle=this.color,t.lineWidth=this._getLineWidth();var e,i;if(this.from!=this.to){e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var n=this.to.x-this.from.x,s=this.to.y-this.from.y,o=Math.sqrt(n*n+s*s),r=this.from.distanceToBorder(t,e+Math.PI),a=(o-r)/o,h=a*this.from.x+(1-a)*this.to.x,d=a*this.from.y+(1-a)*this.to.y,l=this.to.distanceToBorder(t,e),p=(o-l)/o,c=(1-p)*this.from.x+p*this.to.x,u=(1-p)*this.from.y+p*this.to.y;if(t.beginPath(),t.moveTo(h,d),t.lineTo(c,u),t.stroke(),i=10+5*this.width,t.arrow(c,u,e,i),t.fill(),t.stroke(),this.label){var f=this._pointOnLine(.5);this._label(t,this.label,f.x,f.y)}}else{var m,g,v,y=this.from,b=this.length/4;y.width||y.resize(t),y.width>y.height?(m=y.x+y.width/2,g=y.y-b,v={x:m,y:y.y,angle:.9*Math.PI}):(m=y.x+b,g=y.y-y.height/2,v={x:y.x,y:g,angle:.6*Math.PI}),t.beginPath(),t.arc(m,g,b,0,2*Math.PI,!1),t.stroke(),i=10+5*this.width,t.arrow(v.x,v.y,v.angle,i),t.fill(),t.stroke(),this.label&&(f=this._pointOnCircle(m,g,b,.5),this._label(t,this.label,f.x,f.y))}},C._dist=function(t,e,i,n,s,o){var r=i-t,a=n-e,h=r*r+a*a,d=((s-t)*r+(o-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,p=e+d*a,c=l-s,u=p-o;return Math.sqrt(c*c+u*u)},O.prototype.setPosition=function(t,e){this.x=parseInt(t),this.y=parseInt(e)},O.prototype.setText=function(t){this.frame.innerHTML=t},O.prototype.show=function(t){if(void 0===t&&(t=!0),t){var e=this.frame.clientHeight,i=this.frame.clientWidth,n=this.frame.parentNode.clientHeight,s=this.frame.parentNode.clientWidth,o=this.y-e;o+e+this.padding>n&&(o=n-e-this.padding),this.padding>o&&(o=this.padding);var r=this.x;r+i+this.padding>s&&(r=s-i-this.padding),this.padding>r&&(r=this.padding),this.frame.style.left=r+"px",this.frame.style.top=o+"px",this.frame.style.visibility="visible"}else this.hide()},O.prototype.hide=function(){this.frame.style.visibility="hidden"},Groups=function(){this.clear(),this.defaultIndex=0},Groups.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"}}],Groups.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},Groups.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%Groups.DEFAULT.length;this.defaultIndex++,e={},e.color=Groups.DEFAULT[i],this.groups[t]=e}return e},Groups.prototype.add=function(t,e){return this.groups[t]=e,e.color&&(e.color=M.parseColor(e.color)),e},Images=function(){this.images={},this.callback=void 0},Images.prototype.setOnloadCallback=function(t){this.callback=t},Images.prototype.load=function(t){var e=this.images[t];if(void 0==e){var i=this;e=new Image,this.images[t]=e,e.onload=function(){i.callback&&i.callback(this)},e.src=t}return e},N.prototype.setData=function(t){if(t&&t.dot&&(t.nodes||t.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var e=R.util.DOTToGraph(t.dot);return this.setData(e),void 0}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);this.stabilize&&this._doStabilize(),this.start()},N.prototype.setOptions=function(t){if(t){if(void 0!=t.width&&(this.width=t.width),void 0!=t.height&&(this.height=t.height),void 0!=t.stabilize&&(this.stabilize=t.stabilize),void 0!=t.selectable&&(this.selectable=t.selectable),t.edges){for(var e in t.edges)t.edges.hasOwnProperty(e)&&(this.constants.edges[e]=t.edges[e]);void 0!=t.edges.length&&t.nodes&&void 0==t.nodes.distance&&(this.constants.edges.length=t.edges.length,this.constants.nodes.distance=1.25*t.edges.length),t.edges.fontColor||(this.constants.edges.fontColor=t.edges.color),t.edges.dash&&(void 0!=t.edges.dash.length&&(this.constants.edges.dash.length=t.edges.dash.length),void 0!=t.edges.dash.gap&&(this.constants.edges.dash.gap=t.edges.dash.gap),void 0!=t.edges.dash.altLength&&(this.constants.edges.dash.altLength=t.edges.dash.altLength))}if(t.nodes){for(e in t.nodes)t.nodes.hasOwnProperty(e)&&(this.constants.nodes[e]=t.nodes[e]);t.nodes.color&&(this.constants.nodes.color=M.parseColor(t.nodes.color))}if(t.groups)for(var i in t.groups)if(t.groups.hasOwnProperty(i)){var n=t.groups[i];this.groups.add(i,n)}}this.setSize(this.width,this.height),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1)},N.prototype._trigger=function(t,e){F.trigger(this,t,e)},N.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="graph-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),!this.frame.canvas.getContext){var t=document.createElement("DIV");t.style.color="red",t.style.fontWeight="bold",t.style.padding="10px",t.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(t)}var e=this;this.drag={},this.pinch={},this.hammer=L(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",e._onTap.bind(e)),this.hammer.on("hold",e._onHold.bind(e)),this.hammer.on("pinch",e._onPinch.bind(e)),this.hammer.on("touch",e._onTouch.bind(e)),this.hammer.on("dragstart",e._onDragStart.bind(e)),this.hammer.on("drag",e._onDrag.bind(e)),this.hammer.on("dragend",e._onDragEnd.bind(e)),this.hammer.on("mousewheel",e._onMouseWheel.bind(e)),this.hammer.on("DOMMouseScroll",e._onMouseWheel.bind(e)),this.hammer.on("mousemove",e._onMouseMoveTitle.bind(e)),this.containerElement.appendChild(this.frame)},N.prototype._getNodeAt=function(t){var e=this._canvasToX(t.x),i=this._canvasToY(t.y),n={left:e,top:i,right:e,bottom:i},s=this._getNodesOverlappingWith(n);return s.length>0?s[s.length-1]:null},N.prototype._getPointer=function(t){return{x:t.pageX-R.util.getAbsoluteLeft(this.frame.canvas),y:t.pageY-R.util.getAbsoluteTop(this.frame.canvas)}},N.prototype._onTouch=function(t){this.drag.pointer=this._getPointer(t.gesture.touches[0]),this.drag.pinched=!1,this.pinch.scale=this._getScale()},N.prototype._onDragStart=function(){var t=this.drag;t.selection=[],t.translation=this._getTranslation(),t.nodeId=this._getNodeAt(t.pointer);var e=this.nodes[t.nodeId];if(e){e.isSelected()||this._selectNodes([t.nodeId]);var i=this;this.selection.forEach(function(e){var n=i.nodes[e];if(n){var s={id:e,node:n,x:n.x,y:n.y,xFixed:n.xFixed,yFixed:n.yFixed};n.xFixed=!0,n.yFixed=!0,t.selection.push(s)}})}},N.prototype._onDrag=function(t){if(!this.drag.pinched){var e=this._getPointer(t.gesture.touches[0]),i=this,n=this.drag,s=n.selection;if(s&&s.length){var o=e.x-n.pointer.x,r=e.y-n.pointer.y;s.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._canvasToX(i._xToCanvas(t.x)+o)),t.yFixed||(e.y=i._canvasToY(i._yToCanvas(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else{var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw(),this.moved=!0}}},N.prototype._onDragEnd=function(){var t=this.drag.selection;t&&t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed})},N.prototype._onTap=function(t){var e=this._getPointer(t.gesture.touches[0]),i=this._getNodeAt(e),n=this.nodes[i];n?(this._selectNodes([i]),this.moving||this._redraw()):(this._unselectNodes(),this._redraw())},N.prototype._onHold=function(t){var e=this._getPointer(t.gesture.touches[0]),i=this._getNodeAt(e),n=this.nodes[i];if(n){if(n.isSelected())this._unselectNodes([i]);else{var s=!0;this._selectNodes([i],s)}this.moving||this._redraw()}},N.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},N.prototype._zoom=function(t,e){var i=this._getScale();.01>t&&(t=.01),t>10&&(t=10);var n=this._getTranslation(),s=t/i,o=(1-s)*e.x+n.x*s,r=(1-s)*e.y+n.y*s;return this._setScale(t),this._setTranslation(o,r),this._redraw(),t},N.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){"mouswheelScale"in this.pinch||(this.pinch.mouswheelScale=1);var i=this.pinch.mouswheelScale,n=e/10;0>e&&(n/=1-n),i*=1+n;var s=L.event.collectEventData(this,"scroll",t),o=this._getPointer(s.center);i=this._zoom(i,o),this.pinch.mouswheelScale=i}t.preventDefault()},N.prototype._onMouseMoveTitle=function(t){var e=L.event.collectEventData(this,"mousemove",t),i=this._getPointer(e.center);this.popupNode&&this._checkHidePopup(i);var n=this,s=function(){n._checkShowPopup(i)};this.popupTimer&&clearInterval(this.popupTimer),this.leftButtonDown||(this.popupTimer=setTimeout(s,300))},N.prototype._checkShowPopup=function(t){var e,i={left:this._canvasToX(t.x),top:this._canvasToY(t.y),right:this._canvasToX(t.x),bottom:this._canvasToY(t.y)},n=this.popupNode;if(void 0==this.popupNode){var s=this.nodes;for(e in s)if(s.hasOwnProperty(e)){var o=s[e];if(void 0!=o.getTitle()&&o.isOverlappingWith(i)){this.popupNode=o;break}}}if(void 0==this.popupNode){var r=this.edges;for(e in r)if(r.hasOwnProperty(e)){var a=r[e];if(a.connected&&void 0!=a.getTitle()&&a.isOverlappingWith(i)){this.popupNode=a;break}}}if(this.popupNode){if(this.popupNode!=n){var h=this;h.popup||(h.popup=new O(h.frame)),h.popup.setPosition(t.x-3,t.y-3),h.popup.setText(h.popupNode.getTitle()),h.popup.show()}}else this.popup&&this.popup.hide()},N.prototype._checkHidePopup=function(t){this.popupNode&&this._getNodeAt(t)||(this.popupNode=void 0,this.popup&&this.popup.hide())},N.prototype._unselectNodes=function(t,e){var i,n,s,o=!1;if(t)for(i=0,n=t.length;n>i;i++){s=t[i],this.nodes[s].unselect();for(var r=0;this.selection.length>r;)this.selection[r]==s?(this.selection.splice(r,1),o=!0):r++}else if(this.selection&&this.selection.length){for(i=0,n=this.selection.length;n>i;i++)s=this.selection[i],this.nodes[s].unselect(),o=!0;this.selection=[]}return!o||1!=e&&void 0!=e||this._trigger("select"),o},N.prototype._selectNodes=function(t,e){var i,n,s=!1,o=!0;if(t.length!=this.selection.length)o=!1;else for(i=0,n=Math.min(t.length,this.selection.length);n>i;i++)if(t[i]!=this.selection[i]){o=!1;break}if(o)return s;if(void 0==e||0==e){var r=!1;s=this._unselectNodes(void 0,r)}for(i=0,n=t.length;n>i;i++){var a=t[i],h=-1!=this.selection.indexOf(a);h||(this.nodes[a].select(),this.selection.push(a),s=!0)}return s&&this._trigger("select"),s},N.prototype._getNodesOverlappingWith=function(t){var e=this.nodes,i=[];for(var n in e)e.hasOwnProperty(n)&&e[n].isOverlappingWith(t)&&i.push(n);return i},N.prototype.getSelection=function(){return this.selection.concat([])},N.prototype.setSelection=function(t){var e,i,n;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(e=0,i=this.selection.length;i>e;e++)n=this.selection[e],this.nodes[n].unselect();for(this.selection=[],e=0,i=t.length;i>e;e++){n=t[e];var s=this.nodes[n];if(!s)throw new RangeError('Node with id "'+n+'" not found');s.select(),this.selection.push(n)}this.redraw()},N.prototype._updateSelection=function(){for(var t=0;this.selection.length>t;){var e=this.selection[t];this.nodes[e]?t++:this.selection.splice(t,1)}},N.prototype._getConnectionCount=function(t){function e(t){for(var e=[],i=0,n=t.length;n>i;i++)for(var s=t[i],o=s.edges,r=0,a=o.length;a>r;r++){var h=o[r],d=null;h.from==s?d=h.to:h.to==s&&(d=h.from);var l,p;if(d)for(l=0,p=t.length;p>l;l++)if(t[l]==d){d=null;break}if(d)for(l=0,p=e.length;p>l;l++)if(e[l]==d){d=null;break}d&&e.push(d)}return e}void 0==t&&(t=1);var i=[],n=this.nodes;for(var s in n)if(n.hasOwnProperty(s)){for(var o=[n[s]],r=0;t>r;r++)o=o.concat(e(o));i.push(o)}for(var a=[],h=0,d=i.length;d>h;h++)a.push(i[h].length);return a},N.prototype.setSize=function(t,e){this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth,this.frame.canvas.height=this.frame.canvas.clientHeight},N.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof o||t instanceof r)this.nodesData=t;else if(t instanceof Array)this.nodesData=new o,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new o}if(e&&A.forEach(this.nodesListeners,function(t,i){e.unsubscribe(i,t)}),this.nodes={},this.nodesData){var i=this;A.forEach(this.nodesListeners,function(t,e){i.nodesData.subscribe(e,t)});var n=this.nodesData.getIds();this._addNodes(n)}this._updateSelection()},N.prototype._addNodes=function(t){for(var e,i=0,n=t.length;n>i;i++){e=t[i];var s=this.nodesData.get(e),o=new M(s,this.images,this.groups,this.constants);if(this.nodes[e]=o,!o.isFixed()){var r=2*this.constants.edges.length,a=t.length,h=2*Math.PI*(i/a);o.x=r*Math.cos(h),o.y=r*Math.sin(h),this.moving=!0}}this._reconnectEdges(),this._updateValueRange(this.nodes)},N.prototype._updateNodes=function(t){for(var e=this.nodes,i=this.nodesData,n=0,s=t.length;s>n;n++){var o=t[n],r=e[o],a=i.get(o);r?r.setProperties(a,this.constants):(r=new M(properties,this.images,this.groups,this.constants),e[o]=r,r.isFixed()||(this.moving=!0))}this._reconnectEdges(),this._updateValueRange(e)},N.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,n=t.length;n>i;i++){var s=t[i];delete e[s]}this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},N.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof o||t instanceof r)this.edgesData=t;else if(t instanceof Array)this.edgesData=new o,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new o}if(e&&A.forEach(this.edgesListeners,function(t,i){e.unsubscribe(i,t)}),this.edges={},this.edgesData){var i=this;A.forEach(this.edgesListeners,function(t,e){i.edgesData.subscribe(e,t)});var n=this.edgesData.getIds();this._addEdges(n)}this._reconnectEdges()},N.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,n=0,s=t.length;s>n;n++){var o=t[n],r=e[o];r&&r.disconnect();var a=i.get(o);e[o]=new C(a,this,this.constants)}this.moving=!0,this._updateValueRange(e)},N.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,n=0,s=t.length;s>n;n++){var o=t[n],r=i.get(o),a=e[o];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new C(r,this,this.constants),this.edges[o]=a)}this.moving=!0,this._updateValueRange(e)},N.prototype._removeEdges=function(t){for(var e=this.edges,i=0,n=t.length;n>i;i++){var s=t[i],o=e[s];o&&(o.disconnect(),delete e[s])}this.moving=!0,this._updateValueRange(e)},N.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[]);for(t in i)if(i.hasOwnProperty(t)){var n=i[t];n.from=null,n.to=null,n.connect()}},N.prototype._updateValueRange=function(t){var e,i=void 0,n=void 0;for(e in t)if(t.hasOwnProperty(e)){var s=t[e].getValue();void 0!==s&&(i=void 0===i?s:Math.min(s,i),n=void 0===n?s:Math.max(s,n))}if(void 0!==i&&void 0!==n)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,n)},N.prototype.redraw=function(){this.setSize(this.width,this.height),this._redraw()},N.prototype._redraw=function(){var t=this.frame.canvas.getContext("2d"),e=this.frame.canvas.width,i=this.frame.canvas.height;t.clearRect(0,0,e,i),t.save(),t.translate(this.translation.x,this.translation.y),t.scale(this.scale,this.scale),this._drawEdges(t),this._drawNodes(t),t.restore()},N.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e)},N.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},N.prototype._setScale=function(t){this.scale=t},N.prototype._getScale=function(){return this.scale},N.prototype._canvasToX=function(t){return(t-this.translation.x)/this.scale},N.prototype._xToCanvas=function(t){return t*this.scale+this.translation.x},N.prototype._canvasToY=function(t){return(t-this.translation.y)/this.scale},N.prototype._yToCanvas=function(t){return t*this.scale+this.translation.y},N.prototype._drawNodes=function(t){var e=this.nodes,i=[];for(var n in e)e.hasOwnProperty(n)&&(e[n].isSelected()?i.push(n):e[n].draw(t));for(var s=0,o=i.length;o>s;s++)e[i[s]].draw(t)},N.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var n=e[i];n.connected&&e[i].draw(t)}},N.prototype._doStabilize=function(){new Date;for(var t=0,e=this.constants.minVelocity,i=!1;!i&&this.constants.maxIterations>t;)this._calculateForces(),this._discreteStepNodes(),i=!this._isMoving(e),t++;new Date},N.prototype._calculateForces=function(){var t,e,i,n,s,o,r,a,h,d,l,p=this.nodes,c=this.edges,u=.01,f=this.frame.canvas.clientWidth/2,m=this.frame.canvas.clientHeight/2;for(t in p)if(p.hasOwnProperty(t)){var g=p[t];e=f-g.x,i=m-g.y,n=Math.atan2(i,e),o=Math.cos(n)*u,r=Math.sin(n)*u,g._setForce(o,r)}var v=this.constants.nodes.distance,y=10;for(var b in p)if(p.hasOwnProperty(b)){var w=p[b];for(var _ in p)if(p.hasOwnProperty(_)){var E=p[_];e=E.x-w.x,i=E.y-w.y,s=Math.sqrt(e*e+i*i),n=Math.atan2(i,e),a=1/(1+Math.exp((s/v-1)*y)),o=Math.cos(n)*a,r=Math.sin(n)*a,w._addForce(-o,-r),E._addForce(o,r)}}for(t in c)if(c.hasOwnProperty(t)){var T=c[t];T.connected&&(e=T.to.x-T.from.x,i=T.to.y-T.from.y,l=T.length,d=Math.sqrt(e*e+i*i),n=Math.atan2(i,e),h=T.stiffness*(l-d),o=Math.cos(n)*h,r=Math.sin(n)*h,T.from._addForce(-o,-r),T.to._addForce(o,r))}},N.prototype._isMoving=function(t){var e=this.nodes;for(var i in e)if(e.hasOwnProperty(i)&&e[i].isMoving(t))return!0;return!1},N.prototype._discreteStepNodes=function(){var t=this.refreshRate/1e3,e=this.nodes;for(var i in e)e.hasOwnProperty(i)&&e[i].discreteStep(t)},N.prototype.start=function(){if(this.moving){this._calculateForces(),this._discreteStepNodes();var t=this.constants.minVelocity;this.moving=this._isMoving(t)}if(this.moving){if(!this.timer){var e=this;this.timer=window.setTimeout(function(){e.timer=void 0,e.start(),e._redraw()},this.refreshRate)}}else this._redraw()},N.prototype.stop=function(){this.timer&&(window.clearInterval(this.timer),this.timer=void 0)};var R={util:A,events:F,Controller:p,DataSet:o,DataView:r,Range:h,Stack:a,TimeStep:TimeStep,EventBus:s,components:{items:{Item:b,ItemBox:w,ItemPoint:_,ItemRange:E},Component:c,Panel:u,RootPanel:f,ItemSet:y,TimeAxis:m},graph:{Node:M,Edge:C,Popup:O,Groups:Groups,Images:Images},Timeline:D,Graph:N};n!==void 0&&(n=R),i!==void 0&&i.exports!==void 0&&(i.exports=R),"function"==typeof t&&t(function(){return R}),"undefined"!=typeof window&&(window.vis=R),A.loadCss("/* vis.js stylesheet */\n.vis.timeline {\n}\n\n\n.vis.timeline.rootpanel {\n position: relative;\n overflow: hidden;\n\n border: 1px solid #bfbfbf;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n.vis.timeline .panel {\n position: absolute;\n overflow: hidden;\n}\n\n\n.vis.timeline .groupset {\n position: absolute;\n padding: 0;\n margin: 0;\n}\n\n.vis.timeline .labels {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n padding: 0;\n margin: 0;\n\n border-right: 1px solid #bfbfbf;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.vis.timeline .labels .label-set {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n overflow: hidden;\n\n border-top: none;\n border-bottom: 1px solid #bfbfbf;\n}\n\n.vis.timeline .labels .label-set .label {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n color: #4d4d4d;\n}\n\n.vis.timeline.top .labels .label-set .label,\n.vis.timeline.top .groupset .itemset-axis {\n border-top: 1px solid #bfbfbf;\n border-bottom: none;\n}\n\n.vis.timeline.bottom .labels .label-set .label,\n.vis.timeline.bottom .groupset .itemset-axis {\n border-top: none;\n border-bottom: 1px solid #bfbfbf;\n}\n\n.vis.timeline .labels .label-set .label .inner {\n display: inline-block;\n padding: 5px;\n}\n\n\n.vis.timeline .itemset {\n position: absolute;\n padding: 0;\n margin: 0;\n overflow: hidden;\n}\n\n.vis.timeline .background {\n}\n\n.vis.timeline .foreground {\n}\n\n.vis.timeline .itemset-axis {\n position: absolute;\n}\n\n\n.vis.timeline .item {\n position: absolute;\n color: #1A1A1A;\n border-color: #97B0F8;\n background-color: #D5DDF6;\n display: inline-block;\n}\n\n.vis.timeline .item.selected {\n border-color: #FFC200;\n background-color: #FFF785;\n z-index: 999;\n}\n\n.vis.timeline .item.cluster {\n /* TODO: use another color or pattern? */\n background: #97B0F8 url('img/cluster_bg.png');\n color: white;\n}\n.vis.timeline .item.cluster.point {\n border-color: #D5DDF6;\n}\n\n.vis.timeline .item.box {\n text-align: center;\n border-style: solid;\n border-width: 1px;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.point {\n background: none;\n}\n\n.vis.timeline .dot {\n border: 5px solid #97B0F8;\n position: absolute;\n border-radius: 5px;\n -moz-border-radius: 5px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range {\n overflow: hidden;\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.rangeoverflow {\n border-style: solid;\n border-width: 1px;\n border-radius: 2px;\n -moz-border-radius: 2px; /* For Firefox 3.6 and older */\n}\n\n.vis.timeline .item.range .drag-left, .vis.timeline .item.rangeoverflow .drag-left {\n cursor: w-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .drag-right, .vis.timeline .item.rangeoverflow .drag-right {\n cursor: e-resize;\n z-index: 1000;\n}\n\n.vis.timeline .item.range .content, .vis.timeline .item.rangeoverflow .content {\n position: relative;\n display: inline-block;\n}\n\n.vis.timeline .item.line {\n position: absolute;\n width: 0;\n border-left-width: 1px;\n border-left-style: solid;\n}\n\n.vis.timeline .item .content {\n margin: 5px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.vis.timeline .axis {\n position: relative;\n}\n\n.vis.timeline .axis .text {\n position: absolute;\n color: #4d4d4d;\n padding: 3px;\n white-space: nowrap;\n}\n\n.vis.timeline .axis .text.measure {\n position: absolute;\n padding-left: 0;\n padding-right: 0;\n margin-left: 0;\n margin-right: 0;\n visibility: hidden;\n}\n\n.vis.timeline .axis .grid.vertical {\n position: absolute;\n width: 0;\n border-right: 1px solid;\n}\n\n.vis.timeline .axis .grid.horizontal {\n position: absolute;\n left: 0;\n width: 100%;\n height: 0;\n border-bottom: 1px solid;\n}\n\n.vis.timeline .axis .grid.minor {\n border-color: #e5e5e5;\n}\n\n.vis.timeline .axis .grid.major {\n border-color: #bfbfbf;\n}\n\n.vis.timeline .currenttime {\n background-color: #FF7F6E;\n width: 2px;\n z-index: 9;\n}\n.vis.timeline .customtime {\n background-color: #6E94FF;\n width: 2px;\n cursor: move;\n z-index: 9;\n}\n")},{hammerjs:1,moment:2}]},{},[3])(3)});
\ No newline at end of file