diff --git a/.gitignore b/.gitignore index 0a6dbb3cb2..28e3f36ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ !/.gitignore !/.editorconfig !/.scrutinizer.yml - +!/.travis.yml # Others # ########## diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..36f9fbddea --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: php + +php: + - '5.3' + - '5.4' + - '5.5' + - '5.6' + - '7.0' + - hhvm + +matrix: + allow_failures: + - php: 7.0 diff --git a/AUTHORS.md b/AUTHORS.md index acd9aa26f9..a4218d6668 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -86,6 +86,9 @@ LibreNMS contributors: - Henoc Khouilla (henocKA) - Paul Blasquez (pblasquez) - Paul Heinrichs (pheinrichs) +- Georges Dick (GeorgesDick) +- Richard Lawley (richardlawley) +- Marco Di Cerbo (marcodc) [1]: http://observium.org/ "Observium web site" Observium was written by: @@ -93,3 +96,4 @@ Observium was written by: - Tom Laermans - various others as indicated in the file contents and commit logs + diff --git a/check-services.php b/check-services.php index 55295a70f7..e053126b22 100755 --- a/check-services.php +++ b/check-services.php @@ -34,7 +34,7 @@ foreach (dbFetchRows('SELECT * FROM `devices` AS D, `services` AS S WHERE S.devi $cmd = $config['nagios_plugins'] . "/check_" . $service['service_type'] . " -H " . ($service['service_ip'] ? $service['service_ip'] : $service['hostname']); $cmd .= " ".$service['service_param']; $check = shell_exec($cmd); - list($check, $time) = split("\|", $check); + list($check, $time) = explode("|", $check); if(stristr($check, "ok -")) { $status = 1; } diff --git a/daily.php b/daily.php index cc91d132da..7e42c4066d 100644 --- a/daily.php +++ b/daily.php @@ -18,6 +18,10 @@ if (isset($options['d'])) { } if ($options['f'] === 'update') { + if (!$config['update']) { + exit(0); + } + $innodb_buffer = innodb_buffer_check(); if ($innodb_buffer['used'] > $innodb_buffer['size']) { if (!empty($config['alert']['default_mail'])) { @@ -39,19 +43,14 @@ The ' . $config['project_name'] . ' team.'; echo warn_innodb_buffer($innodb_buffer); exit(2); } - else { - if ($config['update']) { - if ($config['update_channel'] == 'master') { - exit(1); - } - elseif ($config['update_channel'] == 'release') { - exit(3); - } - } - else { - exit(0); - } + + if ($config['update_channel'] == 'master') { + exit(1); } + elseif ($config['update_channel'] == 'release') { + exit(3); + } + exit(0); } if ($options['f'] === 'syslog') { diff --git a/daily.sh b/daily.sh index 17591fa8fd..49dcb075e0 100755 --- a/daily.sh +++ b/daily.sh @@ -33,7 +33,10 @@ status_run() { if [ -z "$arg" ]; then up=$(php daily.php -f update >&2; echo $?) - if [ "$up" -eq 1 ]; then + if [ "$up" -eq 0 ]; then + $0 no-code-update + exit + elif [ "$up" -eq 1 ]; then # Update to Master-Branch status_run 'Updating to latest codebase' 'git pull --quiet' elif [ "$up" -eq 3 ]; then @@ -49,6 +52,12 @@ if [ -z "$arg" ]; then fi else case $arg in + no-code-update) + # Updates of the code are disabled, just check for schema updates + # and clean up the db. + status_run 'Updating SQL-Schema' 'php includes/sql-schema/update.php' + status_run 'Cleaning up DB' "$0 cleanup" + ;; post-pull) # List all tasks to do after pull in the order of execution status_run 'Updating SQL-Schema' 'php includes/sql-schema/update.php' diff --git a/discovery.php b/discovery.php index 289e921097..86f3c11567 100755 --- a/discovery.php +++ b/discovery.php @@ -125,7 +125,7 @@ require 'includes/sql-schema/update.php'; $discovered_devices = 0; -if ($config['distributed_poller'] === true) { +if (!empty($config['distributed_poller_group'])) { $where .= ' AND poller_group IN('.$config['distributed_poller_group'].')'; } diff --git a/doc/Developing/Sensor-State-Support.md b/doc/Developing/Sensor-State-Support.md new file mode 100644 index 0000000000..3bdd8a2175 --- /dev/null +++ b/doc/Developing/Sensor-State-Support.md @@ -0,0 +1,97 @@ +# Sensor State Support + +### Introduction + +In this section we are briefly going to walk through, what it takes to write sensor state support. +We will also briefly get around the concepts of the current sensor state monitoring. + +### Logic + +For sensor state monitoring, we have 4 DB tables we need to concentrate about. +- sensors +- state_indexes +- state_translations +- sensors_to_state_indexes + +We will just briefly tie a comment to each one of them. + +#### sensors + +*Each time a sensor needs to be polled, the system needs to know which sensor is it that it need to poll, at what oid is this sensor located and what class the sensor is etc. +This information is fetched from the sensors table.* + +#### state_indexes + +*Is where we keep track of which state sensors we monitor.* + +#### state_translations + +*Is where we map the possible returned state sensor values to a generic LibreNMS value, in order to make displaying and alerting more generic. +We also map these values to the actual state sensor(state_index) where these values are actually returned from.* + + +*The LibreNMS generic states is derived from Nagios:* + +``` +0 = OK + +1 = Warning + +2 = Critical + +3 = Unknown +``` + +#### sensors_to_state_indexes + +*Is as you might have guessed, where the sensor_id is mapped to a state_index_id.* + +### Example +This example will be based on a Cisco power supply sensor and is all it takes to have sensor state support for Cisco power supplys in Cisco switches. +The file should be located in /includes/discovery/sensors/state/cisco.inc.php. + +```php + $value[0], + 'state_descr' => $value[1], + 'state_draw_graph' => $value[2], + 'state_value' => $value[3], + 'state_generic_value' => $value[4] + ); + dbInsert($insert, 'state_translations'); + } + } + + foreach ($temp as $index => $entry) { + //Discover Sensors + discover_sensor($valid['sensor'], 'state', $device, $cur_oid.$index, $index, $state_name, $temp[$index]['ciscoEnvMonSupplyStatusDescr'], '1', '1', null, null, null, null, $temp[$index]['ciscoEnvMonSupplyState'], 'snmp', $index); + + //Create Sensor To State Index + create_sensor_to_state_index($device, $state_name, $index); + } + } +} +``` diff --git a/doc/Installation/Installation-(Debian-Ubuntu).md b/doc/Installation/Installation-(Debian-Ubuntu).md index dea55fb1fb..50d216d76e 100644 --- a/doc/Installation/Installation-(Debian-Ubuntu).md +++ b/doc/Installation/Installation-(Debian-Ubuntu).md @@ -177,6 +177,14 @@ Create the admin user - priv should be 10 Substitute your desired username, password and email address--and leave the angled brackets off. +### Validate your install ### + +Run validate.php as root in the librenms directory + + php validate.php + +This will check your install to verify it is set up correctly. + ### Add localhost ### php addhost.php localhost public v2c diff --git a/doc/Installation/Installation-(RHEL-CentOS).md b/doc/Installation/Installation-(RHEL-CentOS).md index d60fe96af6..a52bc5417b 100644 --- a/doc/Installation/Installation-(RHEL-CentOS).md +++ b/doc/Installation/Installation-(RHEL-CentOS).md @@ -285,6 +285,11 @@ First, create and chown the `rrd` directory and create the `logs` directory restorecon -RFvv /opt/librenms/logs/ ``` +Set selinux to allow httpd to sendmail +```bash + setsebool -P httpd_can_sendmail=1 +``` + Start the web-server: **CentOS 6** @@ -303,6 +308,14 @@ Start the web-server: # For Nginx: systemctl restart nginx +### Validate your install ### + +Run validate.php as root in the librenms directory + + php validate.php + +This will check your install to verify it is set up correctly. + ### Add localhost ### php addhost.php localhost public v2c diff --git a/doc/Installation/Installation-Lighttpd-(Debian-Ubuntu).md b/doc/Installation/Installation-Lighttpd-(Debian-Ubuntu).md index ed48898135..dedb51866d 100644 --- a/doc/Installation/Installation-Lighttpd-(Debian-Ubuntu).md +++ b/doc/Installation/Installation-Lighttpd-(Debian-Ubuntu).md @@ -153,6 +153,14 @@ then restart Lighttpd: service lighttpd restart +### Validate your install ### + +Run validate.php as root in the librenms directory + + php validate.php + +This will check your install to verify it is set up correctly. + ### Add localhost ### php addhost.php localhost public v2c diff --git a/html/ajax_search.php b/html/ajax_search.php index cb8d012a33..ba9dc79b7f 100644 --- a/html/ajax_search.php +++ b/html/ajax_search.php @@ -16,6 +16,7 @@ if (!$_SESSION['authenticated']) { $device = array(); $ports = array(); $bgp = array(); +$limit = $config['webui']['global_search_result_limit']; if (isset($_REQUEST['search'])) { $search = mres($_REQUEST['search']); @@ -49,10 +50,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'device') { // Device search if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT * FROM `devices` WHERE `hostname` LIKE '%".$search."%' OR `location` LIKE '%".$search."%' ORDER BY hostname LIMIT 8"); + $results = dbFetchRows("SELECT * FROM `devices` WHERE `hostname` LIKE '%".$search."%' OR `location` LIKE '%".$search."%' ORDER BY hostname LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT * FROM `devices` AS `D`, `devices_perms` AS `P` WHERE `P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id` AND (`hostname` LIKE '%".$search."%' OR `location` LIKE '%".$search."%') ORDER BY hostname LIMIT 8", array($_SESSION['user_id'])); + $results = dbFetchRows("SELECT * FROM `devices` AS `D`, `devices_perms` AS `P` WHERE `P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id` AND (`hostname` LIKE '%".$search."%' OR `location` LIKE '%".$search."%') ORDER BY hostname LIMIT ".$limit, array($_SESSION['user_id'])); } if (count($results)) { @@ -102,10 +103,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'ports') { // Search ports if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT `ports`.*,`devices`.* FROM `ports` LEFT JOIN `devices` ON `ports`.`device_id` = `devices`.`device_id` WHERE `ifAlias` LIKE '%".$search."%' OR `ifDescr` LIKE '%".$search."%' OR `ifName` LIKE '%".$search."%' ORDER BY ifDescr LIMIT 8"); + $results = dbFetchRows("SELECT `ports`.*,`devices`.* FROM `ports` LEFT JOIN `devices` ON `ports`.`device_id` = `devices`.`device_id` WHERE `ifAlias` LIKE '%".$search."%' OR `ifDescr` LIKE '%".$search."%' OR `ifName` LIKE '%".$search."%' ORDER BY ifDescr LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT DISTINCT(`I`.`port_id`), `I`.*, `D`.`hostname` FROM `ports` AS `I`, `devices` AS `D`, `devices_perms` AS `P`, `ports_perms` AS `PP` WHERE ((`P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id`) OR (`PP`.`user_id` = ? AND `PP`.`port_id` = `I`.`port_id` AND `I`.`device_id` = `D`.`device_id`)) AND `D`.`device_id` = `I`.`device_id` AND (`ifAlias` LIKE '%".$search."%' OR `ifDescr` LIKE '%".$search."%' OR `ifName` LIKE '%".$search."%') ORDER BY ifDescr LIMIT 8", array($_SESSION['user_id'], $_SESSION['user_id'])); + $results = dbFetchRows("SELECT DISTINCT(`I`.`port_id`), `I`.*, `D`.`hostname` FROM `ports` AS `I`, `devices` AS `D`, `devices_perms` AS `P`, `ports_perms` AS `PP` WHERE ((`P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id`) OR (`PP`.`user_id` = ? AND `PP`.`port_id` = `I`.`port_id` AND `I`.`device_id` = `D`.`device_id`)) AND `D`.`device_id` = `I`.`device_id` AND (`ifAlias` LIKE '%".$search."%' OR `ifDescr` LIKE '%".$search."%' OR `ifName` LIKE '%".$search."%') ORDER BY ifDescr LIMIT ".$limit, array($_SESSION['user_id'], $_SESSION['user_id'])); } if (count($results)) { @@ -154,10 +155,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'bgp') { // Search bgp peers if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT `bgpPeers`.*,`devices`.* FROM `bgpPeers` LEFT JOIN `devices` ON `bgpPeers`.`device_id` = `devices`.`device_id` WHERE `astext` LIKE '%".$search."%' OR `bgpPeerIdentifier` LIKE '%".$search."%' OR `bgpPeerRemoteAs` LIKE '%".$search."%' ORDER BY `astext` LIMIT 8"); + $results = dbFetchRows("SELECT `bgpPeers`.*,`devices`.* FROM `bgpPeers` LEFT JOIN `devices` ON `bgpPeers`.`device_id` = `devices`.`device_id` WHERE `astext` LIKE '%".$search."%' OR `bgpPeerIdentifier` LIKE '%".$search."%' OR `bgpPeerRemoteAs` LIKE '%".$search."%' ORDER BY `astext` LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT `bgpPeers`.*,`D`.* FROM `bgpPeers`, `devices` AS `D`, `devices_perms` AS `P` WHERE `P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id` AND `bgpPeers`.`device_id`=`D`.`device_id` AND (`astext` LIKE '%".$search."%' OR `bgpPeerIdentifier` LIKE '%".$search."%' OR `bgpPeerRemoteAs` LIKE '%".$search."%') ORDER BY `astext` LIMIT 8", array($_SESSION['user_id'])); + $results = dbFetchRows("SELECT `bgpPeers`.*,`D`.* FROM `bgpPeers`, `devices` AS `D`, `devices_perms` AS `P` WHERE `P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id` AND `bgpPeers`.`device_id`=`D`.`device_id` AND (`astext` LIKE '%".$search."%' OR `bgpPeerIdentifier` LIKE '%".$search."%' OR `bgpPeerRemoteAs` LIKE '%".$search."%') ORDER BY `astext` LIMIT ".$limit, array($_SESSION['user_id'])); } if (count($results)) { @@ -209,10 +210,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'applications') { // Device search if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT * FROM `applications` INNER JOIN `devices` ON devices.device_id = applications.device_id WHERE `app_type` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%' ORDER BY hostname LIMIT 8"); + $results = dbFetchRows("SELECT * FROM `applications` INNER JOIN `devices` ON devices.device_id = applications.device_id WHERE `app_type` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%' ORDER BY hostname LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT * FROM `applications` INNER JOIN `devices` AS `D` ON `D`.`device_id` = `applications`.`device_id` INNER JOIN `devices_perms` AS `P` ON `P`.`device_id` = `D`.`device_id` WHERE `P`.`user_id` = ? AND (`app_type` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%') ORDER BY hostname LIMIT 8", array($_SESSION['user_id'])); + $results = dbFetchRows("SELECT * FROM `applications` INNER JOIN `devices` AS `D` ON `D`.`device_id` = `applications`.`device_id` INNER JOIN `devices_perms` AS `P` ON `P`.`device_id` = `D`.`device_id` WHERE `P`.`user_id` = ? AND (`app_type` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%') ORDER BY hostname LIMIT ".$limit, array($_SESSION['user_id'])); } if (count($results)) { @@ -255,10 +256,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'munin') { // Device search if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT * FROM `munin_plugins` INNER JOIN `devices` ON devices.device_id = munin_plugins.device_id WHERE `mplug_type` LIKE '%".$search."%' OR `mplug_title` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%' ORDER BY hostname LIMIT 8"); + $results = dbFetchRows("SELECT * FROM `munin_plugins` INNER JOIN `devices` ON devices.device_id = munin_plugins.device_id WHERE `mplug_type` LIKE '%".$search."%' OR `mplug_title` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%' ORDER BY hostname LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT * FROM `munin_plugins` INNER JOIN `devices` AS `D` ON `D`.`device_id` = `munin_plugins`.`device_id` INNER JOIN `devices_perms` AS `P` ON `P`.`device_id` = `D`.`device_id` WHERE `P`.`user_id` = ? AND (`mplug_type` LIKE '%".$search."%' OR `mplug_title` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%') ORDER BY hostname LIMIT 8", array($_SESSION['user_id'])); + $results = dbFetchRows("SELECT * FROM `munin_plugins` INNER JOIN `devices` AS `D` ON `D`.`device_id` = `munin_plugins`.`device_id` INNER JOIN `devices_perms` AS `P` ON `P`.`device_id` = `D`.`device_id` WHERE `P`.`user_id` = ? AND (`mplug_type` LIKE '%".$search."%' OR `mplug_title` LIKE '%".$search."%' OR `hostname` LIKE '%".$search."%') ORDER BY hostname LIMIT ".$limit, array($_SESSION['user_id'])); } if (count($results)) { @@ -301,10 +302,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'iftype') { // Device search if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT `ports`.ifType FROM `ports` WHERE `ifType` LIKE '%".$search."%' GROUP BY ifType ORDER BY ifType LIMIT 8"); + $results = dbFetchRows("SELECT `ports`.ifType FROM `ports` WHERE `ifType` LIKE '%".$search."%' GROUP BY ifType ORDER BY ifType LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT `I`.ifType FROM `ports` AS `I`, `devices` AS `D`, `devices_perms` AS `P`, `ports_perms` AS `PP` WHERE ((`P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id`) OR (`PP`.`user_id` = ? AND `PP`.`port_id` = `I`.`port_id` AND `I`.`device_id` = `D`.`device_id`)) AND `D`.`device_id` = `I`.`device_id` AND (`ifType` LIKE '%".$search."%') GROUP BY ifType ORDER BY ifType LIMIT 8", array($_SESSION['user_id'], $_SESSION['user_id'])); + $results = dbFetchRows("SELECT `I`.ifType FROM `ports` AS `I`, `devices` AS `D`, `devices_perms` AS `P`, `ports_perms` AS `PP` WHERE ((`P`.`user_id` = ? AND `P`.`device_id` = `D`.`device_id`) OR (`PP`.`user_id` = ? AND `PP`.`port_id` = `I`.`port_id` AND `I`.`device_id` = `D`.`device_id`)) AND `D`.`device_id` = `I`.`device_id` AND (`ifType` LIKE '%".$search."%') GROUP BY ifType ORDER BY ifType LIMIT ".$limit, array($_SESSION['user_id'], $_SESSION['user_id'])); } if (count($results)) { $found = 1; @@ -323,10 +324,10 @@ if (isset($_REQUEST['search'])) { else if ($_REQUEST['type'] == 'bill') { // Device search if (is_admin() === true || is_read() === true) { - $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` WHERE `bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%' LIMIT 8"); + $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` WHERE `bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%' LIMIT ".$limit); } else { - $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` INNER JOIN `bill_perms` ON `bills`.bill_id = `bill_perms`.bill_id WHERE `bill_perms`.user_id = ? AND (`bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%') LIMIT 8", array($_SESSION['user_id'])); + $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` INNER JOIN `bill_perms` ON `bills`.bill_id = `bill_perms`.bill_id WHERE `bill_perms`.user_id = ? AND (`bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%') LIMIT ".$limit, array($_SESSION['user_id'])); } $json = json_encode($results); die($json); diff --git a/html/css/styles.css b/html/css/styles.css index ead23f4072..176c7da147 100644 --- a/html/css/styles.css +++ b/html/css/styles.css @@ -488,7 +488,7 @@ a.list-device-disabled, a.list-device-disabled:visited { } .box { - width: 350px; + width: 500px; height: 300px; padding: 0px; vertical-align: center; diff --git a/html/images/os/avtech.png b/html/images/os/avtech.png new file mode 100644 index 0000000000..3f7850cd8d Binary files /dev/null and b/html/images/os/avtech.png differ diff --git a/html/includes/common/alerts.inc.php b/html/includes/common/alerts.inc.php index 44495f347a..db87ba82dd 100644 --- a/html/includes/common/alerts.inc.php +++ b/html/includes/common/alerts.inc.php @@ -156,7 +156,7 @@ else {
- +
@@ -171,7 +171,7 @@ else {
Status
- \ No newline at end of file + diff --git a/lib/vis/docs/data/dataview.html b/lib/vis/docs/data/dataview.html index 1b1246b3d4..9a98f7f2ed 100644 --- a/lib/vis/docs/data/dataview.html +++ b/lib/vis/docs/data/dataview.html @@ -248,7 +248,7 @@ var data = new vis.DataView(dataset, options) Object | Array Get a single item, multiple items, or all items from the DataView. - Usage examples can be found in section Getting Data, and the available options are described in section Data Selection. + Usage examples can be found in section Getting Data, and the available options are described in section Data Selection. When no item is found, null is returned when a single item was requested, and and empty Array is returned in case of multiple id's. @@ -307,7 +307,7 @@ var view = new vis.DataView(data, { - setDataSet(data) + setData(data) none diff --git a/lib/vis/docs/graph2d/index.html b/lib/vis/docs/graph2d/index.html index d0edebd176..abea895d9e 100644 --- a/lib/vis/docs/graph2d/index.html +++ b/lib/vis/docs/graph2d/index.html @@ -246,8 +246,6 @@

Graph2d can load data from an Array, a DataSet (offering 2 way data binding), or a DataView (offering one way data binding). Objects are added to this DataSet by using the add() function. - Data points must have properties x, y, and z, - and can optionally have a property style and filter.

Graph2d can be provided with two types of data:

@@ -383,8 +381,8 @@ groups.add({ visible Boolean - true - This field is optional. If false, this group will not be drawn. + no + This field is optional. If false, this group will not be drawn. By default it is true. @@ -436,7 +434,7 @@ var options = { barChart.sideBySide Boolean false - If two datapoints of a barchart overlap, they are drawn over eachother by default. If sideBySide is set to true, they will be drawn side by side. + If two datapoints of a barchart overlap, they are drawn over eachother by default. If sideBySide is set to true, they will be drawn side by side, within the same width as a single bar.. See example 10 for more information. When using groups, see example 11. @@ -447,7 +445,12 @@ var options = { 50 The width of the bars. - + + barChart.minWidth + Number + + The minimum width of the bars in pixels: by default the bars get smaller while zooming out to prevent overlap, this value is the minimum width of the bar. Default behavior (when minWidth is not set) is 10% of the bar width. + dataAxis Object @@ -472,12 +475,11 @@ var options = { Function Insert a custom function on how to format the label. The function will receive a numeric value and has to return a string. Default function is: -
+            
 function (value) {
-  return value;
-}
-            
- which does nothing to it. + return ''+value.toPrecision(3); +}
+ dataAxis.left.range.min @@ -578,7 +580,7 @@ drawPoints: { 3. Function: If a function is provided it will be used as a callback. The function may return values from listing 1 or 2.
-drawPoints: function(item, group, graph2d) {
+drawPoints: function(item, group) {
   ...
 }
             
@@ -743,7 +745,7 @@ onRender: function(item, group, graph2d) { stack Boolean true - If stack is enabled, the graphs will be stacked upon eachother when applicable. This currently only works with bar graphs but linegraph support is being worked on. + If stack is enabled, the graphs will be stacked upon each-other when applicable. A group can opt-out of stacking through the "excludeFromStacking" option. shaded @@ -761,7 +763,17 @@ onRender: function(item, group, graph2d) { shaded.orientation String 'bottom' - This determines if the shaded area is at the bottom or at the top of the curve. The options are 'bottom' or 'top'. + This determines if the shaded area is at the bottom or at the top of the curve, or always towards the zero-axis of the graph. + The options are 'zero', 'bottom', 'top', or the special case of 'group'. If group is chosen, the option groupId is required. + See Example 20 what these options look like. + + + + shaded.groupId + String + undefined + The id of the group which should be used as the other shading limit. + shaded.style @@ -782,6 +794,18 @@ onRender: function(item, group, graph2d) { This defines with which axis, left or right, the graph is coupled. Example 5 shows groups with different Y axis. If no groups are coupled with an axis, it will not be shown. + + excludeFromLegend + Boolean + false + Group option only. Excludes the group from being listed in the legend. + + + excludeFromStacking + Boolean + false + Group option only. Excludes the group from being included in the stacking. +

Timeline Options

@@ -895,14 +919,14 @@ function (option, path) { locale String none - Select a locale for the Graph2d. See section Localization for more information. + Select a locale for the Graph2d. See section Localization for more information. locales Object none - A map with i18n locales. See section Localization for more information. + A map with i18n locales. See section Localization for more information. @@ -921,6 +945,16 @@ function (option, path) { Specifies the maximum height for the Timeline. Can be a number in pixels or a string like "300px". + + maxMinorChars + number + 7 + + Specifies the maximum number of characters that should fit in minor grid labels. + If larger, less and wider grids will be drawn. + + + min Date or Number or String @@ -991,22 +1025,6 @@ function (option, path) { visible. - - showMajorLines - Boolean - true - By default, the timeline shows both minor and major date lines on the - time axis. You can use this option to hide the lines from the major dates. - - - - showMinorLines - Boolean - true - By default, the timeline shows both minor and major date lines on the - time axis. You can use this option to hide the lines from the minor dates. - - start Date or Number or String @@ -1317,16 +1335,14 @@ Graph2d.off('rangechanged', onChange); Fired when double clicked inside the Graph2d. - - finishedRedraw + changed - none. + Has no properties. - Fired after a redraw is complete. When moving the Graph2d around, this could be fired frequently. + Fired once after each graph redraw. - rangechange @@ -1350,6 +1366,7 @@ Graph2d.off('rangechanged', onChange); Fired once after the user has dragged the Graph2d window. + timechange @@ -1486,4 +1503,4 @@ var options = { - \ No newline at end of file + diff --git a/lib/vis/docs/graph3d/index.html b/lib/vis/docs/graph3d/index.html index 43a5e2c805..c0e604a761 100644 --- a/lib/vis/docs/graph3d/index.html +++ b/lib/vis/docs/graph3d/index.html @@ -379,6 +379,13 @@ var options = { The line width of dots, bars and lines. Applicable for all styles. + + dotSizeRatio + number + 0.02 + Ratio of the size of the dots with respect to the width of the graph. + + gridColor string diff --git a/lib/vis/docs/network/edges.html b/lib/vis/docs/network/edges.html index c3ecdf093e..42c132ac0f 100644 --- a/lib/vis/docs/network/edges.html +++ b/lib/vis/docs/network/edges.html @@ -119,6 +119,7 @@ var options = { middle: {enabled: false, scaleFactor:1}, from: {enabled: false, scaleFactor:1} }, + arrowStrikethrough: true, color: { color:'#848484', highlight:'#848484', @@ -166,6 +167,7 @@ var options = { selfReferenceSize:20, shadow:{ enabled: false, + color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 @@ -253,6 +255,12 @@ network.setOptions(options); Object Exactly the same as the to object but with an arrowhead at the from node of the edge. + + arrowStrikethrough + Boolean + true + When false, the edge stops at the arrow. This can be useful if you have thick lines and you want the arrow to end in a point. Middle arrows are not affected by this. + color Object or String @@ -581,6 +589,12 @@ var options: { in this object are defined. + + shadow.color + String + 'rgba(0,0,0,0.5)' + The color size of the shadow as a string. Supported formats are 'rgb(255,255,255)', 'rgba(255,255,255,1)' and '#FFFFFF'. + shadow.size Number diff --git a/lib/vis/docs/network/index.html b/lib/vis/docs/network/index.html index 041dbb9012..4fe58dfb49 100644 --- a/lib/vis/docs/network/index.html +++ b/lib/vis/docs/network/index.html @@ -323,7 +323,7 @@ network.setOptions(options); locales Object defaultLocales - Locales object. By default only 'en' and 'nl' are supported. Take a look + Locales object. By default only 'en', 'de', 'es' and 'nl' are supported. Take a look at the locales @@ -333,11 +333,7 @@ network.setOptions(options); clickToUse Boolean false - Locales object. By default only 'en' and 'nl' are supported. Take a look - at - the locales - section below for more explaination on how to customize this. + When a Network is configured to be clickToUse, it will react to mouse and touch events only when active. When active, a blue shadow border is displayed around the Network. The network is set active by clicking on it, and is changed to inactive again by clicking outside the Network or by pressing the ESC key. configure @@ -944,6 +940,28 @@ function releaseFunction (clusterPosition, containedNodesPositions) { before selecting its own objects. Does not fire events. + + setSelection( + Object selection, + [Object options]) + + + Returns: none + Sets the selection, wich must be an object like this: +
+{
+  nodes: [Array of nodeIds],
+  edges: [Array of edgeIds]
+}
+ You can also pass only nodes or edges in selection object. + Available options are: +
+{
+  unselectAll: Boolean,
+  highlightEdges: Boolean
+}
+ + unselectAll() @@ -968,8 +986,8 @@ function releaseFunction (clusterPosition, containedNodesPositions) { getViewPosition() - Returns: Number - Returns the current central focus point of the view. + Returns: Object + Returns the current central focus point of the view in the form: { x: {Number}, y: {Number} } @@ -1301,19 +1319,19 @@ var options = { hoverNode {node: nodeId} - Fired interaction:{hover:true} and the mouse hovers over a node. + Fired if the option interaction:{hover:true} is enabled and the mouse hovers over a node. blurNode {node: nodeId} - Fired interaction:{hover:true} and the mouse moved away from a node it was hovering over before. + Fired if the option interaction:{hover:true} is enabled and the mouse moved away from a node it was hovering over before. hoverEdge {edge: edgeId} - Fired interaction:{hover:true} and the mouse hovers over an edge. + Fired if the option interaction:{hover:true} is enabled and the mouse hovers over an edge. blurEdge {edge: edgeId} - Fired interaction:{hover:true} and the mouse moved away from an edge it was hovering over before. + Fired if the option interaction:{hover:true} is enabled and the mouse moved away from an edge it was hovering over before. zoom {direction:'+'/'-', scale: Number} @@ -1557,4 +1575,4 @@ var network = new vis.Network(container, data, options); - \ No newline at end of file + diff --git a/lib/vis/docs/network/layout.html b/lib/vis/docs/network/layout.html index ea33c7ddb9..08f1eca982 100644 --- a/lib/vis/docs/network/layout.html +++ b/lib/vis/docs/network/layout.html @@ -101,11 +101,16 @@ var options = { layout: { randomSeed: undefined, + improvedLayout:true, hierarchical: { enabled:false, levelSeparation: 150, - direction: 'UD', // UD, DU, LR, RL - sortMethod: 'hubsize' // hubsize, directed + nodeSpacing: 100, + treeSpacing: 200, + blockShifting: true, + edgeMinimization: true, + direction: 'UD', // UD, DU, LR, RL + sortMethod: 'hubsize' // hubsize, directed } } } @@ -127,9 +132,16 @@ network.setOptions(options); + + + + + + This example code will show a popup if you connect a node to itself to ask you if that was what you wanted. If you do not want the edge created, do not call the callback function or call the callback function null or no argument. - + - - + + @@ -374,7 +377,9 @@ network.setOptions(options); - + @@ -581,6 +586,12 @@ mySize = minSize + diff * scale; in this object are defined. + + + + + + @@ -642,6 +653,15 @@ mySize = minSize + diff * scale; if this is set to true, the image cannot be scaled with the value option! + + + + + + @@ -704,4 +724,4 @@ mySize = minSize + diff * scale; - \ No newline at end of file + diff --git a/lib/vis/docs/network/physics.html b/lib/vis/docs/network/physics.html index cb9acb076b..c8fe992081 100644 --- a/lib/vis/docs/network/physics.html +++ b/lib/vis/docs/network/physics.html @@ -138,7 +138,8 @@ var options = { onlyDynamicEdges: false, fit: true }, - timestep: 0.5 + timestep: 0.5, + adaptiveTimestep: true } } @@ -201,6 +202,7 @@ network.setOptions(options); +
NameTypeDefaultDescription
randomSeedNumberundefined When NOT using the hierarchical layout, the nodes are randomly positioned initially. This means that the settled result is different every time. If you provide a random seed manually, the layout will be the same every time. Ideally you try with an undefined seed, reload until you are happy with the layout and use the getSeed() method to ascertain the seed.
improvedLayoutBooleantrue When enabled, the network will use the Kamada Kawai algorithm for initial layout. For networks larger than 100 nodes, clustering will be performed automatically to reduce the amount of nodes. This can greatly improve the stabilization times. If the network is very interconnected (no or few leaf nodes), this may not work and it will revert back to the old method. Performance will be improved in the future.
hierarchicalObject or BooleanObject When true, the layout engine positions the nodes in a hierarchical fashion using default settings. For customization you can supply an object.
editNode Function undefined Editing of nodes is only possible when a handling function is supplied. If this is not the case, editing of nodes will be disabled. The function will be called when a node is selected and the 'Edit Node' button on the toolbar is pressed. This function will be called like the addNode function with the node's data and a callback function.
editEdge Boolean or Function true If boolean, toggle the editing of edges in the GUI. When a function is supplied, it will be called when an edge is selcted and the 'Edit Edge' button on the toolbar is pressed. This function will be called in the same way the addEdge function was called. If the callback is not performed, the edge will remain hanging where it was released. To cancel, call the callback function with null as argument or without arguments.
editEdge Boolean or Function true If boolean, toggle the editing of edges in the GUI. When a function is supplied, it will be called when an edge is selected and the 'Edit Edge' button on the toolbar is pressed. This function will be called in the same way the addEdge function was called. If the callback is not performed, the edge will remain hanging where it was released. To cancel, call the callback function with null as argument or without arguments.
deleteNode Boolean or Function true If boolean, toggle the deletion of nodes in the GUI. If function, it will be called when a node is selected and the 'Delete selected' button is pressed. When using a function, it will receive a callback and an object with an array of selected nodeIds and an array of selected edges Ids. These are the items that will be deleted if the callback is performed.
deleteEdge Boolean or Function true If boolean, toggle the deletion of edges in the GUI. If function, it will be called when an edge is selected and the 'Delete selected' button is pressed. When using a function, it will receive a callback and an object with an array of selected nodeIds (empty) and an array of selected edges Ids. These are the items that will be deleted if the callback is performed.
controlNodeStyle Object ObjectYou can supply any styling information you'd like here. All fields described in the nodes module are allowed except obviously for id, x, y and fixed.

Default: diff --git a/lib/vis/docs/network/nodes.html b/lib/vis/docs/network/nodes.html index 83c00b7eb3..146a2233dd 100644 --- a/lib/vis/docs/network/nodes.html +++ b/lib/vis/docs/network/nodes.html @@ -93,7 +93,7 @@

Options

The options for the nodes have to be contained in an object titled 'nodes'. All of these options can be supplied per node as well. Obviously, 'id' should not be defined globally but per node. Options defined in the global nodes object, are applied to all nodes. If a node has options of its own, those will be used instead of the global options.

-

When you have given a node an option, you will override the global option for that property. If you then set that option to null, +

When you have given a node an option, you will override the global option for that property, and also the group option for that property if the node is in a group. If you then set that option to null, it will revert back to the default value.

Click on the full options or shorthand options to show how these options are supposed to be used.

@@ -108,7 +108,7 @@ var options = { nodes:{ borderWidth: 1, - borderWidthSelected: undefined, + borderWidthSelected: 2, brokenImage:undefined, color: { border: '#2B7CE9', @@ -171,14 +171,17 @@ var options = { }, shadow:{ enabled: false, + color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 }, shape: 'ellipse', shapeProperties: { - borderDashes: false, // only for shapes with a border - borderRadius: 6 // only for box shape + borderDashes: false, // only for borders + borderRadius: 6, // only for box shape + useImageSize: false, // only for image and circularImage shapes + useBorderWithImage: false // only for image shape } size: 25, title: undefined, @@ -224,8 +227,8 @@ network.setOptions(options);
borderWidthSelected NumberundefinedThe width of the border of the node when it is selected. When undefined, the borderWidth is used2The width of the border of the node when it is selected. When undefined, the borderWidth * 2 is used.
brokenImagegroup String undefinedWhen not undefined, theWhen not undefined, the node will belong to the defined group. Styling information of + that group will apply to this node. Node specific styling overrides group styling. +
hidden
size Number
timestep Number 0.5 The physics simulation is discrete. This means we take a step in time, calculate the forces, move the nodes and take another step. If you increase this number the steps will be too large and the network can get unstable. If you see a lot of jittery movement in the network, you may want to reduce this value a little.
adaptiveTimestep Boolean true If this is enabled, the timestep will intelligently be adapted (only during the stabilization stage if stabilization is enabled!) to greatly decrease stabilization times. The timestep configured above is taken as the minimum timestep. This can be further improved by using the improvedLayout algorithm.
diff --git a/lib/vis/docs/timeline/index.html b/lib/vis/docs/timeline/index.html index 70f82050e9..83f9afe543 100644 --- a/lib/vis/docs/timeline/index.html +++ b/lib/vis/docs/timeline/index.html @@ -115,7 +115,7 @@

Example

The following code shows how to create a Timeline and provide it with data. - More examples can be found in the examples directory. + More examples can be found in the timeline examples page.

<!DOCTYPE HTML>
@@ -588,12 +588,51 @@ function (option, path) {
       
     
 
+	
+       groupEditable
+      boolean or Object
+      false
+      If true, the groups in the timeline can be manipulated. See also the callbacks onAddGroup, onMoveGroup, and onRemoveGroup. When groupEditable is an object, one can enable or disable individual manipulation actions.
+        The editing of groups follows the same principles as for items, see section Editing Items for a detailed explanation.
+      
+    
+    
+      groupEditable.add
+      boolean
+      false
+      If true, new groups can be created in the Timeline. For now adding new groups is done by the user.
+    
+    
+      groupEditable.remove
+      boolean
+      false
+      If true, groups can be deleted. For now removing groups is done by the user.
+    
+    
+      groupEditable.order
+      boolean
+      false
+      If true, groups can be dragged to change their order. Only applicable when the Timeline has groups. For this option to work properly the groupOrder and groupOrderSwap options have to be set as well.
+    
+
     
       groupOrder
       String or Function
-      none
+      'order'
       Order the groups by a field name or custom sort function.
-        By default, groups are not ordered.
+        By default, groups are ordered by a property order (if set).
+        If no order properties are provided, the order will be undetermined.
+      
+    
+    
+    
+      groupOrderSwap
+      Function
+      none
+      Swaps the positions of two groups. If groups have a custom order (via groupOrder) and groups are configured to be reorderable (via groupEditable.order), the user has to provide a function that swaps the positions of two given groups.
+      If this option is not set, the default implementation assumes that groups hold an attribute order which values are changed. The signature of the groupOrderWap function is:
+      
function groupOrderSwap(fromGroup: Object, toGroup: Object, groups: DataSet)
+ The first to arguments hold the groups of which the positions are to be swapped and the third argument holds the DataSet with all groups. @@ -627,6 +666,14 @@ function (option, path) { and set repeat to weekly. + + + itemsAlwaysDraggable + boolean + false + If true, all items in the Timeline are draggable without being selected. If false, only the selected item(s) are draggable. + + locale String @@ -695,6 +742,16 @@ function (option, path) { Specifies the maximum height for the Timeline. Can be a number in pixels or a string like "300px". + + maxMinorChars + number + 7 + + Specifies the maximum number of characters that should fit in minor grid labels. + If larger, less and wider grids will be drawn. + + + min Date or Number or String or Moment @@ -730,6 +787,16 @@ function (option, path) { Only applicable when option selectable is true. + + + multiselectPerGroup + boolean + false + + If true, selecting multiple items using shift+click will only select items residing in the same group as the first selected item. + Only applicable when option selectable and multiselect are true. + + onAdd @@ -738,6 +805,14 @@ function (option, path) { Callback function triggered when an item is about to be added: when the user double taps an empty space in the Timeline. See section Editing Items for more information. Only applicable when both options selectable and editable.add are set true. + + + onAddGroup + function + none + Callback function triggered when a group is about to be added. The signature and semantics are the same as for onAdd. + + onUpdate @@ -754,6 +829,14 @@ function (option, path) { Callback function triggered when an item has been moved: after the user has dragged the item to an other position. See section Editing Items for more information. Only applicable when both options selectable and editable.updateTime or editable.updateGroup are set true. + + + onMoveGroup + function + none + Callback function triggered when a group has been moved: after the user has dragged the group to an other position. The signature and semantics are the same as for onMove. + + onMoving @@ -770,6 +853,14 @@ function (option, path) { Callback function triggered when an item is about to be removed: when the user tapped the delete button on the top right of a selected item. See section Editing Items for more information. Only applicable when both options selectable and editable.remove are set true. + + + onRemoveGroup + function + none + Callback function triggered when a group is about to be removed. The signature and semantics are the same as for onRemove. + + order @@ -876,6 +967,13 @@ function (option, path) { A template function used to generate the contents of the items. The function is called by the Timeline with an items data as argument, and must return HTML code as result. When the option template is specified, the items do not need to have a field content. See section Templates for a detailed explanation. + + throttleRedraw + number + 0 + Limit the maximum number of redraws to once every x milliseconds. For example setting throttleRedraw to `100` milliseconds will limit the number of redraws to 10 times per second. + + timeAxis Object @@ -892,6 +990,7 @@ function (option, path) { }
+ timeAxis.step number @@ -1125,6 +1224,15 @@ document.getElementById('myTimeline').onclick = function (event) { + + setCustomTimeTitle(title [, id]) + none + Adjust the title attribute of a custom time bar. + Parameter title is the string to be set as title. Use empty string to hide the title completely. + Parameter id is the id of the custom time bar, and is undefined by default. + + + setData({
  groups: groups,
  items: items
}) none @@ -1266,6 +1374,23 @@ timeline.off('select', onSelect); + + groupDragged + + Passes the id of the dragged group. + + Fired after the dragging of a group is finished. + + + + + changed + + Has no properties. + + Fired once after each graph redraw. + + rangechange @@ -1412,7 +1537,7 @@ var items = new vis.DataSet([ }; - A full example is available here: 08_manipulation_callbacks.html. + A full example is available here: editingItemsCallbacks.html.

Templates

diff --git a/lib/vis/examples/graph2d/06_interpolation.html b/lib/vis/examples/graph2d/06_interpolation.html index 47b31d69ee..a6ddc53be7 100644 --- a/lib/vis/examples/graph2d/06_interpolation.html +++ b/lib/vis/examples/graph2d/06_interpolation.html @@ -88,7 +88,7 @@ } var options = { - dataPoints: false, + drawPoints: false, dataAxis: {visible: false}, legend: true, start: '2014-06-11', diff --git a/lib/vis/examples/graph2d/17_dynamicStyling.html b/lib/vis/examples/graph2d/17_dynamicStyling.html index 665f0636ce..414313f516 100644 --- a/lib/vis/examples/graph2d/17_dynamicStyling.html +++ b/lib/vis/examples/graph2d/17_dynamicStyling.html @@ -79,7 +79,8 @@ @@ -98,16 +99,16 @@ Fill Opacity @@ -187,7 +188,7 @@ {x: '2014-06-11', y: 10, group: 0}, {x: '2014-06-12', y: 25, group: 0}, {x: '2014-06-13', y: 30, group: 0}, - {x: '2014-06-14', y: 10, group: 0}, + {x: '2014-06-14', y: -10, group: 0}, {x: '2014-06-15', y: 15, group: 0}, {x: '2014-06-16', y: 30, group: 0} ]; @@ -210,7 +211,7 @@ style: 'square' // square, circle }, shaded: { - orientation: 'bottom' // top, bottom + orientation: 'zero' // top, bottom } } }; diff --git a/lib/vis/examples/graph2d/20_shading.html b/lib/vis/examples/graph2d/20_shading.html new file mode 100644 index 0000000000..1fc0980c01 --- /dev/null +++ b/lib/vis/examples/graph2d/20_shading.html @@ -0,0 +1,117 @@ + + + + Graph2d | Shading Example + + + + + + + + +

Graph2d | Shading Example

+
+ This example shows the shading functionality within Graph2d. +
+
+ +
+ + + + diff --git a/lib/vis/examples/network/datasources/largeHierarchicalDataset.js b/lib/vis/examples/network/datasources/largeHierarchicalDataset.js new file mode 100644 index 0000000000..ddbb524e76 --- /dev/null +++ b/lib/vis/examples/network/datasources/largeHierarchicalDataset.js @@ -0,0 +1,2 @@ +var nodes = [{id:0,label:0},{id:1,label:1},{id:2,label:2},{id:3,label:3},{id:4,label:4},{id:5,label:5},{id:6,label:6},{id:7,label:7},{id:8,label:8},{id:9,label:9},{id:10,label:10},{id:11,label:11},{id:12,label:12},{id:13,label:13},{id:14,label:14},{id:15,label:15},{id:16,label:16},{id:17,label:17},{id:18,label:18},{id:19,label:19},{id:20,label:20},{id:21,label:21},{id:22,label:22},{id:23,label:23},{id:24,label:24},{id:25,label:25},{id:26,label:26},{id:27,label:27},{id:28,label:28},{id:29,label:29},{id:30,label:30},{id:31,label:31},{id:32,label:32},{id:33,label:33},{id:34,label:34},{id:35,label:35},{id:36,label:36},{id:37,label:37},{id:38,label:38},{id:39,label:39},{id:40,label:40},{id:41,label:41},{id:42,label:42},{id:43,label:43},{id:44,label:44},{id:45,label:45},{id:46,label:46},{id:47,label:47},{id:48,label:48},{id:49,label:49},{id:50,label:50},{id:51,label:51},{id:52,label:52},{id:53,label:53},{id:54,label:54},{id:55,label:55},{id:56,label:56},{id:57,label:57},{id:58,label:58},{id:59,label:59},{id:60,label:60},{id:61,label:61},{id:62,label:62},{id:63,label:63},{id:64,label:64},{id:65,label:65},{id:66,label:66},{id:67,label:67},{id:68,label:68},{id:69,label:69},{id:70,label:70},{id:71,label:71},{id:72,label:72},{id:73,label:73},{id:74,label:74},{id:75,label:75},{id:76,label:76},{id:77,label:77},{id:78,label:78},{id:79,label:79},{id:80,label:80},{id:81,label:81},{id:82,label:82},{id:83,label:83},{id:84,label:84},{id:85,label:85},{id:86,label:86},{id:87,label:87},{id:88,label:88},{id:89,label:89},{id:90,label:90},{id:91,label:91},{id:92,label:92},{id:93,label:93},{id:94,label:94},{id:95,label:95},{id:96,label:96},{id:97,label:97},{id:98,label:98},{id:99,label:99},{id:100,label:100},{id:101,label:101},{id:102,label:102},{id:103,label:103},{id:104,label:104},{id:105,label:105},{id:106,label:106},{id:107,label:107},{id:108,label:108},{id:109,label:109},{id:110,label:110},{id:111,label:111},{id:112,label:112},{id:113,label:113},{id:114,label:114},{id:115,label:115},{id:116,label:116},{id:117,label:117},{id:118,label:118},{id:119,label:119},{id:120,label:120},{id:121,label:121},{id:122,label:122},{id:123,label:123},{id:124,label:124},{id:125,label:125},{id:126,label:126},{id:127,label:127},{id:128,label:128},{id:129,label:129},{id:130,label:130},{id:131,label:131},{id:132,label:132},{id:133,label:133},{id:134,label:134},{id:135,label:135},{id:136,label:136},{id:137,label:137},{id:138,label:138},{id:139,label:139},{id:140,label:140},{id:141,label:141},{id:142,label:142},{id:143,label:143},{id:144,label:144},{id:145,label:145},{id:146,label:146},{id:147,label:147},{id:148,label:148},{id:149,label:149},{id:150,label:150},{id:151,label:151},{id:152,label:152},{id:153,label:153},{id:154,label:154},{id:155,label:155},{id:156,label:156},{id:157,label:157},{id:158,label:158},{id:159,label:159},{id:160,label:160},{id:161,label:161},{id:162,label:162},{id:163,label:163},{id:164,label:164},{id:165,label:165},{id:166,label:166},{id:167,label:167},{id:168,label:168},{id:169,label:169},{id:170,label:170},{id:171,label:171},{id:172,label:172},{id:173,label:173},{id:174,label:174},{id:175,label:175},{id:176,label:176},{id:177,label:177},{id:178,label:178},{id:179,label:179},{id:180,label:180},{id:181,label:181},{id:182,label:182},{id:183,label:183},{id:184,label:184},{id:185,label:185},{id:186,label:186},{id:187,label:187},{id:188,label:188},{id:189,label:189},{id:190,label:190},{id:191,label:191},{id:192,label:192},{id:193,label:193},{id:194,label:194},{id:195,label:195},{id:196,label:196},{id:197,label:197},{id:198,label:198},{id:199,label:199},{id:200,label:200},{id:201,label:201},{id:202,label:202},{id:203,label:203},{id:204,label:204},{id:205,label:205},{id:206,label:206},{id:207,label:207},{id:208,label:208},{id:209,label:209},{id:210,label:210},{id:211,label:211},{id:212,label:212},{id:213,label:213},{id:214,label:214},{id:215,label:215},{id:216,label:216},{id:217,label:217},{id:218,label:218},{id:219,label:219},{id:220,label:220},{id:221,label:221},{id:222,label:222},{id:223,label:223},{id:224,label:224},{id:225,label:225},{id:226,label:226},{id:227,label:227},{id:228,label:228},{id:229,label:229},{id:230,label:230},{id:231,label:231},{id:232,label:232},{id:233,label:233},{id:234,label:234},{id:235,label:235},{id:236,label:236},{id:237,label:237},{id:238,label:238},{id:239,label:239},{id:240,label:240},{id:241,label:241},{id:242,label:242},{id:243,label:243},{id:244,label:244},{id:245,label:245},{id:246,label:246},{id:247,label:247},{id:248,label:248},{id:249,label:249},{id:250,label:250},{id:251,label:251},{id:252,label:252},{id:253,label:253},{id:254,label:254},{id:255,label:255},{id:256,label:256},{id:257,label:257},{id:258,label:258},{id:259,label:259},{id:260,label:260},{id:261,label:261},{id:262,label:262},{id:263,label:263},{id:264,label:264},{id:265,label:265},{id:266,label:266},{id:267,label:267},{id:268,label:268},{id:269,label:269},{id:270,label:270},{id:271,label:271},{id:272,label:272},{id:273,label:273},{id:274,label:274},{id:275,label:275},{id:276,label:276},{id:277,label:277},{id:278,label:278},{id:279,label:279},{id:280,label:280},{id:281,label:281},{id:282,label:282},{id:283,label:283},{id:284,label:284},{id:285,label:285},{id:286,label:286},{id:287,label:287},{id:288,label:288},{id:289,label:289},{id:290,label:290},{id:291,label:291},{id:292,label:292},{id:293,label:293},{id:294,label:294},{id:295,label:295},{id:296,label:296},{id:297,label:297},{id:298,label:298},{id:299,label:299},{id:300,label:300},{id:301,label:301},{id:302,label:302},{id:303,label:303},{id:304,label:304},{id:305,label:305},{id:306,label:306},{id:307,label:307},{id:308,label:308},{id:309,label:309},{id:310,label:310},{id:311,label:311},{id:312,label:312},{id:313,label:313},{id:314,label:314},{id:315,label:315},{id:316,label:316},{id:317,label:317},{id:318,label:318},{id:319,label:319},{id:320,label:320},{id:321,label:321},{id:322,label:322},{id:323,label:323},{id:324,label:324},{id:325,label:325},{id:326,label:326},{id:327,label:327},{id:328,label:328},{id:329,label:329},{id:330,label:330},{id:331,label:331},{id:332,label:332},{id:333,label:333},{id:334,label:334},{id:335,label:335},{id:336,label:336},{id:337,label:337},{id:338,label:338},{id:339,label:339},{id:340,label:340},{id:341,label:341},{id:342,label:342},{id:343,label:343},{id:344,label:344},{id:345,label:345},{id:346,label:346},{id:347,label:347}]; +var edges = [{from:331,to:0,id:"e0"},{from:331,to:1,id:"e1"},{from:302,to:2,id:"e2"},{from:321,to:3,id:"e3"},{from:323,to:4,id:"e4"},{from:326,to:5,id:"e5"},{from:24,to:6,id:"e6"},{from:327,to:7,id:"e7"},{from:50,to:8,id:"e8"},{from:275,to:9,id:"e9"},{from:327,to:10,id:"e10"},{from:30,to:11,id:"e11"},{from:327,to:12,id:"e12"},{from:270,to:13,id:"e13"},{from:204,to:14,id:"e14"},{from:42,to:15,id:"e15"},{from:140,to:16,id:"e16"},{from:327,to:17,id:"e17"},{from:80,to:18,id:"e18"},{from:24,to:19,id:"e19"},{from:60,to:20,id:"e20"},{from:323,to:21,id:"e21"},{from:328,to:22,id:"e22"},{from:58,to:23,id:"e23"},{from:0,to:24,id:"e24"},{from:50,to:25,id:"e25"},{from:50,to:26,id:"e26"},{from:36,to:27,id:"e27"},{from:36,to:28,id:"e28"},{from:303,to:29,id:"e29"},{from:1,to:30,id:"e30"},{from:326,to:31,id:"e31"},{from:81,to:32,id:"e32"},{from:60,to:33,id:"e33"},{from:62,to:34,id:"e34"},{from:24,to:35,id:"e35"},{from:319,to:36,id:"e36"},{from:58,to:37,id:"e37"},{from:58,to:38,id:"e38"},{from:80,to:39,id:"e39"},{from:35,to:40,id:"e40"},{from:73,to:41,id:"e41"},{from:327,to:42,id:"e42"},{from:301,to:43,id:"e43"},{from:36,to:44,id:"e44"},{from:322,to:45,id:"e45"},{from:69,to:46,id:"e46"},{from:329,to:47,id:"e47"},{from:199,to:48,id:"e48"},{from:321,to:49,id:"e49"},{from:331,to:50,id:"e50"},{from:70,to:51,id:"e51"},{from:329,to:52,id:"e52"},{from:140,to:53,id:"e53"},{from:1,to:54,id:"e54"},{from:330,to:55,id:"e55"},{from:304,to:56,id:"e56"},{from:214,to:57,id:"e57"},{from:84,to:58,id:"e58"},{from:141,to:59,id:"e59"},{from:36,to:60,id:"e60"},{from:323,to:61,id:"e61"},{from:326,to:62,id:"e62"},{from:323,to:63,id:"e63"},{from:328,to:64,id:"e64"},{from:331,to:65,id:"e65"},{from:140,to:66,id:"e66"},{from:24,to:67,id:"e67"},{from:324,to:68,id:"e68"},{from:326,to:69,id:"e69"},{from:323,to:70,id:"e70"},{from:283,to:71,id:"e71"},{from:27,to:72,id:"e72"},{from:50,to:73,id:"e73"},{from:325,to:74,id:"e74"},{from:58,to:75,id:"e75"},{from:323,to:76,id:"e76"},{from:15,to:77,id:"e77"},{from:70,to:78,id:"e78"},{from:22,to:79,id:"e79"},{from:328,to:80,id:"e80"},{from:0,to:81,id:"e81"},{from:322,to:82,id:"e82"},{from:326,to:83,id:"e83"},{from:325,to:84,id:"e84"},{from:331,to:85,id:"e85"},{from:184,to:86,id:"e86"},{from:250,to:87,id:"e87"},{from:321,to:88,id:"e88"},{from:322,to:89,id:"e89"},{from:326,to:90,id:"e90"},{from:162,to:91,id:"e91"},{from:162,to:92,id:"e92"},{from:99,to:93,id:"e93"},{from:320,to:94,id:"e94"},{from:326,to:95,id:"e95"},{from:0,to:96,id:"e96"},{from:326,to:97,id:"e97"},{from:327,to:98,id:"e98"},{from:0,to:99,id:"e99"},{from:327,to:100,id:"e100"},{from:0,to:101,id:"e101"},{from:0,to:102,id:"e102"},{from:328,to:103,id:"e103"},{from:256,to:104,id:"e104"},{from:326,to:105,id:"e105"},{from:81,to:106,id:"e106"},{from:322,to:107,id:"e107"},{from:326,to:108,id:"e108"},{from:8,to:109,id:"e109"},{from:204,to:110,id:"e110"},{from:163,to:111,id:"e111"},{from:330,to:112,id:"e112"},{from:330,to:113,id:"e113"},{from:324,to:114,id:"e114"},{from:42,to:115,id:"e115"},{from:328,to:116,id:"e116"},{from:331,to:117,id:"e117"},{from:321,to:118,id:"e118"},{from:141,to:119,id:"e119"},{from:321,to:120,id:"e120"},{from:330,to:121,id:"e121"},{from:324,to:122,id:"e122"},{from:199,to:123,id:"e123"},{from:302,to:124,id:"e124"},{from:328,to:125,id:"e125"},{from:307,to:126,id:"e126"},{from:321,to:127,id:"e127"},{from:329,to:128,id:"e128"},{from:0,to:129,id:"e129"},{from:331,to:130,id:"e130"},{from:287,to:131,id:"e131"},{from:322,to:132,id:"e132"},{from:1,to:133,id:"e133"},{from:304,to:134,id:"e134"},{from:295,to:135,id:"e135"},{from:42,to:136,id:"e136"},{from:104,to:137,id:"e137"},{from:321,to:138,id:"e138"},{from:204,to:139,id:"e139"},{from:109,to:140,id:"e140"},{from:324,to:141,id:"e141"},{from:70,to:142,id:"e142"},{from:73,to:143,id:"e143"},{from:304,to:144,id:"e144"},{from:0,to:145,id:"e145"},{from:0,to:146,id:"e146"},{from:327,to:147,id:"e147"},{from:141,to:148,id:"e148"},{from:323,to:149,id:"e149"},{from:184,to:150,id:"e150"},{from:324,to:151,id:"e151"},{from:330,to:152,id:"e152"},{from:75,to:153,id:"e153"},{from:328,to:154,id:"e154"},{from:60,to:155,id:"e155"},{from:331,to:156,id:"e156"},{from:153,to:157,id:"e157"},{from:214,to:158,id:"e158"},{from:129,to:159,id:"e159"},{from:331,to:160,id:"e160"},{from:324,to:161,id:"e161"},{from:322,to:162,id:"e162"},{from:195,to:163,id:"e163"},{from:323,to:164,id:"e164"},{from:275,to:165,id:"e165"},{from:58,to:166,id:"e166"},{from:321,to:167,id:"e167"},{from:325,to:168,id:"e168"},{from:324,to:169,id:"e169"},{from:330,to:170,id:"e170"},{from:330,to:171,id:"e171"},{from:331,to:172,id:"e172"},{from:1,to:173,id:"e173"},{from:50,to:174,id:"e174"},{from:327,to:175,id:"e175"},{from:331,to:176,id:"e176"},{from:324,to:177,id:"e177"},{from:204,to:178,id:"e178"},{from:330,to:179,id:"e179"},{from:330,to:180,id:"e180"},{from:50,to:181,id:"e181"},{from:323,to:182,id:"e182"},{from:106,to:183,id:"e183"},{from:70,to:184,id:"e184"},{from:58,to:185,id:"e185"},{from:0,to:186,id:"e186"},{from:321,to:187,id:"e187"},{from:304,to:188,id:"e188"},{from:307,to:189,id:"e189"},{from:140,to:190,id:"e190"},{from:104,to:191,id:"e191"},{from:50,to:192,id:"e192"},{from:60,to:193,id:"e193"},{from:27,to:194,id:"e194"},{from:297,to:195,id:"e195"},{from:321,to:196,id:"e196"},{from:27,to:197,id:"e197"},{from:1,to:198,id:"e198"},{from:321,to:199,id:"e199"},{from:75,to:200,id:"e200"},{from:30,to:201,id:"e201"},{from:50,to:202,id:"e202"},{from:325,to:203,id:"e203"},{from:173,to:204,id:"e204"},{from:307,to:205,id:"e205"},{from:275,to:206,id:"e206"},{from:275,to:207,id:"e207"},{from:331,to:208,id:"e208"},{from:109,to:209,id:"e209"},{from:0,to:210,id:"e210"},{from:327,to:211,id:"e211"},{from:275,to:212,id:"e212"},{from:304,to:213,id:"e213"},{from:104,to:214,id:"e214"},{from:327,to:215,id:"e215"},{from:53,to:216,id:"e216"},{from:60,to:217,id:"e217"},{from:60,to:218,id:"e218"},{from:109,to:219,id:"e219"},{from:192,to:220,id:"e220"},{from:275,to:221,id:"e221"},{from:326,to:222,id:"e222"},{from:250,to:223,id:"e223"},{from:325,to:224,id:"e224"},{from:321,to:225,id:"e225"},{from:1,to:226,id:"e226"},{from:323,to:227,id:"e227"},{from:322,to:228,id:"e228"},{from:327,to:229,id:"e229"},{from:256,to:230,id:"e230"},{from:250,to:231,id:"e231"},{from:330,to:232,id:"e232"},{from:36,to:233,id:"e233"},{from:328,to:234,id:"e234"},{from:323,to:235,id:"e235"},{from:327,to:236,id:"e236"},{from:328,to:237,id:"e237"},{from:250,to:238,id:"e238"},{from:326,to:239,id:"e239"},{from:327,to:240,id:"e240"},{from:329,to:241,id:"e241"},{from:329,to:242,id:"e242"},{from:109,to:243,id:"e243"},{from:323,to:244,id:"e244"},{from:220,to:245,id:"e245"},{from:326,to:246,id:"e246"},{from:267,to:247,id:"e247"},{from:250,to:248,id:"e248"},{from:256,to:249,id:"e249"},{from:322,to:250,id:"e250"},{from:322,to:251,id:"e251"},{from:322,to:252,id:"e252"},{from:109,to:253,id:"e253"},{from:101,to:254,id:"e254"},{from:328,to:255,id:"e255"},{from:331,to:256,id:"e256"},{from:327,to:257,id:"e257"},{from:24,to:258,id:"e258"},{from:124,to:259,id:"e259"},{from:324,to:260,id:"e260"},{from:322,to:261,id:"e261"},{from:322,to:262,id:"e262"},{from:321,to:263,id:"e263"},{from:283,to:264,id:"e264"},{from:318,to:265,id:"e265"},{from:30,to:266,id:"e266"},{from:8,to:267,id:"e267"},{from:140,to:268,id:"e268"},{from:322,to:269,id:"e269"},{from:24,to:270,id:"e270"},{from:9,to:271,id:"e271"},{from:322,to:272,id:"e272"},{from:99,to:273,id:"e273"},{from:24,to:274,id:"e274"},{from:282,to:275,id:"e275"},{from:250,to:276,id:"e276"},{from:70,to:277,id:"e277"},{from:328,to:278,id:"e278"},{from:250,to:279,id:"e279"},{from:50,to:280,id:"e280"},{from:250,to:281,id:"e281"},{from:173,to:282,id:"e282"},{from:320,to:283,id:"e283"},{from:320,to:284,id:"e284"},{from:250,to:285,id:"e285"},{from:325,to:286,id:"e286"},{from:1,to:287,id:"e287"},{from:1,to:288,id:"e288"},{from:109,to:289,id:"e289"},{from:50,to:290,id:"e290"},{from:250,to:291,id:"e291"},{from:195,to:292,id:"e292"},{from:320,to:293,id:"e293"},{from:331,to:294,id:"e294"},{from:331,to:295,id:"e295"},{from:101,to:296,id:"e296"},{from:58,to:297,id:"e297"},{from:24,to:298,id:"e298"},{from:291,to:299,id:"e299"},{from:302,to:300,id:"e300"},{from:323,to:301,id:"e301"},{from:226,to:302,id:"e302"},{from:53,to:303,id:"e303"},{from:110,to:304,id:"e304"},{from:163,to:305,id:"e305"},{from:324,to:306,id:"e306"},{from:304,to:307,id:"e307"},{from:322,to:308,id:"e308"},{from:140,to:309,id:"e309"},{from:323,to:310,id:"e310"},{from:0,to:311,id:"e311"},{from:250,to:312,id:"e312"},{from:30,to:313,id:"e313"},{from:58,to:314,id:"e314"},{from:104,to:315,id:"e315"},{from:75,to:316,id:"e316"},{from:323,to:317,id:"e317"},{from:321,to:318,id:"e318"},{from:256,to:319,id:"e319"},{from:250,to:320,id:"e320"},{from:330,to:321,id:"e321"},{from:327,to:322,id:"e322"},{from:326,to:323,id:"e323"},{from:328,to:324,id:"e324"},{from:328,to:325,id:"e325"},{from:327,to:326,id:"e326"},{from:0,to:327,id:"e327"},{from:70,to:328,id:"e328"},{from:327,to:329,id:"e329"},{from:324,to:330,id:"e330"},{from:69,to:332,id:"e331"},{from:346,to:333,id:"e332"},{from:346,to:334,id:"e333"},{from:337,to:335,id:"e334"},{from:106,to:336,id:"e335"},{from:341,to:337,id:"e336"},{from:341,to:338,id:"e337"},{from:346,to:339,id:"e338"},{from:337,to:340,id:"e339"},{from:334,to:341,id:"e340"},{from:334,to:342,id:"e341"},{from:334,to:343,id:"e342"},{from:334,to:344,id:"e343"},{from:84,to:345,id:"e344"},{from:14,to:346,id:"e345"},{from:331,to:347,id:"e346"}] \ No newline at end of file diff --git a/lib/vis/examples/network/exampleApplications/lesMiserables.html b/lib/vis/examples/network/exampleApplications/lesMiserables.html index 3ac0114ec4..bf39a46299 100644 --- a/lib/vis/examples/network/exampleApplications/lesMiserables.html +++ b/lib/vis/examples/network/exampleApplications/lesMiserables.html @@ -376,7 +376,7 @@ maxVelocity: 146, solver: 'forceAtlas2Based', timestep: 0.35, - stabilization: {iterations:300} + stabilization: {iterations: 150} } }; var network = new vis.Network(container, data, options); diff --git a/lib/vis/examples/network/exampleUtil.js b/lib/vis/examples/network/exampleUtil.js index a5554ced33..1621a9a505 100644 --- a/lib/vis/examples/network/exampleUtil.js +++ b/lib/vis/examples/network/exampleUtil.js @@ -66,5 +66,68 @@ function getScaleFreeNetwork(nodeCount) { } } + return {nodes:nodes, edges:edges}; +} + +var randomSeed = 764; // Math.round(Math.random()*1000); +function seededRandom() { + var x = Math.sin(randomSeed++) * 10000; + return x - Math.floor(x); +} + +function getScaleFreeNetworkSeeded(nodeCount, seed) { + if (seed) { + randomSeed = Number(seed); + } + var nodes = []; + var edges = []; + var connectionCount = []; + var edgesId = 0; + + + // randomly create some nodes and edges + for (var i = 0; i < nodeCount; i++) { + nodes.push({ + id: i, + label: String(i) + }); + + connectionCount[i] = 0; + + // create edges in a scale-free-network way + if (i == 1) { + var from = i; + var to = 0; + edges.push({ + id: edgesId++, + from: from, + to: to + }); + connectionCount[from]++; + connectionCount[to]++; + } + else if (i > 1) { + var conn = edges.length * 2; + var rand = Math.floor(seededRandom() * conn); + var cum = 0; + var j = 0; + while (j < connectionCount.length && cum < rand) { + cum += connectionCount[j]; + j++; + } + + + var from = i; + var to = j; + edges.push({ + id: edgesId++, + from: from, + to: to + }); + connectionCount[from]++; + connectionCount[to]++; + } + } + return {nodes:nodes, edges:edges}; } \ No newline at end of file diff --git a/lib/vis/examples/network/layout/hierarchicalLayout.html b/lib/vis/examples/network/layout/hierarchicalLayout.html index 4a2539a81a..d827814c40 100644 --- a/lib/vis/examples/network/layout/hierarchicalLayout.html +++ b/lib/vis/examples/network/layout/hierarchicalLayout.html @@ -57,7 +57,7 @@ } - + diff --git a/lib/vis/examples/network/layout/hierarchicalLayoutMethods.html b/lib/vis/examples/network/layout/hierarchicalLayoutMethods.html index 93a16cf43e..51f5526701 100644 --- a/lib/vis/examples/network/layout/hierarchicalLayoutMethods.html +++ b/lib/vis/examples/network/layout/hierarchicalLayoutMethods.html @@ -19,7 +19,7 @@ - + - - + // randomly create some nodes and edges + for (var i = 0; i < 15; i++) { + nodes.push({id: i, label: String(i)}); + } + edges.push({from: 0, to: 1}); + edges.push({from: 0, to: 6}); + edges.push({from: 0, to: 13}); + edges.push({from: 0, to: 11}); + edges.push({from: 1, to: 2}); + edges.push({from: 2, to: 3}); + edges.push({from: 2, to: 4}); + edges.push({from: 3, to: 5}); + edges.push({from: 1, to: 10}); + edges.push({from: 1, to: 7}); + edges.push({from: 2, to: 8}); + edges.push({from: 2, to: 9}); + edges.push({from: 3, to: 14}); + edges.push({from: 1, to: 12}); + nodes[0]["level"] = 0; + nodes[1]["level"] = 1; + nodes[2]["level"] = 3; + nodes[3]["level"] = 4; + nodes[4]["level"] = 4; + nodes[5]["level"] = 5; + nodes[6]["level"] = 1; + nodes[7]["level"] = 2; + nodes[8]["level"] = 4; + nodes[9]["level"] = 4; + nodes[10]["level"] = 2; + nodes[11]["level"] = 1; + nodes[12]["level"] = 2; + nodes[13]["level"] = 1; + nodes[14]["level"] = 5; + + + // create a network + var container = document.getElementById('mynetwork'); + var data = { + nodes: nodes, + edges: edges + }; + + var options = { + edges: { + smooth: { + type: 'cubicBezier', + forceDirection: (directionInput.value == "UD" || directionInput.value == "DU") ? 'vertical' : 'horizontal', + roundness: 0.4 + } + }, + layout: { + hierarchical: { + direction: directionInput.value + } + }, + physics:false + }; + network = new vis.Network(container, data, options); + + // add event listeners + network.on('select', function (params) { + document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes; + }); + } + + +

Hierarchical Layout - User-defined

+
- This example shows a user-defined hierarchical layout. If the user defines levels for nodes but does not do so for all nodes, an alert will show up and hierarchical layout will be disabled. Either all or none can be defined. + This example shows a user-defined hierarchical layout. If the user defines levels for nodes but does not do so for + all nodes, an alert will show up and hierarchical layout will be disabled. Either all or none can be defined. If the smooth curves appear to be inverted, the direction of the edge is not in the same direction as the network.

- - - - - + + + + +

@@ -123,22 +128,22 @@ + + + + + +

Hierarchical Layout without Physics

+The hierarchical layout can now be controlled without the use of physics. This is much quicker. The options for this are:

+ + + + + + + + + + + + + + + + + + + + + + +
levelSeparationDistance between levels.
nodeSpacingMinimum distance between nodes on the free axis.
treeSpacingDistance between different trees (independent networks).
blockShiftingMethod for reducing whitespace. Can be used alone or together with edge minimization. Each node will check for whitespace and will shift + it's branch along with it for as far as it can, respecting the nodeSpacing on any level.
edgeMinimizationMethod for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process. + Each node will try to move along its free axis to reduce the total length of it's edges.
+

+Play with the settings below the network and see how the layout changes! +
+ + + \ No newline at end of file diff --git a/lib/vis/examples/network/nodeStyles/imagesWithBorders.html b/lib/vis/examples/network/nodeStyles/imagesWithBorders.html new file mode 100644 index 0000000000..fdfe4abf40 --- /dev/null +++ b/lib/vis/examples/network/nodeStyles/imagesWithBorders.html @@ -0,0 +1,105 @@ + + + + Network | Images With Borders + + + + + + + + + + + + +
+ + diff --git a/lib/vis/examples/network/nodeStyles/shadows.html b/lib/vis/examples/network/nodeStyles/shadows.html index 4a760e8911..bfa7b86ade 100644 --- a/lib/vis/examples/network/nodeStyles/shadows.html +++ b/lib/vis/examples/network/nodeStyles/shadows.html @@ -87,7 +87,7 @@ {from: 25, to: 24}, {from: 26, to: 25}, {from: 25, to: 7}, - {from: 28, to: 27}, + {from: 28, to: 27, shadow:{color:'rgb(0,255,0)'}}, {from: 29, to: 28}, {from: 28, to: 0} ] diff --git a/lib/vis/examples/network/other/clustering.html b/lib/vis/examples/network/other/clustering.html index 30b745eff0..2f2eb8eb41 100644 --- a/lib/vis/examples/network/other/clustering.html +++ b/lib/vis/examples/network/other/clustering.html @@ -80,7 +80,7 @@ Click any of the buttons below to cluster the network. On every push the network network.openCluster(params.nodes[0]); } } - }) + }); function clusterByCid() { network.setData(data); @@ -89,7 +89,7 @@ Click any of the buttons below to cluster the network. On every push the network return childOptions.cid == 1; }, clusterNodeProperties: {id:'cidCluster', borderWidth:3, shape:'database'} - } + }; network.cluster(clusterOptionsByData); } function clusterByColor() { @@ -111,7 +111,7 @@ Click any of the buttons below to cluster the network. On every push the network return clusterOptions; }, clusterNodeProperties: {id: 'cluster:' + color, borderWidth: 3, shape: 'database', color:color, label:'color:' + color} - } + }; network.cluster(clusterOptionsByData); } } @@ -131,7 +131,7 @@ Click any of the buttons below to cluster the network. On every push the network return clusterOptions; }, clusterNodeProperties: {borderWidth:3, shape:'box', font:{size:30}} - } + }; network.clusterByHubsize(undefined, clusterOptionsByData); } diff --git a/lib/vis/examples/network/other/clusteringByZoom.html b/lib/vis/examples/network/other/clusteringByZoom.html index 40ccf8cc83..f7f8fa5a1a 100644 --- a/lib/vis/examples/network/other/clusteringByZoom.html +++ b/lib/vis/examples/network/other/clusteringByZoom.html @@ -72,7 +72,7 @@ Stabilize when clustering: nodes: nodes, edges: edges }; - var options = {layout: {randomSeed: 8}}; + var options = {layout: {randomSeed: 8}, physics:{adaptiveTimestep:false}}; var network = new vis.Network(container, data, options); // set the first initial zoom level @@ -125,6 +125,8 @@ Stabilize when clustering: } network.clusterOutliers(clusterOptionsByData); if (document.getElementById('stabilizeCheckbox').checked === true) { + // since we use the scale as a unique identifier, we do NOT want to fit after the stabilization + network.setOptions({physics:{stabilization:{fit: false}}}); network.stabilize(); } } @@ -145,6 +147,8 @@ Stabilize when clustering: } clusters = newClusters; if (declustered === true && document.getElementById('stabilizeCheckbox').checked === true) { + // since we use the scale as a unique identifier, we do NOT want to fit after the stabilization + network.setOptions({physics:{stabilization:{fit: false}}}); network.stabilize(); } } diff --git a/lib/vis/examples/network/other/configuration.html b/lib/vis/examples/network/other/configuration.html index 1d8673b25f..74bf9e798c 100644 --- a/lib/vis/examples/network/other/configuration.html +++ b/lib/vis/examples/network/other/configuration.html @@ -52,6 +52,15 @@ configure: true }; network = new vis.Network(container, data, options); + + network.on("configChange", function() { + // this will immediately fix the height of the configuration + // wrapper to prevent unecessary scrolls in chrome. + // see https://github.com/almende/vis/issues/1568 + var div = container.getElementsByClassName('vis-configuration-wrapper')[0]; + div.style["height"] = div.getBoundingClientRect().height + "px"; + }); + } @@ -64,6 +73,8 @@ You can also supply a custom filter function or filter string. You can press the generate options button below to have an options object printed. You can then use this in the network.

+

Note: The configurator is recreated in the dom tree on input change. This may cause undesired scrolls in your application. In order to avoid this, explicitly set the height of the configurator (see this example's source code). +


diff --git a/lib/vis/examples/timeline/groups/groupsEditable.html b/lib/vis/examples/timeline/groups/groupsEditable.html new file mode 100644 index 0000000000..8fce141033 --- /dev/null +++ b/lib/vis/examples/timeline/groups/groupsEditable.html @@ -0,0 +1,315 @@ + + + + Timeline | Editable Groups + + + + + + + + +

+ This example demonstrates editable groups (for now only reordering). +

+
+ + + + \ No newline at end of file diff --git a/lib/vis/gulpfile.js b/lib/vis/gulpfile.js index 636061b93b..af46d93fed 100644 --- a/lib/vis/gulpfile.js +++ b/lib/vis/gulpfile.js @@ -100,6 +100,7 @@ gulp.task('bundle-css', ['clean'], function () { var files = [ './lib/shared/activator.css', './lib/shared/bootstrap.css', + './lib/shared/configuration.css', './lib/timeline/component/css/timeline.css', './lib/timeline/component/css/panel.css', @@ -115,9 +116,8 @@ gulp.task('bundle-css', ['clean'], function () { './lib/timeline/component/css/pathStyles.css', './lib/network/css/network-manipulation.css', - './lib/network/css/network-navigation.css', './lib/network/css/network-tooltip.css', - './lib/network/css/network-configuration.css', + './lib/network/css/network-navigation.css', './lib/network/css/network-colorpicker.css' ]; @@ -132,13 +132,13 @@ gulp.task('bundle-css', ['clean'], function () { }); gulp.task('copy', ['clean'], function () { - var network = gulp.src('./lib/network/img/**/*') + var network = gulp.src('./lib/network/img/**/*') .pipe(gulp.dest(DIST + '/img/network')); - var timeline = gulp.src('./lib/timeline/img/**/*') + var timeline = gulp.src('./lib/timeline/img/**/*') .pipe(gulp.dest(DIST + '/img/timeline')); - return merge(network, timeline); + return merge(network, timeline); }); gulp.task('minify', ['bundle-js'], function (cb) { @@ -148,7 +148,7 @@ gulp.task('minify', ['bundle-js'], function (cb) { // any issues when concatenating the file downstream (the file ends // with a comment). fs.writeFileSync(DIST + '/' + VIS_MIN_JS, result.code + '\n'); - fs.writeFileSync(DIST + '/' + VIS_MAP, result.map); + fs.writeFileSync(DIST + '/' + VIS_MAP, result.map.replace(/"\.\/dist\//g, '"')); cb(); }); diff --git a/lib/vis/index.js b/lib/vis/index.js index 71ef933895..d84e82e8da 100644 --- a/lib/vis/index.js +++ b/lib/vis/index.js @@ -22,8 +22,8 @@ exports.graph3d = { exports.Timeline = require('./lib/timeline/Timeline'); exports.Graph2d = require('./lib/timeline/Graph2d'); exports.timeline = { + Core: require('./lib/timeline/Core'), DateUtil: require('./lib/timeline/DateUtil'), - DataStep: require('./lib/timeline/DataStep'), Range: require('./lib/timeline/Range'), stack: require('./lib/timeline/Stack'), TimeStep: require('./lib/timeline/TimeStep'), @@ -37,13 +37,14 @@ exports.timeline = { RangeItem: require('./lib/timeline/component/item/RangeItem') }, + BackgroundGroup: require('./lib/timeline/component/BackgroundGroup'), Component: require('./lib/timeline/component/Component'), CurrentTime: require('./lib/timeline/component/CurrentTime'), CustomTime: require('./lib/timeline/component/CustomTime'), DataAxis: require('./lib/timeline/component/DataAxis'), + DataScale: require('./lib/timeline/component/DataScale'), GraphGroup: require('./lib/timeline/component/GraphGroup'), Group: require('./lib/timeline/component/Group'), - BackgroundGroup: require('./lib/timeline/component/BackgroundGroup'), ItemSet: require('./lib/timeline/component/ItemSet'), Legend: require('./lib/timeline/component/Legend'), LineGraph: require('./lib/timeline/component/LineGraph'), @@ -62,13 +63,7 @@ exports.network = { exports.network.convertDot = function (input) {return exports.network.dotparser.DOTToGraph(input)}; exports.network.convertGephi = function (input,options) {return exports.network.gephiParser.parseGephi(input,options)}; -// Deprecated since v3.0.0 -exports.Graph = function () { - throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)'); -}; - // bundled external libraries exports.moment = require('./lib/module/moment'); -exports.hammer = require('./lib/module/hammer'); // TODO: deprecate exports.hammer some day exports.Hammer = require('./lib/module/hammer'); exports.keycharm = require('keycharm'); \ No newline at end of file diff --git a/lib/vis/lib/DOMutil.js b/lib/vis/lib/DOMutil.js index ebedeb21b4..1e778bc0f8 100644 --- a/lib/vis/lib/DOMutil.js +++ b/lib/vis/lib/DOMutil.js @@ -36,6 +36,16 @@ exports.cleanupElements = function(JSONcontainer) { } }; +/** + * Ensures that all elements are removed first up so they can be recreated cleanly + * @param JSONcontainer + */ +exports.resetElements = function(JSONcontainer) { + exports.prepareElements(JSONcontainer); + exports.cleanupElements(JSONcontainer); + exports.prepareElements(JSONcontainer); +} + /** * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. @@ -149,8 +159,8 @@ exports.drawPoint = function(x, y, groupTemplate, JSONcontainer, svgContainer, l point.setAttributeNS(null, "height", groupTemplate.size); } - if (groupTemplate.style !== undefined) { - point.setAttributeNS(null, "style", groupTemplate.style); + if (groupTemplate.styles !== undefined) { + point.setAttributeNS(null, "style", groupTemplate.styles); } point.setAttributeNS(null, "class", groupTemplate.className + " vis-point"); //handle label diff --git a/lib/vis/lib/DataSet.js b/lib/vis/lib/DataSet.js index e8d1c973ca..fbc6b92a06 100644 --- a/lib/vis/lib/DataSet.js +++ b/lib/vis/lib/DataSet.js @@ -60,15 +60,15 @@ function DataSet (data, options) { // all variants of a Date are internally stored as Date, so we can convert // from everything to everything (also from ISODate to Number for example) if (this._options.type) { - for (var field in this._options.type) { - if (this._options.type.hasOwnProperty(field)) { - var value = this._options.type[field]; - if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') { - this._type[field] = 'Date'; - } - else { - this._type[field] = value; - } + var fields = Object.keys(this._options.type); + for (var i = 0, len = fields.length; i < len; i++) { + var field = fields[i]; + var value = this._options.type[field]; + if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') { + this._type[field] = 'Date'; + } + else { + this._type[field] = value; } } } @@ -184,7 +184,7 @@ DataSet.prototype._trigger = function (event, params, senderId) { subscribers = subscribers.concat(this._subscribers['*']); } - for (var i = 0; i < subscribers.length; i++) { + for (var i = 0, len = subscribers.length; i < len; i++) { var subscriber = subscribers[i]; if (subscriber.callback) { subscriber.callback(event, params, senderId || null); @@ -236,6 +236,7 @@ DataSet.prototype.add = function (data, senderId) { DataSet.prototype.update = function (data, senderId) { var addedIds = []; var updatedIds = []; + var oldData = []; var updatedData = []; var me = this; var fieldId = me._fieldId; @@ -243,10 +244,12 @@ DataSet.prototype.update = function (data, senderId) { var addOrUpdate = function (item) { var id = item[fieldId]; if (me._data[id]) { + var oldItem = util.extend({}, me._data[id]); // update item id = me._updateItem(item); updatedIds.push(id); updatedData.push(item); + oldData.push(oldItem); } else { // add new item @@ -258,7 +261,11 @@ DataSet.prototype.update = function (data, senderId) { if (Array.isArray(data)) { // Array for (var i = 0, len = data.length; i < len; i++) { - addOrUpdate(data[i]); + if (data[i] instanceof Object){ + addOrUpdate(data[i]); + } else { + console.warn('Ignoring input item, which is not an object at index ' + i); + } } } else if (data instanceof Object) { @@ -273,7 +280,15 @@ DataSet.prototype.update = function (data, senderId) { this._trigger('add', {items: addedIds}, senderId); } if (updatedIds.length) { - this._trigger('update', {items: updatedIds, data: updatedData}, senderId); + var props = { items: updatedIds, oldData: oldData, data: updatedData }; + // TODO: remove deprecated property 'data' some day + //Object.defineProperty(props, 'data', { + // 'get': (function() { + // console.warn('Property data is deprecated. Use DataSet.get(ids) to retrieve the new data, use the oldData property on this object to get the old data'); + // return updatedData; + // }).bind(this) + //}); + this._trigger('update', props, senderId); } return addedIds.concat(updatedIds); @@ -340,13 +355,13 @@ DataSet.prototype.get = function (args) { // build options var type = options && options.type || this._options.type; var filter = options && options.filter; - var items = [], item, itemId, i, len; + var items = [], item, itemIds, itemId, i, len; // convert items if (id != undefined) { // return a single item item = me._getItem(id, type); - if (filter && !filter(item)) { + if (item && filter && !filter(item)) { item = null; } } @@ -361,12 +376,12 @@ DataSet.prototype.get = function (args) { } else { // return all items - for (itemId in this._data) { - if (this._data.hasOwnProperty(itemId)) { - item = me._getItem(itemId, type); - if (!filter || filter(item)) { - items.push(item); - } + itemIds = Object.keys(this._data); + for (i = 0, len = itemIds.length; i < len; i++) { + itemId = itemIds[i]; + item = me._getItem(itemId, type); + if (!filter || filter(item)) { + items.push(item); } } } @@ -391,9 +406,11 @@ DataSet.prototype.get = function (args) { // return the results if (returnType == 'Object') { - var result = {}; - for (i = 0; i < items.length; i++) { - result[items[i].id] = items[i]; + var result = {}, + resultant; + for (i = 0, len = items.length; i < len; i++) { + resultant = items[i]; + result[resultant.id] = resultant; } return result; } @@ -422,6 +439,7 @@ DataSet.prototype.getIds = function (options) { filter = options && options.filter, order = options && options.order, type = options && options.type || this._options.type, + itemIds = Object.keys(data), i, len, id, @@ -434,29 +452,27 @@ DataSet.prototype.getIds = function (options) { if (order) { // create ordered list items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - items.push(item); - } + for (i = 0, len = itemIds.length; i < len; i++) { + id = itemIds[i]; + item = this._getItem(id, type); + 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]; + ids.push(items[i][this._fieldId]); } } else { // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - ids.push(item[this._fieldId]); - } + for (i = 0, len = itemIds.length; i < len; i++) { + id = itemIds[i]; + item = this._getItem(id, type); + if (filter(item)) { + ids.push(item[this._fieldId]); } } } @@ -466,25 +482,23 @@ DataSet.prototype.getIds = function (options) { if (order) { // create an ordered list items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - items.push(data[id]); - } + for (i = 0, len = itemIds.length; i < len; i++) { + id = itemIds[i]; + items.push(data[id]); } this._sort(items, order); for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; + ids.push(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 = itemIds.length; i < len; i++) { + id = itemIds[i]; + item = data[id]; + ids.push(item[this._fieldId]); } } } @@ -514,6 +528,9 @@ DataSet.prototype.forEach = function (callback, options) { var filter = options && options.filter, type = options && options.type || this._options.type, data = this._data, + itemIds = Object.keys(data), + i, + len, item, id; @@ -521,7 +538,7 @@ DataSet.prototype.forEach = function (callback, options) { // execute forEach on ordered list var items = this.get(options); - for (var i = 0, len = items.length; i < len; i++) { + for (i = 0, len = items.length; i < len; i++) { item = items[i]; id = item[this._fieldId]; callback(item, id); @@ -529,12 +546,11 @@ DataSet.prototype.forEach = function (callback, options) { } else { // unordered - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - callback(item, id); - } + for (i = 0, len = itemIds.length; i < len; i++) { + id = itemIds[i]; + item = this._getItem(id, type); + if (!filter || filter(item)) { + callback(item, id); } } } @@ -556,15 +572,18 @@ DataSet.prototype.map = function (callback, options) { type = options && options.type || this._options.type, mappedItems = [], data = this._data, + itemIds = Object.keys(data), + i, + len, + id, item; // convert and filter items - for (var id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - mappedItems.push(callback(item, id)); - } + for (i = 0, len = itemIds.length; i < len; i++) { + id = itemIds[i]; + item = this._getItem(id, type); + if (!filter || filter(item)) { + mappedItems.push(callback(item, id)); } } @@ -588,17 +607,23 @@ DataSet.prototype._filterFields = function (item, fields) { return item; } - var filteredItem = {}; + var filteredItem = {}, + itemFields = Object.keys(item), + len = itemFields.length, + i, + field; if(Array.isArray(fields)){ - for (var field in item) { - if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { + for (i = 0; i < len; i++) { + field = itemFields[i]; + if (fields.indexOf(field) != -1) { filteredItem[field] = item[field]; } } }else{ - for (var field in item) { - if (item.hasOwnProperty(field) && fields.hasOwnProperty(field)) { + for (i = 0; i < len; i++) { + field = itemFields[i]; + if (fields.hasOwnProperty(field)) { filteredItem[fields[field]] = item[field]; } } @@ -683,7 +708,7 @@ DataSet.prototype._remove = function (id) { } else if (id instanceof Object) { var itemId = id[this._fieldId]; - if (itemId && this._data[itemId]) { + if (itemId !== undefined && this._data[itemId]) { delete this._data[itemId]; this.length--; return itemId; @@ -715,17 +740,19 @@ DataSet.prototype.clear = function (senderId) { */ DataSet.prototype.max = function (field) { var data = this._data, + itemIds = Object.keys(data), max = null, - maxField = null; + maxField = null, + i, + len; - 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; - } + for (i = 0, len = itemIds.length; i < len; i++) { + var id = itemIds[i]; + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!max || itemField > maxField)) { + max = item; + maxField = itemField; } } @@ -739,17 +766,19 @@ DataSet.prototype.max = function (field) { */ DataSet.prototype.min = function (field) { var data = this._data, + itemIds = Object.keys(data), min = null, - minField = null; + minField = null, + i, + len; - 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; - } + for (i = 0, len = itemIds.length; i < len; i++) { + var id = itemIds[i]; + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!min || itemField < minField)) { + min = item; + minField = itemField; } } @@ -765,31 +794,33 @@ DataSet.prototype.min = function (field) { */ DataSet.prototype.distinct = function (field) { var data = this._data; + var itemIds = Object.keys(data); var values = []; var fieldType = this._options.type && this._options.type[field] || null; var count = 0; - var i; + var i, + j, + len; - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - var item = data[prop]; - var value = item[field]; - var exists = false; - for (i = 0; i < count; i++) { - if (values[i] == value) { - exists = true; - break; - } - } - if (!exists && (value !== undefined)) { - values[count] = value; - count++; + for (i = 0, len = itemIds.length; i < len; i++) { + var id = itemIds[i]; + var item = data[id]; + var value = item[field]; + var exists = false; + for (j = 0; j < count; j++) { + if (values[j] == value) { + exists = true; + break; } } + if (!exists && (value !== undefined)) { + values[count] = value; + count++; + } } if (fieldType) { - for (i = 0; i < values.length; i++) { + for (i = 0, len = values.length; i < len; i++) { values[i] = util.convert(values[i], fieldType); } } @@ -819,12 +850,14 @@ DataSet.prototype._addItem = function (item) { item[this._fieldId] = id; } - var d = {}; - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } + var d = {}, + fields = Object.keys(item), + i, + len; + for (i = 0, len = fields.length; i < len; i++) { + var field = fields[i]; + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); } this._data[id] = d; this.length++; @@ -840,7 +873,7 @@ DataSet.prototype._addItem = function (item) { * @private */ DataSet.prototype._getItem = function (id, types) { - var field, value; + var field, value, i, len; // get the item from the dataset var raw = this._data[id]; @@ -849,22 +882,22 @@ DataSet.prototype._getItem = function (id, types) { } // convert the items field types - var converted = {}; + var converted = {}, + fields = Object.keys(raw); + if (types) { - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = util.convert(value, types[field]); - } + for (i = 0, len = fields.length; i < len; i++) { + field = fields[i]; + value = raw[field]; + converted[field] = util.convert(value, types[field]); } } else { // no field types specified, no converting needed - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = value; - } + for (i = 0, len = fields.length; i < len; i++) { + field = fields[i]; + value = raw[field]; + converted[field] = value; } } return converted; @@ -890,11 +923,11 @@ DataSet.prototype._updateItem = function (item) { } // merge with current item - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } + var fields = Object.keys(item); + for (var i = 0, len = fields.length; i < len; i++) { + var field = fields[i]; + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); } return id; diff --git a/lib/vis/lib/DataView.js b/lib/vis/lib/DataView.js index 39da84ab3a..32a7325ab6 100644 --- a/lib/vis/lib/DataView.js +++ b/lib/vis/lib/DataView.js @@ -35,7 +35,7 @@ function DataView (data, options) { * @param {DataSet | DataView} data */ DataView.prototype.setData = function (data) { - var ids, i, len; + var ids, id, i, len; if (this._data) { // unsubscribe from current dataset @@ -44,12 +44,7 @@ DataView.prototype.setData = function (data) { } // trigger a remove of all items in memory - ids = []; - for (var id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - ids.push(id); - } - } + ids = Object.keys(this._ids); this._ids = {}; this.length = 0; this._trigger('remove', {items: ids}); @@ -84,34 +79,34 @@ DataView.prototype.setData = function (data) { * containing a variable parameter. */ DataView.prototype.refresh = function () { - var id; + var id, i, len; var ids = this._data.getIds({filter: this._options && this._options.filter}); + var oldIds = Object.keys(this._ids); var newIds = {}; var added = []; var removed = []; // check for additions - for (var i = 0; i < ids.length; i++) { + for (i = 0, len = ids.length; i < len; i++) { id = ids[i]; newIds[id] = true; if (!this._ids[id]) { added.push(id); this._ids[id] = true; - this.length++; } } // check for removals - for (id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - if (!newIds[id]) { - removed.push(id); - delete this._ids[id]; - this.length--; - } + for (i = 0, len = oldIds.length; i < len; i++) { + id = oldIds[i]; + if (!newIds[id]) { + removed.push(id); + delete this._ids[id]; } } + this.length += added.length - removed.length; + // trigger events if (added.length) { this._trigger('add', {items: added}); @@ -235,6 +230,49 @@ DataView.prototype.getIds = function (options) { return ids; }; +/** + * Map every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Object[]} mappedItems + */ +DataView.prototype.map = function (callback,options) { + var mappedItems = []; + 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; + } + + mappedItems = this._data.map(callback,{ + filter: filter, + order: options && options.order + }); + } + else { + mappedItems = []; + } + + return mappedItems; +}; + /** * Get the DataSet to which this DataView is connected. In case there is a chain * of multiple DataViews, the root DataSet of this chain is returned. diff --git a/lib/vis/lib/graph3d/Graph3d.js b/lib/vis/lib/graph3d/Graph3d.js index 20c7b6b1e5..6839007957 100644 --- a/lib/vis/lib/graph3d/Graph3d.js +++ b/lib/vis/lib/graph3d/Graph3d.js @@ -96,6 +96,8 @@ function Graph3d(container, data, options) { strokeWidth: 1 // px }; + this.dotSizeRatio = 0.02; // size of the dots as a fraction of the graph width + // create a frame and canvas this.create(); @@ -844,6 +846,8 @@ Graph3d.prototype.setOptions = function (options) { if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; + if (options.dotSizeRatio !== undefined) this.dotSizeRatio = options.dotSizeRatio; + if (options.style !== undefined) { var styleNumber = this._getStyleNumber(options.style); if (styleNumber !== -1) { @@ -976,7 +980,7 @@ Graph3d.prototype._redrawLegend = function() { if (this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.DOTSIZE) { - var dotSize = this.frame.clientWidth * 0.02; + var dotSize = this.frame.clientWidth * this.dotSizeRatio; var widthMin, widthMax; if (this.style === Graph3d.STYLE.DOTSIZE) { @@ -1613,7 +1617,7 @@ Graph3d.prototype._redrawDataDot = function() { this.dataPoints.sort(sortDepth); // draw the datapoints as colored circles - var dotSize = this.frame.clientWidth * 0.02; // px + var dotSize = this.frame.clientWidth * this.dotSizeRatio; // px for (i = 0; i < this.dataPoints.length; i++) { var point = this.dataPoints[i]; diff --git a/lib/vis/lib/hammerUtil.js b/lib/vis/lib/hammerUtil.js index 9a2f161511..4e1b79d2a8 100644 --- a/lib/vis/lib/hammerUtil.js +++ b/lib/vis/lib/hammerUtil.js @@ -7,23 +7,14 @@ var Hammer = require('./module/hammer'); */ exports.onTouch = function (hammer, callback) { callback.inputHandler = function (event) { - if (event.isFirst && !isTouching) { + if (event.isFirst) { callback(event); - - isTouching = true; - setTimeout(function () { - isTouching = false; - }, 0); } }; hammer.on('hammer.input', callback.inputHandler); }; -// isTouching is true while a touch action is being emitted -// this is a hack to prevent `touch` from being fired twice -var isTouching = false; - /** * Register a release event, taking place after a gesture * @param {Hammer} hammer A hammer instance @@ -31,13 +22,8 @@ var isTouching = false; */ exports.onRelease = function (hammer, callback) { callback.inputHandler = function (event) { - if (event.isFinal && !isReleasing) { + if (event.isFinal) { callback(event); - - isReleasing = true; - setTimeout(function () { - isReleasing = false; - }, 0); } }; @@ -45,11 +31,6 @@ exports.onRelease = function (hammer, callback) { }; -// isReleasing is true while a release action is being emitted -// this is a hack to prevent `release` from being fired twice -var isReleasing = false; - - /** * Unregister a touch event, taking place before a gesture * @param {Hammer} hammer A hammer instance diff --git a/lib/vis/lib/header.js b/lib/vis/lib/header.js index 2c8a3431d2..3045a79719 100644 --- a/lib/vis/lib/header.js +++ b/lib/vis/lib/header.js @@ -8,7 +8,7 @@ * @date @@date * * @license - * Copyright (C) 2011-2014 Almende B.V, http://almende.com + * Copyright (C) 2011-2016 Almende B.V, http://almende.com * * Vis.js is dual licensed under both * diff --git a/lib/vis/lib/network/Network.js b/lib/vis/lib/network/Network.js index deea2d8803..68d10c76c3 100644 --- a/lib/vis/lib/network/Network.js +++ b/lib/vis/lib/network/Network.js @@ -24,11 +24,11 @@ import InteractionHandler from './modules/InteractionHandler'; import SelectionHandler from "./modules/SelectionHandler"; import LayoutEngine from "./modules/LayoutEngine"; import ManipulationSystem from "./modules/ManipulationSystem"; -import Configurator from "./../shared/Configurator"; +import Configurator from "./../shared/Configurator"; import Validator from "./../shared/Validator"; import {printStyle} from "./../shared/Validator"; import {allOptions, configureOptions} from './options.js'; - +import KamadaKawai from "./modules/KamadaKawai.js" /** @@ -92,6 +92,7 @@ function Network(container, data, options) { createEdge: function() {}, getPointer: function() {} }, + modules: {}, view: { scale: 1, translation: {x: 0, y: 0} @@ -119,6 +120,9 @@ function Network(container, data, options) { this.nodesHandler = new NodesHandler(this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options this.edgesHandler = new EdgesHandler(this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options + this.body.modules["kamadaKawai"] = new KamadaKawai(this.body,150,0.05); // Layouting algorithm. + this.body.modules["clustering"] = this.clustering; + // create the DOM elements this.canvas._create(); @@ -140,7 +144,6 @@ Emitter(Network.prototype); */ Network.prototype.setOptions = function (options) { if (options !== undefined) { - let errorFound = Validator.validate(options, allOptions); if (errorFound === true) { console.log('%cErrors have been found in the supplied options object.', printStyle); @@ -243,7 +246,7 @@ Network.prototype._updateVisibleIndices = function () { for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { if (nodes[nodeId].options.hidden === false) { - this.body.nodeIndices.push(nodeId); + this.body.nodeIndices.push(nodes[nodeId].id); } } } @@ -251,7 +254,7 @@ Network.prototype._updateVisibleIndices = function () { for (let edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { if (edges[edgeId].options.hidden === false) { - this.body.edgeIndices.push(edgeId); + this.body.edgeIndices.push(edges[edgeId].id); } } } @@ -266,7 +269,6 @@ Network.prototype.bindEventListeners = function () { this.body.emitter.on("_dataChanged", () => { // update shortcut lists this._updateVisibleIndices(); - this.physics.updatePhysicsData(); this.body.emitter.emit("_requestRedraw"); // call the dataUpdated event because the only difference between the two is the updating of the indices this.body.emitter.emit("_dataUpdated"); @@ -332,6 +334,9 @@ Network.prototype.setData = function (data) { // emit change in data this.body.emitter.emit("_dataChanged"); + // emit data loaded + this.body.emitter.emit("_dataLoaded"); + // find a stable position or start animating to a stable position this.body.emitter.emit("initPhysics"); }; @@ -439,7 +444,7 @@ Network.prototype.enableEditMode = function() {return this.manipulation.ena Network.prototype.disableEditMode = function() {return this.manipulation.disableEditMode.apply(this.manipulation,arguments);}; Network.prototype.addNodeMode = function() {return this.manipulation.addNodeMode.apply(this.manipulation,arguments);}; Network.prototype.editNode = function() {return this.manipulation.editNode.apply(this.manipulation,arguments);}; -Network.prototype.editNodeMode = function() {console.log("Depricated: Please use editNode instead of editNodeMode."); return this.manipulation.editNode.apply(this.manipulation,arguments);}; +Network.prototype.editNodeMode = function() {console.log("Deprecated: Please use editNode instead of editNodeMode."); return this.manipulation.editNode.apply(this.manipulation,arguments);}; Network.prototype.addEdgeMode = function() {return this.manipulation.addEdgeMode.apply(this.manipulation,arguments);}; Network.prototype.editEdgeMode = function() {return this.manipulation.editEdgeMode.apply(this.manipulation,arguments);}; Network.prototype.deleteSelected = function() {return this.manipulation.deleteSelected.apply(this.manipulation,arguments);}; @@ -460,6 +465,7 @@ Network.prototype.startSimulation = function() {return this.physics.startSim Network.prototype.stopSimulation = function() {return this.physics.stopSimulation.apply(this.physics,arguments);}; Network.prototype.stabilize = function() {return this.physics.stabilize.apply(this.physics,arguments);}; Network.prototype.getSelection = function() {return this.selectionHandler.getSelection.apply(this.selectionHandler,arguments);}; +Network.prototype.setSelection = function() {return this.selectionHandler.setSelection.apply(this.selectionHandler,arguments);}; Network.prototype.getSelectedNodes = function() {return this.selectionHandler.getSelectedNodes.apply(this.selectionHandler,arguments);}; Network.prototype.getSelectedEdges = function() {return this.selectionHandler.getSelectedEdges.apply(this.selectionHandler,arguments);}; Network.prototype.getNodeAt = function() { @@ -478,7 +484,10 @@ Network.prototype.getEdgeAt = function() { }; Network.prototype.selectNodes = function() {return this.selectionHandler.selectNodes.apply(this.selectionHandler,arguments);}; Network.prototype.selectEdges = function() {return this.selectionHandler.selectEdges.apply(this.selectionHandler,arguments);}; -Network.prototype.unselectAll = function() {return this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);}; +Network.prototype.unselectAll = function() { + this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments); + this.redraw(); +}; Network.prototype.redraw = function() {return this.renderer.redraw.apply(this.renderer,arguments);}; Network.prototype.getScale = function() {return this.view.getScale.apply(this.view,arguments);}; Network.prototype.getViewPosition = function() {return this.view.getViewPosition.apply(this.view,arguments);}; diff --git a/lib/vis/lib/network/NetworkUtil.js b/lib/vis/lib/network/NetworkUtil.js new file mode 100644 index 0000000000..07fa313f3c --- /dev/null +++ b/lib/vis/lib/network/NetworkUtil.js @@ -0,0 +1,96 @@ +let util = require("../util"); +class NetworkUtil { + constructor() {} + + /** + * Find the center position of the network considering the bounding boxes + */ + static getRange(allNodes, specificNodes = []) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + if (specificNodes.length > 0) { + for (var i = 0; i < specificNodes.length; i++) { + node = allNodes[specificNodes[i]]; + if (minX > node.shape.boundingBox.left) { + minX = node.shape.boundingBox.left; + } + if (maxX < node.shape.boundingBox.right) { + maxX = node.shape.boundingBox.right; + } + if (minY > node.shape.boundingBox.top) { + minY = node.shape.boundingBox.top; + } // top is negative, bottom is positive + if (maxY < node.shape.boundingBox.bottom) { + maxY = node.shape.boundingBox.bottom; + } // top is negative, bottom is positive + } + } + + if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; + } + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + } + + /** + * Find the center position of the network + */ + static getRangeCore(allNodes, specificNodes = []) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + if (specificNodes.length > 0) { + for (var i = 0; i < specificNodes.length; i++) { + node = allNodes[specificNodes[i]]; + if (minX > node.x) { + minX = node.x; + } + if (maxX < node.x) { + maxX = node.x; + } + if (minY > node.y) { + minY = node.y; + } // top is negative, bottom is positive + if (maxY < node.y) { + maxY = node.y; + } // top is negative, bottom is positive + } + } + + if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; + } + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + } + + + /** + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + * @returns {{x: number, y: number}} + */ + static findCenter(range) { + return {x: (0.5 * (range.maxX + range.minX)), + y: (0.5 * (range.maxY + range.minY))}; + } + + + /** + * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes. + * @param item + * @param type + * @returns {{}} + */ + static cloneOptions(item, type) { + let clonedOptions = {}; + if (type === undefined || type === 'node') { + util.deepExtend(clonedOptions, item.options, true); + clonedOptions.x = item.x; + clonedOptions.y = item.y; + clonedOptions.amountOfConnections = item.edges.length; + } + else { + util.deepExtend(clonedOptions, item.options, true); + } + return clonedOptions; + } + +} + +export default NetworkUtil; \ No newline at end of file diff --git a/lib/vis/lib/network/css/network-colorpicker.css b/lib/vis/lib/network/css/network-colorpicker.css index db1d49c631..07406fa859 100644 --- a/lib/vis/lib/network/css/network-colorpicker.css +++ b/lib/vis/lib/network/css/network-colorpicker.css @@ -1,14 +1,17 @@ div.vis-color-picker { position:absolute; + top: 0px; + left: 30px; margin-top:-140px; margin-left:30px; - width:293px; - height:425px; + width:310px; + height:444px; + z-index: 1; padding: 10px; border-radius:15px; background-color:#ffffff; - display:none; + display: none; box-shadow: rgba(0,0,0,0.5) 0px 0px 10px 0px; } @@ -18,8 +21,8 @@ div.vis-color-picker div.vis-arrow { left:5px; } -div.vis-color-picker div.vis-arrow:after, -div.vis-color-picker div.vis-arrow:before { +div.vis-color-picker div.vis-arrow::after, +div.vis-color-picker div.vis-arrow::before { right: 100%; top: 50%; border: solid transparent; diff --git a/lib/vis/lib/network/css/network-manipulation.css b/lib/vis/lib/network/css/network-manipulation.css index 6014a2a2d4..89c1dc2cb3 100644 --- a/lib/vis/lib/network/css/network-manipulation.css +++ b/lib/vis/lib/network/css/network-manipulation.css @@ -12,17 +12,18 @@ div.vis-network div.vis-manipulation { background: linear-gradient(to bottom, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* W3C */ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc',GradientType=0 ); /* IE6-9 */ + padding-top:4px; position: absolute; left: 0; top: 0; width: 100%; - height: 30px; + height: 28px; } div.vis-network div.vis-edit-mode { position:absolute; left: 0; - top: 15px; + top: 5px; height: 30px; } @@ -53,8 +54,7 @@ div.vis-network div.vis-close:hover { div.vis-network div.vis-manipulation div.vis-button, div.vis-network div.vis-edit-mode div.vis-button { - position:relative; - top:-7px; + float:left; font-family: verdana; font-size: 12px; -moz-border-radius: 15px; @@ -63,8 +63,8 @@ div.vis-network div.vis-edit-mode div.vis-button { background-position: 0px 0px; background-repeat:no-repeat; height:24px; - margin: 0px 0px 0px 10px; - vertical-align:middle; + margin-left: 10px; + /*vertical-align:middle;*/ cursor: pointer; padding: 0px 8px 0px 8px; -webkit-touch-callout: none; @@ -130,11 +130,12 @@ div.vis-network div.vis-edit-mode div.vis-label { line-height: 25px; } div.vis-network div.vis-manipulation div.vis-separator-line { + float:left; display:inline-block; width:1px; - height:20px; + height:21px; background-color: #bdbdbd; - margin: 5px 7px 0 15px; + margin: 0px 7px 0 15px; /*top right bottom left*/ } /* TODO: is this redundant? diff --git a/lib/vis/lib/network/gephiParser.js b/lib/vis/lib/network/gephiParser.js index a7deb34e13..124d6b4543 100644 --- a/lib/vis/lib/network/gephiParser.js +++ b/lib/vis/lib/network/gephiParser.js @@ -27,6 +27,11 @@ function parseGephi(gephiJSON, optionsObj) { edge['from'] = gEdge.source; edge['to'] = gEdge.target; edge['attributes'] = gEdge.attributes; + edge['label'] = gEdge.label; + edge['title'] = gEdge.attributes !== undefined ? gEdge.attributes.title : undefined; + if (gEdge['type'] === 'Directed') { + edge['arrows'] = 'to'; + } // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; if (gEdge.color && options.inheritColor === false) { @@ -44,6 +49,7 @@ function parseGephi(gephiJSON, optionsObj) { node['x'] = gNode.x; node['y'] = gNode.y; node['label'] = gNode.label; + node['title'] = gNode.attributes !== undefined ? gNode.attributes.title : undefined; if (options.nodes.parseColor === true) { node['color'] = gNode.color; } diff --git a/lib/vis/lib/network/locales.js b/lib/vis/lib/network/locales.js index 82bf0c5570..ba53f3a42d 100644 --- a/lib/vis/lib/network/locales.js +++ b/lib/vis/lib/network/locales.js @@ -17,6 +17,42 @@ exports['en'] = { exports['en_EN'] = exports['en']; exports['en_US'] = exports['en']; +// German +exports['de'] = { + edit: 'Editieren', + del: 'L\u00f6sche Auswahl', + back: 'Zur\u00fcck', + addNode: 'Knoten hinzuf\u00fcgen', + addEdge: 'Kante hinzuf\u00fcgen', + editNode: 'Knoten editieren', + editEdge: 'Kante editieren', + addDescription: 'Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.', + edgeDescription: 'Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.', + editEdgeDescription: 'Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.', + createEdgeError: 'Es ist nicht m\u00f6glich, Kanten mit Clustern zu verbinden.', + deleteClusterError: 'Cluster k\u00f6nnen nicht gel\u00f6scht werden.', + editClusterError: 'Cluster k\u00f6nnen nicht editiert werden.' +}; +exports['de_DE'] = exports['de']; + +// Spanish +exports['es'] = { + edit: 'Editar', + del: 'Eliminar selecci\u00f3n', + back: '\u00c1tras', + addNode: 'A\u00f1adir nodo', + addEdge: 'A\u00f1adir arista', + editNode: 'Editar nodo', + editEdge: 'Editar arista', + addDescription: 'Haga clic en un lugar vac\u00edo para colocar un nuevo nodo.', + edgeDescription: 'Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.', + editEdgeDescription: 'Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.', + createEdgeError: 'No se puede conectar una arista a un grupo.', + deleteClusterError: 'No es posible eliminar grupos.', + editClusterError: 'No es posible editar grupos.' +}; +exports['es_ES'] = exports['es']; + // Dutch exports['nl'] = { edit: 'Wijzigen', diff --git a/lib/vis/lib/network/modules/Canvas.js b/lib/vis/lib/network/modules/Canvas.js index 5589712efa..9abe1f5df3 100644 --- a/lib/vis/lib/network/modules/Canvas.js +++ b/lib/vis/lib/network/modules/Canvas.js @@ -16,6 +16,7 @@ class Canvas { this.pixelRatio = 1; this.resizeTimer = undefined; this.resizeFunction = this._onResize.bind(this); + this.cameraState = {}; this.options = {}; this.defaultOptions = { @@ -82,6 +83,58 @@ class Canvas { this.body.emitter.emit("_redraw"); } + /** + * Get and store the cameraState + * @private + */ + _getCameraState(pixelRatio = this.pixelRatio) { + this.cameraState.previousWidth = this.frame.canvas.width / pixelRatio; + this.cameraState.previousHeight = this.frame.canvas.height / pixelRatio; + this.cameraState.scale = this.body.view.scale; + this.cameraState.position = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.width / pixelRatio, y: 0.5 * this.frame.canvas.height / pixelRatio}); + } + + /** + * Set the cameraState + * @private + */ + _setCameraState() { + if (this.cameraState.scale !== undefined && + this.frame.canvas.clientWidth !== 0 && + this.frame.canvas.clientHeight !== 0 && + this.pixelRatio !== 0 && + this.cameraState.previousWidth > 0) { + + let widthRatio = (this.frame.canvas.width / this.pixelRatio) / this.cameraState.previousWidth; + let heightRatio = (this.frame.canvas.height / this.pixelRatio) / this.cameraState.previousHeight; + let newScale = this.cameraState.scale; + + if (widthRatio != 1 && heightRatio != 1) { + newScale = this.cameraState.scale * 0.5 * (widthRatio + heightRatio); + } + else if (widthRatio != 1) { + newScale = this.cameraState.scale * widthRatio; + } + else if (heightRatio != 1) { + newScale = this.cameraState.scale * heightRatio; + } + + this.body.view.scale = newScale; + // this comes from the view module. + var currentViewCenter = this.DOMtoCanvas({ + x: 0.5 * this.frame.canvas.clientWidth, + y: 0.5 * this.frame.canvas.clientHeight + }); + + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: currentViewCenter.x - this.cameraState.position.x, + y: currentViewCenter.y - this.cameraState.position.y + }; + this.body.view.translation.x += distanceFromCenter.x * this.body.view.scale; + this.body.view.translation.y += distanceFromCenter.y * this.body.view.scale; + } + } + _prepareValue(value) { if (typeof value === 'number') { return value + 'px'; @@ -94,7 +147,7 @@ class Canvas { return value + 'px'; } } - throw new Error('Could not use the value supplie for width or height:' + value); + throw new Error('Could not use the value supplied for width or height:' + value); } @@ -201,7 +254,18 @@ class Canvas { let oldWidth = this.frame.canvas.width; let oldHeight = this.frame.canvas.height; + // update the pixel ratio + let ctx = this.frame.canvas.getContext("2d"); + let previousRatio = this.pixelRatio; // we cache this because the camera state storage needs the old value + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); + if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) { + this._getCameraState(previousRatio); + this.frame.style.width = width; this.frame.style.height = height; @@ -220,6 +284,11 @@ class Canvas { // this would adapt the width of the canvas to the width from 100% if and only if // there is a change. + // store the camera if there is a change in size. + if (this.frame.canvas.width != Math.round(this.frame.canvas.clientWidth * this.pixelRatio) || this.frame.canvas.height != Math.round(this.frame.canvas.clientHeight * this.pixelRatio)) { + this._getCameraState(previousRatio); + } + if (this.frame.canvas.width != Math.round(this.frame.canvas.clientWidth * this.pixelRatio)) { this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio); emitEvent = true; @@ -237,6 +306,9 @@ class Canvas { oldWidth: Math.round(oldWidth / this.pixelRatio), oldHeight: Math.round(oldHeight / this.pixelRatio) }); + + // restore the camera on change. + this._setCameraState(); } return emitEvent; diff --git a/lib/vis/lib/network/modules/CanvasRenderer.js b/lib/vis/lib/network/modules/CanvasRenderer.js index 8673fdd39d..a6c4ae1b2c 100644 --- a/lib/vis/lib/network/modules/CanvasRenderer.js +++ b/lib/vis/lib/network/modules/CanvasRenderer.js @@ -147,13 +147,11 @@ class CanvasRenderer { this.canvas.setSize(); } - if (this.pixelRatio === undefined) { - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1); - } + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); @@ -162,6 +160,11 @@ class CanvasRenderer { let h = this.canvas.frame.canvas.clientHeight; ctx.clearRect(0, 0, w, h); + // if the div is hidden, we stop the redraw here for performance. + if (this.canvas.frame.clientWidth === 0) { + return; + } + // set scaling and translation ctx.save(); ctx.translate(this.body.view.translation.x, this.body.view.translation.y); @@ -181,17 +184,13 @@ class CanvasRenderer { this._drawNodes(ctx, hidden); } - if (this.controlNodesActive === true) { - this._drawControlNodes(ctx); - } - ctx.beginPath(); - //this.physics.nodesSolver._debug(ctx,"#F00F0F"); this.body.emitter.emit("afterDrawing", ctx); ctx.closePath(); + + // restore original scaling and translation ctx.restore(); - if (hidden === true) { ctx.clearRect(0, 0, w, h); } @@ -256,7 +255,6 @@ class CanvasRenderer { }); let viewableArea = {top:topLeft.y,left:topLeft.x,bottom:bottomRight.y,right:bottomRight.x}; - // draw unselected nodes; for (let i = 0; i < nodeIndices.length; i++) { node = nodes[nodeIndices[i]]; @@ -304,23 +302,6 @@ class CanvasRenderer { } } - /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - _drawControlNodes(ctx) { - let edges = this.body.edges; - let edgeIndices = this.body.edgeIndices; - let edge; - - for (let i = 0; i < edgeIndices.length; i++) { - edge = edges[edgeIndices[i]]; - edge._drawControlNodes(ctx); - } - } - /** * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because * some implementations (safari and IE9) did not support requestAnimationFrame diff --git a/lib/vis/lib/network/modules/Clustering.js b/lib/vis/lib/network/modules/Clustering.js index 9fe8c42108..6b3c14b214 100644 --- a/lib/vis/lib/network/modules/Clustering.js +++ b/lib/vis/lib/network/modules/Clustering.js @@ -1,16 +1,18 @@ let util = require("../../util"); +import NetworkUtil from '../NetworkUtil'; import Cluster from './components/nodes/Cluster' class ClusterEngine { constructor(body) { this.body = body; this.clusteredNodes = {}; + this.clusteredEdges = {}; this.options = {}; this.defaultOptions = {}; util.extend(this.options, this.defaultOptions); - this.body.emitter.on('_resetData', () => {this.clusteredNodes = {};}) + this.body.emitter.on('_resetData', () => {this.clusteredNodes = {}; this.clusteredEdges = {};}) } setOptions(options) { @@ -42,8 +44,9 @@ class ClusterEngine { } for (let i = 0; i < nodesToCluster.length; i++) { - this.clusterByConnection(nodesToCluster[i],options,false); + this.clusterByConnection(nodesToCluster[i],options,true); } + this.body.emitter.emit('_dataChanged'); } @@ -66,14 +69,16 @@ class ClusterEngine { for (let i = 0; i < this.body.nodeIndices.length; i++) { let nodeId = this.body.nodeIndices[i]; let node = this.body.nodes[nodeId]; - let clonedOptions = this._cloneOptions(node); + let clonedOptions = NetworkUtil.cloneOptions(node); if (options.joinCondition(clonedOptions) === true) { childNodesObj[nodeId] = this.body.nodes[nodeId]; // collect the nodes that will be in the cluster for (let i = 0; i < node.edges.length; i++) { let edge = node.edges[i]; - childEdgesObj[edge.id] = edge; + if (this.clusteredEdges[edge.id] === undefined) { + childEdgesObj[edge.id] = edge; + } } } } @@ -83,53 +88,67 @@ class ClusterEngine { /** - * Cluster all nodes in the network that have only 1 edge - * @param options - * @param refreshData - */ - clusterOutliers(options, refreshData = true) { + * Cluster all nodes in the network that have only X edges + * @param edgeCount + * @param options + * @param refreshData + */ + clusterByEdgeCount(edgeCount, options, refreshData = true) { options = this._checkOptions(options); let clusters = []; - + let usedNodes = {}; + let edge, edges, node, nodeId, relevantEdgeCount; // collect the nodes that will be in the cluster for (let i = 0; i < this.body.nodeIndices.length; i++) { let childNodesObj = {}; let childEdgesObj = {}; - let nodeId = this.body.nodeIndices[i]; - let visibleEdges = 0; - let edge; - for (let j = 0; j < this.body.nodes[nodeId].edges.length; j++) { - if (this.body.nodes[nodeId].edges[j].options.hidden === false) { - visibleEdges++; - edge = this.body.nodes[nodeId].edges[j]; + nodeId = this.body.nodeIndices[i]; + + // if this node is already used in another cluster this session, we do not have to re-evaluate it. + if (usedNodes[nodeId] === undefined) { + relevantEdgeCount = 0; + node = this.body.nodes[nodeId]; + edges = []; + for (let j = 0; j < node.edges.length; j++) { + edge = node.edges[j]; + if (this.clusteredEdges[edge.id] === undefined) { + if (edge.toId !== edge.fromId) { + relevantEdgeCount++; + } + edges.push(edge); + } } - } - if (visibleEdges === 1) { - // this is an outlier - let childNodeId = this._getConnectedId(edge, nodeId); - if (childNodeId !== nodeId) { - if (options.joinCondition === undefined) { - if (this._checkIfUsed(clusters,nodeId,edge.id) === false && this._checkIfUsed(clusters,childNodeId,edge.id) === false) { + // this node qualifies, we collect its neighbours to start the clustering process. + if (relevantEdgeCount === edgeCount) { + let gatheringSuccessful = true; + for (let j = 0; j < edges.length; j++) { + edge = edges[j]; + let childNodeId = this._getConnectedId(edge, nodeId); + // add the nodes to the list by the join condition. + if (options.joinCondition === undefined) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = this.body.nodes[nodeId]; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + usedNodes[nodeId] = true; } - } - else { - let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]); - if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { - childEdgesObj[edge.id] = edge; - childNodesObj[nodeId] = this.body.nodes[nodeId]; - } - clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) { - childEdgesObj[edge.id] = edge; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + else { + let clonedOptions = NetworkUtil.cloneOptions(this.body.nodes[nodeId]); + if (options.joinCondition(clonedOptions) === true) { + childEdgesObj[edge.id] = edge; + childNodesObj[nodeId] = this.body.nodes[nodeId]; + usedNodes[nodeId] = true; + } + else { + // this node does not qualify after all. + gatheringSuccessful = false; + break; + } } } - if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) { + // add to the cluster queue + if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0 && gatheringSuccessful === true) { clusters.push({nodes: childNodesObj, edges: childEdgesObj}) } } @@ -145,17 +164,26 @@ class ClusterEngine { } } - - _checkIfUsed(clusters, nodeId, edgeId) { - for (let i = 0; i < clusters.length; i++) { - let cluster = clusters[i]; - if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) { - return true; - } - } - return false; + /** + * Cluster all nodes in the network that have only 1 edge + * @param options + * @param refreshData + */ + clusterOutliers(options, refreshData = true) { + this.clusterByEdgeCount(1,options,refreshData); } + /** + * Cluster all nodes in the network that have only 2 edge + * @param options + * @param refreshData + */ + clusterBridges(options, refreshData = true) { + this.clusterByEdgeCount(2,options,refreshData); + } + + + /** * suck all connected nodes of a node into the node. * @param nodeId @@ -181,31 +209,37 @@ class ClusterEngine { let childNodesObj = {}; let childEdgesObj = {}; let parentNodeId = node.id; - let parentClonedOptions = this._cloneOptions(node); + let parentClonedOptions = NetworkUtil.cloneOptions(node); childNodesObj[parentNodeId] = node; // collect the nodes that will be in the cluster for (let i = 0; i < node.edges.length; i++) { let edge = node.edges[i]; - let childNodeId = this._getConnectedId(edge, parentNodeId); + if (this.clusteredEdges[edge.id] === undefined) { + let childNodeId = this._getConnectedId(edge, parentNodeId); - if (childNodeId !== parentNodeId) { - if (options.joinCondition === undefined) { - childEdgesObj[edge.id] = edge; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; - } - else { - // clone the options and insert some additional parameters that could be interesting. - let childClonedOptions = this._cloneOptions(this.body.nodes[childNodeId]); - if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) { + // if the child node is not in a cluster + if (this.clusteredNodes[childNodeId] === undefined) { + if (childNodeId !== parentNodeId) { + if (options.joinCondition === undefined) { + childEdgesObj[edge.id] = edge; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } + else { + // clone the options and insert some additional parameters that could be interesting. + let childClonedOptions = NetworkUtil.cloneOptions(this.body.nodes[childNodeId]); + if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) { + childEdgesObj[edge.id] = edge; + childNodesObj[childNodeId] = this.body.nodes[childNodeId]; + } + } + } + else { + // swallow the edge if it is self-referencing. childEdgesObj[edge.id] = edge; - childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } } - else { - childEdgesObj[edge.id] = edge; - } } this._cluster(childNodesObj, childEdgesObj, options, refreshData); @@ -213,40 +247,22 @@ class ClusterEngine { /** - * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes. - * @param objId - * @param type - * @returns {{}} - * @private - */ - _cloneOptions(item, type) { - let clonedOptions = {}; - if (type === undefined || type === 'node') { - util.deepExtend(clonedOptions, item.options, true); - clonedOptions.x = item.x; - clonedOptions.y = item.y; - clonedOptions.amountOfConnections = item.edges.length; - } - else { - util.deepExtend(clonedOptions, item.options, true); - } - return clonedOptions; - } - - - /** - * This function creates the edges that will be attached to the cluster. + * This function creates the edges that will be attached to the cluster + * It looks for edges that are connected to the nodes from the "outside' of the cluster. * * @param childNodesObj * @param childEdgesObj - * @param newEdges - * @param options + * @param clusterNodeProperties + * @param clusterEdgeProperties * @private */ - _createClusterEdges (childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, clusterEdgeProperties) { + _createClusterEdges (childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { let edge, childNodeId, childNode, toId, fromId, otherNodeId; + // loop over all child nodes and their edges to find edges going out of the cluster + // these edges will be replaced by clusterEdges. let childKeys = Object.keys(childNodesObj); + let createEdges = []; for (let i = 0; i < childKeys.length; i++) { childNodeId = childKeys[i]; childNode = childNodesObj[childNodeId]; @@ -254,31 +270,62 @@ class ClusterEngine { // construct new edges from the cluster to others for (let j = 0; j < childNode.edges.length; j++) { edge = childNode.edges[j]; - childEdgesObj[edge.id] = edge; + // we only handle edges that are visible to the system, not the disabled ones from the clustering process. + if (this.clusteredEdges[edge.id] === undefined) { + // self-referencing edges will be added to the "hidden" list + if (edge.toId == edge.fromId) { + childEdgesObj[edge.id] = edge; + } + else { + // set up the from and to. + if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here. + toId = clusterNodeProperties.id; + fromId = edge.fromId; + otherNodeId = fromId; + } + else { + toId = edge.toId; + fromId = clusterNodeProperties.id; + otherNodeId = toId; + } + } - // childNodeId position will be replaced by the cluster. - if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here. - toId = clusterNodeProperties.id; - fromId = edge.fromId; - otherNodeId = fromId; - } - else { - toId = edge.toId; - fromId = clusterNodeProperties.id; - otherNodeId = toId; - } - - // if the node connected to the cluster is also in the cluster we do not need a new edge. - if (childNodesObj[otherNodeId] === undefined) { - let clonedOptions = this._cloneOptions(edge, 'edge'); - util.deepExtend(clonedOptions, clusterEdgeProperties); - clonedOptions.from = fromId; - clonedOptions.to = toId; - clonedOptions.id = 'clusterEdge:' + util.randomUUID(); - newEdges.push(this.body.functions.createEdge(clonedOptions)); + // Only edges from the cluster outwards are being replaced. + if (childNodesObj[otherNodeId] === undefined) { + createEdges.push({edge: edge, fromId: fromId, toId: toId}); + } } } } + + // here we actually create the replacement edges. We could not do this in the loop above as the creation process + // would add an edge to the edges array we are iterating over. + for (let j = 0; j < createEdges.length; j++) { + let edge = createEdges[j].edge; + // copy the options of the edge we will replace + let clonedOptions = NetworkUtil.cloneOptions(edge, 'edge'); + // make sure the properties of clusterEdges are superimposed on it + util.deepExtend(clonedOptions, clusterEdgeProperties); + + // set up the edge + clonedOptions.from = createEdges[j].fromId; + clonedOptions.to = createEdges[j].toId; + clonedOptions.id = 'clusterEdge:' + util.randomUUID(); + //clonedOptions.id = '(cf: ' + createEdges[j].fromId + " to: " + createEdges[j].toId + ")" + Math.random(); + + // create the edge and give a reference to the one it replaced. + let newEdge = this.body.functions.createEdge(clonedOptions); + newEdge.clusteringEdgeReplacingId = edge.id; + + // connect the edge. + this.body.edges[newEdge.id] = newEdge; + newEdge.connect(); + + // hide the replaced edge + this._backupEdgeOptions(edge); + edge.setOptions({physics:false, hidden:true}); + } + } /** @@ -304,8 +351,17 @@ class ClusterEngine { * @private */ _cluster(childNodesObj, childEdgesObj, options, refreshData = true) { - // kill condition: no children so cant cluster - if (Object.keys(childNodesObj).length === 0) {return;} + // kill condition: no children so can't cluster or only one node in the cluster, don't bother + if (Object.keys(childNodesObj).length < 2) {return;} + + // check if this cluster call is not trying to cluster anything that is in another cluster. + for (let nodeId in childNodesObj) { + if (childNodesObj.hasOwnProperty(nodeId)) { + if (this.clusteredNodes[nodeId] !== undefined) { + return; + } + } + } let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties); @@ -314,17 +370,21 @@ class ClusterEngine { // get the childNode options let childNodesOptions = []; for (let nodeId in childNodesObj) { - let clonedOptions = this._cloneOptions(childNodesObj[nodeId]); - childNodesOptions.push(clonedOptions); + if (childNodesObj.hasOwnProperty(nodeId)) { + let clonedOptions = NetworkUtil.cloneOptions(childNodesObj[nodeId]); + childNodesOptions.push(clonedOptions); + } } - // get clusterproperties based on childNodes + // get cluster properties based on childNodes let childEdgesOptions = []; for (let edgeId in childEdgesObj) { - // these cluster edges will be removed on creation of the cluster. - if (edgeId.substr(0,12) !== "clusterEdge:") { - let clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge'); - childEdgesOptions.push(clonedOptions); + if (childEdgesObj.hasOwnProperty(edgeId)) { + // these cluster edges will be removed on creation of the cluster. + if (edgeId.substr(0, 12) !== "clusterEdge:") { + let clonedOptions = NetworkUtil.cloneOptions(childEdgesObj[edgeId], 'edge'); + childEdgesOptions.push(clonedOptions); + } } } @@ -343,16 +403,14 @@ class ClusterEngine { } - // give the clusterNode a postion if it does not have one. + // give the clusterNode a position if it does not have one. let pos = undefined; if (clusterNodeProperties.x === undefined) { pos = this._getClusterPosition(childNodesObj); clusterNodeProperties.x = pos.x; } if (clusterNodeProperties.y === undefined) { - if (pos === undefined) { - pos = this._getClusterPosition(childNodesObj); - } + if (pos === undefined) {pos = this._getClusterPosition(childNodesObj);} clusterNodeProperties.y = pos.y; } @@ -370,29 +428,18 @@ class ClusterEngine { // finally put the cluster node into global this.body.nodes[clusterNodeProperties.id] = clusterNode; - // create the new edges that will connect to the cluster - let newEdges = []; - this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, options.clusterEdgeProperties); + // create the new edges that will connect to the cluster, all self-referencing edges will be added to childEdgesObject here. + this._createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties); // disable the childEdges for (let edgeId in childEdgesObj) { if (childEdgesObj.hasOwnProperty(edgeId)) { if (this.body.edges[edgeId] !== undefined) { let edge = this.body.edges[edgeId]; - - // if this is a cluster edge that is fully encompassed in the cluster, we want to delete it - // this check verifies that both of the connected nodes are in this cluster - if (edgeId.substr(0,12) === "clusterEdge:" && childNodesObj[edge.fromId] !== undefined && childNodesObj[edge.toId] !== undefined) { - edge.cleanup(); - // this removes the edge from node.edges, which is why edgeIds is formed - edge.disconnect(); - delete childEdgesObj[edgeId]; - delete this.body.edges[edgeId]; - } - else { - edge.setOptions({physics:false, hidden:true}); - //edge.options.hidden = true; - } + // cache the options before changing + this._backupEdgeOptions(edge); + // disable physics and hide the edge + edge.setOptions({physics:false, hidden:true}); } } } @@ -405,12 +452,6 @@ class ClusterEngine { } } - // push new edges to global - for (let i = 0; i < newEdges.length; i++) { - this.body.edges[newEdges[i].id] = newEdges[i]; - this.body.edges[newEdges[i].id].connect(); - } - // set ID to undefined so no duplicates arise clusterNodeProperties.id = undefined; @@ -420,6 +461,20 @@ class ClusterEngine { } } + _backupEdgeOptions(edge) { + if (this.clusteredEdges[edge.id] === undefined) { + this.clusteredEdges[edge.id] = {physics: edge.options.physics, hidden: edge.options.hidden}; + } + } + + _restoreEdge(edge) { + let originalOptions = this.clusteredEdges[edge.id]; + if (originalOptions !== undefined) { + edge.setOptions({physics: originalOptions.physics, hidden: originalOptions.hidden}); + delete this.clusteredEdges[edge.id]; + } + } + /** * Check if a node is a cluster. @@ -496,8 +551,8 @@ class ClusterEngine { if (containedNodes.hasOwnProperty(nodeId)) { let containedNode = this.body.nodes[nodeId]; if (newPositions[nodeId] !== undefined) { - containedNode.x = newPositions[nodeId].x || clusterNode.x; - containedNode.y = newPositions[nodeId].y || clusterNode.y; + containedNode.x = (newPositions[nodeId].x === undefined ? clusterNode.x : newPositions[nodeId].x); + containedNode.y = (newPositions[nodeId].y === undefined ? clusterNode.y : newPositions[nodeId].y); } } } @@ -509,8 +564,8 @@ class ClusterEngine { let containedNode = this.body.nodes[nodeId]; containedNode = containedNodes[nodeId]; // inherit position - containedNode.x = clusterNode.x; - containedNode.y = clusterNode.y; + if (containedNode.options.fixed.x === false) {containedNode.x = clusterNode.x;} + if (containedNode.options.fixed.y === false) {containedNode.y = clusterNode.y;} } } } @@ -524,83 +579,81 @@ class ClusterEngine { containedNode.vx = clusterNode.vx; containedNode.vy = clusterNode.vy; - // we use these methods to avoid reinstantiating the shape, which happens with setOptions. - //containedNode.toggleHidden(false); - //containedNode.togglePhysics(true); + // we use these methods to avoid re-instantiating the shape, which happens with setOptions. containedNode.setOptions({hidden:false, physics:true}); delete this.clusteredNodes[nodeId]; } } - // release edges - for (let edgeId in containedEdges) { - if (containedEdges.hasOwnProperty(edgeId)) { - let edge = containedEdges[edgeId]; - // if this edge was a temporary edge and it's connected nodes do not exist anymore, we remove it from the data - if (this.body.nodes[edge.fromId] === undefined || this.body.nodes[edge.toId] === undefined || edge.toId == clusterNodeId || edge.fromId == clusterNodeId) { - edge.cleanup(); - // this removes the edge from node.edges, which is why edgeIds is formed - edge.disconnect(); - delete this.body.edges[edgeId]; - } - else { - // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. - if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { - let fromId, toId; - let clusteredNode = this.clusteredNodes[edge.fromId] || this.clusteredNodes[edge.toId]; - let clusterId = clusteredNode.clusterId; - let clusterNode = this.body.nodes[clusterId]; - clusterNode.containedEdges[edgeId] = edge; + // copy the clusterNode edges because we cannot iterate over an object that we add or remove from. + let edgesToBeDeleted = []; + for (let i = 0; i < clusterNode.edges.length; i++) { + edgesToBeDeleted.push(clusterNode.edges[i]); + } - if (this.clusteredNodes[edge.fromId] !== undefined) { - fromId = clusterId; - toId = edge.toId; - } - else { - fromId = edge.fromId; - toId = clusterId; - } + // actually handling the deleting. + for (let i = 0; i < edgesToBeDeleted.length; i++) { + let edge = edgesToBeDeleted[i]; - // if both from and to nodes are visible, we create a new temporary edge - if (this.body.nodes[fromId].options.hidden !== true && this.body.nodes[toId].options.hidden !== true) { - let clonedOptions = this._cloneOptions(edge, 'edge'); - let id = 'clusterEdge:' + util.randomUUID(); - util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties); - util.deepExtend(clonedOptions, {from: fromId, to: toId, hidden: false, physics: true, id: id}); - let newEdge = this.body.functions.createEdge(clonedOptions); + let otherNodeId = this._getConnectedId(edge, clusterNodeId); + // if the other node is in another cluster, we transfer ownership of this edge to the other cluster + if (this.clusteredNodes[otherNodeId] !== undefined) { + // transfer ownership: + let otherCluster = this.body.nodes[this.clusteredNodes[otherNodeId].clusterId]; + let transferEdge = this.body.edges[edge.clusteringEdgeReplacingId]; + if (transferEdge !== undefined) { + otherCluster.containedEdges[transferEdge.id] = transferEdge; - this.body.edges[id] = newEdge; - this.body.edges[id].connect(); - } + // delete local reference + delete containedEdges[transferEdge.id]; + + // create new cluster edge from the otherCluster: + // get to and from + let fromId = transferEdge.fromId; + let toId = transferEdge.toId; + if (transferEdge.toId == otherNodeId) { + toId = this.clusteredNodes[otherNodeId].clusterId; } else { - edge.setOptions({physics:true, hidden:false}); - //edge.options.hidden = false; - //edge.togglePhysics(true); + fromId = this.clusteredNodes[otherNodeId].clusterId; } + + // clone the options and apply the cluster options to them + let clonedOptions = NetworkUtil.cloneOptions(transferEdge, 'edge'); + util.deepExtend(clonedOptions, otherCluster.clusterEdgeProperties); + + // apply the edge specific options to it. + let id = 'clusterEdge:' + util.randomUUID(); + util.deepExtend(clonedOptions, {from: fromId, to: toId, hidden: false, physics: true, id: id}); + + // create it + let newEdge = this.body.functions.createEdge(clonedOptions); + newEdge.clusteringEdgeReplacingId = transferEdge.id; + this.body.edges[id] = newEdge; + this.body.edges[id].connect(); } } + else { + let replacedEdge = this.body.edges[edge.clusteringEdgeReplacingId]; + if (replacedEdge !== undefined) { + this._restoreEdge(replacedEdge); + } + } + edge.cleanup(); + // this removes the edge from node.edges, which is why edgeIds is formed + edge.disconnect(); + delete this.body.edges[edge.id]; + } + + // handle the releasing of the edges + for (let edgeId in containedEdges) { + if (containedEdges.hasOwnProperty(edgeId)) { + this._restoreEdge(containedEdges[edgeId]); + } } - // remove all temporary edges, make an array of ids so we don't remove from the list we're iterating over. - let removeIds = []; - for (let i = 0; i < clusterNode.edges.length; i++) { - let edgeId = clusterNode.edges[i].id; - removeIds.push(edgeId); - } - - // actually removing the edges - for (let i = 0; i < removeIds.length; i++) { - let edgeId = removeIds[i]; - this.body.edges[edgeId].cleanup(); - // this removes the edge from node.edges, which is why edgeIds is formed - this.body.edges[edgeId].disconnect(); - delete this.body.edges[edgeId]; - } - // remove clusterNode - this.body.nodes[clusterNodeId].cleanup(); delete this.body.nodes[clusterNodeId]; if (refreshData === true) { @@ -609,12 +662,12 @@ class ClusterEngine { } getNodesInCluster(clusterId) { - let nodesArray = [] + let nodesArray = []; if (this.isCluster(clusterId) === true) { let containedNodes = this.body.nodes[clusterId].containedNodes; for (let nodeId in containedNodes) { if (containedNodes.hasOwnProperty(nodeId)) { - nodesArray.push(nodeId) + nodesArray.push(this.body.nodes[nodeId].id) } } } @@ -626,7 +679,6 @@ class ClusterEngine { * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node * @param nodeId * @returns {Array} - * @private */ findNode(nodeId) { let stack = []; @@ -634,11 +686,13 @@ class ClusterEngine { let counter = 0; while (this.clusteredNodes[nodeId] !== undefined && counter < max) { - stack.push(this.clusteredNodes[nodeId].node); + stack.push(this.body.nodes[nodeId].id); nodeId = this.clusteredNodes[nodeId].clusterId; counter++; } - stack.push(this.body.nodes[nodeId]); + stack.push(this.body.nodes[nodeId].id); + stack.reverse(); + return stack; } diff --git a/lib/vis/lib/network/modules/EdgesHandler.js b/lib/vis/lib/network/modules/EdgesHandler.js index 41abcb6a20..4be26d95d1 100644 --- a/lib/vis/lib/network/modules/EdgesHandler.js +++ b/lib/vis/lib/network/modules/EdgesHandler.js @@ -27,6 +27,7 @@ class EdgesHandler { middle: {enabled: false, scaleFactor:1}, from: {enabled: false, scaleFactor:1} }, + arrowStrikethrough: true, color: { color:'#848484', highlight:'#848484', @@ -74,6 +75,7 @@ class EdgesHandler { selfReferenceSize:20, shadow:{ enabled: false, + color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 @@ -106,7 +108,7 @@ class EdgesHandler { let edge = this.body.edges[edgeId]; let edgeData = this.body.data.edges._data[edgeId]; - // only forcilby remove the smooth curve if the data has been set of the edge has the smooth curves defined. + // only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined. // this is because a change in the global would not affect these curves. if (edgeData !== undefined) { let edgeOptions = edgeData.smooth; @@ -139,6 +141,10 @@ class EdgesHandler { this.body.emitter.on("refreshEdges", this.refresh.bind(this)); this.body.emitter.on("refresh", this.refresh.bind(this)); this.body.emitter.on("destroy", () => { + util.forEach(this.edgesListeners, (callback, event) => { + if (this.body.data.edges) + this.body.data.edges.off(event, callback); + }); delete this.body.functions.createEdge; delete this.edgesListeners.add; delete this.edgesListeners.update; @@ -153,7 +159,7 @@ class EdgesHandler { // use the parser from the Edge class to fill in all shorthand notations Edge.parseOptions(this.options, options); - // hanlde multiple input cases for color + // handle multiple input cases for color if (options.color !== undefined) { this.markAllEdgesAsDirty(); } diff --git a/lib/vis/lib/network/modules/InteractionHandler.js b/lib/vis/lib/network/modules/InteractionHandler.js index 831dde5e58..f1b7fecd02 100644 --- a/lib/vis/lib/network/modules/InteractionHandler.js +++ b/lib/vis/lib/network/modules/InteractionHandler.js @@ -190,10 +190,12 @@ class InteractionHandler { let currentSelection = this.selectionHandler.getSelection(); let {nodesChanges, edgesChanges} = this._determineIfDifferent(previousSelection, currentSelection); + let nodeSelected = false; if (selectedNodesCount - previouslySelectedNodeCount > 0) { // node was selected this.selectionHandler._generateClickEvent('selectNode', event, pointer); selected = true; + nodeSelected = true; } else if (selectedNodesCount - previouslySelectedNodeCount < 0) { // node was deselected this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); @@ -202,10 +204,12 @@ class InteractionHandler { else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) { this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); this.selectionHandler._generateClickEvent('selectNode', event, pointer); + nodeSelected = true; selected = true; } - if (selectedEdgesCount - previouslySelectedEdgeCount > 0) { // edge was selected + // handle the selected edges + if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) { // edge was selected this.selectionHandler._generateClickEvent('selectEdge', event, pointer); selected = true; } @@ -219,7 +223,7 @@ class InteractionHandler { selected = true; } - + // fire the select event if anything has been selected or deselected if (selected === true) { // select or unselect this.selectionHandler._generateClickEvent('select', event, pointer); } @@ -483,38 +487,41 @@ class InteractionHandler { * @private */ onMouseWheel(event) { - // retrieve delta - let 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 !== 0) { - - // calculate the new scale - let scale = this.body.view.scale; - let zoom = delta / 10; - if (delta < 0) { - zoom = zoom / (1 - zoom); + if (this.options.zoomView === true) { + // retrieve delta + let 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; } - scale *= (1 + zoom); - // calculate the pointer location - let pointer = this.getPointer({x:event.clientX, y:event.clientY}); + // 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 !== 0) { - // apply the new scale - this.zoom(scale, pointer); + // calculate the new scale + let scale = this.body.view.scale; + let zoom = delta / 10; + if (delta < 0) { + zoom = zoom / (1 - zoom); + } + scale *= (1 + zoom); + + // calculate the pointer location + let pointer = this.getPointer({x: event.clientX, y: event.clientY}); + + // apply the new scale + this.zoom(scale, pointer); + } + + // Prevent default actions caused by mouse wheel. + event.preventDefault(); } - - // Prevent default actions caused by mouse wheel. - event.preventDefault(); } diff --git a/lib/vis/lib/network/modules/KamadaKawai.js b/lib/vis/lib/network/modules/KamadaKawai.js new file mode 100644 index 0000000000..63d620ff83 --- /dev/null +++ b/lib/vis/lib/network/modules/KamadaKawai.js @@ -0,0 +1,215 @@ +// distance finding algorithm +import FloydWarshall from "./components/algorithms/FloydWarshall.js" + + +/** + * KamadaKawai positions the nodes initially based on + * + * "AN ALGORITHM FOR DRAWING GENERAL UNDIRECTED GRAPHS" + * -- Tomihisa KAMADA and Satoru KAWAI in 1989 + * + * Possible optimizations in the distance calculation can be implemented. + */ +class KamadaKawai { + constructor(body, edgeLength, edgeStrength) { + this.body = body; + this.springLength = edgeLength; + this.springConstant = edgeStrength; + this.distanceSolver = new FloydWarshall(); + } + + /** + * Not sure if needed but can be used to update the spring length and spring constant + * @param options + */ + setOptions(options) { + if (options) { + if (options.springLength) { + this.springLength = options.springLength; + } + if (options.springConstant) { + this.springConstant = options.springConstant; + } + } + } + + + /** + * Position the system + * @param nodesArray + * @param edgesArray + */ + solve(nodesArray, edgesArray, ignoreClusters = false) { + // get distance matrix + let D_matrix = this.distanceSolver.getDistances(this.body, nodesArray, edgesArray); // distance matrix + + // get the L Matrix + this._createL_matrix(D_matrix); + + // get the K Matrix + this._createK_matrix(D_matrix); + + // calculate positions + let threshold = 0.01; + let innerThreshold = 1; + let iterations = 0; + let maxIterations = Math.max(1000,Math.min(10*this.body.nodeIndices.length,6000)); + let maxInnerIterations = 5; + + let maxEnergy = 1e9; + let highE_nodeId = 0, dE_dx = 0, dE_dy = 0, delta_m = 0, subIterations = 0; + + while (maxEnergy > threshold && iterations < maxIterations) { + iterations += 1; + [highE_nodeId, maxEnergy, dE_dx, dE_dy] = this._getHighestEnergyNode(ignoreClusters); + delta_m = maxEnergy; + subIterations = 0; + while(delta_m > innerThreshold && subIterations < maxInnerIterations) { + subIterations += 1; + this._moveNode(highE_nodeId, dE_dx, dE_dy); + [delta_m,dE_dx,dE_dy] = this._getEnergy(highE_nodeId); + } + } + } + + /** + * get the node with the highest energy + * @returns {*[]} + * @private + */ + _getHighestEnergyNode(ignoreClusters) { + let nodesArray = this.body.nodeIndices; + let nodes = this.body.nodes; + let maxEnergy = 0; + let maxEnergyNodeId = nodesArray[0]; + let dE_dx_max = 0, dE_dy_max = 0; + + for (let nodeIdx = 0; nodeIdx < nodesArray.length; nodeIdx++) { + let m = nodesArray[nodeIdx]; + // by not evaluating nodes with predefined positions we should only move nodes that have no positions. + if ((nodes[m].predefinedPosition === false || nodes[m].isCluster === true && ignoreClusters === true) || nodes[m].options.fixed.x === true || nodes[m].options.fixed.y === true) { + let [delta_m,dE_dx,dE_dy] = this._getEnergy(m); + if (maxEnergy < delta_m) { + maxEnergy = delta_m; + maxEnergyNodeId = m; + dE_dx_max = dE_dx; + dE_dy_max = dE_dy; + } + } + } + + return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max]; + } + + /** + * calculate the energy of a single node + * @param m + * @returns {*[]} + * @private + */ + _getEnergy(m) { + let nodesArray = this.body.nodeIndices; + let nodes = this.body.nodes; + + let x_m = nodes[m].x; + let y_m = nodes[m].y; + let dE_dx = 0; + let dE_dy = 0; + for (let iIdx = 0; iIdx < nodesArray.length; iIdx++) { + let i = nodesArray[iIdx]; + if (i !== m) { + let x_i = nodes[i].x; + let y_i = nodes[i].y; + let denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2)); + dE_dx += this.K_matrix[m][i] * ((x_m - x_i) - this.L_matrix[m][i] * (x_m - x_i) * denominator); + dE_dy += this.K_matrix[m][i] * ((y_m - y_i) - this.L_matrix[m][i] * (y_m - y_i) * denominator); + } + } + + let delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2)); + return [delta_m, dE_dx, dE_dy]; + } + + /** + * move the node based on it's energy + * the dx and dy are calculated from the linear system proposed by Kamada and Kawai + * @param m + * @param dE_dx + * @param dE_dy + * @private + */ + _moveNode(m, dE_dx, dE_dy) { + let nodesArray = this.body.nodeIndices; + let nodes = this.body.nodes; + let d2E_dx2 = 0; + let d2E_dxdy = 0; + let d2E_dy2 = 0; + + let x_m = nodes[m].x; + let y_m = nodes[m].y; + for (let iIdx = 0; iIdx < nodesArray.length; iIdx++) { + let i = nodesArray[iIdx]; + if (i !== m) { + let x_i = nodes[i].x; + let y_i = nodes[i].y; + let denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5); + d2E_dx2 += this.K_matrix[m][i] * (1 - this.L_matrix[m][i] * Math.pow(y_m - y_i, 2) * denominator); + d2E_dxdy += this.K_matrix[m][i] * (this.L_matrix[m][i] * (x_m - x_i) * (y_m - y_i) * denominator); + d2E_dy2 += this.K_matrix[m][i] * (1 - this.L_matrix[m][i] * Math.pow(x_m - x_i, 2) * denominator); + } + } + // make the variable names easier to make the solving of the linear system easier to read + let A = d2E_dx2, B = d2E_dxdy, C = dE_dx, D = d2E_dy2, E = dE_dy; + + // solve the linear system for dx and dy + let dy = (C / A + E / B) / (B / A - D / B); + let dx = -(B * dy + C) / A; + + // move the node + nodes[m].x += dx; + nodes[m].y += dy; + } + + + /** + * Create the L matrix: edge length times shortest path + * @param D_matrix + * @private + */ + _createL_matrix(D_matrix) { + let nodesArray = this.body.nodeIndices; + let edgeLength = this.springLength; + + this.L_matrix = []; + for (let i = 0; i < nodesArray.length; i++) { + this.L_matrix[nodesArray[i]] = {}; + for (let j = 0; j < nodesArray.length; j++) { + this.L_matrix[nodesArray[i]][nodesArray[j]] = edgeLength * D_matrix[nodesArray[i]][nodesArray[j]]; + } + } + } + + + /** + * Create the K matrix: spring constants times shortest path + * @param D_matrix + * @private + */ + _createK_matrix(D_matrix) { + let nodesArray = this.body.nodeIndices; + let edgeStrength = this.springConstant; + + this.K_matrix = []; + for (let i = 0; i < nodesArray.length; i++) { + this.K_matrix[nodesArray[i]] = {}; + for (let j = 0; j < nodesArray.length; j++) { + this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2); + } + } + } + + + +} + +export default KamadaKawai; \ No newline at end of file diff --git a/lib/vis/lib/network/modules/LayoutEngine.js b/lib/vis/lib/network/modules/LayoutEngine.js index be54e15157..8e4fc6b354 100644 --- a/lib/vis/lib/network/modules/LayoutEngine.js +++ b/lib/vis/lib/network/modules/LayoutEngine.js @@ -1,6 +1,7 @@ -'use strict' +'use strict'; -var util = require('../../util'); +let util = require('../../util'); +import NetworkUtil from '../NetworkUtil'; class LayoutEngine { constructor(body) { @@ -8,22 +9,25 @@ class LayoutEngine { this.initialRandomSeed = Math.round(Math.random() * 1000000); this.randomSeed = this.initialRandomSeed; + this.setPhysics = false; this.options = {}; - this.optionsBackup = {}; + this.optionsBackup = {physics:{}}; this.defaultOptions = { randomSeed: undefined, + improvedLayout: true, hierarchical: { enabled:false, levelSeparation: 150, + nodeSpacing: 100, + treeSpacing: 200, + blockShifting: true, + edgeMinimization: true, direction: 'UD', // UD, DU, LR, RL sortMethod: 'hubsize' // hubsize, directed } }; util.extend(this.options, this.defaultOptions); - - this.hierarchicalLevels = {}; - this.bindEventListeners(); } @@ -31,6 +35,9 @@ class LayoutEngine { this.body.emitter.on('_dataChanged', () => { this.setupHierarchicalLayout(); }); + this.body.emitter.on('_dataLoaded', () => { + this.layoutNetwork(); + }); this.body.emitter.on('_resetHierarchicalLayout', () => { this.setupHierarchicalLayout(); }); @@ -39,11 +46,9 @@ class LayoutEngine { setOptions(options, allOptions) { if (options !== undefined) { let prevHierarchicalState = this.options.hierarchical.enabled; - + util.selectiveDeepExtend(["randomSeed", "improvedLayout"],this.options, options); util.mergeOptions(this.options, options, 'hierarchical'); - if (options.randomSeed !== undefined) { - this.initialRandomSeed = options.randomSeed; - } + if (options.randomSeed !== undefined) {this.initialRandomSeed = options.randomSeed;} if (this.options.hierarchical.enabled === true) { if (prevHierarchicalState === true) { @@ -51,7 +56,7 @@ class LayoutEngine { this.body.emitter.emit('refresh', true); } - // make sure the level seperation is the right way up + // make sure the level separation is the right way up if (this.options.hierarchical.direction === 'RL' || this.options.hierarchical.direction === 'DU') { if (this.options.hierarchical.levelSeparation > 0) { this.options.hierarchical.levelSeparation *= -1; @@ -65,7 +70,7 @@ class LayoutEngine { this.body.emitter.emit('_resetHierarchicalLayout'); // because the hierarchical system needs it's own physics and smooth curve settings, we adapt the other options if needed. - return this.adaptAllOptions(allOptions); + return this.adaptAllOptionsForHierarchicalLayout(allOptions); } else { if (prevHierarchicalState === true) { @@ -78,23 +83,25 @@ class LayoutEngine { return allOptions; } - adaptAllOptions(allOptions) { + adaptAllOptionsForHierarchicalLayout(allOptions) { if (this.options.hierarchical.enabled === true) { // set the physics if (allOptions.physics === undefined || allOptions.physics === true) { - allOptions.physics = {solver: 'hierarchicalRepulsion'}; - this.optionsBackup.physics = {solver:'barnesHut'}; + allOptions.physics = { + enabled:this.optionsBackup.physics.enabled === undefined ? true : this.optionsBackup.physics.enabled, + solver:'hierarchicalRepulsion' + }; + this.optionsBackup.physics.enabled = this.optionsBackup.physics.enabled === undefined ? true : this.optionsBackup.physics.enabled; + this.optionsBackup.physics.solver = this.optionsBackup.physics.solver || 'barnesHut'; } else if (typeof allOptions.physics === 'object') { - this.optionsBackup.physics = {solver:'barnesHut'}; - if (allOptions.physics.solver !== undefined) { - this.optionsBackup.physics = {solver:allOptions.physics.solver}; - } - allOptions.physics['solver'] = 'hierarchicalRepulsion'; + this.optionsBackup.physics.enabled = allOptions.physics.enabled === undefined ? true : allOptions.physics.enabled; + this.optionsBackup.physics.solver = allOptions.physics.solver || 'barnesHut'; + allOptions.physics.solver = 'hierarchicalRepulsion'; } else if (allOptions.physics !== false) { - this.optionsBackup.physics = {solver:'barnesHut'}; - allOptions.physics['solver'] = 'hierarchicalRepulsion'; + this.optionsBackup.physics.solver ='barnesHut'; + allOptions.physics = {solver:'hierarchicalRepulsion'}; } // get the type of static smooth curve in case it is required @@ -125,7 +132,7 @@ class LayoutEngine { this.optionsBackup.edges = { smooth: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled, - type:allOptions.edges.smooth.type === undefined ? 'dynamic' : allOptions.edges.smooth.type, + type: allOptions.edges.smooth.type === undefined ? 'dynamic' : allOptions.edges.smooth.type, roundness: allOptions.edges.smooth.roundness === undefined ? 0.5 : allOptions.edges.smooth.roundness, forceDirection: allOptions.edges.smooth.forceDirection === undefined ? false : allOptions.edges.smooth.forceDirection }; @@ -141,11 +148,12 @@ class LayoutEngine { // force all edges into static smooth curves. Only applies to edges that do not use the global options for smooth. this.body.emitter.emit('_forceDisableDynamicCurves', type); } + return allOptions; } seededRandom() { - var x = Math.sin(this.randomSeed++) * 10000; + let x = Math.sin(this.randomSeed++) * 10000; return x - Math.floor(x); } @@ -166,6 +174,107 @@ class LayoutEngine { } } + + /** + * Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we + * cluster them first to reduce the amount. + */ + layoutNetwork() { + if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { + // first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible + // nodes have predefined positions we use this. + let positionDefined = 0; + for (let i = 0; i < this.body.nodeIndices.length; i++) { + let node = this.body.nodes[this.body.nodeIndices[i]]; + if (node.predefinedPosition === true) { + positionDefined += 1; + } + } + + // if less than half of the nodes have a predefined position we continue + if (positionDefined < 0.5 * this.body.nodeIndices.length) { + let MAX_LEVELS = 10; + let level = 0; + let clusterThreshold = 100; + // if there are a lot of nodes, we cluster before we run the algorithm. + if (this.body.nodeIndices.length > clusterThreshold) { + let startLength = this.body.nodeIndices.length; + while (this.body.nodeIndices.length > clusterThreshold) { + //console.time("clustering") + level += 1; + let before = this.body.nodeIndices.length; + // if there are many nodes we do a hubsize cluster + if (level % 3 === 0) { + this.body.modules.clustering.clusterBridges(); + } + else { + this.body.modules.clustering.clusterOutliers(); + } + let after = this.body.nodeIndices.length; + if ((before == after && level % 3 !== 0) || level > MAX_LEVELS) { + this._declusterAll(); + this.body.emitter.emit("_layoutFailed"); + console.info("This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance."); + return; + } + //console.timeEnd("clustering") + //console.log(level,after) + } + // increase the size of the edges + this.body.modules.kamadaKawai.setOptions({springLength: Math.max(150, 2 * startLength)}) + } + + // position the system for these nodes and edges + this.body.modules.kamadaKawai.solve(this.body.nodeIndices, this.body.edgeIndices, true); + + // shift to center point + this._shiftToCenter(); + + // perturb the nodes a little bit to force the physics to kick in + let offset = 70; + for (let i = 0; i < this.body.nodeIndices.length; i++) { + this.body.nodes[this.body.nodeIndices[i]].x += (0.5 - this.seededRandom())*offset; + this.body.nodes[this.body.nodeIndices[i]].y += (0.5 - this.seededRandom())*offset; + } + + // uncluster all clusters + this._declusterAll(); + + // reposition all bezier nodes. + this.body.emitter.emit("_repositionBezierNodes"); + } + } + } + + /** + * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view + * @private + */ + _shiftToCenter() { + let range = NetworkUtil.getRangeCore(this.body.nodes, this.body.nodeIndices); + let center = NetworkUtil.findCenter(range); + for (let i = 0; i < this.body.nodeIndices.length; i++) { + this.body.nodes[this.body.nodeIndices[i]].x -= center.x; + this.body.nodes[this.body.nodeIndices[i]].y -= center.y; + } + } + + _declusterAll() { + let clustersPresent = true; + while (clustersPresent === true) { + clustersPresent = false; + for (let i = 0; i < this.body.nodeIndices.length; i++) { + if (this.body.nodes[this.body.nodeIndices[i]].isCluster === true) { + clustersPresent = true; + this.body.modules.clustering.openCluster(this.body.nodeIndices[i], {}, false); + } + } + if (clustersPresent === true) { + this.body.emitter.emit('_dataChanged'); + } + } + } + getSeed() { return this.initialRandomSeed; } @@ -181,13 +290,26 @@ class LayoutEngine { // get the size of the largest hubs and check if the user has defined a level for a node. let node, nodeId; let definedLevel = false; + let definedPositions = true; let undefinedLevel = false; this.hierarchicalLevels = {}; - this.nodeSpacing = 100; + this.lastNodeOnLevel = {}; + this.hierarchicalParents = {}; + this.hierarchicalChildren = {}; + this.hierarchicalTrees = {}; + this.treeIndex = -1; + + this.distributionOrdering = {}; + this.distributionIndex = {}; + this.distributionOrderingPresence = {}; + for (nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { node = this.body.nodes[nodeId]; + if (node.options.x === undefined && node.options.y === undefined) { + definedPositions = false; + } if (node.options.level !== undefined) { definedLevel = true; this.hierarchicalLevels[nodeId] = node.options.level; @@ -204,28 +326,506 @@ class LayoutEngine { return; } else { - // setup the system to use hierarchical method. - //this._changeConstants(); - - // define levels if undefined by the users. Based on hubsize + // define levels if undefined by the users. Based on hubsize. if (undefinedLevel === true) { if (this.options.hierarchical.sortMethod === 'hubsize') { this._determineLevelsByHubsize(); } - else if (this.options.hierarchical.sortMethod === 'directed' || 'direction') { + else if (this.options.hierarchical.sortMethod === 'directed') { this._determineLevelsDirected(); } + else if (this.options.hierarchical.sortMethod === 'custom') { + this._determineLevelsCustomCallback(); + } } + + // fallback for cases where there are nodes but no edges + for (let nodeId in this.body.nodes) { + if (this.body.nodes.hasOwnProperty(nodeId)) { + if (this.hierarchicalLevels[nodeId] === undefined) { + this.hierarchicalLevels[nodeId] = 0; + } + } + } // check the distribution of the nodes per level. let distribution = this._getDistribution(); + // get the parent children relations. + this._generateMap(); + // place the nodes on the canvas. this._placeNodesByHierarchy(distribution); + + // condense the whitespace. + this._condenseHierarchy(); + + // shift to center so gravity does not have to do much + this._shiftToCenter(); } } } + /** + * @private + */ + _condenseHierarchy() { + // Global var in this scope to define when the movement has stopped. + let stillShifting = false; + let branches = {}; + // first we have some methods to help shifting trees around. + // the main method to shift the trees + let shiftTrees = () => { + let treeSizes = getTreeSizes(); + for (let i = 0; i < treeSizes.length - 1; i++) { + let diff = treeSizes[i].max - treeSizes[i+1].min; + if (diff !== this.options.hierarchical.treeSpacing) { + shiftTree(i + 1, diff - this.options.hierarchical.treeSpacing); + } + } + }; + + // shift a single tree by an offset + let shiftTree = (index, offset) => { + for (let nodeId in this.hierarchicalTrees) { + if (this.hierarchicalTrees.hasOwnProperty(nodeId)) { + if (this.hierarchicalTrees[nodeId] === index) { + this._setPositionForHierarchy(this.body.nodes[nodeId], offset, undefined, true); + } + } + } + }; + + // get the width of a tree + let getTreeSize = (index) => { + let min = 1e9; + let max = -1e9; + for (let nodeId in this.hierarchicalTrees) { + if (this.hierarchicalTrees.hasOwnProperty(nodeId)) { + if (this.hierarchicalTrees[nodeId] === index) { + let pos = this._getPositionForHierarchy(this.body.nodes[nodeId]); + min = Math.min(pos, min); + max = Math.max(pos, max); + } + } + } + return {min:min, max:max}; + }; + + // get the width of all trees + let getTreeSizes = () => { + let treeWidths = []; + for (let i = 0; i < this.treeIndex; i++) { + treeWidths.push(getTreeSize(i)); + } + return treeWidths; + }; + + + // get a map of all nodes in this branch + let getBranchNodes = (source, map) => { + map[source.id] = true; + if (this.hierarchicalParents[source.id]) { + let children = this.hierarchicalParents[source.id].children; + if (children.length > 0) { + for (let i = 0; i < children.length; i++) { + getBranchNodes(this.body.nodes[children[i]], map); + } + } + } + }; + + // get a min max width as well as the maximum movement space it has on either sides + // we use min max terminology because width and height can interchange depending on the direction of the layout + let getBranchBoundary = (branchMap, maxLevel = 1e9) => { + let minSpace = 1e9; + let maxSpace = 1e9; + let min = 1e9; + let max = -1e9; + for (let branchNode in branchMap) { + if (branchMap.hasOwnProperty(branchNode)) { + let node = this.body.nodes[branchNode]; + let level = this.hierarchicalLevels[node.id]; + let position = this._getPositionForHierarchy(node); + + // get the space around the node. + let [minSpaceNode, maxSpaceNode] = this._getSpaceAroundNode(node,branchMap); + minSpace = Math.min(minSpaceNode, minSpace); + maxSpace = Math.min(maxSpaceNode, maxSpace); + + // the width is only relevant for the levels two nodes have in common. This is why we filter on this. + if (level <= maxLevel) { + min = Math.min(position, min); + max = Math.max(position, max); + } + } + } + + return [min, max, minSpace, maxSpace]; + }; + + // get the maximum level of a branch. + let getMaxLevel = (nodeId) => { + let level = this.hierarchicalLevels[nodeId]; + if (this.hierarchicalParents[nodeId]) { + let children = this.hierarchicalParents[nodeId].children; + if (children.length > 0) { + for (let i = 0; i < children.length; i++) { + level = Math.max(level,getMaxLevel(children[i])); + } + } + } + return level; + }; + + // check what the maximum level is these nodes have in common. + let getCollisionLevel = (node1, node2) => { + let maxLevel1 = getMaxLevel(node1.id); + let maxLevel2 = getMaxLevel(node2.id); + return Math.min(maxLevel1, maxLevel2); + }; + + // check if two nodes have the same parent(s) + let hasSameParent = (node1, node2) => { + let parents1 = this.hierarchicalChildren[node1.id]; + let parents2 = this.hierarchicalChildren[node2.id]; + if (parents1 === undefined || parents2 === undefined) { + return false; + } + parents1 = parents1.parents; + parents2 = parents2.parents; + for (let i = 0; i < parents1.length; i++) { + for (let j = 0; j < parents2.length; j++) { + if (parents1[i] == parents2[j]) { + return true; + } + } + } + return false; + }; + + // condense elements. These can be nodes or branches depending on the callback. + let shiftElementsCloser = (callback, levels, centerParents) => { + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + let levelNodes = this.distributionOrdering[level]; + if (levelNodes.length > 1) { + for (let j = 0; j < levelNodes.length - 1; j++) { + if (hasSameParent(levelNodes[j],levelNodes[j+1]) === true) { + if (this.hierarchicalTrees[levelNodes[j].id] === this.hierarchicalTrees[levelNodes[j+1].id]) { + callback(levelNodes[j],levelNodes[j+1], centerParents); + } + }} + } + } + }; + + // callback for shifting branches + let branchShiftCallback = (node1, node2, centerParent = false) => { + //window.CALLBACKS.push(() => { + let pos1 = this._getPositionForHierarchy(node1); + let pos2 = this._getPositionForHierarchy(node2); + let diffAbs = Math.abs(pos2 - pos1); + //console.log("NOW CHEcKING:", node1.id, node2.id, diffAbs); + if (diffAbs > this.options.hierarchical.nodeSpacing) { + let branchNodes1 = {}; branchNodes1[node1.id] = true; + let branchNodes2 = {}; branchNodes2[node2.id] = true; + + getBranchNodes(node1, branchNodes1); + getBranchNodes(node2, branchNodes2); + + // check the largest distance between the branches + let maxLevel = getCollisionLevel(node1, node2); + let [min1,max1, minSpace1, maxSpace1] = getBranchBoundary(branchNodes1, maxLevel); + let [min2,max2, minSpace2, maxSpace2] = getBranchBoundary(branchNodes2, maxLevel); + + //console.log(node1.id, getBranchBoundary(branchNodes1, maxLevel), node2.id, getBranchBoundary(branchNodes2, maxLevel), maxLevel); + let diffBranch = Math.abs(max1 - min2); + if (diffBranch > this.options.hierarchical.nodeSpacing) { + let offset = max1 - min2 + this.options.hierarchical.nodeSpacing; + if (offset < -minSpace2 + this.options.hierarchical.nodeSpacing) { + offset = -minSpace2 + this.options.hierarchical.nodeSpacing; + //console.log("RESETTING OFFSET", max1 - min2 + this.options.hierarchical.nodeSpacing, -minSpace2, offset); + } + if (offset < 0) { + //console.log("SHIFTING", node2.id, offset); + this._shiftBlock(node2.id, offset); + stillShifting = true; + + if (centerParent === true) + this._centerParent(node2); + } + } + + } + //this.body.emitter.emit("_redraw");}) + }; + + let minimizeEdgeLength = (iterations, node) => { + //window.CALLBACKS.push(() => { + // console.log("ts",node.id); + let nodeId = node.id; + let allEdges = node.edges; + let nodeLevel = this.hierarchicalLevels[node.id]; + + // gather constants + let C2 = this.options.hierarchical.levelSeparation * this.options.hierarchical.levelSeparation; + let referenceNodes = {}; + let aboveEdges = []; + for (let i = 0; i < allEdges.length; i++) { + let edge = allEdges[i]; + if (edge.toId != edge.fromId) { + let otherNode = edge.toId == nodeId ? edge.from : edge.to; + referenceNodes[allEdges[i].id] = otherNode; + if (this.hierarchicalLevels[otherNode.id] < nodeLevel) { + aboveEdges.push(edge); + } + } + } + + // differentiated sum of lengths based on only moving one node over one axis + let getFx = (point, edges) => { + let sum = 0; + for (let i = 0; i < edges.length; i++) { + if (referenceNodes[edges[i].id] !== undefined) { + let a = this._getPositionForHierarchy(referenceNodes[edges[i].id]) - point; + sum += a / Math.sqrt(a * a + C2); + } + } + return sum; + }; + + // doubly differentiated sum of lengths based on only moving one node over one axis + let getDFx = (point, edges) => { + let sum = 0; + for (let i = 0; i < edges.length; i++) { + if (referenceNodes[edges[i].id] !== undefined) { + let a = this._getPositionForHierarchy(referenceNodes[edges[i].id]) - point; + sum -= (C2 * Math.pow(a * a + C2, -1.5)); + } + } + return sum; + }; + + let getGuess = (iterations, edges) => { + let guess = this._getPositionForHierarchy(node); + // Newton's method for optimization + let guessMap = {}; + for (let i = 0; i < iterations; i++) { + let fx = getFx(guess, edges); + let dfx = getDFx(guess, edges); + + // we limit the movement to avoid instability. + let limit = 40; + let ratio = Math.max(-limit, Math.min(limit, Math.round(fx/dfx))); + guess = guess - ratio; + // reduce duplicates + if (guessMap[guess] !== undefined) { + break; + } + guessMap[guess] = i; + } + return guess; + }; + + let moveBranch = (guess) => { + // position node if there is space + let nodePosition = this._getPositionForHierarchy(node); + + // check movable area of the branch + if (branches[node.id] === undefined) { + let branchNodes = {}; + branchNodes[node.id] = true; + getBranchNodes(node, branchNodes); + branches[node.id] = branchNodes; + } + let [minBranch, maxBranch, minSpaceBranch, maxSpaceBranch] = getBranchBoundary(branches[node.id]); + + let diff = guess - nodePosition; + + // check if we are allowed to move the node: + let branchOffset = 0; + if (diff > 0) { + branchOffset = Math.min(diff, maxSpaceBranch - this.options.hierarchical.nodeSpacing); + } + else if (diff < 0) { + branchOffset = -Math.min(-diff, minSpaceBranch - this.options.hierarchical.nodeSpacing); + } + + if (branchOffset != 0) { + //console.log("moving branch:",branchOffset, maxSpaceBranch, minSpaceBranch) + this._shiftBlock(node.id, branchOffset); + //this.body.emitter.emit("_redraw"); + stillShifting = true; + } + }; + + let moveNode = (guess) => { + let nodePosition = this._getPositionForHierarchy(node); + + // position node if there is space + let [minSpace, maxSpace] = this._getSpaceAroundNode(node); + let diff = guess - nodePosition; + // check if we are allowed to move the node: + let newPosition = nodePosition; + if (diff > 0) { + newPosition = Math.min(nodePosition + (maxSpace - this.options.hierarchical.nodeSpacing), guess); + } + else if (diff < 0) { + newPosition = Math.max(nodePosition - (minSpace - this.options.hierarchical.nodeSpacing), guess); + } + + if (newPosition !== nodePosition) { + //console.log("moving Node:",diff, minSpace, maxSpace) + this._setPositionForHierarchy(node, newPosition, undefined, true); + //this.body.emitter.emit("_redraw"); + stillShifting = true; + } + }; + + let guess = getGuess(iterations, aboveEdges); + moveBranch(guess); + guess = getGuess(iterations, allEdges); + moveNode(guess); + //}) + }; + + // method to remove whitespace between branches. Because we do bottom up, we can center the parents. + let minimizeEdgeLengthBottomUp = (iterations) => { + let levels = Object.keys(this.distributionOrdering); + levels = levels.reverse(); + for (let i = 0; i < iterations; i++) { + stillShifting = false; + for (let j = 0; j < levels.length; j++) { + let level = levels[j]; + let levelNodes = this.distributionOrdering[level]; + for (let k = 0; k < levelNodes.length; k++) { + minimizeEdgeLength(1000, levelNodes[k]); + } + } + if (stillShifting !== true) { + //console.log("FINISHED minimizeEdgeLengthBottomUp IN " + i); + break; + } + } + }; + + //// method to remove whitespace between branches. Because we do bottom up, we can center the parents. + let shiftBranchesCloserBottomUp = (iterations) => { + let levels = Object.keys(this.distributionOrdering); + levels = levels.reverse(); + for (let i = 0; i < iterations; i++) { + stillShifting = false; + shiftElementsCloser(branchShiftCallback, levels, true); + if (stillShifting !== true) { + //console.log("FINISHED shiftBranchesCloserBottomUp IN " + (i+1)); + break; + } + } + }; + + // center all parents + let centerAllParents = () => { + for (let nodeId in this.body.nodes) { + if (this.body.nodes.hasOwnProperty(nodeId)) + this._centerParent(this.body.nodes[nodeId]); + } + }; + + // the actual work is done here. + if (this.options.hierarchical.blockShifting === true) { + shiftBranchesCloserBottomUp(5); + centerAllParents(); + } + + // minimize edge length + if (this.options.hierarchical.edgeMinimization === true) { + minimizeEdgeLengthBottomUp(20); + } + + shiftTrees(); + } + + /** + * This gives the space around the node. IF a map is supplied, it will only check against nodes NOT in the map. + * This is used to only get the distances to nodes outside of a branch. + * @param node + * @param map + * @returns {*[]} + * @private + */ + _getSpaceAroundNode(node, map) { + let useMap = true; + if (map === undefined) { + useMap = false; + } + let level = this.hierarchicalLevels[node.id]; + if (level !== undefined) { + let index = this.distributionIndex[node.id]; + let position = this._getPositionForHierarchy(node); + let minSpace = 1e9; + let maxSpace = 1e9; + if (index !== 0) { + let prevNode = this.distributionOrdering[level][index - 1]; + if ((useMap === true && map[prevNode.id] === undefined) || useMap === false) { + let prevPos = this._getPositionForHierarchy(prevNode); + minSpace = position - prevPos; + } + } + + if (index != this.distributionOrdering[level].length - 1) { + let nextNode = this.distributionOrdering[level][index + 1]; + if ((useMap === true && map[nextNode.id] === undefined) || useMap === false) { + let nextPos = this._getPositionForHierarchy(nextNode); + maxSpace = Math.min(maxSpace, nextPos - position); + } + } + + return [minSpace, maxSpace]; + } + else { + return [0, 0]; + } + } + + /** + * We use this method to center a parent node and check if it does not cross other nodes when it does. + * @param node + * @private + */ + _centerParent(node) { + if (this.hierarchicalChildren[node.id]) { + let parents = this.hierarchicalChildren[node.id].parents; + for (var i = 0; i < parents.length; i++) { + let parentId = parents[i]; + let parentNode = this.body.nodes[parentId]; + if (this.hierarchicalParents[parentId]) { + // get the range of the children + let minPos = 1e9; + let maxPos = -1e9; + let children = this.hierarchicalParents[parentId].children; + if (children.length > 0) { + for (let i = 0; i < children.length; i++) { + let childNode = this.body.nodes[children[i]]; + minPos = Math.min(minPos, this._getPositionForHierarchy(childNode)); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(childNode)); + } + } + + let position = this._getPositionForHierarchy(parentNode); + let [minSpace, maxSpace] = this._getSpaceAroundNode(parentNode); + let newPosition = 0.5 * (minPos + maxPos); + let diff = position - newPosition; + if ((diff < 0 && Math.abs(diff) < maxSpace - this.options.hierarchical.nodeSpacing) || (diff > 0 && Math.abs(diff) < minSpace - this.options.hierarchical.nodeSpacing)) { + this._setPositionForHierarchy(parentNode, newPosition, undefined, true); + } + } + } + } + } + + + /** * This function places the nodes on the canvas based on the hierarchial distribution. * @@ -233,33 +833,39 @@ class LayoutEngine { * @private */ _placeNodesByHierarchy(distribution) { - let nodeId, node; this.positionedNodes = {}; // start placing all the level 0 nodes first. Then recursively position their branches. for (let level in distribution) { if (distribution.hasOwnProperty(level)) { - for (nodeId in distribution[level].nodes) { - if (distribution[level].nodes.hasOwnProperty(nodeId)) { + // sort nodes in level by position: + let nodeArray = Object.keys(distribution[level]); + nodeArray = this._indexArrayToNodes(nodeArray); + this._sortNodeArray(nodeArray); - node = distribution[level].nodes[nodeId]; - - if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { - if (node.x === undefined) {node.x = distribution[level].distance;} - distribution[level].distance = node.x + this.nodeSpacing; - } - else { - if (node.y === undefined) {node.y = distribution[level].distance;} - distribution[level].distance = node.y + this.nodeSpacing; - } - - this.positionedNodes[nodeId] = true; - this._placeBranchNodes(node.edges,node.id,distribution,level); + for (let i = 0; i < nodeArray.length; i++) { + let node = nodeArray[i]; + if (this.positionedNodes[node.id] === undefined) { + this._setPositionForHierarchy(node, this.options.hierarchical.nodeSpacing * i, level); + this.positionedNodes[node.id] = true; + this._placeBranchNodes(node.id, level); } } } } } + /** + * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on + * node properties. + * @param idArray + */ + _indexArrayToNodes(idArray) { + let array = []; + for (let i = 0; i < idArray.length; i++) { + array.push(this.body.nodes[idArray[i]]) + } + return array; + } /** * This function get the distribution of levels based on hubsize @@ -286,10 +892,9 @@ class LayoutEngine { node.options.fixed.x = true; } if (distribution[level] === undefined) { - distribution[level] = {amount: 0, nodes: {}, distance: 0}; + distribution[level] = {}; } - distribution[level].amount += 1; - distribution[level].nodes[nodeId] = node; + distribution[level][nodeId] = node; } } return distribution; @@ -323,54 +928,66 @@ class LayoutEngine { * @private */ _determineLevelsByHubsize() { - let nodeId, node; let hubSize = 1; + let levelDownstream = (nodeA, nodeB) => { + if (this.hierarchicalLevels[nodeB.id] === undefined) { + // set initial level + if (this.hierarchicalLevels[nodeA.id] === undefined) { + this.hierarchicalLevels[nodeA.id] = 0; + } + // set level + this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] + 1; + } + }; + while (hubSize > 0) { // determine hubs hubSize = this._getHubSize(); if (hubSize === 0) break; - for (nodeId in this.body.nodes) { + for (let nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { - node = this.body.nodes[nodeId]; + let node = this.body.nodes[nodeId]; if (node.edges.length === hubSize) { - this._setLevelByHubsize(0, node); + this._crawlNetwork(levelDownstream,nodeId); } } } } } - /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. - * - * @param level - * @param edges - * @param parentId + * TODO: release feature * @private */ - _setLevelByHubsize(level, node) { - if (this.hierarchicalLevels[node.id] !== undefined) - return; + _determineLevelsCustomCallback() { + let minLevel = 100000; - let childNode; - this.hierarchicalLevels[node.id] = level; - for (let i = 0; i < node.edges.length; i++) { - if (node.edges[i].toId === node.id) { - childNode = node.edges[i].from; - } - else { - childNode = node.edges[i].to; - } - this._setLevelByHubsize(level + 1, childNode); - } + // TODO: this should come from options. + let customCallback = function(nodeA, nodeB, edge) { + + }; + + let levelByDirection = (nodeA, nodeB, edge) => { + let levelA = this.hierarchicalLevels[nodeA.id]; + // set initial level + if (levelA === undefined) {this.hierarchicalLevels[nodeA.id] = minLevel;} + + let diff = customCallback( + NetworkUtil.cloneOptions(nodeA,'node'), + NetworkUtil.cloneOptions(nodeB,'node'), + NetworkUtil.cloneOptions(edge,'edge') + ); + + this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] + diff; + }; + + this._crawlNetwork(levelByDirection); + this._setMinLevelToZero(); } - - /** * this function allocates nodes in levels based on the direction of the edges * @@ -378,111 +995,340 @@ class LayoutEngine { * @private */ _determineLevelsDirected() { - let nodeId, node; let minLevel = 10000; - - // set first node to source - for (nodeId in this.body.nodes) { - if (this.body.nodes.hasOwnProperty(nodeId)) { - node = this.body.nodes[nodeId]; - this._setLevelDirected(minLevel,node); + let levelByDirection = (nodeA, nodeB, edge) => { + let levelA = this.hierarchicalLevels[nodeA.id]; + // set initial level + if (levelA === undefined) {this.hierarchicalLevels[nodeA.id] = minLevel;} + if (edge.toId == nodeB.id) { + this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] + 1; } - } + else { + this.hierarchicalLevels[nodeB.id] = this.hierarchicalLevels[nodeA.id] - 1; + } + }; + this._crawlNetwork(levelByDirection); + this._setMinLevelToZero(); + } + + /** + * Small util method to set the minimum levels of the nodes to zero. + * @private + */ + _setMinLevelToZero() { + let minLevel = 1e9; // get the minimum level - for (nodeId in this.body.nodes) { + for (let nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { - minLevel = this.hierarchicalLevels[nodeId] < minLevel ? this.hierarchicalLevels[nodeId] : minLevel; + if (this.hierarchicalLevels[nodeId] !== undefined) { + minLevel = Math.min(this.hierarchicalLevels[nodeId], minLevel); + } } } // subtract the minimum from the set so we have a range starting from 0 - for (nodeId in this.body.nodes) { + for (let nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { - this.hierarchicalLevels[nodeId] -= minLevel; + if (this.hierarchicalLevels[nodeId] !== undefined) { + this.hierarchicalLevels[nodeId] -= minLevel; + } } } } /** - * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction - * - * @param level - * @param edges - * @param parentId + * Update the bookkeeping of parent and child. * @private */ - _setLevelDirected(level, node) { - if (this.hierarchicalLevels[node.id] !== undefined) - return; - - let childNode; - this.hierarchicalLevels[node.id] = level; - - for (let i = 0; i < node.edges.length; i++) { - if (node.edges[i].toId === node.id) { - childNode = node.edges[i].from; - this._setLevelDirected(level - 1, childNode); + _generateMap() { + let fillInRelations = (parentNode, childNode) => { + if (this.hierarchicalLevels[childNode.id] > this.hierarchicalLevels[parentNode.id]) { + let parentNodeId = parentNode.id; + let childNodeId = childNode.id; + if (this.hierarchicalParents[parentNodeId] === undefined) { + this.hierarchicalParents[parentNodeId] = {children: [], amount: 0}; + } + this.hierarchicalParents[parentNodeId].children.push(childNodeId); + if (this.hierarchicalChildren[childNodeId] === undefined) { + this.hierarchicalChildren[childNodeId] = {parents: [], amount: 0}; + } + this.hierarchicalChildren[childNodeId].parents.push(parentNodeId); } - else { - childNode = node.edges[i].to; - this._setLevelDirected(level + 1, childNode); - } - } + }; + + this._crawlNetwork(fillInRelations); } + /** + * Crawl over the entire network and use a callback on each node couple that is connected to each other. + * @param callback | will receive nodeA nodeB and the connecting edge. A and B are unique. + * @param startingNodeId + * @private + */ + _crawlNetwork(callback = function() {}, startingNodeId) { + let progress = {}; + let crawler = (node) => { + if (progress[node.id] === undefined) { + progress[node.id] = true; + let childNode; + for (let i = 0; i < node.edges.length; i++) { + if (node.edges[i].connected === true) { + if (node.edges[i].toId === node.id) { + childNode = node.edges[i].from; + } + else { + childNode = node.edges[i].to; + } + + if (node.id !== childNode.id) { + callback(node, childNode, node.edges[i]); + crawler(childNode); + } + } + } + } + }; + + + // we can crawl from a specific node or over all nodes. + if (startingNodeId === undefined) { + for (let i = 0; i < this.body.nodeIndices.length; i++) { + let node = this.body.nodes[this.body.nodeIndices[i]]; + crawler(node); + } + } + else { + let node = this.body.nodes[startingNodeId]; + if (node === undefined) { + console.error("Node not found:", startingNodeId); + return; + } + crawler(node); + } + } + /** * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes * on a X position that ensures there will be no overlap. * - * @param edges * @param parentId - * @param distribution * @param parentLevel * @private */ - _placeBranchNodes(edges, parentId, distribution, parentLevel) { - for (let i = 0; i < edges.length; i++) { - let childNode = undefined; - let parentNode = undefined; - if (edges[i].toId === parentId) { - childNode = edges[i].from; - parentNode = edges[i].to; - } - else { - childNode = edges[i].to; - parentNode = edges[i].from; - } + _placeBranchNodes(parentId, parentLevel) { + // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. + if (this.hierarchicalParents[parentId] === undefined) { + return; + } + + // get a list of childNodes + let childNodes = []; + for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalParents[parentId].children[i]]); + } + + // use the positions to order the nodes. + this._sortNodeArray(childNodes); + + // position the childNodes + for (let i = 0; i < childNodes.length; i++) { + let childNode = childNodes[i]; let childNodeLevel = this.hierarchicalLevels[childNode.id]; + // check if the child node is below the parent node and if it has already been positioned. + if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { + // get the amount of space required for this node. If parent the width is based on the amount of children. + let pos; - if (this.positionedNodes[childNode.id] === undefined) { - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - if (childNodeLevel > parentLevel) { - if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { - if (childNode.x === undefined) { - childNode.x = Math.max(distribution[childNodeLevel].distance, parentNode.x); - } - distribution[childNodeLevel].distance = childNode.x + this.nodeSpacing; - this.positionedNodes[childNode.id] = true; - } - else { - if (childNode.y === undefined) { - childNode.y = Math.max(distribution[childNodeLevel].distance, parentNode.y) - } - distribution[childNodeLevel].distance = childNode.y + this.nodeSpacing; - } - this.positionedNodes[childNode.id] = true; + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);} + else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;} + this._setPositionForHierarchy(childNode, pos, childNodeLevel); - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges, childNode.id, distribution, childNodeLevel); + // if overlap has been detected, we shift the branch + if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { + let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]); + if (pos - previousPos < this.options.hierarchical.nodeSpacing) { + let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos; + let sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id); + this._shiftBlock(sharedParent.withChild, diff); } } + + // store change in position. + this.lastNodeOnLevel[childNodeLevel] = childNode.id; + + this.positionedNodes[childNode.id] = true; + + this._placeBranchNodes(childNode.id, childNodeLevel); + } + else { + return; + } + } + + // center the parent nodes. + let minPos = 1e9; + let maxPos = -1e9; + for (let i = 0; i < childNodes.length; i++) { + let childNodeId = childNodes[i].id; + minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + } + this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); + } + + + /** + * Shift a branch a certain distance + * @param parentId + * @param diff + * @private + */ + _shiftBlock(parentId, diff) { + if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { + this.body.nodes[parentId].x += diff; + } + else { + this.body.nodes[parentId].y += diff; + } + if (this.hierarchicalParents[parentId] !== undefined) { + for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) { + this._shiftBlock(this.hierarchicalParents[parentId].children[i], diff); } } } + + + /** + * Find a common parent between branches. + * @param childA + * @param childB + * @returns {{foundParent, withChild}} + * @private + */ + _findCommonParent(childA,childB) { + let parents = {}; + let iterateParents = (parents,child) => { + if (this.hierarchicalChildren[child] !== undefined) { + for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { + let parent = this.hierarchicalChildren[child].parents[i]; + parents[parent] = true; + iterateParents(parents, parent) + } + } + }; + let findParent = (parents, child) => { + if (this.hierarchicalChildren[child] !== undefined) { + for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) { + let parent = this.hierarchicalChildren[child].parents[i]; + if (parents[parent] !== undefined) { + return {foundParent:parent, withChild:child}; + } + let branch = findParent(parents, parent); + if (branch.foundParent !== null) { + return branch; + } + } + } + return {foundParent:null, withChild:child}; + }; + + iterateParents(parents, childA); + return findParent(parents, childB); + } + + /** + * Abstract the getting of the position so we won't have to repeat the check for direction all the time + * @param node + * @param position + * @param level + * @private + */ + _setPositionForHierarchy(node, position, level, doNotUpdate = false) { + if (doNotUpdate !== true) { + if (this.distributionOrdering[level] === undefined) { + this.distributionOrdering[level] = []; + this.distributionOrderingPresence[level] = {}; + } + + if (this.distributionOrderingPresence[level][node.id] === undefined) { + this.distributionOrdering[level].push(node); + this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; + } + this.distributionOrderingPresence[level][node.id] = true; + + if (this.hierarchicalTrees[node.id] === undefined) { + if (this.hierarchicalChildren[node.id] !== undefined) { + let tree = 1; + // get the lowest tree denominator. + for (let i = 0; i < this.hierarchicalChildren[node.id].parents.length; i++) { + let parentId = this.hierarchicalChildren[node.id].parents[i]; + if (this.hierarchicalTrees[parentId] !== undefined) { + //tree = Math.min(tree,this.hierarchicalTrees[parentId]); + tree = this.hierarchicalTrees[parentId]; + } + } + //for (let i = 0; i < this.hierarchicalChildren.parents.length; i++) { + // let parentId = this.hierarchicalChildren.parents[i]; + // this.hierarchicalTrees[parentId] = tree; + //} + this.hierarchicalTrees[node.id] = tree; + } + else { + this.hierarchicalTrees[node.id] = ++this.treeIndex; + } + } + } + + if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { + node.x = position; + } + else { + node.y = position; + } + } + + /** + * Abstract the getting of the position of a node so we do not have to repeat the direction check all the time. + * @param node + * @returns {number|*} + * @private + */ + _getPositionForHierarchy(node) { + if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { + return node.x; + } + else { + return node.y; + } + } + + /** + * Use the x or y value to sort the array, allowing users to specify order. + * @param nodeArray + * @private + */ + _sortNodeArray(nodeArray) { + if (nodeArray.length > 1) { + if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') { + nodeArray.sort(function (a, b) { + return a.x - b.x; + }) + } + else { + nodeArray.sort(function (a, b) { + return a.y - b.y; + }) + } + } + } + + + } export default LayoutEngine; \ No newline at end of file diff --git a/lib/vis/lib/network/modules/ManipulationSystem.js b/lib/vis/lib/network/modules/ManipulationSystem.js index 1d81d1f5d8..158f6a9a1d 100644 --- a/lib/vis/lib/network/modules/ManipulationSystem.js +++ b/lib/vis/lib/network/modules/ManipulationSystem.js @@ -110,6 +110,7 @@ class ManipulationSystem { } } + enableEditMode() { this.editMode = true; @@ -143,7 +144,7 @@ class ManipulationSystem { // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); - // reset global letiables + // reset global variables this.manipulationDOM = {}; // if the gui is enabled, draw all elements. @@ -221,8 +222,6 @@ class ManipulationSystem { /** * Create the toolbar for adding Nodes - * - * @private */ addNodeMode() { // when using the gui, enable edit mode if it wasnt already. @@ -250,8 +249,6 @@ class ManipulationSystem { /** * call the bound function to handle the editing of the node. The node has to be selected. - * - * @private */ editNode() { // when using the gui, enable edit mode if it wasnt already. @@ -298,8 +295,6 @@ class ManipulationSystem { /** * create the toolbar to connect nodes - * - * @private */ addEdgeMode() { // when using the gui, enable edit mode if it wasnt already. @@ -334,11 +329,9 @@ class ManipulationSystem { /** * create the toolbar to edit edges - * - * @private */ editEdgeMode() { - // when using the gui, enable edit mode if it wasnt already. + // when using the gui, enable edit mode if it wasn't already. if (this.editMode !== true) { this.enableEditMode(); } @@ -376,15 +369,12 @@ class ManipulationSystem { // temporarily overload UI functions, cleaned up automatically because of _temporaryBindUI this._temporaryBindUI('onTouch', this._controlNodeTouch.bind(this)); // used to get the position - this._temporaryBindUI('onTap', () => { - }); // disabled - this._temporaryBindUI('onHold', () => { - }); // disabled + this._temporaryBindUI('onTap', () => {}); // disabled + this._temporaryBindUI('onHold', () => {}); // disabled this._temporaryBindUI('onDragStart', this._controlNodeDragStart.bind(this));// used to select control node this._temporaryBindUI('onDrag', this._controlNodeDrag.bind(this)); // used to drag control node this._temporaryBindUI('onDragEnd', this._controlNodeDragEnd.bind(this)); // used to connect or revert control nodes - this._temporaryBindUI('onMouseMove', () => { - }); // disabled + this._temporaryBindUI('onMouseMove', () => {}); // disabled // create function to position control nodes correctly on movement // automatically cleaned up because we use the temporary bind @@ -409,8 +399,6 @@ class ManipulationSystem { /** * delete everything in the selection - * - * @private */ deleteSelected() { // when using the gui, enable edit mode if it wasnt already. @@ -560,7 +548,11 @@ class ManipulationSystem { controlNodeStyle.x = x; controlNodeStyle.y = y; - return this.body.functions.createNode(controlNodeStyle); + // we have to define the bounding box in order for the nodes to be drawn immediately + let node = this.body.functions.createNode(controlNodeStyle); + node.shape.boundingBox = {left: x, right:x, top:y, bottom:y}; + + return node; } @@ -650,7 +642,7 @@ class ManipulationSystem { // remove the manipulation divs if (this.manipulationDiv) {this.canvas.frame.removeChild(this.manipulationDiv);} if (this.editModeDiv) {this.canvas.frame.removeChild(this.editModeDiv);} - if (this.closeDiv) {this.canvas.frame.removeChild(this.manipulationDiv);} + if (this.closeDiv) {this.canvas.frame.removeChild(this.closeDiv);} // set the references to undefined this.manipulationDiv = undefined; @@ -893,6 +885,11 @@ class ManipulationSystem { let pointerObj = this.selectionHandler._pointerToPositionObject(pointer); let edge = this.body.edges[this.edgeBeingEditedId]; + // if the node that was dragged is not a control node, return + if (this.selectedControlNode === undefined) { + return; + } + let overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); let node = undefined; for (let i = overlappingNodeIds.length-1; i >= 0; i--) { diff --git a/lib/vis/lib/network/modules/NodesHandler.js b/lib/vis/lib/network/modules/NodesHandler.js index 68e92e7537..f7f6d6503d 100644 --- a/lib/vis/lib/network/modules/NodesHandler.js +++ b/lib/vis/lib/network/modules/NodesHandler.js @@ -87,6 +87,7 @@ class NodesHandler { }, shadow: { enabled: false, + color: 'rgba(0,0,0,0.5)', size: 10, x: 5, y: 5 @@ -95,7 +96,8 @@ class NodesHandler { shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape - useImageSize: false // only for image and circularImage shapes + useImageSize: false, // only for image and circularImage shapes + useBorderWithImage: false // only for image shape }, size: 25, title: undefined, @@ -113,6 +115,10 @@ class NodesHandler { this.body.emitter.on('refreshNodes', this.refresh.bind(this)); this.body.emitter.on('refresh', this.refresh.bind(this)); this.body.emitter.on('destroy', () => { + util.forEach(this.nodesListeners, (callback, event) => { + if (this.body.data.nodes) + this.body.data.nodes.off(event, callback); + }); delete this.body.functions.createNode; delete this.nodesListeners.add; delete this.nodesListeners.update; @@ -134,7 +140,7 @@ class NodesHandler { } } - // update the shape size in all nodes + // update the font in all nodes if (options.font !== undefined) { Label.parseOptions(this.options.font, options); for (let nodeId in this.body.nodes) { @@ -275,7 +281,6 @@ class NodesHandler { for (let i = 0; i < ids.length; i++) { let id = ids[i]; - nodes[id].cleanup(); delete nodes[id]; } @@ -335,11 +340,9 @@ class NodesHandler { } } else { - for (let nodeId in this.body.nodes) { - if (this.body.nodes.hasOwnProperty(nodeId)) { - let node = this.body.nodes[nodeId]; - dataArray[nodeId] = { x: Math.round(node.x), y: Math.round(node.y) }; - } + for (let i = 0; i < this.body.nodeIndices.length; i++) { + let node = this.body.nodes[this.body.nodeIndices[i]]; + dataArray[this.body.nodeIndices[i]] = { x: Math.round(node.x), y: Math.round(node.y) }; } } return dataArray; @@ -358,7 +361,7 @@ class NodesHandler { if (dataset._data.hasOwnProperty(nodeId)) { let node = this.body.nodes[nodeId]; if (dataset._data[nodeId].x != Math.round(node.x) || dataset._data[nodeId].y != Math.round(node.y)) { - dataArray.push({ id: nodeId, x: Math.round(node.x), y: Math.round(node.y) }); + dataArray.push({ id: node.id, x: Math.round(node.x), y: Math.round(node.y) }); } } } @@ -389,13 +392,13 @@ class NodesHandler { let nodeObj = {}; // used to quickly check if node already exists for (let i = 0; i < node.edges.length; i++) { let edge = node.edges[i]; - if (edge.toId == nodeId) { // these are double equals since ids can be numeric or string + if (edge.toId == node.id) { // these are double equals since ids can be numeric or string if (nodeObj[edge.fromId] === undefined) { nodeList.push(edge.fromId); nodeObj[edge.fromId] = true; } } - else if (edge.fromId == nodeId) { // these are double equals since ids can be numeric or string + else if (edge.fromId == node.id) { // these are double equals since ids can be numeric or string if (nodeObj[edge.toId] === undefined) { nodeList.push(edge.toId); nodeObj[edge.toId] = true; diff --git a/lib/vis/lib/network/modules/PhysicsEngine.js b/lib/vis/lib/network/modules/PhysicsEngine.js index 762469a5a1..7d53890659 100644 --- a/lib/vis/lib/network/modules/PhysicsEngine.js +++ b/lib/vis/lib/network/modules/PhysicsEngine.js @@ -19,9 +19,15 @@ class PhysicsEngine { this.simulationInterval = 1000 / 60; this.requiresTimeout = true; this.previousStates = {}; + this.referenceState = {}; this.freezeCache = {}; this.renderTimer = undefined; - this.initialStabilizationEmitted = false; + + // parameters for the adaptive timestep + this.adaptiveTimestep = false; + this.adaptiveTimestepEnabled = false; + this.adaptiveCounter = 0; + this.adaptiveInterval = 3; this.stabilized = false; this.startedStabilization = false; @@ -66,7 +72,7 @@ class PhysicsEngine { damping: 0.09 }, maxVelocity: 50, - minVelocity: 0.1, // px/s + minVelocity: 0.75, // px/s solver: 'barnesHut', stabilization: { enabled: true, @@ -75,15 +81,19 @@ class PhysicsEngine { onlyDynamicEdges: false, fit: true }, - timestep: 0.5 + timestep: 0.5, + adaptiveTimestep: true }; util.extend(this.options, this.defaultOptions); + this.timestep = 0.5; + this.layoutFailed = false; this.bindEventListeners(); } bindEventListeners() { this.body.emitter.on('initPhysics', () => {this.initPhysics();}); + this.body.emitter.on('_layoutFailed', () => {this.layoutFailed = true;}); this.body.emitter.on('resetPhysics', () => {this.stopSimulation(); this.ready = false;}); this.body.emitter.on('disablePhysics', () => {this.physicsEnabled = false; this.stopSimulation();}); this.body.emitter.on('restorePhysics', () => { @@ -102,8 +112,21 @@ class PhysicsEngine { this.stopSimulation(false); this.body.emitter.off(); }); + // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed. + this.body.emitter.on("_dataChanged", () => { + // update shortcut lists + this.updatePhysicsData(); + }); + + // debug: show forces + // this.body.emitter.on("afterDrawing", (ctx) => {this._drawForces(ctx);}); } + + /** + * set the physics options + * @param options + */ setOptions(options) { if (options !== undefined) { if (options === false) { @@ -114,7 +137,7 @@ class PhysicsEngine { else { this.physicsEnabled = true; util.selectiveNotDeepExtend(['stabilization'], this.options, options); - util.mergeOptions(this.options, options, 'stabilization') + util.mergeOptions(this.options, options, 'stabilization'); if (options.enabled === undefined) { this.options.enabled = true; @@ -124,12 +147,18 @@ class PhysicsEngine { this.physicsEnabled = false; this.stopSimulation(); } + + // set the timestep + this.timestep = this.options.timestep; } } this.init(); } + /** + * configure the engine. + */ init() { var options; if (this.options.solver === 'forceAtlas2Based') { @@ -160,6 +189,10 @@ class PhysicsEngine { this.modelOptions = options; } + + /** + * initialize the engine + */ initPhysics() { if (this.physicsEnabled === true && this.options.enabled === true) { if (this.options.stabilization.enabled === true) { @@ -168,7 +201,7 @@ class PhysicsEngine { else { this.stabilized = false; this.ready = true; - this.body.emitter.emit('fit', {}, true); + this.body.emitter.emit('fit', {}, this.layoutFailed); // if the layout failed, we use the approximation for the zoom this.startSimulation(); } } @@ -185,6 +218,9 @@ class PhysicsEngine { if (this.physicsEnabled === true && this.options.enabled === true) { this.stabilized = false; + // when visible, adaptivity is disabled. + this.adaptiveTimestep = false; + // this sets the width of all nodes initially which could be required for the avoidOverlap this.body.emitter.emit("_resizeNodes"); if (this.viewFunction === undefined) { @@ -218,7 +254,7 @@ class PhysicsEngine { /** - * The viewFunction inserts this step into each renderloop. It calls the physics tick and handles the cleanup at stabilized. + * The viewFunction inserts this step into each render loop. It calls the physics tick and handles the cleanup at stabilized. * */ simulationStep() { @@ -236,23 +272,20 @@ class PhysicsEngine { } if (this.stabilized === true) { - if (this.stabilizationIterations > 1) { - // trigger the 'stabilized' event. - // The event is triggered on the next tick, to prevent the case that - // it is fired while initializing the Network, in which case you would not - // be able to catch it - this.startedStabilization = false; - //this._emitStabilized(); - } this.stopSimulation(); } } - _emitStabilized() { - if (this.stabilizationIterations > 1 || this.initialStabilizationEmitted === false) { - this.initialStabilizationEmitted = true; + + /** + * trigger the stabilized event. + * @private + */ + _emitStabilized(amountOfIterations = this.stabilizationIterations) { + if (this.stabilizationIterations > 1 || this.startedStabilization === true) { setTimeout(() => { - this.body.emitter.emit('stabilized', {iterations: this.stabilizationIterations}); + this.body.emitter.emit('stabilized', {iterations: amountOfIterations}); + this.startedStabilization = false; this.stabilizationIterations = 0; }, 0); } @@ -264,21 +297,74 @@ class PhysicsEngine { * @private */ physicsTick() { + // this is here to ensure that there is no start event when the network is already stable. + if (this.startedStabilization === false) { + this.body.emitter.emit('startStabilizing'); + this.startedStabilization = true; + } + if (this.stabilized === false) { - this.calculateForces(); - this.stabilized = this.moveNodes(); + // adaptivity means the timestep adapts to the situation, only applicable for stabilization + if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { + // this is the factor for increasing the timestep on success. + let factor = 1.2; + + // we assume the adaptive interval is + if (this.adaptiveCounter % this.adaptiveInterval === 0) { // we leave the timestep stable for "interval" iterations. + // first the big step and revert. Revert saves the reference state. + this.timestep = 2 * this.timestep; + this.calculateForces(); + this.moveNodes(); + this.revert(); + + // now the normal step. Since this is the last step, it is the more stable one and we will take this. + this.timestep = 0.5 * this.timestep; + + // since it's half the step, we do it twice. + this.calculateForces(); + this.moveNodes(); + this.calculateForces(); + this.moveNodes(); + + // we compare the two steps. if it is acceptable we double the step. + if (this._evaluateStepQuality() === true) { + this.timestep = factor * this.timestep; + } + else { + // if not, we decrease the step to a minimum of the options timestep. + // if the decreased timestep is smaller than the options step, we do not reset the counter + // we assume that the options timestep is stable enough. + if (this.timestep/factor < this.options.timestep) { + this.timestep = this.options.timestep; + } + else { + // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure + // that large instabilities do not form. + this.adaptiveCounter = -1; // check again next iteration + this.timestep = Math.max(this.options.timestep, this.timestep/factor); + } + } + } + else { + // normal step, keeping timestep constant + this.calculateForces(); + this.moveNodes(); + } + + // increment the counter + this.adaptiveCounter += 1; + } + else { + // case for the static timestep, we reset it to the one in options and take a normal step. + this.timestep = this.options.timestep; + this.calculateForces(); + this.moveNodes(); + } // determine if the network has stabilzied if (this.stabilized === true) { this.revert(); } - else { - // this is here to ensure that there is no start event when the network is already stable. - if (this.startedStabilization === false) { - this.body.emitter.emit('startStabilizing'); - this.startedStabilization = true; - } - } this.stabilizationIterations++; } @@ -300,7 +386,7 @@ class PhysicsEngine { for (let nodeId in nodes) { if (nodes.hasOwnProperty(nodeId)) { if (nodes[nodeId].options.physics === true) { - this.physicsBody.physicsNodeIndices.push(nodeId); + this.physicsBody.physicsNodeIndices.push(nodes[nodeId].id); } } } @@ -309,7 +395,7 @@ class PhysicsEngine { for (let edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { if (edges[edgeId].options.physics === true) { - this.physicsBody.physicsEdgeIndices.push(edgeId); + this.physicsBody.physicsEdgeIndices.push(edges[edgeId].id); } } } @@ -341,11 +427,15 @@ class PhysicsEngine { var nodeIds = Object.keys(this.previousStates); var nodes = this.body.nodes; var velocities = this.physicsBody.velocities; + this.referenceState = {}; for (let i = 0; i < nodeIds.length; i++) { let nodeId = nodeIds[i]; if (nodes[nodeId] !== undefined) { if (nodes[nodeId].options.physics === true) { + this.referenceState[nodeId] = { + positions: {x:nodes[nodeId].x, y:nodes[nodeId].y} + }; velocities[nodeId].x = this.previousStates[nodeId].vx; velocities[nodeId].y = this.previousStates[nodeId].vy; nodes[nodeId].x = this.previousStates[nodeId].x; @@ -359,34 +449,53 @@ class PhysicsEngine { } /** - * move the nodes one timestap and check if they are stabilized + * This compares the reference state to the current state + */ + _evaluateStepQuality() { + let dx, dy, dpos; + let nodes = this.body.nodes; + let reference = this.referenceState; + let posThreshold = 0.3; + + for (let nodeId in this.referenceState) { + if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) { + dx = nodes[nodeId].x - reference[nodeId].positions.x; + dy = nodes[nodeId].y - reference[nodeId].positions.y; + + dpos = Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2)) + + if (dpos > posThreshold) { + return false; + } + } + } + return true; + } + + /** + * move the nodes one timestep and check if they are stabilized * @returns {boolean} */ moveNodes() { - var nodesPresent = false; var nodeIndices = this.physicsBody.physicsNodeIndices; var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; - var stabilized = true; - var vminCorrected = this.options.minVelocity / Math.max(this.body.view.scale,0.05); + var maxNodeVelocity = 0; + var averageNodeVelocity = 0; + + // the velocity threshold (energy in the system) for the adaptivity toggle + var velocityAdaptiveThreshold = 5; for (let i = 0; i < nodeIndices.length; i++) { let nodeId = nodeIndices[i]; let nodeVelocity = this._performStep(nodeId, maxVelocity); // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized - stabilized = nodeVelocity < vminCorrected && stabilized === true; - nodesPresent = true; + maxNodeVelocity = Math.max(maxNodeVelocity,nodeVelocity); + averageNodeVelocity += nodeVelocity; } - - if (nodesPresent === true) { - if (vminCorrected > 0.5*this.options.maxVelocity) { - return false; - } - else { - return stabilized; - } - } - return true; + // evaluating the stabilized and adaptiveTimestepEnabled conditions + this.adaptiveTimestepEnabled = (averageNodeVelocity/nodeIndices.length) < velocityAdaptiveThreshold; + this.stabilized = maxNodeVelocity < this.options.minVelocity; } @@ -400,7 +509,7 @@ class PhysicsEngine { */ _performStep(nodeId,maxVelocity) { let node = this.body.nodes[nodeId]; - let timestep = this.options.timestep; + let timestep = this.timestep; let forces = this.physicsBody.forces; let velocities = this.physicsBody.velocities; @@ -486,7 +595,6 @@ class PhysicsEngine { /** * Find a stable position for all nodes - * @private */ stabilize(iterations = this.options.stabilization.iterations) { if (typeof iterations !== 'number') { @@ -499,6 +607,8 @@ class PhysicsEngine { return; } + // enable adaptive timesteps + this.adaptiveTimestep = true && this.options.adaptiveTimestep; // this sets the width of all nodes initially which could be required for the avoidOverlap this.body.emitter.emit("_resizeNodes"); @@ -522,11 +632,21 @@ class PhysicsEngine { setTimeout(() => this._stabilizationBatch(),0); } + + /** + * One batch of stabilization + * @private + */ _stabilizationBatch() { + // this is here to ensure that there is at least one start event. + if (this.startedStabilization === false) { + this.body.emitter.emit('startStabilizing'); + this.startedStabilization = true; + } + var count = 0; while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) { this.physicsTick(); - this.stabilizationIterations++; count++; } @@ -539,6 +659,11 @@ class PhysicsEngine { } } + + /** + * Wrap up the stabilization, fit and emit the events. + * @private + */ _finalizeStabilization() { this.body.emitter.emit('_allowRedraw'); if (this.options.stabilization.fit === true) { @@ -548,7 +673,7 @@ class PhysicsEngine { if (this.options.stabilization.onlyDynamicEdges === true) { this._restoreFrozenNodes(); } - + this.body.emitter.emit('stabilizationIterationsDone'); this.body.emitter.emit('_requestRedraw'); @@ -561,6 +686,34 @@ class PhysicsEngine { this.ready = true; } + + + _drawForces(ctx) { + for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { + let node = this.body.nodes[this.physicsBody.physicsNodeIndices[i]]; + let force = this.physicsBody.forces[this.physicsBody.physicsNodeIndices[i]]; + let factor = 20; + let colorFactor = 0.03; + let forceSize = Math.sqrt(Math.pow(force.x,2) + Math.pow(force.x,2)); + + let size = Math.min(Math.max(5,forceSize),15); + let arrowSize = 3*size; + + let color = util.HSVToHex((180 - Math.min(1,Math.max(0,colorFactor*forceSize))*180) / 360,1,1); + + ctx.lineWidth = size; + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(node.x,node.y); + ctx.lineTo(node.x+factor*force.x, node.y+factor*force.y); + ctx.stroke(); + + let angle = Math.atan2(force.y, force.x); + ctx.fillStyle = color; + ctx.arrow(node.x + factor*force.x + Math.cos(angle)*arrowSize, node.y + factor*force.y+Math.sin(angle)*arrowSize, angle, arrowSize); + ctx.fill(); + } + } } diff --git a/lib/vis/lib/network/modules/SelectionHandler.js b/lib/vis/lib/network/modules/SelectionHandler.js index 891ec7ebee..415b6ba801 100644 --- a/lib/vis/lib/network/modules/SelectionHandler.js +++ b/lib/vis/lib/network/modules/SelectionHandler.js @@ -159,7 +159,6 @@ class SelectionHandler { * * @param {{x: Number, y: Number}} pointer * @return {Node | undefined} node - * @private */ getNodeAt(pointer, returnNode = true) { // we first check if this is an navigation controls element @@ -217,7 +216,6 @@ class SelectionHandler { * * @param pointer * @returns {undefined} - * @private */ getEdgeAt(pointer, returnEdge = true) { let positionObject = this._pointerToPositionObject(pointer); @@ -277,6 +275,7 @@ class SelectionHandler { _removeFromSelection(obj) { if (obj instanceof Node) { delete this.selectionObj.nodes[obj.id]; + this._unselectConnectedEdges(obj); } else { delete this.selectionObj.edges[obj.id]; @@ -285,8 +284,6 @@ class SelectionHandler { /** * Unselect all. The selectionObj is useful for this. - * - * @private */ unselectAll() { for(let nodeId in this.selectionObj.nodes) { @@ -299,7 +296,7 @@ class SelectionHandler { this.selectionObj.edges[edgeId].unselect(); } } - + this.selectionObj = {nodes:{},edges:{}}; } @@ -575,7 +572,7 @@ class SelectionHandler { if (this.options.selectable === true) { for (let nodeId in this.selectionObj.nodes) { if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); + idArray.push(this.selectionObj.nodes[nodeId].id); } } } @@ -593,13 +590,54 @@ class SelectionHandler { if (this.options.selectable === true) { for (let edgeId in this.selectionObj.edges) { if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); + idArray.push(this.selectionObj.edges[edgeId].id); } } } return idArray; } + /** + * Updates the current selection + * @param {{nodes: Array., edges: Array.}} Selection + * @param {Object} options Options + */ + setSelection(selection, options = {}) { + let i, id; + + if (!selection || (!selection.nodes && !selection.edges)) + throw 'Selection must be an object with nodes and/or edges properties'; + // first unselect any selected node, if option is true or undefined + if (options.unselectAll || options.unselectAll === undefined) { + this.unselectAll(); + } + if (selection.nodes) { + for (i = 0; i < selection.nodes.length; i++) { + id = selection.nodes[i]; + + let node = this.body.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + // don't select edges with it + this.selectObject(node, options.highlightEdges); + } + } + + if (selection.edges) { + for (i = 0; i < selection.edges.length; i++) { + id = selection.edges[i]; + + let edge = this.body.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); + } + this.selectObject(edge); + } + } + this.body.emitter.emit('_requestRedraw'); + } + /** * select zero or more nodes with the option to highlight edges @@ -608,24 +646,10 @@ class SelectionHandler { * @param {boolean} [highlightEdges] */ selectNodes(selection, highlightEdges = true) { - let i, id; - if (!selection || (selection.length === undefined)) throw 'Selection must be an array with ids'; - - // first unselect any selected node - this.unselectAll(); - - for (i = 0; i < selection.length; i++) { - id = selection[i]; - - let node = this.body.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); - } - this.selectObject(node,highlightEdges); - } - this.body.emitter.emit('_requestRedraw'); + + this.setSelection({nodes: selection}, {highlightEdges: highlightEdges}); } @@ -635,24 +659,10 @@ class SelectionHandler { * selected nodes. */ selectEdges(selection) { - let i, id; - if (!selection || (selection.length === undefined)) throw 'Selection must be an array with ids'; - - // first unselect any selected objects - this.unselectAll(); - - for (i = 0; i < selection.length; i++) { - id = selection[i]; - - let edge = this.body.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); - } - this.selectObject(edge); - } - this.body.emitter.emit('_requestRedraw'); + + this.setSelection({edges: selection}); } /** diff --git a/lib/vis/lib/network/modules/View.js b/lib/vis/lib/network/modules/View.js index 586831156a..d1004c8053 100644 --- a/lib/vis/lib/network/modules/View.js +++ b/lib/vis/lib/network/modules/View.js @@ -1,4 +1,6 @@ -var util = require('../../util'); +let util = require('../../util'); + +import NetworkUtil from '../NetworkUtil'; class View { constructor(body, canvas) { @@ -29,80 +31,25 @@ class View { } - /** - * Find the center position of the network - * @private - */ - _getRange(specificNodes = []) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - if (specificNodes.length > 0) { - for (var i = 0; i < specificNodes.length; i++) { - node = this.body.nodes[specificNodes[i]]; - if (minX > (node.shape.boundingBox.left)) { - minX = node.shape.boundingBox.left; - } - if (maxX < (node.shape.boundingBox.right)) { - maxX = node.shape.boundingBox.right; - } - if (minY > (node.shape.boundingBox.top)) { - minY = node.shape.boundingBox.top; - } // top is negative, bottom is positive - if (maxY < (node.shape.boundingBox.bottom)) { - maxY = node.shape.boundingBox.bottom; - } // top is negative, bottom is positive - } - } - else { - for (var i = 0; i < this.body.nodeIndices.length; i++) { - node = this.body.nodes[this.body.nodeIndices[i]]; - if (minX > (node.shape.boundingBox.left)) { - minX = node.shape.boundingBox.left; - } - if (maxX < (node.shape.boundingBox.right)) { - maxX = node.shape.boundingBox.right; - } - if (minY > (node.shape.boundingBox.top)) { - minY = node.shape.boundingBox.top; - } // top is negative, bottom is positive - if (maxY < (node.shape.boundingBox.bottom)) { - maxY = node.shape.boundingBox.bottom; - } // top is negative, bottom is positive - } - } - - if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { - minY = 0, maxY = 0, minX = 0, maxX = 0; - } - return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - } - - - /** - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - * @returns {{x: number, y: number}} - * @private - */ - _findCenter(range) { - return {x: (0.5 * (range.maxX + range.minX)), - y: (0.5 * (range.maxY + range.minY))}; - } - - /** * This function zooms out to fit all data on screen based on amount of nodes * @param {Object} Options * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; */ fit(options = {nodes:[]}, initialZoom = false) { - var range; - var zoomLevel; + let range; + let zoomLevel; + if (options.nodes === undefined || options.nodes.length === 0) { + options.nodes = this.body.nodeIndices; + } + if (initialZoom === true) { // check if more than half of the nodes have a predefined position. If so, we use the range, not the approximation. - var positionDefined = 0; - for (var nodeId in this.body.nodes) { + let positionDefined = 0; + for (let nodeId in this.body.nodes) { if (this.body.nodes.hasOwnProperty(nodeId)) { - var node = this.body.nodes[nodeId]; + let node = this.body.nodes[nodeId]; if (node.predefinedPosition === true) { positionDefined += 1; } @@ -113,24 +60,24 @@ class View { return; } - range = this._getRange(options.nodes); + range = NetworkUtil.getRange(this.body.nodes, options.nodes); - var numberOfNodes = this.body.nodeIndices.length; + let numberOfNodes = this.body.nodeIndices.length; zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. // correct for larger canvasses. - var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); + let factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); zoomLevel *= factor; } else { this.body.emitter.emit("_resizeNodes"); - range = this._getRange(options.nodes); + range = NetworkUtil.getRange(this.body.nodes, options.nodes); - var xDistance = Math.abs(range.maxX - range.minX) * 1.1; - var yDistance = Math.abs(range.maxY - range.minY) * 1.1; + let xDistance = Math.abs(range.maxX - range.minX) * 1.1; + let yDistance = Math.abs(range.maxY - range.minY) * 1.1; - var xZoomLevel = this.canvas.frame.canvas.clientWidth / xDistance; - var yZoomLevel = this.canvas.frame.canvas.clientHeight / yDistance; + let xZoomLevel = this.canvas.frame.canvas.clientWidth / xDistance; + let yZoomLevel = this.canvas.frame.canvas.clientHeight / yDistance; zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; } @@ -142,8 +89,8 @@ class View { zoomLevel = 1.0; } - var center = this._findCenter(range); - var animationOptions = {position: center, scale: zoomLevel, animation: options.animation}; + let center = NetworkUtil.findCenter(range); + let animationOptions = {position: center, scale: zoomLevel, animation: options.animation}; this.moveTo(animationOptions); } @@ -157,7 +104,7 @@ class View { */ focus(nodeId, options = {}) { if (this.body.nodes[nodeId] !== undefined) { - var nodePosition = {x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y}; + let nodePosition = {x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y}; options.position = nodePosition; options.lockedOnNode = nodeId; @@ -229,9 +176,9 @@ class View { // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw // but at least then we'll have the target transition this.body.view.scale = this.targetScale; - var viewCenter = this.canvas.DOMtoCanvas({x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight}); + let viewCenter = this.canvas.DOMtoCanvas({x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + let distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node x: viewCenter.x - options.position.x, y: viewCenter.y - options.position.y }; @@ -268,14 +215,14 @@ class View { * @private */ _lockedRedraw() { - var nodePosition = {x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y}; - var viewCenter = this.canvas.DOMtoCanvas({x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + let nodePosition = {x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y}; + let viewCenter = this.canvas.DOMtoCanvas({x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight}); + let distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node x: viewCenter.x - nodePosition.x, y: viewCenter.y - nodePosition.y }; - var sourceTranslation = this.body.view.translation; - var targetTranslation = { + let sourceTranslation = this.body.view.translation; + let targetTranslation = { x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x, y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y }; @@ -300,7 +247,7 @@ class View { this.easingTime += this.animationSpeed; this.easingTime = finished === true ? 1.0 : this.easingTime; - var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); + let progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress; this.body.view.translation = { diff --git a/lib/vis/lib/network/modules/components/Edge.js b/lib/vis/lib/network/modules/components/Edge.js index c4d362301d..01c84829de 100644 --- a/lib/vis/lib/network/modules/components/Edge.js +++ b/lib/vis/lib/network/modules/components/Edge.js @@ -27,6 +27,7 @@ class Edge { throw "No body provided"; } this.options = util.bridgeObject(globalOptions); + this.globalOptions = globalOptions; this.body = body; // initialize variables @@ -65,7 +66,7 @@ class Edge { } this.colorDirty = true; - Edge.parseOptions(this.options, options, true); + Edge.parseOptions(this.options, options, true, this.globalOptions); if (options.id !== undefined) {this.id = options.id;} if (options.from !== undefined) {this.fromId = options.from;} @@ -92,8 +93,9 @@ class Edge { return dataChanged; } - static parseOptions(parentOptions, newOptions, allowDeletion = false) { + static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { var fields = [ + 'arrowStrikethrough', 'id', 'from', 'hidden', @@ -104,6 +106,7 @@ class Edge { 'line', 'opacity', 'physics', + 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', @@ -115,29 +118,27 @@ class Edge { // only deep extend the items in the field array. These do not have shorthand. util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); - util.mergeOptions(parentOptions, newOptions, 'smooth'); - util.mergeOptions(parentOptions, newOptions, 'shadow'); + util.mergeOptions(parentOptions, newOptions, 'smooth', allowDeletion, globalOptions); + util.mergeOptions(parentOptions, newOptions, 'shadow', allowDeletion, globalOptions); if (newOptions.dashes !== undefined && newOptions.dashes !== null) { parentOptions.dashes = newOptions.dashes; } else if (allowDeletion === true && newOptions.dashes === null) { - parentOptions.dashes = undefined; - delete parentOptions.dashes; + parentOptions.dashes = Object.create(globalOptions.dashes); // this sets the pointer of the option back to the global option. } // set the scaling newOptions if (newOptions.scaling !== undefined && newOptions.scaling !== null) { if (newOptions.scaling.min !== undefined) {parentOptions.scaling.min = newOptions.scaling.min;} if (newOptions.scaling.max !== undefined) {parentOptions.scaling.max = newOptions.scaling.max;} - util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); + util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', allowDeletion, globalOptions.scaling); } else if (allowDeletion === true && newOptions.scaling === null) { - parentOptions.scaling = undefined; - delete parentOptions.scaling; + parentOptions.scaling = Object.create(globalOptions.scaling); // this sets the pointer of the option back to the global option. } - // hanlde multiple input cases for arrows + // handle multiple input cases for arrows if (newOptions.arrows !== undefined && newOptions.arrows !== null) { if (typeof newOptions.arrows === 'string') { let arrows = newOptions.arrows.toLowerCase(); @@ -146,21 +147,22 @@ class Edge { if (arrows.indexOf("from") != -1) {parentOptions.arrows.from.enabled = true;} } else if (typeof newOptions.arrows === 'object') { - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to'); - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle'); - util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from'); + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to', allowDeletion, globalOptions.arrows); + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', allowDeletion, globalOptions.arrows); + util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', allowDeletion, globalOptions.arrows); } else { throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + JSON.stringify(newOptions.arrows)); } } else if (allowDeletion === true && newOptions.arrows === null) { - parentOptions.arrows = undefined; - delete parentOptions.arrows; + parentOptions.arrows = Object.create(globalOptions.arrows); // this sets the pointer of the option back to the global option. } - // hanlde multiple input cases for color + // handle multiple input cases for color if (newOptions.color !== undefined && newOptions.color !== null) { + // make a copy of the parent object in case this is referring to the global one (due to object create once, then update) + parentOptions.color = util.deepExtend({}, parentOptions.color, true); if (util.isString(newOptions.color)) { parentOptions.color.color = newOptions.color; parentOptions.color.highlight = newOptions.color; @@ -181,14 +183,16 @@ class Edge { } } else if (allowDeletion === true && newOptions.color === null) { - parentOptions.color = undefined; - delete parentOptions.color; + parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options } // handle the font settings - if (newOptions.font !== undefined) { + if (newOptions.font !== undefined && newOptions.font !== null) { Label.parseOptions(parentOptions.font, newOptions); } + else if (allowDeletion === true && newOptions.font === null) { + parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options + } } @@ -343,6 +347,7 @@ class Edge { } this._setInteractionWidths(); + this.updateLabelModule(); } _setInteractionWidths() { @@ -369,17 +374,45 @@ class Edge { * @param {CanvasRenderingContext2D} ctx */ draw(ctx) { - let via = this.edgeType.drawLine(ctx, this.selected, this.hover); - this.drawArrows(ctx, via); - this.drawLabel (ctx, via); + // get the via node from the edge type + let viaNode = this.edgeType.getViaNode(); + let arrowData = {}; + + // restore edge targets to defaults + this.edgeType.fromPoint = this.from; + this.edgeType.toPoint = this.to; + + // from and to arrows give a different end point for edges. we set them here + if (this.options.arrows.from.enabled === true) { + arrowData.from = this.edgeType.getArrowData(ctx,'from', viaNode, this.selected, this.hover); + if (this.options.arrowStrikethrough === false) + this.edgeType.fromPoint = arrowData.from.core; + } + if (this.options.arrows.to.enabled === true) { + arrowData.to = this.edgeType.getArrowData(ctx,'to', viaNode, this.selected, this.hover); + if (this.options.arrowStrikethrough === false) + this.edgeType.toPoint = arrowData.to.core; + } + + // the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly. + if (this.options.arrows.middle.enabled === true) { + arrowData.middle = this.edgeType.getArrowData(ctx,'middle', viaNode, this.selected, this.hover); + } + + // draw everything + this.edgeType.drawLine(ctx, this.selected, this.hover, viaNode); + this.drawArrows(ctx, arrowData); + this.drawLabel (ctx, viaNode); } - drawArrows(ctx, viaNode) { - if (this.options.arrows.from.enabled === true) {this.edgeType.drawArrowHead(ctx,'from', viaNode, this.selected, this.hover);} - if (this.options.arrows.middle.enabled === true) {this.edgeType.drawArrowHead(ctx,'middle', viaNode, this.selected, this.hover);} - if (this.options.arrows.to.enabled === true) {this.edgeType.drawArrowHead(ctx,'to', viaNode, this.selected, this.hover);} + + drawArrows(ctx, arrowData) { + if (this.options.arrows.from.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.from);} + if (this.options.arrows.middle.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.middle);} + if (this.options.arrows.to.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.to);} } + drawLabel(ctx, viaNode) { if (this.options.label !== undefined) { // set style diff --git a/lib/vis/lib/network/modules/components/Node.js b/lib/vis/lib/network/modules/components/Node.js index dabb63486b..79e32375cb 100644 --- a/lib/vis/lib/network/modules/components/Node.js +++ b/lib/vis/lib/network/modules/components/Node.js @@ -48,6 +48,7 @@ import {printStyle} from "../../../shared/Validator"; class Node { constructor(options, body, imagelist, grouplist, globalOptions) { this.options = util.bridgeObject(globalOptions); + this.globalOptions = globalOptions; this.body = body; this.edges = []; // all edges connected to this node @@ -125,7 +126,6 @@ class Node { if (options.size !== undefined) {this.baseSize = options.size;} if (options.value !== undefined) {options.value = parseFloat(options.value);} - // copy group options if (typeof options.group === 'number' || (typeof options.group === 'string' && options.group != '')) { var groupObj = this.grouplist.get(options.group); @@ -135,7 +135,7 @@ class Node { } // this transforms all shorthands into fully defined options - Node.parseOptions(this.options, options, true); + Node.parseOptions(this.options, options, true, this.globalOptions); // load the images if (this.options.image !== undefined) { @@ -147,8 +147,8 @@ class Node { } } - this.updateShape(currentShape); this.updateLabelModule(); + this.updateShape(currentShape); if (options.hidden !== undefined || options.physics !== undefined) { return true; @@ -162,8 +162,10 @@ class Node { * Static so it can also be used by the handler. * @param parentOptions * @param newOptions + * @param allowDeletion + * @param globalOptions */ - static parseOptions(parentOptions, newOptions, allowDeletion = false) { + static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) { var fields = [ 'color', 'font', @@ -173,7 +175,7 @@ class Node { util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); // merge the shadow options into the parent. - util.mergeOptions(parentOptions, newOptions, 'shadow'); + util.mergeOptions(parentOptions, newOptions, 'shadow', allowDeletion, globalOptions); // individual shape newOptions if (newOptions.color !== undefined && newOptions.color !== null) { @@ -181,8 +183,7 @@ class Node { util.fillIfDefined(parentOptions.color, parsedColor); } else if (allowDeletion === true && newOptions.color === null) { - parentOptions.color = undefined; - delete parentOptions.color; + parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options } // handle the fixed options @@ -202,13 +203,16 @@ class Node { } // handle the font options - if (newOptions.font !== undefined) { + if (newOptions.font !== undefined && newOptions.font !== null) { Label.parseOptions(parentOptions.font, newOptions); } + else if (allowDeletion === true && newOptions.font === null) { + parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options + } // handle the scaling options, specifically the label part if (newOptions.scaling !== undefined) { - util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label'); + util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', allowDeletion, globalOptions.scaling); } } @@ -224,13 +228,9 @@ class Node { updateShape(currentShape) { if (currentShape === this.options.shape && this.shape) { - this.shape.setOptions(this.options); + this.shape.setOptions(this.options, this.imageObj); } else { - // clean up the shape if it is already made so the new shape can start clean. - if (this.shape) { - this.shape.cleanup(); - } // choose draw method depending on the shape switch (this.options.shape) { case 'box': @@ -381,6 +381,8 @@ class Node { this.options.size = this.baseSize; this.options.font.size = this.baseFontSize; } + + this.updateLabelModule(); } @@ -432,20 +434,12 @@ class Node { */ isBoundingBoxOverlappingWith(obj) { return ( - this.shape.boundingBox.left < obj.right && - this.shape.boundingBox.right > obj.left && - this.shape.boundingBox.top < obj.bottom && - this.shape.boundingBox.bottom > obj.top + this.shape.boundingBox.left < obj.right && + this.shape.boundingBox.right > obj.left && + this.shape.boundingBox.top < obj.bottom && + this.shape.boundingBox.bottom > obj.top ); } - - /** - * clean all required things on delete. - * @returns {*} - */ - cleanup() { - return this.shape.cleanup(); - } } export default Node; diff --git a/lib/vis/lib/network/modules/components/algorithms/FloydWarshall.js b/lib/vis/lib/network/modules/components/algorithms/FloydWarshall.js new file mode 100644 index 0000000000..e0d3f92d40 --- /dev/null +++ b/lib/vis/lib/network/modules/components/algorithms/FloydWarshall.js @@ -0,0 +1,49 @@ +/** + * Created by Alex on 10-Aug-15. + */ + + +class FloydWarshall { + constructor(){} + + getDistances(body, nodesArray, edgesArray) { + let D_matrix = {}; + let edges = body.edges; + + // prepare matrix with large numbers + for (let i = 0; i < nodesArray.length; i++) { + D_matrix[nodesArray[i]] = {}; + D_matrix[nodesArray[i]] = {}; + for (let j = 0; j < nodesArray.length; j++) { + D_matrix[nodesArray[i]][nodesArray[j]] = (i == j ? 0 : 1e9); + D_matrix[nodesArray[i]][nodesArray[j]] = (i == j ? 0 : 1e9); + } + } + + // put the weights for the edges in. This assumes unidirectionality. + for (let i = 0; i < edgesArray.length; i++) { + let edge = edges[edgesArray[i]]; + // edge has to be connected if it counts to the distances. If it is connected to inner clusters it will crash so we also check if it is in the D_matrix + if (edge.connected === true && D_matrix[edge.fromId] !== undefined && D_matrix[edge.toId] !== undefined) { + D_matrix[edge.fromId][edge.toId] = 1; + D_matrix[edge.toId][edge.fromId] = 1; + } + } + + let nodeCount = nodesArray.length; + + // Adapted FloydWarshall based on unidirectionality to greatly reduce complexity. + for (let k = 0; k < nodeCount; k++) { + for (let i = 0; i < nodeCount-1; i++) { + for (let j = i+1; j < nodeCount; j++) { + D_matrix[nodesArray[i]][nodesArray[j]] = Math.min(D_matrix[nodesArray[i]][nodesArray[j]],D_matrix[nodesArray[i]][nodesArray[k]] + D_matrix[nodesArray[k]][nodesArray[j]]) + D_matrix[nodesArray[j]][nodesArray[i]] = D_matrix[nodesArray[i]][nodesArray[j]]; + } + } + } + + return D_matrix; + } +} + +export default FloydWarshall; \ No newline at end of file diff --git a/lib/vis/lib/network/modules/components/edges/BezierEdgeDynamic.js b/lib/vis/lib/network/modules/components/edges/BezierEdgeDynamic.js index 893f00e4a4..0108d50a36 100644 --- a/lib/vis/lib/network/modules/components/edges/BezierEdgeDynamic.js +++ b/lib/vis/lib/network/modules/components/edges/BezierEdgeDynamic.js @@ -4,19 +4,32 @@ class BezierEdgeDynamic extends BezierEdgeBase { constructor(options, body, labelModule) { //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. super(options, body, labelModule); // --> this calls the setOptions below + this._boundFunction = () => {this.positionBezierNode();}; + this.body.emitter.on("_repositionBezierNodes", this._boundFunction); } setOptions(options) { + // check if the physics has changed. + let physicsChange = false; + if (this.options.physics !== options.physics) { + physicsChange = true; + } + + // set the options and the to and from nodes this.options = options; this.id = this.options.id; + this.from = this.body.nodes[this.options.from]; + this.to = this.body.nodes[this.options.to]; + + // setup the support node and connect this.setupSupportNode(); + this.connect(); // when we change the physics state of the edge, we reposition the support node. - if (this.options.physics !== options.physics) { + if (physicsChange === true) { this.via.setOptions({physics: this.options.physics}) this.positionBezierNode(); } - this.connect(); } connect() { @@ -26,7 +39,7 @@ class BezierEdgeDynamic extends BezierEdgeBase { this.via.setOptions({physics:false}) } else { - // fix weird behaviour where a selfreferencing node has physics enabled + // fix weird behaviour where a self referencing node has physics enabled if (this.from.id === this.to.id) { this.via.setOptions({physics: false}) } @@ -41,6 +54,7 @@ class BezierEdgeDynamic extends BezierEdgeBase { * @returns {boolean} */ cleanup() { + this.body.emitter.off("_repositionBezierNodes", this._boundFunction); if (this.via !== undefined) { delete this.body.nodes[this.via.id]; this.via = undefined; @@ -88,15 +102,24 @@ class BezierEdgeDynamic extends BezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx) { + _line(ctx, viaNode) { // draw a straight line ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y); + ctx.moveTo(this.fromPoint.x, this.fromPoint.y); + // fallback to normal straight edges + if (viaNode.x === undefined) { + ctx.lineTo(this.toPoint.x, this.toPoint.y); + } + else { + ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y); + } // draw shadow if enabled this.enableShadow(ctx); ctx.stroke(); this.disableShadow(ctx); + } + + getViaNode() { return this.via; } @@ -104,14 +127,14 @@ class BezierEdgeDynamic extends BezierEdgeBase { /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way * @param percentage - * @param via + * @param viaNode * @returns {{x: number, y: number}} * @private */ - getPoint(percentage) { + getPoint(percentage, viaNode = this.via) { let t = percentage; - let x = Math.pow(1 - t, 2) * this.from.x + (2 * t * (1 - t)) * this.via.x + Math.pow(t, 2) * this.to.x; - let y = Math.pow(1 - t, 2) * this.from.y + (2 * t * (1 - t)) * this.via.y + Math.pow(t, 2) * this.to.y; + let x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x; + let y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y; return {x: x, y: y}; } diff --git a/lib/vis/lib/network/modules/components/edges/BezierEdgeStatic.js b/lib/vis/lib/network/modules/components/edges/BezierEdgeStatic.js index 9b3619b0af..d70ed7c0d2 100644 --- a/lib/vis/lib/network/modules/components/edges/BezierEdgeStatic.js +++ b/lib/vis/lib/network/modules/components/edges/BezierEdgeStatic.js @@ -10,28 +10,34 @@ class BezierEdgeStatic extends BezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx) { + _line(ctx, viaNode) { // draw a straight line ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - let via = this._getViaCoordinates(); - let returnValue = via; + ctx.moveTo(this.fromPoint.x, this.fromPoint.y); // fallback to normal straight edges - if (via.x === undefined) { - ctx.lineTo(this.to.x, this.to.y); - returnValue = undefined; + if (viaNode.x === undefined) { + ctx.lineTo(this.toPoint.x, this.toPoint.y); } else { - ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y); + ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y); } // draw shadow if enabled this.enableShadow(ctx); ctx.stroke(); this.disableShadow(ctx); - return returnValue; } + getViaNode() { + return this._getViaCoordinates(); + } + + + /** + * We do not use the to and fromPoints here to make the via nodes the same as edges without arrows. + * @returns {{x: undefined, y: undefined}} + * @private + */ _getViaCoordinates() { let xVia = undefined; let yVia = undefined; @@ -214,21 +220,21 @@ class BezierEdgeStatic extends BezierEdgeBase { return this._findBorderPositionBezier(nearNode, ctx, options.via); } - _getDistanceToEdge(x1, y1, x2, y2, x3, y3, via = this._getViaCoordinates()) { // x3,y3 is the point - return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via); + _getDistanceToEdge(x1, y1, x2, y2, x3, y3, viaNode = this._getViaCoordinates()) { // x3,y3 is the point + return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode); } /** * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way * @param percentage - * @param via + * @param viaNode * @returns {{x: number, y: number}} * @private */ - getPoint(percentage, via = this._getViaCoordinates()) { + getPoint(percentage, viaNode = this._getViaCoordinates()) { var t = percentage; - var x = Math.pow(1 - t, 2) * this.from.x + (2 * t * (1 - t)) * via.x + Math.pow(t, 2) * this.to.x; - var y = Math.pow(1 - t, 2) * this.from.y + (2 * t * (1 - t)) * via.y + Math.pow(t, 2) * this.to.y; + var x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x; + var y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y; return {x: x, y: y}; } diff --git a/lib/vis/lib/network/modules/components/edges/CubicBezierEdge.js b/lib/vis/lib/network/modules/components/edges/CubicBezierEdge.js index b5a3b3b305..e7b7669523 100644 --- a/lib/vis/lib/network/modules/components/edges/CubicBezierEdge.js +++ b/lib/vis/lib/network/modules/components/edges/CubicBezierEdge.js @@ -10,28 +10,26 @@ class CubicBezierEdge extends CubicBezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx) { + _line(ctx, viaNodes) { // get the coordinates of the support points. - let [via1,via2] = this._getViaCoordinates(); - let returnValue = [via1,via2]; + let via1 = viaNodes[0]; + let via2 = viaNodes[1]; // start drawing the line. ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); + ctx.moveTo(this.fromPoint.x, this.fromPoint.y); // fallback to normal straight edges - if (via1.x === undefined) { - ctx.lineTo(this.to.x, this.to.y); - returnValue = undefined; + if (viaNodes === undefined || via1.x === undefined) { + ctx.lineTo(this.toPoint.x, this.toPoint.y); } else { - ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.to.x, this.to.y); + ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.toPoint.x, this.toPoint.y); } // draw shadow if enabled this.enableShadow(ctx); ctx.stroke(); this.disableShadow(ctx); - return returnValue; } _getViaCoordinates() { @@ -39,7 +37,7 @@ class CubicBezierEdge extends CubicBezierEdgeBase { let dy = this.from.y - this.to.y; let x1, y1, x2, y2; - let roundness = this.options.smooth.roundness;; + let roundness = this.options.smooth.roundness; // horizontal if x > y or if direction is forced or if direction is horizontal if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') { @@ -58,6 +56,10 @@ class CubicBezierEdge extends CubicBezierEdgeBase { return [{x: x1, y: y1},{x: x2, y: y2}]; } + getViaNode() { + return this._getViaCoordinates(); + } + _findBorderPosition(nearNode, ctx) { return this._findBorderPositionBezier(nearNode, ctx); } @@ -80,8 +82,8 @@ class CubicBezierEdge extends CubicBezierEdgeBase { vec[1] = 3 * t * Math.pow(1 - t, 2); vec[2] = 3 * Math.pow(t,2) * (1 - t); vec[3] = Math.pow(t, 3); - let x = vec[0] * this.from.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.to.x; - let y = vec[0] * this.from.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.to.y; + let x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x; + let y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y; return {x: x, y: y}; } diff --git a/lib/vis/lib/network/modules/components/edges/StraightEdge.js b/lib/vis/lib/network/modules/components/edges/StraightEdge.js index 0fc08a0fa9..6d7bf231dc 100644 --- a/lib/vis/lib/network/modules/components/edges/StraightEdge.js +++ b/lib/vis/lib/network/modules/components/edges/StraightEdge.js @@ -13,12 +13,15 @@ class StraightEdge extends EdgeBase { _line(ctx) { // draw a straight line ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); + ctx.moveTo(this.fromPoint.x, this.fromPoint.y); + ctx.lineTo(this.toPoint.x, this.toPoint.y); // draw shadow if enabled this.enableShadow(ctx); ctx.stroke(); this.disableShadow(ctx); + } + + getViaNode() { return undefined; } @@ -31,8 +34,8 @@ class StraightEdge extends EdgeBase { */ getPoint(percentage) { return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y + x: (1 - percentage) * this.fromPoint.x + percentage * this.toPoint.x, + y: (1 - percentage) * this.fromPoint.y + percentage * this.toPoint.y } } diff --git a/lib/vis/lib/network/modules/components/edges/util/EdgeBase.js b/lib/vis/lib/network/modules/components/edges/util/EdgeBase.js index 86412f9418..659313a1c6 100644 --- a/lib/vis/lib/network/modules/components/edges/util/EdgeBase.js +++ b/lib/vis/lib/network/modules/components/edges/util/EdgeBase.js @@ -4,11 +4,14 @@ class EdgeBase { constructor(options, body, labelModule) { this.body = body; this.labelModule = labelModule; + this.options = {}; this.setOptions(options); this.colorDirty = true; this.color = {}; this.selectionWidth = 2; this.hoverWidth = 1.5; + this.fromPoint = this.from; + this.toPoint = this.to; } connect() { @@ -31,36 +34,32 @@ class EdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - drawLine(ctx, selected, hover) { + drawLine(ctx, selected, hover, viaNode) { // set style ctx.strokeStyle = this.getColor(ctx, selected, hover); ctx.lineWidth = this.getLineWidth(selected, hover); - let via = undefined; + if (this.options.dashes !== false) { - via = this._drawDashedLine(ctx); + this._drawDashedLine(ctx, viaNode); } else { - via = this._drawLine(ctx); + this._drawLine(ctx, viaNode); } - return via; } - _drawLine(ctx) { - let via = undefined; + _drawLine(ctx, viaNode, fromPoint, toPoint) { if (this.from != this.to) { // draw line - via = this._line(ctx); + this._line(ctx, viaNode, fromPoint, toPoint); } else { let [x,y,radius] = this._getCircleData(ctx); this._circle(ctx, x, y, radius); } - return via; } - _drawDashedLine(ctx) { - let via = undefined; + _drawDashedLine(ctx, viaNode, fromPoint, toPoint) { ctx.lineCap = 'round'; let pattern = [5,5]; if (Array.isArray(this.options.dashes) === true) { @@ -78,7 +77,7 @@ class EdgeBase { // draw the line if (this.from != this.to) { // draw line - via = this._line(ctx); + this._line(ctx, viaNode); } else { let [x,y,radius] = this._getCircleData(ctx); @@ -107,7 +106,6 @@ class EdgeBase { // disable shadows for other elements. this.disableShadow(ctx); } - return via; } @@ -399,26 +397,22 @@ class EdgeBase { return Math.sqrt(dx * dx + dy * dy); } + /** * * @param ctx * @param position * @param viaNode */ - drawArrowHead(ctx, position, viaNode, selected, hover) { - // set style - ctx.strokeStyle = this.getColor(ctx, selected, hover); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this.getLineWidth(selected, hover); - + getArrowData(ctx, position, viaNode, selected, hover) { // set lets let angle; - let length; - let arrowPos; + let arrowPoint; let node1; let node2; let guideOffset; let scaleFactor; + let lineWidth = this.getLineWidth(selected, hover); if (position === 'from') { node1 = this.from; @@ -443,67 +437,74 @@ class EdgeBase { if (position !== 'middle') { // draw arrow head if (this.options.smooth.enabled === true) { - arrowPos = this.findBorderPosition(node1, ctx, {via: viaNode}); - let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPos.t + guideOffset)), viaNode); - angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); + arrowPoint = this.findBorderPosition(node1, ctx, {via: viaNode}); + let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode); + angle = Math.atan2((arrowPoint.y - guidePos.y), (arrowPoint.x - guidePos.x)); } else { angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); - arrowPos = this.findBorderPosition(node1, ctx); + arrowPoint = this.findBorderPosition(node1, ctx); } } else { angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); - arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow. + arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow. } - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * scaleFactor; - ctx.arrow(arrowPos.x, arrowPos.y, angle, length); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - ctx.stroke(); } else { // draw circle - let angle, point; let [x,y,radius] = this._getCircleData(ctx); if (position === 'from') { - point = this.findBorderPosition(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1}); - angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; + arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1}); + angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; } else if (position === 'to') { - point = this.findBorderPosition(this.from, ctx, {x, y, low:0.6, high:1.0, direction:1}); - angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; + arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.6, high:1.0, direction:1}); + angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; } else { - point = this._pointOnCircle(x, y, radius, 0.175); + arrowPoint = this._pointOnCircle(x, y, radius, 0.175); angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; } - - // draw the arrowhead - let length = (10 + 5 * this.options.width) * scaleFactor; - ctx.arrow(point.x, point.y, angle, length); - - // draw shadow if enabled - this.enableShadow(ctx); - ctx.fill(); - - // disable shadows for other elements. - this.disableShadow(ctx); - ctx.stroke(); } + + let length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge. + + var xi = arrowPoint.x - length * 0.9 * Math.cos(angle); + var yi = arrowPoint.y - length * 0.9 * Math.sin(angle); + let arrowCore = {x: xi, y: yi}; + + return {point: arrowPoint, core: arrowCore, angle: angle, length: length}; + } + + /** + * + * @param ctx + * @param selected + * @param hover + * @param arrowData + */ + drawArrowHead(ctx, selected, hover, arrowData) { + // set style + ctx.strokeStyle = this.getColor(ctx, selected, hover); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this.getLineWidth(selected, hover); + + // draw arrow at the end of the line + ctx.arrow(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length); + + // draw shadow if enabled + this.enableShadow(ctx); + ctx.fill(); + // disable shadows for other elements. + this.disableShadow(ctx); } enableShadow(ctx) { if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0.5)'; + ctx.shadowColor = this.options.shadow.color; ctx.shadowBlur = this.options.shadow.size; ctx.shadowOffsetX = this.options.shadow.x; ctx.shadowOffsetY = this.options.shadow.y; diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Box.js b/lib/vis/lib/network/modules/components/nodes/shapes/Box.js index 9e9b78f524..d2d157408f 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Box.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Box.js @@ -44,34 +44,39 @@ class Box extends NodeBase { //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); - this.enableBorderDashes(ctx); - //draw the border - ctx.stroke(); - //disable dashed border for other elements - this.disableBorderDashes(ctx); + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } ctx.restore(); - this.updateBoundingBox(x,y); + this.updateBoundingBox(x,y,ctx,selected); this.labelModule.draw(ctx, x, y, selected); } - updateBoundingBox(x,y) { + updateBoundingBox(x,y, ctx, selected) { + this.resize(ctx, selected); this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; - this.boundingBox.left = this.left; - this.boundingBox.top = this.top; - this.boundingBox.bottom = this.top + this.height; - this.boundingBox.right = this.left + this.width; + let borderRadius = this.options.shapeProperties.borderRadius; // only effective for box + this.boundingBox.left = this.left - borderRadius; + this.boundingBox.top = this.top - borderRadius; + this.boundingBox.bottom = this.top + this.height + borderRadius; + this.boundingBox.right = this.left + this.width + borderRadius; } distanceToBorder(ctx, angle) { this.resize(ctx); - let a = this.width / 2; - let b = this.height / 2; - let w = (Math.sin(angle) * a); - let h = (Math.cos(angle) * b); - return a * b / Math.sqrt(w * w + h * h); + let borderWidth = this.options.borderWidth; + + return Math.min( + Math.abs((this.width) / 2 / Math.cos(angle)), + Math.abs((this.height) / 2 / Math.sin(angle))) + borderWidth; } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Circle.js b/lib/vis/lib/network/modules/components/nodes/shapes/Circle.js index 3bfb828f5a..221e92df3a 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Circle.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Circle.js @@ -45,11 +45,7 @@ class Circle extends CircleImageBase { distanceToBorder(ctx, angle) { this.resize(ctx); - 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); + return this.width * 0.5; } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/CircularImage.js b/lib/vis/lib/network/modules/components/nodes/shapes/CircularImage.js index 7b4121c0fa..7d04b3f56f 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/CircularImage.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/CircularImage.js @@ -38,10 +38,10 @@ class CircularImage extends CircleImageBase { let size = Math.min(0.5*this.height, 0.5*this.width); - // draw the backgroun circle. IMPORTANT: the stroke in this method is used by the clip method below. + // draw the background circle. IMPORTANT: the stroke in this method is used by the clip method below. this._drawRawCircle(ctx, x, y, selected, hover, size); - // now we draw in the cicle, we save so we can revert the clip operation after drawing. + // now we draw in the circle, we save so we can revert the clip operation after drawing. ctx.save(); // clip is used to use the stroke in drawRawCircle as an area that we can draw in. ctx.clip(); @@ -68,7 +68,7 @@ class CircularImage extends CircleImageBase { distanceToBorder(ctx, angle) { this.resize(ctx); - return this._distanceToBorder(angle); + return this.width * 0.5; } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Database.js b/lib/vis/lib/network/modules/components/nodes/shapes/Database.js index 8ab35d5c42..dabc02fff0 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Database.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Database.js @@ -23,13 +23,12 @@ class Database extends NodeBase { this.left = x - this.width / 2; this.top = y - this.height / 2; - var borderWidth = this.options.borderWidth; + var neutralborderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + ctx.lineWidth = Math.min(this.width, borderWidth); ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); @@ -43,11 +42,14 @@ class Database extends NodeBase { //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); - this.enableBorderDashes(ctx); - //draw the border - ctx.stroke(); - //disable dashed border for other elements - this.disableBorderDashes(ctx); + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } ctx.restore(); this.updateBoundingBox(x,y,ctx,selected); @@ -67,12 +69,7 @@ class Database extends NodeBase { } distanceToBorder(ctx, angle) { - this.resize(ctx); - 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); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Diamond.js b/lib/vis/lib/network/modules/components/nodes/shapes/Diamond.js index 15d348bf40..9a72caaa00 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Diamond.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Diamond.js @@ -16,7 +16,7 @@ class Diamond extends ShapeBase { } distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Dot.js b/lib/vis/lib/network/modules/components/nodes/shapes/Dot.js index 28fb920cb7..6840913931 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Dot.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Dot.js @@ -16,7 +16,8 @@ class Dot extends ShapeBase { } distanceToBorder(ctx, angle) { - return this.options.size + this.options.borderWidth; + this.resize(ctx); + return this.options.size; } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Ellipse.js b/lib/vis/lib/network/modules/components/nodes/shapes/Ellipse.js index 0265b1fe7e..b4d0a28a6b 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Ellipse.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Ellipse.js @@ -25,15 +25,13 @@ class Ellipse extends NodeBase { this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; - var borderWidth = this.options.borderWidth; + var neutralborderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + ctx.lineWidth = Math.min(this.width, borderWidth); ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; ctx.ellipse(this.left, this.top, this.width, this.height); @@ -46,11 +44,16 @@ class Ellipse extends NodeBase { //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); - this.enableBorderDashes(ctx); - //draw the border - ctx.stroke(); - //disable dashed border for other elements - this.disableBorderDashes(ctx); + + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } + ctx.restore(); this.updateBoundingBox(x, y, ctx, selected); diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Icon.js b/lib/vis/lib/network/modules/components/nodes/shapes/Icon.js index 183b07725b..c01df0527a 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Icon.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Icon.js @@ -75,8 +75,7 @@ class Icon extends NodeBase { } distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Image.js b/lib/vis/lib/network/modules/components/nodes/shapes/Image.js index 78aef4b4eb..1a0e8aecf6 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Image.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Image.js @@ -17,6 +17,42 @@ class Image extends CircleImageBase { this.left = x - this.width / 2; this.top = y - this.height / 2; + if (this.options.shapeProperties.useBorderWithImage === true) { + var neutralborderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + ctx.lineWidth = Math.min(this.width, borderWidth); + + ctx.beginPath(); + + // setup the line properties. + ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + + // set a fillstyle + ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + + // draw a rectangle to form the border around. This rectangle is filled so the opacity of a picture (in future vis releases?) can be used to tint the image + ctx.rect(this.left - 0.5 * ctx.lineWidth, + this.top - 0.5 * ctx.lineWidth, + this.width + ctx.lineWidth, + this.height + ctx.lineWidth); + ctx.fill(); + + //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. + ctx.save(); + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } + ctx.restore(); + + ctx.closePath(); + } + this._drawImageAtPosition(ctx); this._drawImageLabel(ctx, x, y, selected || hover); @@ -42,12 +78,7 @@ class Image extends CircleImageBase { } distanceToBorder(ctx, angle) { - this.resize(ctx); - 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); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Square.js b/lib/vis/lib/network/modules/components/nodes/shapes/Square.js index 80024615d5..e2fc6ebba6 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Square.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Square.js @@ -16,8 +16,7 @@ class Square extends ShapeBase { } distanceToBorder(ctx, angle) { - this.resize(); - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Star.js b/lib/vis/lib/network/modules/components/nodes/shapes/Star.js index df23b45873..4aae4fae1f 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Star.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Star.js @@ -16,7 +16,7 @@ class Star extends ShapeBase { } distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Text.js b/lib/vis/lib/network/modules/components/nodes/shapes/Text.js index 4f0fb81a9f..ac22dc5ce4 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Text.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Text.js @@ -45,8 +45,7 @@ class Text extends NodeBase { } distanceToBorder(ctx, angle) { - this.resize(ctx); - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/Triangle.js b/lib/vis/lib/network/modules/components/nodes/shapes/Triangle.js index c8aac1d7fd..6c58a4f122 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/Triangle.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/Triangle.js @@ -16,7 +16,7 @@ class Triangle extends ShapeBase { } distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/shapes/TriangleDown.js b/lib/vis/lib/network/modules/components/nodes/shapes/TriangleDown.js index 6b64f5956f..044fd7c519 100644 --- a/lib/vis/lib/network/modules/components/nodes/shapes/TriangleDown.js +++ b/lib/vis/lib/network/modules/components/nodes/shapes/TriangleDown.js @@ -16,7 +16,7 @@ class TriangleDown extends ShapeBase { } distanceToBorder(ctx, angle) { - return this._distanceToBorder(angle); + return this._distanceToBorder(ctx,angle); } } diff --git a/lib/vis/lib/network/modules/components/nodes/util/CircleImageBase.js b/lib/vis/lib/network/modules/components/nodes/util/CircleImageBase.js index f262b68c83..ab0cf97f3b 100644 --- a/lib/vis/lib/network/modules/components/nodes/util/CircleImageBase.js +++ b/lib/vis/lib/network/modules/components/nodes/util/CircleImageBase.js @@ -7,6 +7,13 @@ class CircleImageBase extends NodeBase { this.imageLoaded = false; } + setOptions(options, imageObj) { + this.options = options; + if (imageObj) { + this.imageObj = imageObj; + } + } + /** * This function resizes the image by the options size when the image has not yet loaded. If the image has loaded, we * force the update of the size again. @@ -59,14 +66,12 @@ class CircleImageBase extends NodeBase { } _drawRawCircle(ctx, x, y, selected, hover, size) { - var borderWidth = this.options.borderWidth; + var neutralborderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + ctx.lineWidth = Math.min(this.width, borderWidth); ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - - ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; ctx.circle(x, y, size); @@ -79,11 +84,14 @@ class CircleImageBase extends NodeBase { //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); - this.enableBorderDashes(ctx); - //draw the border - ctx.stroke(); - //disable dashed border for other elements - this.disableBorderDashes(ctx); + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } ctx.restore(); } diff --git a/lib/vis/lib/network/modules/components/nodes/util/NodeBase.js b/lib/vis/lib/network/modules/components/nodes/util/NodeBase.js index 8bde8dec02..1cf6bf5336 100644 --- a/lib/vis/lib/network/modules/components/nodes/util/NodeBase.js +++ b/lib/vis/lib/network/modules/components/nodes/util/NodeBase.js @@ -15,8 +15,9 @@ class NodeBase { this.options = options; } - _distanceToBorder(angle) { - var borderWidth = 1; + _distanceToBorder(ctx,angle) { + var borderWidth = this.options.borderWidth; + this.resize(ctx); return Math.min( Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; @@ -24,7 +25,7 @@ class NodeBase { enableShadow(ctx) { if (this.options.shadow.enabled === true) { - ctx.shadowColor = 'rgba(0,0,0,0.5)'; + ctx.shadowColor = this.options.shadow.color; ctx.shadowBlur = this.options.shadow.size; ctx.shadowOffsetX = this.options.shadow.x; ctx.shadowOffsetY = this.options.shadow.y; @@ -67,9 +68,6 @@ class NodeBase { } } } - - // possible cleanup for use in shapes - cleanup() {} } export default NodeBase; \ No newline at end of file diff --git a/lib/vis/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/vis/lib/network/modules/components/nodes/util/ShapeBase.js index 62901f0c51..d9e2cce24d 100644 --- a/lib/vis/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/vis/lib/network/modules/components/nodes/util/ShapeBase.js @@ -20,13 +20,12 @@ class ShapeBase extends NodeBase { this.left = x - this.width / 2; this.top = y - this.height / 2; - var borderWidth = this.options.borderWidth; + var neutralborderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + ctx.lineWidth = Math.min(this.width, borderWidth); ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); - ctx.lineWidth /= this.body.view.scale; - ctx.lineWidth = Math.min(this.width, ctx.lineWidth); ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; ctx[shape](x, y, this.options.size); @@ -39,11 +38,14 @@ class ShapeBase extends NodeBase { //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); - this.enableBorderDashes(ctx); - //draw the border - ctx.stroke(); - //disable dashed border for other elements - this.disableBorderDashes(ctx); + // if borders are zero width, they will be drawn with width 1 by default. This prevents that + if (borderWidth > 0) { + this.enableBorderDashes(ctx); + //draw the border + ctx.stroke(); + //disable dashed border for other elements + this.disableBorderDashes(ctx); + } ctx.restore(); if (this.options.label !== undefined) { diff --git a/lib/vis/lib/network/modules/components/physics/BarnesHutSolver.js b/lib/vis/lib/network/modules/components/physics/BarnesHutSolver.js index b2da2d60fe..2c514e2075 100644 --- a/lib/vis/lib/network/modules/components/physics/BarnesHutSolver.js +++ b/lib/vis/lib/network/modules/components/physics/BarnesHutSolver.js @@ -6,6 +6,9 @@ class BarnesHutSolver { this.barnesHutTree; this.setOptions(options); this.randomSeed = 5; + + // debug: show grid + //this.body.emitter.on("afterDrawing", (ctx) => {this._debug(ctx,'#ff0000')}) } setOptions(options) { @@ -21,7 +24,7 @@ class BarnesHutSolver { /** - * This function calculates the forces the nodes apply on eachother based on a gravitational model. + * This function calculates the forces the nodes apply on each other based on a gravitational model. * The Barnes Hut method is used to speed up this N-body simulation. * * @private @@ -285,7 +288,7 @@ class BarnesHutSolver { break; case 1: // convert into children // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) - // we move one node a pixel and we do not put it in the tree. + // we move one node a little bit and we do not put it in the tree. if (parentBranch.children[region].children.data.x === node.x && parentBranch.children[region].children.data.y === node.y) { node.x += this.seededRandom(); diff --git a/lib/vis/lib/network/modules/components/shared/Label.js b/lib/vis/lib/network/modules/components/shared/Label.js index 0141bc3634..64c0c3748d 100644 --- a/lib/vis/lib/network/modules/components/shared/Label.js +++ b/lib/vis/lib/network/modules/components/shared/Label.js @@ -6,21 +6,26 @@ class Label { this.pointToSelf = false; this.baseSize = undefined; + this.fontOptions = {}; this.setOptions(options); this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached } setOptions(options, allowDeletion = false) { - this.options = options; + this.nodeOptions = options; + + // We want to keep the font options seperated from the node options. + // The node options have to mirror the globals when they are not overruled. + this.fontOptions = util.deepExtend({},options.font, true); if (options.label !== undefined) { this.labelDirty = true; } if (options.font !== undefined) { - Label.parseOptions(this.options.font, options, allowDeletion); + Label.parseOptions(this.fontOptions, options, allowDeletion); if (typeof options.font === 'string') { - this.baseSize = this.options.font.size; + this.baseSize = this.fontOptions.size; } else if (typeof options.font === 'object') { if (options.font.size !== undefined) { @@ -54,12 +59,12 @@ class Label { */ draw(ctx, x, y, selected, baseline = 'middle') { // if no label, return - if (this.options.label === undefined) + if (this.nodeOptions.label === undefined) return; // check if we have to render the label - let viewFontSize = this.options.font.size * this.body.view.scale; - if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) + let viewFontSize = this.fontOptions.size * this.body.view.scale; + if (this.nodeOptions.label && viewFontSize < this.nodeOptions.scaling.label.drawThreshold - 1) return; // update the size cache if required @@ -77,12 +82,12 @@ class Label { * @private */ _drawBackground(ctx) { - if (this.options.font.background !== undefined && this.options.font.background !== "none") { - ctx.fillStyle = this.options.font.background; + if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { + ctx.fillStyle = this.fontOptions.background; let lineMargin = 2; - switch (this.options.font.align) { + switch (this.fontOptions.align) { case 'middle': ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); break; @@ -108,11 +113,11 @@ class Label { * @private */ _drawText(ctx, selected, x, y, baseline = 'middle') { - let fontSize = this.options.font.size; + let fontSize = this.fontOptions.size; let viewFontSize = fontSize * this.body.view.scale; // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) - if (viewFontSize >= this.options.scaling.label.maxVisible) { - fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; + if (viewFontSize >= this.nodeOptions.scaling.label.maxVisible) { + fontSize = Number(this.nodeOptions.scaling.label.maxVisible) / this.body.view.scale; } let yLine = this.size.yLine; @@ -120,20 +125,20 @@ class Label { [x, yLine] = this._setAlignment(ctx, x, yLine, baseline); // configure context for drawing the text - ctx.font = (selected && this.options.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.options.font.face; + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.fontOptions.face; ctx.fillStyle = fontColor; ctx.textAlign = 'center'; // set the strokeWidth - if (this.options.font.strokeWidth > 0) { - ctx.lineWidth = this.options.font.strokeWidth; + if (this.fontOptions.strokeWidth > 0) { + ctx.lineWidth = this.fontOptions.strokeWidth; ctx.strokeStyle = strokeColor; ctx.lineJoin = 'round'; } // draw the text for (let i = 0; i < this.lineCount; i++) { - if (this.options.font.strokeWidth > 0) { + if (this.fontOptions.strokeWidth > 0) { ctx.strokeText(this.lines[i], x, yLine); } ctx.fillText(this.lines[i], x, yLine); @@ -144,16 +149,16 @@ class Label { _setAlignment(ctx, x, yLine, baseline) { // check for label alignment (for edges) // TODO: make alignment for nodes - if (this.options.font.align !== 'horizontal' && this.pointToSelf === false) { + if (this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) { x = 0; yLine = 0; let lineMargin = 2; - if (this.options.font.align === 'top') { + if (this.fontOptions.align === 'top') { ctx.textBaseline = 'alphabetic'; yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers } - else if (this.options.font.align === 'bottom') { + else if (this.fontOptions.align === 'bottom') { ctx.textBaseline = 'hanging'; yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers } @@ -177,10 +182,10 @@ class Label { * @private */ _getColor(viewFontSize) { - let fontColor = this.options.font.color || '#000000'; - let strokeColor = this.options.font.strokeColor || '#ffffff'; - if (viewFontSize <= this.options.scaling.label.drawThreshold) { - let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); + let fontColor = this.fontOptions.color || '#000000'; + let strokeColor = this.fontOptions.strokeColor || '#ffffff'; + if (viewFontSize <= this.nodeOptions.scaling.label.drawThreshold) { + let opacity = Math.max(0, Math.min(1, 1 - (this.nodeOptions.scaling.label.drawThreshold - viewFontSize))); fontColor = util.overrideOpacity(fontColor, opacity); strokeColor = util.overrideOpacity(strokeColor, opacity); } @@ -197,7 +202,7 @@ class Label { getTextSize(ctx, selected = false) { let size = { width: this._processLabel(ctx,selected), - height: this.options.font.size * this.lineCount, + height: this.fontOptions.size * this.lineCount, lineCount: this.lineCount }; return size; @@ -216,12 +221,12 @@ class Label { if (this.labelDirty === true) { this.size.width = this._processLabel(ctx,selected); } - this.size.height = this.options.font.size * this.lineCount; + this.size.height = this.fontOptions.size * this.lineCount; this.size.left = x - this.size.width * 0.5; this.size.top = y - this.size.height * 0.5; - this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; + this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; if (baseline === "hanging") { - this.size.top += 0.5 * this.options.font.size; + this.size.top += 0.5 * this.fontOptions.size; this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers this.size.yLine += 4; // distance from node } @@ -241,10 +246,10 @@ class Label { let width = 0; let lines = ['']; let lineCount = 0; - if (this.options.label !== undefined) { - lines = String(this.options.label).split('\n'); + if (this.nodeOptions.label !== undefined) { + lines = String(this.nodeOptions.label).split('\n'); lineCount = lines.length; - ctx.font = (selected && this.options.labelHighlightBold ? 'bold ' : '') + this.options.font.size + "px " + this.options.font.face; + ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; width = ctx.measureText(lines[0]).width; for (let i = 1; i < lineCount; i++) { let lineWidth = ctx.measureText(lines[i]).width; diff --git a/lib/vis/lib/network/options.js b/lib/vis/lib/network/options.js index 7f4de30dd6..599041630e 100644 --- a/lib/vis/lib/network/options.js +++ b/lib/vis/lib/network/options.js @@ -29,6 +29,7 @@ let allOptions = { from: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } }, __type__: { string: ['from', 'to', 'middle'], object } }, + arrowStrikethrough: { boolean }, color: { color: { string }, highlight: { string }, @@ -72,6 +73,7 @@ let allOptions = { selfReferenceSize: { number }, shadow: { enabled: { boolean }, + color: { string }, size: { number }, x: { number }, y: { number }, @@ -117,9 +119,14 @@ let allOptions = { }, layout: { randomSeed: { 'undefined': 'undefined', number }, + improvedLayout: { boolean }, hierarchical: { enabled: { boolean }, levelSeparation: { number }, + nodeSpacing: { number }, + treeSpacing: { number }, + blockShifting: { boolean }, + edgeMinimization: { boolean }, direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed __type__: { object, boolean } @@ -203,6 +210,7 @@ let allOptions = { }, shadow: { enabled: { boolean }, + color: { string }, size: { number }, x: { number }, y: { number }, @@ -213,6 +221,7 @@ let allOptions = { borderDashes: { boolean, array }, borderRadius: { number }, useImageSize: { boolean }, + useBorderWithImage: { boolean }, __type__: { object } }, size: { number }, @@ -270,6 +279,7 @@ let allOptions = { __type__: { object, boolean } }, timestep: { number }, + adaptiveTimestep: { boolean }, __type__: { object, boolean } }, @@ -342,6 +352,7 @@ let configureOptions = { }, shadow: { enabled: false, + color: 'rgba(0,0,0,0.5)', size: [10, 0, 20, 1], x: [5, -30, 30, 1], y: [5, -30, 30, 1] @@ -360,6 +371,7 @@ let configureOptions = { middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }, from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] } }, + arrowStrikethrough: true, color: { color: ['color', '#848484'], highlight: ['color', '#848484'], @@ -396,6 +408,7 @@ let configureOptions = { selfReferenceSize: [20, 0, 200, 1], shadow: { enabled: false, + color: 'rgba(0,0,0,0.5)', size: [10, 0, 20, 1], x: [5, -30, 30, 1], y: [5, -30, 30, 1] @@ -410,9 +423,14 @@ let configureOptions = { }, layout: { //randomSeed: [0, 0, 500, 1], + //improvedLayout: true, hierarchical: { enabled: false, levelSeparation: [150, 20, 500, 5], + nodeSpacing: [100, 20, 500, 5], + treeSpacing: [200, 20, 500, 5], + blockShifting: true, + edgeMinimization: true, direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL sortMethod: ['hubsize', 'directed'] // hubsize, directed } @@ -477,7 +495,8 @@ let configureOptions = { maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], - timestep: [0.5, 0.01, 1, 0.01] + timestep: [0.5, 0.01, 1, 0.01], + //adaptiveTimestep: true }, global: { locale: ['en', 'nl'] diff --git a/lib/vis/lib/shared/ColorPicker.js b/lib/vis/lib/shared/ColorPicker.js index a0d81b1221..adbfe10beb 100644 --- a/lib/vis/lib/shared/ColorPicker.js +++ b/lib/vis/lib/shared/ColorPicker.js @@ -16,6 +16,7 @@ class ColorPicker { // bound by this.updateCallback = () => {}; + this.closeCallback = () => {}; // create all DOM elements this._create(); @@ -42,12 +43,25 @@ class ColorPicker { * the callback is executed on apply and save. Bind it to the application * @param callback */ - setCallback(callback) { + setUpdateCallback(callback) { if (typeof callback === 'function') { this.updateCallback = callback; } else { - throw new Error("Function attempted to set as colorPicker callback is not a function."); + throw new Error("Function attempted to set as colorPicker update callback is not a function."); + } + } + + /** + * the callback is executed on apply and save. Bind it to the application + * @param callback + */ + setCloseCallback(callback) { + if (typeof callback === 'function') { + this.closeCallback = callback; + } + else { + throw new Error("Function attempted to set as colorPicker closing callback is not a function."); } } @@ -119,19 +133,20 @@ class ColorPicker { /** - * this shows the color picker at a location. The hue circle is constructed once and stored. - * @param x - * @param y + * this shows the color picker. + * The hue circle is constructed once and stored. */ - show(x,y) { + show() { + if (this.closeCallback !== undefined) { + this.closeCallback(); + this.closeCallback = undefined; + } + this.applied = false; this.frame.style.display = 'block'; - this.frame.style.top = y + 'px'; - this.frame.style.left = x + 'px'; this._generateHueCircle(); } - // ------------------------------------------ PRIVATE ----------------------------- // /** @@ -151,6 +166,15 @@ class ColorPicker { } this.frame.style.display = 'none'; + + // call the closing callback, restoring the onclick method. + // this is in a setTimeout because it will trigger the show again before the click is done. + setTimeout(() => { + if (this.closeCallback !== undefined) { + this.closeCallback(); + this.closeCallback = undefined; + } + },0); } @@ -244,7 +268,7 @@ class ColorPicker { /** - * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. + * update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing. * @param rgba * @private */ diff --git a/lib/vis/lib/shared/Configurator.js b/lib/vis/lib/shared/Configurator.js index e1d7f2bbaf..e564bc2d50 100644 --- a/lib/vis/lib/shared/Configurator.js +++ b/lib/vis/lib/shared/Configurator.js @@ -24,6 +24,8 @@ class Configurator { this.allowCreation = false; this.options = {}; + this.initialized = false; + this.popupCounter = 0; this.defaultOptions = { enabled: false, filter: true, @@ -35,6 +37,9 @@ class Configurator { this.configureOptions = configureOptions; this.moduleOptions = {}; this.domElements = []; + this.popupDiv = {}; + this.popupLimit = 5; + this.popupHistory = {}; this.colorPicker = new ColorPicker(pixelRatio); this.wrapper = undefined; } @@ -48,6 +53,10 @@ class Configurator { */ setOptions(options) { if (options !== undefined) { + // reset the popup history because the indices may have been changed. + this.popupHistory = {}; + this._removePopup(); + let enabled = true; if (typeof options === 'string') { this.options.filter = options; @@ -131,7 +140,7 @@ class Configurator { // a header for the category this._makeHeader(option); - // get the suboptions + // get the sub options this._handleObject(this.configureOptions[option], [option]); } counter++; @@ -140,21 +149,21 @@ class Configurator { if (this.options.showButton === true) { let generateButton = document.createElement('div'); - generateButton.className = 'vis-network-configuration button'; + generateButton.className = 'vis-configuration vis-config-button'; generateButton.innerHTML = 'generate options'; generateButton.onclick = () => {this._printOptions();}; - generateButton.onmouseover = () => {generateButton.className = 'vis-network-configuration button hover';}; - generateButton.onmouseout = () => {generateButton.className = 'vis-network-configuration button';}; + generateButton.onmouseover = () => {generateButton.className = 'vis-configuration vis-config-button hover';}; + generateButton.onmouseout = () => {generateButton.className = 'vis-configuration vis-config-button';}; this.optionsContainer = document.createElement('div'); - this.optionsContainer.className = 'vis-network-configuration vis-option-container'; + this.optionsContainer.className = 'vis-configuration vis-config-option-container'; this.domElements.push(this.optionsContainer); this.domElements.push(generateButton); } this._push(); - this.colorPicker.insertTo(this.container); + //~ this.colorPicker.insertTo(this.container); } @@ -164,11 +173,13 @@ class Configurator { */ _push() { this.wrapper = document.createElement('div'); - this.wrapper.className = 'vis-network-configuration-wrapper'; + this.wrapper.className = 'vis-configuration-wrapper'; this.container.appendChild(this.wrapper); for (var i = 0; i < this.domElements.length; i++) { this.wrapper.appendChild(this.domElements[i]); } + + this._showPopupIfNeeded() } @@ -186,6 +197,8 @@ class Configurator { this.wrapper = undefined; } this.domElements = []; + + this._removePopup(); } @@ -219,12 +232,14 @@ class Configurator { _makeItem(path, ...domElements) { if (this.allowCreation === true) { let item = document.createElement('div'); - item.className = 'vis-network-configuration item s' + path.length; + item.className = 'vis-configuration vis-config-item vis-config-s' + path.length; domElements.forEach((element) => { item.appendChild(element); }); this.domElements.push(item); + return this.domElements.length; } + return 0; } @@ -235,7 +250,7 @@ class Configurator { */ _makeHeader(name) { let div = document.createElement('div'); - div.className = 'vis-network-configuration header'; + div.className = 'vis-configuration vis-config-header'; div.innerHTML = name; this._makeItem([],div); } @@ -251,7 +266,7 @@ class Configurator { */ _makeLabel(name, path, objectLabel = false) { let div = document.createElement('div'); - div.className = 'vis-network-configuration label s' + path.length; + div.className = 'vis-configuration vis-config-label vis-config-s' + path.length; if (objectLabel === true) { div.innerHTML = '' + name + ':'; } @@ -271,7 +286,7 @@ class Configurator { */ _makeDropdown(arr, value, path) { let select = document.createElement('select'); - select.className = 'vis-network-configuration select'; + select.className = 'vis-configuration vis-config-select'; let selectedValue = 0; if (value !== undefined) { if (arr.indexOf(value) !== -1) { @@ -310,7 +325,7 @@ class Configurator { let max = arr[2]; let step = arr[3]; let range = document.createElement('input'); - range.className = 'vis-network-configuration range'; + range.className = 'vis-configuration vis-config-range'; try { range.type = 'range'; // not supported on IE9 range.min = min; @@ -319,15 +334,26 @@ class Configurator { catch (err) {} range.step = step; + // set up the popup settings in case they are needed. + let popupString = ''; + let popupValue = 0; + if (value !== undefined) { - if (value < 0 && value * 2 < min) { - range.min = value*2; + let factor = 1.20; + if (value < 0 && value * factor < min) { + range.min = Math.ceil(value * factor); + popupValue = range.min; + popupString = 'range increased'; } - else if (value * 0.1 < min) { - range.min = value / 10; + else if (value / factor < min) { + range.min = Math.ceil(value / factor); + popupValue = range.min; + popupString = 'range increased'; } - if (value * 2 > max && max !== 1) { - range.max = value * 2; + if (value * factor > max && max !== 1) { + range.max = Math.ceil(value * factor); + popupValue = range.max; + popupString = 'range increased'; } range.value = value; } @@ -336,7 +362,7 @@ class Configurator { } let input = document.createElement('input'); - input.className = 'vis-network-configuration rangeinput'; + input.className = 'vis-configuration vis-config-rangeinput'; input.value = range.value; var me = this; @@ -344,10 +370,70 @@ class Configurator { range.oninput = function () {input.value = this.value; }; let label = this._makeLabel(path[path.length-1], path); - this._makeItem(path, label, range, input); + let itemIndex = this._makeItem(path, label, range, input); + + // if a popup is needed AND it has not been shown for this value, show it. + if (popupString !== '' && this.popupHistory[itemIndex] !== popupValue) { + this.popupHistory[itemIndex] = popupValue; + this._setupPopup(popupString, itemIndex); + } + } + /** + * prepare the popup + * @param string + * @param index + * @private + */ + _setupPopup(string, index) { + if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) { + let div = document.createElement("div"); + div.id = "vis-configuration-popup"; + div.className = "vis-configuration-popup"; + div.innerHTML = string; + div.onclick = () => {this._removePopup()}; + this.popupCounter += 1; + this.popupDiv = {html:div, index:index}; + } + } + + + /** + * remove the popup from the dom + * @private + */ + _removePopup() { + if (this.popupDiv.html !== undefined) { + this.popupDiv.html.parentNode.removeChild(this.popupDiv.html); + clearTimeout(this.popupDiv.hideTimeout); + clearTimeout(this.popupDiv.deleteTimeout); + this.popupDiv = {}; + } + } + + + /** + * Show the popup if it is needed. + * @private + */ + _showPopupIfNeeded() { + if (this.popupDiv.html !== undefined) { + let correspondingElement = this.domElements[this.popupDiv.index]; + let rect = correspondingElement.getBoundingClientRect(); + this.popupDiv.html.style.left = rect.left + "px"; + this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height; + document.body.appendChild(this.popupDiv.html) + this.popupDiv.hideTimeout = setTimeout(() => { + this.popupDiv.html.style.opacity = 0; + },1500); + this.popupDiv.deleteTimeout = setTimeout(() => { + this._removePopup(); + },1800) + } + } + /** * make a checkbox for boolean options. * @param defaultValue @@ -358,7 +444,7 @@ class Configurator { _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.className = 'vis-network-configuration checkbox'; + checkbox.className = 'vis-configuration vis-config-checkbox'; checkbox.checked = defaultValue; if (value !== undefined) { checkbox.checked = value; @@ -391,7 +477,7 @@ class Configurator { _makeTextInput(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'text'; - checkbox.className = 'vis-network-configuration text'; + checkbox.className = 'vis-configuration vis-config-text'; checkbox.value = value; if (value !== defaultValue) { this.changedOptions.push({path:path, value:value}); @@ -418,11 +504,11 @@ class Configurator { value = value === undefined ? defaultColor : value; if (value !== 'none') { - div.className = 'vis-network-configuration colorBlock'; + div.className = 'vis-configuration vis-config-colorBlock'; div.style.backgroundColor = value; } else { - div.className = 'vis-network-configuration colorBlock none'; + div.className = 'vis-configuration vis-config-colorBlock none'; } value = value === undefined ? defaultColor : value; @@ -444,17 +530,25 @@ class Configurator { * @private */ _showColorPicker(value, div, path) { - let rect = div.getBoundingClientRect(); - let bodyRect = document.body.getBoundingClientRect(); - let pickerX = rect.left + rect.width + 5; - let pickerY = rect.top - bodyRect.top + rect.height*0.5; - this.colorPicker.show(pickerX,pickerY); + // clear the callback from this div + div.onclick = function() {}; + + this.colorPicker.insertTo(div); + this.colorPicker.show(); + this.colorPicker.setColor(value); - this.colorPicker.setCallback((color) => { + this.colorPicker.setUpdateCallback((color) => { let colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')'; div.style.backgroundColor = colorString; this._update(colorString,path); - }) + }); + + // on close of the colorpicker, restore the callback. + this.colorPicker.setCloseCallback(() => { + div.onclick = () => { + this._showColorPicker(value,div,path); + }; + }); } @@ -576,7 +670,7 @@ class Configurator { if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) { this.parent.body.emitter.emit("configChange", options); } - + this.initialized = true; this.parent.setOptions(options); } diff --git a/lib/vis/lib/network/css/network-configuration.css b/lib/vis/lib/shared/configuration.css similarity index 69% rename from lib/vis/lib/network/css/network-configuration.css rename to lib/vis/lib/shared/configuration.css index 4d7daecaf3..1913c98851 100644 --- a/lib/vis/lib/network/css/network-configuration.css +++ b/lib/vis/lib/shared/configuration.css @@ -1,17 +1,22 @@ -div.vis-network-configuration { +div.vis-configuration { position:relative; display:block; float:left; font-size:12px; } -div.vis-network-configuration-wrapper { +div.vis-configuration-wrapper { display:block; width:700px; } +div.vis-configuration-wrapper::after { + clear: both; + content: ""; + display: block; +} -div.vis-network-configuration.vis-option-container{ +div.vis-configuration.vis-config-option-container{ display:block; width:495px; background-color: #ffffff; @@ -22,7 +27,7 @@ div.vis-network-configuration.vis-option-container{ padding-left:5px; } -div.vis-network-configuration.button{ +div.vis-configuration.vis-config-button{ display:block; width:495px; height:25px; @@ -38,13 +43,13 @@ div.vis-network-configuration.button{ margin-bottom:30px; } -div.vis-network-configuration.button.hover{ +div.vis-configuration.vis-config-button.hover{ background-color: #4588e6; border:2px solid #214373; color:#ffffff; } -div.vis-network-configuration.item{ +div.vis-configuration.vis-config-item{ display:block; float:left; width:495px; @@ -54,44 +59,44 @@ div.vis-network-configuration.item{ } -div.vis-network-configuration.item.s2{ +div.vis-configuration.vis-config-item.vis-config-s2{ left:10px; background-color: #f7f8fa; padding-left:5px; border-radius:3px; } -div.vis-network-configuration.item.s3{ +div.vis-configuration.vis-config-item.vis-config-s3{ left:20px; background-color: #e4e9f0; padding-left:5px; border-radius:3px; } -div.vis-network-configuration.item.s4{ +div.vis-configuration.vis-config-item.vis-config-s4{ left:30px; background-color: #cfd8e6; padding-left:5px; border-radius:3px; } -div.vis-network-configuration.header{ +div.vis-configuration.vis-config-header{ font-size:18px; font-weight: bold; } -div.vis-network-configuration.label{ +div.vis-configuration.vis-config-label{ width:120px; height:25px; line-height: 25px; } -div.vis-network-configuration.label.s3{ +div.vis-configuration.vis-config-label.vis-config-s3{ width:110px; } -div.vis-network-configuration.label.s4{ +div.vis-configuration.vis-config-label.vis-config-s4{ width:100px; } -div.vis-network-configuration.colorBlock{ +div.vis-configuration.vis-config-colorBlock{ top:1px; width:30px; height:19px; @@ -102,22 +107,22 @@ div.vis-network-configuration.colorBlock{ cursor:pointer; } -input.vis-network-configuration.checkbox { +input.vis-configuration.vis-config-checkbox { left:-5px; } -input.vis-network-configuration.rangeinput{ +input.vis-configuration.vis-config-rangeinput{ position:relative; top:-5px; width:60px; - height:13px; + /*height:13px;*/ padding:1px; margin:0; pointer-events:none; } -input.vis-network-configuration.range{ +input.vis-configuration.vis-config-range{ /*removes default webkit styles*/ -webkit-appearance: none; @@ -129,7 +134,7 @@ input.vis-network-configuration.range{ width: 300px; height:20px; } -input.vis-network-configuration.range::-webkit-slider-runnable-track { +input.vis-configuration.vis-config-range::-webkit-slider-runnable-track { width: 300px; height: 5px; background: #dedede; /* Old browsers */ @@ -145,7 +150,7 @@ input.vis-network-configuration.range::-webkit-slider-runnable-track { box-shadow: #aaaaaa 0px 0px 3px 0px; border-radius: 3px; } -input.vis-network-configuration.range::-webkit-slider-thumb { +input.vis-configuration.vis-config-range::-webkit-slider-thumb { -webkit-appearance: none; border: 1px solid #14334b; height: 17px; @@ -162,10 +167,10 @@ input.vis-network-configuration.range::-webkit-slider-thumb { box-shadow: #111927 0px 0px 1px 0px; margin-top: -7px; } -input.vis-network-configuration.range:focus { +input.vis-configuration.vis-config-range:focus { outline: none; } -input.vis-network-configuration.range:focus::-webkit-slider-runnable-track { +input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track { background: #9d9d9d; /* Old browsers */ background: -moz-linear-gradient(top, #9d9d9d 0%, #c8c8c8 99%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9d9d9d), color-stop(99%,#c8c8c8)); /* Chrome,Safari4+ */ @@ -176,7 +181,7 @@ input.vis-network-configuration.range:focus::-webkit-slider-runnable-track { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8',GradientType=0 ); /* IE6-9 */ } -input.vis-network-configuration.range::-moz-range-track { +input.vis-configuration.vis-config-range::-moz-range-track { width: 300px; height: 10px; background: #dedede; /* Old browsers */ @@ -192,7 +197,7 @@ input.vis-network-configuration.range::-moz-range-track { box-shadow: #aaaaaa 0px 0px 3px 0px; border-radius: 3px; } -input.vis-network-configuration.range::-moz-range-thumb { +input.vis-configuration.vis-config-range::-moz-range-thumb { border: none; height: 16px; width: 16px; @@ -202,12 +207,12 @@ input.vis-network-configuration.range::-moz-range-thumb { } /*hide the outline behind the border*/ -input.vis-network-configuration.range:-moz-focusring{ +input.vis-configuration.vis-config-range:-moz-focusring{ outline: 1px solid white; outline-offset: -1px; } -input.vis-network-configuration.range::-ms-track { +input.vis-configuration.vis-config-range::-ms-track { width: 300px; height: 5px; @@ -221,24 +226,63 @@ input.vis-network-configuration.range::-ms-track { /*remove default tick marks*/ color: transparent; } -input.vis-network-configuration.range::-ms-fill-lower { +input.vis-configuration.vis-config-range::-ms-fill-lower { background: #777; border-radius: 10px; } -input.vis-network-configuration.range::-ms-fill-upper { +input.vis-configuration.vis-config-range::-ms-fill-upper { background: #ddd; border-radius: 10px; } -input.vis-network-configuration.range::-ms-thumb { +input.vis-configuration.vis-config-range::-ms-thumb { border: none; height: 16px; width: 16px; border-radius: 50%; background: #385380; } -input.vis-network-configuration.range:focus::-ms-fill-lower { +input.vis-configuration.vis-config-range:focus::-ms-fill-lower { background: #888; } -input.vis-network-configuration.range:focus::-ms-fill-upper { +input.vis-configuration.vis-config-range:focus::-ms-fill-upper { background: #ccc; +} + +.vis-configuration-popup { + position: absolute; + background: rgba(57, 76, 89, 0.85); + border: 2px solid #f2faff; + line-height:30px; + height:30px; + width:150px; + text-align:center; + color: #ffffff; + font-size:14px; + border-radius:4px; + -webkit-transition: opacity 0.3s ease-in-out; + -moz-transition: opacity 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; +} +.vis-configuration-popup:after, .vis-configuration-popup:before { + left: 100%; + top: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +.vis-configuration-popup:after { + border-color: rgba(136, 183, 213, 0); + border-left-color: rgba(57, 76, 89, 0.85); + border-width: 8px; + margin-top: -8px; +} +.vis-configuration-popup:before { + border-color: rgba(194, 225, 245, 0); + border-left-color: #f2faff; + border-width: 12px; + margin-top: -12px; } \ No newline at end of file diff --git a/lib/vis/lib/timeline/Core.js b/lib/vis/lib/timeline/Core.js index d0cffbbbdc..86350197ac 100644 --- a/lib/vis/lib/timeline/Core.js +++ b/lib/vis/lib/timeline/Core.js @@ -90,12 +90,16 @@ Core.prototype._create = function (container) { this.dom.rightContainer.appendChild(this.dom.shadowTopRight); this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); - this.on('rangechange', this.redraw.bind(this)); + this.on('rangechange', function () { + if (this.initialDrawDone === true) { + this._redraw(); // this allows overriding the _redraw method + } + }.bind(this)); this.on('touch', this._onTouch.bind(this)); this.on('pan', this._onDrag.bind(this)); var me = this; - this.on('change', function (properties) { + this.on('_change', function (properties) { if (properties && properties.queue == true) { // redraw once on next tick if (!me._redrawTimer) { @@ -179,6 +183,7 @@ Core.prototype._create = function (container) { this.touch = {}; this.redrawCount = 0; + this.initialDrawDone = false; // attach the root panel to the provided container if (!container) throw new Error('No container provided'); @@ -216,10 +221,12 @@ Core.prototype.setOptions = function (options) { var fields = [ 'width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', - 'locale', 'locales', 'moment' + 'locale', 'locales', 'moment', + 'throttleRedraw' ]; util.selectiveExtend(fields, this.options, options); + this.options.orientation = {item:undefined,axis:undefined}; if ('orientation' in options) { if (typeof options.orientation === 'string') { this.options.orientation = { @@ -265,7 +272,7 @@ Core.prototype.setOptions = function (options) { onRender: options.drawPoints }; } - + if ('hiddenDates' in this.options) { DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates); } @@ -311,8 +318,15 @@ Core.prototype.setOptions = function (options) { this.configurator.setModuleOptions({global: appliedOptions}); } - // redraw everything - this._redraw(); + // override redraw with a throttled version + if (!this._origRedraw) { + this._origRedraw = this._redraw.bind(this); + this._redraw = util.throttle(this._origRedraw, this.options.throttleRedraw); + } else { + // Not the initial run: redraw everything + this._redraw(); + } + }; /** @@ -400,6 +414,24 @@ Core.prototype.getCustomTime = function(id) { return customTimes[0].getCustomTime(); }; +/** + * Set a custom title for the custom time bar. + * @param {String} [title] Custom title + * @param {number} [id=undefined] Id of the custom time bar. + */ +Core.prototype.setCustomTimeTitle = function(title, id) { + var customTimes = this.customTimes.filter(function (component) { + return component.options.id === id; + }); + + if (customTimes.length === 0) { + throw new Error('No custom time bar found with id ' + JSON.stringify(id)) + } + if (customTimes.length > 0) { + return customTimes[0].setCustomTitle(title); + } +}; + /** * Retrieve meta information from an event. * Should be overridden by classes extending Core @@ -439,7 +471,7 @@ Core.prototype.addCustomTime = function (time, id) { this.customTimes.push(customTime); this.components.push(customTime); - this.redraw(); + this._redraw(); return id; }; @@ -581,6 +613,8 @@ Core.prototype.getWindow = function() { /** * Force a redraw. Can be overridden by implementations of Core + * + * Note: this function will be overridden on construction with a trottled version */ Core.prototype.redraw = function() { this._redraw(); @@ -592,12 +626,13 @@ Core.prototype.redraw = function() { * @protected */ Core.prototype._redraw = function() { + this.redrawCount++; var resized = false; var options = this.options; var props = this.props; var dom = this.dom; - if (!dom) return; // when destroyed + if (!dom|| !dom.container || dom.container.clientWidth == 0 ) return;// when destroyed, or invisible DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); @@ -737,18 +772,22 @@ Core.prototype._redraw = function() { this.components.forEach(function (component) { resized = component.redraw() || resized; }); + var MAX_REDRAW = 5; if (resized) { - // keep repainting until all sizes are settled - var MAX_REDRAWS = 3; // maximum number of consecutive redraws - if (this.redrawCount < MAX_REDRAWS) { - this.redrawCount++; - this._redraw(); + if (this.redrawCount < MAX_REDRAW) { + this.body.emitter.emit('_change'); + return; } else { console.log('WARNING: infinite loop in redraw?'); } + } else { this.redrawCount = 0; } + this.initialDrawDone = true; + + //Emit public 'changed' event for UI updates, see issue #1592 + this.body.emitter.emit("changed"); }; // TODO: deprecated since version 1.1.0, remove some day @@ -878,7 +917,7 @@ Core.prototype._startAutoResize = function () { me.props.lastWidth = me.dom.root.offsetWidth; me.props.lastHeight = me.dom.root.offsetHeight; - me.emit('change'); + me.body.emitter.emit('_change'); } } }; @@ -886,6 +925,12 @@ Core.prototype._startAutoResize = function () { // add event listener to window resize util.addEventListener(window, 'resize', this._onResize); + //Prevent initial unnecessary redraw + if (me.dom.root) { + me.props.lastWidth = me.dom.root.offsetWidth; + me.props.lastHeight = me.dom.root.offsetHeight; + } + this.watchTimer = setInterval(this._onResize, 1000); }; @@ -900,8 +945,10 @@ Core.prototype._stopAutoResize = function () { } // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; + if (this._onResize) { + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; + } }; /** @@ -940,7 +987,6 @@ Core.prototype._onDrag = function (event) { if (newScrollTop != oldScrollTop) { - this._redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already this.emit("verticalDrag"); } }; diff --git a/lib/vis/lib/timeline/DataStep.js b/lib/vis/lib/timeline/DataStep.js deleted file mode 100644 index 9bc67fecf4..0000000000 --- a/lib/vis/lib/timeline/DataStep.js +++ /dev/null @@ -1,233 +0,0 @@ -/** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ -function DataStep(start, end, minimumStep, containerHeight, customRange, formattingFunction, alignZeros) { - // variables - this.current = 0; - - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; - this.formattingFunction = formattingFunction; - - this.marginStart; - this.marginEnd; - this.deadSpace = 0; - - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; - - this.alignZeros = alignZeros; - - this.setRange(start, end, minimumStep, containerHeight, customRange); -} - - - -/** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ -DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; - if (this._start === this._end) { - this._start = customRange.min === undefined ? this._start - 0.75 : this._start; - this._end = customRange.max === undefined ? this._end + 1 : this._end;; - } - - if (this.autoScale === true) { - this.setMinimumStep(minimumStep, containerHeight); - } - - this.setFirst(customRange); -}; - -/** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in pixels - */ -DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var range = this._end - this._start; - var safeRange = range * 1.2; - var minimumStepValue = minimumStep * (safeRange / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeRange)/Math.LN10); - - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); - - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } - - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; - } - } - if (solutionFound === true) { - break; - } - } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; -}; - - - -/** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ -DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } - - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros === true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; - } - - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; - - this.current = this.marginEnd; -}; - -DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); - } - else { - return rounded; - } -} - - -/** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ -DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); -}; - -/** - * Do the next step - */ -DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; - - // safety mechanism: if current time is still unchanged, move to the end - if (this.current === prev) { - this.current = this._end; - } -}; - -/** - * Do the next step - */ -DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; -}; - - - -/** - * Get the current datetime - * @return {String} current The current date - */ -DataStep.prototype.getCurrent = function() { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var returnValue = current.toPrecision(5); - if (typeof this.formattingFunction === 'function') { - returnValue = this.formattingFunction(current); - } - - if (typeof returnValue === 'number') { - return '' + returnValue; - } - else if (typeof returnValue === 'string') { - return returnValue; - } - else { - return current.toPrecision(5); - } - -}; - -/** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. - */ -DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) === 0); -}; - - -DataStep.prototype.shift = function(steps) { - if (steps < 0) { - for (let i = 0; i < -steps; i++) { - this.previous(); - } - } - else if (steps > 0) { - for (let i = 0; i < steps; i++) { - this.next(); - } - } -} - -module.exports = DataStep; diff --git a/lib/vis/lib/timeline/DateUtil.js b/lib/vis/lib/timeline/DateUtil.js index 0f93e3e8e7..1a8a90761d 100644 --- a/lib/vis/lib/timeline/DateUtil.js +++ b/lib/vis/lib/timeline/DateUtil.js @@ -4,9 +4,13 @@ * * @param {function} moment * @param {Object} body - * @param {Array} hiddenDates + * @param {Array | Object} hiddenDates */ exports.convertHiddenOptions = function(moment, body, hiddenDates) { + if (hiddenDates && !Array.isArray(hiddenDates)) { + return exports.convertHiddenOptions(moment, body, [hiddenDates]) + } + body.hiddenDates = []; if (hiddenDates) { if (Array.isArray(hiddenDates) == true) { @@ -30,9 +34,13 @@ exports.convertHiddenOptions = function(moment, body, hiddenDates) { * create new entrees for the repeating hidden dates * @param {function} moment * @param {Object} body - * @param {Array} hiddenDates + * @param {Array | Object} hiddenDates */ exports.updateHiddenDates = function (moment, body, hiddenDates) { + if (hiddenDates && !Array.isArray(hiddenDates)) { + return exports.updateHiddenDates(moment, body, [hiddenDates]) + } + if (hiddenDates && body.domProps.centerContainer.width !== undefined) { exports.convertHiddenOptions(moment, body, hiddenDates); @@ -135,7 +143,7 @@ exports.updateHiddenDates = function (moment, body, hiddenDates) { case "weekly": startDate.add(1, 'weeks'); endDate.add(1, 'weeks'); - break + break; case "monthly": startDate.add(1, 'months'); endDate.add(1, 'months'); diff --git a/lib/vis/lib/timeline/Graph2d.js b/lib/vis/lib/timeline/Graph2d.js index c4354df26e..a5498eb5e5 100644 --- a/lib/vis/lib/timeline/Graph2d.js +++ b/lib/vis/lib/timeline/Graph2d.js @@ -27,7 +27,7 @@ var configureOptions = require('./optionsGraph2d').configureOptions; */ function Graph2d (container, items, groups, options) { // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) { var forthArgument = options; options = groups; groups = forthArgument; @@ -93,11 +93,13 @@ function Graph2d (container, items, groups, options) { // item set this.linegraph = new LineGraph(this.body); + this.components.push(this.linegraph); this.itemsData = null; // DataSet this.groupsData = null; // DataSet + this.on('tap', function (event) { me.emit('click', me.getEventProperties(event)) }); @@ -122,9 +124,9 @@ function Graph2d (container, items, groups, options) { if (items) { this.setItems(items); } - else { - this._redraw(); - } + + // draw for the first time + this._redraw(); } // Extend the functionality from Core @@ -173,7 +175,6 @@ Graph2d.prototype.setItems = function(items) { if (this.options.start != undefined || this.options.end != undefined) { var start = this.options.start != undefined ? this.options.start : null; var end = this.options.end != undefined ? this.options.end : null; - this.setWindow(start, end, {animation: false}); } else { @@ -217,7 +218,7 @@ Graph2d.prototype.getLegend = function(groupId, width, height) { return this.linegraph.groups[groupId].getLegend(width,height); } else { - return "cannot find group:" + groupId; + return "cannot find group:'" + groupId + "'"; } }; diff --git a/lib/vis/lib/timeline/Range.js b/lib/vis/lib/timeline/Range.js index 3d02930c99..97e690e2ff 100644 --- a/lib/vis/lib/timeline/Range.js +++ b/lib/vis/lib/timeline/Range.js @@ -429,10 +429,14 @@ Range.prototype._onDrag = function (event) { this.previousDelta = delta; this._applyRange(newStart, newEnd); + + var startDate = new Date(this.start); + var endDate = new Date(this.end); + // fire a rangechange event this.body.emitter.emit('rangechange', { - start: new Date(this.start), - end: new Date(this.end), + start: startDate, + end: endDate, byUser: true }); }; @@ -551,7 +555,7 @@ Range.prototype._onPinch = function (event) { var scale = 1 / (event.scale + this.scaleOffset); var centerDate = this._pointerToDate(this.props.touch.center); - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.options.moment, this.body.hiddenDates, this.start, this.end); + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate); var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; diff --git a/lib/vis/lib/timeline/TimeStep.js b/lib/vis/lib/timeline/TimeStep.js index df6301a375..e7cb1888f8 100644 --- a/lib/vis/lib/timeline/TimeStep.js +++ b/lib/vis/lib/timeline/TimeStep.js @@ -47,8 +47,13 @@ function TimeStep(start, end, minimumStep, hiddenDates) { this.switchedDay = false; this.switchedMonth = false; this.switchedYear = false; - this.hiddenDates = hiddenDates; - if (hiddenDates === undefined) { + if (Array.isArray(hiddenDates)) { + this.hiddenDates = hiddenDates; + } + else if (hiddenDates != undefined) { + this.hiddenDates = [hiddenDates]; + } + else { this.hiddenDates = []; } @@ -353,7 +358,7 @@ TimeStep.snap = function(date, scale, step) { clone.hours(0); clone.minutes(0); clone.seconds(0); - clone.mlliseconds(0); + clone.milliseconds(0); } else if (scale == 'month') { if (clone.date() > 15) { diff --git a/lib/vis/lib/timeline/Timeline.js b/lib/vis/lib/timeline/Timeline.js index 0b34be5718..e2bfd55515 100644 --- a/lib/vis/lib/timeline/Timeline.js +++ b/lib/vis/lib/timeline/Timeline.js @@ -44,6 +44,7 @@ function Timeline (container, items, groups, options) { end: null, autoResize: true, + throttleRedraw: 0, // ms orientation: { axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' @@ -120,6 +121,28 @@ function Timeline (container, items, groups, options) { me.emit('contextmenu', me.getEventProperties(event)) }; + //Single time autoscale/fit + this.fitDone = false; + this.on('changed', function (){ + if (this.itemsData == null) return; + if (!me.fitDone) { + me.fitDone = true; + if (me.options.start != undefined || me.options.end != undefined) { + if (me.options.start == undefined || me.options.end == undefined) { + var range = me.getItemRange(); + } + + var start = me.options.start != undefined ? me.options.start : range.min; + var end = me.options.end != undefined ? me.options.end : range.max; + + me.setWindow(start, end, {animation: false}); + } + else { + me.fit({animation: false}); + } + } + }); + // apply options if (options) { this.setOptions(options); @@ -134,9 +157,9 @@ function Timeline (container, items, groups, options) { if (items) { this.setItems(items); } - else { - this._redraw(); - } + + // draw for the first time + this._redraw(); } // Extend the functionality from Core @@ -155,6 +178,8 @@ Timeline.prototype._createConfigurator = function () { * Force a redraw. The size of all items will be recalculated. * Can be useful to manually redraw when option autoResize=false and the window * has been resized, or when the items CSS has been changed. + * + * Note: this function will be overridden on construction with a trottled version */ Timeline.prototype.redraw = function() { this.itemSet && this.itemSet.markDirty({refreshItems: true}); @@ -191,8 +216,6 @@ Timeline.prototype.setOptions = function (options) { * @param {vis.DataSet | Array | null} items */ Timeline.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); - // convert to type DataSet when needed var newDataSet; if (!items) { @@ -214,22 +237,6 @@ Timeline.prototype.setItems = function(items) { // set items this.itemsData = newDataSet; this.itemSet && this.itemSet.setItems(newDataSet); - - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - if (this.options.start == undefined || this.options.end == undefined) { - var range = this.getItemRange(); - } - - var start = this.options.start != undefined ? this.options.start : range.min; - var end = this.options.end != undefined ? this.options.end : range.max; - - this.setWindow(start, end, {animation: false}); - } - else { - this.fit({animation: false}); - } - } }; /** @@ -376,8 +383,8 @@ Timeline.prototype.fit = function (options) { Timeline.prototype.getItemRange = function () { // get a rough approximation for the range based on the items start and end dates var range = this.getDataRange(); - var min = range.min; - var max = range.max; + var min = range.min !== null ? range.min.valueOf() : null; + var max = range.max !== null ? range.max.valueOf() : null; var minItem = null; var maxItem = null; @@ -400,12 +407,13 @@ Timeline.prototype.getItemRange = function () { // calculate the date of the left side and right side of the items given util.forEach(this.itemSet.items, function (item) { item.show(); + item.repositionX(); var start = getStart(item); var end = getEnd(item); - var left = new Date(start - (item.getWidthLeft() + 10) * factor); - var right = new Date(end + (item.getWidthRight() + 10) * factor); + var left = start - (item.getWidthLeft() + 10) * factor; + var right = end + (item.getWidthRight() + 10) * factor; if (left < min) { min = left; @@ -452,7 +460,7 @@ Timeline.prototype.getDataRange = function() { min = start; } if (max === null || end > max) { - max = start; + max = end; } }); } diff --git a/lib/vis/lib/timeline/component/CurrentTime.js b/lib/vis/lib/timeline/component/CurrentTime.js index c9479c520b..d23ba377df 100644 --- a/lib/vis/lib/timeline/component/CurrentTime.js +++ b/lib/vis/lib/timeline/component/CurrentTime.js @@ -91,7 +91,7 @@ CurrentTime.prototype.redraw = function() { var locale = this.options.locales[this.options.locale]; if (!locale) { if (!this.warned) { - console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline.html#Localization'); + console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization'); this.warned = true; } locale = this.options.locales['en']; // fall back on english when not available diff --git a/lib/vis/lib/timeline/component/CustomTime.js b/lib/vis/lib/timeline/component/CustomTime.js index f71ac686a3..72ee0000ed 100644 --- a/lib/vis/lib/timeline/component/CustomTime.js +++ b/lib/vis/lib/timeline/component/CustomTime.js @@ -23,16 +23,17 @@ function CustomTime (body, options) { moment: moment, locales: locales, locale: 'en', - id: undefined + id: undefined, + title: undefined }; this.options = util.extend({}, this.defaultOptions); if (options && options.time) { this.customTime = options.time; } else { - this.customTime = new Date(); + this.customTime = new Date(); } - + this.eventParams = {}; // stores state parameters while dragging the bar this.setOptions(options); @@ -123,8 +124,12 @@ CustomTime.prototype.redraw = function () { locale = this.options.locales['en']; // fall back on english when not available } - var title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); + var title = this.options.title; + // To hide the title completely use empty string ''. + if (title === undefined) { + title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); + } this.bar.style.left = x + 'px'; this.bar.title = title; @@ -159,6 +164,14 @@ CustomTime.prototype.getCustomTime = function() { return new Date(this.customTime.valueOf()); }; +/** + * Set custom title. + * @param {Date | number | string} title + */ +CustomTime.prototype.setCustomTitle = function(title) { + this.options.title = title; +}; + /** * Start moving horizontally * @param {Event} event diff --git a/lib/vis/lib/timeline/component/DataAxis.js b/lib/vis/lib/timeline/component/DataAxis.js index 8e67320769..0f3ec598bc 100644 --- a/lib/vis/lib/timeline/component/DataAxis.js +++ b/lib/vis/lib/timeline/component/DataAxis.js @@ -1,8 +1,7 @@ var util = require('../../util'); var DOMutil = require('../../DOMutil'); var Component = require('./Component'); -var DataStep = require('../DataStep'); - +var DataScale = require('./DataScale'); /** * A horizontal time axis * @param {Object} [options] See DataAxis.setOptions for the available @@ -19,7 +18,7 @@ function DataAxis (body, options, svg, linegraphOptions) { orientation: 'left', // supported: 'left', 'right' showMinorLabels: true, showMajorLabels: true, - icons: true, + icons: false, majorLinesOffset: 7, minorLinesOffset: 4, labelOffsetX: 10, @@ -30,12 +29,12 @@ function DataAxis (body, options, svg, linegraphOptions) { alignZeros: true, left:{ range: {min:undefined,max:undefined}, - format: function (value) {return value;}, + format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));}, title: {text:undefined,style:undefined} }, right:{ range: {min:undefined,max:undefined}, - format: function (value) {return value;}, + format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));}, title: {text:undefined,style:undefined} } }; @@ -50,7 +49,7 @@ function DataAxis (body, options, svg, linegraphOptions) { }; this.dom = {}; - + this.scale= undefined; this.range = {start:0, end:0}; this.options = util.extend({}, this.defaultOptions); @@ -59,7 +58,7 @@ function DataAxis (body, options, svg, linegraphOptions) { this.setOptions(options); this.width = Number(('' + this.options.width).replace("px","")); this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; + this.height = this.linegraphSVG.getBoundingClientRect().height; this.hidden = false; this.stepPixels = 25; @@ -68,15 +67,16 @@ function DataAxis (body, options, svg, linegraphOptions) { this.lineOffset = 0; this.master = true; + this.masterAxis = null; this.svgElements = {}; this.iconsRemoved = false; - this.groups = {}; this.amountOfGroups = 0; // create the HTML DOM this._create(); + this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; var me = this; this.body.emitter.on("verticalDrag", function() { @@ -95,6 +95,9 @@ DataAxis.prototype.addGroup = function(label, graphOptions) { }; DataAxis.prototype.updateGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.amountOfGroups += 1; + } this.groups[label] = graphOptions; }; @@ -128,10 +131,9 @@ DataAxis.prototype.setOptions = function (options) { 'right', 'alignZeros' ]; - util.selectiveExtend(fields, this.options, options); + util.selectiveDeepExtend(fields, this.options, options); this.minWidth = Number(('' + this.options.width).replace("px","")); - if (redraw === true && this.dom.frame) { this.hide(); this.show(); @@ -187,7 +189,7 @@ DataAxis.prototype._redrawGroupIcons = function () { for (var i = 0; i < groupArray.length; i++) { var groupId = groupArray[i]; if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y); y += iconHeight + iconOffset; } } @@ -244,11 +246,6 @@ DataAxis.prototype.hide = function() { * @param end */ DataAxis.prototype.setRange = function (start, end) { - if (this.master === false && this.options.alignZeros === true && this.zeroCrossing != -1) { - if (start > 0) { - start = 0; - } - } this.range.start = start; this.range.end = end; }; @@ -260,7 +257,7 @@ DataAxis.prototype.setRange = function (start, end) { DataAxis.prototype.redraw = function () { var resized = false; var activeGroups = 0; - + // Make sure the line container adheres to the vertical scrolling. this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; @@ -348,105 +345,60 @@ DataAxis.prototype._redrawLabels = function () { DOMutil.prepareElements(this.DOMelements.lines); DOMutil.prepareElements(this.DOMelements.labels); var orientation = this.options['orientation']; + var customRange = this.options[orientation].range != undefined? this.options[orientation].range:{}; - // get the range for the slaved axis - var step; - if (this.master === false) { - var stepSize, rangeStart, rangeEnd, minimumStep; - if (this.zeroCrossing !== -1 && this.options.alignZeros === true) { - if (this.range.end > 0) { - stepSize = this.range.end / this.zeroCrossing; // size of one step - rangeStart = this.range.end - this.amountOfSteps * stepSize; - rangeEnd = this.range.end; - } - else { - // all of the range (including start) has to be done before the zero crossing. - stepSize = -1 * this.range.start / (this.amountOfSteps - this.zeroCrossing); // absolute size of a step - rangeStart = this.range.start; - rangeEnd = this.range.start + stepSize * this.amountOfSteps; - } - } - else { - rangeStart = this.range.start; - rangeEnd = this.range.end; - } - minimumStep = this.stepPixels; + //Override range with manual options: + var autoScaleEnd = true; + if (customRange.max != undefined){ + this.range.end = customRange.max; + autoScaleEnd = false; } - else { - // calculate range and step (step such that we have space for 7 characters per label) - minimumStep = this.props.majorCharHeight; - rangeStart = this.range.start; - rangeEnd = this.range.end; + var autoScaleStart = true; + if (customRange.min != undefined){ + this.range.start = customRange.min; + autoScaleStart = false; } - this.step = step = new DataStep( - rangeStart, - rangeEnd, - minimumStep, + this.scale = new DataScale( + this.range.start, + this.range.end, + autoScaleStart, + autoScaleEnd, this.dom.frame.offsetHeight, - this.options[this.options.orientation].range, - this.options[this.options.orientation].format, - this.master === false && this.options.alignZeros // does the step have to align zeros? only if not master and the options is on + this.props.majorCharHeight, + this.options.alignZeros, + this.options[orientation].format ); - // the slave axis needs to use the same horizontal lines as the master axis. - if (this.master === true) { - this.stepPixels = ((this.dom.frame.offsetHeight) / step.marginRange) * step.step; - this.amountOfSteps = Math.ceil(this.dom.frame.offsetHeight / this.stepPixels); - } - else { - // align with zero - if (this.options.alignZeros === true && this.zeroCrossing !== -1) { - // distance is the amount of steps away from the zero crossing we are. - let distance = (step.current - this.zeroCrossing * step.step) / step.step; - this.step.shift(distance); - } + if (this.master === false && this.masterAxis != undefined){ + this.scale.followScale(this.masterAxis.scale); } - // value at the bottom of the SVG - this.valueAtBottom = step.marginEnd; - - + //Is updated in side-effect of _redrawLabel(): this.maxLabelSize = 0; - var y = 0; // init value - var stepIndex = 0; // init value - var isMajor = false; // init value - while (stepIndex < this.amountOfSteps) { - y = Math.round(stepIndex * this.stepPixels); - isMajor = step.isMajor(); - if (stepIndex > 0 && stepIndex !== this.amountOfSteps) { - if (this.options['showMinorLabels'] && isMajor === false || this.master === false && this.options['showMinorLabels'] === true) { - this._redrawLabel(y - 2, step.getCurrent(), orientation, 'vis-y-axis vis-minor', this.props.minorCharHeight); + var lines = this.scale.getLines(); + lines.forEach( + line=> { + var y = line.y; + var isMajor = line.major; + if (this.options['showMinorLabels'] && isMajor === false) { + this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-minor', this.props.minorCharHeight); } - - if (isMajor && this.options['showMajorLabels'] && this.master === true || - this.options['showMinorLabels'] === false && this.master === false && isMajor === true) { + if (isMajor) { if (y >= 0) { - this._redrawLabel(y - 2, step.getCurrent(), orientation, 'vis-y-axis vis-major', this.props.majorCharHeight); + this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-major', this.props.majorCharHeight); } - this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', this.options.majorLinesOffset, this.props.majorLineWidth); } - else { - this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', this.options.minorLinesOffset, this.props.minorLineWidth); + if (this.master === true) { + if (isMajor) { + this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', this.options.majorLinesOffset, this.props.majorLineWidth); + } + else { + this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', this.options.minorLinesOffset, this.props.minorLineWidth); + } } - } - - // get zero crossing - if (this.master === true && step.current === 0) { - this.zeroCrossing = stepIndex; - } - - step.next(); - stepIndex += 1; - } - - // get zero crossing if it's the last step - if (this.master === true && step.current === 0) { - this.zeroCrossing = stepIndex; - } - - this.conversionFactor = this.stepPixels / step.step; + }); // Note that title is rotated, so we're using the height, not width! var titleWidth = 0; @@ -483,13 +435,11 @@ DataAxis.prototype._redrawLabels = function () { }; DataAxis.prototype.convertValue = function (value) { - var invertedValue = this.valueAtBottom - value; - var convertedValue = invertedValue * this.conversionFactor; - return convertedValue; + return this.scale.convertValue(value); }; DataAxis.prototype.screenToValue = function (x) { - return this.valueAtBottom - (x / this.conversionFactor); + return this.scale.screenToValue(x); }; /** diff --git a/lib/vis/lib/timeline/component/DataScale.js b/lib/vis/lib/timeline/component/DataScale.js new file mode 100644 index 0000000000..5065f0e317 --- /dev/null +++ b/lib/vis/lib/timeline/component/DataScale.js @@ -0,0 +1,235 @@ +/** + * Created by ludo on 25-1-16. + */ + +function DataScale(start, end, autoScaleStart, autoScaleEnd, containerHeight, majorCharHeight, zeroAlign = false, formattingFunction=false) { + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; + this.customLines = null; + + this.containerHeight = containerHeight; + this.majorCharHeight = majorCharHeight; + this._start = start; + this._end = end; + + this.scale = 1; + this.minorStepIdx = -1; + this.magnitudefactor = 1; + this.determineScale(); + + this.zeroAlign = zeroAlign; + this.autoScaleStart = autoScaleStart; + this.autoScaleEnd = autoScaleEnd; + + this.formattingFunction = formattingFunction; + + if (autoScaleStart || autoScaleEnd) { + var me = this; + var roundToMinor = function (value) { + var rounded = value - (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx])); + if (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]) > 0.5 * (me.magnitudefactor * me.minorSteps[me.minorStepIdx])) { + return rounded + (me.magnitudefactor * me.minorSteps[me.minorStepIdx]); + } + else { + return rounded; + } + }; + if (autoScaleStart) { + this._start -= this.magnitudefactor * 2 * this.minorSteps[this.minorStepIdx]; + this._start = roundToMinor(this._start); + } + + if (autoScaleEnd) { + this._end += this.magnitudefactor * this.minorSteps[this.minorStepIdx]; + this._end = roundToMinor(this._end); + } + this.determineScale(); + } +} + +DataScale.prototype.setCharHeight = function (majorCharHeight) { + this.majorCharHeight = majorCharHeight; +}; + +DataScale.prototype.setHeight = function (containerHeight) { + this.containerHeight = containerHeight; +}; + +DataScale.prototype.determineScale = function () { + var range = this._end - this._start; + this.scale = this.containerHeight / range; + var minimumStepValue = this.majorCharHeight / this.scale; + var orderOfMagnitude = Math.round(Math.log(range) / Math.LN10); + + this.minorStepIdx = -1; + this.magnitudefactor = Math.pow(10, orderOfMagnitude); + + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; + } + + var solutionFound = false; + for (var l = start; Math.abs(l) <= Math.abs(orderOfMagnitude); l++) { + this.magnitudefactor = Math.pow(10, l); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = this.magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + this.minorStepIdx = j; + break; + } + } + if (solutionFound === true) { + break; + } + } +}; + +DataScale.prototype.is_major = function (value) { + return (value % (this.magnitudefactor * this.majorSteps[this.minorStepIdx]) === 0); +}; + +DataScale.prototype.getStep = function(){ + return this.magnitudefactor * this.minorSteps[this.minorStepIdx]; +}; + +DataScale.prototype.getFirstMajor = function(){ + var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx]; + return this.convertValue(this._start + ((majorStep - (this._start % majorStep)) % majorStep)); +}; + +DataScale.prototype.formatValue = function(current) { + var returnValue = current.toPrecision(5); + if (typeof this.formattingFunction === 'function') { + returnValue = this.formattingFunction(current); + } + + if (typeof returnValue === 'number') { + return '' + returnValue; + } + else if (typeof returnValue === 'string') { + return returnValue; + } + else { + return current.toPrecision(5); + } + +}; + +DataScale.prototype.getLines = function () { + var lines = []; + var step = this.getStep(); + var bottomOffset = (step - (this._start % step)) % step; + for (var i = (this._start + bottomOffset); this._end-i > 0.00001; i += step) { + if (i != this._start) { //Skip the bottom line + lines.push({major: this.is_major(i), y: this.convertValue(i), val: this.formatValue(i)}); + } + } + return lines; +}; + +DataScale.prototype.followScale = function (other) { + var oldStepIdx = this.minorStepIdx; + var oldStart = this._start; + var oldEnd = this._end; + + var me = this; + var increaseMagnitude = function () { + me.magnitudefactor *= 2; + }; + var decreaseMagnitude = function () { + me.magnitudefactor /= 2; + }; + + if ((other.minorStepIdx <= 1 && this.minorStepIdx <= 1) || (other.minorStepIdx > 1 && this.minorStepIdx > 1)) { + //easy, no need to change stepIdx nor multiplication factor + } else if (other.minorStepIdx < this.minorStepIdx) { + //I'm 5, they are 4 per major. + this.minorStepIdx = 1; + if (oldStepIdx == 2) { + increaseMagnitude(); + } else { + increaseMagnitude(); + increaseMagnitude(); + } + } else { + //I'm 4, they are 5 per major + this.minorStepIdx = 2; + if (oldStepIdx == 1) { + decreaseMagnitude(); + } else { + decreaseMagnitude(); + decreaseMagnitude(); + } + } + + //Get masters stats: + var lines = other.getLines(); + var otherZero = other.convertValue(0); + var otherStep = other.getStep() * other.scale; + + var done = false; + var count = 0; + //Loop until magnitude is correct for given constrains. + while (!done && count++ <5) { + + //Get my stats: + this.scale = otherStep / (this.minorSteps[this.minorStepIdx] * this.magnitudefactor); + var newRange = this.containerHeight / this.scale; + + //For the case the magnitudefactor has changed: + this._start = oldStart; + this._end = this._start + newRange; + + var myOriginalZero = this._end * this.scale; + var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx]; + var majorOffset = this.getFirstMajor() - other.getFirstMajor(); + + if (this.zeroAlign) { + var zeroOffset = otherZero - myOriginalZero; + this._end += (zeroOffset / this.scale); + this._start = this._end - newRange; + } else { + if (!this.autoScaleStart) { + this._start += majorStep - (majorOffset / this.scale); + this._end = this._start + newRange; + } else { + this._start -= majorOffset / this.scale; + this._end = this._start + newRange; + } + } + if (!this.autoScaleEnd && this._end > oldEnd+0.00001) { + //Need to decrease magnitude to prevent scale overshoot! (end) + decreaseMagnitude(); + done = false; + continue; + } + if (!this.autoScaleStart && this._start < oldStart-0.00001) { + if (this.zeroAlign && oldStart >= 0) { + console.warn("Can't adhere to given 'min' range, due to zeroalign"); + } else { + //Need to decrease magnitude to prevent scale overshoot! (start) + decreaseMagnitude(); + done = false; + continue; + } + } + if (this.autoScaleStart && this.autoScaleEnd && newRange < (oldEnd-oldStart)){ + increaseMagnitude(); + done = false; + continue; + } + done = true; + } +}; + +DataScale.prototype.convertValue = function (value) { + return this.containerHeight - ((value - this._start) * this.scale); +}; + +DataScale.prototype.screenToValue = function (pixels) { + return ((this.containerHeight - pixels) / this.scale) + this._start; +}; + +module.exports = DataScale; \ No newline at end of file diff --git a/lib/vis/lib/timeline/component/GraphGroup.js b/lib/vis/lib/timeline/component/GraphGroup.js index 0aac3316c3..20f52e9f06 100644 --- a/lib/vis/lib/timeline/component/GraphGroup.js +++ b/lib/vis/lib/timeline/component/GraphGroup.js @@ -1,7 +1,7 @@ var util = require('../../util'); var DOMutil = require('../../DOMutil'); -var Line = require('./graph2d_types/line'); -var Bar = require('./graph2d_types/bar'); +var Bars = require('./graph2d_types/bar'); +var Lines = require('./graph2d_types/line'); var Points = require('./graph2d_types/points'); /** @@ -14,10 +14,10 @@ var Points = require('./graph2d_types/points'); * It enumerates through the default styles * @constructor */ -function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { +function GraphGroup(group, groupId, options, groupsUsingDefaultStyles) { this.id = groupId; - var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','interpolation'] - this.options = util.selectiveBridgeObject(fields,options); + var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'drawPoints', 'shaded', 'interpolation', 'zIndex','excludeFromStacking', 'excludeFromLegend']; + this.options = util.selectiveBridgeObject(fields, options); this.usingDefaultStyle = group.className === undefined; this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; this.zeroPosition = 0; @@ -29,20 +29,17 @@ function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { this.visible = group.visible === undefined ? true : group.visible; } - /** * this loads a reference to all items in this group into this group. * @param {array} items */ -GraphGroup.prototype.setItems = function(items) { +GraphGroup.prototype.setItems = function (items) { if (items != null) { this.itemsData = items; if (this.options.sort == true) { - this.itemsData.sort(function (a,b) {return a.x - b.x;}) - } - // typecast all items to numbers. Takes around 10ms for 500.000 items - for (var i = 0; i < this.itemsData.length; i++) { - this.itemsData[i].y = Number(this.itemsData[i].y); + util.insertSort(this.itemsData,function (a, b) { + return a.x > b.x ? 1 : -1; + }); } } else { @@ -50,35 +47,37 @@ GraphGroup.prototype.setItems = function(items) { } }; +GraphGroup.prototype.getItems = function () { + return this.itemsData; +} /** - * this is used for plotting barcharts, this way, we only have to calculate it once. + * this is used for barcharts and shading, this way, we only have to calculate it once. * @param pos */ -GraphGroup.prototype.setZeroPosition = function(pos) { +GraphGroup.prototype.setZeroPosition = function (pos) { this.zeroPosition = pos; }; - /** * set the options of the graph group over the default options. * @param options */ -GraphGroup.prototype.setOptions = function(options) { +GraphGroup.prototype.setOptions = function (options) { if (options !== undefined) { - var fields = ['sampling','style','sort','yAxisOrientation','barChart']; + var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'zIndex','excludeFromStacking', 'excludeFromLegend']; util.selectiveDeepExtend(fields, this.options, options); // if the group's drawPoints is a function delegate the callback to the onRender property if (typeof options.drawPoints == 'function') { - options.drawPoints = { - onRender: options.drawPoints - } + options.drawPoints = { + onRender: options.drawPoints + } } - - util.mergeOptions(this.options, options,'interpolation'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); + + util.mergeOptions(this.options, options, 'interpolation'); + util.mergeOptions(this.options, options, 'drawPoints'); + util.mergeOptions(this.options, options, 'shaded'); if (options.interpolation) { if (typeof options.interpolation == 'object') { @@ -97,16 +96,6 @@ GraphGroup.prototype.setOptions = function(options) { } } } - - if (this.options.style == 'line') { - this.type = new Line(this.id, this.options); - } - else if (this.options.style == 'bar') { - this.type = new Bar(this.id, this.options); - } - else if (this.options.style == 'points') { - this.type = new Points(this.id, this.options); - } }; @@ -114,7 +103,7 @@ GraphGroup.prototype.setOptions = function(options) { * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph * @param group */ -GraphGroup.prototype.update = function(group) { +GraphGroup.prototype.update = function (group) { this.group = group; this.content = group.content || 'graph'; this.className = group.className || this.className || 'vis-graph-group' + this.groupsUsingDefaultStyles[0] % 10; @@ -123,73 +112,6 @@ GraphGroup.prototype.update = function(group) { this.setOptions(group.options); }; - -/** - * draw the icon for the legend. - * - * @param x - * @param y - * @param JSONcontainer - * @param SVGcontainer - * @param iconWidth - * @param iconHeight - */ -GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { - var fillHeight = iconHeight * 0.5; - var path, fillPath; - - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2*fillHeight); - outline.setAttributeNS(null, "class", "vis-outline"); - - if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); - if(this.style !== undefined) { - path.setAttributeNS(null, "style", this.style); - } - - path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); - if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + - "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); - } - else { - fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + - "L"+x+"," + (y + fillHeight) + " " + - "L"+ (x + iconWidth) + "," + (y + fillHeight) + - "L"+ (x + iconWidth) + ","+y); - } - fillPath.setAttributeNS(null, "class", this.className + " vis-icon-fill"); - } - - if (this.options.drawPoints.enabled == true) { - var groupTemplate = { - style: this.options.drawPoints.style, - size:this.options.drawPoints.size, - className: this.className - }; - DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, JSONcontainer, SVGcontainer); - } - } - else { - var barWidth = Math.round(0.3 * iconWidth); - var bar1Height = Math.round(0.4 * iconHeight); - var bar2Height = Math.round(0.75 * iconHeight); - - var offset = Math.round((iconWidth - (2 * barWidth))/3); - - DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' vis-bar', JSONcontainer, SVGcontainer, this.style); - DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' vis-bar', JSONcontainer, SVGcontainer, this.style); - } -}; - - /** * return the legend entree for this group. * @@ -197,23 +119,40 @@ GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, icon * @param iconHeight * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} */ -GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); - return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; +GraphGroup.prototype.getLegend = function (iconWidth, iconHeight, framework, x, y) { + if (framework == undefined || framework == null) { + var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); + framework = {svg: svg, svgElements:{}, options: this.options, groups: [this]} + } + if (x == undefined || x == null){ + x = 0; + } + if (y == undefined || y == null){ + y = 0.5 * iconHeight; + } + switch (this.options.style){ + case "line": + Lines.drawIcon(this, x, y, iconWidth, iconHeight, framework); + break; + case "points": //explicit no break + case "point": + Points.drawIcon(this, x, y, iconWidth, iconHeight, framework); + break; + case "bar": + Bars.drawIcon(this, x, y, iconWidth, iconHeight, framework); + break; + } + return {icon: framework.svg, label: this.content, orientation: this.options.yAxisOrientation}; }; -GraphGroup.prototype.getYRange = function(groupData) { - return this.type.getYRange(groupData); +GraphGroup.prototype.getYRange = function (groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; }; -GraphGroup.prototype.getData = function(groupData) { - return this.type.getData(groupData); -}; - -GraphGroup.prototype.draw = function(dataset, group, framework) { - this.type.draw(dataset, group, framework); -}; - - module.exports = GraphGroup; diff --git a/lib/vis/lib/timeline/component/Group.js b/lib/vis/lib/timeline/component/Group.js index ae60270a94..8a09e4aec8 100644 --- a/lib/vis/lib/timeline/component/Group.js +++ b/lib/vis/lib/timeline/component/Group.js @@ -47,7 +47,11 @@ function Group (groupId, data, itemSet) { */ Group.prototype._create = function() { var label = document.createElement('div'); - label.className = 'vis-label'; + if (this.itemSet.options.groupEditable.order) { + label.className = 'vis-label draggable'; + } else { + label.className = 'vis-label'; + } this.dom.label = label; var inner = document.createElement('div'); @@ -173,6 +177,9 @@ Group.prototype.redraw = function(range, margin, restack) { restack = true; } + // recalculate the height of the subgroups + this._calculateSubGroupHeights(); + // reposition visible items vertically if (typeof this.itemSet.options.order === 'function') { // a custom order function @@ -240,6 +247,25 @@ Group.prototype.redraw = function(range, margin, restack) { return resized; }; +/** + * recalculate the height of the subgroups + * @private + */ +Group.prototype._calculateSubGroupHeights = function () { + if (Object.keys(this.subgroups).length > 0) { + var me = this; + + this.resetSubgroups(); + + util.forEach(this.visibleItems, function (item) { + if (item.data.subgroup !== undefined) { + me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height, item.height); + me.subgroups[item.data.subgroup].visible = true; + } + }); + } +}; + /** * recalculate the height of the group * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin @@ -250,20 +276,12 @@ Group.prototype._calculateHeight = function (margin) { // recalculate the height of the group var height; var visibleItems = this.visibleItems; - //var visibleSubgroups = []; - //this.visibleSubgroups = 0; - this.resetSubgroups(); - var me = this; if (visibleItems.length > 0) { 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)); - if (item.data.subgroup !== undefined) { - me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); - me.subgroups[item.data.subgroup].visible = true; - } }); if (min > margin.axis) { // there is an empty gap between the lowest item and the axis diff --git a/lib/vis/lib/timeline/component/ItemSet.js b/lib/vis/lib/timeline/component/ItemSet.js index e4d7378932..d66c77e468 100644 --- a/lib/vis/lib/timeline/component/ItemSet.js +++ b/lib/vis/lib/timeline/component/ItemSet.js @@ -34,10 +34,16 @@ function ItemSet(body, options) { }, align: 'auto', // alignment of box items stack: true, - groupOrder: null, + groupOrderSwap: function(fromGroup, toGroup, groups) { + var targetOrder = toGroup.order; + toGroup.order = fromGroup.order; + fromGroup.order = targetOrder; + }, + groupOrder: 'order', selectable: true, multiselect: false, + itemsAlwaysDraggable: false, editable: { updateTime: false, @@ -46,6 +52,12 @@ function ItemSet(body, options) { remove: false }, + groupEditable: { + order: false, + add: false, + remove: false + }, + snap: TimeStep.snap, onAdd: function (item, callback) { @@ -63,6 +75,15 @@ function ItemSet(body, options) { onMoving: function (item, callback) { callback(item); }, + onAddGroup: function (item, callback) { + callback(item); + }, + onMoveGroup: function (item, callback) { + callback(item); + }, + onRemoveGroup: function (item, callback) { + callback(item); + }, margin: { item: { @@ -127,6 +148,7 @@ function ItemSet(body, options) { this.stackDirty = true; // if true, all items will be restacked on next redraw this.touchParams = {}; // stores properties while dragging + this.groupTouchParams = {}; // create the HTML DOM this._create(); @@ -209,6 +231,12 @@ ItemSet.prototype._create = function(){ // add item on doubletap this.hammer.on('doubletap', this._onAddItem.bind(this)); + this.groupHammer = new Hammer(this.body.dom.leftContainer); + this.groupHammer.on('panstart', this._onGroupDragStart.bind(this)); + this.groupHammer.on('panmove', this._onGroupDrag.bind(this)); + this.groupHammer.on('panend', this._onGroupDragEnd.bind(this)); + this.groupHammer.get('pan').set({threshold:5, direction:30}); + // attach to the DOM this.show(); }; @@ -280,7 +308,7 @@ ItemSet.prototype._create = function(){ ItemSet.prototype.setOptions = function(options) { if (options) { // copy all options that we know - var fields = ['type', 'align', 'order', 'stack', 'selectable', 'multiselect', 'groupOrder', 'dataAttributes', 'template','groupTemplate','hide', 'snap']; + var fields = ['type', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap']; util.selectiveExtend(fields, this.options, options); if ('orientation' in options) { @@ -323,6 +351,17 @@ ItemSet.prototype.setOptions = function(options) { util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); } } + + if ('groupEditable' in options) { + if (typeof options.groupEditable === 'boolean') { + this.options.groupEditable.order = options.groupEditable; + this.options.groupEditable.add = options.groupEditable; + this.options.groupEditable.remove = options.groupEditable; + } + else if (typeof options.groupEditable === 'object') { + util.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable); + } + } // callback functions var addCallback = (function (name) { @@ -334,7 +373,7 @@ ItemSet.prototype.setOptions = function(options) { this.options[name] = fn; } }).bind(this); - ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback); + ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup'].forEach(addCallback); // force the itemSet to refresh: options like orientation and margins may be changed this.markDirty(); @@ -687,6 +726,8 @@ ItemSet.prototype.setItems = function(items) { // update the group holding all ungrouped items this._updateUngrouped(); } + + this.body.emitter.emit('_change', {queue: true}); }; /** @@ -746,7 +787,7 @@ ItemSet.prototype.setGroups = function(groups) { // update the order of all items in each group this._order(); - this.body.emitter.emit('change', {queue: true}); + this.body.emitter.emit('_change', {queue: true}); }; /** @@ -857,7 +898,7 @@ ItemSet.prototype._onUpdate = function(ids) { this._order(); this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); + this.body.emitter.emit('_change', {queue: true}); }; /** @@ -887,7 +928,7 @@ ItemSet.prototype._onRemove = function(ids) { // update order this._order(); this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); + this.body.emitter.emit('_change', {queue: true}); } }; @@ -957,7 +998,7 @@ ItemSet.prototype._onAddGroups = function(ids) { } }); - this.body.emitter.emit('change', {queue: true}); + this.body.emitter.emit('_change', {queue: true}); }; /** @@ -978,7 +1019,7 @@ ItemSet.prototype._onRemoveGroups = function(ids) { this.markDirty(); - this.body.emitter.emit('change', {queue: true}); + this.body.emitter.emit('_change', {queue: true}); }; /** @@ -1122,7 +1163,7 @@ ItemSet.prototype._getGroupIndex = function(groupId) { if (groupId == this.groupIds[i]) return i; } -} +}; /** * Start dragging the selected events @@ -1134,10 +1175,10 @@ ItemSet.prototype._onDragStart = function (event) { var me = this; var props; - if (item && item.selected) { + if (item && (item.selected || this.options.itemsAlwaysDraggable)) { - if (!this.options.editable.updateTime && - !this.options.editable.updateGroup && + if (!this.options.editable.updateTime && + !this.options.editable.updateGroup && !item.editable) { return; } @@ -1155,7 +1196,7 @@ ItemSet.prototype._onDragStart = function (event) { item: dragLeftItem, initialX: event.center.x, dragLeft: true, - data: util.extend({}, item.data) // clone the items data + data: this._cloneItemData(item.data) }; this.touchParams.itemProps = [props]; @@ -1165,7 +1206,7 @@ ItemSet.prototype._onDragStart = function (event) { item: dragRightItem, initialX: event.center.x, dragRight: true, - data: util.extend({}, item.data) // clone the items data + data: this._cloneItemData(item.data) }; this.touchParams.itemProps = [props]; @@ -1175,18 +1216,18 @@ ItemSet.prototype._onDragStart = function (event) { var baseGroupIndex = this._getGroupIndex(item.data.group); - this.touchParams.itemProps = this.getSelection().map(function (id) { + var itemsToDrag = (this.options.itemsAlwaysDraggable && !item.selected) ? [item.id] : this.getSelection(); + + this.touchParams.itemProps = itemsToDrag.map(function (id) { var item = me.items[id]; var groupIndex = me._getGroupIndex(item.data.group); - var props = { + return { item: item, initialX: event.center.x, groupOffset: baseGroupIndex-groupIndex, - data: util.extend({}, item.data) // clone the items data + data: this._cloneItemData(item.data) }; - - return props; - }); + }.bind(this)); } event.stopPropagation(); @@ -1209,7 +1250,7 @@ ItemSet.prototype._onDragStartAddItem = function (event) { var time = this.body.util.toTime(x); var scale = this.body.util.getScale(); var step = this.body.util.getStep(); - var start = snap ? snap(time, scale, step) : start; + var start = snap ? snap(time, scale, step) : time; var end = start; var itemData = { @@ -1229,14 +1270,14 @@ ItemSet.prototype._onDragStartAddItem = function (event) { var newItem = new RangeItem(itemData, this.conversion, this.options); newItem.id = id; // TODO: not so nice setting id afterwards - newItem.data = itemData; + newItem.data = this._cloneItemData(itemData); this._addItem(newItem); var props = { item: newItem, dragRight: true, initialX: event.center.x, - data: util.extend({}, itemData) + data: newItem.data }; this.touchParams.itemProps = [props]; @@ -1263,32 +1304,30 @@ ItemSet.prototype._onDrag = function (event) { var updateGroupAllowed = me.options.editable.updateGroup; var newGroupBase = null; if (updateGroupAllowed && selectedItem) { - if (selectedItem.data.group != undefined) { - // drag from one group to another - var group = me.groupFromTarget(event); - if (group) { - //we know the offset for all items, so the new group for all items - //will be relative to this one. - newGroupBase = this._getGroupIndex(group.groupId); - } + if (selectedItem.data.group != undefined) { + // drag from one group to another + var group = me.groupFromTarget(event); + if (group) { + //we know the offset for all items, so the new group for all items + //will be relative to this one. + newGroupBase = this._getGroupIndex(group.groupId); } + } } // move this.touchParams.itemProps.forEach(function (props) { - var newProps = {}; var current = me.body.util.toTime(event.center.x - xOffset); var initial = me.body.util.toTime(props.initialX - xOffset); - var offset = current - initial; - - var itemData = util.extend({}, props.item.data); // clone the data + var offset = current - initial; // ms + var itemData = this._cloneItemData(props.item.data); // clone the data if (props.item.editable === false) { return; } - var updateTimeAllowed = me.options.editable.updateTime || - props.item.editable === true; + var updateTimeAllowed = me.options.editable.updateTime || + props.item.editable === true; if (updateTimeAllowed) { if (props.dragLeft) { @@ -1296,6 +1335,7 @@ ItemSet.prototype._onDrag = function (event) { if (itemData.start != undefined) { var initialStart = util.convert(props.data.start, 'Date'); var start = new Date(initialStart.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) itemData.start = snap ? snap(start, scale, step) : start; } } @@ -1304,6 +1344,7 @@ ItemSet.prototype._onDrag = function (event) { if (itemData.end != undefined) { var initialEnd = util.convert(props.data.end, 'Date'); var end = new Date(initialEnd.valueOf() + offset); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) itemData.end = snap ? snap(end, scale, step) : end; } } @@ -1317,18 +1358,20 @@ ItemSet.prototype._onDrag = function (event) { var initialEnd = util.convert(props.data.end, 'Date'); var duration = initialEnd.valueOf() - initialStart.valueOf(); + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) itemData.start = snap ? snap(start, scale, step) : start; itemData.end = new Date(itemData.start.valueOf() + duration); } else { + // TODO: pass a Moment instead of a Date to snap(). (Breaking change) itemData.start = snap ? snap(start, scale, step) : start; } } } } - var updateGroupAllowed = me.options.editable.updateGroup || - props.item.editable === true; + var updateGroupAllowed = me.options.editable.updateGroup || + props.item.editable === true; if (updateGroupAllowed && (!props.dragLeft && !props.dragRight) && newGroupBase!=null) { if (itemData.group != undefined) { @@ -1343,15 +1386,16 @@ ItemSet.prototype._onDrag = function (event) { } // confirm moving the item + itemData = this._cloneItemData(itemData); // convert start and end to the correct type me.options.onMoving(itemData, function (itemData) { if (itemData) { - props.item.setData(itemData); + props.item.setData(this._cloneItemData(itemData, 'Date')); } - }); - }); + }.bind(this)); + }.bind(this)); this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); + this.body.emitter.emit('_change'); } }; @@ -1402,12 +1446,12 @@ ItemSet.prototype._onDragEnd = function (event) { // force re-stacking of all items next redraw me.stackDirty = true; - me.body.emitter.emit('change'); + me.body.emitter.emit('_change'); }); } else { // update existing item - var itemData = util.extend({}, props.item.data); // clone the data + var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type me.options.onMove(itemData, function (itemData) { if (itemData) { // apply changes @@ -1419,14 +1463,190 @@ ItemSet.prototype._onDragEnd = function (event) { props.item.setData(props.data); me.stackDirty = true; // force re-stacking of all items next redraw - me.body.emitter.emit('change'); + me.body.emitter.emit('_change'); } }); } - }); + }.bind(this)); } }; +ItemSet.prototype._onGroupDragStart = function (event) { + if (this.options.groupEditable.order) { + this.groupTouchParams.group = this.groupFromTarget(event); + + if (this.groupTouchParams.group) { + event.stopPropagation(); + + this.groupTouchParams.originalOrder = this.groupsData.getIds({ + order: this.options.groupOrder + }); + } + } +} + +ItemSet.prototype._onGroupDrag = function (event) { + if (this.options.groupEditable.order && this.groupTouchParams.group) { + event.stopPropagation(); + + // drag from one group to another + var group = this.groupFromTarget(event); + + // try to avoid toggling when groups differ in height + if (group && group.height != this.groupTouchParams.group.height) { + var movingUp = (group.top < this.groupTouchParams.group.top); + var clientY = event.center ? event.center.y : event.clientY; + var targetGroupTop = util.getAbsoluteTop(group.dom.foreground); + var draggedGroupHeight = this.groupTouchParams.group.height; + if (movingUp) { + // skip swapping the groups when the dragged group is not below clientY afterwards + if (targetGroupTop + draggedGroupHeight < clientY) { + return; + } + } else { + var targetGroupHeight = group.height; + // skip swapping the groups when the dragged group is not below clientY afterwards + if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) { + return; + } + } + } + + if (group && group != this.groupTouchParams.group) { + var groupsData = this.groupsData; + var targetGroup = groupsData.get(group.groupId); + var draggedGroup = groupsData.get(this.groupTouchParams.group.groupId); + + // switch groups + if (draggedGroup && targetGroup) { + this.options.groupOrderSwap(draggedGroup, targetGroup, this.groupsData); + this.groupsData.update(draggedGroup); + this.groupsData.update(targetGroup); + } + + // fetch current order of groups + var newOrder = this.groupsData.getIds({ + order: this.options.groupOrder + }); + + // in case of changes since _onGroupDragStart + if (!util.equalArray(newOrder, this.groupTouchParams.originalOrder)) { + var groupsData = this.groupsData; + var origOrder = this.groupTouchParams.originalOrder; + var draggedId = this.groupTouchParams.group.groupId; + var numGroups = Math.min(origOrder.length, newOrder.length); + var curPos = 0; + var newOffset = 0; + var orgOffset = 0; + while (curPos < numGroups) { + // as long as the groups are where they should be step down along the groups order + while ((curPos+newOffset) < numGroups + && (curPos+orgOffset) < numGroups + && newOrder[curPos+newOffset] == origOrder[curPos+orgOffset]) { + curPos++; + } + + // all ok + if (curPos+newOffset >= numGroups) { + break; + } + + // not all ok + // if dragged group was move upwards everything below should have an offset + if (newOrder[curPos+newOffset] == draggedId) { + newOffset = 1; + continue; + } + // if dragged group was move downwards everything above should have an offset + else if (origOrder[curPos+orgOffset] == draggedId) { + orgOffset = 1; + continue; + } + // found a group (apart from dragged group) that has the wrong position -> switch with the + // group at the position where other one should be, fix index arrays and continue + else { + var slippedPosition = newOrder.indexOf(origOrder[curPos+orgOffset]) + var switchGroup = groupsData.get(newOrder[curPos+newOffset]); + var shouldBeGroup = groupsData.get(origOrder[curPos+orgOffset]); + this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); + groupsData.update(switchGroup); + groupsData.update(shouldBeGroup); + + var switchGroupId = newOrder[curPos+newOffset]; + newOrder[curPos+newOffset] = origOrder[curPos+orgOffset]; + newOrder[slippedPosition] = switchGroupId; + + curPos++; + } + } + } + + } + } +} + +ItemSet.prototype._onGroupDragEnd = function (event) { + if (this.options.groupEditable.order && this.groupTouchParams.group) { + event.stopPropagation(); + + // update existing group + var me = this; + var id = me.groupTouchParams.group.groupId; + var dataset = me.groupsData.getDataSet(); + var groupData = util.extend({}, dataset.get(id)); // clone the data + me.options.onMoveGroup(groupData, function (groupData) { + if (groupData) { + // apply changes + groupData[dataset._fieldId] = id; // ensure the group contains its id (can be undefined) + dataset.update(groupData); + } + else { + + // fetch current order of groups + var newOrder = dataset.getIds({ + order: me.options.groupOrder + }); + + // restore original order + if (!util.equalArray(newOrder, me.groupTouchParams.originalOrder)) { + var origOrder = me.groupTouchParams.originalOrder; + var numGroups = Math.min(origOrder.length, newOrder.length); + var curPos = 0; + while (curPos < numGroups) { + // as long as the groups are where they should be step down along the groups order + while (curPos < numGroups && newOrder[curPos] == origOrder[curPos]) { + curPos++; + } + + // all ok + if (curPos >= numGroups) { + break; + } + + // found a group that has the wrong position -> switch with the + // group at the position where other one should be, fix index arrays and continue + var slippedPosition = newOrder.indexOf(origOrder[curPos]) + var switchGroup = dataset.get(newOrder[curPos]); + var shouldBeGroup = dataset.get(origOrder[curPos]); + me.options.groupOrderSwap(switchGroup, shouldBeGroup, dataset); + groupsData.update(switchGroup); + groupsData.update(shouldBeGroup); + + var switchGroupId = newOrder[curPos]; + newOrder[curPos] = origOrder[curPos]; + newOrder[slippedPosition] = switchGroupId; + + curPos++; + } + } + + } + }); + + me.body.emitter.emit('groupDragged', { groupId: id }); + } +} + /** * Handle selecting/deselecting an item when tapping it * @param {Event} event @@ -1473,8 +1693,6 @@ ItemSet.prototype._onAddItem = function (event) { var snap = this.options.snap || null; var item = this.itemFromTarget(event); - event.stopPropagation(); - if (item) { // update item @@ -1494,7 +1712,7 @@ ItemSet.prototype._onAddItem = function (event) { var scale = this.body.util.getScale(); var step = this.body.util.getStep(); - var newItem = { + var newItemData = { start: snap ? snap(start, scale, step) : start, content: 'new item' }; @@ -1502,18 +1720,19 @@ ItemSet.prototype._onAddItem = function (event) { // when default type is a range, add a default end date to the new item if (this.options.type === 'range') { var end = this.body.util.toTime(x + this.props.width / 5); - newItem.end = snap ? snap(end, scale, step) : end; + newItemData.end = snap ? snap(end, scale, step) : end; } - newItem[this.itemsData._fieldId] = util.randomUUID(); + newItemData[this.itemsData._fieldId] = util.randomUUID(); var group = this.groupFromTarget(event); if (group) { - newItem.group = group.groupId; + newItemData.group = group.groupId; } // execute async handler to customize (or cancel) adding an item - this.options.onAdd(newItem, function (item) { + newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type + this.options.onAdd(newItemData, function (item) { if (item) { me.itemsData.getDataSet().add(item); // TODO: need to trigger a redraw? @@ -1543,23 +1762,37 @@ ItemSet.prototype._onMultiSelectItem = function (event) { if (shiftKey && this.options.multiselect) { // select all items between the old selection and the tapped item - + var itemGroup = this.itemsData.get(item.id).group; + + // when filtering get the group of the last selected item + var lastSelectedGroup = undefined; + if (this.options.multiselectPerGroup) { + if (selection.length > 0) { + lastSelectedGroup = this.itemsData.get(selection[0]).group; + } + } + // determine the selection range - selection.push(item.id); + if (!this.options.multiselectPerGroup || lastSelectedGroup == undefined || lastSelectedGroup == itemGroup) { + selection.push(item.id); + } var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); + + if (!this.options.multiselectPerGroup || lastSelectedGroup == itemGroup) { + // select all items within the selection range + selection = []; + for (var id in this.items) { + if (this.items.hasOwnProperty(id)) { + var _item = this.items[id]; + var start = _item.data.start; + var end = (_item.data.end !== undefined) ? _item.data.end : start; - // select all items within the selection range - selection = []; - for (var id in this.items) { - if (this.items.hasOwnProperty(id)) { - var _item = this.items[id]; - var start = _item.data.start; - var end = (_item.data.end !== undefined) ? _item.data.end : start; - - if (start >= range.min && - end <= range.max && - !(_item instanceof BackgroundItem)) { - selection.push(_item.id); // do not use id but item.id, id itself is stringified + if (start >= range.min && + end <= range.max && + (!this.options.multiselectPerGroup || lastSelectedGroup == this.itemsData.get(_item.id).group) && + !(_item instanceof BackgroundItem)) { + selection.push(_item.id); // do not use id but item.id, id itself is stringified + } } } } @@ -1687,4 +1920,31 @@ ItemSet.itemSetFromTarget = function(event) { return null; }; +/** + * Clone the data of an item, and "normalize" it: convert the start and end date + * to the type (Date, Moment, ...) configured in the DataSet. If not configured, + * start and end are converted to Date. + * @param {Object} itemData, typically `item.data` + * @param {string} [type] Optional Date type. If not provided, the type from the DataSet is taken + * @return {Object} The cloned object + * @private + */ +ItemSet.prototype._cloneItemData = function (itemData, type) { + var clone = util.extend({}, itemData); + + if (!type) { + // convert start and end date to the type (Date, Moment, ...) configured in the DataSet + type = this.itemsData.getDataSet()._options.type; + } + + if (clone.start != undefined) { + clone.start = util.convert(clone.start, type && type.start || 'Date'); + } + if (clone.end != undefined) { + clone.end = util.convert(clone.end , type && type.end || 'Date'); + } + + return clone; +}; + module.exports = ItemSet; diff --git a/lib/vis/lib/timeline/component/Legend.js b/lib/vis/lib/timeline/component/Legend.js index e34e9d9514..90b9725950 100644 --- a/lib/vis/lib/timeline/component/Legend.js +++ b/lib/vis/lib/timeline/component/Legend.js @@ -8,7 +8,7 @@ var Component = require('./Component'); function Legend(body, options, side, linegraphOptions) { this.body = body; this.defaultOptions = { - enabled: true, + enabled: false, icons: true, iconSize: 20, iconSpacing: 6, @@ -18,9 +18,10 @@ function Legend(body, options, side, linegraphOptions) { }, right: { visible: true, - position: 'top-left' // top/bottom - left,center,right + position: 'top-right' // top/bottom - left,center,right } } + this.side = side; this.options = util.extend({},this.defaultOptions); this.linegraphOptions = linegraphOptions; @@ -30,6 +31,7 @@ function Legend(body, options, side, linegraphOptions) { this.groups = {}; this.amountOfGroups = 0; this._create(); + this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; this.setOptions(options); } @@ -43,10 +45,13 @@ Legend.prototype.clear = function() { Legend.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + // Include a group only if the group option 'excludeFromLegend: false' is not set. + if (graphOptions.options.excludeFromLegend != true) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; } - this.amountOfGroups += 1; }; Legend.prototype.updateGroup = function(label, graphOptions) { @@ -184,9 +189,11 @@ Legend.prototype.drawLegendIcons = function() { var groupArray = Object.keys(this.groups); groupArray.sort(function (a,b) { return (a < b ? -1 : 1); - }) + }); + + // this resets the elements so the order is maintained + DOMutil.resetElements(this.svgElements); - DOMutil.prepareElements(this.svgElements); var padding = window.getComputedStyle(this.dom.frame).paddingTop; var iconOffset = Number(padding.replace('px','')); var x = iconOffset; @@ -199,12 +206,10 @@ Legend.prototype.drawLegendIcons = function() { for (var i = 0; i < groupArray.length; i++) { var groupId = groupArray[i]; if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y); y += iconHeight + this.options.iconSpacing; } } - - DOMutil.cleanupElements(this.svgElements); } }; diff --git a/lib/vis/lib/timeline/component/LineGraph.js b/lib/vis/lib/timeline/component/LineGraph.js index f612ff5279..961a293dc5 100644 --- a/lib/vis/lib/timeline/component/LineGraph.js +++ b/lib/vis/lib/timeline/component/LineGraph.js @@ -6,8 +6,9 @@ var Component = require('./Component'); var DataAxis = require('./DataAxis'); var GraphGroup = require('./GraphGroup'); var Legend = require('./Legend'); -var BarFunctions = require('./graph2d_types/bar'); -var LineFunctions = require('./graph2d_types/line'); +var Bars = require('./graph2d_types/bar'); +var Lines = require('./graph2d_types/line'); +var Points = require('./graph2d_types/points'); var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items @@ -27,11 +28,11 @@ function LineGraph(body, options) { defaultGroup: 'default', sort: true, sampling: true, - stack:false, + stack: false, graphHeight: '400px', shaded: { enabled: false, - orientation: 'bottom' // top, bottom + orientation: 'bottom' // top, bottom, zero }, style: 'line', // line, bar barChart: { @@ -49,42 +50,14 @@ function LineGraph(body, options) { size: 6, style: 'square' // square, circle }, - dataAxis: { - showMinorLabels: true, - showMajorLabels: true, - icons: false, - width: '40px', - visible: true, - alignZeros: true, - left:{ - range: {min:undefined,max:undefined}, - format: function (value) {return value;}, - title: {text:undefined,style:undefined} - }, - right:{ - range: {min:undefined,max:undefined}, - format: function (value) {return value;}, - title: {text:undefined,style:undefined} - } - }, - legend: { - enabled: false, - icons: true, - left: { - visible: true, - position: 'top-left' // top/bottom - left,right - }, - right: { - visible: true, - position: 'top-right' // top/bottom - left,right - } - }, + dataAxis: {}, //Defaults are done on DataAxis level + legend: {}, //Defaults are done on Legend level groups: { visibility: {} } }; - // options is shared by this ItemSet and all its items + // options is shared by this lineGraph and all its items this.options = util.extend({}, this.defaultOptions); this.dom = {}; this.props = {}; @@ -93,6 +66,7 @@ function LineGraph(body, options) { this.abortedGraphUpdate = false; this.updateSVGheight = false; this.updateSVGheightOnResize = false; + this.forceGraphUpdate = true; var me = this; this.itemsData = null; // DataSet @@ -132,18 +106,18 @@ function LineGraph(body, options) { this.svgElements = {}; this.setOptions(options); this.groupsUsingDefaultStyles = [0]; - this.COUNTER = 0; - this.body.emitter.on('rangechanged', function() { + this.body.emitter.on('rangechanged', function () { me.lastStart = me.body.range.start; me.svg.style.left = util.option.asSize(-me.props.width); - me.redraw.call(me,true); + + me.forceGraphUpdate = true; + //Is this local redraw necessary? (Core also does a change event!) + me.redraw.call(me); }); // create the HTML DOM this._create(); this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; - this.body.emitter.emit('change'); - } LineGraph.prototype = new Component(); @@ -151,15 +125,15 @@ LineGraph.prototype = new Component(); /** * Create the HTML DOM for the ItemSet */ -LineGraph.prototype._create = function(){ +LineGraph.prototype._create = function () { var frame = document.createElement('div'); frame.className = 'vis-line-graph'; this.dom.frame = frame; // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.svg.style.position = 'relative'; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px'; this.svg.style.display = 'block'; frame.appendChild(this.svg); @@ -182,23 +156,23 @@ LineGraph.prototype._create = function(){ * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. * @param {object} options */ -LineGraph.prototype.setOptions = function(options) { +LineGraph.prototype.setOptions = function (options) { if (options) { - var fields = ['sampling','defaultGroup','stack','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; - if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { + var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups']; + if (options.graphHeight === undefined && options.height !== undefined) { this.updateSVGheight = true; this.updateSVGheightOnResize = true; } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { + if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) { this.updateSVGheight = true; } } util.selectiveDeepExtend(fields, this.options, options); - util.mergeOptions(this.options, options,'interpolation'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - util.mergeOptions(this.options, options,'legend'); + util.mergeOptions(this.options, options, 'interpolation'); + util.mergeOptions(this.options, options, 'drawPoints'); + util.mergeOptions(this.options, options, 'shaded'); + util.mergeOptions(this.options, options, 'legend'); if (options.interpolation) { if (typeof options.interpolation == 'object') { @@ -237,15 +211,16 @@ LineGraph.prototype.setOptions = function(options) { } // this is used to redraw the graph if the visibility of the groups is changed. - if (this.dom.frame) { - this.redraw(true); + if (this.dom.frame) { //not on initial run? + this.forceGraphUpdate=true; + this.body.emitter.emit("_change",{queue: true}); } }; /** * Hide the component from the DOM */ -LineGraph.prototype.hide = function() { +LineGraph.prototype.hide = function () { // remove the frame containing the items if (this.dom.frame.parentNode) { this.dom.frame.parentNode.removeChild(this.dom.frame); @@ -257,7 +232,7 @@ LineGraph.prototype.hide = function() { * Show the component in the DOM (when not already visible). * @return {Boolean} changed */ -LineGraph.prototype.show = function() { +LineGraph.prototype.show = function () { // show frame containing the items if (!this.dom.frame.parentNode) { this.body.dom.center.appendChild(this.dom.frame); @@ -269,7 +244,7 @@ LineGraph.prototype.show = function() { * Set items * @param {vis.DataSet | null} items */ -LineGraph.prototype.setItems = function(items) { +LineGraph.prototype.setItems = function (items) { var me = this, ids, oldItemsData = this.itemsData; @@ -307,9 +282,6 @@ LineGraph.prototype.setItems = function(items) { ids = this.itemsData.getIds(); this._onAdd(ids); } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); }; @@ -317,7 +289,7 @@ LineGraph.prototype.setItems = function(items) { * Set groups * @param {vis.DataSet} groups */ -LineGraph.prototype.setGroups = function(groups) { +LineGraph.prototype.setGroups = function (groups) { var me = this; var ids; @@ -330,7 +302,9 @@ LineGraph.prototype.setGroups = function(groups) { // remove all drawn groups ids = this.groupsData.getIds(); this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw + for (var i = 0; i < ids.length; i++) { + this._removeGroup(ids[i]); + } } // replace the dataset @@ -355,34 +329,23 @@ LineGraph.prototype.setGroups = function(groups) { ids = this.groupsData.getIds(); this._onAddGroups(ids); } - this._onUpdate(); }; - -/** - * Update the data - * @param [ids] - * @private - */ -LineGraph.prototype._onUpdate = function(ids) { - this._updateUngrouped(); +LineGraph.prototype._onUpdate = function (ids) { this._updateAllGroupData(); - //this._updateGraph(); - this.redraw(true); }; -LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; -LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; -LineGraph.prototype._onUpdateGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - var group = this.groupsData.get(groupIds[i]); - this._updateGroup(group, groupIds[i]); - } - - //this._updateGraph(); - this.redraw(true); +LineGraph.prototype._onAdd = function (ids) { + this._onUpdate(ids); +}; +LineGraph.prototype._onRemove = function (ids) { + this._onUpdate(ids); +}; +LineGraph.prototype._onUpdateGroups = function (groupIds) { + this._updateAllGroupData(); +}; +LineGraph.prototype._onAddGroups = function (groupIds) { + this._onUpdateGroups(groupIds); }; -LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; - /** * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph @@ -391,25 +354,32 @@ LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(gro */ LineGraph.prototype._onRemoveGroups = function (groupIds) { for (var i = 0; i < groupIds.length; i++) { - if (this.groups.hasOwnProperty(groupIds[i])) { - if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { - this.yAxisRight.removeGroup(groupIds[i]); - this.legendRight.removeGroup(groupIds[i]); - this.legendRight.redraw(); - } - else { - this.yAxisLeft.removeGroup(groupIds[i]); - this.legendLeft.removeGroup(groupIds[i]); - this.legendLeft.redraw(); - } - delete this.groups[groupIds[i]]; - } + this._removeGroup(groupIds[i]); } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); + this.forceGraphUpdate = true; + this.body.emitter.emit("_change",{queue: true}); }; +/** + * this cleans the group out off the legends and the dataaxis + * @param groupId + * @private + */ +LineGraph.prototype._removeGroup = function (groupId) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.removeGroup(groupId); + this.legendRight.removeGroup(groupId); + this.legendRight.redraw(); + } + else { + this.yAxisLeft.removeGroup(groupId); + this.legendLeft.removeGroup(groupId); + this.legendLeft.redraw(); + } + delete this.groups[groupId]; + } +} /** * update a group object with the group dataset entree @@ -435,10 +405,16 @@ LineGraph.prototype._updateGroup = function (group, groupId) { if (this.groups[groupId].options.yAxisOrientation == 'right') { this.yAxisRight.updateGroup(groupId, this.groups[groupId]); this.legendRight.updateGroup(groupId, this.groups[groupId]); + //If yAxisOrientation changed, clean out the group from the other axis. + this.yAxisLeft.removeGroup(groupId); + this.legendLeft.removeGroup(groupId); } else { this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); this.legendLeft.updateGroup(groupId, this.groups[groupId]); + //If yAxisOrientation changed, clean out the group from the other axis. + this.yAxisRight.removeGroup(groupId); + this.legendRight.removeGroup(groupId); } } this.legendLeft.redraw(); @@ -454,98 +430,83 @@ LineGraph.prototype._updateGroup = function (group, groupId) { LineGraph.prototype._updateAllGroupData = function () { if (this.itemsData != null) { var groupsContent = {}; - var groupId; - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupsContent[groupId] = []; + var items = this.itemsData.get(); + //pre-Determine array sizes, for more efficient memory claim + var groupCounts = {}; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + var groupId = item.group; + if (groupId === null || groupId === undefined) { + groupId = UNGROUPED; } + groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1; } - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (groupsContent[item.group] === undefined) { - throw new Error('Cannot find referenced group ' + item.group + '. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') - } - item.x = util.convert(item.x,'Date'); - groupsContent[item.group].push(item); + //Now insert data into the arrays. + for (var i = 0; i < items.length; i++) { + var item = items[i]; + var groupId = item.group; + if (groupId === null || groupId === undefined) { + groupId = UNGROUPED; } - } - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].setItems(groupsContent[groupId]); + if (!groupsContent.hasOwnProperty(groupId)) { + groupsContent[groupId] = new Array(groupCounts[groupId]); } - } - } -}; + //Copy data (because of unmodifiable DataView input. + var extended = util.bridgeObject(item); + extended.x = util.convert(item.x, 'Date'); + extended.orginalY = item.y; //real Y + extended.y = Number(item.y); + var index= groupsContent[groupId].length - groupCounts[groupId]--; + groupsContent[groupId][index] = extended; + } -/** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. This anonymous group is called 'graph'. - * @protected - */ -LineGraph.prototype._updateUngrouped = function() { - if (this.itemsData && this.itemsData != null) { - var ungroupedCounter = 0; - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (item != undefined) { - if (item.hasOwnProperty('group')) { - if (item.group === undefined) { - item.group = UNGROUPED; - } - } - else { - item.group = UNGROUPED; - } - ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; + //Make sure all groups are present, to allow removal of old groups + for (var groupId in this.groups){ + if (this.groups.hasOwnProperty(groupId)){ + if (!groupsContent.hasOwnProperty(groupId)) { + groupsContent[groupId] = new Array(0); } } } - if (ungroupedCounter == 0) { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - else { - var group = {id: UNGROUPED, content: this.options.defaultGroup}; - this._updateGroup(group, UNGROUPED); + //Update legendas, style and axis + for (var groupId in groupsContent) { + if (groupsContent.hasOwnProperty(groupId)) { + if (groupsContent[groupId].length == 0) { + if (this.groups.hasOwnProperty(groupId)) { + this._removeGroup(groupId); + } + } else { + var group = undefined; + if (this.groupsData != undefined) { + group = this.groupsData.get(groupId); + } + if (group == undefined) { + group = {id: groupId, content: this.options.defaultGroup + groupId}; + } + this._updateGroup(group, groupId); + this.groups[groupId].setItems(groupsContent[groupId]); + } + } } + this.forceGraphUpdate = true; + this.body.emitter.emit("_change",{queue: true}); } - else { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - - this.legendLeft.redraw(); - this.legendRight.redraw(); }; - /** * Redraw the component, mandatory function * @return {boolean} Returns true if the component is resized */ -LineGraph.prototype.redraw = function(forceGraphUpdate) { +LineGraph.prototype.redraw = function () { var resized = false; // calculate actual size and position this.props.width = this.dom.frame.offsetWidth; this.props.height = this.body.domProps.centerContainer.height - - this.body.domProps.border.top - - this.body.domProps.border.bottom; - - // update the graph if there is no lastWidth or with, used for the initial draw - if (this.lastWidth === undefined && this.props.width) { - forceGraphUpdate = true; - } + - this.body.domProps.border.top + - this.body.domProps.border.bottom; // check if this component is resized resized = this._isResized() || resized; @@ -559,7 +520,7 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) { // the svg element is three times as big as the width, this allows for fully dragging left and right // without reloading the graph. the controls for this are bound to events in the constructor if (resized == true) { - this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.width = util.option.asSize(3 * this.props.width); this.svg.style.left = util.option.asSize(-this.props.width); // if the height of the graph is set as proportional, change the height of the svg @@ -577,12 +538,13 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) { this.updateSVGheight = false; } else { - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px'; } // zoomed is here to ensure that animations are shown correctly. - if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || this.forceGraphUpdate == true) { resized = this._updateGraph() || resized; + this.forceGraphUpdate = false; } else { // move the whole svg while dragging @@ -590,19 +552,43 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) { var offset = this.body.range.start - this.lastStart; var range = this.body.range.end - this.body.range.start; if (this.props.width != 0) { - var rangePerPixelInv = this.props.width/range; + var rangePerPixelInv = this.props.width / range; var xOffset = offset * rangePerPixelInv; this.svg.style.left = (-this.props.width - xOffset) + 'px'; } } } - this.legendLeft.redraw(); this.legendRight.redraw(); return resized; }; +LineGraph.prototype._getSortedGroupIds = function(){ + // getting group Ids + var grouplist = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + var group = this.groups[groupId]; + if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { + grouplist.push({id:groupId,zIndex:group.options.zIndex}); + } + } + } + util.insertSort(grouplist,function(a,b){ + var az = a.zIndex; + var bz = b.zIndex; + if (az === undefined) az=0; + if (bz === undefined) bz=0; + return az==bz? 0: (az 0) { - // this is the range of the SVG canvas - var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); - var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); var groupsData = {}; + // fill groups data, this only loads the data we require based on the timewindow this._getRelevantData(groupIds, groupsData, minDate, maxDate); @@ -640,44 +617,104 @@ LineGraph.prototype._updateGraph = function () { // we transform the X coordinates to detect collisions for (i = 0; i < groupIds.length; i++) { - preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); + this._convertXcoordinates(groupsData[groupIds[i]]); } // now all needed data has been collected we start the processing. - this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + this._getYRanges(groupIds, groupsData, groupRanges); // update the Y axis first, we use this data to draw at the correct Y points - // changeCalled is required to clean the SVG on a change emit. changeCalled = this._updateYAxis(groupIds, groupRanges); - var MAX_CYCLES = 5; - if (changeCalled == true && this.COUNTER < MAX_CYCLES) { + + // at changeCalled, abort this update cycle as the graph needs another update with new Width input from the Redraw container. + // Cleanup SVG elements on abort. + if (changeCalled == true) { DOMutil.cleanupElements(this.svgElements); this.abortedGraphUpdate = true; - this.COUNTER++; - this.body.emitter.emit('change'); return true; } - else { - if (this.COUNTER > MAX_CYCLES) { - console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."); - } - this.COUNTER = 0; - this.abortedGraphUpdate = false; + this.abortedGraphUpdate = false; - // With the yAxis scaled correctly, use this to get the Y values of the points. - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); - } - - // draw the groups - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.style != 'bar') { // bar needs to be drawn enmasse - group.draw(processedGroupData[groupIds[i]], group, this.framework); + // With the yAxis scaled correctly, use this to get the Y values of the points. + var below = undefined; + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (this.options.stack === true && this.options.style === 'line') { + if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) { + if (below != undefined) { + this._stack(groupsData[group.id], groupsData[below.id]); + if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group"){ + if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group"){ + below.options.shaded.orientation="group"; + below.options.shaded.groupId=group.id; + } else { + group.options.shaded.orientation="group"; + group.options.shaded.groupId=below.id; + } + } + } + below = group; } } - BarFunctions.draw(groupIds, processedGroupData, this.framework); + this._convertYcoordinates(groupsData[groupIds[i]], group); + } + + //Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines. + var paths = {}; + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.style === 'line' && group.options.shaded.enabled == true) { + var dataset = groupsData[groupIds[i]]; + if (dataset == null || dataset.length == 0) { + continue; + } + if (!paths.hasOwnProperty(groupIds[i])) { + paths[groupIds[i]] = Lines.calcPath(dataset, group); + } + if (group.options.shaded.orientation === "group") { + var subGroupId = group.options.shaded.groupId; + if (groupIds.indexOf(subGroupId) === -1) { + console.log(group.id + ": Unknown shading group target given:" + subGroupId); + continue; + } + if (!paths.hasOwnProperty(subGroupId)) { + paths[subGroupId] = Lines.calcPath(groupsData[subGroupId], this.groups[subGroupId]); + } + Lines.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework); + } + else { + Lines.drawShading(paths[groupIds[i]], group, undefined, this.framework); + } + } + } + + // draw the groups, calculating paths if still necessary. + Bars.draw(groupIds, groupsData, this.framework); + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (groupsData[groupIds[i]].length > 0) { + switch (group.options.style) { + case "line": + if (!paths.hasOwnProperty(groupIds[i])) { + paths[groupIds[i]] = Lines.calcPath(groupsData[groupIds[i]], group); + } + Lines.draw(paths[groupIds[i]], group, this.framework); + //explicit no break; + case "point": + //explicit no break; + case "points": + if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) { + Points.draw(groupsData[groupIds[i]], group, this.framework); + } + break; + case "bar": + // bar needs to be drawn enmasse + //explicit no break + default: + //do nothing... + } + } + } } } @@ -687,6 +724,51 @@ LineGraph.prototype._updateGraph = function () { return false; }; +LineGraph.prototype._stack = function (data, subData) { + var index, dx, dy, subPrevPoint, subNextPoint; + index = 0; + // for each data point we look for a matching on in the set below + for (var j = 0; j < data.length; j++) { + subPrevPoint = undefined; + subNextPoint = undefined; + // we look for time matches or a before-after point + for (var k = index; k < subData.length; k++) { + // if times match exactly + if (subData[k].x === data[j].x) { + subPrevPoint = subData[k]; + subNextPoint = subData[k]; + index = k; + break; + } + else if (subData[k].x > data[j].x) { // overshoot + subNextPoint = subData[k]; + if (k == 0) { + subPrevPoint = subNextPoint; + } + else { + subPrevPoint = subData[k - 1]; + } + index = k; + break; + } + } + // in case the last data point has been used, we assume it stays like this. + if (subNextPoint === undefined) { + subPrevPoint = subData[subData.length - 1]; + subNextPoint = subData[subData.length - 1]; + } + // linear interpolation + dx = subNextPoint.x - subPrevPoint.x; + dy = subNextPoint.y - subPrevPoint.y; + if (dx == 0) { + data[j].y = data[j].orginalY + subNextPoint.y; + } + else { + data[j].y = data[j].orginalY + (dy / dx) * (data[j].x - subPrevPoint.x) + subPrevPoint.y; // ax + b where b is data[j].y + } + } +} + /** * first select and preprocess the data from the datasets. @@ -706,33 +788,27 @@ LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, if (groupIds.length > 0) { for (i = 0; i < groupIds.length; i++) { group = this.groups[groupIds[i]]; - groupsData[groupIds[i]] = []; - var dataContainer = groupsData[groupIds[i]]; + var itemsData = group.getItems(); // optimization for sorted data if (group.options.sort == true) { - var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); - for (j = guess; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > maxDate) { - dataContainer.push(item); - break; - } - else { - dataContainer.push(item); - } - } + var dateComparator = function (a, b) { + return a.getTime() == b.getTime() ? 0 : a < b ? -1 : 1 + }; + var first = Math.max(0, util.binarySearchValue(itemsData, minDate, 'x', 'before', dateComparator)); + var last = Math.min(itemsData.length, util.binarySearchValue(itemsData, maxDate, 'x', 'after', dateComparator) + 1); + if (last <= 0) { + last = itemsData.length; } + var dataContainer = new Array(last-first); + for (j = first; j < last; j++) { + item = group.itemsData[j]; + dataContainer[j-first] = item; + } + groupsData[groupIds[i]] = dataContainer; } else { - for (j = 0; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > minDate && item.x < maxDate) { - dataContainer.push(item); - } - } - } + // If unsorted data, all data is relevant, just returning entire structure + groupsData[groupIds[i]] = group.itemsData; } } } @@ -762,12 +838,12 @@ LineGraph.prototype._applySampling = function (groupIds, groupsData) { var pointsPerPixel = amountOfPoints / xDistance; increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); - var sampledData = []; + var sampledData = new Array(amountOfPoints); for (var j = 0; j < amountOfPoints; j += increment) { - sampledData.push(dataContainer[j]); - + var idx = Math.round(j/increment); + sampledData[idx]=dataContainer[j]; } - groupsData[groupIds[i]] = sampledData; + groupsData[groupIds[i]] = sampledData.splice(0,Math.round(amountOfPoints/increment)); } } } @@ -796,22 +872,22 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { group = this.groups[groupIds[i]]; // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. if (options.stack === true && options.style === 'bar') { - if (options.yAxisOrientation === 'left') {combinedDataLeft = combinedDataLeft .concat(group.getData(groupData));} - else {combinedDataRight = combinedDataRight.concat(group.getData(groupData));} + if (options.yAxisOrientation === 'left') { + combinedDataLeft = combinedDataLeft.concat(group.getItems()); + } + else { + combinedDataRight = combinedDataRight.concat(group.getItems()); + } } else { - groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); + groupRanges[groupIds[i]] = group.getYRange(groupData, groupIds[i]); } } } // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - BarFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__barStackLeft' , 'left' ); - BarFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right'); - // if line graphs are stacked, their range need to be handled differently and accumulated over all groups. - //LineFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__lineStackLeft' , 'left' ); - //LineFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__lineStackRight', 'right'); - + Bars.getStackedYRange(combinedDataLeft, groupRanges, groupIds, '__barStackLeft', 'left'); + Bars.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right'); } }; @@ -872,7 +948,7 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { this.yAxisRight.setRange(minRight, maxRight); } } - resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; + resized = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft) || resized; resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; if (yAxisRightUsed == true && yAxisLeftUsed == true) { @@ -884,14 +960,17 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { this.yAxisRight.drawIcons = false; } this.yAxisRight.master = !yAxisLeftUsed; + this.yAxisRight.masterAxis = this.yAxisLeft; + if (this.yAxisRight.master == false) { - if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} - else {this.yAxisLeft.lineOffset = 0;} + if (yAxisRightUsed == true) { + this.yAxisLeft.lineOffset = this.yAxisRight.width; + } + else { + this.yAxisLeft.lineOffset = 0; + } resized = this.yAxisLeft.redraw() || resized; - this.yAxisRight.stepPixels = this.yAxisLeft.stepPixels; - this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; - this.yAxisRight.amountOfSteps = this.yAxisLeft.amountOfSteps; resized = this.yAxisRight.redraw() || resized; } else { @@ -899,9 +978,11 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { } // clean the accumulated lists - var tempGroups = ['__barStackLeft','__barStackRight','__lineStackLeft','__lineStackRight']; + var tempGroups = ['__barStackLeft', '__barStackRight', '__lineStackLeft', '__lineStackRight']; for (var i = 0; i < tempGroups.length; i++) { - if (groupIds.indexOf(tempGroups[i]) != -1) {groupIds.splice(groupIds.indexOf(tempGroups[i]),1);} + if (groupIds.indexOf(tempGroups[i]) != -1) { + groupIds.splice(groupIds.indexOf(tempGroups[i]), 1); + } } return resized; @@ -920,7 +1001,7 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { var changed = false; if (axisUsed == false) { if (axis.dom.frame.parentNode && axis.hidden == false) { - axis.hide() + axis.hide(); changed = true; } } @@ -944,17 +1025,11 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { * @private */ LineGraph.prototype._convertXcoordinates = function (datapoints) { - var extractedData = []; - var xValue, yValue; var toScreen = this.body.util.toScreen; - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = datapoints[i].y; - extractedData.push({x: xValue, y: yValue}); + datapoints[i].screen_x = toScreen(datapoints[i].x) + this.props.width; + datapoints[i].screen_y = datapoints[i].y; //starting point for range calculations } - - return extractedData; }; @@ -969,25 +1044,15 @@ LineGraph.prototype._convertXcoordinates = function (datapoints) { * @private */ LineGraph.prototype._convertYcoordinates = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; var axis = this.yAxisLeft; - var svgHeight = Number(this.svg.style.height.replace('px','')); + var svgHeight = Number(this.svg.style.height.replace('px', '')); if (group.options.yAxisOrientation == 'right') { axis = this.yAxisRight; } - for (var i = 0; i < datapoints.length; i++) { - var labelValue = datapoints[i].label ? datapoints[i].label : null; - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = Math.round(axis.convertValue(datapoints[i].y)); - extractedData.push({x: xValue, y: yValue, label:labelValue}); + datapoints[i].screen_y = Math.round(axis.convertValue(datapoints[i].y)); } - group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); - - return extractedData; }; diff --git a/lib/vis/lib/timeline/component/TimeAxis.js b/lib/vis/lib/timeline/component/TimeAxis.js index dec23f838f..136179b415 100644 --- a/lib/vis/lib/timeline/component/TimeAxis.js +++ b/lib/vis/lib/timeline/component/TimeAxis.js @@ -39,6 +39,7 @@ function TimeAxis (body, options) { }, // axis orientation: 'top' or 'bottom' showMinorLabels: true, showMajorLabels: true, + maxMinorChars: 7, format: TimeStep.FORMAT, moment: moment, timeAxis: null @@ -69,6 +70,7 @@ TimeAxis.prototype.setOptions = function(options) { util.selectiveExtend([ 'showMinorLabels', 'showMajorLabels', + 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment' @@ -195,7 +197,7 @@ TimeAxis.prototype._repaintLabels = function () { // calculate range and step (step such that we have space for 7 characters per label) var start = util.convert(this.body.range.start, 'Number'); var end = util.convert(this.body.range.end, 'Number'); - var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); + var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * this.options.maxMinorChars).valueOf(); var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize); minimumStep -= this.body.util.toTime(0).valueOf(); @@ -224,19 +226,20 @@ TimeAxis.prototype._repaintLabels = function () { var next; var x; var xNext; - var isMajor; - var width; + var isMajor, nextIsMajor; + var width = 0, prevWidth; var line; var labelMinor; var xFirstMajorLabel = undefined; - var max = 0; + var count = 0; + const MAX = 1000; var className; step.start(); next = step.getCurrent(); xNext = this.body.util.toScreen(next); - while (step.hasNext() && max < 1000) { - max++; + while (step.hasNext() && count < MAX) { + count++; isMajor = step.isMajor(); className = step.getClassName(); @@ -247,13 +250,16 @@ TimeAxis.prototype._repaintLabels = function () { step.next(); next = step.getCurrent(); + nextIsMajor = step.isMajor(); xNext = this.body.util.toScreen(next); + prevWidth = width; width = xNext - x; - var labelFits = (labelMinor.length + 1) * this.props.minorCharWidth < width; + var showMinorGrid = (width >= prevWidth * 0.4); // prevent displaying of the 31th of the month on a scale of 5 days - if (this.options.showMinorLabels && labelFits) { - this._repaintMinorText(x, labelMinor, orientation, className); + if (this.options.showMinorLabels && showMinorGrid) { + var label = this._repaintMinorText(x, labelMinor, orientation, className); + label.style.width = width + 'px'; // set width to prevent overflow } if (isMajor && this.options.showMajorLabels) { @@ -261,22 +267,28 @@ TimeAxis.prototype._repaintLabels = function () { if (xFirstMajorLabel == undefined) { xFirstMajorLabel = x; } - this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + label = this._repaintMajorText(x, step.getLabelMajor(), orientation, className); } line = this._repaintMajorLine(x, width, orientation, className); } - else { - if (labelFits) { + else { // minor line + if (showMinorGrid) { line = this._repaintMinorLine(x, width, orientation, className); } else { if (line) { - line.style.width = (parseInt (line.style.width) + width) + 'px' + // adjust the width of the previous grid + line.style.width = (parseInt (line.style.width) + width) + 'px'; } } } } + if (count === MAX && !warnedForOverflow) { + console.warn(`Something is wrong with the Timeline scale. Limited drawing of grid lines to ${MAX} lines.`); + warnedForOverflow = true; + } + // create a major label on the left when needed if (this.options.showMajorLabels) { var leftTime = this.body.util.toTime(0), @@ -467,4 +479,7 @@ TimeAxis.prototype._calculateCharSize = function () { this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; }; + +var warnedForOverflow = false; + module.exports = TimeAxis; diff --git a/lib/vis/lib/timeline/component/css/item.css b/lib/vis/lib/timeline/component/css/item.css index e843e1e0a5..4e04888511 100644 --- a/lib/vis/lib/timeline/component/css/item.css +++ b/lib/vis/lib/timeline/component/css/item.css @@ -123,3 +123,8 @@ cursor: e-resize; } + +.vis-range.vis-item.vis-readonly .vis-drag-left, +.vis-range.vis-item.vis-readonly .vis-drag-right { + cursor: auto; +} diff --git a/lib/vis/lib/timeline/component/css/labelset.css b/lib/vis/lib/timeline/component/css/labelset.css index c2690e9712..3aeabd102b 100644 --- a/lib/vis/lib/timeline/component/css/labelset.css +++ b/lib/vis/lib/timeline/component/css/labelset.css @@ -21,6 +21,10 @@ border-bottom: 1px solid #bfbfbf; } +.vis-labelset .vis-label.draggable { + cursor: pointer; +} + .vis-labelset .vis-label:last-child { border-bottom: none; } diff --git a/lib/vis/lib/timeline/component/graph2d_types/bar.js b/lib/vis/lib/timeline/component/graph2d_types/bar.js index c5474b9469..32b3d36e77 100644 --- a/lib/vis/lib/timeline/component/graph2d_types/bar.js +++ b/lib/vis/lib/timeline/component/graph2d_types/bar.js @@ -2,35 +2,43 @@ var DOMutil = require('../../../DOMutil'); var Points = require('./points'); function Bargraph(groupId, options) { - this.groupId = groupId; - this.options = options; } -Bargraph.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; -}; +Bargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; + var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2 * fillHeight); + outline.setAttributeNS(null, "class", "vis-outline"); -Bargraph.prototype.getData = function(groupData) { - var combinedData = []; - for (var j = 0; j < groupData.length; j++) { - combinedData.push({ - x: groupData[j].x, - y: groupData[j].y, - groupId: this.groupId - }); + var barWidth = Math.round(0.3 * iconWidth); + var originalWidth = group.options.barChart.width; + var scale = originalWidth / barWidth; + var bar1Height = Math.round(0.4 * iconHeight); + var bar2Height = Math.round(0.75 * iconHeight); + + var offset = Math.round((iconWidth - (2 * barWidth)) / 3); + + DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); + DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); + + if (group.options.drawPoints.enabled == true) { + var groupTemplate = { + style: group.options.drawPoints.style, + styles: group.options.drawPoints.styles, + size: (group.options.drawPoints.size / scale), + className: group.className + }; + DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg); + DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg); } - return combinedData; + } - - /** * draw a bar graph * @@ -43,7 +51,7 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) { var coreDistance; var key, drawData; var group; - var i,j; + var i, j; var barPoints = 0; // combine all barchart data @@ -53,6 +61,8 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) { if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) { for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { combinedData.push({ + screen_x: processedGroupData[groupIds[i]][j].screen_x, + screen_y: processedGroupData[groupIds[i]][j].screen_y, x: processedGroupData[groupIds[i]][j].x, y: processedGroupData[groupIds[i]][j].y, groupId: groupIds[i], @@ -64,15 +74,17 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) { } } - if (barPoints === 0) {return;} + if (barPoints === 0) { + return; + } // sort by time and by group combinedData.sort(function (a, b) { - if (a.x === b.x) { + if (a.screen_x === b.screen_x) { return a.groupId < b.groupId ? -1 : 1; } else { - return a.x - b.x; + return a.screen_x - b.screen_x; } }); @@ -82,44 +94,52 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) { // plot barchart for (i = 0; i < combinedData.length; i++) { group = framework.groups[combinedData[i].groupId]; - var minWidth = 0.1 * group.options.barChart.width; + var minWidth = group.options.barChart.minWidth != undefined ? group.options.barChart.minWidth : 0.1 * group.options.barChart.width; - key = combinedData[i].x; + key = combinedData[i].screen_x; var heightOffset = 0; if (intersections[key] === undefined) { - if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} - if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} + if (i + 1 < combinedData.length) { + coreDistance = Math.abs(combinedData[i + 1].screen_x - key); + } drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); } else { var nextKey = i + (intersections[key].amount - intersections[key].resolved); var prevKey = i - (intersections[key].resolved + 1); - if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} - if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} + if (nextKey < combinedData.length) { + coreDistance = Math.abs(combinedData[nextKey].screen_x - key); + } drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); intersections[key].resolved += 1; - if (group.options.stack === true) { - if (combinedData[i].y < group.zeroPosition) { + if (group.options.stack === true && group.options.excludeFromStacking !== true) { + if (combinedData[i].screen_y < group.zeroPosition) { heightOffset = intersections[key].accumulatedNegative; - intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].y; + intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].screen_y; } else { heightOffset = intersections[key].accumulatedPositive; - intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].y; + intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].screen_y; } } else if (group.options.barChart.sideBySide === true) { drawData.width = drawData.width / intersections[key].amount; - drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); - if (group.options.barChart.align === 'left') {drawData.offset -= 0.5*drawData.width;} - else if (group.options.barChart.align === 'right') {drawData.offset += 0.5*drawData.width;} + drawData.offset += (intersections[key].resolved) * drawData.width - (0.5 * drawData.width * (intersections[key].amount + 1)); } } - DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); + DOMutil.drawBar(combinedData[i].screen_x + drawData.offset, combinedData[i].screen_y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); // draw points if (group.options.drawPoints.enabled === true) { - Points.draw([combinedData[i]], group, framework, drawData.offset); + let pointData = { + screen_x: combinedData[i].screen_x, + screen_y: combinedData[i].screen_y - heightOffset, + x: combinedData[i].x, + y: combinedData[i].y, + groupId: combinedData[i].groupId, + label: combinedData[i].label + }; + Points.draw([pointData], group, framework, drawData.offset); //DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); } } @@ -137,16 +157,21 @@ Bargraph._getDataIntersections = function (intersections, combinedData) { var coreDistance; for (var i = 0; i < combinedData.length; i++) { if (i + 1 < combinedData.length) { - coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); + coreDistance = Math.abs(combinedData[i + 1].screen_x - combinedData[i].screen_x); } if (i > 0) { - coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); + coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].screen_x - combinedData[i].screen_x)); } if (coreDistance === 0) { - if (intersections[combinedData[i].x] === undefined) { - intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulatedPositive: 0, accumulatedNegative: 0}; + if (intersections[combinedData[i].screen_x] === undefined) { + intersections[combinedData[i].screen_x] = { + amount: 0, + resolved: 0, + accumulatedPositive: 0, + accumulatedNegative: 0 + }; } - intersections[combinedData[i].x].amount += 1; + intersections[combinedData[i].screen_x].amount += 1; } } }; @@ -164,7 +189,7 @@ Bargraph._getDataIntersections = function (intersections, combinedData) { Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { var width, offset; if (coreDistance < group.options.barChart.width && coreDistance > 0) { - width = coreDistance < minWidth ? minWidth : coreDistance; + width = coreDistance < minWidth ? minWidth : coreDistance offset = 0; // recalculate offset with the new width; if (group.options.barChart.align === 'left') { @@ -189,15 +214,15 @@ Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { return {width: width, offset: offset}; }; -Bargraph.getStackedYRange = function(combinedData, groupRanges, groupIds, groupLabel, orientation) { +Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) { if (combinedData.length > 0) { // sort by time and by group combinedData.sort(function (a, b) { - if (a.x === b.x) { + if (a.screen_x === b.screen_x) { return a.groupId < b.groupId ? -1 : 1; - } + } else { - return a.x - b.x; + return a.screen_x - b.screen_x; } }); var intersections = {}; @@ -211,20 +236,20 @@ Bargraph.getStackedYRange = function(combinedData, groupRanges, groupIds, groupL Bargraph._getStackedYRange = function (intersections, combinedData) { var key; - var yMin = combinedData[0].y; - var yMax = combinedData[0].y; + var yMin = combinedData[0].screen_y; + var yMax = combinedData[0].screen_y; for (var i = 0; i < combinedData.length; i++) { - key = combinedData[i].x; + key = combinedData[i].screen_x; if (intersections[key] === undefined) { - yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; - yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; + yMin = yMin > combinedData[i].screen_y ? combinedData[i].screen_y : yMin; + yMax = yMax < combinedData[i].screen_y ? combinedData[i].screen_y : yMax; } else { - if (combinedData[i].y < 0) { - intersections[key].accumulatedNegative += combinedData[i].y; + if (combinedData[i].screen_y < 0) { + intersections[key].accumulatedNegative += combinedData[i].screen_y; } else { - intersections[key].accumulatedPositive += combinedData[i].y; + intersections[key].accumulatedPositive += combinedData[i].screen_y; } } } diff --git a/lib/vis/lib/timeline/component/graph2d_types/line.js b/lib/vis/lib/timeline/component/graph2d_types/line.js index 7d70e8d313..18b9877b45 100644 --- a/lib/vis/lib/timeline/component/graph2d_types/line.js +++ b/lib/vis/lib/timeline/component/graph2d_types/line.js @@ -1,109 +1,112 @@ var DOMutil = require('../../../DOMutil'); -var Points = require('./points'); function Line(groupId, options) { - this.groupId = groupId; - this.options = options; } -Line.prototype.getData = function(groupData) { - var combinedData = []; - for (var j = 0; j < groupData.length; j++) { - combinedData.push({ - x: groupData[j].x, - y: groupData[j].y, - groupId: this.groupId - }); - } - return combinedData; +Line.calcPath = function (dataset, group) { + if (dataset != null) { + if (dataset.length > 0) { + var d = []; + + // construct path from dataset + if (group.options.interpolation.enabled == true) { + d = Line._catmullRom(dataset, group); + } + else { + d = Line._linear(dataset); + } + return d; + } + } } -Line.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; -}; +Line.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; -Line.getStackedYRange = function(combinedData, groupRanges, groupIds, groupLabel, orientation) { - if (combinedData.length > 0) { - // sort by time and by group - combinedData.sort(function (a, b) { - if (a.x === b.x) { - return a.groupId < b.groupId ? -1 : 1; - } - else { - return a.x - b.x; - } - }); - var intersections = {}; + var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2 * fillHeight); + outline.setAttributeNS(null, "class", "vis-outline"); - Line._getDataIntersections(intersections, combinedData); - groupRanges[groupLabel] = Line._getStackedYRange(intersections, combinedData); - groupRanges[groupLabel].yAxisOrientation = orientation; - groupIds.push(groupLabel); - } + path = DOMutil.getSVGElement("path", framework.svgElements, framework.svg); + path.setAttributeNS(null, "class", group.className); + if (group.style !== undefined) { + path.setAttributeNS(null, "style", group.style); + } + + path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + ""); + if (group.options.shaded.enabled == true) { + fillPath = DOMutil.getSVGElement("path", framework.svgElements, framework.svg); + if (group.options.shaded.orientation == 'top') { + fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight)); + } + else { + fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + + "L" + x + "," + (y + fillHeight) + " " + + "L" + (x + iconWidth) + "," + (y + fillHeight) + + "L" + (x + iconWidth) + "," + y); + } + fillPath.setAttributeNS(null, "class", group.className + " vis-icon-fill"); + if (group.options.shaded.style !== undefined && group.options.shaded.style !== "") { + fillPath.setAttributeNS(null, "style", group.options.shaded.style); + } + } + + if (group.options.drawPoints.enabled == true) { + var groupTemplate = { + style: group.options.drawPoints.style, + styles: group.options.drawPoints.styles, + size: group.options.drawPoints.size, + className: group.className + }; + DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, framework.svgElements, framework.svg); + } } -Line._getStackedYRange = function (intersections, combinedData) { - var key; - var yMin = combinedData[0].y; - var yMax = combinedData[0].y; - for (var i = 0; i < combinedData.length; i++) { - key = combinedData[i].x; - if (intersections[key] === undefined) { - yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; - yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; - } - else { - if (combinedData[i].y < 0) { - intersections[key].accumulatedNegative += combinedData[i].y; - } - else { - intersections[key].accumulatedPositive += combinedData[i].y; - } - } - } - for (var xpos in intersections) { - if (intersections.hasOwnProperty(xpos)) { - yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin; - yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin; - yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax; - yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax; - } - } +Line.drawShading = function (pathArray, group, subPathArray, framework) { + // append shading to the path + if (group.options.shaded.enabled == true) { + var svgHeight = Number(framework.svg.style.height.replace('px','')); + var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + var type = "L"; + if (group.options.interpolation.enabled == true){ + type = "C"; + } + var dFill; + var zero = 0; + if (group.options.shaded.orientation == 'top') { + zero = 0; + } + else if (group.options.shaded.orientation == 'bottom') { + zero = svgHeight; + } + else { + zero = Math.min(Math.max(0, group.zeroPosition), svgHeight); + } + if (group.options.shaded.orientation == 'group' && (subPathArray != null && subPathArray != undefined)) { + dFill = 'M' + pathArray[0][0]+ ","+pathArray[0][1] + " " + + this.serializePath(pathArray,type,false) + + ' L'+ subPathArray[subPathArray.length-1][0]+ "," + subPathArray[subPathArray.length-1][1] + " " + + this.serializePath(subPathArray,type,true) + + subPathArray[0][0]+ ","+subPathArray[0][1] + " Z"; + } + else { + dFill = 'M' + pathArray[0][0]+ ","+pathArray[0][1] + " " + + this.serializePath(pathArray,type,false) + + ' V' + zero + ' H'+ pathArray[0][0] + " Z"; + } - return {min: yMin, max: yMax}; -}; - -/** - * Fill the intersections object with counters of how many datapoints share the same x coordinates - * @param intersections - * @param combinedData - * @private - */ -Line._getDataIntersections = function (intersections, combinedData) { - // get intersections - var coreDistance; - for (var i = 0; i < combinedData.length; i++) { - if (i + 1 < combinedData.length) { - coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); + fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill'); + if (group.options.shaded.style !== undefined) { + fillPath.setAttributeNS(null, 'style', group.options.shaded.style); + } + fillPath.setAttributeNS(null, 'd', dFill); } - if (i > 0) { - coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); - } - if (coreDistance === 0) { - if (intersections[combinedData[i].x] === undefined) { - intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulatedPositive: 0, accumulatedNegative: 0}; - } - intersections[combinedData[i].x].amount += 1; - } - } -}; - +} /** * draw a line graph @@ -111,53 +114,41 @@ Line._getDataIntersections = function (intersections, combinedData) { * @param dataset * @param group */ -Line.prototype.draw = function (dataset, group, framework) { - if (dataset != null) { - if (dataset.length > 0) { - var path, d; - var svgHeight = Number(framework.svg.style.height.replace('px','')); - path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, "class", group.className); - if(group.style !== undefined) { - path.setAttributeNS(null, "style", group.style); - } - - // construct path from dataset - if (group.options.interpolation.enabled == true) { - d = Line._catmullRom(dataset, group); - } - else { - d = Line._linear(dataset); - } - - // append with points for fill and finalize the path - if (group.options.shaded.enabled == true) { - var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - var dFill; - if (group.options.shaded.orientation == 'top') { - dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; +Line.draw = function (pathArray, group, framework) { + if (pathArray != null && pathArray != undefined) { + var path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + path.setAttributeNS(null, "class", group.className); + if (group.style !== undefined) { + path.setAttributeNS(null, "style", group.style); } - else { - dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; - } - fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill'); - if(group.options.shaded.style !== undefined) { - fillPath.setAttributeNS(null, 'style', group.options.shaded.style); - } - fillPath.setAttributeNS(null, 'd', dFill); - } - // copy properties to path for drawing. - path.setAttributeNS(null, 'd', 'M' + d); - // draw points - if (group.options.drawPoints.enabled == true) { - Points.draw(dataset, group, framework); - } + var type = "L"; + if (group.options.interpolation.enabled == true){ + type = "C"; + } + // copy properties to path for drawing. + path.setAttributeNS(null, 'd', 'M' + pathArray[0][0]+ ","+pathArray[0][1] + " " + this.serializePath(pathArray,type,false)); } - } }; - +Line.serializePath = function(pathArray,type,inverse){ + if (pathArray.length < 2){ + //Too little data to create a path. + return ""; + } + var d = type; + if (inverse){ + for (var i = pathArray.length-2; i > 0; i--){ + d += pathArray[i][0] + "," + pathArray[i][1] + " "; + } + } + else { + for (var i = 1; i < pathArray.length; i++){ + d += pathArray[i][0] + "," + pathArray[i][1] + " "; + } + } + return d; +} /** * This uses an uniform parametrization of the interpolation algorithm: @@ -166,41 +157,44 @@ Line.prototype.draw = function (dataset, group, framework) { * @returns {string} * @private */ -Line._catmullRomUniform = function(data) { - // catmull rom - var p0, p1, p2, p3, bp1, bp2; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var normalization = 1/6; - var length = data.length; - for (var i = 0; i < length - 1; i++) { +Line._catmullRomUniform = function (data) { + // catmull rom + var p0, p1, p2, p3, bp1, bp2; + var d = []; + d.push( [ Math.round(data[0].screen_x) , Math.round(data[0].screen_y) ]); + var normalization = 1 / 6; + var length = data.length; + for (var i = 0; i < length - 1; i++) { - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + p0 = (i == 0) ? data[0] : data[i - 1]; + p1 = data[i]; + p2 = data[i + 1]; + p3 = (i + 2 < length) ? data[i + 2] : p2; - // Catmull-Rom to Cubic Bezier conversion matrix - // 0 1 0 0 - // -1/6 1 1/6 0 - // 0 1/6 1 -1/6 - // 0 0 1 0 + // Catmull-Rom to Cubic Bezier conversion matrix + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 - // bp0 = { x: p1.x, y: p1.y }; - bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; - bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; - // bp0 = { x: p2.x, y: p2.y }; + // bp0 = { x: p1.x, y: p1.y }; + bp1 = { + screen_x: ((-p0.screen_x + 6 * p1.screen_x + p2.screen_x) * normalization), + screen_y: ((-p0.screen_y + 6 * p1.screen_y + p2.screen_y) * normalization) + }; + bp2 = { + screen_x: (( p1.screen_x + 6 * p2.screen_x - p3.screen_x) * normalization), + screen_y: (( p1.screen_y + 6 * p2.screen_y - p3.screen_y) * normalization) + }; + // bp0 = { x: p2.x, y: p2.y }; - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; - } + d.push( [ bp1.screen_x , bp1.screen_y ]); + d.push( [ bp2.screen_x , bp2.screen_y ]); + d.push( [ p2.screen_x , p2.screen_y ]); + } - return d; + return d; }; /** @@ -214,70 +208,79 @@ Line._catmullRomUniform = function(data) { * @returns {string} * @private */ -Line._catmullRom = function(data, group) { - var alpha = group.options.interpolation.alpha; - if (alpha == 0 || alpha === undefined) { - return this._catmullRomUniform(data); - } - else { - var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; - var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var length = data.length; - for (var i = 0; i < length - 1; i++) { - - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; - - d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); - d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); - d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); - - // Catmull-Rom to Cubic Bezier conversion matrix - - // A = 2d1^2a + 3d1^a * d2^a + d3^2a - // B = 2d3^2a + 3d3^a * d2^a + d2^2a - - // [ 0 1 0 0 ] - // [ -d2^2a /N A/N d1^2a /N 0 ] - // [ 0 d3^2a /M B/M -d2^2a /M ] - // [ 0 0 1 0 ] - - d3powA = Math.pow(d3, alpha); - d3pow2A = Math.pow(d3,2*alpha); - d2powA = Math.pow(d2, alpha); - d2pow2A = Math.pow(d2,2*alpha); - d1powA = Math.pow(d1, alpha); - d1pow2A = Math.pow(d1,2*alpha); - - A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; - B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; - N = 3*d1powA * (d1powA + d2powA); - if (N > 0) {N = 1 / N;} - M = 3*d3powA * (d3powA + d2powA); - if (M > 0) {M = 1 / M;} - - bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), - y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; - - bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), - y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; - - if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} - if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; +Line._catmullRom = function (data, group) { + var alpha = group.options.interpolation.alpha; + if (alpha == 0 || alpha === undefined) { + return this._catmullRomUniform(data); } + else { + var p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M; + var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; + var d = []; + d.push( [ Math.round(data[0].screen_x) , Math.round(data[0].screen_y) ]); + var length = data.length; + for (var i = 0; i < length - 1; i++) { - return d; - } + p0 = (i == 0) ? data[0] : data[i - 1]; + p1 = data[i]; + p2 = data[i + 1]; + p3 = (i + 2 < length) ? data[i + 2] : p2; + + d1 = Math.sqrt(Math.pow(p0.screen_x - p1.screen_x, 2) + Math.pow(p0.screen_y - p1.screen_y, 2)); + d2 = Math.sqrt(Math.pow(p1.screen_x - p2.screen_x, 2) + Math.pow(p1.screen_y - p2.screen_y, 2)); + d3 = Math.sqrt(Math.pow(p2.screen_x - p3.screen_x, 2) + Math.pow(p2.screen_y - p3.screen_y, 2)); + + // Catmull-Rom to Cubic Bezier conversion matrix + + // A = 2d1^2a + 3d1^a * d2^a + d3^2a + // B = 2d3^2a + 3d3^a * d2^a + d2^2a + + // [ 0 1 0 0 ] + // [ -d2^2a /N A/N d1^2a /N 0 ] + // [ 0 d3^2a /M B/M -d2^2a /M ] + // [ 0 0 1 0 ] + + d3powA = Math.pow(d3, alpha); + d3pow2A = Math.pow(d3, 2 * alpha); + d2powA = Math.pow(d2, alpha); + d2pow2A = Math.pow(d2, 2 * alpha); + d1powA = Math.pow(d1, alpha); + d1pow2A = Math.pow(d1, 2 * alpha); + + A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A; + B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A; + N = 3 * d1powA * (d1powA + d2powA); + if (N > 0) { + N = 1 / N; + } + M = 3 * d3powA * (d3powA + d2powA); + if (M > 0) { + M = 1 / M; + } + + bp1 = { + screen_x: ((-d2pow2A * p0.screen_x + A * p1.screen_x + d1pow2A * p2.screen_x) * N), + screen_y: ((-d2pow2A * p0.screen_y + A * p1.screen_y + d1pow2A * p2.screen_y) * N) + }; + + bp2 = { + screen_x: (( d3pow2A * p1.screen_x + B * p2.screen_x - d2pow2A * p3.screen_x) * M), + screen_y: (( d3pow2A * p1.screen_y + B * p2.screen_y - d2pow2A * p3.screen_y) * M) + }; + + if (bp1.screen_x == 0 && bp1.screen_y == 0) { + bp1 = p1; + } + if (bp2.screen_x == 0 && bp2.screen_y == 0) { + bp2 = p2; + } + d.push( [ bp1.screen_x , bp1.screen_y ]); + d.push( [ bp2.screen_x , bp2.screen_y ]); + d.push( [ p2.screen_x , p2.screen_y ]); + } + + return d; + } }; /** @@ -286,18 +289,13 @@ Line._catmullRom = function(data, group) { * @returns {string} * @private */ -Line._linear = function(data) { - // linear - var d = ''; - for (var i = 0; i < data.length; i++) { - if (i == 0) { - d += data[i].x + ',' + data[i].y; +Line._linear = function (data) { + // linear + var d = []; + for (var i = 0; i < data.length; i++) { + d.push([ data[i].screen_x , data[i].screen_y ]); } - else { - d += ' ' + data[i].x + ',' + data[i].y; - } - } - return d; + return d; }; module.exports = Line; diff --git a/lib/vis/lib/timeline/component/graph2d_types/points.js b/lib/vis/lib/timeline/component/graph2d_types/points.js index f407fddc50..c6a05a9255 100644 --- a/lib/vis/lib/timeline/component/graph2d_types/points.js +++ b/lib/vis/lib/timeline/component/graph2d_types/points.js @@ -1,25 +1,8 @@ var DOMutil = require('../../../DOMutil'); function Points(groupId, options) { - this.groupId = groupId; - this.options = options; } - -Points.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; -}; - -Points.prototype.draw = function(dataset, group, framework, offset) { - Points.draw(dataset, group, framework, offset); -}; - /** * draw the data points * @@ -31,45 +14,60 @@ Points.prototype.draw = function(dataset, group, framework, offset) { */ Points.draw = function (dataset, group, framework, offset) { offset = offset || 0; - var callback = getCallback(); + var callback = getCallback(framework, group); for (var i = 0; i < dataset.length; i++) { if (!callback) { - // draw the point the simple way. - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, getGroupTemplate(), framework.svgElements, framework.svg, dataset[i].label); - } else { - var callbackResult = callback(dataset[i], group, framework); // result might be true, false or an object - if (callbackResult === true || typeof callbackResult === 'object') { - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, getGroupTemplate(callbackResult), framework.svgElements, framework.svg, dataset[i].label); - } + // draw the point the simple way. + DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group), framework.svgElements, framework.svg, dataset[i].label); } - } - - function getGroupTemplate(callbackResult) { - callbackResult = (typeof callbackResult === 'undefined') ? {} : callbackResult; - return { - style: callbackResult.style || group.options.drawPoints.style, - size: callbackResult.size || group.options.drawPoints.size, - className: callbackResult.className || group.className - }; - } - - function getCallback() { - var callback = undefined; - // check for the graph2d onRender - if (framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') { - callback = framework.options.drawPoints.onRender; + else { + var callbackResult = callback(dataset[i], group); // result might be true, false or an object + if (callbackResult === true || typeof callbackResult === 'object') { + DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group, callbackResult), framework.svgElements, framework.svg, dataset[i].label); } - - // override it with the group onRender if defined - if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') { - callback = group.group.options.drawPoints.onRender; - } - - return callback; + } } }; +Points.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; + + var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2 * fillHeight); + outline.setAttributeNS(null, "class", "vis-outline"); + + //Don't call callback on icon + DOMutil.drawPoint(x + 0.5 * iconWidth, y, getGroupTemplate(group), framework.svgElements, framework.svg); +}; + +function getGroupTemplate(group, callbackResult) { + callbackResult = (typeof callbackResult === 'undefined') ? {} : callbackResult; + return { + style: callbackResult.style || group.options.drawPoints.style, + styles: callbackResult.styles || group.options.drawPoints.styles, + size: callbackResult.size || group.options.drawPoints.size, + className: callbackResult.className || group.className + }; +} + +function getCallback(framework, group) { + var callback = undefined; + // check for the graph2d onRender + if (framework.options && framework.options.drawPoints && framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') { + callback = framework.options.drawPoints.onRender; + } + + // override it with the group onRender if defined + if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') { + callback = group.group.options.drawPoints.onRender; + } + return callback; +} module.exports = Points; \ No newline at end of file diff --git a/lib/vis/lib/timeline/component/item/PointItem.js b/lib/vis/lib/timeline/component/item/PointItem.js index 8f22ad993b..6238e09ef2 100644 --- a/lib/vis/lib/timeline/component/item/PointItem.js +++ b/lib/vis/lib/timeline/component/item/PointItem.js @@ -120,13 +120,14 @@ PointItem.prototype.redraw = function() { dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; //dom.content.style.marginRight = ... + 'px'; // TODO: margin right - dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; - // recalculate size this.width = dom.point.offsetWidth; this.height = dom.point.offsetHeight; + // reposition the dot + dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + this.dirty = false; } diff --git a/lib/vis/lib/timeline/optionsGraph2d.js b/lib/vis/lib/timeline/optionsGraph2d.js index 5ea43cc806..fb8e04557f 100644 --- a/lib/vis/lib/timeline/optionsGraph2d.js +++ b/lib/vis/lib/timeline/optionsGraph2d.js @@ -33,12 +33,14 @@ let allOptions = { graphHeight: {string, number}, shaded: { enabled: {boolean}, - orientation: {string:['bottom','top']}, // top, bottom + orientation: {string:['bottom','top','zero','group']}, // top, bottom, zero, group + groupId: {object}, __type__: {boolean,object} }, style: {string:['line','bar','points']}, // line, bar barChart: { width: {number}, + minWidth: {number}, sideBySide: {boolean}, align: {string:['left','center','right']}, __type__: {object} @@ -98,6 +100,7 @@ let allOptions = { }, autoResize: {boolean}, + throttleRedraw: {number}, clickToUse: {boolean}, end: {number, date, string, moment}, format: { @@ -127,7 +130,12 @@ let allOptions = { }, moment: {'function': 'function'}, height: {string, number}, - hiddenDates: {object, array}, + hiddenDates: { + start: {date, number, string, moment}, + end: {date, number, string, moment}, + repeat: {string}, + __type__: {object, array} + }, locale:{string}, locales:{ __any__: {any}, @@ -135,6 +143,7 @@ let allOptions = { }, max: {date, number, string, moment}, maxHeight: {number, string}, + maxMinorChars: {number}, min: {date, number, string, moment}, minHeight: {number, string}, moveable: {boolean}, @@ -154,6 +163,7 @@ let allOptions = { zoomKey: {string: ['ctrlKey', 'altKey', 'metaKey', '']}, zoomMax: {number}, zoomMin: {number}, + zIndex: {number}, __type__: {object} }; @@ -165,11 +175,12 @@ let configureOptions = { stack:false, shaded: { enabled: false, - orientation: ['top','bottom'] // top, bottom + orientation: ['zero','top','bottom','group'] // zero, top, bottom }, style: ['line','bar','points'], // line, bar barChart: { width: [50,5,100,5], + minWidth: [50,5,100,5], sideBySide: false, align: ['left','center','right'] // left, center, right }, @@ -214,6 +225,7 @@ let configureOptions = { }, autoResize: true, + throttleRedraw: [10, 0, 1000, 10], clickToUse: false, end: '', format: { @@ -243,6 +255,7 @@ let configureOptions = { locale: '', max: '', maxHeight: '', + maxMinorChars: [7, 0, 20, 1], min: '', minHeight: '', moveable:true, @@ -255,8 +268,9 @@ let configureOptions = { zoomable: true, zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''], zoomMax: [315360000000000, 10, 315360000000000, 1], - zoomMin: [10, 10, 315360000000000, 1] + zoomMin: [10, 10, 315360000000000, 1], + zIndex: 0 } }; -export {allOptions, configureOptions}; \ No newline at end of file +export {allOptions, configureOptions}; diff --git a/lib/vis/lib/timeline/optionsTimeline.js b/lib/vis/lib/timeline/optionsTimeline.js index edfb455468..acf93a17e4 100644 --- a/lib/vis/lib/timeline/optionsTimeline.js +++ b/lib/vis/lib/timeline/optionsTimeline.js @@ -27,6 +27,7 @@ let allOptions = { //globals : align: {string}, autoResize: {boolean}, + throttleRedraw: {number}, clickToUse: {boolean}, dataAttributes: {string, array}, editable: { @@ -64,8 +65,21 @@ let allOptions = { }, moment: {'function': 'function'}, groupOrder: {string, 'function': 'function'}, + groupEditable: { + add: {boolean, 'undefined': 'undefined'}, + remove: {boolean, 'undefined': 'undefined'}, + order: {boolean, 'undefined': 'undefined'}, + __type__: {boolean, object} + }, + groupOrderSwap: {'function': 'function'}, height: {string, number}, - hiddenDates: {object, array}, + hiddenDates: { + start: {date, number, string, moment}, + end: {date, number, string, moment}, + repeat: {string}, + __type__: {object, array} + }, + itemsAlwaysDraggable: { boolean: boolean }, locale:{string}, locales:{ __any__: {any}, @@ -82,15 +96,20 @@ let allOptions = { }, max: {date, number, string, moment}, maxHeight: {number, string}, + maxMinorChars: {number}, min: {date, number, string, moment}, minHeight: {number, string}, moveable: {boolean}, multiselect: {boolean}, + multiselectPerGroup: {boolean}, onAdd: {'function': 'function'}, onUpdate: {'function': 'function'}, onMove: {'function': 'function'}, onMoving: {'function': 'function'}, onRemove: {'function': 'function'}, + onAddGroup: {'function': 'function'}, + onMoveGroup: {'function': 'function'}, + onRemoveGroup: {'function': 'function'}, order: {'function': 'function'}, orientation: { axis: {string,'undefined': 'undefined'}, @@ -125,6 +144,7 @@ let configureOptions = { global: { align: ['center', 'left', 'right'], autoResize: true, + throttleRedraw: [10, 0, 1000, 10], clickToUse: false, // dataAttributes: ['all'], // FIXME: can be 'all' or string[] editable: { @@ -158,6 +178,7 @@ let configureOptions = { }, //groupOrder: {string, 'function': 'function'}, + groupsDraggable: false, height: '', //hiddenDates: {object, array}, locale: '', @@ -170,10 +191,12 @@ let configureOptions = { }, max: '', maxHeight: '', + maxMinorChars: [7, 0, 20, 1], min: '', minHeight: '', moveable: false, multiselect: false, + multiselectPerGroup: false, //onAdd: {'function': 'function'}, //onUpdate: {'function': 'function'}, //onMove: {'function': 'function'}, diff --git a/lib/vis/lib/util.js b/lib/vis/lib/util.js index 47960085e5..a433075ad8 100644 --- a/lib/vis/lib/util.js +++ b/lib/vis/lib/util.js @@ -232,7 +232,12 @@ exports.selectiveDeepExtend = function (props, a, b, allowDeletion = false) { } else if (Array.isArray(b[prop])) { throw new TypeError('Arrays are not supported by deepExtend'); } else { - a[prop] = b[prop]; + if ((b[prop] === null) && a[prop] !== undefined && allowDeletion === true) { + delete a[prop]; + } + else { + a[prop] = b[prop]; + } } } @@ -278,7 +283,12 @@ exports.selectiveNotDeepExtend = function (props, a, b, allowDeletion = false) { a[prop].push(b[prop][i]); } } else { - a[prop] = b[prop]; + if ((b[prop] === null) && a[prop] !== undefined && allowDeletion === true) { + delete a[prop]; + } + else { + a[prop] = b[prop]; + } } } } @@ -319,7 +329,12 @@ exports.deepExtend = function (a, b, protoExtend, allowDeletion) { a[prop].push(b[prop][i]); } } else { - a[prop] = b[prop]; + if ((b[prop] === null) && a[prop] !== undefined && allowDeletion === true) { + delete a[prop]; + } + else { + a[prop] = b[prop]; + } } } } @@ -663,7 +678,7 @@ exports.toArray = function (object) { } return array; -} +}; /** * Update a property in an object @@ -682,6 +697,34 @@ exports.updateProperty = function (object, key, value) { } }; +/** + * Throttle the given function to be only executed once every `wait` milliseconds + * @param {function} fn + * @param {number} wait Time in milliseconds + * @returns {function} Returns the throttled function + */ +exports.throttle = function (fn, wait) { + var timeout = null; + var needExecution = false; + + return function throttled () { + if (!timeout) { + needExecution = false; + fn(); + + timeout = setTimeout(function() { + timeout = null; + if (needExecution) { + throttled(); + } + }, wait) + } + else { + needExecution = true; + } + } +}; + /** * Add and event listener. Works for all browsers * @param {Element} element An html element @@ -1210,6 +1253,23 @@ exports.bridgeObject = function (referenceObject) { } }; +/** + * This method provides a stable sort implementation, very fast for presorted data + * + * @param a the array + * @param a order comparator + * @returns {the array} + */ +exports.insertSort = function (a,compare) { + for (var i = 0; i < a.length; i++) { + var k = a[i]; + for (var j = i; j > 0 && compare(k,a[j - 1])<0; j--) { + a[j] = a[j - 1]; + } + a[j] = k; + } + return a; +} /** * this is used to set the options of subobjects in the options object. A requirement of these subobjects @@ -1218,12 +1278,10 @@ exports.bridgeObject = function (referenceObject) { * @param [object] mergeTarget | this is either this.options or the options used for the groups. * @param [object] options | options * @param [String] option | this is the option key in the options argument - * @private */ -exports.mergeOptions = function (mergeTarget, options, option, allowDeletion = false) { +exports.mergeOptions = function (mergeTarget, options, option, allowDeletion = false, globalOptions = {}) { if (options[option] === null) { - mergeTarget[option] = undefined; - delete mergeTarget[option]; + mergeTarget[option] = Object.create(globalOptions[option]); } else { if (options[option] !== undefined) { @@ -1250,13 +1308,13 @@ exports.mergeOptions = function (mergeTarget, options, option, allowDeletion = f * this function will then iterate in both directions over this sorted list to find all visible items. * * @param {Item[]} orderedItems | Items ordered by start - * @param {function} searchFunction | -1 is lower, 0 is found, 1 is higher + * @param {function} comparator | -1 is lower, 0 is equal, 1 is higher * @param {String} field * @param {String} field2 * @returns {number} * @private */ -exports.binarySearchCustom = function (orderedItems, searchFunction, field, field2) { +exports.binarySearchCustom = function (orderedItems, comparator, field, field2) { var maxIterations = 10000; var iteration = 0; var low = 0; @@ -1268,7 +1326,7 @@ exports.binarySearchCustom = function (orderedItems, searchFunction, field, fiel var item = orderedItems[middle]; var value = (field2 === undefined) ? item[field] : item[field][field2]; - var searchResult = searchFunction(value); + var searchResult = comparator(value); if (searchResult == 0) { // jihaa, found a visible item! return middle; } @@ -1294,16 +1352,21 @@ exports.binarySearchCustom = function (orderedItems, searchFunction, field, fiel * @param {{start: number, end: number}} target * @param {String} field * @param {String} sidePreference 'before' or 'after' + * @param {function} comparator an optional comparator, returning -1,0,1 for <,==,>. * @returns {number} * @private */ -exports.binarySearchValue = function (orderedItems, target, field, sidePreference) { +exports.binarySearchValue = function (orderedItems, target, field, sidePreference, comparator) { var maxIterations = 10000; var iteration = 0; var low = 0; var high = orderedItems.length - 1; var prevValue, value, nextValue, middle; + var comparator = comparator != undefined ? comparator : function (a, b) { + return a == b ? 0 : a < b ? -1 : 1 + }; + while (low <= high && iteration < maxIterations) { // get a new guess middle = Math.floor(0.5 * (high + low)); @@ -1311,17 +1374,17 @@ exports.binarySearchValue = function (orderedItems, target, field, sidePreferenc value = orderedItems[middle][field]; nextValue = orderedItems[Math.min(orderedItems.length - 1, middle + 1)][field]; - if (value == target) { // we found the target + if (comparator(value, target) == 0) { // we found the target return middle; } - else if (prevValue < target && value > target) { // target is in between of the previous and the current + else if (comparator(prevValue, target) < 0 && comparator(value, target) > 0) { // target is in between of the previous and the current return sidePreference == 'before' ? Math.max(0, middle - 1) : middle; } - else if (value < target && nextValue > target) { // target is in between of the current and the next + else if (comparator(value, target) < 0 && comparator(nextValue, target) > 0) { // target is in between of the current and the next return sidePreference == 'before' ? middle : Math.min(orderedItems.length - 1, middle + 1); } else { // didnt find the target, we need to change our boundaries. - if (value < target) { // it is too small --> increase low + if (comparator(value, target) < 0) { // it is too small --> increase low low = middle + 1; } else { // it is too big --> decrease high diff --git a/lib/vis/misc/how_to_publish.md b/lib/vis/misc/how_to_publish.md index 2a53de25cb..38492d76cf 100644 --- a/lib/vis/misc/how_to_publish.md +++ b/lib/vis/misc/how_to_publish.md @@ -5,7 +5,7 @@ This document describes how to publish vis.js. ## Build -- Change the version number of the library in both `package.json` and `bower.json`. +- Change the version number of the library in `package.json`. - Open `HISTORY.md`, write down the changes, version number, and release date. - Build the library by running: @@ -76,7 +76,7 @@ This generates the vis.js library in the folder `./dist`. - Update the library version number in the index.html page. -- Update the CDN links at the download section of index.html AND the CDN link at the top. +- Update the CDN links at the download section of index.html AND the CDN link at the top. (replace all) - Commit the changes in the `gh-pages` branch. diff --git a/lib/vis/package.json b/lib/vis/package.json index f3dc995fff..c33714425c 100644 --- a/lib/vis/package.json +++ b/lib/vis/package.json @@ -1,6 +1,6 @@ { "name": "vis", - "version": "4.7.0", + "version": "4.14.0", "description": "A dynamic, browser-based visualization library.", "homepage": "http://visjs.org/", "license": "(Apache-2.0 OR MIT)", @@ -21,22 +21,21 @@ "network", "browser" ], - "main": "./index", + "main": "./dist/vis.js", "scripts": { "test": "mocha", "build": "gulp", "watch": "gulp watch", "watch-dev": "gulp watch --bundle" }, - "dependencies": { - "emitter-component": "^1.1.1", - "hammerjs": "^2.0.4", - "keycharm": "^0.2.0", - "uuid": "^2.0.1", - "moment": "^2.10.2", - "propagating-hammerjs": "^1.4.3" - }, + "dependencies": {}, "devDependencies": { + "emitter-component": "^1.1.1", + "hammerjs": "^2.0.6", + "keycharm": "^0.2.0", + "moment": "^2.10.2", + "propagating-hammerjs": "^1.4.5", + "uuid": "^2.0.1", "babel": "^5.1.11", "babel-loader": "^5.0.0", "babelify": "^6.0.2", @@ -52,8 +51,5 @@ "uglify-js": "^2.4.20", "webpack": "^1.8.5", "yargs": "^3.7.2" - }, - "browserify": { - "transform": ["babelify"] } } diff --git a/lib/vis/test/DataView.test.js b/lib/vis/test/DataView.test.js index 8a26752860..4d3c5f97ee 100644 --- a/lib/vis/test/DataView.test.js +++ b/lib/vis/test/DataView.test.js @@ -177,15 +177,27 @@ describe('DataView', function () { // make a change not affecting the DataView data.update({id: 1, title: 'Item 1 (changed)'}); assert.deepEqual(dataUpdates, [ - ['update', {items: [1], data: [{id: 1, title: 'Item 1 (changed)'}]}] + ['update', { + items: [1], + data: [{id: 1, title: 'Item 1 (changed)'}], + oldData: [{"group": 1, "id": 1, "title": "Item 1"}] + }] ]); assert.deepEqual(viewUpdates, []); // make a change affecting the DataView data.update({id: 2, title: 'Item 2 (changed)'}); assert.deepEqual(dataUpdates, [ - ['update', {items: [1], data: [{id: 1, title: 'Item 1 (changed)'}]}], - ['update', {items: [2], data: [{id: 2, title: 'Item 2 (changed)'}]}] + ['update', { + items: [1], + data: [{id: 1, title: 'Item 1 (changed)'}], + oldData: [{"group": 1, "id": 1, "title": "Item 1"}] + }], + ['update', { + items: [2], + data: [{id: 2, title: 'Item 2 (changed)'}], + oldData: [{"group": 2, "id": 2, "title": "Item 2"}] + }] ]); assert.deepEqual(viewUpdates, [ ['update', {items: [2], data: [{id: 2, title: 'Item 2 (changed)'}]}] diff --git a/lib/vis/test/networkTest.html b/lib/vis/test/networkTest.html index 18daec5eb2..a7ec8161ed 100644 --- a/lib/vis/test/networkTest.html +++ b/lib/vis/test/networkTest.html @@ -1,557 +1,68 @@ - + - Test - + + JS Bin + + - - - - - - - - -
-
- - +

Network Test

+
- + \ No newline at end of file diff --git a/lib/vis/test/network_unittests.html b/lib/vis/test/network_unittests.html index 1d89eb0526..2d3e2eaab3 100644 --- a/lib/vis/test/network_unittests.html +++ b/lib/vis/test/network_unittests.html @@ -188,6 +188,10 @@ var allOptions = vis.network.allOptions.allOptions; var testOptions = {}; constructOptions(allOptions, testOptions); + if (testOptions.physics === undefined) {testOptions.physics = {};} + if (testOptions.layout === undefined) {testOptions.layout = {};} + testOptions.physics.enabled = true; + testOptions.layout.improvedLayout = false; var failed = setTimeout(function () { console.error("FAILED", JSON.stringify(testOptions, null, 4)) }, 500); @@ -211,6 +215,7 @@ setTimeout(checkOptions, 100); }) console.log("now testing:",testOptions) + network.setOptions(testOptions); } } diff --git a/lib/vis/test/timeline.html b/lib/vis/test/timeline.html index 27e5ed25b1..9cc1f384b5 100644 --- a/lib/vis/test/timeline.html +++ b/lib/vis/test/timeline.html @@ -216,6 +216,8 @@ items.on('update', console.log.bind(console)); items.on('remove', console.log.bind(console)); + +// timeline.setOptions({timeAxis:{scale: 'minute', step: 5}}) \ No newline at end of file diff --git a/lib/vis/test/timeline_groups.html b/lib/vis/test/timeline_groups.html index aac1c2ca23..4df1b4330b 100644 --- a/lib/vis/test/timeline_groups.html +++ b/lib/vis/test/timeline_groups.html @@ -71,7 +71,9 @@ } // create a dataset with items - var items = new vis.DataSet(); + var items = new vis.DataSet({ + type: {start: 'Moment', end: 'Moment'} + }); for (var i = 0; i < itemCount; i++) { var start = now.clone().add(Math.random() * 200, 'hours'); var end = Math.random() > 0.5 ? start.clone().add(24, 'hours') : undefined; @@ -117,6 +119,7 @@ }, onMove: function (item, callback) { + console.log('onMove', item) if (confirm('Do you really want to move the item to\n' + 'start: ' + item.start + '\n' + 'end: ' + item.end + '?')) { diff --git a/mibs/avtech/ROOMALERT11E.MIB b/mibs/avtech/ROOMALERT11E.MIB new file mode 100644 index 0000000000..6dfb872189 --- /dev/null +++ b/mibs/avtech/ROOMALERT11E.MIB @@ -0,0 +1,693 @@ +-- +--defines ROOMALERT11E-MIB for SNMP agent extension of Room Alert 11E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.1.3 +--Date: 9 January 2014 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2014 AVTECH Software, Inc. +-- + +ROOMALERT11E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert11E OBJECT IDENTIFIER ::= { products 3 } +sensors OBJECT IDENTIFIER ::= { roomalert11E 1 } +traps OBJECT IDENTIFIER ::= { roomalert11E 2 } +thresholds OBJECT IDENTIFIER ::= { roomalert11E 3 } +channel1 OBJECT IDENTIFIER ::= { sensors 1 } +channel2 OBJECT IDENTIFIER ::= { sensors 2 } +channel3 OBJECT IDENTIFIER ::= { sensors 3 } +channel4 OBJECT IDENTIFIER ::= { sensors 4 } +channels OBJECT IDENTIFIER ::= { thresholds 1 } +channels1 OBJECT IDENTIFIER ::= { channels 1 } +channels2 OBJECT IDENTIFIER ::= { channels 2 } +channels3 OBJECT IDENTIFIER ::= { channels 3 } +channels4 OBJECT IDENTIFIER ::= { channels 4 } + +-- Room Alert 11E MIB +-- Parameters + +sensor1-1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { channel1 1 } + +sensor1-2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 2 (0 = OPEN, 1 = CLOSED)." +::= { channel1 2 } + +sensor1-3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 3 (0 = OPEN, 1 = CLOSED)." +::= { channel1 3 } + +sensor1-4 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 4 (0 = OPEN, 1 = CLOSED)." +::= { channel1 4 } + +sensor1-5 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 5 (0 = OPEN, 1 = CLOSED)." +::= { channel1 5 } + +sensor1-6 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 6 (0 = OPEN, 1 = CLOSED)." +::= { channel1 6 } + +sensor1-7 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 7 (0 = OPEN, 1 = CLOSED)." +::= { channel1 7 } + +sensor1-8 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for internal switch sensor 8 (0 = OPEN, 1 = CLOSED)." +::= { channel1 8 } + +switch-label-1 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 1" +::= { channel1 9 } + +switch-label-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 2" +::= { channel1 10 } + +switch-label-3 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 3" +::= { channel1 11 } + +switch-label-4 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 4" +::= { channel1 12 } + +switch-label-5 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 5" +::= { channel1 13 } + +switch-label-6 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 6" +::= { channel1 14 } + +switch-label-7 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 7" +::= { channel1 15 } + +switch-label-8 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 8" +::= { channel1 16 } + +sensor2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 2-1. If a temp or temp/humidity sensor is connected, value will be current temperature in Celsius." +::= { channel2 1 } + +sensor2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 2-2. If a temp or temp/humidity sensor is connected, value will be current temperature in Fahrenheit." +::= { channel2 2 } + +sensor2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 2-3. If temp/humidity sensor is connected, value will be current %RH." +::= { channel2 3 } + +sensor2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 2-4. If temp/humidity sensor is connected, value will be current Heat Index in Fahrenheit." +::= { channel2 4 } + +sensor2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 2-5. If temp/humidity sensor is connected, value will be current Heat Index in Celsius." +::= { channel2 5 } + +sensor2-6-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= { channel2 6} + +sensor3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 3-1. If a temp or temp/humidity sensor is connected, value will be current temperature in Celsius." +::= { channel3 1 } + +sensor3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 3-2. If a temp or temp/humidity sensor is connected, value will be current temperature in Fahrenheit." +::= { channel3 2 } + +sensor3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 3-3. If temp/humidity sensor is connected, value will be current %RH." +::= { channel3 3 } + +sensor3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 3-4. If temp/humidity sensor is connected, value will be current Heat Index in Fahrenheit." +::= { channel3 4 } + +sensor3-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 3-5. If temp/humidity sensor is connected, value will be current Heat Index in Celsius." +::= { channel3 5 } + +sensor3-6-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= { channel3 6} + +sensor4-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 4-1. If a temp or temp/humidity sensor is connected, value will be current temperature in Celsius." +::= { channel4 1 } + +sensor4-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 4-2. If a temp or temp/humidity sensor is connected, value will be current temperature in Fahrenheit." +::= { channel4 2 } + +sensor4-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 4-3. If temp/humidity sensor is connected, value will be current %RH." +::= { channel4 3 } + +sensor4-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 4-4. If temp/humidity sensor is connected, value will be current Heat Index in Fahrenheit." +::= { channel4 4 } + +sensor4-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for sensor 4-5. If temp/humidity sensor is connected, value will be current Heat Index in Celsius." +::= { channel4 5 } + +sensor4-6-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= { channel4 6} + +alarm1 OBJECT-TYPE + SYNTAX INTEGER(0..255) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for sensor channel 1. The binary equivalent of the decimal number displayed will indicate which switch sensors are currently in an alarm state (0 = sensor ok, 1 = sensor in alarm state). For example, if switch sensors 1 and 4 are in alarm state, the value displayed will be 9, which equals 00001001 in binary." +::= { traps 1 } + +alarm2 OBJECT-TYPE + SYNTAX INTEGER(0..255) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for sensor channel 2. If a digital temperature/humidity or digital temperature sensor is connected to this channel, 0 = sensor ok, 1 = high temp alarm, 2 = low temp alarm, 3 = humidity alarm. If a Switch Sensor Expansion box is connected, the binary equivalent of the decimal number displayed will indicate which switch sensors are currently in an alarm state (0 = sensor ok, 1 = sensor in alarm state). For example, if switch sensors 1 and 4 are in alarm state, the value displayed will be 9, which equals 00001001 in binary." +::= { traps 2 } + +alarm3 OBJECT-TYPE + SYNTAX INTEGER(0..255) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for sensor channel 3. If a digital temperature/humidity or digital temperature sensor is connected to this channel, 0 = sensor ok, 1 = high temp alarm, 2 = low temp alarm, 3 = humidity alarm. If a Switch Sensor Expansion box is connected, the binary equivalent of the decimal number displayed will indicate which switch sensors are currently in an alarm state (0 = sensor ok, 1 = sensor in alarm state). For example, if switch sensors 1 and 4 are in alarm state, the value displayed will be 9, which equals 00001001 in binary." +::= { traps 3 } + +alarm4 OBJECT-TYPE + SYNTAX INTEGER(0..255) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for sensor channel 4. If a digital temperature/humidity or digital temperature sensor is connected to this channel, 0 = sensor ok, 1 = high temp alarm, 2 = low temp alarm, 3 = humidity alarm. If a Switch Sensor Expansion box is connected, the binary equivalent of the decimal number displayed will indicate which switch sensors are currently in an alarm state (0 = sensor ok, 1 = sensor in alarm state). For example, if switch sensors 1 and 4 are in alarm state, the value displayed will be 9, which equals 00001001 in binary." +::= { traps 4 } + +alarmMessage1 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm Message from sensor channel 1 (Internal)" +::= { traps 5 } + +alarmMessage2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm Message from sensor channel 2 (External)" +::= { traps 6 } + +alarmMessage3 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm Message from sensor channel 3 (External)" +::= { traps 7 } + +alarmMessage4 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm Message from sensor channel 4 (External)" +::= { traps 8 } + +threshold1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 1 defined for Channel 1" +::= { channels1 1 } + +threshold1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 2 defined for Channel 1" +::= { channels1 2 } + +threshold1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 3 defined for Channel 1" +::= { channels1 3 } + +threshold1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 4 defined for Channel 1" +::= { channels1 4 } + +threshold1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 5 defined for Channel 1" +::= { channels1 5 } + +threshold1-6 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 6 defined for Channel 1" +::= { channels1 6 } + +threshold1-7 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 7 defined for Channel 1" +::= { channels1 7 } + +threshold1-8 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 8 defined for Channel 1" +::= { channels1 8 } + +threshold2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 1 defined for Channel 2" +::= { channels2 1 } + +threshold2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 2 defined for Channel 2" +::= { channels2 2 } + +threshold2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 3 defined for Channel 2" +::= { channels2 3 } + +threshold2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 4 defined for Channel 2" +::= { channels2 4 } + +threshold3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 1 defined for Channel 3" +::= { channels3 1 } + +threshold3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 2 defined for Channel 3" +::= { channels3 2 } + +threshold3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 3 defined for Channel 3" +::= { channels3 3 } + +threshold3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 4 defined for Channel 3" +::= { channels3 4 } + +threshold4-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 1 defined for Channel 4" +::= { channels4 1 } + +threshold4-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 2 defined for Channel 4" +::= { channels4 2 } + +threshold4-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 3 defined for Channel 4" +::= { channels4 3 } + +threshold4-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Threshold 4 defined for Channel 4" +::= { channels4 4 } + + +-- ROOMALERT11E TRAPS + +tempAlarm1-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage2, sensor2-1, sensor2-2, sensor2-1 } + DESCRIPTION "A tempAlarm1 trap signifies that the current + temperature on external sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-11E-SNMP-trap TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1,alarmMessage2,alarmMessage3,alarmMessage4 } + DESCRIPTION "A Room Alert 11E SNMP Trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmMessage variable." +::= 2 + +tempAlarm2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage3, sensor3-1, sensor3-2, sensor3-1 } + DESCRIPTION "A tempAlarm2 trap signifies that the current + temperature on external sensor 2 is outside the + defined high or low threshold." +::= 3 + +tempClear2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage3, sensor3-1, sensor3-2, sensor3-1 } + DESCRIPTION "A tempClear2 trap signifies that the current + temperature on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 4 + +tempAlarm3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage4, sensor4-1, sensor4-2, sensor4-1 } + DESCRIPTION "A tempAlarm3 trap signifies that the current + temperature on external sensor 3 is outside the + defined high or low threshold." +::= 5 + +tempClear3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage4, sensor4-1, sensor4-2, sensor4-1 } + DESCRIPTION "A tempClear3 trap signifies that the current + temperature on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 6 + +humidityAlarm1-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage2, sensor2-1, sensor2-2, sensor2-3 } + DESCRIPTION "A humidityAlarm1 trap signifies that the current + humidity on external sensor 1 is outside the + defined high or low threshold." +::= 7 + +humidityClear1-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage2, sensor2-1, sensor2-2, sensor2-3 } + DESCRIPTION "A humidityClear1 trap signifies that the current + humidity on external sensor 1 has returned to + a normal condition and is within the defined + high or low threshold." +::= 8 + +humidityAlarm2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage3, sensor3-1, sensor3-2, sensor3-3 } + DESCRIPTION "A humidityAlarm2 trap signifies that the current + humidity on external sensor 2 is outside the + defined high or low threshold." +::= 9 + +humidityClear2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage3, sensor3-1, sensor3-2, sensor3-3 } + DESCRIPTION "A humidityClear2 trap signifies that the current + humidity on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 10 + +humidityAlarm3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage4, sensor4-1, sensor4-2, sensor4-3 } + DESCRIPTION "A humidityAlarm3 trap signifies that the current + humidity on external sensor 3 is outside the + defined high or low threshold." +::= 11 + +humidityClear3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage4, sensor4-1, sensor4-2, sensor4-3 } + DESCRIPTION "A humidityClear3 trap signifies that the current + humidity on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 12 + +switchAlarm1-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-1, sensor1-1, sensor1-1 } + DESCRIPTION "A switchAlarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 13 + +switchClear1-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-1, sensor1-1, sensor1-1 } + DESCRIPTION "A switchClear1 trap signifies that the switch sensor 1 + has returned to a normal state." +::= 14 + +switchAlarm2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-2, sensor1-2, sensor1-2 } + DESCRIPTION "A switchAlarm2 trap signifies that switch sensor 2 + is in an alarm state." +::= 15 + +switchClear2-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-2, sensor1-2, sensor1-2 } + DESCRIPTION "A switchClear2 trap signifies that the switch sensor 2 + has returned to a normal state." +::= 16 + +switchAlarm3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-3, sensor1-3, sensor1-3 } + DESCRIPTION "A switchAlarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 17 + +switchClear3-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-3, sensor1-3, sensor1-3 } + DESCRIPTION "A switchClear3 trap signifies that the switch sensor 3 + has returned to a normal state." +::= 18 + +switchAlarm4-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-4, sensor1-4, sensor1-4 } + DESCRIPTION "A switchAlarm4 trap signifies that switch sensor 4 + is in an alarm state." +::= 19 + +switchClear4-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-4, sensor1-4, sensor1-4 } + DESCRIPTION "A switchClear4 trap signifies that the switch sensor 4 + has returned to a normal state." +::= 20 + +switchAlarm5-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-5, sensor1-5, sensor1-5 } + DESCRIPTION "A switchAlarm5 trap signifies that switch sensor 5 + is in an alarm state." +::= 21 + +switchClear5-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-5, sensor1-5, sensor1-5 } + DESCRIPTION "A switchClear5 trap signifies that the switch sensor 5 + has returned to a normal state." +::= 22 + +switchAlarm6-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-6, sensor1-6, sensor1-6 } + DESCRIPTION "A switchAlarm6 trap signifies that switch sensor 6 + is in an alarm state." +::= 23 + +switchClear6-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-6, sensor1-6, sensor1-6 } + DESCRIPTION "A switchClear6 trap signifies that the switch sensor 6 + has returned to a normal state." +::= 24 + +switchAlarm7-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-7, sensor1-7, sensor1-7 } + DESCRIPTION "A switchAlarm7 trap signifies that switch sensor 7 + is in an alarm state." +::= 25 + +switchClear7-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-7, sensor1-7, sensor1-7 } + DESCRIPTION "A switchClear7 trap signifies that the switch sensor 7 + has returned to a normal state." +::= 26 + +switchAlarm8-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-8, sensor1-8, sensor1-8 } + DESCRIPTION "A switchAlarm8 trap signifies that switch sensor 8 + is in an alarm state." +::= 27 + +switchClear8-11e TRAP-TYPE + ENTERPRISE roomalert11E + VARIABLES { alarmMessage1, sensor1-8, sensor1-8, sensor1-8 } + DESCRIPTION "A switchClear8 trap signifies that the switch sensor 8 + has returned to a normal state." +::= 28 +END diff --git a/mibs/avtech/ROOMALERT12E.MIB b/mibs/avtech/ROOMALERT12E.MIB new file mode 100644 index 0000000000..f10d53bce6 --- /dev/null +++ b/mibs/avtech/ROOMALERT12E.MIB @@ -0,0 +1,525 @@ +-- +--defines ROOMALERT12E-MIB for SNMP agent extension of Room Alert 12E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.0 +--Date: WEB_RELEASE_DATE_PLACEHOLDER +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) COPYRIGHT_YEAR_PLACEHOLDER AVTECH Software, Inc. +-- + +ROOMALERT12E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert12E OBJECT IDENTIFIER ::= { products 9 } +sensors OBJECT IDENTIFIER ::= { roomalert12E 1 } +lightTower OBJECT IDENTIFIER ::= { roomalert12E 2 } +internal-sen OBJECT IDENTIFIER ::= { sensors 1 } +digital-sen1 OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen2 OBJECT IDENTIFIER ::= { sensors 3 } +digital-sen3 OBJECT IDENTIFIER ::= { sensors 4 } +switch1 OBJECT IDENTIFIER ::= { sensors 5 } +switch2 OBJECT IDENTIFIER ::= { sensors 6 } +switch3 OBJECT IDENTIFIER ::= { sensors 7 } +switch4 OBJECT IDENTIFIER ::= { sensors 8 } +analog OBJECT IDENTIFIER ::= { sensors 9 } +relay OBJECT IDENTIFIER ::= { sensors 10 } +traps OBJECT IDENTIFIER ::= { roomalert12E 3 } + +-- Room Alert 12E MIB +-- Parameters + + + + +internal-sen-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { internal-sen 1 } + +internal-sen-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { internal-sen 2 } + +internal-sen-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { internal-sen 3 } + +internal-sen-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { internal-sen 4 } + +internal-sen-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { internal-sen 5 } + +internal-sen-6 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Represents the sensor's label/" +::= { internal-sen 6 } + +analog-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 12E analog input (0-5VDC)." +::= { analog 1 } + +analog-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The analog sensor's label." +::= { analog 2 } + +relay-1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 12E relay output." +::= { relay 1 } + +relay-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The relay output's label." +::= { relay 2 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen1 5 } + +digital-sen1-6 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Represents the sensor's label/" +::= { digital-sen1 6 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +digital-sen2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen2 5 } + +digital-sen2-6 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Represents the sensor's label/" +::= { digital-sen2 6 } + +digital-sen3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen3 1 } + +digital-sen3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen3 2 } + +digital-sen3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen3 3 } + +digital-sen3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen3 4 } + +digital-sen3-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen3 5 } + +digital-sen3-6 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Represents the sensor's label/" +::= { digital-sen3 6 } + +switch-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch1 1 } + +switch-sen1-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 1." +::= { switch1 2 } + +switch-sen2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 2 (0 = OPEN, 1 = CLOSED)." +::= { switch2 1 } + +switch-sen2-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 2." +::= { switch2 2 } + +switch-sen3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 3 (0 = OPEN, 1 = CLOSED)." +::= { switch3 1 } + +switch-sen3-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 3." +::= { switch3 2 } + +switch-sen4 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 4 (0 = OPEN, 1 = CLOSED)." +::= { switch4 1 } + +switch-sen4-2 OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for switch sensor 4." +::= { switch4 2 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- Light Tower + +lightTower-RE OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the red LED on the Light Tower." +::= { lightTower 1 } + +lightTower-OR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the orange LED on the Light Tower." +::= { lightTower 2 } + +lightTower-GR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the green LED on the Light Tower." +::= { lightTower 3 } + +lightTower-WH OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the white LED on the Light Tower." +::= { lightTower 4 } + +lightTower-BL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the blue LED on the Light Tower." +::= { lightTower 5 } + +lightTower-A1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 1st audio alarm on the Light Tower." +::= { lightTower 6 } + +lightTower-A2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 2nd audio alarm on the Light Tower." +::= { lightTower 7 } + +lightTower-RL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the LTA's on-board relay output" +::= { lightTower 8 } + + +-- ROOMALERT12E TRAPS + +tempalarm1-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm1 trap signifies that the current + temperature on external sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-12E-snmp-trap TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-12E-snmp-trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmMessage variable." +::= 2 + +tempalarm2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm2 trap signifies that the current + temperature on external sensor 2 is outside the + defined high or low threshold." +::= 3 + +tempclear2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear2 trap signifies that the current + temperature on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 4 + +tempalarm3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm3 trap signifies that the current + temperature on external sensor 3 is outside the + defined high or low threshold." +::= 5 + +tempclear3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear3 trap signifies that the current + temperature on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 6 + +humidityalarm1-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm1 trap signifies that the current + humidity on external sensor 1 is outside the + defined high or low threshold." +::= 7 + +humidityclear1-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear1 trap signifies that the current + humidity on external sensor 1 has returned to + a normal condition and is within the defined + high or low threshold." +::= 8 + +humidityalarm2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm2 trap signifies that the current + humidity on external sensor 2 is outside the + defined high or low threshold." +::= 9 + +humidityclear2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear2 trap signifies that the current + humidity on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 10 + +humidityalarm3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm3 trap signifies that the current + humidity on external sensor 3 is outside the + defined high or low threshold." +::= 11 + +humidityclear3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear3 trap signifies that the current + humidity on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 12 + +switchalarm1-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 13 + +switchclear1-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear1 trap signifies that the switch sensor 1 + has returned to a normal state." +::= 14 + +switchalarm2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm2 trap signifies that switch sensor 2 + is in an alarm state." +::= 15 + +switchclear2-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear2 trap signifies that the switch sensor 2 + has returned to a normal state." +::= 16 + +switchalarm3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm3 trap signifies that switch sensor 3 + is in an alarm state." +::= 17 + +switchclear3-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear3 trap signifies that the switch sensor 3 + has returned to a normal state." +::= 18 + +switchalarm4-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm4 trap signifies that switch sensor 4 + is in an alarm state." +::= 19 + +switchclear4-12E TRAP-TYPE + ENTERPRISE roomalert12E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear4 trap signifies that the switch sensor 4 + has returned to a normal state." +::= 20 +END diff --git a/mibs/avtech/ROOMALERT24E.MIB b/mibs/avtech/ROOMALERT24E.MIB new file mode 100644 index 0000000000..9b82372474 --- /dev/null +++ b/mibs/avtech/ROOMALERT24E.MIB @@ -0,0 +1,627 @@ +-- +--defines ROOMALERT24E-MIB for SNMP agent extension of Room Alert 24E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.2 +--Date: 11 September 2007 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2007 AVTECH Software, Inc. +-- + +ROOMALERT24E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert24e OBJECT IDENTIFIER ::= { products 5 } +sensors OBJECT IDENTIFIER ::= { roomalert24e 1 } +traps OBJECT IDENTIFIER ::= { roomalert24e 2 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +humidity OBJECT IDENTIFIER ::= { internal 2 } +heat-index OBJECT IDENTIFIER ::= { internal 3 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +digital-sen3 OBJECT IDENTIFIER ::= { digital 3 } +digital-sen4 OBJECT IDENTIFIER ::= { digital 4 } +digital-sen5 OBJECT IDENTIFIER ::= { digital 5 } +digital-sen6 OBJECT IDENTIFIER ::= { digital 6 } +switch OBJECT IDENTIFIER ::= { sensors 3 } + + +-- Room Alert 24E MIB +-- Parameters + + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +internal-humidity OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal relative humidity reading in %RH. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { humidity 1 } + +internal-heat-index OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal heat index reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 1 } + +internal-heat-indexc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal heat index reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 2 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen1 5 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +digital-sen2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen2 5 } + +digital-sen3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen3 1 } + +digital-sen3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen3 2 } + +digital-sen3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen3 3 } + +digital-sen3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen3 4 } + +digital-sen3-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen3 5 } + +digital-sen4-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen4 1 } + +digital-sen4-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen4 2 } + +digital-sen4-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen4 3 } + +digital-sen4-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen4 4 } + +digital-sen4-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen4 5 } + +digital-sen5-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen5 1 } + +digital-sen5-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen5 2 } + +digital-sen5-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen5 3 } + +digital-sen5-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen5 4 } + +digital-sen5-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen5 5 } + +digital-sen6-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen6 1 } + +digital-sen6-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen6 2 } + +digital-sen6-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen6 3 } + +digital-sen6-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen6 4 } + +digital-sen6-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen6 5 } + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +switch-sen2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 2 (0 = OPEN, 1 = CLOSED)." +::= { switch 2 } + +switch-sen3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 3 (0 = OPEN, 1 = CLOSED)." +::= { switch 3 } + +switch-sen4 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 4 (0 = OPEN, 1 = CLOSED)." +::= { switch 4 } + +switch-sen5 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 5 (0 = OPEN, 1 = CLOSED)." +::= { switch 5 } + +switch-sen6 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 6 (0 = OPEN, 1 = CLOSED)." +::= { switch 6 } + +switch-sen7 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 7 (0 = OPEN, 1 = CLOSED)." +::= { switch 7 } + +switch-sen8 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 8 (0 = OPEN, 1 = CLOSED)." +::= { switch 8 } + +switch-sen9 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 9 (0 = OPEN, 1 = CLOSED)." +::= { switch 9 } + +switch-sen10 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 10 (0 = OPEN, 1 = CLOSED)." +::= { switch 10 } + +switch-sen11 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 11 (0 = OPEN, 1 = CLOSED)." +::= { switch 11 } + +switch-sen12 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 12 (0 = OPEN, 1 = CLOSED)." +::= { switch 12 } + +switch-sen13 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 13 (0 = OPEN, 1 = CLOSED)." +::= { switch 13 } + +switch-sen14 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 14 (0 = OPEN, 1 = CLOSED)." +::= { switch 14 } + +switch-sen15 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 15 (0 = OPEN, 1 = CLOSED)." +::= { switch 15 } + +switch-sen16 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 16 (0 = OPEN, 1 = CLOSED)." +::= { switch 16 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- ROOMALERT24E TRAPS + +tempalarm1-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm1 trap signifies that the current + temperature on external sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-24e-snmp-trap TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-24e-snmp-trap indicates that an alarm + condition has occurred on the sensor indicated + by the alarmmessage variable." +::= 2 + +tempalarm2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm2 trap signifies that the current + temperature on external sensor 2 is outside the + defined high or low threshold." +::= 3 + +tempclear2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear2 trap signifies that the current + temperature on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 4 + +tempalarm3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm3 trap signifies that the current + temperature on external sensor 3 is outside the + defined high or low threshold." +::= 5 + +tempclear3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear3 trap signifies that the current + temperature on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 6 + +humidityalarm1-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm1 trap signifies that the current + humidity on external sensor 1 is outside the + defined high or low threshold." +::= 7 + +humidityclear1-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear1 trap signifies that the current + humidity on external sensor 1 has returned to + a normal condition and is within the defined + high or low threshold." +::= 8 + +humidityalarm2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm2 trap signifies that the current + humidity on external sensor 2 is outside the + defined high or low threshold." +::= 9 + +humidityclear2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear2 trap signifies that the current + humidity on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 10 + +humidityalarm3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm3 trap signifies that the current + humidity on external sensor 3 is outside the + defined high or low threshold." +::= 11 + +humidityclear3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear3 trap signifies that the current + humidity on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 12 + +switchalarm1-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 13 + +switchclear1-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear1 trap signifies that the switch sensor 1 + has returned to a normal state." +::= 14 + +switchalarm2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm2 trap signifies that switch sensor 2 + is in an alarm state." +::= 15 + +switchclear2-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear2 trap signifies that the switch sensor 2 + has returned to a normal state." +::= 16 + +switchalarm3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 17 + +switchclear3-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear3 trap signifies that the switch sensor 3 + has returned to a normal state." +::= 18 + +switchalarm4-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm4 trap signifies that switch sensor 4 + is in an alarm state." +::= 19 + +switchclear4-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear4 trap signifies that the switch sensor 4 + has returned to a normal state." +::= 20 + +switchalarm5-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm5 trap signifies that switch sensor 5 + is in an alarm state." +::= 21 + +switchclear5-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear5 trap signifies that the switch sensor 5 + has returned to a normal state." +::= 22 + +switchalarm6-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm6 trap signifies that switch sensor 6 + is in an alarm state." +::= 23 + +switchclear6-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear6 trap signifies that the switch sensor 6 + has returned to a normal state." +::= 24 + +switchalarm7-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm7 trap signifies that switch sensor 7 + is in an alarm state." +::= 25 + +switchclear7-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear7 trap signifies that the switch sensor 7 + has returned to a normal state." +::= 26 + +switchalarm8-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm8 trap signifies that switch sensor 8 + is in an alarm state." +::= 27 + +switchclear8-24e TRAP-TYPE + ENTERPRISE roomalert24e + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear8 trap signifies that the switch sensor 8 + has returned to a normal state." +::= 28 +END diff --git a/mibs/avtech/ROOMALERT26W.MIB b/mibs/avtech/ROOMALERT26W.MIB new file mode 100644 index 0000000000..8401fb2ea8 --- /dev/null +++ b/mibs/avtech/ROOMALERT26W.MIB @@ -0,0 +1,2700 @@ +-- +--defines ROOMALERT26W-MIB for SNMP agent extension of Room Alert 26W +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.1.3 +--Date: 11 September 2007 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2007 AVTECH Software, Inc. +-- + +ROOMALERT26W-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert26w OBJECT IDENTIFIER ::= { products 4 } +sensors OBJECT IDENTIFIER ::= { roomalert26w 1 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +humidity OBJECT IDENTIFIER ::= { internal 2 } +heat-index OBJECT IDENTIFIER ::= { internal 5 } +power OBJECT IDENTIFIER ::= { internal 3 } +flood OBJECT IDENTIFIER ::= { internal 4 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +digital-sen3 OBJECT IDENTIFIER ::= { digital 3 } +digital-sen4 OBJECT IDENTIFIER ::= { digital 4 } +digital-sen5 OBJECT IDENTIFIER ::= { digital 5 } +digital-sen6 OBJECT IDENTIFIER ::= { digital 6 } +switch OBJECT IDENTIFIER ::= { sensors 3 } +wireless OBJECT IDENTIFIER ::= { sensors 4 } +wish-1 OBJECT IDENTIFIER ::= { wireless 1 } +wish-1-sensors OBJECT IDENTIFIER ::= { wish-1 4 } +wish-1-internal OBJECT IDENTIFIER ::= { wish-1-sensors 1 } +wish-1-external OBJECT IDENTIFIER ::= { wish-1-sensors 2 } +wish-1-external-1 OBJECT IDENTIFIER ::= { wish-1-external 1 } +wish-1-external-2 OBJECT IDENTIFIER ::= { wish-1-external 2 } +wish-2 OBJECT IDENTIFIER ::= { wireless 2 } +wish-2-sensors OBJECT IDENTIFIER ::= { wish-2 4 } +wish-2-internal OBJECT IDENTIFIER ::= { wish-2-sensors 1 } +wish-2-external OBJECT IDENTIFIER ::= { wish-2-sensors 2 } +wish-2-external-1 OBJECT IDENTIFIER ::= { wish-2-external 1 } +wish-2-external-2 OBJECT IDENTIFIER ::= { wish-2-external 2 } +wish-3 OBJECT IDENTIFIER ::= { wireless 3 } +wish-3-sensors OBJECT IDENTIFIER ::= { wish-3 4 } +wish-3-internal OBJECT IDENTIFIER ::= { wish-3-sensors 1 } +wish-3-external OBJECT IDENTIFIER ::= { wish-3-sensors 2 } +wish-3-external-1 OBJECT IDENTIFIER ::= { wish-3-external 1 } +wish-3-external-2 OBJECT IDENTIFIER ::= { wish-3-external 2 } +wish-4 OBJECT IDENTIFIER ::= { wireless 4 } +wish-4-sensors OBJECT IDENTIFIER ::= { wish-4 4 } +wish-4-internal OBJECT IDENTIFIER ::= { wish-4-sensors 1 } +wish-4-external OBJECT IDENTIFIER ::= { wish-4-sensors 2 } +wish-4-external-1 OBJECT IDENTIFIER ::= { wish-4-external 1 } +wish-4-external-2 OBJECT IDENTIFIER ::= { wish-4-external 2 } +wish-5 OBJECT IDENTIFIER ::= { wireless 5 } +wish-5-sensors OBJECT IDENTIFIER ::= { wish-5 4 } +wish-5-internal OBJECT IDENTIFIER ::= { wish-5-sensors 1 } +wish-5-external OBJECT IDENTIFIER ::= { wish-5-sensors 2 } +wish-5-external-1 OBJECT IDENTIFIER ::= { wish-5-external 1 } +wish-5-external-2 OBJECT IDENTIFIER ::= { wish-5-external 2 } +wish-6 OBJECT IDENTIFIER ::= { wireless 6 } +wish-6-sensors OBJECT IDENTIFIER ::= { wish-6 4 } +wish-6-internal OBJECT IDENTIFIER ::= { wish-6-sensors 1 } +wish-6-external OBJECT IDENTIFIER ::= { wish-6-sensors 2 } +wish-6-external-1 OBJECT IDENTIFIER ::= { wish-6-external 1 } +wish-6-external-2 OBJECT IDENTIFIER ::= { wish-6-external 2 } +wish-7 OBJECT IDENTIFIER ::= { wireless 7 } +wish-7-sensors OBJECT IDENTIFIER ::= { wish-7 4 } +wish-7-internal OBJECT IDENTIFIER ::= { wish-7-sensors 1 } +wish-7-external OBJECT IDENTIFIER ::= { wish-7-sensors 2 } +wish-7-external-1 OBJECT IDENTIFIER ::= { wish-7-external 1 } +wish-7-external-2 OBJECT IDENTIFIER ::= { wish-7-external 2 } +wish-8 OBJECT IDENTIFIER ::= { wireless 8 } +wish-8-sensors OBJECT IDENTIFIER ::= { wish-8 4 } +wish-8-internal OBJECT IDENTIFIER ::= { wish-8-sensors 1 } +wish-8-external OBJECT IDENTIFIER ::= { wish-8-sensors 2 } +wish-8-external-1 OBJECT IDENTIFIER ::= { wish-8-external 1 } +wish-8-external-2 OBJECT IDENTIFIER ::= { wish-8-external 2 } +wish-9 OBJECT IDENTIFIER ::= { wireless 9 } +wish-9-sensors OBJECT IDENTIFIER ::= { wish-9 4 } +wish-9-internal OBJECT IDENTIFIER ::= { wish-9-sensors 1 } +wish-9-external OBJECT IDENTIFIER ::= { wish-9-sensors 2 } +wish-9-external-1 OBJECT IDENTIFIER ::= { wish-9-external 1 } +wish-9-external-2 OBJECT IDENTIFIER ::= { wish-9-external 2 } +wish-10 OBJECT IDENTIFIER ::= { wireless 10 } +wish-10-sensors OBJECT IDENTIFIER ::= { wish-10 4 } +wish-10-internal OBJECT IDENTIFIER ::= { wish-10-sensors 1 } +wish-10-external OBJECT IDENTIFIER ::= { wish-10-sensors 2 } +wish-10-external-1 OBJECT IDENTIFIER ::= { wish-10-external 1 } +wish-10-external-2 OBJECT IDENTIFIER ::= { wish-10-external 2 } +wish-11 OBJECT IDENTIFIER ::= { wireless 11 } +wish-11-sensors OBJECT IDENTIFIER ::= { wish-11 4 } +wish-11-internal OBJECT IDENTIFIER ::= { wish-11-sensors 1 } +wish-11-external OBJECT IDENTIFIER ::= { wish-11-sensors 2 } +wish-11-external-1 OBJECT IDENTIFIER ::= { wish-11-external 1 } +wish-11-external-2 OBJECT IDENTIFIER ::= { wish-11-external 2 } +wish-12 OBJECT IDENTIFIER ::= { wireless 12 } +wish-12-sensors OBJECT IDENTIFIER ::= { wish-12 4 } +wish-12-internal OBJECT IDENTIFIER ::= { wish-12-sensors 1 } +wish-12-external OBJECT IDENTIFIER ::= { wish-12-sensors 2 } +wish-12-external-1 OBJECT IDENTIFIER ::= { wish-12-external 1 } +wish-12-external-2 OBJECT IDENTIFIER ::= { wish-12-external 2 } +wish-13 OBJECT IDENTIFIER ::= { wireless 13 } +wish-13-sensors OBJECT IDENTIFIER ::= { wish-13 4 } +wish-13-internal OBJECT IDENTIFIER ::= { wish-13-sensors 1 } +wish-13-external OBJECT IDENTIFIER ::= { wish-13-sensors 2 } +wish-13-external-1 OBJECT IDENTIFIER ::= { wish-13-external 1 } +wish-13-external-2 OBJECT IDENTIFIER ::= { wish-13-external 2 } +wish-14 OBJECT IDENTIFIER ::= { wireless 14 } +wish-14-sensors OBJECT IDENTIFIER ::= { wish-14 4 } +wish-14-internal OBJECT IDENTIFIER ::= { wish-14-sensors 1 } +wish-14-external OBJECT IDENTIFIER ::= { wish-14-sensors 2 } +wish-14-external-1 OBJECT IDENTIFIER ::= { wish-14-external 1 } +wish-14-external-2 OBJECT IDENTIFIER ::= { wish-14-external 2 } +wish-15 OBJECT IDENTIFIER ::= { wireless 15 } +wish-15-sensors OBJECT IDENTIFIER ::= { wish-15 4 } +wish-15-internal OBJECT IDENTIFIER ::= { wish-15-sensors 1 } +wish-15-external OBJECT IDENTIFIER ::= { wish-15-sensors 2 } +wish-15-external-1 OBJECT IDENTIFIER ::= { wish-15-external 1 } +wish-15-external-2 OBJECT IDENTIFIER ::= { wish-15-external 2 } +traps OBJECT IDENTIFIER ::= { roomalert26w 2 } + +-- Room Alert 26W MIB +-- Parameters + + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +internal-humidity OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal relative humidity reading in %RH. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { humidity 1 } + +internal-heat-index OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS optional + DESCRIPTION "The internal heat index reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 1 } + +internal-heat-indexC OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS optional + DESCRIPTION "The internal heat index reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 2 } + +internal-power OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 26E/W power supply. A '0' indicates the unit is running on AC/Utility power. A '1' indicates the unit is running on battery backup power." +::= { power 1 } + +flood-detected OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the internal flood sensor. A '0' indicates that no water is currently detected. A '1' indicates the unit has detected water somewhere on the connected sensor cable." +::= { flood 1 } + +cable-fault OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the flood sensor cable. A '0' indicates the flood cable is connected and functioning properly. A '1' indicates the flood cable is not connected has a malfunction." +::= { flood 2 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen1 5 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +digital-sen3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen3 1 } + +digital-sen3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen3 2 } + +digital-sen3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen3 3 } + +digital-sen3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen3 4 } + +digital-sen4-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen4 1 } + +digital-sen4-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen4 2 } + +digital-sen4-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen4 3 } + +digital-sen4-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen4 4 } + +digital-sen5-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen5 1 } + +digital-sen5-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen5 2 } + +digital-sen5-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen5 3 } + +digital-sen5-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen5 4 } + +digital-sen6-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen6 1 } + +digital-sen6-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen6 2 } + +digital-sen6-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen6 3 } + +digital-sen6-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen6 4 } + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +switch-sen2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 2 (0 = OPEN, 1 = CLOSED)." +::= { switch 2 } + +switch-sen3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 3 (0 = OPEN, 1 = CLOSED)." +::= { switch 3 } + +switch-sen4 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 4 (0 = OPEN, 1 = CLOSED)." +::= { switch 4 } + +switch-sen5 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 5 (0 = OPEN, 1 = CLOSED)." +::= { switch 5 } + +switch-sen6 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 6 (0 = OPEN, 1 = CLOSED)." +::= { switch 6 } + +switch-sen7 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 7 (0 = OPEN, 1 = CLOSED)." +::= { switch 7 } + +switch-sen8 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 8 (0 = OPEN, 1 = CLOSED)." +::= { switch 8 } + +switch-sen9 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 9 (0 = OPEN, 1 = CLOSED)." +::= { switch 9 } + +switch-sen10 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 10 (0 = OPEN, 1 = CLOSED)." +::= { switch 10 } + +switch-sen11 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 11 (0 = OPEN, 1 = CLOSED)." +::= { switch 11 } + +switch-sen12 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 12 (0 = OPEN, 1 = CLOSED)." +::= { switch 12 } + +switch-sen13 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 13 (0 = OPEN, 1 = CLOSED)." +::= { switch 13 } + +switch-sen14 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 14 (0 = OPEN, 1 = CLOSED)." +::= { switch 14 } + +switch-sen15 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 15 (0 = OPEN, 1 = CLOSED)." +::= { switch 15 } + +switch-sen16 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 16 (0 = OPEN, 1 = CLOSED)." +::= { switch 16 } + +wish-1-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-1 1 } + +wish-1-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-1 2 } + +wish-1-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-1 3 } + +wish-1-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-1-internal 1 } + +wish-1-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-1-internal 2 } + +wish-1-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-1-internal 3 } + +wish-1-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-1-external-1 1 } + +wish-1-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-1-external-1 2 } + +wish-1-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-1-external-1 3 } + +wish-1-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-1-external-1 4 } + +wish-1-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-1-external-1 5 } + +wish-1-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-1-external-1 6 } + +wish-1-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-1-external-2 1 } + +wish-1-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-1-external-2 2 } + +wish-1-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-1-external-2 3 } + +wish-1-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-1-external-2 4 } + +wish-1-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-1-external-2 5 } + +wish-1-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-1-external-2 6 } + +wish-1-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-1-external 3 } + +wish-2-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-2 1 } + +wish-2-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-2 2 } + +wish-2-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-2 3 } + +wish-2-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-2-internal 1 } + +wish-2-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-2-internal 2 } + +wish-2-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-2-internal 3 } + +wish-2-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-2-external-1 1 } + +wish-2-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-2-external-1 2 } + +wish-2-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-2-external-1 3 } + +wish-2-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-2-external-1 4 } + +wish-2-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-2-external-1 5 } + +wish-2-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-2-external-1 6 } + +wish-2-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-2-external-2 1 } + +wish-2-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-2-external-2 2 } + +wish-2-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-2-external-2 3 } + +wish-2-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-2-external-2 4 } + +wish-2-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-2-external-2 5 } + +wish-2-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-2-external-2 6 } + +wish-2-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-2-external 3 } + +wish-3-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-3 1 } + +wish-3-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-3 2 } + +wish-3-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-3 3 } + +wish-3-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-3-internal 1 } + +wish-3-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-3-internal 2 } + +wish-3-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-3-internal 3 } + +wish-3-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-3-external-1 1 } + +wish-3-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-3-external-1 2 } + +wish-3-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-3-external-1 3 } + +wish-3-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-3-external-1 4 } + +wish-3-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-3-external-1 5 } + +wish-3-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-3-external-1 6 } + +wish-3-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-3-external-2 1 } + +wish-3-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-3-external-2 2 } + +wish-3-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-3-external-2 3 } + +wish-3-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-3-external-2 4 } + +wish-3-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-3-external-2 5 } + +wish-3-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-3-external-2 6 } + +wish-3-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-3-external 3 } + +wish-4-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-4 1 } + +wish-4-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-4 2 } + +wish-4-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-4 3 } + +wish-4-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-4-internal 1 } + +wish-4-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-4-internal 2 } + +wish-4-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-4-internal 3 } + +wish-4-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-4-external-1 1 } + +wish-4-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-4-external-1 2 } + +wish-4-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-4-external-1 3 } + +wish-4-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-4-external-1 4 } + +wish-4-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-4-external-1 5 } + +wish-4-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-4-external-1 6 } + +wish-4-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-4-external-2 1 } + +wish-4-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-4-external-2 2 } + +wish-4-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-4-external-2 3 } + +wish-4-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-4-external-2 4 } + +wish-4-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-4-external-2 5 } + +wish-4-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-4-external-2 6 } + +wish-4-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-4-external 3 } + +wish-5-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-5 1 } + +wish-5-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-5 2 } + +wish-5-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-5 3 } + +wish-5-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-5-internal 1 } + +wish-5-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-5-internal 2 } + +wish-5-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-5-internal 3 } + +wish-5-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-5-external-1 1 } + +wish-5-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-5-external-1 2 } + +wish-5-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-5-external-1 3 } + +wish-5-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-5-external-1 4 } + +wish-5-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-5-external-1 5 } + +wish-5-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-5-external-1 6 } + +wish-5-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-5-external-2 1 } + +wish-5-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-5-external-2 2 } + +wish-5-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-5-external-2 3 } + +wish-5-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-5-external-2 4 } + +wish-5-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-5-external-2 5 } + +wish-5-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-5-external-2 6 } + +wish-5-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-5-external 3 } + +wish-6-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-6 1 } + +wish-6-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-6 2 } + +wish-6-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-6 3 } + +wish-6-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-6-internal 1 } + +wish-6-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-6-internal 2 } + +wish-6-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-6-internal 3 } + +wish-6-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-6-external-1 1 } + +wish-6-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-6-external-1 2 } + +wish-6-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-6-external-1 3 } + +wish-6-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-6-external-1 4 } + +wish-6-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-6-external-1 5 } + +wish-6-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-6-external-1 6 } + +wish-6-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-6-external-2 1 } + +wish-6-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-6-external-2 2 } + +wish-6-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-6-external-2 3 } + +wish-6-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-6-external-2 4 } + +wish-6-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-6-external-2 5 } + +wish-6-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-6-external-2 6 } + +wish-6-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-6-external 3 } + +wish-7-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-7 1 } + +wish-7-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-7 2 } + +wish-7-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-7 3 } + +wish-7-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-7-internal 1 } + +wish-7-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-7-internal 2 } + +wish-7-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-7-internal 3 } + +wish-7-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-7-external-1 1 } + +wish-7-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-7-external-1 2 } + +wish-7-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-7-external-1 3 } + +wish-7-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-7-external-1 4 } + +wish-7-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-7-external-1 5 } + +wish-7-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-7-external-1 6 } + +wish-7-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-7-external-2 1 } + +wish-7-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-7-external-2 2 } + +wish-7-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-7-external-2 3 } + +wish-7-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-7-external-2 4 } + +wish-7-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-7-external-2 5 } + +wish-7-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-7-external-2 6 } + +wish-7-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-7-external 3 } + +wish-8-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-8 1 } + +wish-8-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-8 2 } + +wish-8-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-8 3 } + +wish-8-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-8-internal 1 } + +wish-8-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-8-internal 2 } + +wish-8-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-8-internal 3 } + +wish-8-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-8-external-1 1 } + +wish-8-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-8-external-1 2 } + +wish-8-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-8-external-1 3 } + +wish-8-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-8-external-1 4 } + +wish-8-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-8-external-1 5 } + +wish-8-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-8-external-1 6 } + +wish-8-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-8-external-2 1 } + +wish-8-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-8-external-2 2 } + +wish-8-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-8-external-2 3 } + +wish-8-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-8-external-2 4 } + +wish-8-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-8-external-2 5 } + +wish-8-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-8-external-2 6 } + +wish-8-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-8-external 3 } + +wish-9-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-9 1 } + +wish-9-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-9 2 } + +wish-9-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-9 3 } + +wish-9-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-9-internal 1 } + +wish-9-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-9-internal 2 } + +wish-9-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-9-internal 3 } + +wish-9-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-9-external-1 1 } + +wish-9-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-9-external-1 2 } + +wish-9-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-9-external-1 3 } + +wish-9-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-9-external-1 4 } + +wish-9-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-9-external-1 5 } + +wish-9-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-9-external-1 6 } + +wish-9-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-9-external-2 1 } + +wish-9-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-9-external-2 2 } + +wish-9-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-9-external-2 3 } + +wish-9-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-9-external-2 4 } + +wish-9-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-9-external-2 5 } + +wish-9-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-9-external-2 6 } + +wish-9-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-9-external 3 } + +wish-10-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-10 1 } + +wish-10-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-10 2 } + +wish-10-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-10 3 } + +wish-10-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-10-internal 1 } + +wish-10-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-10-internal 2 } + +wish-10-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-10-internal 3 } + +wish-10-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-10-external-1 1 } + +wish-10-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-10-external-1 2 } + +wish-10-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-10-external-1 3 } + +wish-10-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-10-external-1 4 } + +wish-10-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-10-external-1 5 } + +wish-10-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-10-external-1 6 } + +wish-10-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-10-external-2 1 } + +wish-10-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-10-external-2 2 } + +wish-10-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-10-external-2 3 } + +wish-10-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-10-external-2 4 } + +wish-10-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-10-external-2 5 } + +wish-10-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-10-external-2 6 } + +wish-10-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-10-external 3 } + +wish-11-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-11 1 } + +wish-11-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-11 2 } + +wish-11-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-11 3 } + +wish-11-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-11-internal 1 } + +wish-11-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-11-internal 2 } + +wish-11-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-11-internal 3 } + +wish-11-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-11-external-1 1 } + +wish-11-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-11-external-1 2 } + +wish-11-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-11-external-1 3 } + +wish-11-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-11-external-1 4 } + +wish-11-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-11-external-1 5 } + +wish-11-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-11-external-1 6 } + +wish-11-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-11-external-2 1 } + +wish-11-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-11-external-2 2 } + +wish-11-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-11-external-2 3 } + +wish-11-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-11-external-2 4 } + +wish-11-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-11-external-2 5 } + +wish-11-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-11-external-2 6 } + +wish-11-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-11-external 3 } + +wish-12-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-12 1 } + +wish-12-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-12 2 } + +wish-12-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-12 3 } + +wish-12-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-12-internal 1 } + +wish-12-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-12-internal 2 } + +wish-12-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-12-internal 3 } + +wish-12-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-12-external-1 1 } + +wish-12-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-12-external-1 2 } + +wish-12-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-12-external-1 3 } + +wish-12-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-12-external-1 4 } + +wish-12-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-12-external-1 5 } + +wish-12-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-12-external-1 6 } + +wish-12-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-12-external-2 1 } + +wish-12-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-12-external-2 2 } + +wish-12-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-12-external-2 3 } + +wish-12-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-12-external-2 4 } + +wish-12-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-12-external-2 5 } + +wish-12-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-12-external-2 6 } + +wish-12-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-12-external 3 } + +wish-13-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-13 1 } + +wish-13-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-13 2 } + +wish-13-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-13 3 } + +wish-13-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-13-internal 1 } + +wish-13-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-13-internal 2 } + +wish-13-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-13-internal 3 } + +wish-13-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-13-external-1 1 } + +wish-13-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-13-external-1 2 } + +wish-13-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-13-external-1 3 } + +wish-13-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-13-external-1 4 } + +wish-13-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-13-external-1 5 } + +wish-13-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-13-external-1 6 } + +wish-13-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-13-external-2 1 } + +wish-13-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-13-external-2 2 } + +wish-13-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-13-external-2 3 } + +wish-13-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-13-external-2 4 } + +wish-13-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-13-external-2 5 } + +wish-13-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-13-external-2 6 } + +wish-13-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-13-external 3 } + +wish-14-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-14 1 } + +wish-14-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-14 2 } + +wish-14-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-14 3 } + +wish-14-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-14-internal 1 } + +wish-14-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-14-internal 2 } + +wish-14-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-14-internal 3 } + +wish-14-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-14-external-1 1 } + +wish-14-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-14-external-1 2 } + +wish-14-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-14-external-1 3 } + +wish-14-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-14-external-1 4 } + +wish-14-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-14-external-1 5 } + +wish-14-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-14-external-1 6 } + +wish-14-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-14-external-2 1 } + +wish-14-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-14-external-2 2 } + +wish-14-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-14-external-2 3 } + +wish-14-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-14-external-2 4 } + +wish-14-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-14-external-2 5 } + +wish-14-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-14-external-2 6 } + +wish-14-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-14-external 3 } + +wish-15-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-15 1 } + +wish-15-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-15 2 } + +wish-15-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-15 3 } + +wish-15-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-15-internal 1 } + +wish-15-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-15-internal 2 } + +wish-15-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-15-internal 3 } + +wish-15-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-15-external-1 1 } + +wish-15-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-15-external-1 2 } + +wish-15-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-15-external-1 3 } + +wish-15-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-15-external-1 4 } + +wish-15-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-15-external-1 5 } + +wish-15-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-15-external-1 6 } + +wish-15-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-15-external-2 1 } + +wish-15-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-15-external-2 2 } + +wish-15-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-15-external-2 3 } + +wish-15-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-15-external-2 4 } + +wish-15-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-15-external-2 5 } + +wish-15-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-15-external-2 6 } + +wish-15-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-15-external 3 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- ROOMALERT26W TRAPS + +tempalarm1-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm1 trap signifies that the current + temperature on external sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-26w-snmp-trap TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-26w-snmp-trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmMessage variable." +::= 2 + +tempalarm2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm2 trap signifies that the current + temperature on external sensor 2 is outside the + defined high or low threshold." +::= 3 + +tempclear2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear2 trap signifies that the current + temperature on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 4 + +tempalarm3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm3 trap signifies that the current + temperature on external sensor 3 is outside the + defined high or low threshold." +::= 5 + +tempclear3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear3 trap signifies that the current + temperature on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 6 + +humidityalarm1-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm1 trap signifies that the current + humidity on external sensor 1 is outside the + defined high or low threshold." +::= 7 + +humidityclear1-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear1 trap signifies that the current + humidity on external sensor 1 has returned to + a normal condition and is within the defined + high or low threshold." +::= 8 + +humidityalarm2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm2 trap signifies that the current + humidity on external sensor 2 is outside the + defined high or low threshold." +::= 9 + +humidityclear2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear2 trap signifies that the current + humidity on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 10 + +humidityalarm3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm3 trap signifies that the current + humidity on external sensor 3 is outside the + defined high or low threshold." +::= 11 + +humidityclear3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear3 trap signifies that the current + humidity on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 12 + +switchalarm1-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 13 + +switchclear1-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear1 trap signifies that the switch sensor 1 + has returned to a normal state." +::= 14 + +switchalarm2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm2 trap signifies that switch sensor 2 + is in an alarm state." +::= 15 + +switchclear2-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear2 trap signifies that the switch sensor 2 + has returned to a normal state." +::= 16 + +switchalarm3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm3 trap signifies that switch sensor 3 + is in an alarm state." +::= 17 + +switchclear3-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear3 trap signifies that the switch sensor 3 + has returned to a normal state." +::= 18 + +switchalarm4-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm4 trap signifies that switch sensor 4 + is in an alarm state." +::= 19 + +switchclear4-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear4 trap signifies that the switch sensor 4 + has returned to a normal state." +::= 20 + +switchalarm5-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm5 trap signifies that switch sensor 5 + is in an alarm state." +::= 21 + +switchclear5-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear5 trap signifies that the switch sensor 5 + has returned to a normal state." +::= 22 + +switchalarm6-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm6 trap signifies that switch sensor 6 + is in an alarm state." +::= 23 + +switchclear6-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear6 trap signifies that the switch sensor 6 + has returned to a normal state." +::= 24 + +switchalarm7-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm7 trap signifies that switch sensor 7 + is in an alarm state." +::= 25 + +switchclear7-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear7 trap signifies that the switch sensor 7 + has returned to a normal state." +::= 26 + +switchalarm8-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm8 trap signifies that switch sensor 8 + is in an alarm state." +::= 27 + +switchclear8-26w TRAP-TYPE + ENTERPRISE roomalert26w + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear8 trap signifies that the switch sensor 8 + has returned to a normal state." +::= 28 +END diff --git a/mibs/avtech/ROOMALERT32E.MIB b/mibs/avtech/ROOMALERT32E.MIB new file mode 100644 index 0000000000..7fefe26733 --- /dev/null +++ b/mibs/avtech/ROOMALERT32E.MIB @@ -0,0 +1,2943 @@ +-- +--defines ROOMALERT32E-MIB for SNMP agent extension of Room Alert 32E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.0 +--Date: WEB_RELEASE_DATE_PLACEHOLDER +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) COPYRIGHT_YEAR_PLACEHOLDER AVTECH Software, Inc. +-- + +ROOMALERT32E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert32E OBJECT IDENTIFIER ::= { products 8 } +sensors OBJECT IDENTIFIER ::= { roomalert32E 1 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +humidity OBJECT IDENTIFIER ::= { internal 2 } +power OBJECT IDENTIFIER ::= { internal 3 } +heat-index OBJECT IDENTIFIER ::= { internal 4 } +analog OBJECT IDENTIFIER ::= { internal 5 } +relay OBJECT IDENTIFIER ::= { internal 6 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +digital-sen3 OBJECT IDENTIFIER ::= { digital 3 } +digital-sen4 OBJECT IDENTIFIER ::= { digital 4 } +digital-sen5 OBJECT IDENTIFIER ::= { digital 5 } +digital-sen6 OBJECT IDENTIFIER ::= { digital 6 } +digital-sen7 OBJECT IDENTIFIER ::= { digital 7 } +digital-sen8 OBJECT IDENTIFIER ::= { digital 8 } +switch OBJECT IDENTIFIER ::= { sensors 3 } +wireless OBJECT IDENTIFIER ::= { sensors 4 } +wish-1 OBJECT IDENTIFIER ::= { wireless 1 } +wish-1-sensors OBJECT IDENTIFIER ::= { wish-1 4 } +wish-1-internal OBJECT IDENTIFIER ::= { wish-1-sensors 1 } +wish-1-external OBJECT IDENTIFIER ::= { wish-1-sensors 2 } +wish-1-external-1 OBJECT IDENTIFIER ::= { wish-1-external 1 } +wish-1-external-2 OBJECT IDENTIFIER ::= { wish-1-external 2 } +wish-2 OBJECT IDENTIFIER ::= { wireless 2 } +wish-2-sensors OBJECT IDENTIFIER ::= { wish-2 4 } +wish-2-internal OBJECT IDENTIFIER ::= { wish-2-sensors 1 } +wish-2-external OBJECT IDENTIFIER ::= { wish-2-sensors 2 } +wish-2-external-1 OBJECT IDENTIFIER ::= { wish-2-external 1 } +wish-2-external-2 OBJECT IDENTIFIER ::= { wish-2-external 2 } +wish-3 OBJECT IDENTIFIER ::= { wireless 3 } +wish-3-sensors OBJECT IDENTIFIER ::= { wish-3 4 } +wish-3-internal OBJECT IDENTIFIER ::= { wish-3-sensors 1 } +wish-3-external OBJECT IDENTIFIER ::= { wish-3-sensors 2 } +wish-3-external-1 OBJECT IDENTIFIER ::= { wish-3-external 1 } +wish-3-external-2 OBJECT IDENTIFIER ::= { wish-3-external 2 } +wish-4 OBJECT IDENTIFIER ::= { wireless 4 } +wish-4-sensors OBJECT IDENTIFIER ::= { wish-4 4 } +wish-4-internal OBJECT IDENTIFIER ::= { wish-4-sensors 1 } +wish-4-external OBJECT IDENTIFIER ::= { wish-4-sensors 2 } +wish-4-external-1 OBJECT IDENTIFIER ::= { wish-4-external 1 } +wish-4-external-2 OBJECT IDENTIFIER ::= { wish-4-external 2 } +wish-5 OBJECT IDENTIFIER ::= { wireless 5 } +wish-5-sensors OBJECT IDENTIFIER ::= { wish-5 4 } +wish-5-internal OBJECT IDENTIFIER ::= { wish-5-sensors 1 } +wish-5-external OBJECT IDENTIFIER ::= { wish-5-sensors 2 } +wish-5-external-1 OBJECT IDENTIFIER ::= { wish-5-external 1 } +wish-5-external-2 OBJECT IDENTIFIER ::= { wish-5-external 2 } +wish-6 OBJECT IDENTIFIER ::= { wireless 6 } +wish-6-sensors OBJECT IDENTIFIER ::= { wish-6 4 } +wish-6-internal OBJECT IDENTIFIER ::= { wish-6-sensors 1 } +wish-6-external OBJECT IDENTIFIER ::= { wish-6-sensors 2 } +wish-6-external-1 OBJECT IDENTIFIER ::= { wish-6-external 1 } +wish-6-external-2 OBJECT IDENTIFIER ::= { wish-6-external 2 } +wish-7 OBJECT IDENTIFIER ::= { wireless 7 } +wish-7-sensors OBJECT IDENTIFIER ::= { wish-7 4 } +wish-7-internal OBJECT IDENTIFIER ::= { wish-7-sensors 1 } +wish-7-external OBJECT IDENTIFIER ::= { wish-7-sensors 2 } +wish-7-external-1 OBJECT IDENTIFIER ::= { wish-7-external 1 } +wish-7-external-2 OBJECT IDENTIFIER ::= { wish-7-external 2 } +wish-8 OBJECT IDENTIFIER ::= { wireless 8 } +wish-8-sensors OBJECT IDENTIFIER ::= { wish-8 4 } +wish-8-internal OBJECT IDENTIFIER ::= { wish-8-sensors 1 } +wish-8-external OBJECT IDENTIFIER ::= { wish-8-sensors 2 } +wish-8-external-1 OBJECT IDENTIFIER ::= { wish-8-external 1 } +wish-8-external-2 OBJECT IDENTIFIER ::= { wish-8-external 2 } +wish-9 OBJECT IDENTIFIER ::= { wireless 9 } +wish-9-sensors OBJECT IDENTIFIER ::= { wish-9 4 } +wish-9-internal OBJECT IDENTIFIER ::= { wish-9-sensors 1 } +wish-9-external OBJECT IDENTIFIER ::= { wish-9-sensors 2 } +wish-9-external-1 OBJECT IDENTIFIER ::= { wish-9-external 1 } +wish-9-external-2 OBJECT IDENTIFIER ::= { wish-9-external 2 } +wish-10 OBJECT IDENTIFIER ::= { wireless 10 } +wish-10-sensors OBJECT IDENTIFIER ::= { wish-10 4 } +wish-10-internal OBJECT IDENTIFIER ::= { wish-10-sensors 1 } +wish-10-external OBJECT IDENTIFIER ::= { wish-10-sensors 2 } +wish-10-external-1 OBJECT IDENTIFIER ::= { wish-10-external 1 } +wish-10-external-2 OBJECT IDENTIFIER ::= { wish-10-external 2 } +wish-11 OBJECT IDENTIFIER ::= { wireless 11 } +wish-11-sensors OBJECT IDENTIFIER ::= { wish-11 4 } +wish-11-internal OBJECT IDENTIFIER ::= { wish-11-sensors 1 } +wish-11-external OBJECT IDENTIFIER ::= { wish-11-sensors 2 } +wish-11-external-1 OBJECT IDENTIFIER ::= { wish-11-external 1 } +wish-11-external-2 OBJECT IDENTIFIER ::= { wish-11-external 2 } +wish-12 OBJECT IDENTIFIER ::= { wireless 12 } +wish-12-sensors OBJECT IDENTIFIER ::= { wish-12 4 } +wish-12-internal OBJECT IDENTIFIER ::= { wish-12-sensors 1 } +wish-12-external OBJECT IDENTIFIER ::= { wish-12-sensors 2 } +wish-12-external-1 OBJECT IDENTIFIER ::= { wish-12-external 1 } +wish-12-external-2 OBJECT IDENTIFIER ::= { wish-12-external 2 } +wish-13 OBJECT IDENTIFIER ::= { wireless 13 } +wish-13-sensors OBJECT IDENTIFIER ::= { wish-13 4 } +wish-13-internal OBJECT IDENTIFIER ::= { wish-13-sensors 1 } +wish-13-external OBJECT IDENTIFIER ::= { wish-13-sensors 2 } +wish-13-external-1 OBJECT IDENTIFIER ::= { wish-13-external 1 } +wish-13-external-2 OBJECT IDENTIFIER ::= { wish-13-external 2 } +wish-14 OBJECT IDENTIFIER ::= { wireless 14 } +wish-14-sensors OBJECT IDENTIFIER ::= { wish-14 4 } +wish-14-internal OBJECT IDENTIFIER ::= { wish-14-sensors 1 } +wish-14-external OBJECT IDENTIFIER ::= { wish-14-sensors 2 } +wish-14-external-1 OBJECT IDENTIFIER ::= { wish-14-external 1 } +wish-14-external-2 OBJECT IDENTIFIER ::= { wish-14-external 2 } +wish-15 OBJECT IDENTIFIER ::= { wireless 15 } +wish-15-sensors OBJECT IDENTIFIER ::= { wish-15 4 } +wish-15-internal OBJECT IDENTIFIER ::= { wish-15-sensors 1 } +wish-15-external OBJECT IDENTIFIER ::= { wish-15-sensors 2 } +wish-15-external-1 OBJECT IDENTIFIER ::= { wish-15-external 1 } +wish-15-external-2 OBJECT IDENTIFIER ::= { wish-15-external 2 } +traps OBJECT IDENTIFIER ::= { roomalert32E 2 } +lighttowers OBJECT IDENTIFIER ::= { roomalert32E 3 } +lighttower1 OBJECT IDENTIFIER ::= { lighttowers 1 } +lighttower2 OBJECT IDENTIFIER ::= { lighttowers 2 } + +-- Room Alert 32E MIB +-- Parameters + + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +internal-humidity OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal relative humidity reading in %RH. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { humidity 1 } + +internal-heat-index OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS optional + DESCRIPTION "The internal heat index reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 1 } + +internal-heat-indexC OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS optional + DESCRIPTION "The internal heat index reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 2 } + +internal-power OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 32E power supply. A '1' indicates the unit is running on AC/Utility power. A '0' indicates the unit is running on battery backup power." +::= { power 1 } + +internal-analog1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 32E analog input (0-5VDC)." +::= { analog 1 } + +internal-analog2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current status of the Room Alert 32E analog input (0-5VDC)." +::= { analog 2 } + +internal-relay1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the on-board relay output" +::= { relay 1 } + +internal-relay2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the on-board relay output" +::= { relay 2 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen1 5 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +digital-sen2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen2 5 } + +digital-sen3-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen3 1 } + +digital-sen3-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen3 2 } + +digital-sen3-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen3 3 } + +digital-sen3-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen3 4 } + +digital-sen3-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen3 5 } + +digital-sen4-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen4 1 } + +digital-sen4-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen4 2 } + +digital-sen4-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen4 3 } + +digital-sen4-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen4 4 } + +digital-sen4-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen4 5 } + +digital-sen5-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen5 1 } + +digital-sen5-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen5 2 } + +digital-sen5-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen5 3 } + +digital-sen5-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen5 4 } + +digital-sen5-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen5 5 } + +digital-sen6-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen6 1 } + +digital-sen6-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen6 2 } + +digital-sen6-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen6 3 } + +digital-sen6-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen6 4 } + +digital-sen6-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen6 5 } + +digital-sen7-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen7 1 } + +digital-sen7-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen7 2 } + +digital-sen7-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen7 3 } + +digital-sen7-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen7 4 } + +digital-sen7-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen7 5 } + +digital-sen8-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen8 1 } + +digital-sen8-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen8 2 } + +digital-sen8-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen8 3 } + +digital-sen8-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen8 4 } + +digital-sen8-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen8 5 } + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +switch-sen2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 2 (0 = OPEN, 1 = CLOSED)." +::= { switch 2 } + +switch-sen3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 3 (0 = OPEN, 1 = CLOSED)." +::= { switch 3 } + +switch-sen4 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 4 (0 = OPEN, 1 = CLOSED)." +::= { switch 4 } + +switch-sen5 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 5 (0 = OPEN, 1 = CLOSED)." +::= { switch 5 } + +switch-sen6 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 6 (0 = OPEN, 1 = CLOSED)." +::= { switch 6 } + +switch-sen7 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 7 (0 = OPEN, 1 = CLOSED)." +::= { switch 7 } + +switch-sen8 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 8 (0 = OPEN, 1 = CLOSED)." +::= { switch 8 } + +switch-sen9 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 9 (0 = OPEN, 1 = CLOSED)." +::= { switch 9 } + +switch-sen10 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 10 (0 = OPEN, 1 = CLOSED)." +::= { switch 10 } + +switch-sen11 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 11 (0 = OPEN, 1 = CLOSED)." +::= { switch 11 } + +switch-sen12 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 12 (0 = OPEN, 1 = CLOSED)." +::= { switch 12 } + +switch-sen13 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 13 (0 = OPEN, 1 = CLOSED)." +::= { switch 13 } + +switch-sen14 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 14 (0 = OPEN, 1 = CLOSED)." +::= { switch 14 } + +switch-sen15 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 15 (0 = OPEN, 1 = CLOSED)." +::= { switch 15 } + +switch-sen16 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 16 (0 = OPEN, 1 = CLOSED)." +::= { switch 16 } + +wish-1-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-1 1 } + +wish-1-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-1 2 } + +wish-1-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-1 3 } + +wish-1-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-1-internal 1 } + +wish-1-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-1-internal 2 } + +wish-1-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-1-internal 3 } + +wish-1-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-1-external-1 1 } + +wish-1-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-1-external-1 2 } + +wish-1-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-1-external-1 3 } + +wish-1-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-1-external-1 4 } + +wish-1-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-1-external-1 5 } + +wish-1-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-1-external-1 6 } + +wish-1-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-1-external-2 1 } + +wish-1-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-1-external-2 2 } + +wish-1-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-1-external-2 3 } + +wish-1-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-1-external-2 4 } + +wish-1-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-1-external-2 5 } + +wish-1-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-1-external-2 6 } + +wish-1-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-1-external 3 } + +wish-2-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-2 1 } + +wish-2-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-2 2 } + +wish-2-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-2 3 } + +wish-2-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-2-internal 1 } + +wish-2-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-2-internal 2 } + +wish-2-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-2-internal 3 } + +wish-2-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-2-external-1 1 } + +wish-2-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-2-external-1 2 } + +wish-2-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-2-external-1 3 } + +wish-2-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-2-external-1 4 } + +wish-2-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-2-external-1 5 } + +wish-2-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-2-external-1 6 } + +wish-2-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-2-external-2 1 } + +wish-2-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-2-external-2 2 } + +wish-2-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-2-external-2 3 } + +wish-2-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-2-external-2 4 } + +wish-2-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-2-external-2 5 } + +wish-2-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-2-external-2 6 } + +wish-2-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-2-external 3 } + +wish-3-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-3 1 } + +wish-3-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-3 2 } + +wish-3-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-3 3 } + +wish-3-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-3-internal 1 } + +wish-3-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-3-internal 2 } + +wish-3-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-3-internal 3 } + +wish-3-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-3-external-1 1 } + +wish-3-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-3-external-1 2 } + +wish-3-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-3-external-1 3 } + +wish-3-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-3-external-1 4 } + +wish-3-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-3-external-1 5 } + +wish-3-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-3-external-1 6 } + +wish-3-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-3-external-2 1 } + +wish-3-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-3-external-2 2 } + +wish-3-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-3-external-2 3 } + +wish-3-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-3-external-2 4 } + +wish-3-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-3-external-2 5 } + +wish-3-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-3-external-2 6 } + +wish-3-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-3-external 3 } + +wish-4-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-4 1 } + +wish-4-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-4 2 } + +wish-4-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-4 3 } + +wish-4-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-4-internal 1 } + +wish-4-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-4-internal 2 } + +wish-4-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-4-internal 3 } + +wish-4-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-4-external-1 1 } + +wish-4-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-4-external-1 2 } + +wish-4-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-4-external-1 3 } + +wish-4-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-4-external-1 4 } + +wish-4-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-4-external-1 5 } + +wish-4-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-4-external-1 6 } + +wish-4-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-4-external-2 1 } + +wish-4-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-4-external-2 2 } + +wish-4-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-4-external-2 3 } + +wish-4-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-4-external-2 4 } + +wish-4-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-4-external-2 5 } + +wish-4-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-4-external-2 6 } + +wish-4-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-4-external 3 } + +wish-5-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-5 1 } + +wish-5-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-5 2 } + +wish-5-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-5 3 } + +wish-5-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-5-internal 1 } + +wish-5-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-5-internal 2 } + +wish-5-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-5-internal 3 } + +wish-5-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-5-external-1 1 } + +wish-5-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-5-external-1 2 } + +wish-5-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-5-external-1 3 } + +wish-5-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-5-external-1 4 } + +wish-5-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-5-external-1 5 } + +wish-5-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-5-external-1 6 } + +wish-5-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-5-external-2 1 } + +wish-5-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-5-external-2 2 } + +wish-5-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-5-external-2 3 } + +wish-5-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-5-external-2 4 } + +wish-5-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-5-external-2 5 } + +wish-5-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-5-external-2 6 } + +wish-5-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-5-external 3 } + +wish-6-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-6 1 } + +wish-6-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-6 2 } + +wish-6-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-6 3 } + +wish-6-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-6-internal 1 } + +wish-6-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-6-internal 2 } + +wish-6-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-6-internal 3 } + +wish-6-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-6-external-1 1 } + +wish-6-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-6-external-1 2 } + +wish-6-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-6-external-1 3 } + +wish-6-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-6-external-1 4 } + +wish-6-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-6-external-1 5 } + +wish-6-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-6-external-1 6 } + +wish-6-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-6-external-2 1 } + +wish-6-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-6-external-2 2 } + +wish-6-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-6-external-2 3 } + +wish-6-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-6-external-2 4 } + +wish-6-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-6-external-2 5 } + +wish-6-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-6-external-2 6 } + +wish-6-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-6-external 3 } + +wish-7-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-7 1 } + +wish-7-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-7 2 } + +wish-7-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-7 3 } + +wish-7-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-7-internal 1 } + +wish-7-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-7-internal 2 } + +wish-7-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-7-internal 3 } + +wish-7-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-7-external-1 1 } + +wish-7-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-7-external-1 2 } + +wish-7-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-7-external-1 3 } + +wish-7-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-7-external-1 4 } + +wish-7-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-7-external-1 5 } + +wish-7-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-7-external-1 6 } + +wish-7-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-7-external-2 1 } + +wish-7-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-7-external-2 2 } + +wish-7-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-7-external-2 3 } + +wish-7-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-7-external-2 4 } + +wish-7-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-7-external-2 5 } + +wish-7-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-7-external-2 6 } + +wish-7-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-7-external 3 } + +wish-8-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-8 1 } + +wish-8-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-8 2 } + +wish-8-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-8 3 } + +wish-8-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-8-internal 1 } + +wish-8-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-8-internal 2 } + +wish-8-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-8-internal 3 } + +wish-8-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-8-external-1 1 } + +wish-8-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-8-external-1 2 } + +wish-8-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-8-external-1 3 } + +wish-8-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-8-external-1 4 } + +wish-8-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-8-external-1 5 } + +wish-8-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-8-external-1 6 } + +wish-8-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-8-external-2 1 } + +wish-8-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-8-external-2 2 } + +wish-8-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-8-external-2 3 } + +wish-8-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-8-external-2 4 } + +wish-8-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-8-external-2 5 } + +wish-8-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-8-external-2 6 } + +wish-8-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-8-external 3 } + +wish-9-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-9 1 } + +wish-9-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-9 2 } + +wish-9-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-9 3 } + +wish-9-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-9-internal 1 } + +wish-9-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-9-internal 2 } + +wish-9-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-9-internal 3 } + +wish-9-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-9-external-1 1 } + +wish-9-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-9-external-1 2 } + +wish-9-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-9-external-1 3 } + +wish-9-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-9-external-1 4 } + +wish-9-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-9-external-1 5 } + +wish-9-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-9-external-1 6 } + +wish-9-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-9-external-2 1 } + +wish-9-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-9-external-2 2 } + +wish-9-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-9-external-2 3 } + +wish-9-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-9-external-2 4 } + +wish-9-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-9-external-2 5 } + +wish-9-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-9-external-2 6 } + +wish-9-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-9-external 3 } + +wish-10-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-10 1 } + +wish-10-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-10 2 } + +wish-10-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-10 3 } + +wish-10-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-10-internal 1 } + +wish-10-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-10-internal 2 } + +wish-10-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-10-internal 3 } + +wish-10-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-10-external-1 1 } + +wish-10-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-10-external-1 2 } + +wish-10-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-10-external-1 3 } + +wish-10-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-10-external-1 4 } + +wish-10-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-10-external-1 5 } + +wish-10-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-10-external-1 6 } + +wish-10-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-10-external-2 1 } + +wish-10-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-10-external-2 2 } + +wish-10-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-10-external-2 3 } + +wish-10-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-10-external-2 4 } + +wish-10-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-10-external-2 5 } + +wish-10-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-10-external-2 6 } + +wish-10-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-10-external 3 } + +wish-11-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-11 1 } + +wish-11-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-11 2 } + +wish-11-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-11 3 } + +wish-11-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-11-internal 1 } + +wish-11-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-11-internal 2 } + +wish-11-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-11-internal 3 } + +wish-11-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-11-external-1 1 } + +wish-11-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-11-external-1 2 } + +wish-11-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-11-external-1 3 } + +wish-11-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-11-external-1 4 } + +wish-11-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-11-external-1 5 } + +wish-11-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-11-external-1 6 } + +wish-11-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-11-external-2 1 } + +wish-11-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-11-external-2 2 } + +wish-11-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-11-external-2 3 } + +wish-11-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-11-external-2 4 } + +wish-11-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-11-external-2 5 } + +wish-11-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-11-external-2 6 } + +wish-11-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-11-external 3 } + +wish-12-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-12 1 } + +wish-12-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-12 2 } + +wish-12-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-12 3 } + +wish-12-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-12-internal 1 } + +wish-12-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-12-internal 2 } + +wish-12-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-12-internal 3 } + +wish-12-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-12-external-1 1 } + +wish-12-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-12-external-1 2 } + +wish-12-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-12-external-1 3 } + +wish-12-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-12-external-1 4 } + +wish-12-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-12-external-1 5 } + +wish-12-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-12-external-1 6 } + +wish-12-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-12-external-2 1 } + +wish-12-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-12-external-2 2 } + +wish-12-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-12-external-2 3 } + +wish-12-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-12-external-2 4 } + +wish-12-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-12-external-2 5 } + +wish-12-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-12-external-2 6 } + +wish-12-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-12-external 3 } + +wish-13-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-13 1 } + +wish-13-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-13 2 } + +wish-13-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-13 3 } + +wish-13-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-13-internal 1 } + +wish-13-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-13-internal 2 } + +wish-13-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-13-internal 3 } + +wish-13-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-13-external-1 1 } + +wish-13-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-13-external-1 2 } + +wish-13-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-13-external-1 3 } + +wish-13-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-13-external-1 4 } + +wish-13-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-13-external-1 5 } + +wish-13-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-13-external-1 6 } + +wish-13-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-13-external-2 1 } + +wish-13-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-13-external-2 2 } + +wish-13-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-13-external-2 3 } + +wish-13-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-13-external-2 4 } + +wish-13-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-13-external-2 5 } + +wish-13-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-13-external-2 6 } + +wish-13-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-13-external 3 } + +wish-14-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-14 1 } + +wish-14-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-14 2 } + +wish-14-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-14 3 } + +wish-14-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-14-internal 1 } + +wish-14-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-14-internal 2 } + +wish-14-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-14-internal 3 } + +wish-14-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-14-external-1 1 } + +wish-14-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-14-external-1 2 } + +wish-14-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-14-external-1 3 } + +wish-14-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-14-external-1 4 } + +wish-14-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-14-external-1 5 } + +wish-14-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-14-external-1 6 } + +wish-14-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-14-external-2 1 } + +wish-14-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-14-external-2 2 } + +wish-14-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-14-external-2 3 } + +wish-14-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-14-external-2 4 } + +wish-14-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-14-external-2 5 } + +wish-14-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-14-external-2 6 } + +wish-14-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-14-external 3 } + +wish-15-enabled OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current 'enabled' status for this WiSH/WiSPR Sensor. A '0' indicates the WiSH/WiSPR is disabled. A '1' indicates the WiSH/WiSPR is enabled." +::= { wish-15 1 } + +wish-15-serial-num OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The unique serial number for this WiSH/WiSPR Sensor." +::= { wish-15 2 } + +wish-15-updates OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current update interval for this WiSH/WiSPR Sensor." +::= { wish-15 3 } + +wish-15-battery-voltage OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current voltage reading of the internal battery for this WiSH/WiSPR Sensor." +::= { wish-15-internal 1 } + +wish-15-internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Celsius (C) for this WiSH/WiSPR Sensor." +::= { wish-15-internal 2 } + +wish-15-internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature of the internal sensor in Fahrenheit (F) for this WiSH/WiSPR Sensor." +::= { wish-15-internal 3 } + +wish-15-external-1-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-15-external-1 1 } + +wish-15-external-1-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-15-external-1 2 } + +wish-15-external-1-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-15-external-1 3 } + +wish-15-external-1-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-15-external-1 4 } + +wish-15-external-1-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-15-external-1 5 } + +wish-15-external-1-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-15-external-1 6 } + +wish-15-external-2-type OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The sensor type of the digital sensor attached to this digital sensor port on the WiSH/WiSPR Sensor." +::= { wish-15-external-2 1 } + +wish-15-external-2-val1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Current reading in Amperage." +::= { wish-15-external-2 2 } + +wish-15-external-2-val2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Power reading in Watts." +::= { wish-15-external-2 3 } + +wish-15-external-2-val3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Voltage reading in Volts." +::= { wish-15-external-2 4 } + +wish-15-external-2-val4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor and connection of a Digital Power Sensor is supported by your model, this value represents the Reference reading in Volts." +::= { wish-15-external-2 5 } + +wish-15-external-2-val5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { wish-15-external-2 6 } + +wish-15-external-switch OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor contacts of this WiSH/WiSPR Sensor (0 = OPEN, 1 = CLOSED)." +::= { wish-15-external 3 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- Lighttower1 + +lighttower1-RE OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the red LED on the Light Tower." +::= { lighttower1 1 } + +lighttower1-OR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the orange LED on the Light Tower." +::= { lighttower1 2 } + +lighttower1-GR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the green LED on the Light Tower." +::= { lighttower1 3 } + +lighttower1-WH OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the white LED on the Light Tower." +::= { lighttower1 4 } + +lighttower1-BL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the blue LED on the Light Tower." +::= { lighttower1 5 } + +lighttower1-A1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 1st audio alarm on the Light Tower." +::= { lighttower1 6 } + +lighttower1-A2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 2nd audio alarm on the Light Tower." +::= { lighttower1 7 } + +lighttower1-RL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the LTA's on-board relay output" +::= { lighttower1 8 } + + +-- Lighttower2 + +lighttower2-RE OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the red LED on the Light Tower." +::= { lighttower2 1 } + +lighttower2-OR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the orange LED on the Light Tower." +::= { lighttower2 2 } + +lighttower2-GR OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the green LED on the Light Tower." +::= { lighttower2 3 } + +lighttower2-WH OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the white LED on the Light Tower." +::= { lighttower2 4 } + +lighttower2-BL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the blue LED on the Light Tower." +::= { lighttower2 5 } + +lighttower2-A1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 1st audio alarm on the Light Tower." +::= { lighttower2 6 } + +lighttower2-A2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the 2nd audio alarm on the Light Tower." +::= { lighttower2 7 } + +lighttower2-RL OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS mandatory + DESCRIPTION "The status of the LTA's on-board relay output" +::= { lighttower2 8 } + + +-- ROOMALERT32E TRAPS + +tempalarm1-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm1 trap signifies that the current + temperature on external sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-32E-snmp-trap TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-32E-snmp-trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmMessage variable." +::= 2 + +tempalarm2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm2 trap signifies that the current + temperature on external sensor 2 is outside the + defined high or low threshold." +::= 3 + +tempclear2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear2 trap signifies that the current + temperature on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 4 + +tempalarm3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A tempalarm3 trap signifies that the current + temperature on external sensor 3 is outside the + defined high or low threshold." +::= 5 + +tempclear3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A tempclear3 trap signifies that the current + temperature on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 6 + +humidityalarm1-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm1 trap signifies that the current + humidity on external sensor 1 is outside the + defined high or low threshold." +::= 7 + +humidityclear1-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear1 trap signifies that the current + humidity on external sensor 1 has returned to + a normal condition and is within the defined + high or low threshold." +::= 8 + +humidityalarm2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm2 trap signifies that the current + humidity on external sensor 2 is outside the + defined high or low threshold." +::= 9 + +humidityclear2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear2 trap signifies that the current + humidity on external sensor 2 has returned to + a normal condition and is within the defined + high or low threshold." +::= 10 + +humidityalarm3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityalarm3 trap signifies that the current + humidity on external sensor 3 is outside the + defined high or low threshold." +::= 11 + +humidityclear3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A humidityclear3 trap signifies that the current + humidity on external sensor 3 has returned to + a normal condition and is within the defined + high or low threshold." +::= 12 + +switchalarm1-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm1 trap signifies that switch sensor 1 + is in an alarm state." +::= 13 + +switchclear1-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear1 trap signifies that the switch sensor 1 + has returned to a normal state." +::= 14 + +switchalarm2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm2 trap signifies that switch sensor 2 + is in an alarm state." +::= 15 + +switchclear2-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear2 trap signifies that the switch sensor 2 + has returned to a normal state." +::= 16 + +switchalarm3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm3 trap signifies that switch sensor 3 + is in an alarm state." +::= 17 + +switchclear3-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear3 trap signifies that the switch sensor 3 + has returned to a normal state." +::= 18 + +switchalarm4-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm4 trap signifies that switch sensor 4 + is in an alarm state." +::= 19 + +switchclear4-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear4 trap signifies that the switch sensor 4 + has returned to a normal state." +::= 20 + +switchalarm5-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm5 trap signifies that switch sensor 5 + is in an alarm state." +::= 21 + +switchclear5-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear5 trap signifies that the switch sensor 5 + has returned to a normal state." +::= 22 + +switchalarm6-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm6 trap signifies that switch sensor 6 + is in an alarm state." +::= 23 + +switchclear6-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear6 trap signifies that the switch sensor 6 + has returned to a normal state." +::= 24 + +switchalarm7-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm7 trap signifies that switch sensor 7 + is in an alarm state." +::= 25 + +switchclear7-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear7 trap signifies that the switch sensor 7 + has returned to a normal state." +::= 26 + +switchalarm8-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchalarm8 trap signifies that switch sensor 8 + is in an alarm state." +::= 27 + +switchclear8-32E TRAP-TYPE + ENTERPRISE roomalert32E + VARIABLES { alarmmessage } + DESCRIPTION "A switchclear8 trap signifies that the switch sensor 8 + has returned to a normal state." +::= 28 +END diff --git a/mibs/avtech/ROOMALERT3E.MIB b/mibs/avtech/ROOMALERT3E.MIB new file mode 100644 index 0000000000..1cb0db5b71 --- /dev/null +++ b/mibs/avtech/ROOMALERT3E.MIB @@ -0,0 +1,183 @@ +-- +--defines ROOMALERT3E-MIB for SNMP agent extension of Room Alert 3E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.1 +--Date: WEB_RELEASE_DATE_PLACEHOLDER +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) COPYRIGHT_YEAR_PLACEHOLDER AVTECH Software, Inc. +-- + +ROOMALERT3E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +ROOMALERT3E OBJECT IDENTIFIER ::= { products 9 } +sensors OBJECT IDENTIFIER ::= { ROOMALERT3E 1 } +signaltower OBJECT IDENTIFIER ::= { ROOMALERT3E 2 } +traps OBJECT IDENTIFIER ::= { ROOMALERT3E 3 } +digital OBJECT IDENTIFIER ::= { sensors 1 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +switch OBJECT IDENTIFIER ::= { sensors 2 } + + +-- Room Alert 3E MIB +-- Parameters + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature reading in Celsius of the Internal Temperature Sensor." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The current temperature reading in Fahrenheit of the Internal Temperature Sensor." +::= { digital-sen1 2 } + +digital-sen1-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= {digital-sen1 3} + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit." +::= { digital-sen2 4 } + +digital-sen2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen2 5 } + +digital-sen2-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= {digital-sen2 6} + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for the switch sensor (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +switch-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for the switch sensor" +::= { switch 2 } + +red-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 1 } + +amber-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 2 } + +green-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 3 } + +blue-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 4 } + +white-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 5 } + +alarm1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 6 } + +alarm2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 7 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- ROOMALERT3E TRAPS + +room-alert-3e-snmp-trap TRAP-TYPE + ENTERPRISE ROOMALERT3E + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-3e-snmp-trap indicates that an alarm + condition has occurred on the sensor indicated + by the alarmmessage variable." +::= 2 +END diff --git a/mibs/avtech/ROOMALERT4E.MIB b/mibs/avtech/ROOMALERT4E.MIB new file mode 100644 index 0000000000..1a8efbcf9c --- /dev/null +++ b/mibs/avtech/ROOMALERT4E.MIB @@ -0,0 +1,229 @@ +-- +--defines ROOMALERT4E-MIB for SNMP agent extension of Room Alert 4E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.1 +--Date: WEB_RELEASE_DATE_PLACEHOLDER +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) COPYRIGHT_YEAR_PLACEHOLDER AVTECH Software, Inc. +-- + +ROOMALERT4E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +ROOMALERT4E OBJECT IDENTIFIER ::= { products 6 } +sensors OBJECT IDENTIFIER ::= { ROOMALERT4E 1 } +signaltower OBJECT IDENTIFIER ::= { ROOMALERT4E 2 } +traps OBJECT IDENTIFIER ::= { ROOMALERT4E 3 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +label OBJECT IDENTIFIER ::= { internal 2 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +switch OBJECT IDENTIFIER ::= { sensors 3 } + + +-- Room Alert 4E MIB +-- Parameters + + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +internal-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= { label 1 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit." +::= { digital-sen1 4 } + +digital-sen1-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen1 5 } + +digital-sen1-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= {digital-sen1 6} + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Fahrenheit." +::= { digital-sen2 4 } + +digital-sen2-5 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current heat index in Celsius." +::= { digital-sen2 5 } + +digital-sen2-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label of this sensor" +::= {digital-sen2 6} + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for the switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +switch-label OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "The label for the switch sensor" +::= { switch 2 } + +red-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 1 } + +amber-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 2 } + +green-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 3 } + +blue-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 4 } + +white-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 5 } + +alarm1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 6 } + +alarm2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Light Tower element (0 = OFF, 1 = ON)." +::= { signaltower 7 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- ROOMALERT4E TRAPS + +room-alert-4e-snmp-trap TRAP-TYPE + ENTERPRISE ROOMALERT4E + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-4e-snmp-trap indicates that an alarm + condition has occurred on the sensor indicated + by the alarmmessage variable." +::= 2 +END diff --git a/mibs/avtech/ROOMALERT7E.MIB b/mibs/avtech/ROOMALERT7E.MIB new file mode 100644 index 0000000000..9a11c32d88 --- /dev/null +++ b/mibs/avtech/ROOMALERT7E.MIB @@ -0,0 +1,362 @@ +-- +--defines ROOMALERT7E-MIB for SNMP agent extension of Room Alert 7E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 2.0.3 +--Date: 11 September 2007 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2007 AVTECH Software, Inc. +-- + +ROOMALERT7E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +roomalert7e OBJECT IDENTIFIER ::= { products 2 } +sensors OBJECT IDENTIFIER ::= { roomalert7e 1 } +traps OBJECT IDENTIFIER ::= { roomalert7e 2 } +thresholds OBJECT IDENTIFIER ::= { roomalert7e 3 } +temperature OBJECT IDENTIFIER ::= { sensors 1 } +switch OBJECT IDENTIFIER ::= { sensors 2 } + +-- Room Alert 7E MIB +-- Parameters + +tempreading1c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 1 (Celsius)" +::= { temperature 1 } + +tempreading2c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 2 (Celsius)" +::= { temperature 2 } + +tempreading3c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 3 (Celsius)" +::= { temperature 3 } + + +tempreading4c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 4 (Celsius)" +::= { temperature 4 } + +tempreading1f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 1 (Fahrenheit)" +::= { temperature 5 } + +tempreading2f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 2 (Fahrenheit)" +::= { temperature 6 } + +tempreading3f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 3 (Fahrenheit)" +::= { temperature 7 } + + +tempreading4f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 4 (Fahrenheit)" +::= { temperature 8 } + +switch1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Switch Sensor 1 (0 = OFF, 1 = ON)" +::= { switch 1 } + +switch2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Switch Sensor 2 (0 = OFF, 1 = ON)" +::= { switch 2 } + +switch3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Switch Sensor 3 (0 = OFF, 1 = ON)" +::= { switch 3 } + +alarmtemp1 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 1 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 1 } + +alarmtemp2 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 2 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 2 } + +alarmtemp3 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 3 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 3 } + +alarmtemp4 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 4 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 4 } + +alarmswitch1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for switch sensor 1 + 0 = Switch Open + 1 = Switch Closed" +::= { traps 5 } + +alarmswitch2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for switch sensor 2 + 0 = Switch Open + 1 = Switch Closed" +::= { traps 6 } + +alarmswitch3 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for switch sensor 3 + 0 = Switch Open + 1 = Switch Closed" +::= { traps 7 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-write + STATUS mandatory + DESCRIPTION "Message string to send with trap messages" +::= { traps 8 } + +upperlimit1 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 1" +::= { thresholds 1 } + +lowerlimit1 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 1" +::= { thresholds 2 } + +upperlimit2 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 2" +::= { thresholds 3 } + +lowerlimit2 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 2" +::= { thresholds 4 } + +upperlimit3 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 3" +::= { thresholds 5 } + +lowerlimit3 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 3" +::= { thresholds 6 } + +upperlimit4 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 4" +::= { thresholds 7 } + +lowerlimit4 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 4" +::= { thresholds 8 } + +-- ROOMALERT7E TRAPS + +alarmstart1-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading1c, tempreading1f } + DESCRIPTION "A alarmstart1 trap signifies that the current + temperature on sensor 1 is outside the + defined high or low threshold." +::= 1 + +room-alert-7e-snmp-trap TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-7e-snmp-trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmmessage variable." +::= 2 + +alarmstart2-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading2c, tempreading2f } + DESCRIPTION "A alarmstart2 trap signifies that the current + temperature on sensor 2 is outside the + defined high or low threshold." +::= 3 + +alarmclear2-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading2c, tempreading2f } + DESCRIPTION "A alarmclear2 trap signifies that the current + temperature on sensor 2 has returned to a + normal condition and is within the defined + high or low threshold." +::= 4 + +alarmstart3-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading3c, tempreading3f } + DESCRIPTION "A alarmstart3 trap signifies that the current + temperature on sensor 3 is outside the + defined high or low threshold." +::= 5 + +alarmclear3-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading3c, tempreading3f } + DESCRIPTION "A alarmclear3 trap signifies that the current + temperature on sensor 3 has returned to a + normal condition and is within the defined + high or low threshold." +::= 6 + +alarmstart4-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading4c, tempreading4f } + DESCRIPTION "A alarmstart4 trap signifies that the current + temperature on sensor 4 is outside the + defined high or low threshold." +::= 7 + +alarmclear4-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, tempreading4c, tempreading4f } + DESCRIPTION "A alarmclear4 trap signifies that the current + temperature on sensor 4 has returned to a + normal condition and is within the defined + high or low threshold." +::= 8 + +alarmstart5-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch1, switch1 } + DESCRIPTION "A alarmstart5 trap signifies that the current + status of switch sensor 1 is outside the + defined threshold." +::= 9 + +alarmclear5-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch1, switch1 } + DESCRIPTION "A alarmclear5 trap signifies that the current + status of switch sensor 1 is outside the + defined threshold." +::= 10 + +alarmstart6-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch2, switch2 } + DESCRIPTION "A alarmstart6 trap signifies that the current + status of switch sensor 2 is outside the + defined threshold." +::= 11 + +alarmclear6-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch2, switch2 } + DESCRIPTION "A alarmclear6 trap signifies that the current + status of switch sensor 2 is outside the + defined threshold." +::= 12 + +alarmstart7-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch3, switch3 } + DESCRIPTION "A alarmstart7 trap signifies that the current + status of switch sensor 3 is outside the + defined threshold." +::= 13 + +alarmclear7-7e TRAP-TYPE + ENTERPRISE roomalert7e + VARIABLES { alarmmessage, switch3, switch3 } + DESCRIPTION "A alarmclear7 trap signifies that the current + status of switch sensor 3 is outside the + defined threshold." +::= 14 +END diff --git a/mibs/avtech/ROOMALERTST.MIB b/mibs/avtech/ROOMALERTST.MIB new file mode 100644 index 0000000000..4321de4c7a --- /dev/null +++ b/mibs/avtech/ROOMALERTST.MIB @@ -0,0 +1,202 @@ +-- +--defines ROOMALERTST4E-MIB for SNMP agent extension of Room Alert ST4E +--Contents: Global Registration Module +-- Room Alert OIDs - Enterprise 20916 +--Version: 1.0.0 +--Date: 29 January 2008 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2008 AVTECH Software, Inc. +-- + +ROOMALERTST4E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +ROOMALERTST4E OBJECT IDENTIFIER ::= { products 6 } +sensors OBJECT IDENTIFIER ::= { ROOMALERTST4E 1 } +signaltower OBJECT IDENTIFIER ::= { ROOMALERTST4E 2 } +traps OBJECT IDENTIFIER ::= { ROOMALERTST4E 3 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +humidity OBJECT IDENTIFIER ::= { internal 2 } +heat-index OBJECT IDENTIFIER ::= { internal 3 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } +switch OBJECT IDENTIFIER ::= { sensors 3 } + + +-- Room Alert ST4E MIB +-- Parameters + + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +internal-humidity OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal relative humidity reading in %RH. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { humidity 1 } + +internal-heat-index OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal heat index reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { heat-index 1 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +switch-sen1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The reading for switch sensor 1 (0 = OPEN, 1 = CLOSED)." +::= { switch 1 } + +red-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 1 } + +amber-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 2 } + +green-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 3 } + +blue-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 4 } + +white-led OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 5 } + +alarm1 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 6 } + +alarm2 OBJECT-TYPE + SYNTAX INTEGER(0..1) + ACCESS read-write + STATUS current + DESCRIPTION "The status of this Signal Tower element (0 = OFF, 1 = ON)." +::= { signaltower 7 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- ROOMALERTST4E TRAPS + +room-alert-st4e-snmp-trap TRAP-TYPE + ENTERPRISE ROOMALERTST4E + VARIABLES { alarmmessage } + DESCRIPTION "A room-alert-st4e-snmp-trap indicates that an alarm + condition has occurred on the sensor indicated + by the alarmmessage variable." +::= 2 +END diff --git a/mibs/avtech/TEMPAGER.MIB b/mibs/avtech/TEMPAGER.MIB new file mode 100644 index 0000000000..47e8cd2de6 --- /dev/null +++ b/mibs/avtech/TEMPAGER.MIB @@ -0,0 +1,266 @@ +-- +--defines TEMPAGER-MIB for SNMP agent extension of TemPageR +--Contents: Global Registration Module +-- TemPageR OIDs - Enterprise 20916 +--Version: 2.5.1 +--Date: 11 September 2007 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2007 AVTECH Software, Inc. +-- + +TEMPAGER-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +tempager OBJECT IDENTIFIER ::= { products 1 } +sensors OBJECT IDENTIFIER ::= { tempager 1 } +traps OBJECT IDENTIFIER ::= { tempager 2 } +thresholds OBJECT IDENTIFIER ::= { tempager 3 } +temperature OBJECT IDENTIFIER ::= { sensors 1 } + +-- TemPageR MIB +-- Parameters + +tempreading1c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 1 (Celsius)" +::= { temperature 1 } + +tempreading2c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 2 (Celsius)" +::= { temperature 2 } + +tempreading3c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 3 (Celsius)" +::= { temperature 3 } + + +tempreading4c OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 4 (Celsius)" +::= { temperature 4 } + +tempreading1f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 1 (Fahrenheit)" +::= { temperature 5 } + +tempreading2f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 2 (Fahrenheit)" +::= { temperature 6 } + +tempreading3f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 3 (Fahrenheit)" +::= { temperature 7 } + + +tempreading4f OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Temperature Sensor 4 (Fahrenheit)" +::= { temperature 8 } + +alarmtemp1 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 1 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 1 } + +alarmtemp2 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 2 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 2 } + +alarmtemp3 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 3 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 3 } + +alarmtemp4 OBJECT-TYPE + SYNTAX INTEGER(0..2) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Alarm for temperature 4 + 0 = temperature OK + 1 = temperature too high + 2 = temperature too low" +::= { traps 4 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-write + STATUS mandatory + DESCRIPTION "Message string to send with trap messages" +::= { traps 5 } + +upperlimit1 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 1" +::= { thresholds 1 } + +lowerlimit1 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 1" +::= { thresholds 2 } + +upperlimit2 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 2" +::= { thresholds 3 } + +lowerlimit2 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 2" +::= { thresholds 4 } + +upperlimit3 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 3" +::= { thresholds 5 } + +lowerlimit3 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 3" +::= { thresholds 6 } + +upperlimit4 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "High temperature threshold for temperature sensor 4" +::= { thresholds 7 } + +lowerlimit4 OBJECT-TYPE + SYNTAX INTEGER(0..15000) + ACCESS read-only + STATUS mandatory + DESCRIPTION "Low temperature threshold for temperature sensor 4" +::= { thresholds 8 } + + +-- TEMPAGER TRAPS + +alarmstart1-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading1c, tempreading1f } + DESCRIPTION "A alarmstart1 trap signifies that the current + temperature on sensor 1 is outside the + defined high or low threshold." +::= 1 + +tempager-snmp-trap TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading1c, tempreading1f, tempreading2c, tempreading2f, tempreading3c, tempreading3f, tempreading4c, tempreading4f } + DESCRIPTION "A tempager-snmp-trap indicates that an alarm + condition has occurred on the sensor inidcated + by the alarmmessage variable." +::= 2 + +alarmstart2-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading2c, tempreading2f } + DESCRIPTION "A alarmstart2 trap signifies that the current + temperature on sensor 2 is outside the + defined high or low threshold." +::= 3 + +alarmclear2-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading2c, tempreading2f } + DESCRIPTION "A alarmclear2 trap signifies that the current + temperature on sensor 2 has returned to a + normal condition and is within the defined + high or low threshold." +::= 4 + +alarmstart3-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading3c, tempreading3f } + DESCRIPTION "A alarmstart3 trap signifies that the current + temperature on sensor 3 is outside the + defined high or low threshold." +::= 5 + +alarmclear3-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading3c, tempreading3f } + DESCRIPTION "A alarmclear3 trap signifies that the current + temperature on sensor 3 has returned to a + normal condition and is within the defined + high or low threshold." +::= 6 + +alarmstart4-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading4c, tempreading4f } + DESCRIPTION "A alarmstart4 trap signifies that the current + temperature on sensor 4 is outside the + defined high or low threshold." +::= 7 + +alarmclear4-t4 TRAP-TYPE + ENTERPRISE tempager + VARIABLES { alarmmessage, tempreading4c, tempreading4f } + DESCRIPTION "A alarmclear4 trap signifies that the current + temperature on sensor 4 has returned to a + normal condition and is within the defined + high or low threshold." +::= 8 +END diff --git a/mibs/avtech/TEMPAGER3E.MIB b/mibs/avtech/TEMPAGER3E.MIB new file mode 100644 index 0000000000..453d475564 --- /dev/null +++ b/mibs/avtech/TEMPAGER3E.MIB @@ -0,0 +1,127 @@ +-- +--defines TEMPAGER3E-MIB for SNMP agent extension of TemPageR 3E +--Contents: Global Registration Module +-- TemPageR OIDs - Enterprise 20916 +--Version: 1.0.0 +--Date: 30 July 2008 +--Developed By: AVTECH Software, Inc. + +-- +--Copyright (c) 2008 AVTECH Software, Inc. +-- + +TEMPAGER3E-MIB DEFINITIONS ::= BEGIN + + +IMPORTS + enterprises, IpAddress, Counter, TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212 + TRAP-TYPE + FROM RFC-1215; + + +avtech OBJECT IDENTIFIER ::= { enterprises 20916 } +products OBJECT IDENTIFIER ::= { avtech 1 } +TEMPAGER3E OBJECT IDENTIFIER ::= { products 7 } +sensors OBJECT IDENTIFIER ::= { TEMPAGER3E 1 } +traps OBJECT IDENTIFIER ::= { TEMPAGER3E 2 } +internal OBJECT IDENTIFIER ::= { sensors 1 } +temperature OBJECT IDENTIFIER ::= { internal 1 } +digital OBJECT IDENTIFIER ::= { sensors 2 } +digital-sen1 OBJECT IDENTIFIER ::= { digital 1 } +digital-sen2 OBJECT IDENTIFIER ::= { digital 2 } + + +-- TemPageR 3E MIB +-- Parameters + +internal-tempc OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Celsius. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 1 } + +internal-tempf OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "The internal temperature reading in Fahrenheit. Because the SNMP Protocol does not support floating point numbers, values are scaled by 100 and should be divided by 100 to get the actual value." +::= { temperature 2 } + +digital-sen1-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen1 1 } + +digital-sen1-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen1 2 } + +digital-sen1-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen1 3 } + +digital-sen1-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen1 4 } + +digital-sen2-1 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Celsius. If this sensor is a Digital Power Sensor, this value represents the Current reading in Amperage." +::= { digital-sen2 1 } + +digital-sen2-2 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current temperature in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Power reading in Watts." +::= { digital-sen2 2 } + +digital-sen2-3 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temp/Humidity sensor, this value represents the current relative humidity in % Relative Humidity. If this sensor is a Digital Power Sensor, this value represents the Voltage reading in Volts." +::= { digital-sen2 3 } + +digital-sen2-4 OBJECT-TYPE + SYNTAX INTEGER(0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION "If this sensor is a Temperature or Temp/Humidity sensor, this value represents the current heat index in Fahrenheit. If this sensor is a Digital Power Sensor, this value represents the Reference reading in Volts." +::= { digital-sen2 4 } + +alarmmessage OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + DESCRIPTION "Last Alarm Message" +::= { traps 1 } + + +-- TEMPAGER3E TRAPS + +tempager3e-snmp-trap TRAP-TYPE + ENTERPRISE TEMPAGER3E + VARIABLES { alarmmessage } + DESCRIPTION "A tempager3e-snmp-trap indicates that an alarm + condition has occurred on the sensor indicated + by the alarmmessage variable." +::= 2 +END diff --git a/misc/notifications.rss b/misc/notifications.rss index f4baa96ca4..5d094945b1 100644 --- a/misc/notifications.rss +++ b/misc/notifications.rss @@ -12,5 +12,10 @@ This is the first notification. We will post these whenever an upcoming major change is about to happen. Tue, 19 Jan 2016 12:00:00 +0000 + + Cisco syslog parsing changes + We have overhauled the Cisco syslog parsing. Please monitor your syslog entries from Cisco IOS, IOSXR, and CatOS devices. If you notice any issues please open a new issue on GitHub and include the original syslog message. + Thu, 03 Mar 2016 12:00:00 +0000 + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000000..26bc37ed3b --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,8 @@ + + + + + ./tests/ + + + diff --git a/poll-billing.php b/poll-billing.php index 26def37c5c..5f2c278ab1 100755 --- a/poll-billing.php +++ b/poll-billing.php @@ -59,7 +59,7 @@ function CollectData($bill_id) { if ($last_data['state'] == 'ok') { $port_data['last_in_measurement'] = $last_data[counter]; $port_data['last_in_delta'] = $last_data[delta]; - if ($port_data['in_measurement'] > $port_data['last_in_measurement']) { + if ($port_data['in_measurement'] >= $port_data['last_in_measurement']) { $port_data['in_delta'] = ($port_data['in_measurement'] - $port_data['last_in_measurement']); } else { @@ -76,7 +76,7 @@ function CollectData($bill_id) { if ($last_data[state] == 'ok') { $port_data['last_out_measurement'] = $last_data[counter]; $port_data['last_out_delta'] = $last_data[delta]; - if ($port_data['out_measurement'] > $port_data['last_out_measurement']) { + if ($port_data['out_measurement'] >= $port_data['last_out_measurement']) { $port_data['out_delta'] = ($port_data['out_measurement'] - $port_data['last_out_measurement']); } else { diff --git a/poller-wrapper.py b/poller-wrapper.py index d7b8f8bebe..43e91f76ab 100755 --- a/poller-wrapper.py +++ b/poller-wrapper.py @@ -163,6 +163,8 @@ if ('distributed_poller' in config and print "Could not connect to memcached, disabling distributed poller." distpoll = False IsNode = False + except SystemExit: + raise except ImportError: print "ERROR: missing memcache python module:" print "On deb systems: apt-get install python-memcache" diff --git a/scripts/shoutcast.php b/scripts/shoutcast.php index c123312dfe..4c588355a2 100644 --- a/scripts/shoutcast.php +++ b/scripts/shoutcast.php @@ -63,7 +63,7 @@ $servers = file($config); $data = array(); foreach ($servers as $item=>$server) { - list($host, $port) = split(":", $server, 2); + list($host, $port) = explode(":", $server, 2); array_push($data, get_data(trim($host), trim($port))); } return $data; diff --git a/sql-schema/102.sql b/sql-schema/102.sql new file mode 100644 index 0000000000..c1f56b5f65 --- /dev/null +++ b/sql-schema/102.sql @@ -0,0 +1 @@ +INSERT INTO `config` (`config_name`,`config_value`,`config_default`,`config_descr`,`config_group`,`config_group_order`,`config_sub_group`,`config_sub_group_order`,`config_hidden`,`config_disabled`) VALUES ('webui.global_search_result_limit','8','8','Global search results limit','webui',0,'search',0,'1','0'); diff --git a/sql-schema/103.sql b/sql-schema/103.sql new file mode 100644 index 0000000000..fb73b7e681 --- /dev/null +++ b/sql-schema/103.sql @@ -0,0 +1 @@ +ALTER TABLE `stp` CHANGE `timeSinceTopologyChange` `timeSinceTopologyChange` INT UNSIGNED NOT NULL diff --git a/sql-schema/104.sql b/sql-schema/104.sql new file mode 100644 index 0000000000..6a433b371d --- /dev/null +++ b/sql-schema/104.sql @@ -0,0 +1 @@ +ALTER TABLE `devices` CHANGE `purpose` `purpose` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL; diff --git a/sql-schema/105.sql b/sql-schema/105.sql new file mode 100644 index 0000000000..70f4286357 --- /dev/null +++ b/sql-schema/105.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS `state_indexes` ( `state_index_id` int(11) NOT NULL AUTO_INCREMENT, `state_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`state_index_id`), UNIQUE KEY `state_name` (`state_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1; +CREATE TABLE IF NOT EXISTS `state_translations` ( `state_translation_id` int(11) NOT NULL AUTO_INCREMENT, `state_index_id` int(11) NOT NULL, `state_descr` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `state_draw_graph` tinyint(1) NOT NULL, `state_value` tinyint(1) NOT NULL, `state_generic_value` tinyint(1) NOT NULL, `state_lastupdated` timestamp NOT NULL, PRIMARY KEY (`state_translation_id`), UNIQUE KEY `state_index_id_value` (`state_index_id`,`state_value`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1; +CREATE TABLE IF NOT EXISTS `sensors_to_state_indexes` ( `sensors_to_state_translations_id` int(11) NOT NULL AUTO_INCREMENT, `sensor_id` int(11) NOT NULL, `state_index_id` int(11) NOT NULL, PRIMARY KEY (`sensors_to_state_translations_id`), UNIQUE KEY `sensor_id_state_index_id` (`sensor_id`,`state_index_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1; +ALTER TABLE `sensors_to_state_indexes` ADD FOREIGN KEY (`sensor_id`) REFERENCES `sensors`(`sensor_id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `sensors_to_state_indexes` ADD FOREIGN KEY (`state_index_id`) REFERENCES `state_indexes`(`state_index_id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `sensors` ADD `sensor_prev` float default NULL; diff --git a/sql-schema/106.sql b/sql-schema/106.sql new file mode 100644 index 0000000000..58ab4eb1d0 --- /dev/null +++ b/sql-schema/106.sql @@ -0,0 +1,3 @@ +ALTER TABLE port_in_measurements ADD INDEX (`port_id`, `timestamp`); +ALTER TABLE port_out_measurements ADD INDEX (`port_id`, `timestamp`); +ALTER TABLE bill_data ADD INDEX (`bill_id`, `timestamp`); \ No newline at end of file diff --git a/tests/SyslogTest.php b/tests/SyslogTest.php new file mode 100644 index 0000000000..7023f06d0a --- /dev/null +++ b/tests/SyslogTest.php @@ -0,0 +1,89 @@ +fillLine($line); + $data = array(); + $data['input'] = $entry; + unset($entry['msg']); // empty msg + $data['result'] = array_merge($entry, $resultDelta); + return $data; + } + + public function testCiscoSyslog() + { + // populate fake $dev_cache and $config + global $config, $dev_cache; + $dev_cache['1.1.1.1'] = array('device_id' => 1, 'os' => 'ios', 'version' => 1); + $config = array(); + $config['syslog_filter'] = array(); + + // populate test data + $testdata = array(); + + // ---- IOS ---- + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%CARD-SEVERITY-MSG:SLOT %FACILITY-SEVERITY-MNEMONIC: Message-text||", + array('device_id'=>1, 'program'=>'%CARD-SEVERITY-MSG:SLOT %FACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC: Message-text||", + array('device_id'=>1, 'program'=>'%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text') + ); + + // ---- CatOS ---- + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%IP-3-UDP_SOCKOVFL:UDP socket overflow||", + array('device_id'=>1, 'program'=>'%IP-3-UDP_SOCKOVFL', 'msg'=>'UDP socket overflow') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||DTP-1-ILGLCFG: Illegal config (on, isl--on,dot1q) on Port [mod/port]||", + array('device_id'=>1, 'program'=>'DTP-1-ILGLCFG', 'msg'=>'Illegal config (on, isl--on,dot1q) on Port [mod/port]') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||Cannot enable text mode config if ACL config is cleared from nvram||", + array('device_id'=>1, 'program'=>'', 'msg'=>'Cannot enable text mode config if ACL config is cleared from nvram') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%PAGP-5-PORTFROMSTP / %PAGP-5-PORTTOSTP||", + array('device_id'=>1, 'program'=>'%PAGP-5-PORTFROMSTP / %PAGP-5-PORTTOSTP') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%SYS-3-EOBC_CHANNELREINIT||", + array('device_id'=>1, 'program'=>'%SYS-3-EOBC_CHANNELREINIT') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%SYS-4-MODHPRESET:||", + array('device_id'=>1, 'program'=>'%SYS-4-MODHPRESET', 'msg'=>'') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||InbandPingProcessFailure:Module x not responding over inband||", + array('device_id'=>1, 'program'=>'INBANDPINGPROCESSFAILURE', 'msg'=>'Module x not responding over inband') + ); + $testdata[] = $this->createData( + "1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||RxSBIF_SEQ_NUM_ERROR:slot=x||", + array('device_id'=>1, 'program'=>'RXSBIF_SEQ_NUM_ERROR', 'msg'=>'slot=x') + ); + + + // run tests + foreach($testdata as $data) { + $res = process_syslog($data['input'], 0); + $this->assertEquals($data['result'], $res); + } + } +} +