You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mt...@apache.org on 2023/04/24 17:16:32 UTC

[nifi] branch main updated: [NIFI-11433] update angular, d3, moment, slickgrid, and jquery depen… (#7174)

This is an automated email from the ASF dual-hosted git repository.

mtien pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 38ece1d7d9 [NIFI-11433] update angular, d3, moment, slickgrid, and jquery depen… (#7174)
38ece1d7d9 is described below

commit 38ece1d7d9949d72e94757ea8e2ed948fe5c5973
Author: Scott Aslan <sc...@users.noreply.github.com>
AuthorDate: Mon Apr 24 13:16:20 2023 -0400

    [NIFI-11433] update angular, d3, moment, slickgrid, and jquery depen… (#7174)
    
    * [NIFIF-11433] update angular, d3, moment, slickgrid, and jquery dependencies
    
    * address review feedback
    
    * .entries instead of .keys
    
    Merged #7174 into main.
---
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml    |    3 +-
 .../src/main/frontend/package-lock.json            | 1599 +++++++++++---------
 .../nifi-web-ui/src/main/frontend/package.json     |   27 +-
 .../src/main/resources/filters/canvas.properties   |    1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp       |    3 +-
 .../src/main/webapp/WEB-INF/pages/provenance.jsp   |    3 +-
 .../src/main/webapp/WEB-INF/pages/summary.jsp      |    2 +-
 .../src/main/webapp/WEB-INF/pages/users.jsp        |    2 +-
 .../jquery/propertytable/jquery.propertytable.js   |    2 +-
 .../header/components/nf-ng-processor-component.js |    8 +-
 .../main/webapp/js/nf/canvas/helpers/d3-helpers.js |   43 +
 .../src/main/webapp/js/nf/canvas/nf-actions.js     |    8 +-
 .../src/main/webapp/js/nf/canvas/nf-birdseye.js    |   33 +-
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js    |   40 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js      |  186 ++-
 .../webapp/js/nf/canvas/nf-component-version.js    |    6 +-
 .../src/main/webapp/js/nf/canvas/nf-connectable.js |   82 +-
 .../src/main/webapp/js/nf/canvas/nf-connection.js  |  545 ++++---
 .../main/webapp/js/nf/canvas/nf-context-menu.js    |   12 +-
 .../webapp/js/nf/canvas/nf-controller-service.js   |    6 +-
 .../webapp/js/nf/canvas/nf-controller-services.js  |    8 +-
 .../src/main/webapp/js/nf/canvas/nf-draggable.js   |   26 +-
 .../src/main/webapp/js/nf/canvas/nf-funnel.js      |   64 +-
 .../src/main/webapp/js/nf/canvas/nf-label.js       |   95 +-
 .../src/main/webapp/js/nf/canvas/nf-port.js        |  151 +-
 .../main/webapp/js/nf/canvas/nf-process-group.js   |  545 ++++---
 .../src/main/webapp/js/nf/canvas/nf-processor.js   |  280 ++--
 .../main/webapp/js/nf/canvas/nf-quick-select.js    |   10 +-
 .../webapp/js/nf/canvas/nf-remote-process-group.js |  214 +--
 .../src/main/webapp/js/nf/canvas/nf-selectable.js  |   12 +-
 .../src/main/webapp/js/nf/canvas/nf-settings.js    |   16 +-
 .../src/main/webapp/js/nf/nf-status-history.js     |    4 +-
 .../js/nf/provenance/nf-provenance-lineage.js      |  237 ++-
 33 files changed, 2370 insertions(+), 1903 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 37a524220f..73a4c67fb6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -148,7 +148,7 @@
                                     <filtering>false</filtering>
                                     <includes>
                                         <!-- D3 -->
-                                        <include>d3/build/d3.min.js*</include>
+                                        <include>d3/dist/d3.min.js*</include>
                                         <include>d3/LICENSE</include>
                                         <!-- D3 selection multi -->
                                         <include>d3-selection-multi/build/d3-selection-multi.min.js*</include>
@@ -496,6 +496,7 @@
                                                 <include>${staging.dir}/js/nf/canvas/nf-component-state.js</include>
                                                 <include>${staging.dir}/js/nf/nf-shell.js</include>
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
+                                                <include>${staging.dir}/js/nf/canvas/helpers/d3-helpers.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-clipboard.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-canvas-utils.js</include>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
index da06a46175..fff952e8de 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
@@ -7,108 +7,270 @@
       "name": "apache-nifi",
       "hasInstallScript": true,
       "dependencies": {
-        "angular": "1.8.2",
-        "angular-animate": "1.8.2",
-        "angular-aria": "1.8.2",
         "angular-material": "1.1.26",
-        "angular-messages": "1.8.2",
-        "angular-mocks": "1.8.2",
-        "angular-resource": "1.8.2",
-        "angular-route": "1.8.2",
-        "angular-sanitize": "1.8.2",
-        "d3": "4.13.0",
-        "d3-selection-multi": "1.0.1",
+        "lodash": "4.17.21",
+        "url-search-params": "0.6.1",
+        "qtip2": "3.0.3",
+        "angular-route": "1.8.3",
+        "d3": "7.6.1",
+        "JSON2": "0.1.0",
+        "angular-messages": "1.8.3",
+        "moment": "2.29.4",
+        "angular-mocks": "1.8.3",
+        "angular-sanitize": "1.8.3",
+        "angular-resource": "1.8.3",
+        "jsonlint": "1.6.3",
         "font-awesome": "4.7.0",
-        "jquery": "3.6.0",
-        "jquery-form": "3.50.0",
         "jquery-minicolors": "2.1.10",
         "jquery-ui-dist": "1.13.2",
-        "JSON2": "0.1.0",
-        "jsonlint": "1.6.3",
-        "lodash": "4.17.21",
-        "moment": "2.29.2",
-        "qtip2": "3.0.3",
         "reset.css": "2.0.2",
-        "slickgrid": "2.4.38",
-        "url-search-params": "0.6.1"
+        "angular": "1.8.3",
+        "angular-aria": "1.8.3",
+        "jquery": "3.6.4",
+        "slickgrid": "2.4.45",
+        "jquery-form": "3.50.0",
+        "angular-animate": "1.8.3"
       },
       "devDependencies": {
-        "npm-force-resolutions": "0.0.10"
+        "npm-force-resolutions": "^0.0.10"
       },
       "engines": {
         "node": ">=16.13.2",
         "npm": ">=8.1.2"
       }
     },
-    "node_modules/angular": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
-      "integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
+    "node_modules/d3-scale": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+      "dependencies": {
+        "d3-array": "2.10.0 - 3",
+        "d3-format": "1 - 3",
+        "d3-interpolate": "1.2.0 - 3",
+        "d3-time": "2.1.1 - 3",
+        "d3-time-format": "2 - 4"
+      },
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/angular-animate": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
-      "integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
+    "node_modules/d3-time": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+      "dependencies": {
+        "d3-array": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/angular-aria": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.8.2.tgz",
-      "integrity": "sha512-xWT1Lm+Xug2GM6nGNMioBP4hXt3sj2eGaExa4cVCZUYSVWezgyuD1RiLgzJzwTkBGYplHQUzQaz3yDY8jVI6yQ=="
+    "node_modules/d3-dsv": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+      "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+      "dependencies": {
+        "commander": "7",
+        "iconv-lite": "0.6",
+        "rw": "1"
+      },
+      "bin": {
+        "csv2json": "bin/dsv2json.js",
+        "csv2tsv": "bin/dsv2dsv.js",
+        "dsv2dsv": "bin/dsv2dsv.js",
+        "dsv2json": "bin/dsv2json.js",
+        "json2csv": "bin/json2dsv.js",
+        "json2dsv": "bin/json2dsv.js",
+        "json2tsv": "bin/json2dsv.js",
+        "tsv2csv": "bin/dsv2dsv.js",
+        "tsv2json": "bin/dsv2json.js"
+      },
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/angular-material": {
-      "version": "1.1.26",
-      "resolved": "https://registry.npmjs.org/angular-material/-/angular-material-1.1.26.tgz",
-      "integrity": "sha512-DBLsoOP1D1E14EQsECZYabt3Jh1PpvsG8k1aZgaP/Ml57n4stpClzLhCsuTNbtB/pqq9CL8XtpCfB6fhVRWqIQ==",
-      "peerDependencies": {
-        "angular": "^1.7.2",
-        "angular-animate": "^1.7.2",
-        "angular-aria": "^1.7.2",
-        "angular-messages": "^1.7.2"
+    "node_modules/JSV": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+      "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==",
+      "engines": {
+        "node": "*"
       }
     },
-    "node_modules/angular-messages": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.8.2.tgz",
-      "integrity": "sha512-M1qNh/30cLJi4yJJ+3YB8saPonRcavz5Dquqz0T/aUySKJhIkUoeCkmF+BcLH4SJ5PBp04yy4CZUUeNRVi7jZA=="
+    "node_modules/jquery-minicolors": {
+      "version": "2.1.10",
+      "resolved": "https://registry.npmjs.org/jquery-minicolors/-/jquery-minicolors-2.1.10.tgz",
+      "integrity": "sha512-vXG4hz0h97VtmXXXrG6ceM0kjANjMziWfT1L0TZ1PaTEN2AYdJZWCndrGX/ep4AWMGgXsnEfGl/h95Gjm/4eKw==",
+      "dependencies": {
+        "jquery": ">= 1.7.x"
+      }
     },
-    "node_modules/angular-mocks": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.8.2.tgz",
-      "integrity": "sha512-I5L3P0l21HPdVsP4A4qWmENt4ePjjbkDFdAzOaM7QiibFySbt14DptPbt2IjeG4vFBr4vSLbhIz8Fk03DISl8Q=="
+    "node_modules/jsonlint": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
+      "integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==",
+      "dependencies": {
+        "JSV": "^4.0.x",
+        "nomnom": "^1.5.x"
+      },
+      "bin": {
+        "jsonlint": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
     },
-    "node_modules/angular-resource": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-resource/-/angular-resource-1.8.2.tgz",
-      "integrity": "sha512-CbTu8ypkVZMBLm+Wd+jI9xR7QEVCeUgN8WXsr9Yi9l0s/8ZQa5kEIuYX3F/75Ej8Xs1Jp9DoKjAyvACI6Q8IJA=="
+    "node_modules/d3-chord": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+      "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+      "dependencies": {
+        "d3-path": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/angular-route": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.8.2.tgz",
-      "integrity": "sha512-49LJYxuaQ/ZDiu9dD2xo1LkazqObkGjw0a7dUF7UKCT8EELgBsMd6QrI6aEVGtI62ppkiFkxqmiV8fcwYdXpug=="
+    "node_modules/font-awesome": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
+      "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
+      "engines": {
+        "node": ">=0.10.3"
+      }
     },
-    "node_modules/angular-sanitize": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.2.tgz",
-      "integrity": "sha512-OB6Goa+QN3byf5asQ7XRl7DKZejm/F/ZOqa9z1skqYVOWA2hoBxoCmt9E7+i7T/TbxZP5zYzKxNZVVJNu860Hg=="
+    "node_modules/robust-predicates": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+      "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
     },
-    "node_modules/ansi-styles": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
-      "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=",
+    "node_modules/ev-emitter": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz",
+      "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q=="
+    },
+    "node_modules/jquery": {
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
+      "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
+    },
+    "node_modules/d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      },
       "engines": {
-        "node": ">=0.8.0"
+        "node": ">=12"
       }
     },
-    "node_modules/buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "dev": true
+    "node_modules/delaunator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+      "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+      "dependencies": {
+        "robust-predicates": "^3.0.0"
+      }
+    },
+    "node_modules/d3": {
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
+      "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==",
+      "dependencies": {
+        "d3-axis": "3",
+        "d3-zoom": "3",
+        "d3-fetch": "3",
+        "d3-random": "3",
+        "d3-path": "3",
+        "d3-time": "3",
+        "d3-force": "3",
+        "d3-delaunay": "6",
+        "d3-chord": "3",
+        "d3-brush": "3",
+        "d3-hierarchy": "3",
+        "d3-dsv": "3",
+        "d3-selection": "3",
+        "d3-drag": "3",
+        "d3-transition": "3",
+        "d3-time-format": "4",
+        "d3-dispatch": "3",
+        "d3-scale-chromatic": "3",
+        "d3-ease": "3",
+        "d3-format": "3",
+        "d3-contour": "4",
+        "d3-quadtree": "3",
+        "d3-geo": "3",
+        "d3-color": "3",
+        "d3-timer": "3",
+        "d3-array": "3",
+        "d3-shape": "3",
+        "d3-polygon": "3",
+        "d3-scale": "4",
+        "d3-interpolate": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/slickgrid": {
+      "version": "2.4.45",
+      "resolved": "https://registry.npmjs.org/slickgrid/-/slickgrid-2.4.45.tgz",
+      "integrity": "sha512-WvygGTaLU9LnMWZSxqW1agwq8IcDGeCZ289vP1E07eh3ffyLm5TGQsYHXvTwWCHtriZBw+L9RFK/h7TsIMcoLQ==",
+      "dependencies": {
+        "jquery": ">=1.8.0",
+        "jquery-ui": ">=1.8.0"
+      }
+    },
+    "node_modules/d3-geo": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
+      "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
+      "dependencies": {
+        "d3-array": "2.5.0 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-time-format": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+      "dependencies": {
+        "d3-time": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+      "integrity": "sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg==",
+      "bin": {
+        "strip-ansi": "cli.js"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
     },
     "node_modules/chalk": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
-      "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+      "integrity": "sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==",
       "dependencies": {
         "ansi-styles": "~1.0.0",
         "has-color": "~0.1.0",
@@ -118,421 +280,360 @@
         "node": ">=0.8.0"
       }
     },
-    "node_modules/commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-    },
-    "node_modules/d3": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz",
-      "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==",
+    "node_modules/d3-force": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+      "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
       "dependencies": {
-        "d3-array": "1.2.1",
-        "d3-axis": "1.0.8",
-        "d3-brush": "1.0.4",
-        "d3-chord": "1.0.4",
-        "d3-collection": "1.0.4",
-        "d3-color": "1.0.3",
-        "d3-dispatch": "1.0.3",
-        "d3-drag": "1.2.1",
-        "d3-dsv": "1.0.8",
-        "d3-ease": "1.0.3",
-        "d3-force": "1.1.0",
-        "d3-format": "1.2.2",
-        "d3-geo": "1.9.1",
-        "d3-hierarchy": "1.1.5",
-        "d3-interpolate": "1.1.6",
-        "d3-path": "1.0.5",
-        "d3-polygon": "1.0.3",
-        "d3-quadtree": "1.0.3",
-        "d3-queue": "3.0.7",
-        "d3-random": "1.1.0",
-        "d3-request": "1.0.6",
-        "d3-scale": "1.0.7",
-        "d3-selection": "1.3.0",
-        "d3-shape": "1.2.0",
-        "d3-time": "1.0.8",
-        "d3-time-format": "2.1.1",
-        "d3-timer": "1.0.7",
-        "d3-transition": "1.1.1",
-        "d3-voronoi": "1.1.2",
-        "d3-zoom": "1.7.1"
+        "d3-dispatch": "1 - 3",
+        "d3-quadtree": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
-      "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
-    },
-    "node_modules/d3-axis": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz",
-      "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo="
-    },
-    "node_modules/d3-brush": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
-      "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=",
+    "node_modules/jquery-ui": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.2.tgz",
+      "integrity": "sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==",
       "dependencies": {
-        "d3-dispatch": "1",
-        "d3-drag": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "1",
-        "d3-transition": "1"
+        "jquery": ">=1.8.0 <4.0.0"
       }
     },
-    "node_modules/d3-chord": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
-      "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=",
-      "dependencies": {
-        "d3-array": "1",
-        "d3-path": "1"
+    "node_modules/JSON2": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/JSON2/-/JSON2-0.1.0.tgz",
+      "integrity": "sha512-MRguCg79vy8Kx15/CXzoO5pEPi0tQq7T70mL/t1Hv3G+hVfJVO2BZqX3sl2kbWW08GAmzQYSRfWRtQhmK/eaYA==",
+      "engines": {
+        "node": "*"
       }
     },
-    "node_modules/d3-collection": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz",
-      "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI="
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "node_modules/d3-color": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
-      "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs="
+    "node_modules/angular": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
+      "integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
     },
-    "node_modules/d3-dispatch": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
-      "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg="
+    "node_modules/reset.css": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/reset.css/-/reset.css-2.0.2.tgz",
+      "integrity": "sha512-HG5RQLYcAxou3yw+Gnzwan1jJCbOcIBZjwoD/YLWuAd8kkuDI/ZqC0uRhY4ReKOOJaM1Y1kvwebHOwK4P8Sevg=="
     },
-    "node_modules/d3-drag": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz",
-      "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==",
-      "dependencies": {
-        "d3-dispatch": "1",
-        "d3-selection": "1"
+    "node_modules/d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-dsv": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz",
-      "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==",
+    "node_modules/d3-delaunay": {
+      "version": "6.0.4",
+      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+      "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
       "dependencies": {
-        "commander": "2",
-        "iconv-lite": "0.4",
-        "rw": "1"
+        "delaunator": "5"
       },
-      "bin": {
-        "csv2json": "bin/dsv2json",
-        "csv2tsv": "bin/dsv2dsv",
-        "dsv2dsv": "bin/dsv2dsv",
-        "dsv2json": "bin/dsv2json",
-        "json2csv": "bin/json2dsv",
-        "json2dsv": "bin/json2dsv",
-        "json2tsv": "bin/json2dsv",
-        "tsv2csv": "bin/dsv2dsv",
-        "tsv2json": "bin/dsv2json"
-      }
-    },
-    "node_modules/d3-ease": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
-      "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4="
-    },
-    "node_modules/d3-force": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz",
-      "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==",
-      "dependencies": {
-        "d3-collection": "1",
-        "d3-dispatch": "1",
-        "d3-quadtree": "1",
-        "d3-timer": "1"
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-format": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz",
-      "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw=="
+    "node_modules/json-format": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-format/-/json-format-1.0.1.tgz",
+      "integrity": "sha512-MoKIg/lBeQALqjYnqEanikfo3zBKRwclpXJexdF0FUniYAAN2ypEIXBEtpQb+9BkLFtDK1fyTLAsnGlyGfLGxw==",
+      "dev": true
     },
-    "node_modules/d3-geo": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz",
-      "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==",
+    "node_modules/d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
       "dependencies": {
-        "d3-array": "1"
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "d3-selection": "2 - 3"
       }
     },
-    "node_modules/d3-hierarchy": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz",
-      "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY="
-    },
-    "node_modules/d3-interpolate": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz",
-      "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==",
+    "node_modules/d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
       "dependencies": {
-        "d3-color": "1"
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-path": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
-      "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q="
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
-    "node_modules/d3-polygon": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
-      "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI="
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
-    "node_modules/d3-quadtree": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
-      "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg="
+    "node_modules/d3-hierarchy": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+      "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/d3-queue": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz",
-      "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg="
+    "node_modules/angular-route": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.8.3.tgz",
+      "integrity": "sha512-kpIcRmDR2+o1FxDVVYy8Rvfab86/7LDbOgTRb9T+X9ewPQiBRuDEnZtM3oJYBiQLvAXDYTJXHV48n/bGE9Mv2g==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
     },
-    "node_modules/d3-random": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz",
-      "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM="
-    },
-    "node_modules/d3-request": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz",
-      "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==",
+    "node_modules/nomnom": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+      "integrity": "sha512-5s0JxqhDx9/rksG2BTMVN1enjWSvPidpoSgViZU4ZXULyTe+7jxcCRLB6f42Z0l1xYJpleCBtSyY6Lwg3uu5CQ==",
+      "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.",
       "dependencies": {
-        "d3-collection": "1",
-        "d3-dispatch": "1",
-        "d3-dsv": "1",
-        "xmlhttprequest": "1"
+        "chalk": "~0.4.0",
+        "underscore": "~1.6.0"
       }
     },
-    "node_modules/d3-scale": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz",
-      "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==",
-      "dependencies": {
-        "d3-array": "^1.2.0",
-        "d3-collection": "1",
-        "d3-color": "1",
-        "d3-format": "1",
-        "d3-interpolate": "1",
-        "d3-time": "1",
-        "d3-time-format": "2"
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
       }
     },
-    "node_modules/d3-selection": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz",
-      "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA=="
+    "node_modules/internmap": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/d3-selection-multi": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/d3-selection-multi/-/d3-selection-multi-1.0.1.tgz",
-      "integrity": "sha1-zWwlQT0EosuXRw54byzYd/PjT1g=",
-      "dependencies": {
-        "d3-selection": "1",
-        "d3-transition": "1"
+    "node_modules/angular-material": {
+      "version": "1.1.26",
+      "resolved": "https://registry.npmjs.org/angular-material/-/angular-material-1.1.26.tgz",
+      "integrity": "sha512-DBLsoOP1D1E14EQsECZYabt3Jh1PpvsG8k1aZgaP/Ml57n4stpClzLhCsuTNbtB/pqq9CL8XtpCfB6fhVRWqIQ==",
+      "deprecated": "For the actively supported Angular Material, see https://www.npmjs.com/package/@angular/material. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward.",
+      "peerDependencies": {
+        "angular": "^1.7.2",
+        "angular-animate": "^1.7.2",
+        "angular-aria": "^1.7.2",
+        "angular-messages": "^1.7.2"
       }
     },
-    "node_modules/d3-shape": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz",
-      "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=",
+    "node_modules/d3-contour": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+      "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
       "dependencies": {
-        "d3-path": "1"
+        "d3-array": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-time": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz",
-      "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ=="
-    },
-    "node_modules/d3-time-format": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz",
-      "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==",
+    "node_modules/d3-array": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz",
+      "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==",
       "dependencies": {
-        "d3-time": "1"
+        "internmap": "1 - 2"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/d3-timer": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz",
-      "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA=="
-    },
-    "node_modules/d3-transition": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz",
-      "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==",
+    "node_modules/jquery-ui-dist": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
+      "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
       "dependencies": {
-        "d3-color": "1",
-        "d3-dispatch": "1",
-        "d3-ease": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "^1.1.0",
-        "d3-timer": "1"
+        "jquery": ">=1.8.0 <4.0.0"
       }
     },
-    "node_modules/d3-voronoi": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
-      "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw="
-    },
-    "node_modules/d3-zoom": {
-      "version": "1.7.1",
-      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz",
-      "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==",
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
       "dependencies": {
-        "d3-dispatch": "1",
-        "d3-drag": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "1",
-        "d3-transition": "1"
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
       }
     },
-    "node_modules/ev-emitter": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz",
-      "integrity": "sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q=="
+    "node_modules/rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
     },
-    "node_modules/font-awesome": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
-      "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=",
+    "node_modules/url-search-params": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/url-search-params/-/url-search-params-0.6.1.tgz",
+      "integrity": "sha512-haKLRCc3JyU8uVBKNRgWJUu+iTgvfaiDZ3RT8OO3Dx0Ypq1G6cY1HhhcAOUL1y1+rfYR+5UcOyxgNcSa7g+Xbg==",
+      "deprecated": "now available as @ungap/url-search-params"
+    },
+    "node_modules/angular-resource": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-resource/-/angular-resource-1.8.3.tgz",
+      "integrity": "sha512-x3+4w2SyIDEIe6PUBTzcjDFI5MhZNWAPELUxkYK0QMOkOo4w3BN/qhpd+fO1deFiDS23mZKqT8kfctO1IDkCDA==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
+    },
+    "node_modules/moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
       "engines": {
-        "node": ">=0.10.3"
+        "node": "*"
       }
     },
     "node_modules/has-color": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
-      "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=",
+      "integrity": "sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw==",
       "engines": {
         "node": ">=0.10.0"
       }
     },
-    "node_modules/iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=12"
       }
     },
-    "node_modules/imagesloaded": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz",
-      "integrity": "sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==",
-      "dependencies": {
-        "ev-emitter": "^1.0.0"
+    "node_modules/ansi-styles": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+      "integrity": "sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA==",
+      "engines": {
+        "node": ">=0.8.0"
       }
     },
-    "node_modules/jquery": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
-      "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
-    },
-    "node_modules/jquery-form": {
-      "version": "3.50.0",
-      "resolved": "https://registry.npmjs.org/jquery-form/-/jquery-form-3.50.0.tgz",
-      "integrity": "sha1-18lptR4JThLaaZHrYf9a8l9ZeY8=",
-      "dependencies": {
-        "jquery": ">=1.5"
+    "node_modules/d3-random": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+      "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/jquery-minicolors": {
-      "version": "2.1.10",
-      "resolved": "https://registry.npmjs.org/jquery-minicolors/-/jquery-minicolors-2.1.10.tgz",
-      "integrity": "sha1-sp7qVB2aMrTiaSMWj7LBYnGGc3k=",
-      "dependencies": {
-        "jquery": ">= 1.7.x"
+    "node_modules/angular-mocks": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.8.3.tgz",
+      "integrity": "sha512-vqsT6zwu80cZ8RY7qRQBZuy6Fq5X7/N5hkV9LzNT0c8b546rw4ErGK6muW1u2JnDKYa7+jJuaGM702bWir4HGw=="
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "node_modules/d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/jquery-ui": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.0.tgz",
-      "integrity": "sha512-Osf7ECXNTYHtKBkn9xzbIf9kifNrBhfywFEKxOeB/OVctVmLlouV9mfc2qXCp6uyO4Pn72PXKOnj09qXetopCw==",
-      "dependencies": {
-        "jquery": ">=1.8.0 <4.0.0"
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/jquery-ui-dist": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz",
-      "integrity": "sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA==",
+    "node_modules/qtip2": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/qtip2/-/qtip2-3.0.3.tgz",
+      "integrity": "sha512-vfSEVYg1teA4g3RhT9fnBXzEQrkgyU5CrQE8j6WzZs5LBupG+Nq97mFq4BmDKcA6mGSMFiIqm70+ydvWW1KnGg==",
       "dependencies": {
-        "jquery": ">=1.8.0 <4.0.0"
+        "imagesloaded": ">=3.0.0",
+        "jquery": ">=1.6.0"
       }
     },
-    "node_modules/json-format": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-format/-/json-format-1.0.1.tgz",
-      "integrity": "sha1-FD9n5irxKda//tKIpGJl6iPQ3ww=",
-      "dev": true
-    },
-    "node_modules/JSON2": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/JSON2/-/JSON2-0.1.0.tgz",
-      "integrity": "sha1-jXSTBApj1YNa919H3suDq2yMB5A=",
+    "node_modules/d3-polygon": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+      "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
       "engines": {
-        "node": "*"
+        "node": ">=12"
       }
     },
-    "node_modules/jsonlint": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
-      "integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==",
-      "dependencies": {
-        "JSV": "^4.0.x",
-        "nomnom": "^1.5.x"
-      },
-      "bin": {
-        "jsonlint": "lib/cli.js"
-      },
+    "node_modules/angular-animate": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.3.tgz",
+      "integrity": "sha512-/LtTKvy5sD6MZbV0v+nHgOIpnFF0mrUp+j5WIxVprVhcrJriYpuCZf4S7Owj1o76De/J0eRzANUozNJ6hVepnQ==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
+    },
+    "node_modules/d3-quadtree": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+      "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
       "engines": {
-        "node": ">= 0.6"
+        "node": ">=12"
       }
     },
-    "node_modules/JSV": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
-      "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=",
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
       "engines": {
-        "node": "*"
+        "node": ">=12"
       }
     },
-    "node_modules/lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
-    },
-    "node_modules/moment": {
-      "version": "2.29.2",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
-      "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==",
+    "node_modules/d3-shape": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+      "dependencies": {
+        "d3-path": "^3.1.0"
+      },
       "engines": {
-        "node": "*"
+        "node": ">=12"
       }
     },
-    "node_modules/nomnom": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.0.tgz",
-      "integrity": "sha1-8XRItQxW8o0ZIdDoiZKgZrTxUVs=",
-      "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.",
+    "node_modules/d3-brush": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+      "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
       "dependencies": {
-        "chalk": "~0.4.0",
-        "underscore": "~1.6.0"
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "3",
+        "d3-transition": "3"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
     "node_modules/npm-force-resolutions": {
@@ -549,140 +650,174 @@
         "npm-force-resolutions": "index.js"
       }
     },
-    "node_modules/qtip2": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/qtip2/-/qtip2-3.0.3.tgz",
-      "integrity": "sha1-ffCIrkQSwkpAZN5p6CTLPPdiENw=",
-      "dependencies": {
-        "imagesloaded": ">=3.0.0",
-        "jquery": ">=1.6.0"
+    "node_modules/d3-format": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/reset.css": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/reset.css/-/reset.css-2.0.2.tgz",
-      "integrity": "sha1-nAEwnIILcujyGevMjhQRlJYE99c="
-    },
-    "node_modules/rw": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
-      "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
-    },
-    "node_modules/safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
-    },
-    "node_modules/slickgrid": {
-      "version": "2.4.38",
-      "resolved": "https://registry.npmjs.org/slickgrid/-/slickgrid-2.4.38.tgz",
-      "integrity": "sha512-BFbRXOkpbF44vCr6MxEpO/A7ruiPSINN7WoKaDkLza62CX14kq7Me6v77Q/bDsZsOeRsHudpRZufmt1GQixq5g==",
+    "node_modules/jquery-form": {
+      "version": "3.50.0",
+      "resolved": "https://registry.npmjs.org/jquery-form/-/jquery-form-3.50.0.tgz",
+      "integrity": "sha512-WcBPCGF5U/ECu5p/2IkezCBcTBqGPwlbCIKyueWAgXxReNhpaOpSpG/dMr9yra42+ylFELzsuoDWuMCuc3lftQ==",
       "dependencies": {
-        "jquery": ">=1.8.0",
-        "jquery-ui": ">=1.8.0"
+        "jquery": ">=1.5"
       }
     },
-    "node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
+    "node_modules/d3-axis": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+      "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=12"
       }
     },
-    "node_modules/source-map-support": {
-      "version": "0.5.21",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-      "dev": true,
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
       "dependencies": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
-      "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=",
-      "bin": {
-        "strip-ansi": "cli.js"
+        "d3-color": "1 - 3"
       },
       "engines": {
-        "node": ">=0.8.0"
+        "node": ">=12"
+      }
+    },
+    "node_modules/imagesloaded": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-5.0.0.tgz",
+      "integrity": "sha512-/0JGSubc1MTCoDKVmonLHgbifBWHdyLkun+R/151E1c5n79hiSxcd7cB7mPXFgojYu8xnRZv7GYxzKoxW8BetQ==",
+      "dependencies": {
+        "ev-emitter": "^2.1.2"
       }
     },
+    "node_modules/angular-messages": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.8.3.tgz",
+      "integrity": "sha512-f/ywtg32lqzX8FnXkBJOyn13lbCbo333/xy/5TTFcsH/gZdXoiuERj+dLTOs8xHCkOeFQhFx0VD0DgtMgSag7A=="
+    },
     "node_modules/underscore": {
       "version": "1.13.2",
       "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
       "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g=="
     },
-    "node_modules/url-search-params": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/url-search-params/-/url-search-params-0.6.1.tgz",
-      "integrity": "sha1-Ott4WLgH1WuB56vzudrkl4wD+Z0=",
-      "deprecated": "now available as @ungap/url-search-params"
+    "node_modules/d3-path": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "node_modules/xmlhttprequest": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
-      "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=",
+    "node_modules/d3-fetch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+      "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+      "dependencies": {
+        "d3-dsv": "1 - 3"
+      },
       "engines": {
-        "node": ">=0.4.0"
+        "node": ">=12"
+      }
+    },
+    "node_modules/angular-sanitize": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.3.tgz",
+      "integrity": "sha512-2rxdqzlUVafUeWOwvY/FtyWk1pFTyCtzreeiTytG9m4smpuAEKaIJAjYeVwWsoV+nlTOcgpwV4W1OCmR+BQbUg==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
+    },
+    "node_modules/d3-scale-chromatic": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+      "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-interpolate": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
       }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/angular-aria": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.8.3.tgz",
+      "integrity": "sha512-qTXclmTW/KGw5JNKKQPcCKKq6hCBZ39jYINmLgMsjUHBAoxULaMRRTaRj/L2VTOjKvK5f9enkx+EUqRqzXDSFQ==",
+      "deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
     }
   },
   "dependencies": {
+    "JSON2": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/JSON2/-/JSON2-0.1.0.tgz",
+      "integrity": "sha512-MRguCg79vy8Kx15/CXzoO5pEPi0tQq7T70mL/t1Hv3G+hVfJVO2BZqX3sl2kbWW08GAmzQYSRfWRtQhmK/eaYA=="
+    },
+    "JSV": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+      "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw=="
+    },
     "angular": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
-      "integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
+      "integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
     },
     "angular-animate": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
-      "integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.3.tgz",
+      "integrity": "sha512-/LtTKvy5sD6MZbV0v+nHgOIpnFF0mrUp+j5WIxVprVhcrJriYpuCZf4S7Owj1o76De/J0eRzANUozNJ6hVepnQ=="
     },
     "angular-aria": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.8.2.tgz",
-      "integrity": "sha512-xWT1Lm+Xug2GM6nGNMioBP4hXt3sj2eGaExa4cVCZUYSVWezgyuD1RiLgzJzwTkBGYplHQUzQaz3yDY8jVI6yQ=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.8.3.tgz",
+      "integrity": "sha512-qTXclmTW/KGw5JNKKQPcCKKq6hCBZ39jYINmLgMsjUHBAoxULaMRRTaRj/L2VTOjKvK5f9enkx+EUqRqzXDSFQ=="
     },
     "angular-material": {
       "version": "1.1.26",
       "resolved": "https://registry.npmjs.org/angular-material/-/angular-material-1.1.26.tgz",
-      "integrity": "sha512-DBLsoOP1D1E14EQsECZYabt3Jh1PpvsG8k1aZgaP/Ml57n4stpClzLhCsuTNbtB/pqq9CL8XtpCfB6fhVRWqIQ==",
-      "requires": {}
+      "integrity": "sha512-DBLsoOP1D1E14EQsECZYabt3Jh1PpvsG8k1aZgaP/Ml57n4stpClzLhCsuTNbtB/pqq9CL8XtpCfB6fhVRWqIQ=="
     },
     "angular-messages": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.8.2.tgz",
-      "integrity": "sha512-M1qNh/30cLJi4yJJ+3YB8saPonRcavz5Dquqz0T/aUySKJhIkUoeCkmF+BcLH4SJ5PBp04yy4CZUUeNRVi7jZA=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.8.3.tgz",
+      "integrity": "sha512-f/ywtg32lqzX8FnXkBJOyn13lbCbo333/xy/5TTFcsH/gZdXoiuERj+dLTOs8xHCkOeFQhFx0VD0DgtMgSag7A=="
     },
     "angular-mocks": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.8.2.tgz",
-      "integrity": "sha512-I5L3P0l21HPdVsP4A4qWmENt4ePjjbkDFdAzOaM7QiibFySbt14DptPbt2IjeG4vFBr4vSLbhIz8Fk03DISl8Q=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.8.3.tgz",
+      "integrity": "sha512-vqsT6zwu80cZ8RY7qRQBZuy6Fq5X7/N5hkV9LzNT0c8b546rw4ErGK6muW1u2JnDKYa7+jJuaGM702bWir4HGw=="
     },
     "angular-resource": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-resource/-/angular-resource-1.8.2.tgz",
-      "integrity": "sha512-CbTu8ypkVZMBLm+Wd+jI9xR7QEVCeUgN8WXsr9Yi9l0s/8ZQa5kEIuYX3F/75Ej8Xs1Jp9DoKjAyvACI6Q8IJA=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-resource/-/angular-resource-1.8.3.tgz",
+      "integrity": "sha512-x3+4w2SyIDEIe6PUBTzcjDFI5MhZNWAPELUxkYK0QMOkOo4w3BN/qhpd+fO1deFiDS23mZKqT8kfctO1IDkCDA=="
     },
     "angular-route": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.8.2.tgz",
-      "integrity": "sha512-49LJYxuaQ/ZDiu9dD2xo1LkazqObkGjw0a7dUF7UKCT8EELgBsMd6QrI6aEVGtI62ppkiFkxqmiV8fcwYdXpug=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.8.3.tgz",
+      "integrity": "sha512-kpIcRmDR2+o1FxDVVYy8Rvfab86/7LDbOgTRb9T+X9ewPQiBRuDEnZtM3oJYBiQLvAXDYTJXHV48n/bGE9Mv2g=="
     },
     "angular-sanitize": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.2.tgz",
-      "integrity": "sha512-OB6Goa+QN3byf5asQ7XRl7DKZejm/F/ZOqa9z1skqYVOWA2hoBxoCmt9E7+i7T/TbxZP5zYzKxNZVVJNu860Hg=="
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.3.tgz",
+      "integrity": "sha512-2rxdqzlUVafUeWOwvY/FtyWk1pFTyCtzreeiTytG9m4smpuAEKaIJAjYeVwWsoV+nlTOcgpwV4W1OCmR+BQbUg=="
     },
     "ansi-styles": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
-      "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg="
+      "integrity": "sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA=="
     },
     "buffer-from": {
       "version": "1.1.2",
@@ -693,7 +828,7 @@
     "chalk": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
-      "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+      "integrity": "sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==",
       "requires": {
         "ansi-styles": "~1.0.0",
         "has-color": "~0.1.0",
@@ -701,314 +836,326 @@
       }
     },
     "commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
     },
     "d3": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz",
-      "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==",
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
+      "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==",
       "requires": {
-        "d3-array": "1.2.1",
-        "d3-axis": "1.0.8",
-        "d3-brush": "1.0.4",
-        "d3-chord": "1.0.4",
-        "d3-collection": "1.0.4",
-        "d3-color": "1.0.3",
-        "d3-dispatch": "1.0.3",
-        "d3-drag": "1.2.1",
-        "d3-dsv": "1.0.8",
-        "d3-ease": "1.0.3",
-        "d3-force": "1.1.0",
-        "d3-format": "1.2.2",
-        "d3-geo": "1.9.1",
-        "d3-hierarchy": "1.1.5",
-        "d3-interpolate": "1.1.6",
-        "d3-path": "1.0.5",
-        "d3-polygon": "1.0.3",
-        "d3-quadtree": "1.0.3",
-        "d3-queue": "3.0.7",
-        "d3-random": "1.1.0",
-        "d3-request": "1.0.6",
-        "d3-scale": "1.0.7",
-        "d3-selection": "1.3.0",
-        "d3-shape": "1.2.0",
-        "d3-time": "1.0.8",
-        "d3-time-format": "2.1.1",
-        "d3-timer": "1.0.7",
-        "d3-transition": "1.1.1",
-        "d3-voronoi": "1.1.2",
-        "d3-zoom": "1.7.1"
+        "d3-array": "3",
+        "d3-axis": "3",
+        "d3-brush": "3",
+        "d3-chord": "3",
+        "d3-color": "3",
+        "d3-contour": "4",
+        "d3-delaunay": "6",
+        "d3-dispatch": "3",
+        "d3-drag": "3",
+        "d3-dsv": "3",
+        "d3-ease": "3",
+        "d3-fetch": "3",
+        "d3-force": "3",
+        "d3-format": "3",
+        "d3-geo": "3",
+        "d3-hierarchy": "3",
+        "d3-interpolate": "3",
+        "d3-path": "3",
+        "d3-polygon": "3",
+        "d3-quadtree": "3",
+        "d3-random": "3",
+        "d3-scale": "4",
+        "d3-scale-chromatic": "3",
+        "d3-selection": "3",
+        "d3-shape": "3",
+        "d3-time": "3",
+        "d3-time-format": "4",
+        "d3-timer": "3",
+        "d3-transition": "3",
+        "d3-zoom": "3"
       }
     },
     "d3-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
-      "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz",
+      "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==",
+      "requires": {
+        "internmap": "1 - 2"
+      }
     },
     "d3-axis": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz",
-      "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo="
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+      "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
     },
     "d3-brush": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
-      "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+      "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
       "requires": {
-        "d3-dispatch": "1",
-        "d3-drag": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "1",
-        "d3-transition": "1"
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "3",
+        "d3-transition": "3"
       }
     },
     "d3-chord": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
-      "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+      "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
       "requires": {
-        "d3-array": "1",
-        "d3-path": "1"
+        "d3-path": "1 - 3"
       }
     },
-    "d3-collection": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz",
-      "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI="
-    },
     "d3-color": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
-      "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
+    },
+    "d3-contour": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+      "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+      "requires": {
+        "d3-array": "^3.2.0"
+      }
+    },
+    "d3-delaunay": {
+      "version": "6.0.4",
+      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+      "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+      "requires": {
+        "delaunator": "5"
+      }
     },
     "d3-dispatch": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
-      "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
     },
     "d3-drag": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz",
-      "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
       "requires": {
-        "d3-dispatch": "1",
-        "d3-selection": "1"
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
       }
     },
     "d3-dsv": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz",
-      "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+      "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
       "requires": {
-        "commander": "2",
-        "iconv-lite": "0.4",
+        "commander": "7",
+        "iconv-lite": "0.6",
         "rw": "1"
       }
     },
     "d3-ease": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
-      "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
+    },
+    "d3-fetch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+      "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+      "requires": {
+        "d3-dsv": "1 - 3"
+      }
     },
     "d3-force": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz",
-      "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+      "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
       "requires": {
-        "d3-collection": "1",
-        "d3-dispatch": "1",
-        "d3-quadtree": "1",
-        "d3-timer": "1"
+        "d3-dispatch": "1 - 3",
+        "d3-quadtree": "1 - 3",
+        "d3-timer": "1 - 3"
       }
     },
     "d3-format": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz",
-      "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw=="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
     },
     "d3-geo": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz",
-      "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
+      "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
       "requires": {
-        "d3-array": "1"
+        "d3-array": "2.5.0 - 3"
       }
     },
     "d3-hierarchy": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz",
-      "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY="
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+      "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="
     },
     "d3-interpolate": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz",
-      "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
       "requires": {
-        "d3-color": "1"
+        "d3-color": "1 - 3"
       }
     },
     "d3-path": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
-      "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="
     },
     "d3-polygon": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
-      "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+      "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
     },
     "d3-quadtree": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
-      "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg="
-    },
-    "d3-queue": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz",
-      "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+      "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
     },
     "d3-random": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz",
-      "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM="
-    },
-    "d3-request": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz",
-      "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==",
-      "requires": {
-        "d3-collection": "1",
-        "d3-dispatch": "1",
-        "d3-dsv": "1",
-        "xmlhttprequest": "1"
-      }
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+      "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
     },
     "d3-scale": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz",
-      "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
       "requires": {
-        "d3-array": "^1.2.0",
-        "d3-collection": "1",
-        "d3-color": "1",
-        "d3-format": "1",
-        "d3-interpolate": "1",
-        "d3-time": "1",
-        "d3-time-format": "2"
+        "d3-array": "2.10.0 - 3",
+        "d3-format": "1 - 3",
+        "d3-interpolate": "1.2.0 - 3",
+        "d3-time": "2.1.1 - 3",
+        "d3-time-format": "2 - 4"
       }
     },
-    "d3-selection": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz",
-      "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA=="
-    },
-    "d3-selection-multi": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/d3-selection-multi/-/d3-selection-multi-1.0.1.tgz",
-      "integrity": "sha1-zWwlQT0EosuXRw54byzYd/PjT1g=",
+    "d3-scale-chromatic": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+      "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
       "requires": {
-        "d3-selection": "1",
-        "d3-transition": "1"
+        "d3-color": "1 - 3",
+        "d3-interpolate": "1 - 3"
       }
     },
+    "d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
+    },
     "d3-shape": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz",
-      "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
       "requires": {
-        "d3-path": "1"
+        "d3-path": "^3.1.0"
       }
     },
     "d3-time": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz",
-      "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ=="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+      "requires": {
+        "d3-array": "2 - 3"
+      }
     },
     "d3-time-format": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz",
-      "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
       "requires": {
-        "d3-time": "1"
+        "d3-time": "1 - 3"
       }
     },
     "d3-timer": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz",
-      "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA=="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
     },
     "d3-transition": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz",
-      "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
       "requires": {
-        "d3-color": "1",
-        "d3-dispatch": "1",
-        "d3-ease": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "^1.1.0",
-        "d3-timer": "1"
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
       }
     },
-    "d3-voronoi": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
-      "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw="
-    },
     "d3-zoom": {
-      "version": "1.7.1",
-      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz",
-      "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "requires": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      }
+    },
+    "delaunator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+      "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
       "requires": {
-        "d3-dispatch": "1",
-        "d3-drag": "1",
-        "d3-interpolate": "1",
-        "d3-selection": "1",
-        "d3-transition": "1"
+        "robust-predicates": "^3.0.0"
       }
     },
     "ev-emitter": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz",
-      "integrity": "sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q=="
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz",
+      "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q=="
     },
     "font-awesome": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
-      "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
+      "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg=="
     },
     "has-color": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
-      "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
+      "integrity": "sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw=="
     },
     "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
       "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
       }
     },
     "imagesloaded": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz",
-      "integrity": "sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-5.0.0.tgz",
+      "integrity": "sha512-/0JGSubc1MTCoDKVmonLHgbifBWHdyLkun+R/151E1c5n79hiSxcd7cB7mPXFgojYu8xnRZv7GYxzKoxW8BetQ==",
       "requires": {
-        "ev-emitter": "^1.0.0"
+        "ev-emitter": "^2.1.2"
       }
     },
+    "internmap": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
+    },
     "jquery": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
-      "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
+      "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
     },
     "jquery-form": {
       "version": "3.50.0",
       "resolved": "https://registry.npmjs.org/jquery-form/-/jquery-form-3.50.0.tgz",
-      "integrity": "sha1-18lptR4JThLaaZHrYf9a8l9ZeY8=",
+      "integrity": "sha512-WcBPCGF5U/ECu5p/2IkezCBcTBqGPwlbCIKyueWAgXxReNhpaOpSpG/dMr9yra42+ylFELzsuoDWuMCuc3lftQ==",
       "requires": {
         "jquery": ">=1.5"
       }
@@ -1016,15 +1163,15 @@
     "jquery-minicolors": {
       "version": "2.1.10",
       "resolved": "https://registry.npmjs.org/jquery-minicolors/-/jquery-minicolors-2.1.10.tgz",
-      "integrity": "sha1-sp7qVB2aMrTiaSMWj7LBYnGGc3k=",
+      "integrity": "sha512-vXG4hz0h97VtmXXXrG6ceM0kjANjMziWfT1L0TZ1PaTEN2AYdJZWCndrGX/ep4AWMGgXsnEfGl/h95Gjm/4eKw==",
       "requires": {
         "jquery": ">= 1.7.x"
       }
     },
     "jquery-ui": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.0.tgz",
-      "integrity": "sha512-Osf7ECXNTYHtKBkn9xzbIf9kifNrBhfywFEKxOeB/OVctVmLlouV9mfc2qXCp6uyO4Pn72PXKOnj09qXetopCw==",
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.2.tgz",
+      "integrity": "sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==",
       "requires": {
         "jquery": ">=1.8.0 <4.0.0"
       }
@@ -1040,14 +1187,9 @@
     "json-format": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-format/-/json-format-1.0.1.tgz",
-      "integrity": "sha1-FD9n5irxKda//tKIpGJl6iPQ3ww=",
+      "integrity": "sha512-MoKIg/lBeQALqjYnqEanikfo3zBKRwclpXJexdF0FUniYAAN2ypEIXBEtpQb+9BkLFtDK1fyTLAsnGlyGfLGxw==",
       "dev": true
     },
-    "JSON2": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/JSON2/-/JSON2-0.1.0.tgz",
-      "integrity": "sha1-jXSTBApj1YNa919H3suDq2yMB5A="
-    },
     "jsonlint": {
       "version": "1.6.3",
       "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
@@ -1057,28 +1199,30 @@
         "nomnom": "^1.5.x"
       }
     },
-    "JSV": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
-      "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
-    },
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "moment": {
-      "version": "2.29.2",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
-      "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg=="
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
     },
     "nomnom": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.0.tgz",
-      "integrity": "sha1-8XRItQxW8o0ZIdDoiZKgZrTxUVs=",
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+      "integrity": "sha512-5s0JxqhDx9/rksG2BTMVN1enjWSvPidpoSgViZU4ZXULyTe+7jxcCRLB6f42Z0l1xYJpleCBtSyY6Lwg3uu5CQ==",
       "requires": {
         "chalk": "~0.4.0",
-        "underscore": "~1.6.0"
+        "underscore": "1.13.2"
+      },
+      "dependencies": {
+        "underscore": {
+          "version": "1.13.2",
+          "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
+          "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g=="
+        }
       }
     },
     "npm-force-resolutions": {
@@ -1095,7 +1239,7 @@
     "qtip2": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/qtip2/-/qtip2-3.0.3.tgz",
-      "integrity": "sha1-ffCIrkQSwkpAZN5p6CTLPPdiENw=",
+      "integrity": "sha512-vfSEVYg1teA4g3RhT9fnBXzEQrkgyU5CrQE8j6WzZs5LBupG+Nq97mFq4BmDKcA6mGSMFiIqm70+ydvWW1KnGg==",
       "requires": {
         "imagesloaded": ">=3.0.0",
         "jquery": ">=1.6.0"
@@ -1104,12 +1248,17 @@
     "reset.css": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/reset.css/-/reset.css-2.0.2.tgz",
-      "integrity": "sha1-nAEwnIILcujyGevMjhQRlJYE99c="
+      "integrity": "sha512-HG5RQLYcAxou3yw+Gnzwan1jJCbOcIBZjwoD/YLWuAd8kkuDI/ZqC0uRhY4ReKOOJaM1Y1kvwebHOwK4P8Sevg=="
+    },
+    "robust-predicates": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+      "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
     },
     "rw": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
-      "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
     },
     "safer-buffer": {
       "version": "2.1.2",
@@ -1117,9 +1266,9 @@
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
     "slickgrid": {
-      "version": "2.4.38",
-      "resolved": "https://registry.npmjs.org/slickgrid/-/slickgrid-2.4.38.tgz",
-      "integrity": "sha512-BFbRXOkpbF44vCr6MxEpO/A7ruiPSINN7WoKaDkLza62CX14kq7Me6v77Q/bDsZsOeRsHudpRZufmt1GQixq5g==",
+      "version": "2.4.45",
+      "resolved": "https://registry.npmjs.org/slickgrid/-/slickgrid-2.4.45.tgz",
+      "integrity": "sha512-WvygGTaLU9LnMWZSxqW1agwq8IcDGeCZ289vP1E07eh3ffyLm5TGQsYHXvTwWCHtriZBw+L9RFK/h7TsIMcoLQ==",
       "requires": {
         "jquery": ">=1.8.0",
         "jquery-ui": ">=1.8.0"
@@ -1144,21 +1293,23 @@
     "strip-ansi": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
-      "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
+      "integrity": "sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg=="
     },
     "underscore": {
-      "version": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
       "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g=="
     },
     "url-search-params": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/url-search-params/-/url-search-params-0.6.1.tgz",
-      "integrity": "sha1-Ott4WLgH1WuB56vzudrkl4wD+Z0="
+      "integrity": "sha512-haKLRCc3JyU8uVBKNRgWJUu+iTgvfaiDZ3RT8OO3Dx0Ypq1G6cY1HhhcAOUL1y1+rfYR+5UcOyxgNcSa7g+Xbg=="
     },
     "xmlhttprequest": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
-      "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "dev": true
     }
   }
-}
+}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
index 1fb7de320e..6fbb5dd4f9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
@@ -20,33 +20,32 @@
     "preinstall": "npm --ignore-scripts install npm-force-resolutions && npm-force-resolutions"
   },
   "dependencies": {
-    "angular": "1.8.2",
-    "angular-animate": "1.8.2",
-    "angular-aria": "1.8.2",
+    "angular": "1.8.3",
+    "angular-animate": "1.8.3",
+    "angular-aria": "1.8.3",
     "angular-material": "1.1.26",
-    "angular-messages": "1.8.2",
-    "angular-mocks": "1.8.2",
-    "angular-resource": "1.8.2",
-    "angular-route": "1.8.2",
-    "angular-sanitize": "1.8.2",
-    "d3": "4.13.0",
-    "d3-selection-multi": "1.0.1",
+    "angular-messages": "1.8.3",
+    "angular-mocks": "1.8.3",
+    "angular-resource": "1.8.3",
+    "angular-route": "1.8.3",
+    "angular-sanitize": "1.8.3",
+    "d3": "7.6.1",
     "font-awesome": "4.7.0",
-    "jquery": "3.6.0",
+    "jquery": "3.6.4",
     "jquery-form": "3.50.0",
     "jquery-minicolors": "2.1.10",
     "jquery-ui-dist": "1.13.2",
     "JSON2": "0.1.0",
     "jsonlint": "1.6.3",
     "lodash": "4.17.21",
-    "moment": "2.29.2",
+    "moment": "2.29.4",
     "qtip2": "3.0.3",
     "reset.css": "2.0.2",
-    "slickgrid": "2.4.38",
+    "slickgrid": "2.4.45",
     "url-search-params": "0.6.1"
   },
   "devDependencies": {
-    "npm-force-resolutions": "0.0.10"
+    "npm-force-resolutions": "^0.0.10"
   },
   "resolutions": {
     "underscore": "1.13.2"
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index 664389c0b7..4c39bcd776 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -26,6 +26,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-ng-bridge.js?
 <script type="text/javascript" src="js/nf/canvas/nf-component-state.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-shell.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-client.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/helpers/d3-helpers.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-clipboard.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-canvas-utils.js?${project.version}"></script>\n\
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index fa6f20e016..3284677eae 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -47,8 +47,7 @@
         </script>
         <script type="text/javascript" src="assets/url-search-params/build/url-search-params.js"></script>
         <script type="text/javascript" src="js/codemirror/lib/codemirror-compressed.js"></script>
-        <script type="text/javascript" src="assets/d3/build/d3.min.js"></script>
-        <script type="text/javascript" src="assets/d3-selection-multi/build/d3-selection-multi.min.js"></script>
+        <script type="text/javascript" src="assets/d3/dist/d3.min.js"></script>
         <script type="text/javascript" src="assets/jquery/dist/jquery.min.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
index fa8d368356..64e0c9697d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
@@ -33,8 +33,7 @@
         <link rel="stylesheet" href="fonts/flowfont/flowfont.css" type="text/css" />
         <link rel="stylesheet" href="assets/angular-material/angular-material.min.css" type="text/css" />
         <link rel="stylesheet" href="assets/font-awesome/css/font-awesome.min.css" type="text/css" />
-        <script type="text/javascript" src="assets/d3/build/d3.min.js"></script>
-        <script type="text/javascript" src="assets/d3-selection-multi/build/d3-selection-multi.min.js"></script>
+        <script type="text/javascript" src="assets/d3/dist/d3.min.js"></script>
         <script type="text/javascript" src="assets/jquery/dist/jquery.min.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.center.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
index 89e4629f15..7a90f28c26 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
@@ -40,7 +40,7 @@
         <link rel="stylesheet" href="assets/angular-material/angular-material.min.css" type="text/css" />
         <link rel="stylesheet" href="assets/font-awesome/css/font-awesome.min.css" type="text/css" />
         <script type="text/javascript" src="js/codemirror/lib/codemirror-compressed.js"></script>
-        <script type="text/javascript" src="assets/d3/build/d3.min.js"></script>
+        <script type="text/javascript" src="assets/d3/dist/d3.min.js"></script>
         <script type="text/javascript" src="assets/jquery/dist/jquery.min.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
index e7a0dc8cb1..273896547b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
@@ -32,7 +32,7 @@
         <link rel="stylesheet" href="css/slick-nifi-theme.css" type="text/css" />
         <link rel="stylesheet" href="fonts/flowfont/flowfont.css" type="text/css" />
         <link rel="stylesheet" href="assets/font-awesome/css/font-awesome.min.css" type="text/css" />
-        <script type="text/javascript" src="assets/d3/build/d3.min.js"></script>
+        <script type="text/javascript" src="assets/d3/dist/d3.min.js"></script>
         <script type="text/javascript" src="assets/jquery/dist/jquery.min.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index 17db5f12ea..4fc73ba7da 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -1033,7 +1033,7 @@
             var descriptors = gridContainer.data('descriptors');
             var propertyDescriptor = descriptors[item.property];
 
-            var controllerServiceLookup = d3.map();
+            var controllerServiceLookup = new Map();
             var options = [];
             $.each(response.controllerServiceTypes, function (i, controllerServiceType) {
                 controllerServiceLookup.set(i, controllerServiceType);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-processor-component.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-processor-component.js
index 1a39e95865..2be3679537 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-processor-component.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-processor-component.js
@@ -486,9 +486,9 @@
                             dataType: 'json'
                         }).done(function (response) {
                             var tags = [];
-                            var groups = d3.set();
-                            var restrictedUsage = d3.map();
-                            var requiredPermissions = d3.map();
+                            var groups = new Set();
+                            var restrictedUsage = new Map();
+                            var requiredPermissions = new Map();
 
                             // begin the update
                             processorTypesData.beginUpdate();
@@ -584,7 +584,7 @@
                                 text: 'all groups',
                                 value: ''
                             }];
-                            groups.each(function (group) {
+                            groups.forEach(function (group) {
                                 options.push({
                                     text: group,
                                     value: group
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/helpers/d3-helpers.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/helpers/d3-helpers.js
new file mode 100644
index 0000000000..b88603de38
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/helpers/d3-helpers.js
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* global define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define([],
+            function () {
+                return (nf.ng.D3Helpers = factory());
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ng.D3Helpers =
+            factory());
+    } else {
+        nf.ng.D3Helpers = factory();
+    }
+}(this, function () {
+    'use strict';
+
+    return {
+        multiAttr: function(selection, attrs) {
+            Object.keys(attrs).forEach(function (key) {
+                selection.attr(key, attrs[key]);
+            });
+            return selection;
+        }
+    };
+}));
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
index 7cfbd2a1fa..6745d3094b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
@@ -1286,7 +1286,7 @@
                     nfSnippet.create(snippet).done(function (response) {
                         // remove the snippet, effectively removing the components
                         nfSnippet.remove(response.snippet.id).done(function () {
-                            var components = d3.map();
+                            var components = new Map();
 
                             // add the id to the type's array
                             var addComponent = function (type, id) {
@@ -1313,7 +1313,7 @@
                             });
 
                             // remove all the non connections in the snippet first
-                            components.each(function (ids, type) {
+                            components.forEach(function (ids, type) {
                                 if (type !== 'Connection') {
                                     nfCanvasUtils.getComponentByType(type).remove(ids);
                                 }
@@ -1919,7 +1919,7 @@
          * @param {array} selection      The selection
          */
         alignVertical: function (selection) {
-            var updates = d3.map();
+            var updates = new Map();
             // ensure every component is writable
             if (nfCanvasUtils.canModify(selection) === false) {
                 nfDialog.showOkDialog({
@@ -1987,7 +1987,7 @@
          * @param {array} selection      The selection
          */
         alignHorizontal: function (selection) {
-            var updates = d3.map();
+            var updates = new Map();
             // ensure every component is writable
             if (nfCanvasUtils.canModify(selection) === false) {
                 nfDialog.showOkDialog({
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
index 6cacb7f64b..e19dc2de09 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
@@ -24,9 +24,10 @@
                 'nf.Common',
                 'nf.CanvasUtils',
                 'nf.ContextMenu',
-                'nf.Label'],
-            function ($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel) {
-                return (nf.Birdseye = factory($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel));
+                'nf.Label',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel, d3Helpers) {
+                return (nf.Birdseye = factory($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Birdseye =
@@ -35,16 +36,18 @@
                 require('nf.Common'),
                 require('nf.CanvasUtils'),
                 require('nf.ContextMenu'),
-                require('nf.Label')));
+                require('nf.Label'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Birdseye = factory(root.$,
             root.d3,
             root.nf.Common,
             root.nf.CanvasUtils,
             root.nf.ContextMenu,
-            root.nf.Label);
+            root.nf.Label,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel) {
+}(this, function ($, d3, nfCommon, nfCanvasUtils, nfContextMenu, nfLabel, d3Helpers) {
     'use strict';
 
     var nfGraph;
@@ -147,8 +150,9 @@
         componentGroup.attr('transform', 'translate(' + birdseyeTranslate + ')');
 
         // update the brush
-        d3.select('rect.birdseye-brush')
-            .attrs({
+        d3Helpers.multiAttr(
+            d3.select('rect.birdseye-brush'),
+            {
                 'width': screenWidth,
                 'height': screenHeight,
                 'stroke-width': (2 / birdseyeScale),
@@ -280,9 +284,9 @@
                         source: 'birdseye'
                     };
                 })
-                .on('drag', function (d) {
-                    d.x += d3.event.dx;
-                    d.y += d3.event.dy;
+                .on('drag', function (event, d) {
+                    d.x += event.dx;
+                    d.y += event.dy;
 
                     // update the location of the brush
                     d3.select(this).attr('transform', function () {
@@ -290,7 +294,7 @@
                     });
 
                     // transform the canvas
-                    nfCanvasUtils.translateCanvas([-d3.event.dx, -d3.event.dy]);
+                    nfCanvasUtils.translateCanvas([-event.dx, -event.dy]);
                 })
                 .on('end', function () {
                     // update component visibility
@@ -304,8 +308,9 @@
                 });
 
             // context area
-            birdseyeGroup.append('g')
-                .attrs({
+            d3Helpers.multiAttr(
+                birdseyeGroup.append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'birdseye-brush-container'
                 })
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index fff859bbd3..c70bfb327b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -57,8 +57,8 @@
     var nfGraph;
     var trimLengthCaches = {};
 
-    var restrictedUsage = d3.map();
-    var requiredPermissions = d3.map();
+    var restrictedUsage = new Map();
+    var requiredPermissions = new Map();
 
     var config = {
         storage: {
@@ -103,7 +103,7 @@
             nfSnippet.create(snippet).done(function (response) {
                 // move the snippet into the target
                 nfSnippet.move(response.snippet.id, groupId).done(function () {
-                    var componentMap = d3.map();
+                    var componentMap = new Map();
 
                     // add the id to the type's array
                     var addComponent = function (type, id) {
@@ -119,7 +119,7 @@
                     });
 
                     // refresh all component types as necessary (handle components that have been removed)
-                    componentMap.each(function (ids, type) {
+                    componentMap.forEach(function (ids, type) {
                         nfCanvasUtils.getComponentByType(type).remove(ids);
                     });
 
@@ -808,11 +808,9 @@
 
             var line = [];
             var tspan = selection.append('tspan')
-                .attrs({
-                    'x': x,
-                    'y': y,
-                    'width': width
-                });
+                .attr('x', x)
+                .attr('y', y)
+                .attr('width', width);
 
             // go through each word
             var word = words.pop();
@@ -833,11 +831,9 @@
 
                     // create the tspan for the next line
                     tspan = selection.append('tspan')
-                        .attrs({
-                            'x': x,
-                            'dy': '1.2em',
-                            'width': width
-                        });
+                        .attr('x', x)
+                        .attr('dy', '1.2em')
+                        .attr('width', width);
 
                     // if we've reached the last line, use single line ellipsis
                     if (++i >= lineCount) {
@@ -952,9 +948,9 @@
          * @param {selection} selection                 The image
          */
         disableImageHref: function (selection) {
-            selection.on('click.disableImageHref', function () {
-                if (d3.event.ctrlKey || d3.event.shiftKey) {
-                    d3.event.preventDefault();
+            selection.on('click.disableImageHref', function (event) {
+                if (event.ctrlKey || event.shiftKey) {
+                    event.preventDefault();
                 }
             });
         },
@@ -1029,11 +1025,11 @@
          * @param {selection} target        The target of the tooltip
          */
         canvasTooltip: function (tip, target) {
-            target.on('mouseenter', function () {
-                tip.style('top', (d3.event.pageY + 15) + 'px').style('left', (d3.event.pageX + 15) + 'px').style('display', 'block');
+            target.on('mouseenter', function (event) {
+                tip.style('top', (event.pageY + 15) + 'px').style('left', (event.pageX + 15) + 'px').style('display', 'block');
             })
                 .on('mousemove', function () {
-                    tip.style('top', (d3.event.pageY + 15) + 'px').style('left', (d3.event.pageX + 15) + 'px');
+                    tip.style('top', (event.pageY + 15) + 'px').style('left', (event.pageX + 15) + 'px');
                 })
                 .on('mouseleave', function () {
                     tip.style('display', 'none');
@@ -2089,7 +2085,7 @@
          * @param additionalRequiredPermissions
          */
         addComponentRestrictions: function (additionalRestrictedUsages, additionalRequiredPermissions) {
-            additionalRestrictedUsages.each(function (componentRestrictions, requiredPermissionId) {
+            additionalRestrictedUsages.forEach(function (componentRestrictions, requiredPermissionId) {
                 if (!restrictedUsage.has(requiredPermissionId)) {
                     restrictedUsage.set(requiredPermissionId, []);
                 }
@@ -2098,7 +2094,7 @@
                     restrictedUsage.get(requiredPermissionId).push(componentRestriction);
                 });
             });
-            additionalRequiredPermissions.each(function (requiredPermissionLabel, requiredPermissionId) {
+            additionalRequiredPermissions.forEach(function (requiredPermissionLabel, requiredPermissionId) {
                 if (!requiredPermissions.has(requiredPermissionId)) {
                     requiredPermissions.set(requiredPermissionId, requiredPermissionLabel);
                 }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 77104bc3ce..e31afa850a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -35,9 +35,10 @@
                 'nf.ContextMenu',
                 'nf.Actions',
                 'nf.ProcessGroup',
-                'nf.ParameterContexts'],
-            function ($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts) {
-                return (nf.Canvas = factory($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts));
+                'nf.ParameterContexts',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts, d3Helpers) {
+                return (nf.Canvas = factory($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Canvas =
@@ -57,7 +58,8 @@
                 require('nf.ContextMenu'),
                 require('nf.Actions'),
                 require('nf.ProcessGroup'),
-                require('nf.ParameterContexts')));
+                require('nf.ParameterContexts'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Canvas = factory(root.$,
             root.d3,
@@ -75,9 +77,10 @@
             root.nf.ContextMenu,
             root.nf.Actions,
             root.nf.ProcessGroup,
-            root.nf.ParameterContexts);
+            root.nf.ParameterContexts,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts) {
+}(this, function ($, d3, nfCommon, nfDialog, nfGraph, nfShell, nfNgBridge, nfClusterSummary, nfErrorHandler, nfAuthorizationStorage, nfStorage, nfCanvasUtils, nfBirdseye, nfContextMenu, nfActions, nfProcessGroup, nfParameterContexts, d3Helpers) {
     'use strict';
 
     var SCALE = 1;
@@ -366,7 +369,7 @@
 
             // create the canvas
             svg = d3.select('#canvas-container').append('svg')
-                .on('contextmenu', function () {
+                .on('contextmenu', function (event) {
                     // reset the canvas click flag
                     canvasClicked = false;
 
@@ -377,20 +380,21 @@
                     nfCanvasUtils.setURLParameters();
 
                     // show the context menu on the canvas
-                    nfContextMenu.show();
+                    nfContextMenu.show(event);
 
                     // prevent default browser behavior
-                    d3.event.preventDefault();
+                    event.preventDefault();
                 });
 
             // create the definitions element
             var defs = svg.append('defs');
 
             // create arrow definitions for the various line types
-            defs.selectAll('marker')
+            d3Helpers.multiAttr(
+                defs.selectAll('marker')
                 .data(['normal', 'ghost', 'unauthorized', 'full'])
-                .enter().append('marker')
-                .attrs({
+                .enter().append('marker'),
+                {
                     'id': function (d) {
                         return d;
                     },
@@ -416,46 +420,56 @@
                 .attr('d', 'M2,3 L0,6 L6,3 L0,0 z');
 
             // filter for drop shadow
-            var componentDropShadowFilter = defs.append('filter')
-                .attrs({
-                    'id': 'component-drop-shadow',
-                    'height': '140%',
-                    'y': '-20%'
-                });
+            var componentDropShadowFilter = d3Helpers.multiAttr(
+                defs.append('filter'),
+                {
+                    id: 'component-drop-shadow',
+                    height: '140%',
+                    y: '-20%'
+                }
+            );
 
             // blur
-            componentDropShadowFilter.append('feGaussianBlur')
-                .attrs({
+            d3Helpers.multiAttr(
+                componentDropShadowFilter.append('feGaussianBlur'),
+                {
                     'in': 'SourceAlpha',
                     'stdDeviation': 3,
                     'result': 'blur'
-                });
+                }
+            );
 
             // offset
-            componentDropShadowFilter.append('feOffset')
-                .attrs({
+            d3Helpers.multiAttr(
+                componentDropShadowFilter.append('feOffset'),
+                {
                     'in': 'blur',
                     'dx': 0,
                     'dy': 1,
                     'result': 'offsetBlur'
-                });
+                }
+            );
 
             // color/opacity
-            componentDropShadowFilter.append('feFlood')
-                .attrs({
+            d3Helpers.multiAttr(
+                componentDropShadowFilter.append('feFlood'),
+                {
                     'flood-color': '#000000',
                     'flood-opacity': 0.4,
                     'result': 'offsetColor'
-                });
+                }
+            );
 
             // combine
-            componentDropShadowFilter.append('feComposite')
-                .attrs({
+            d3Helpers.multiAttr(
+                componentDropShadowFilter.append('feComposite'),
+                {
                     'in': 'offsetColor',
                     'in2': 'offsetBlur',
                     'operator': 'in',
                     'result': 'offsetColorBlur'
-                });
+                }
+            );
 
             // stack the effect under the source graph
             var componentDropShadowFeMerge = componentDropShadowFilter.append('feMerge');
@@ -465,46 +479,56 @@
                 .attr('in', 'SourceGraphic');
 
             // filter for drop shadow
-            var connectionFullDropShadowFilter = defs.append('filter')
-                .attrs({
-                    'id': 'connection-full-drop-shadow',
-                    'height': '140%',
-                    'y': '-20%'
-                });
+            var connectionFullDropShadowFilter = d3Helpers.multiAttr(
+                defs.append('filter'),
+                {
+                    id: 'connection-full-drop-shadow',
+                    height: '140%',
+                    y: '-20%'
+                }
+            );
 
             // blur
-            connectionFullDropShadowFilter.append('feGaussianBlur')
-                .attrs({
+            d3Helpers.multiAttr(
+                connectionFullDropShadowFilter.append('feGaussianBlur'),
+                {
                     'in': 'SourceAlpha',
                     'stdDeviation': 3,
                     'result': 'blur'
-                });
+                }
+            );
 
             // offset
-            connectionFullDropShadowFilter.append('feOffset')
-                .attrs({
+            d3Helpers.multiAttr(
+                connectionFullDropShadowFilter.append('feOffset'),
+                {
                     'in': 'blur',
                     'dx': 0,
                     'dy': 1,
                     'result': 'offsetBlur'
-                });
+                }
+            );
 
             // color/opacity
-            connectionFullDropShadowFilter.append('feFlood')
-                .attrs({
+            d3Helpers.multiAttr(
+                connectionFullDropShadowFilter.append('feFlood'),
+                {
                     'flood-color': '#ba554a',
                     'flood-opacity': 1,
                     'result': 'offsetColor'
-                });
+                }
+            );
 
             // combine
-            connectionFullDropShadowFilter.append('feComposite')
-                .attrs({
+            d3Helpers.multiAttr(
+                connectionFullDropShadowFilter.append('feComposite'),
+                {
                     'in': 'offsetColor',
                     'in2': 'offsetBlur',
                     'operator': 'in',
                     'result': 'offsetColorBlur'
-                });
+                }
+            );
 
             // stack the effect under the source graph
             var connectionFullFeMerge = connectionFullDropShadowFilter.append('feMerge');
@@ -514,27 +538,29 @@
                 .attr('in', 'SourceGraphic');
 
             // create the canvas element
-            canvas = svg.append('g')
-                .attrs({
+            canvas = d3Helpers.multiAttr(
+                svg.append('g'),
+                {
                     'transform': 'translate(' + TRANSLATE + ') scale(' + SCALE + ')',
                     'pointer-events': 'all',
                     'id': 'canvas'
-                });
+                }
+            );
 
             // handle canvas events
-            svg.on('mousedown.selection', function () {
+            svg.on('mousedown.selection', function (event) {
                 canvasClicked = true;
 
-                if (d3.event.button !== 0) {
+                if (event.button !== 0) {
                     // prevent further propagation (to parents and others handlers
                     // on the same element to prevent zoom behavior)
-                    d3.event.stopImmediatePropagation();
+                    event.stopImmediatePropagation();
                     return;
                 }
 
                 // show selection box if shift is held down
-                if (d3.event.shiftKey) {
-                    var position = d3.mouse(canvas.node());
+                if (event.shiftKey) {
+                    var position = d3.pointer(event, canvas.node());
                     canvas.append('rect')
                         .attr('rx', 6)
                         .attr('ry', 6)
@@ -553,21 +579,21 @@
 
                     // prevent further propagation (to parents and others handlers
                     // on the same element to prevent zoom behavior)
-                    d3.event.stopImmediatePropagation();
+                    event.stopImmediatePropagation();
 
                     // prevents the browser from changing to a text selection cursor
-                    d3.event.preventDefault();
+                    event.preventDefault();
                 }
             })
-            .on('mousemove.selection', function () {
+            .on('mousemove.selection', function (event) {
                 // update selection box if shift is held down
-                if (d3.event.shiftKey) {
+                if (event.shiftKey) {
                     // get the selection box
                     var selectionBox = d3.select('rect.component-selection');
                     if (!selectionBox.empty()) {
                         // get the original position
                         var originalPosition = selectionBox.datum();
-                        var position = d3.mouse(canvas.node());
+                        var position = d3.pointer(event, canvas.node());
 
                         var d = {};
                         if (originalPosition[0] < position[0]) {
@@ -587,10 +613,14 @@
                         }
 
                         // update the selection box
-                        selectionBox.attrs(d);
+                        selectionBox
+                            .attr('width', d.width)
+                            .attr('height', d.height)
+                            .attr('x', d.x)
+                            .attr('y', d.y);
 
                         // prevent further propagation (to parents)
-                        d3.event.stopPropagation();
+                        event.stopPropagation();
                     }
                 }
             })
@@ -673,10 +703,12 @@
                     'height': canvasHeight + 'px',
                     'bottom': bottom + 'px'
                 });
-                svg.attrs({
-                    'height': canvasContainer.height(),
-                    'width': $(window).width()
-                });
+                d3Helpers.multiAttr(
+                    svg,
+                    {
+                        'height': canvasContainer.height(),
+                        'width': $(window).width()
+                    });
 
                 //breadcrumbs
                 nfNgBridge.injector.get('breadcrumbsCtrl').updateBreadcrumbsCss({'bottom': bottom + 'px'});
@@ -1166,16 +1198,16 @@
                             // hide the context menu
                             nfContextMenu.hide();
                         })
-                        .on('zoom', function () {
+                        .on('zoom', function (event) {
                             // update the current translation and scale
-                            if (!isNaN(d3.event.transform.x)) {
-                                x = d3.event.transform.x;
+                            if (!isNaN(event.transform.x)) {
+                                x = event.transform.x;
                             }
-                            if (!isNaN(d3.event.transform.y)) {
-                                y = d3.event.transform.y;
+                            if (!isNaN(event.transform.y)) {
+                                y = event.transform.y;
                             }
-                            if (!isNaN(d3.event.transform.k)) {
-                                k = d3.event.transform.k;
+                            if (!isNaN(event.transform.k)) {
+                                k = event.transform.k;
                             }
 
                             // indicate that we are panning to prevent deselection in zoom.end below
@@ -1184,13 +1216,13 @@
                             // refresh the canvas
                             refreshed = nfCanvas.View.refresh({
                                 persist: false,
-                                transition: shouldTransition(d3.event.sourceEvent),
+                                transition: shouldTransition(event.sourceEvent),
                                 refreshComponents: false,
                                 refreshBirdseye: false
                             });
                         })
-                        .on('end', function () {
-                            if (!isBirdseyeEvent(d3.event.sourceEvent)) {
+                        .on('end', function (event) {
+                            if (!isBirdseyeEvent(event.sourceEvent)) {
                                 // ensure the canvas was actually refreshed
                                 if (nfCommon.isDefinedAndNotNull(refreshed)) {
                                     nfGraph.updateVisibility();
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-component-version.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-component-version.js
index b8b50381d6..e515a323a5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-component-version.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-component-version.js
@@ -96,9 +96,9 @@
      */
     var resetDialog = function () {
         // clear the versions
-        var versions = versionMap.keys();
+        var versions = Array.from(versionMap.keys());
         $.each(versions, function (_, version) {
-            versionMap.remove(version);
+            versionMap['delete'](version);
         });
 
         // clear the service apis
@@ -149,7 +149,7 @@
 
     return {
         init: function (settings) {
-            versionMap = d3.map();
+            versionMap = new Map();
             nfSettings = settings;
 
             // initialize the component version dialog
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
index 807c759a26..c43fc746ca 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
@@ -22,23 +22,26 @@
         define(['d3',
                 'nf.Connection',
                 'nf.ConnectionConfiguration',
-                'nf.CanvasUtils'],
-            function (d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils) {
-                return (nf.Connectable = factory(d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function (d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils, d3Helpers) {
+                return (nf.Connectable = factory(d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Connectable =
             factory(require('d3'),
                 require('nf.Connection'),
                 require('nf.ConnectionConfiguration'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Connectable = factory(root.d3,
             root.nf.Connection,
             root.nf.ConnectionConfiguration,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function (d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils) {
+}(this, function (d3, nfConnection, nfConnectionConfiguration, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var connect;
@@ -62,8 +65,8 @@
      *
      * @returns {boolean}
      */
-    var allowConnection = function () {
-        return !d3.event.shiftKey && d3.select('rect.drag-selection').empty() && d3.select('rect.component-selection').empty();
+    var allowConnection = function (event) {
+        return !event.shiftKey && d3.select('rect.drag-selection').empty() && d3.select('rect.component-selection').empty();
     };
 
     return {
@@ -72,16 +75,16 @@
 
             // dragging behavior for the connector
             connect = d3.drag()
-                .subject(function (d) {
-                    origin = d3.mouse(canvas.node());
+                .subject(function (event, d) {
+                    origin = d3.pointer(event, canvas.node());
                     return {
                         x: origin[0],
                         y: origin[1]
                     };
                 })
-                .on('start', function (d) {
+                .on('start', function (event) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
 
                     // unselect the previous components
                     nfCanvasUtils.getSelection().classed('selected', false);
@@ -96,20 +99,22 @@
                     var sourceData = source.datum();
 
                     // start the drag line and insert it first to keep it on the bottom
-                    var position = d3.mouse(canvas.node());
-                    canvas.insert('path', ':first-child')
-                        .datum({
-                            'sourceId': sourceData.id,
-                            'sourceWidth': sourceData.dimensions.width,
-                            'x': position[0],
-                            'y': position[1]
-                        })
-                        .attrs({
+                    const position = d3.pointer(event, canvas.node());
+                    d3Helpers.multiAttr(
+                        canvas.insert('path', ':first-child')
+                            .datum({
+                                sourceId: sourceData.id,
+                                sourceWidth: sourceData.dimensions.width / 2,
+                                x: sourceData.position.x + (sourceData.dimensions.width / 2),
+                                y: sourceData.position.y + (sourceData.dimensions.height / 2)
+                            }),
+                        {
                             'class': 'connector',
                             'd': function (pathDatum) {
                                 return 'M' + pathDatum.x + ' ' + pathDatum.y + 'L' + pathDatum.x + ' ' + pathDatum.y;
                             }
-                        });
+                        }
+                    );
 
                     // updates the location of the connection img
                     d3.select(this).attr('transform', function () {
@@ -119,10 +124,10 @@
                     // re-append the image to keep it on top
                     canvas.node().appendChild(this);
                 })
-                .on('drag', function (d) {
+                .on('drag', function (event) {
                     // updates the location of the connection img
                     d3.select(this).attr('transform', function () {
-                        return 'translate(' + d3.event.x + ', ' + (d3.event.y + 50) + ')';
+                        return 'translate(' + event.x + ', ' + (event.y + 50) + ')';
                     });
 
                     // mark node's connectable if supported
@@ -133,7 +138,7 @@
                         // click and contextmenu events to appear like an attempt to connection the
                         // component to itself. requiring the mouse to have actually moved before
                         // checking the eligiblity of the destination addresses the issue
-                        return (Math.abs(origin[0] - d3.event.x) > 10 || Math.abs(origin[1] - d3.event.y) > 10) &&
+                        return (Math.abs(origin[0] - event.x) > 10 || Math.abs(origin[1] - event.y) > 10) &&
                             nfCanvasUtils.isValidConnectionDestination(d3.select(this));
                     });
 
@@ -170,13 +175,13 @@
                                 return 'M' + pathDatum.x + ' ' + pathDatum.y + 'L' + end.x + ' ' + end.y;
                             }
                         } else {
-                            return 'M' + pathDatum.x + ' ' + pathDatum.y + 'L' + d3.event.x + ' ' + d3.event.y;
+                            return 'M' + pathDatum.x + ' ' + pathDatum.y + 'L' + event.x + ' ' + event.y;
                         }
                     });
                 })
-                .on('end', function (d) {
+                .on('end', function (event, d) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
 
                     // get the add connect img
                     var addConnect = d3.select(this);
@@ -197,7 +202,7 @@
                         var sourceData = source.datum();
 
                         // get the mouse position relative to the source
-                        var position = d3.mouse(source.node());
+                        var position = d3.pointer(event, source.node());
 
                         // if the position is outside the component, remove the add connect img
                         if (position[0] < 0 || position[0] > sourceData.dimensions.width || position[1] < 0 || position[1] > sourceData.dimensions.height) {
@@ -243,8 +248,8 @@
         activate: function (components) {
             components
                 .classed('connectable', true)
-                .on('mouseenter.connectable', function (d) {
-                    if (allowConnection()) {
+                .on('mouseenter.connectable', function (event, d) {
+                    if (allowConnection(event)) {
                         var selection = d3.select(this);
 
                         // ensure the current component supports connection source
@@ -255,17 +260,16 @@
                                 var x = (d.dimensions.width / 2) - 14;
                                 var y = (d.dimensions.height / 2) + 14;
 
-                                selection.append('text')
+                                selection
+                                    .append('text')
+                                    .attr('class', 'add-connect')
+                                    .attr('transform', 'translate(' + x + ', ' + y + ')')
+                                    .text('\ue834')
                                     .datum({
                                         origX: x,
                                         origY: y
                                     })
                                     .call(connect)
-                                    .attrs({
-                                        'class': 'add-connect',
-                                        'transform': 'translate(' + x + ', ' + y + ')'
-                                    })
-                                    .text('\ue834');
                             }
                         }
                     }
@@ -280,8 +284,8 @@
                 // Using mouseover/out to workaround chrome issue #122746
                 .on('mouseover.connectable', function () {
                     // mark that we are hovering when appropriate
-                    d3.select(this).classed('hover', function () {
-                        return allowConnection();
+                    d3.select(this).classed('hover', function (event) {
+                        return allowConnection(event);
                     });
                 })
                 .on('mouseout.connection', function () {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
index ad1b086801..424ec9a5ff 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
@@ -27,9 +27,10 @@
                 'nf.ErrorHandler',
                 'nf.Client',
                 'nf.CanvasUtils',
-                'lodash'],
-            function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) {
-                return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _));
+                'lodash',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _, d3Helpers) {
+                return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Connection =
@@ -41,7 +42,8 @@
                 require('nf.ErrorHandler'),
                 require('nf.Client'),
                 require('nf.CanvasUtils'),
-                require('lodash-code')));
+                require('lodash-code'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Connection = factory(root.$,
             root.d3,
@@ -51,9 +53,10 @@
             root.nf.ErrorHandler,
             root.nf.Client,
             root.nf.CanvasUtils,
-            root._);
+            root._,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) {
+}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _, d3Helpers) {
     'use strict';
 
     var nfSelectable;
@@ -294,7 +297,7 @@
      * Selects the connection elements against the current connection map.
      */
     var select = function () {
-        return connectionContainer.selectAll('g.connection').data(connectionMap.values(), function (d) {
+        return connectionContainer.selectAll('g.connection').data(Array.from(connectionMap.values()), function (d) {
             return d.id;
         });
     };
@@ -311,8 +314,9 @@
             return entered;
         }
 
-        var connection = entered.append('g')
-            .attrs({
+        var connection = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -321,28 +325,31 @@
             .classed('selected', selected);
 
         // create a connection between the two components
-        connection.append('path')
-            .attrs({
+        d3Helpers.multiAttr(
+            connection.append('path'),
+            {
                 'class': 'connection-path',
                 'pointer-events': 'none'
             });
 
         // path to show when selection
-        connection.append('path')
-            .attrs({
+        d3Helpers.multiAttr(
+            connection.append('path'),
+            {
                 'class': 'connection-selection-path',
                 'pointer-events': 'none'
             });
 
         // path to make selection easier
-        connection.append('path')
-            .attrs({
+        d3Helpers.multiAttr(
+            connection.append('path'),
+            {
                 'class': 'connection-path-selectable',
                 'pointer-events': 'stroke'
             })
-            .on('mousedown.selection', function () {
+            .on('mousedown.selection', function (event) {
                 // select the connection when clicking the selectable path
-                nfSelectable.select(d3.select(this.parentNode));
+                nfSelectable.select(event, d3.select(this.parentNode));
 
                 // update URL deep linking params
                 nfCanvasUtils.setURLParameters();
@@ -423,7 +430,7 @@
 
     // determines whether the connection is full based on the data size threshold
     var isFullBytes = function (d) {
-        return d.status.aggregateSnapshot.percentUseBytes === 100
+        return d.status.aggregateSnapshot.percentUseBytes === 100;
     };
 
     // determines whether the connection is in warning based on the data size threshold
@@ -498,9 +505,9 @@
 
             // update connection behavior
             updated.select('path.connection-path-selectable')
-                .on('dblclick', function (d) {
+                .on('dblclick', function (event, d) {
                     if (d.permissions.canWrite && d.permissions.canRead) {
-                        var position = d3.mouse(this.parentNode);
+                        var position = d3.pointer(event, this.parentNode);
 
                         // find where to put this bend point
                         var bendIndex = getNearestSegment({
@@ -533,7 +540,7 @@
                         // save the new state
                         save(d, connection);
 
-                        d3.event.stopPropagation();
+                        event.stopPropagation();
                     } else {
                         return null;
                     }
@@ -617,22 +624,25 @@
                 d.end = end;
 
                 // update the connection paths
-                nfCanvasUtils.transition(connection.select('path.connection-path'), transition)
-                    .attrs({
+                d3Helpers.multiAttr(
+                    nfCanvasUtils.transition(connection.select('path.connection-path'), transition),
+                    {
                         'd': function () {
                             var datum = [d.start].concat(d.bends, [d.end]);
                             return lineGenerator(datum);
                         }
                     });
-                nfCanvasUtils.transition(connection.select('path.connection-selection-path'), transition)
-                    .attrs({
+                d3Helpers.multiAttr(
+                    nfCanvasUtils.transition(connection.select('path.connection-selection-path'), transition),
+                    {
                         'd': function () {
                             var datum = [d.start].concat(d.bends, [d.end]);
                             return lineGenerator(datum);
                         }
                     });
-                nfCanvasUtils.transition(connection.select('path.connection-path-selectable'), transition)
-                    .attrs({
+                d3Helpers.multiAttr(
+                    nfCanvasUtils.transition(connection.select('path.connection-path-selectable'), transition),
+                    {
                         'd': function () {
                             var datum = [d.start].concat(d.bends, [d.end]);
                             return lineGenerator(datum);
@@ -658,16 +668,17 @@
                     startpoints = startpoints.data([d.start]);
 
                     // create a point for the start
-                    var startpointsEntered = startpoints.enter().append('rect')
-                        .attrs({
+                    var startpointsEntered = d3Helpers.multiAttr(
+                        startpoints.enter().append('rect'),
+                        {
                             'class': 'startpoint linepoint',
                             'pointer-events': 'all',
                             'width': 8,
                             'height': 8
                         })
-                        .on('mousedown.selection', function () {
+                        .on('mousedown.selection', function (event) {
                             // select the connection when clicking the label
-                            nfSelectable.select(d3.select(this.parentNode));
+                            nfSelectable.select(event, d3.select(this.parentNode));
 
                             // update URL deep linking params
                             nfCanvasUtils.setURLParameters();
@@ -690,16 +701,17 @@
                     var endpoints = endpoints.data([d.end]);
 
                     // create a point for the end
-                    var endpointsEntered = endpoints.enter().append('rect')
-                        .attrs({
+                    var endpointsEntered = d3Helpers.multiAttr(
+                        endpoints.enter().append('rect'),
+                        {
                             'class': 'endpoint linepoint',
                             'pointer-events': 'all',
                             'width': 8,
                             'height': 8
                         })
-                        .on('mousedown.selection', function () {
+                        .on('mousedown.selection', function (event) {
                             // select the connection when clicking the label
-                            nfSelectable.select(d3.select(this.parentNode));
+                            nfSelectable.select(event, d3.select(this.parentNode));
 
                             // update URL deep linking params
                             nfCanvasUtils.setURLParameters();
@@ -723,16 +735,17 @@
                     var midpoints = midpoints.data(d.bends);
 
                     // create a point for the end
-                    var midpointsEntered = midpoints.enter().append('rect')
-                        .attrs({
+                    var midpointsEntered = d3Helpers.multiAttr(
+                        midpoints.enter().append('rect'),
+                        {
                             'class': 'midpoint linepoint',
                             'pointer-events': 'all',
                             'width': 8,
                             'height': 8
                         })
-                        .on('dblclick', function (p) {
+                        .on('dblclick', function (event, p) {
                             // stop even propagation
-                            d3.event.stopPropagation();
+                            event.stopPropagation();
 
                             var connection = d3.select(this.parentNode);
                             var connectionData = connection.datum();
@@ -780,9 +793,9 @@
                             // save the updated connection
                             save(connectionData, connection);
                         })
-                        .on('mousedown.selection', function () {
+                        .on('mousedown.selection', function (event) {
                             // select the connection when clicking the label
-                            nfSelectable.select(d3.select(this.parentNode));
+                            nfSelectable.select(event, d3.select(this.parentNode));
 
                             // update URL deep linking params
                             nfCanvasUtils.setURLParameters();
@@ -816,14 +829,15 @@
                     // visible so we need to render it
                     if (connectionLabelContainer.empty()) {
                         // connection label container
-                        connectionLabelContainer = connection.insert('g', 'rect.startpoint')
-                            .attrs({
+                        connectionLabelContainer = d3Helpers.multiAttr(
+                            connection.insert('g', 'rect.startpoint'),
+                            {
                                 'class': 'connection-label-container',
                                 'pointer-events': 'all'
                             })
-                            .on('mousedown.selection', function () {
+                            .on('mousedown.selection', function (event) {
                                 // select the connection when clicking the label
-                                nfSelectable.select(d3.select(this.parentNode));
+                                nfSelectable.select(event, d3.select(this.parentNode));
 
                                 // update URL deep linking params
                                 nfCanvasUtils.setURLParameters();
@@ -831,8 +845,9 @@
                             .call(nfContextMenu.activate).call(nfQuickSelect.activate);
 
                         // connection label
-                        connectionLabelContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            connectionLabelContainer.append('rect'),
+                            {
                                 'class': 'body',
                                 'width': dimensions.width,
                                 'x': 0,
@@ -840,8 +855,9 @@
                             });
 
                         // processor border
-                        connectionLabelContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            connectionLabelContainer.append('rect'),
+                            {
                                 'class': 'border',
                                 'width': dimensions.width,
                                 'fill': 'transparent',
@@ -868,45 +884,51 @@
                         if (isGroup(d.component.source)) {
                             // see if the connection from label is already rendered
                             if (connectionFrom.empty()) {
-                                connectionFrom = connectionLabelContainer.append('g')
-                                    .attrs({
+                                connectionFrom = d3Helpers.multiAttr(
+                                    connectionLabelContainer.append('g'),
+                                    {
                                         'class': 'connection-from-container'
                                     });
 
                                 // background
-                                backgrounds.push(connectionFrom.append('rect')
-                                    .attrs({
+                                backgrounds.push(d3Helpers.multiAttr(
+                                    connectionFrom.append('rect'),
+                                    {
                                         'class': 'connection-label-background',
                                         'width': dimensions.width,
                                         'height': rowHeight
                                     }));
 
                                 // border
-                                borders.push(connectionFrom.append('rect')
-                                    .attrs({
+                                borders.push(d3Helpers.multiAttr(
+                                    connectionFrom.append('rect'),
+                                    {
                                         'class': 'connection-label-border',
                                         'width': dimensions.width,
                                         'height': 1
                                     }));
 
-                                connectionFrom.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionFrom.append('text'),
+                                    {
                                         'class': 'stats-label',
                                         'x': 5,
                                         'y': 14
                                     })
                                     .text('From');
 
-                                connectionFrom.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionFrom.append('text'),
+                                    {
                                         'class': 'stats-value connection-from',
                                         'x': 43,
                                         'y': 14,
                                         'width': 130
                                     });
 
-                                connectionFrom.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionFrom.append('text'),
+                                    {
                                         'class': 'connection-from-run-status',
                                         'x': 208,
                                         'y': 14
@@ -977,45 +999,51 @@
                         if (isGroup(d.component.destination)) {
                             // see if the connection to label is already rendered
                             if (connectionTo.empty()) {
-                                connectionTo = connectionLabelContainer.append('g')
-                                    .attrs({
+                                connectionTo = d3Helpers.multiAttr(
+                                    connectionLabelContainer.append('g'),
+                                    {
                                         'class': 'connection-to-container'
                                     });
 
                                 // background
-                                backgrounds.push(connectionTo.append('rect')
-                                    .attrs({
+                                backgrounds.push(d3Helpers.multiAttr(
+                                    connectionTo.append('rect'),
+                                    {
                                         'class': 'connection-label-background',
                                         'width': dimensions.width,
                                         'height': rowHeight
                                     }));
 
                                 // border
-                                borders.push(connectionTo.append('rect')
-                                    .attrs({
+                                borders.push(d3Helpers.multiAttr(
+                                    connectionTo.append('rect'),
+                                    {
                                         'class': 'connection-label-border',
                                         'width': dimensions.width,
                                         'height': 1
                                     }));
 
-                                connectionTo.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionTo.append('text'),
+                                    {
                                         'class': 'stats-label',
                                         'x': 5,
                                         'y': 14
                                     })
                                     .text('To');
 
-                                connectionTo.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionTo.append('text'),
+                                    {
                                         'class': 'stats-value connection-to',
                                         'x': 25,
                                         'y': 14,
                                         'width': 145
                                     });
 
-                                connectionTo.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionTo.append('text'),
+                                    {
                                         'class': 'connection-to-run-status',
                                         'x': 208,
                                         'y': 14
@@ -1089,37 +1117,42 @@
                         if (!nfCommon.isBlank(connectionNameValue)) {
                             // see if the connection name label is already rendered
                             if (connectionName.empty()) {
-                                connectionName = connectionLabelContainer.append('g')
-                                    .attrs({
+                                connectionName = d3Helpers.multiAttr(
+                                    connectionLabelContainer.append('g'),
+                                    {
                                         'class': 'connection-name-container'
                                     });
 
                                 // background
-                                backgrounds.push(connectionName.append('rect')
-                                    .attrs({
+                                backgrounds.push(d3Helpers.multiAttr(
+                                    connectionName.append('rect'),
+                                    {
                                         'class': 'connection-label-background',
                                         'width': dimensions.width,
                                         'height': rowHeight
                                     }));
 
                                 // border
-                                borders.push(connectionName.append('rect')
-                                    .attrs({
+                                borders.push(d3Helpers.multiAttr(
+                                    connectionName.append('rect'),
+                                    {
                                         'class': 'connection-label-border',
                                         'width': dimensions.width,
                                         'height': 1
                                     }));
 
-                                connectionName.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionName.append('text'),
+                                    {
                                         'class': 'stats-label',
                                         'x': 5,
                                         'y': 14
                                     })
                                     .text('Name');
 
-                                connectionName.append('text')
-                                    .attrs({
+                                d3Helpers.multiAttr(
+                                    connectionName.append('text'),
+                                    {
                                         'class': 'stats-value connection-name',
                                         'x': 45,
                                         'y': 14,
@@ -1169,58 +1202,66 @@
                     // see if the queue label is already rendered
                     var queued = connectionLabelContainer.select('g.queued-container');
                     if (queued.empty()) {
-                        queued = connectionLabelContainer.append('g')
-                            .attrs({
+                        queued = d3Helpers.multiAttr(
+                            connectionLabelContainer.append('g'),
+                            {
                                 'class': 'queued-container'
                             });
 
                         // background
-                        backgrounds.push(queued.append('rect')
-                            .attrs({
+                        backgrounds.push(d3Helpers.multiAttr(
+                            queued.append('rect'),
+                            {
                                 'class': 'connection-label-background',
                                 'width': dimensions.width,
                                 'height': rowHeight + HEIGHT_FOR_BACKPRESSURE
                             }));
 
                         // border
-                        borders.push(queued.append('rect')
-                            .attrs({
+                        borders.push(d3Helpers.multiAttr(
+                            queued.append('rect'),
+                            {
                                 'class': 'connection-label-border',
                                 'width': dimensions.width,
                                 'height': 1
                             }));
 
-                        queued.append('text')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            queued.append('text'),
+                            {
                                 'class': 'stats-label',
                                 'x': 5,
                                 'y': 14
                             })
                             .text('Queued');
 
-                        var queuedText = queued.append('text')
-                            .attrs({
+                        var queuedText = d3Helpers.multiAttr(
+                            queued.append('text'),
+                            {
                                 'class': 'stats-value queued',
                                 'x': 55,
                                 'y': 14
                             });
 
                         // queued count
-                        queuedText.append('tspan')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            queuedText.append('tspan'),
+                            {
                                 'class': 'count'
                             });
 
                         // queued size
-                        queuedText.append('tspan')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            queuedText.append('tspan'),
+                            {
                                 'class': 'size'
                             });
 
                         // load balance icon
                         // x is set dynamically to slide to right, depending on whether expiration icon is shown.
-                        queued.append('text')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            queued.append('text'),
+                            {
                                 'class': 'load-balance-icon',
                                 'y': 14
                             })
@@ -1230,8 +1271,9 @@
                             .append('title');
 
                         // expiration icon
-                        queued.append('text')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            queued.append('text'),
+                            {
                                 'class': 'expiration-icon',
                                 'x': 208,
                                 'y': 14
@@ -1244,15 +1286,17 @@
                         var yBackpressureOffset = rowHeight + HEIGHT_FOR_BACKPRESSURE - 4;
 
                         // backpressure object threshold
-                        var backpressureObjectContainer = queued.append('g')
-                            .attrs({
+                        var backpressureObjectContainer = d3Helpers.multiAttr(
+                            queued.append('g'),
+                            {
                                 'transform': 'translate(' + backpressureCountOffset + ', ' + yBackpressureOffset + ')',
                                 'class': 'backpressure-object-container'
                             });
 
                         // start
-                        backpressureObjectContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureObjectContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick object',
                                 'width': 1,
                                 'height': 3,
@@ -1261,8 +1305,9 @@
                             });
 
                         // bar
-                        backpressureObjectContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureObjectContainer.append('rect'),
+                            {
                                 'class': 'backpressure-object',
                                 'width': backpressureBarWidth,
                                 'height': 3,
@@ -1271,8 +1316,9 @@
                             });
 
                         // end
-                        backpressureObjectContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureObjectContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick object',
                                 'width': 1,
                                 'height': 3,
@@ -1281,8 +1327,9 @@
                             });
 
                         // percent full
-                        backpressureObjectContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureObjectContainer.append('rect'),
+                            {
                                 'class': 'backpressure-percent object',
                                 'width': 0,
                                 'height': 3,
@@ -1291,8 +1338,9 @@
                             });
 
                         // prediction indicator
-                        backpressureObjectContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureObjectContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick object-prediction',
                                 'width': 1,
                                 'height': 3,
@@ -1302,15 +1350,17 @@
 
                         // backpressure data size threshold
 
-                        var backpressureDataSizeContainer = queued.append('g')
-                            .attrs({
+                        var backpressureDataSizeContainer = d3Helpers.multiAttr(
+                            queued.append('g'),
+                            {
                                 'transform': 'translate(' + backpressureDataSizeOffset + ', ' + yBackpressureOffset + ')',
                                 'class': 'backpressure-data-size-container'
                             });
 
                         // start
-                        backpressureDataSizeContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureDataSizeContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick data-size',
                                 'width': 1,
                                 'height': 3,
@@ -1319,8 +1369,9 @@
                             });
 
                         // bar
-                        backpressureDataSizeContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureDataSizeContainer.append('rect'),
+                            {
                                 'class': 'backpressure-data-size',
                                 'width': backpressureBarWidth,
                                 'height': 3,
@@ -1330,8 +1381,9 @@
                             .append('title');
 
                         // end
-                        backpressureDataSizeContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureDataSizeContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick data-size',
                                 'width': 1,
                                 'height': 3,
@@ -1340,8 +1392,9 @@
                             });
 
                         // percent full
-                        backpressureDataSizeContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureDataSizeContainer.append('rect'),
+                            {
                                 'class': 'backpressure-percent data-size',
                                 'width': 0,
                                 'height': 3,
@@ -1350,8 +1403,9 @@
                             });
 
                         // prediction indicator
-                        backpressureDataSizeContainer.append('rect')
-                            .attrs({
+                        d3Helpers.multiAttr(
+                            backpressureDataSizeContainer.append('rect'),
+                            {
                                 'class': 'backpressure-tick data-size-prediction',
                                 'width': 1,
                                 'height': 3,
@@ -1413,42 +1467,42 @@
                                 return true;
                             }
                         }).classed('load-balance-icon-active fa-rotate-90', function (d) {
-                            return d.permissions.canRead && d.component.loadBalanceStatus === 'LOAD_BALANCE_ACTIVE';
+                        return d.permissions.canRead && d.component.loadBalanceStatus === 'LOAD_BALANCE_ACTIVE';
 
-                        }).classed('load-balance-icon-184', function() {
-                            return d.permissions.canRead && isExpirationConfigured(d.component);
+                    }).classed('load-balance-icon-184', function () {
+                        return d.permissions.canRead && isExpirationConfigured(d.component);
 
-                        }).classed('load-balance-icon-200', function() {
-                            return d.permissions.canRead && !isExpirationConfigured(d.component);
+                    }).classed('load-balance-icon-200', function () {
+                        return d.permissions.canRead && !isExpirationConfigured(d.component);
 
-                        }).attr('x', function() {
-                            return d.permissions.canRead && isExpirationConfigured(d.component) ? 192 : 208;
+                    }).attr('x', function () {
+                        return d.permissions.canRead && isExpirationConfigured(d.component) ? 192 : 208;
 
-                        }).select('title').text(function () {
-                            if (d.permissions.canRead) {
-                                var loadBalanceStrategy = nfCommon.getComboOptionText(nfCommon.loadBalanceStrategyOptions, d.component.loadBalanceStrategy);
-                                if ('PARTITION_BY_ATTRIBUTE' === d.component.loadBalanceStrategy) {
-                                    loadBalanceStrategy += ' (' + d.component.loadBalancePartitionAttribute + ')'
-                                }
+                    }).select('title').text(function () {
+                        if (d.permissions.canRead) {
+                            var loadBalanceStrategy = nfCommon.getComboOptionText(nfCommon.loadBalanceStrategyOptions, d.component.loadBalanceStrategy);
+                            if ('PARTITION_BY_ATTRIBUTE' === d.component.loadBalanceStrategy) {
+                                loadBalanceStrategy += ' (' + d.component.loadBalancePartitionAttribute + ')';
+                            }
 
-                                var loadBalanceCompression = 'no compression';
-                                switch (d.component.loadBalanceCompression) {
-                                    case 'COMPRESS_ATTRIBUTES_ONLY':
-                                        loadBalanceCompression = '\'Attribute\' compression';
-                                        break;
-                                    case 'COMPRESS_ATTRIBUTES_AND_CONTENT':
-                                        loadBalanceCompression = '\'Attribute and content\' compression';
-                                        break;
-                                }
-                                var loadBalanceStatus = 'LOAD_BALANCE_ACTIVE' === d.component.loadBalanceStatus ? ' Actively balancing...' : '';
-                                return 'Load Balance is configured'
-                                        + ' with \'' + loadBalanceStrategy + '\' strategy'
-                                        + ' and ' + loadBalanceCompression + '.'
-                                        + loadBalanceStatus;
-                            } else {
-                                return '';
+                            var loadBalanceCompression = 'no compression';
+                            switch (d.component.loadBalanceCompression) {
+                                case 'COMPRESS_ATTRIBUTES_ONLY':
+                                    loadBalanceCompression = '\'Attribute\' compression';
+                                    break;
+                                case 'COMPRESS_ATTRIBUTES_AND_CONTENT':
+                                    loadBalanceCompression = '\'Attribute and content\' compression';
+                                    break;
                             }
-                        });
+                            var loadBalanceStatus = 'LOAD_BALANCE_ACTIVE' === d.component.loadBalanceStatus ? ' Actively balancing...' : '';
+                            return 'Load Balance is configured'
+                                + ' with \'' + loadBalanceStrategy + '\' strategy'
+                                + ' and ' + loadBalanceCompression + '.'
+                                + loadBalanceStatus;
+                        } else {
+                            return '';
+                        }
+                    });
 
                     // determine whether or not to show the expiration icon
                     connectionLabelContainer.select('text.expiration-icon')
@@ -1546,9 +1600,9 @@
                 // only show predicted percent if it is non-negative
                 var predictionIntervalSeconds = _.get(predictions, 'predictionIntervalSeconds', 60 * 5);
                 if (_.isNumber(predictedPercentCount) && predictedPercentCount > -1) {
-                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): ' + _.clamp(predictedPercentCount, 0, 100) + '%')
+                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60) + ' mins): ' + _.clamp(predictedPercentCount, 0, 100) + '%');
                 } else {
-                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): NA' )
+                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60) + ' mins): NA');
                 }
 
                 // only show an estimate if it is valid (non-negative but less than the max number supported)
@@ -1559,7 +1613,7 @@
                     tooltipLines.push('Estimated time to back pressure: ' + (isAtBackPressure(d) ? 'now' : 'NA'));
                 }
             } else {
-                tooltipLines.push('Queue Prediction is not configured')
+                tooltipLines.push('Queue Prediction is not configured');
             }
 
             if (_.isEmpty(tooltipLines)) {
@@ -1567,7 +1621,7 @@
             } else if (_.size(tooltipLines) === 1) {
                 return tooltipLines[0];
             } else {
-                tooltipContent = nfCommon.formatUnorderedList(tooltipLines)
+                tooltipContent = nfCommon.formatUnorderedList(tooltipLines);
             }
         } else {
             tooltipContent = 'Back Pressure Object Threshold is not configured';
@@ -1597,9 +1651,9 @@
                 // only show predicted percent if it is non-negative
                 var predictionIntervalSeconds = _.get(predictions, 'predictionIntervalSeconds', 60 * 5);
                 if (_.isNumber(predictedPercentBytes) && predictedPercentBytes > -1) {
-                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60) + ' mins): ' + _.clamp(predictedPercentBytes, 0, 100) + '%')
+                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60) + ' mins): ' + _.clamp(predictedPercentBytes, 0, 100) + '%');
                 } else {
-                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): NA' )
+                    tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60) + ' mins): NA');
                 }
 
                 // only show an estimate if it is valid (non-negative but less than the max number supported)
@@ -1610,7 +1664,7 @@
                     tooltipLines.push('Estimated time to back pressure: ' + (isAtBackPressure(d) ? 'now' : 'NA'));
                 }
             } else {
-                tooltipLines.push('Queue Prediction is not configured')
+                tooltipLines.push('Queue Prediction is not configured');
             }
 
             if (_.isEmpty(tooltipLines)) {
@@ -1618,7 +1672,7 @@
             } else if (_.size(tooltipLines) === 1) {
                 return tooltipLines[0];
             } else {
-                tooltipContent = nfCommon.formatUnorderedList(tooltipLines)
+                tooltipContent = nfCommon.formatUnorderedList(tooltipLines);
             }
         } else {
             tooltipContent = 'Back Pressure Data Size Threshold is not configured';
@@ -1640,9 +1694,10 @@
         // penalized icon
         var connectionLabelContainer = updated.select('g.connection-label-container');
         if (connectionLabelContainer.select('text.penalized-icon').empty()) {
-            connectionLabelContainer.select('g.queued-container')
-                .append('text')
-                .attrs({
+            d3Helpers.multiAttr(
+                connectionLabelContainer.select('g.queued-container')
+                    .append('text'),
+                {
                     'class': 'penalized-icon',
                     'y': 14
                 })
@@ -1655,7 +1710,7 @@
         // determine whether or not to show the penalized icon
         connectionLabelContainer.select('text.penalized-icon')
             .classed('hidden', function (d) {
-                var flowFileAvailability = _.get(d, 'status.aggregateSnapshot.flowFileAvailability', null)
+                var flowFileAvailability = _.get(d, 'status.aggregateSnapshot.flowFileAvailability', null);
                 return flowFileAvailability !== 'HEAD_OF_QUEUE_PENALIZED';
             })
             .attr('x', function () {
@@ -1669,8 +1724,8 @@
                 return 208 - offset;
             })
             .select('title').text(function () {
-                return 'A FlowFile is currently penalized and data cannot be processed at this time.';
-            });
+            return 'A FlowFile is currently penalized and data cannot be processed at this time.';
+        });
 
         // update data size
         var dataSizeDeferred = $.Deferred(function (deferred) {
@@ -1681,9 +1736,10 @@
                 });
 
             var backpressurePercentDataSize = updated.select('rect.backpressure-percent.data-size');
-            backpressurePercentDataSize.transition()
-                .duration(400)
-                .attrs({
+            d3Helpers.multiAttr(
+                backpressurePercentDataSize.transition()
+                    .duration(400),
+                {
                     'width': function (d) {
                         if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseBytes)) {
                             return (backpressureBarWidth * d.status.aggregateSnapshot.percentUseBytes) / 100;
@@ -1704,9 +1760,10 @@
             });
 
             var backpressurePercentDataSizePrediction = updated.select('rect.backpressure-tick.data-size-prediction');
-            backpressurePercentDataSizePrediction.transition()
-                .duration(400)
-                .attrs({
+            d3Helpers.multiAttr(
+                backpressurePercentDataSizePrediction.transition()
+                    .duration(400),
+                {
                     'x': function (d) {
                         // clamp the prediction between 0 and 100 percent
                         var predicted = _.get(d, 'status.aggregateSnapshot.predictions.predictedPercentBytes', 0);
@@ -1726,25 +1783,25 @@
                         var actual = _.get(d, 'status.aggregateSnapshot.predictions.percentUseBytes', 0);
                         var predicted = _.get(d, 'status.aggregateSnapshot.predictions.predictedPercentBytes', 0);
                         return predicted < actual;
-                })
-            });
+                    });
+                });
 
             updated.select('g.backpressure-data-size-container')
-                .each(function(d) {
+                .each(function (d) {
                     var tip = d3.select('#back-pressure-size-tip-' + d.id);
 
                     // create a DOM element for the tooltip if ones does not already exist
                     if (tip.empty()) {
                         tip = d3.select('#connection-tooltips')
                             .append('div')
-                            .attr('id', function() {
-                                return 'back-pressure-size-tip-' + d.id
+                            .attr('id', function () {
+                                return 'back-pressure-size-tip-' + d.id;
                             })
                             .attr('class', 'tooltip nifi-tooltip');
                     }
 
                     // update the tooltip
-                    tip.html(function() {
+                    tip.html(function () {
                         return $('<div></div>').append(getBackPressureSizeTip(d)).html();
                     });
 
@@ -1761,9 +1818,10 @@
                 });
 
             var backpressurePercentObject = updated.select('rect.backpressure-percent.object');
-            backpressurePercentObject.transition()
-                .duration(400)
-                .attrs({
+            d3Helpers.multiAttr(
+                backpressurePercentObject.transition()
+                    .duration(400),
+                {
                     'width': function (d) {
                         if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseCount)) {
                             return (backpressureBarWidth * d.status.aggregateSnapshot.percentUseCount) / 100;
@@ -1783,11 +1841,11 @@
                     deferred.resolve();
                 });
 
-
             var backpressurePercentObjectPrediction = updated.select('rect.backpressure-tick.object-prediction');
-            backpressurePercentObjectPrediction.transition()
-                .duration(400)
-                .attrs({
+            d3Helpers.multiAttr(
+                backpressurePercentObjectPrediction.transition()
+                    .duration(400),
+                {
                     'x': function (d) {
                         // clamp the prediction between 0 and 100 percent
                         var predicted = _.get(d, 'status.aggregateSnapshot.predictions.predictedPercentCount', 0);
@@ -1802,30 +1860,31 @@
                             return 'none';
                         }
                     }
-                }).on('end', function () {
+                })
+                .on('end', function () {
                     backpressurePercentObjectPrediction.classed('prediction-down', function (d) {
                         var actual = _.get(d, 'status.aggregateSnapshot.percentUseCount', 0);
                         var predicted = _.get(d, 'status.aggregateSnapshot.predictions.predictedPercentCount', 0);
                         return predicted < actual;
-                    })
+                    });
                 });
 
             updated.select('g.backpressure-object-container')
-                .each(function(d) {
+                .each(function (d) {
                     var tip = d3.select('#back-pressure-count-tip-' + d.id);
 
                     // create a DOM element for the tooltip if ones does not already exist
                     if (tip.empty()) {
                         tip = d3.select('#connection-tooltips')
                             .append('div')
-                            .attr('id', function() {
-                                return 'back-pressure-count-tip-' + d.id
+                            .attr('id', function () {
+                                return 'back-pressure-count-tip-' + d.id;
                             })
                             .attr('class', 'tooltip nifi-tooltip');
                     }
 
                     // update the tooltip
-                    tip.html(function() {
+                    tip.html(function () {
                         return $('<div></div>').append(getBackPressureCountTip(d)).html();
                     });
 
@@ -1836,11 +1895,12 @@
         // update connection once progress bars have transitioned
         $.when(dataSizeDeferred, objectCountDeferred).done(function () {
             // connection stroke
-            updated.select('path.connection-path')
-                .classed('full', function (d) {
-                    return isFullCount(d) || isFullBytes(d);
-                })
-                .attrs({
+            d3Helpers.multiAttr(
+                updated.select('path.connection-path')
+                    .classed('full', function (d) {
+                        return isFullCount(d) || isFullBytes(d);
+                    }),
+                {
                     'marker-end': getEndMarker
                 });
 
@@ -1851,8 +1911,9 @@
                 });
 
             // drop shadow
-            updated.select('rect.body')
-                .attrs({
+            d3Helpers.multiAttr(
+                updated.select('rect.body'),
+                {
                     'filter': getDropShadow
                 });
         });
@@ -1917,6 +1978,7 @@
          * @param nfSelectableRef   The nfSelectable module.
          * @param nfContextMenuRef   The nfContextMenu module.
          * @param nfQuickSelectRef   The nfQuickSelect module.
+         * @param nfConnectionConfigurationRef   The nfConnectionConfigurationRef module.
          */
         init: function (nfSelectableRef, nfContextMenuRef, nfQuickSelectRef, nfConnectionConfigurationRef) {
             nfSelectable = nfSelectableRef;
@@ -1924,13 +1986,14 @@
             nfQuickSelect = nfQuickSelectRef;
             nfConnectionConfiguration = nfConnectionConfigurationRef;
 
-            connectionMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            connectionMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the connection container
-            connectionContainer = d3.select('#canvas').append('g')
-                .attrs({
+            connectionContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'stroke',
                     'class': 'connections'
                 });
@@ -1947,14 +2010,14 @@
 
             // handle bend point drag events
             bendPointDrag = d3.drag()
-                .on('start', function () {
+                .on('start', function (event) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 })
-                .on('drag', function (d) {
-                    snapEnabled = !d3.event.sourceEvent.shiftKey;
-                    d.x = snapEnabled ? (Math.round(d3.event.x/snapAlignmentPixels) * snapAlignmentPixels) : d3.event.x;
-                    d.y = snapEnabled ? (Math.round(d3.event.y/snapAlignmentPixels) * snapAlignmentPixels) : d3.event.y;
+                .on('drag', function (event, d) {
+                    snapEnabled = !event.sourceEvent.shiftKey;
+                    d.x = snapEnabled ? (Math.round(event.x / snapAlignmentPixels) * snapAlignmentPixels) : event.x;
+                    d.y = snapEnabled ? (Math.round(event.y / snapAlignmentPixels) * snapAlignmentPixels) : event.y;
 
                     // redraw this connection
                     d3.select(this.parentNode).call(updateConnections, {
@@ -1962,7 +2025,7 @@
                         'updateLabel': false
                     });
                 })
-                .on('end', function () {
+                .on('end', function (event) {
                     var connection = d3.select(this.parentNode);
                     var connectionData = connection.datum();
                     var bends = connection.selectAll('rect.midpoint').data();
@@ -2001,21 +2064,21 @@
                     }
 
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 });
 
             // handle endpoint drag events
             endpointDrag = d3.drag()
-                .on('start', function (d) {
+                .on('start', function (event, d) {
                     // indicate that dragging has begun
                     d.dragging = true;
 
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 })
-                .on('drag', function (d) {
-                    d.x = d3.event.x - 8;
-                    d.y = d3.event.y - 8;
+                .on('drag', function (event, d) {
+                    d.x = event.x - 8;
+                    d.y = event.y - 8;
 
                     // ensure the new destination is valid
                     d3.select('g.hover').classed('connectable-destination', function () {
@@ -2028,7 +2091,7 @@
                         'updateLabel': false
                     });
                 })
-                .on('end', function (d) {
+                .on('end', function (event, d) {
                     // indicate that dragging as stopped
                     d.dragging = false;
 
@@ -2136,17 +2199,17 @@
                     }
 
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 });
 
             // label drag behavior
             labelDrag = d3.drag()
-                .on('start', function (d) {
+                .on('start', function (event, d) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 })
-                .on('drag', function (d) {
-                    if (d.bends.length > 1) {
+                .on('drag', function (event, d) {
+                    if (nfCommon.isDefinedAndNotNull(d) && d.bends.length > 1) {
                         // get the dragged component
                         var drag = d3.select('rect.label-drag');
 
@@ -2180,11 +2243,11 @@
                         } else {
                             // update the position of the drag selection
                             drag.attr('x', function (d) {
-                                d.x += d3.event.dx;
+                                d.x += event.dx;
                                 return d.x;
                             })
                                 .attr('y', function (d) {
-                                    d.y += d3.event.dy;
+                                    d.y += event.dy;
                                     return d.y;
                                 });
                         }
@@ -2224,7 +2287,7 @@
                         });
                     }
                 })
-                .on('end', function (d) {
+                .on('end', function (event, d) {
                     if (d.bends.length > 1) {
                         // get the drag selection
                         var drag = d3.select('rect.label-drag');
@@ -2258,7 +2321,7 @@
                     }
 
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 });
         },
 
@@ -2321,8 +2384,8 @@
                 return false;
             }
 
-            var connections = d3.map();
-            var components = d3.map();
+            var connections = new Map();
+            var components = new Map();
             var isDisconnected = true;
 
             // include connections
@@ -2349,7 +2412,7 @@
 
             if (isDisconnected) {
                 // go through each connection to ensure its source and destination are included
-                connections.each(function (connection, id) {
+                connections.forEach(function (connection, id) {
                     if (isDisconnected) {
                         // determine whether this connection and its components are included within the selection
                         isDisconnected = components.has(nfCanvasUtils.getConnectionSourceComponentId(connection)) && components.has(nfCanvasUtils.getConnectionDestinationComponentId(connection));
@@ -2388,7 +2451,7 @@
 
             // determine how to handle the specified connection
             if ($.isArray(connectionEntities)) {
-                $.each(connectionMap.keys(), function (_, key) {
+                $.each(Array.from(connectionMap.keys()), function (_, key) {
                     var currentConnectionEntity = connectionMap.get(key);
                     var isPresent = $.grep(connectionEntities, function (proposedConnectionEntity) {
                         return proposedConnectionEntity.id === currentConnectionEntity.id;
@@ -2396,7 +2459,7 @@
 
                     // if the current connection is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        connectionMap.remove(key);
+                        connectionMap['delete'](key);
                     }
                 });
                 $.each(connectionEntities, function (_, connectionEntity) {
@@ -2464,11 +2527,11 @@
             if ($.isArray(connectionIds)) {
                 $.each(connectionIds, function (_, connectionId) {
                     removedCache.set(connectionId, now);
-                    connectionMap.remove(connectionId);
+                    connectionMap['delete'](connectionId);
                 });
             } else {
                 removedCache.set(connectionIds, now);
-                connectionMap.remove(connectionIds);
+                connectionMap['delete'](connectionIds);
             }
 
             // apply the selection and handle all removed connections
@@ -2479,7 +2542,7 @@
          * Removes all processors.
          */
         removeAll: function () {
-            nfConnection.remove(connectionMap.keys());
+            nfConnection.remove(Array.from(connectionMap.keys()));
         },
 
         /**
@@ -2531,7 +2594,7 @@
          */
         getComponentConnections: function (id) {
             var connections = [];
-            connectionMap.each(function (entry, _) {
+            connectionMap.forEach(function (entry, _) {
                 // see if this component is the source or destination of this connection
                 if (nfCanvasUtils.getConnectionSourceComponentId(entry) === id || nfCanvasUtils.getConnectionDestinationComponentId(entry) === id) {
                     connections.push(entry);
@@ -2548,7 +2611,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return connectionMap.values();
+                return Array.from(connectionMap.values());
             } else {
                 return connectionMap.get(id);
             }
@@ -2561,9 +2624,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
index cb09577fc9..cfc6f95317 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
@@ -917,7 +917,7 @@
         /**
          * Shows the context menu.
          */
-        show: function () {
+        show: function (event) {
             // hide the menu if currently visible
             nf.ContextMenu.hide();
 
@@ -930,7 +930,7 @@
             var selection = nfCanvasUtils.getSelection();
 
             // get the location for the context menu
-            var position = d3.mouse(canvasBody);
+            var position = d3.pointer(event, canvasBody);
 
             // determines if the specified menu positioned at x would overflow the available width
             var overflowRight = function (x, menu) {
@@ -1104,13 +1104,13 @@
          * @param {selection} components    The components to enable the context menu for
          */
         activate: function (components) {
-            components.on('contextmenu.selection', function () {
+            components.on('contextmenu.selection', function (event) {
                 // get the clicked component to update selection
-                nfContextMenu.show();
+                nfContextMenu.show(event);
 
                 // stop propagation and prevent default
-                d3.event.preventDefault();
-                d3.event.stopPropagation();
+                event.preventDefault();
+                event.stopPropagation();
             });
         }
     };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 8046f7d642..9c09beb398 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -829,15 +829,15 @@
      * @param {object} controllerService
      */
     var getReferencingControllerServiceIds = function (controllerService) {
-        var ids = d3.set();
-        ids.add(controllerService.id);
+        var ids = new Set();
+        ids.push(controllerService.id);
 
         var checkReferencingServices = function (referencingComponents) {
             $.each(referencingComponents, function (_, referencingComponentEntity) {
                 var referencingComponent = referencingComponentEntity.component;
                 if (referencingComponent.referenceType === 'ControllerService') {
                     // add the id
-                    ids.add(referencingComponent.id);
+                    ids.push(referencingComponent.id);
 
                     // consider it's referencing components if appropriate
                     if (referencingComponent.referenceCycle === false) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js
index 7dff947913..1c8b1995ec 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js
@@ -541,9 +541,9 @@
         }).done(function (response) {
             var id = 0;
             var tags = [];
-            var groups = d3.set();
-            var restrictedUsage = d3.map();
-            var requiredPermissions = d3.map();
+            var groups = new Set();
+            var restrictedUsage = new Map();
+            var requiredPermissions = new Map();
 
             // begin the update
             controllerServiceTypesData.beginUpdate();
@@ -638,7 +638,7 @@
                 text: 'all groups',
                 value: ''
             }];
-            groups.each(function (group) {
+            groups.forEach(function (group) {
                 options.push({
                     text: group,
                     value: group
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
index 37904a3ecd..7148d05b9e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
@@ -70,7 +70,7 @@
      * @param {selection} dragSelection The current drag selection
      */
     var updateComponentsPosition = function (dragSelection) {
-        var updates = d3.map();
+        var updates = new Map();
 
         // determine the drag delta
         var dragData = dragSelection.datum();
@@ -158,11 +158,11 @@
 
             // handle component drag events
             drag = d3.drag()
-                .on('start', function () {
+                .on('start', function (event) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 })
-                .on('drag', function () {
+                .on('drag', function (event) {
                     var dragSelection = d3.select('rect.drag-selection');
 
                     // lazily create the drag selection box
@@ -216,19 +216,19 @@
                     } else {
                         // update the position of the drag selection
                         // snap align the position unless the user is holding shift
-                        snapEnabled = !d3.event.sourceEvent.shiftKey;
+                        snapEnabled = !event.sourceEvent.shiftKey;
                         dragSelection.attr('x', function (d) {
-                            d.x += d3.event.dx;
+                            d.x += event.dx;
                             return snapEnabled ? (Math.round(d.x/snapAlignmentPixels) * snapAlignmentPixels) : d.x;
                         }).attr('y', function (d) {
-                                d.y += d3.event.dy;
+                                d.y += event.dy;
                                 return snapEnabled ? (Math.round(d.y/snapAlignmentPixels) * snapAlignmentPixels) : d.y;
                         });
                      }
                 })
-                .on('end', function () {
+                .on('end', function (event) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
 
                     // get the drag selection
                     var dragSelection = d3.select('rect.drag-selection');
@@ -362,11 +362,11 @@
          * @param updates
          */
         refreshConnections: function (updates) {
-            if (updates.size() > 0) {
+            if (updates.size > 0) {
                 // wait for all updates to complete
-                $.when.apply(window, updates.values()).done(function () {
+                $.when.apply(window, Array.from(updates.values())).done(function () {
                     var dragged = $.makeArray(arguments);
-                    var connections = d3.set();
+                    var connections = new Set();
 
                     // refresh this component
                     $.each(dragged, function (_, component) {
@@ -383,7 +383,7 @@
                     });
 
                     // refresh the connections
-                    connections.each(function (connectionId) {
+                    connections.forEach(function (connectionId) {
                         nfConnection.refresh(connectionId);
                     });
                 }).always(function () {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
index a18640798c..1565637440 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
@@ -23,9 +23,10 @@
                 'd3',
                 'nf.Common',
                 'nf.Client',
-                'nf.CanvasUtils'],
-            function ($, d3, nfCommon, nfClient, nfCanvasUtils) {
-                return (nf.Funnel = factory($, d3, nfCommon, nfClient, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
+                return (nf.Funnel = factory($, d3, nfCommon, nfClient, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Funnel =
@@ -33,15 +34,17 @@
                 require('d3'),
                 require('nf.Common'),
                 require('nf.Client'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Funnel = factory(root.$,
             root.d3,
             root.nf.Common,
             root.nf.Client,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfCommon, nfClient, nfCanvasUtils) {
+}(this, function ($, d3, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -81,7 +84,7 @@
      * Selects the funnel elements against the current funnel map.
      */
     var select = function () {
-        return funnelContainer.selectAll('g.funnel').data(funnelMap.values(), function (d) {
+        return funnelContainer.selectAll('g.funnel').data(Array.from(funnelMap.values()), function (d) {
             return d.id;
         });
     };
@@ -98,8 +101,9 @@
             return entered;
         }
 
-        var funnel = entered.append('g')
-            .attrs({
+        var funnel = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -109,8 +113,9 @@
             .call(nfCanvasUtils.position);
 
         // funnel border
-        funnel.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            funnel.append('rect'),
+            {
                 'rx': 2,
                 'ry': 2,
                 'class': 'border',
@@ -125,8 +130,9 @@
             });
 
         // funnel body
-        funnel.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            funnel.append('rect'),
+            {
                 'rx': 2,
                 'ry': 2,
                 'class': 'body',
@@ -141,8 +147,9 @@
             });
 
         // funnel icon
-        funnel.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            funnel.append('text'),
+            {
                 'class': 'funnel-icon',
                 'x': 9,
                 'y': 34
@@ -209,13 +216,14 @@
             nfSelectable = nfSelectableRef;
             nfContextMenu = nfContextMenuRef;
 
-            funnelMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            funnelMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the funnel container
-            funnelContainer = d3.select('#canvas').append('g')
-                .attrs({
+            funnelContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'funnels'
                 });
@@ -294,7 +302,7 @@
             };
 
             if ($.isArray(funnelEntities)) {
-                $.each(funnelMap.keys(), function (_, key) {
+                $.each(Array.from(funnelMap.keys()), function (_, key) {
                     var currentFunnelEntity = funnelMap.get(key);
                     var isPresent = $.grep(funnelEntities, function (proposedFunnelEntity) {
                         return proposedFunnelEntity.id === currentFunnelEntity.id;
@@ -302,7 +310,7 @@
 
                     // if the current funnel is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        funnelMap.remove(key);
+                        funnelMap['delete'](key);
                     }
                 });
                 $.each(funnelEntities, function (_, funnelEntity) {
@@ -334,7 +342,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return funnelMap.values();
+                return Array.from(funnelMap.values());
             } else {
                 return funnelMap.get(id);
             }
@@ -393,11 +401,11 @@
             if ($.isArray(funnelIds)) {
                 $.each(funnelIds, function (_, funnelId) {
                     removedCache.set(funnelId, now);
-                    funnelMap.remove(funnelId);
+                    funnelMap['delete'](funnelId);
                 });
             } else {
                 removedCache.set(funnelIds, now);
-                funnelMap.remove(funnelIds);
+                funnelMap['delete'](funnelIds);
             }
 
             // apply the selection and handle all removed funnels
@@ -408,7 +416,7 @@
          * Removes all processors.
          */
         removeAll: function () {
-            nfFunnel.remove(funnelMap.keys());
+            nfFunnel.remove(Array.from(funnelMap.keys()));
         },
 
         /**
@@ -418,9 +426,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
index bee6e47f1f..92ce9e74b4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
@@ -24,9 +24,10 @@
                 'nf.Storage',
                 'nf.Common',
                 'nf.Client',
-                'nf.CanvasUtils'],
-            function ($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils) {
-                return (nf.Label = factory($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
+                return (nf.Label = factory($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Label =
@@ -35,16 +36,18 @@
                 require('nf.Storage'),
                 require('nf.Common'),
                 require('nf.Client'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Label = factory(root.$,
             root.d3,
             root.nf.Storage,
             root.nf.Common,
             root.nf.Client,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils) {
+}(this, function ($, d3, nfStorage, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -100,7 +103,7 @@
      * Selects the labels elements against the current label map.
      */
     var select = function () {
-        return labelContainer.selectAll('g.label').data(labelMap.values(), function (d) {
+        return labelContainer.selectAll('g.label').data(Array.from(labelMap.values()), function (d) {
             return d.id;
         });
     };
@@ -128,8 +131,9 @@
             return entered;
         }
 
-        var label = entered.append('g')
-            .attrs({
+        var label = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -139,24 +143,27 @@
             .call(nfCanvasUtils.position);
 
         // label border
-        label.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            label.append('rect'),
+            {
                 'class': 'border',
                 'fill': 'transparent',
                 'stroke': 'transparent'
             });
 
         // label
-        label.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            label.append('rect'),
+            {
                 'class': 'body',
                 'filter': 'url(#component-drop-shadow)',
                 'stroke-width': 0
             });
 
         // label value
-        label.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            label.append('text'),
+            {
                 'xml:space': 'preserve',
                 'font-weight': 'bold',
                 'fill': 'black',
@@ -180,8 +187,9 @@
         }
 
         // update the border using the configured color
-        updated.select('rect.border')
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('rect.border'),
+            {
                 'width': function (d) {
                     return d.dimensions.width;
                 },
@@ -194,8 +202,9 @@
             });
 
         // update the body fill using the configured color
-        updated.select('rect.body')
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('rect.body'),
+            {
                 'width': function (d) {
                     return d.dimensions.width;
                 },
@@ -289,8 +298,9 @@
                     var points = labelPoint.data(pointData);
 
                     // create a point for the end
-                    var pointsEntered = points.enter().append('rect')
-                        .attrs({
+                    var pointsEntered = d3Helpers.multiAttr(
+                        points.enter().append('rect'),
+                        {
                             'class': 'labelpoint',
                             'width': 10,
                             'height': 10
@@ -346,37 +356,38 @@
             nfContextMenu = nfContextMenuRef;
             nfQuickSelect = nfQuickSelectRef;
 
-            labelMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            labelMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the label container
-            labelContainer = d3.select('#canvas').append('g')
-                .attrs({
+            labelContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'labels'
                 });
 
             // handle bend point drag events
             labelPointDrag = d3.drag()
-                .on('start', function () {
+                .on('start', function (event) {
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 })
-                .on('drag', function () {
+                .on('drag', function (event) {
                     var label = d3.select(this.parentNode);
                     var labelData = label.datum();
 
                     // update the dimensions and ensure they are still within bounds
                     // snap between aligned sizes unless the user is holding shift
-                    snapEnabled = !d3.event.sourceEvent.shiftKey;
-                    labelData.dimensions.width = Math.max(MIN_WIDTH, snapEnabled ? (Math.round(d3.event.x/snapAlignmentPixels) * snapAlignmentPixels) : d3.event.x);
-                    labelData.dimensions.height = Math.max(MIN_HEIGHT, snapEnabled ? (Math.round(d3.event.y/snapAlignmentPixels) * snapAlignmentPixels) : d3.event.y);
+                    snapEnabled = !event.sourceEvent.shiftKey;
+                    labelData.dimensions.width = Math.max(MIN_WIDTH, snapEnabled ? (Math.round(event.x/snapAlignmentPixels) * snapAlignmentPixels) : event.x);
+                    labelData.dimensions.height = Math.max(MIN_HEIGHT, snapEnabled ? (Math.round(event.y/snapAlignmentPixels) * snapAlignmentPixels) : event.y);
 
                     // redraw this connection
                     updateLabels(label);
                 })
-                .on('end', function () {
+                .on('end', function (event) {
                     var label = d3.select(this.parentNode);
                     var labelData = label.datum();
 
@@ -437,7 +448,7 @@
                     }
 
                     // stop further propagation
-                    d3.event.sourceEvent.stopPropagation();
+                    event.sourceEvent.stopPropagation();
                 });
         },
 
@@ -513,7 +524,7 @@
             };
 
             if ($.isArray(labelEntities)) {
-                $.each(labelMap.keys(), function (_, key) {
+                $.each(Array.from(labelMap.keys()), function (_, key) {
                     var currentLabelEntity = labelMap.get(key);
                     var isPresent = $.grep(labelEntities, function (proposedLabelEntity) {
                         return proposedLabelEntity.id === currentLabelEntity.id;
@@ -521,7 +532,7 @@
 
                     // if the current label is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        labelMap.remove(key);
+                        labelMap['delete'](key);
                     }
                 });
                 $.each(labelEntities, function (_, labelEntity) {
@@ -553,7 +564,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return labelMap.values();
+                return Array.from(labelMap.values());
             } else {
                 return labelMap.get(id);
             }
@@ -612,11 +623,11 @@
             if ($.isArray(labelIds)) {
                 $.each(labelIds, function (_, labelId) {
                     removedCache.set(labelId, now);
-                    labelMap.remove(labelId);
+                    labelMap['delete'](labelId);
                 });
             } else {
                 removedCache.set(labelIds, now);
-                labelMap.remove(labelIds);
+                labelMap['delete'](labelIds);
             }
 
             // apply the selection and handle all removed labels
@@ -627,7 +638,7 @@
          * Removes all label.
          */
         removeAll: function () {
-            nfLabel.remove(labelMap.keys());
+            nfLabel.remove(Array.from(labelMap.keys()));
         },
 
         /**
@@ -637,9 +648,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
index 1f59f0510e..b6aa449d87 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
@@ -24,9 +24,10 @@
                 'nf.Connection',
                 'nf.Common',
                 'nf.Client',
-                'nf.CanvasUtils'],
-            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils) {
-                return (nf.Port = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
+                return (nf.Port = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Port =
@@ -35,16 +36,18 @@
                 require('nf.Connection'),
                 require('nf.Common'),
                 require('nf.Client'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Port = factory(root.$,
             root.d3,
             root.nf.Connection,
             root.nf.Common,
             root.nf.Client,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils) {
+}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -96,7 +99,7 @@
      * Selects the port elements against the current port map.
      */
     var select = function () {
-        return portContainer.selectAll('g.input-port, g.output-port').data(portMap.values(), function (d) {
+        return portContainer.selectAll('g.input-port, g.output-port').data(Array.from(portMap.values()), function (d) {
             return d.id;
         });
     };
@@ -129,8 +132,9 @@
             return entered;
         }
 
-        var port = entered.append('g')
-            .attrs({
+        var port = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -146,8 +150,9 @@
             .call(nfCanvasUtils.position);
 
         // port border
-        port.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            port.append('rect'),
+            {
                 'class': 'border',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -160,8 +165,9 @@
             });
 
         // port body
-        port.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            port.append('rect'),
+            {
                 'class': 'body',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -174,8 +180,9 @@
             });
 
         // port remote banner
-        port.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            port.append('rect'),
+            {
                 'class': 'remote-banner',
                 'width': remotePortDimensions.width,
                 'height': OFFSET_VALUE,
@@ -184,8 +191,9 @@
             .classed('hidden', isLocalPort);
 
         // port icon
-        port.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            port.append('text'),
+            {
                 'class': 'port-icon',
                 'x': 10,
                 'y': offsetY(38)
@@ -199,8 +207,9 @@
             });
 
         // port name
-        port.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            port.append('text'),
+            {
                 'x': 70,
                 'y': offsetY(25),
                 'width': 95,
@@ -230,25 +239,27 @@
         }
 
         // port border authorization
-        updated.select('rect.border')
-            .classed('unauthorized', function (d) {
-                return d.permissions.canRead === false;
-            })
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('rect.border'),
+            {
                 'height': function(d) {
                     return d.dimensions.height;
                 }
+            })
+            .classed('unauthorized', function (d) {
+                return d.permissions.canRead === false;
             });
 
         // port body authorization
-        updated.select('rect.body')
-            .classed('unauthorized', function (d) {
-                return d.permissions.canRead === false;
-            })
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('rect.body'),
+            {
                 'height': function(d) {
                     return d.dimensions.height;
                 }
+            })
+            .classed('unauthorized', function (d) {
+                return d.permissions.canRead === false;
             });
 
         updated.each(function (portData) {
@@ -265,8 +276,9 @@
                     details = port.append('g').attr('class', 'port-details');
 
                     // port transmitting icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'port-transmission-icon',
                             'x': 10,
                             'y': 18
@@ -274,8 +286,9 @@
                         .classed('hidden', isLocalPort);
 
                     // bulletin background
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'class': 'bulletin-background',
                             'x': remotePortDimensions.width - OFFSET_VALUE,
                             'width': OFFSET_VALUE,
@@ -284,8 +297,9 @@
                         .classed('hidden', isLocalPort);
 
                     // bulletin icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'bulletin-icon',
                             'x': remotePortDimensions.width - 18,
                             'y': 18
@@ -294,8 +308,9 @@
                         .classed('hidden', isLocalPort);
 
                     // run status icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'run-status-icon',
                             'x': 50,
                             'y': offsetY(25)
@@ -305,8 +320,9 @@
                     // comments
                     // --------
 
-                    details.append('path')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('path'),
+                        {
                             'class': 'component-comments',
                             'transform': 'translate(' + (portData.dimensions.width - 2) + ', ' + (portData.dimensions.height - 10) + ')',
                             'd': 'm0,0 l0,8 l-8,0 z'
@@ -317,16 +333,18 @@
                     // -------------------
 
                     // active thread count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count-icon',
                             'y': offsetY(43)
                         })
                         .text('\ue83f');
 
                     // active thread icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count',
                             'y': offsetY(43)
                         });
@@ -338,8 +356,9 @@
                     port.select('rect.remote-banner')
                         .classed('hidden', isLocalPort);
 
-                    port.select('text.port-icon')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        port.select('text.port-icon'),
+                        {
                             'y': offsetY(38)
                         });
 
@@ -353,7 +372,8 @@
                         .classed('hidden', isLocalPort);
 
                     // update the port name
-                    port.select('text.port-name')
+                    d3Helpers.multiAttr(
+                        port.select('text.port-name')
                         .each(function (d) {
                             var portName = d3.select(this);
                             var name = d.component.name;
@@ -369,7 +389,7 @@
                             } else {
                                 nfCanvasUtils.multilineEllipsis(portName, 2, name, 'port-name');
                             }
-                        }).attrs({
+                        }), {
                             'y': offsetY(25)
                         }).append('title').text(function (d) {
                             return d.component.name;
@@ -464,8 +484,9 @@
         }
 
         // update the run status
-        updated.select('text.run-status-icon')
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('text.run-status-icon'),
+            {
                 'fill': function (d) {
                     var fill = '#728e9b';
 
@@ -536,8 +557,9 @@
                 }
             });
 
-        updated.select('text.port-transmission-icon')
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('text.port-transmission-icon'),
+            {
                 'font-family': function (d) {
                     if (d.status.transmitting === true) {
                         return 'FontAwesome';
@@ -641,13 +663,14 @@
             nfContextMenu = nfContextMenuRef;
             nfQuickSelect = nfQuickSelectRef;
 
-            portMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            portMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the port container
-            portContainer = d3.select('#canvas').append('g')
-                .attrs({
+            portContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'ports'
                 });
@@ -734,7 +757,7 @@
 
             // determine how to handle the specified port status
             if ($.isArray(portEntities)) {
-                $.each(portMap.keys(), function (_, key) {
+                $.each(Array.from(portMap.keys()), function (_, key) {
                     var currentPortEntity = portMap.get(key);
                     var isPresent = $.grep(portEntities, function (proposedPortEntity) {
                         return proposedPortEntity.id === currentPortEntity.id;
@@ -742,7 +765,7 @@
 
                     // if the current port is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        portMap.remove(key);
+                        portMap['delete'](key);
                     }
                 });
                 $.each(portEntities, function (_, portNode) {
@@ -774,7 +797,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return portMap.values();
+                return Array.from(portMap.values());
             } else {
                 return portMap.get(id);
             }
@@ -840,11 +863,11 @@
             if ($.isArray(portIds)) {
                 $.each(portIds, function (_, portId) {
                     removedCache.set(portId, now);
-                    portMap.remove(portId);
+                    portMap['delete'](portId);
                 });
             } else {
                 removedCache.set(portIds, now);
-                portMap.remove(portIds);
+                portMap['delete'](portIds);
             }
 
             // apply the selection and handle all removed ports
@@ -855,7 +878,7 @@
          * Removes all ports..
          */
         removeAll: function () {
-            nfPort.remove(portMap.keys());
+            nfPort.remove(Array.from(portMap.keys()));
         },
 
         /**
@@ -865,9 +888,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
index 463433a8b6..a080537fb9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
@@ -25,9 +25,10 @@
                 'nf.Common',
                 'nf.Client',
                 'nf.CanvasUtils',
-                'nf.Dialog'],
-            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
-                return (nf.ProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog));
+                'nf.Dialog',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog, d3Helpers) {
+                return (nf.ProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.ProcessGroup =
@@ -37,7 +38,8 @@
                 require('nf.Common'),
                 require('nf.Client'),
                 require('nf.CanvasUtils'),
-                require('nf.Dialog')));
+                require('nf.Dialog'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.ProcessGroup = factory(root.$,
             root.d3,
@@ -45,9 +47,10 @@
             root.nf.Common,
             root.nf.Client,
             root.nf.CanvasUtils,
-            root.nf.Dialog);
+            root.nf.Dialog,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog) {
+}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, nfDialog, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -98,7 +101,7 @@
      * Selects the process group elements against the current process group map.
      */
     var select = function () {
-        return processGroupContainer.selectAll('g.process-group').data(processGroupMap.values(), function (d) {
+        return processGroupContainer.selectAll('g.process-group').data(Array.from(processGroupMap.values()), function (d) {
             return d.id;
         });
     };
@@ -115,8 +118,9 @@
             return entered;
         }
 
-        var processGroup = entered.append('g')
-            .attrs({
+        var processGroup = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -130,8 +134,9 @@
         // ----
 
         // process group border
-        processGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processGroup.append('rect'),
+            {
                 'class': 'border',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -144,8 +149,9 @@
             });
 
         // process group body
-        processGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processGroup.append('rect'),
+            {
                 'class': 'body',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -158,8 +164,9 @@
             });
 
         // process group name background
-        processGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processGroup.append('rect'),
+            {
                 'width': function (d) {
                     return d.dimensions.width;
                 },
@@ -168,8 +175,9 @@
             });
 
         // process group name
-        processGroup.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processGroup.append('text'),
+            {
                 'x': 10,
                 'y': 20,
                 'width': 300,
@@ -178,15 +186,16 @@
             });
 
         // process group name
-        processGroup.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processGroup.append('text'),
+            {
                 'x': 10,
                 'y': 21,
                 'class': 'version-control'
             });
 
         // always support selecting and navigation
-        processGroup.on('dblclick', function (d) {
+        processGroup.on('dblclick', function (event, d) {
             // enter this group on double click
             nfProcessGroup.enterGroup(d.id);
         })
@@ -275,19 +284,21 @@
                     // contents background
                     // -------------------
 
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'x': 0,
                             'y': 32,
                             'width': function () {
-                                return processGroupData.dimensions.width
+                                return processGroupData.dimensions.width;
                             },
                             'height': 24,
                             'fill': '#e3e8eb'
                         });
 
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'x': 0,
                             'y': function () {
                                 return processGroupData.dimensions.height - 24;
@@ -304,118 +315,130 @@
                     // --------
 
                     // transmitting icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'x': 10,
                             'y': 49,
                             'class': 'process-group-transmitting process-group-contents-icon',
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf140')
-                        .append("title")
-                        .text("Transmitting Remote Process Groups");
-
+                        .append('title')
+                        .text('Transmitting Remote Process Groups');
 
                     // transmitting count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-transmitting-count process-group-contents-count'
                         });
 
                     // not transmitting icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-not-transmitting process-group-contents-icon',
                             'font-family': 'flowfont'
                         })
                         .text('\ue80a')
-                        .append("title")
-                        .text("Not Transmitting Remote Process Groups");
+                        .append('title')
+                        .text('Not Transmitting Remote Process Groups');
 
                     // not transmitting count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-not-transmitting-count process-group-contents-count'
                         });
 
                     // running icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-running process-group-contents-icon',
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf04b')
-                        .append("title")
-                        .text("Running Components");
+                        .append('title')
+                        .text('Running Components');
 
                     // running count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-running-count process-group-contents-count'
                         });
 
                     // stopped icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-stopped process-group-contents-icon',
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf04d')
-                        .append("title")
-                        .text("Stopped Components");
+                        .append('title')
+                        .text('Stopped Components');
 
                     // stopped count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-stopped-count process-group-contents-count'
                         });
 
                     // invalid icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-invalid process-group-contents-icon',
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf071')
-                        .append("title")
-                        .text("Invalid Components");
+                        .append('title')
+                        .text('Invalid Components');
 
                     // invalid count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-invalid-count process-group-contents-count'
                         });
 
                     // disabled icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-disabled process-group-contents-icon',
                             'font-family': 'flowfont'
                         })
                         .text('\ue802')
-                        .append("title")
-                        .text("Disabled Components");
+                        .append('title')
+                        .text('Disabled Components');
 
                     // disabled count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': 49,
                             'class': 'process-group-disabled-count process-group-contents-count'
                         });
 
                     // up to date icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'x': 10,
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
@@ -424,12 +447,13 @@
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf00c')
-                        .append("title")
-                        .text("Up to date Versioned Process Groups");
+                        .append('title')
+                        .text('Up to date Versioned Process Groups');
 
                     // up to date count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -437,8 +461,9 @@
                         });
 
                     // locally modified icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -446,12 +471,13 @@
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf069')
-                        .append("title")
-                        .text("Locally modified Versioned Process Groups");
+                        .append('title')
+                        .text('Locally modified Versioned Process Groups');
 
                     // locally modified count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -459,8 +485,9 @@
                         });
 
                     // stale icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -468,12 +495,13 @@
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf0aa')
-                        .append("title")
-                        .text("Stale Versioned Process Groups");
+                        .append('title')
+                        .text('Stale Versioned Process Groups');
 
                     // stale count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -481,8 +509,9 @@
                         });
 
                     // locally modified and stale icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -490,12 +519,13 @@
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf06a')
-                        .append("title")
-                        .text("Locally modified and stale Versioned Process Groups");
+                        .append('title')
+                        .text('Locally modified and stale Versioned Process Groups');
 
                     // locally modified and stale count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -503,8 +533,9 @@
                         });
 
                     // sync failure icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -512,12 +543,13 @@
                             'font-family': 'FontAwesome'
                         })
                         .text('\uf128')
-                        .append("title")
-                        .text("Sync failure Versioned Process Groups");
+                        .append('title')
+                        .text('Sync failure Versioned Process Groups');
 
                     // sync failure count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'y': function () {
                                 return processGroupData.dimensions.height - 7;
                             },
@@ -529,8 +561,9 @@
                     // ----------------
 
                     // queued
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -541,8 +574,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -553,8 +587,9 @@
                         });
 
                     // in
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -565,8 +600,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -577,8 +613,9 @@
                         });
 
                     // read/write
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -589,8 +626,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -601,8 +639,9 @@
                         });
 
                     // out
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processGroupData.dimensions.width;
                             },
@@ -617,14 +656,16 @@
                     // -----
 
                     // stats label container
-                    var processGroupStatsLabel = details.append('g')
-                        .attrs({
+                    var processGroupStatsLabel = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(6, 75)'
                         });
 
                     // queued label
-                    processGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -634,8 +675,9 @@
                         .text('Queued');
 
                     // in label
-                    processGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -645,8 +687,9 @@
                         .text('In');
 
                     // read/write label
-                    processGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -656,8 +699,9 @@
                         .text('Read/Write');
 
                     // out label
-                    processGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -667,14 +711,16 @@
                         .text('Out');
 
                     // stats value container
-                    var processGroupStatsValue = details.append('g')
-                        .attrs({
+                    var processGroupStatsValue = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(95, 75)'
                         });
 
                     // queued value
-                    var queuedText = processGroupStatsValue.append('text')
-                        .attrs({
+                    var queuedText = d3Helpers.multiAttr(
+                        processGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -683,20 +729,23 @@
                         });
 
                     // queued count
-                    queuedText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        queuedText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // queued size
-                    queuedText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        queuedText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // in value
-                    var inText = processGroupStatsValue.append('text')
-                        .attrs({
+                    var inText = d3Helpers.multiAttr(
+                        processGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -705,32 +754,37 @@
                         });
 
                     // in count
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // in size
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // in
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'ports'
                         });
 
                     // in (remote)
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'public-ports'
                         });
 
                     // read/write value
-                    processGroupStatsValue.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -739,8 +793,9 @@
                         });
 
                     // out value
-                    var outText = processGroupStatsValue.append('text')
-                        .attrs({
+                    var outText = d3Helpers.multiAttr(
+                        processGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -749,38 +804,44 @@
                         });
 
                     // out ports
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'ports'
                         });
 
                     // out ports (remote)
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'public-ports'
                         });
 
                     // out count
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // out size
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // stats value container
-                    var processGroupStatsInfo = details.append('g')
-                        .attrs({
+                    var processGroupStatsInfo = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(335, 75)'
                         });
 
                     // in info
-                    processGroupStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'x': 4,
@@ -790,8 +851,9 @@
                         .text('5 min');
 
                     // read/write info
-                    processGroupStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'x': 4,
@@ -801,8 +863,9 @@
                         .text('5 min');
 
                     // out info
-                    processGroupStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'x': 4,
@@ -815,8 +878,9 @@
                     // comments
                     // --------
 
-                    details.append('path')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('path'),
+                        {
                             'class': 'component-comments',
                             'transform': 'translate(' + (processGroupData.dimensions.width - 2) + ', ' + (processGroupData.dimensions.height - 10) + ')',
                             'd': 'm0,0 l0,8 l-8,0 z'
@@ -827,16 +891,18 @@
                     // -------------------
 
                     // active thread count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count-icon',
                             'y': 20
                         })
                         .text('\ue83f');
 
                     // active thread icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count',
                             'y': 20
                         });
@@ -846,8 +912,9 @@
                     // ---------
 
                     // bulletin background
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'class': 'bulletin-background',
                             'x': function () {
                                 return processGroupData.dimensions.width - 24;
@@ -858,8 +925,9 @@
                         });
 
                     // bulletin icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'bulletin-icon',
                             'x': function () {
                                 return processGroupData.dimensions.width - 17;
@@ -885,7 +953,7 @@
                     .text(function (d) {
                         return d.activeRemotePortCount;
                     });
-                transmittingCount.append("title").text("Transmitting Remote Process Groups");
+                transmittingCount.append('title').text('Transmitting Remote Process Groups');
 
                 // update not transmitting
                 var notTransmitting = details.select('text.process-group-not-transmitting')
@@ -907,7 +975,7 @@
                     .text(function (d) {
                         return d.inactiveRemotePortCount;
                     });
-                notTransmittingCount.append("title").text("Not transmitting Remote Process Groups")
+                notTransmittingCount.append('title').text('Not transmitting Remote Process Groups');
 
                 // update running
                 var running = details.select('text.process-group-running')
@@ -929,7 +997,7 @@
                     .text(function (d) {
                         return d.runningCount;
                     });
-                runningCount.append("title").text("Running Components");
+                runningCount.append('title').text('Running Components');
 
                 // update stopped
                 var stopped = details.select('text.process-group-stopped')
@@ -951,7 +1019,7 @@
                     .text(function (d) {
                         return d.stoppedCount;
                     });
-                stoppedCount.append("title").text("Stopped Components");
+                stoppedCount.append('title').text('Stopped Components');
 
                 // update invalid
                 var invalid = details.select('text.process-group-invalid')
@@ -973,7 +1041,7 @@
                     .text(function (d) {
                         return d.invalidCount;
                     });
-                invalidCount.append("title").text("Invalid Components");
+                invalidCount.append('title').text('Invalid Components');
 
                 // update disabled
                 var disabled = details.select('text.process-group-disabled')
@@ -995,7 +1063,7 @@
                     .text(function (d) {
                         return d.disabledCount;
                     });
-                disabledCount.append("title").text("Disabled Components");
+                disabledCount.append('title').text('Disabled Components');
 
                 // up to date current
                 var upToDate = details.select('text.process-group-up-to-date')
@@ -1013,7 +1081,7 @@
                     .text(function (d) {
                         return d.upToDateCount;
                     });
-                upToDateCount.append("title").text("Up to date Versioned Process Groups");
+                upToDateCount.append('title').text('Up to date Versioned Process Groups');
 
                 // update locally modified
                 var locallyModified = details.select('text.process-group-locally-modified')
@@ -1035,7 +1103,7 @@
                     .text(function (d) {
                         return d.locallyModifiedCount;
                     });
-                locallyModifiedCount.append("title").text("Locally modified Versioned Process Groups");
+                locallyModifiedCount.append('title').text('Locally modified Versioned Process Groups');
 
                 // update stale
                 var stale = details.select('text.process-group-stale')
@@ -1057,7 +1125,7 @@
                     .text(function (d) {
                         return d.staleCount;
                     });
-                staleCount.append("title").text("Stale Versioned Process Groups");
+                staleCount.append('title').text('Stale Versioned Process Groups');
 
                 // update locally modified and stale
                 var locallyModifiedAndStale = details.select('text.process-group-locally-modified-and-stale')
@@ -1079,7 +1147,7 @@
                     .text(function (d) {
                         return d.locallyModifiedAndStaleCount;
                     });
-                locallyModifiedAndStaleCount.append("title").text("Locally modified and stale Versioned Process Groups");
+                locallyModifiedAndStaleCount.append('title').text('Locally modified and stale Versioned Process Groups');
 
                 // update sync failure
                 var syncFailure = details.select('text.process-group-sync-failure')
@@ -1101,36 +1169,34 @@
                     .text(function (d) {
                         return d.syncFailureCount;
                     });
-                syncFailureCount.append("title").text("Sync failure Versioned Process Groups");
+                syncFailureCount.append('title').text('Sync failure Versioned Process Groups');
 
                 // update version control information
                 var versionControl = processGroup.select('text.version-control')
-                    .styles({
-                        'visibility': isUnderVersionControl(processGroupData) ? 'visible' : 'hidden',
-                        'fill': function () {
-                            if (isUnderVersionControl(processGroupData)) {
-                                var vciState = processGroupData.versionedFlowState;
-                                if (vciState === 'SYNC_FAILURE') {
-                                    return '#666666';
-                                } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
-                                    return '#BA554A';
-                                } else if (vciState === 'STALE') {
-                                    return '#BA554A';
-                                } else if (vciState === 'LOCALLY_MODIFIED') {
-                                    return '#666666';
-                                } else {
-                                    return '#1A9964';
-                                }
+                    .style('visibility', isUnderVersionControl(processGroupData) ? 'visible' : 'hidden')
+                    .style('fill', function () {
+                        if (isUnderVersionControl(processGroupData)) {
+                            var vciState = processGroupData.versionedFlowState;
+                            if (vciState === 'SYNC_FAILURE') {
+                                return '#666666';
+                            } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
+                                return '#BA554A';
+                            } else if (vciState === 'STALE') {
+                                return '#BA554A';
+                            } else if (vciState === 'LOCALLY_MODIFIED') {
+                                return '#666666';
                             } else {
-                                return '#000';
+                                return '#1A9964';
                             }
+                        } else {
+                            return '#000';
                         }
                     })
                     .text(function () {
                         if (isUnderVersionControl(processGroupData)) {
                             var vciState = processGroupData.versionedFlowState;
                             if (vciState === 'SYNC_FAILURE') {
-                                return '\uf128'
+                                return '\uf128';
                             } else if (vciState === 'LOCALLY_MODIFIED_AND_STALE') {
                                 return '\uf06a';
                             } else if (vciState === 'STALE') {
@@ -1148,37 +1214,37 @@
                 if (processGroupData.permissions.canRead) {
                     // version control tooltip
                     versionControl.each(function () {
-                            // get the tip
-                            var tip = d3.select('#version-control-tip-' + processGroupData.id);
+                        // get the tip
+                        var tip = d3.select('#version-control-tip-' + processGroupData.id);
 
-                            // if there are validation errors generate a tooltip
-                            if (isUnderVersionControl(processGroupData)) {
-                                // create the tip if necessary
-                                if (tip.empty()) {
-                                    tip = d3.select('#process-group-tooltips').append('div')
-                                        .attr('id', function () {
-                                            return 'version-control-tip-' + processGroupData.id;
-                                        })
-                                        .attr('class', 'tooltip nifi-tooltip');
-                                }
+                        // if there are validation errors generate a tooltip
+                        if (isUnderVersionControl(processGroupData)) {
+                            // create the tip if necessary
+                            if (tip.empty()) {
+                                tip = d3.select('#process-group-tooltips').append('div')
+                                    .attr('id', function () {
+                                        return 'version-control-tip-' + processGroupData.id;
+                                    })
+                                    .attr('class', 'tooltip nifi-tooltip');
+                            }
 
-                                // update the tip
-                                tip.html(function () {
-                                    var vci = processGroupData.component.versionControlInformation;
-                                    var versionControlTip = $('<div></div>').text('Tracking to "' + vci.flowName + '" Version ' + vci.version + ' in "' + vci.registryName + ' - ' + vci.bucketName + '"');
-                                    var versionControlStateTip = $('<div></div>').text(nfCommon.getVersionControlTooltip(vci));
-                                    return $('<div></div>').append(versionControlTip).append('<br/>').append(versionControlStateTip).html();
-                                });
+                            // update the tip
+                            tip.html(function () {
+                                var vci = processGroupData.component.versionControlInformation;
+                                var versionControlTip = $('<div></div>').text('Tracking to "' + vci.flowName + '" Version ' + vci.version + ' in "' + vci.registryName + ' - ' + vci.bucketName + '"');
+                                var versionControlStateTip = $('<div></div>').text(nfCommon.getVersionControlTooltip(vci));
+                                return $('<div></div>').append(versionControlTip).append('<br/>').append(versionControlStateTip).html();
+                            });
 
-                                // add the tooltip
-                                nfCanvasUtils.canvasTooltip(tip, d3.select(this));
-                            } else {
-                                // remove the tip if necessary
-                                if (!tip.empty()) {
-                                    tip.remove();
-                                }
+                            // add the tooltip
+                            nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                        } else {
+                            // remove the tip if necessary
+                            if (!tip.empty()) {
+                                tip.remove();
                             }
-                        });
+                        }
+                    });
 
                     // update the process group comments
                     processGroup.select('path.component-comments')
@@ -1212,8 +1278,9 @@
                         });
 
                     // update the process group name
-                    processGroup.select('text.process-group-name')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroup.select('text.process-group-name'),
+                        {
                             'x': function () {
                                 if (isUnderVersionControl(processGroupData)) {
                                     var versionControlX = parseInt(versionControl.attr('x'), 10);
@@ -1250,8 +1317,9 @@
                     processGroup.select('path.component-comments').style('visibility', 'hidden');
 
                     // clear the process group name
-                    processGroup.select('text.process-group-name')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroup.select('text.process-group-name'),
+                        {
                             'x': 10,
                             'width': 316
                         })
@@ -1358,7 +1426,7 @@
         // out count value
         updated.select('text.process-group-out tspan.count')
             .text(function (d) {
-                return  ' ' + String.fromCharCode(8594) + ' ' + nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
+                return ' ' + String.fromCharCode(8594) + ' ' + nfCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
             });
 
         // out size value
@@ -1435,13 +1503,14 @@
             nfSelectable = nfSelectableRef;
             nfContextMenu = nfContextMenuRef;
 
-            processGroupMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            processGroupMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the process group container
-            processGroupContainer = d3.select('#canvas').append('g')
-                .attrs({
+            processGroupContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'process-groups'
                 });
@@ -1521,7 +1590,7 @@
 
             // determine how to handle the specified process groups
             if ($.isArray(processGroupEntities)) {
-                $.each(processGroupMap.keys(), function (_, key) {
+                $.each(Array.from(processGroupMap.keys()), function (_, key) {
                     var currentProcessGroupEntity = processGroupMap.get(key);
                     var isPresent = $.grep(processGroupEntities, function (proposedProcessGroupEntity) {
                         return proposedProcessGroupEntity.id === currentProcessGroupEntity.id;
@@ -1529,7 +1598,7 @@
 
                     // if the current process group is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        processGroupMap.remove(key);
+                        processGroupMap['delete'](key);
                     }
                 });
                 $.each(processGroupEntities, function (_, processGroupEntity) {
@@ -1561,7 +1630,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return processGroupMap.values();
+                return Array.from(processGroupMap.values());
             } else {
                 return processGroupMap.get(id);
             }
@@ -1627,11 +1696,11 @@
             if ($.isArray(processGroupIds)) {
                 $.each(processGroupIds, function (_, processGroupId) {
                     removedCache.set(processGroupId, now);
-                    processGroupMap.remove(processGroupId);
+                    processGroupMap['delete'](processGroupId);
                 });
             } else {
                 removedCache.set(processGroupIds, now);
-                processGroupMap.remove(processGroupIds);
+                processGroupMap['delete'](processGroupIds);
             }
 
             // apply the selection and handle all removed process groups
@@ -1642,7 +1711,7 @@
          * Removes all process groups.
          */
         removeAll: function () {
-            nfProcessGroup.remove(processGroupMap.keys());
+            nfProcessGroup.remove(Array.from(processGroupMap.keys()));
         },
 
         /**
@@ -1652,9 +1721,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
index 056769930f..a16183e4d6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
@@ -24,9 +24,10 @@
                 'nf.Common',
                 'nf.Client',
                 'nf.ClusterSummary',
-                'nf.CanvasUtils'],
-            function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils) {
-                return (nf.Processor = factory($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils, d3Helpers) {
+                return (nf.Processor = factory($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Processor =
@@ -35,16 +36,18 @@
                 require('nf.Common'),
                 require('nf.Client'),
                 require('nf.ClusterSummary'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.Processor = factory(root.$,
             root.d3,
             root.nf.Common,
             root.nf.Client,
             root.nf.ClusterSummary,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils) {
+}(this, function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -88,7 +91,7 @@
      * Selects the processor elements against the current processor map.
      */
     var select = function () {
-        return processorContainer.selectAll('g.processor').data(processorMap.values(), function (d) {
+        return processorContainer.selectAll('g.processor').data(Array.from(processorMap.values()), function (d) {
             return d.id;
         });
     };
@@ -105,8 +108,9 @@
             return entered;
         }
 
-        var processor = entered.append('g')
-            .attrs({
+        var processor = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -116,8 +120,9 @@
             .call(nfCanvasUtils.position);
 
         // processor border
-        processor.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('rect'),
+            {
                 'class': 'border',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -130,8 +135,9 @@
             });
 
         // processor body
-        processor.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('rect'),
+            {
                 'class': 'body',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -144,8 +150,9 @@
             });
 
         // processor name
-        processor.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('text'),
+            {
                 'x': 75,
                 'y': 18,
                 'width': 230,
@@ -154,8 +161,9 @@
             });
 
         // processor icon container
-        processor.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('rect'),
+            {
                 'x': 0,
                 'y': 0,
                 'width': 50,
@@ -164,8 +172,9 @@
             });
 
         // processor icon
-        processor.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('text'),
+            {
                 'x': 9,
                 'y': 35,
                 'class': 'processor-icon'
@@ -173,8 +182,9 @@
             .text('\ue807');
 
         // restricted icon background
-        processor.append('circle')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('circle'),
+            {
                 'r': 9,
                 'cx': 12,
                 'cy': 12,
@@ -182,8 +192,9 @@
             });
 
         // restricted icon
-        processor.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('text'),
+            {
                 'x': 7.75,
                 'y': 17,
                 'class': 'restricted'
@@ -191,8 +202,9 @@
             .text('\uf132');
 
         // is primary icon background
-        processor.append('circle')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('circle'),
+            {
                 'r': 9,
                 'cx': 38,
                 'cy': 36,
@@ -200,16 +212,17 @@
             });
 
         // is primary icon
-        processor.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            processor.append('text'),
+            {
                 'x': 34.75,
                 'y': 40,
                 'class': 'is-primary'
             })
             .text('P')
             .append('title').text(function (d) {
-                return 'This component is only scheduled to execute on the Primary Node';
-            });
+            return 'This component is only scheduled to execute on the Primary Node';
+        });
 
         // make processors selectable
         processor.call(nfSelectable.activate).call(nfContextMenu.activate).call(nfQuickSelect.activate);
@@ -255,8 +268,9 @@
                     details = processor.append('g').attr('class', 'processor-canvas-details');
 
                     // run status icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'run-status-icon',
                             'x': 55,
                             'y': 23,
@@ -265,8 +279,9 @@
                         });
 
                     // processor type
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'processor-type',
                             'x': 75,
                             'y': 32,
@@ -275,8 +290,9 @@
                         });
 
                     // processor type
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'processor-bundle',
                             'x': 75,
                             'y': 45,
@@ -291,8 +307,9 @@
                     // draw the processor statistics table
 
                     // in
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -303,8 +320,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -315,8 +333,9 @@
                         });
 
                     // read/write
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -327,8 +346,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -339,8 +359,9 @@
                         });
 
                     // out
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -351,8 +372,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -363,8 +385,9 @@
                         });
 
                     // tasks/time
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return processorData.dimensions.width;
                             },
@@ -375,14 +398,16 @@
                         });
 
                     // stats label container
-                    var processorStatsLabel = details.append('g')
-                        .attrs({
+                    var processorStatsLabel = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(10, 55)'
                         });
 
                     // in label
-                    processorStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'y': 9,
@@ -391,8 +416,9 @@
                         .text('In');
 
                     // read/write label
-                    processorStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'y': 27,
@@ -401,8 +427,9 @@
                         .text('Read/Write');
 
                     // out label
-                    processorStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'y': 46,
@@ -411,8 +438,9 @@
                         .text('Out');
 
                     // tasks/time label
-                    processorStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'y': 65,
@@ -421,14 +449,16 @@
                         .text('Tasks/Time');
 
                     // stats value container
-                    var processorStatsValue = details.append('g')
-                        .attrs({
+                    var processorStatsValue = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(85, 55)'
                         });
 
                     // in value
-                    var inText = processorStatsValue.append('text')
-                        .attrs({
+                    var inText = d3Helpers.multiAttr(
+                        processorStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 9,
                             'y': 9,
@@ -436,20 +466,23 @@
                         });
 
                     // in count
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // in size
-                    inText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        inText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // read/write value
-                    processorStatsValue.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'y': 27,
@@ -457,8 +490,9 @@
                         });
 
                     // out value
-                    var outText = processorStatsValue.append('text')
-                        .attrs({
+                    var outText = d3Helpers.multiAttr(
+                        processorStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'y': 46,
@@ -466,20 +500,23 @@
                         });
 
                     // out count
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // out size
-                    outText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        outText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // tasks/time value
-                    processorStatsValue.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'y': 65,
@@ -491,8 +528,9 @@
                         .attr('transform', 'translate(305, 55)');
 
                     // in info
-                    processorStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'y': 9,
@@ -501,8 +539,9 @@
                         .text('5 min');
 
                     // read/write info
-                    processorStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'y': 27,
@@ -511,8 +550,9 @@
                         .text('5 min');
 
                     // out info
-                    processorStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'y': 46,
@@ -521,8 +561,9 @@
                         .text('5 min');
 
                     // tasks/time info
-                    processorStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processorStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'y': 65,
@@ -534,8 +575,9 @@
                     // comments
                     // --------
 
-                    details.append('path')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('path'),
+                        {
                             'class': 'component-comments',
                             'transform': 'translate(' + (processorData.dimensions.width - 2) + ', ' + (processorData.dimensions.height - 10) + ')',
                             'd': 'm0,0 l0,8 l-8,0 z'
@@ -546,16 +588,18 @@
                     // -------------------
 
                     // active thread count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count-icon',
                             'y': 46
                         })
                         .text('\ue83f');
 
                     // active thread background
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count',
                             'y': 46
                         });
@@ -565,8 +609,9 @@
                     // ---------
 
                     // bulletin background
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'class': 'bulletin-background',
                             'x': function (d) {
                                 return processorData.dimensions.width - 24;
@@ -576,8 +621,9 @@
                         });
 
                     // bulletin icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'bulletin-icon',
                             'x': function (d) {
                                 return processorData.dimensions.width - 17;
@@ -599,8 +645,8 @@
                             // apply ellipsis to the processor name as necessary
                             nfCanvasUtils.ellipsis(processorName, d.component.name, 'processor-name');
                         }).append('title').text(function (d) {
-                            return d.component.name;
-                        });
+                        return d.component.name;
+                    });
 
                     // update the processor type
                     processor.select('text.processor-type')
@@ -613,8 +659,8 @@
                             // apply ellipsis to the processor type as necessary
                             nfCanvasUtils.ellipsis(processorType, nfCommon.formatType(d.component), 'processor-type');
                         }).append('title').text(function (d) {
-                            return nfCommon.formatType(d.component);
-                        });
+                        return nfCommon.formatType(d.component);
+                    });
 
                     // update the processor bundle
                     processor.select('text.processor-bundle')
@@ -627,8 +673,8 @@
                             // apply ellipsis to the processor type as necessary
                             nfCanvasUtils.ellipsis(processorBundle, nfCommon.formatBundle(d.component.bundle), 'processor-bundle');
                         }).append('title').text(function (d) {
-                            return nfCommon.formatBundle(d.component.bundle);
-                        });
+                        return nfCommon.formatBundle(d.component.bundle);
+                    });
 
                     // update the processor comments
                     processor.select('path.component-comments')
@@ -837,8 +883,9 @@
         }
 
         // update the run status
-        updated.select('text.run-status-icon')
-            .attrs({
+        d3Helpers.multiAttr(
+            updated.select('text.run-status-icon'),
+            {
                 'fill': function (d) {
                     var fill = '#728e9b';
 
@@ -1013,13 +1060,14 @@
             nfContextMenu = nfContextMenuRef;
             nfQuickSelect = nfQuickSelectRef;
 
-            processorMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            processorMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the processor container
-            processorContainer = d3.select('#canvas').append('g')
-                .attrs({
+            processorContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'processors'
                 });
@@ -1099,7 +1147,7 @@
 
             // determine how to handle the specified processor
             if ($.isArray(processorEntities)) {
-                $.each(processorMap.keys(), function (_, key) {
+                $.each(Array.from(processorMap.keys()), function (_, key) {
                     var currentProcessorEntity = processorMap.get(key);
                     var isPresent = $.grep(processorEntities, function (proposedProcessorEntity) {
                         return proposedProcessorEntity.id === currentProcessorEntity.id;
@@ -1107,7 +1155,7 @@
 
                     // if the current processor is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        processorMap.remove(key);
+                        processorMap['delete'](key);
                     }
                 });
                 $.each(processorEntities, function (_, processorEntity) {
@@ -1140,7 +1188,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return processorMap.values();
+                return Array.from(processorMap.values());
             } else {
                 return processorMap.get(id);
             }
@@ -1206,11 +1254,11 @@
             if ($.isArray(processorIds)) {
                 $.each(processorIds, function (_, processorId) {
                     removedCache.set(processorId, now);
-                    processorMap.remove(processorId);
+                    processorMap['delete'](processorId);
                 });
             } else {
                 removedCache.set(processorIds, now);
-                processorMap.remove(processorIds);
+                processorMap['delete'](processorIds);
             }
 
             // apply the selection and handle all removed processors
@@ -1221,7 +1269,7 @@
          * Removes all processors.
          */
         removeAll: function () {
-            nfProcessor.remove(processorMap.keys());
+            nfProcessor.remove(Array.from(processorMap.keys()));
         },
 
         /**
@@ -1231,9 +1279,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-quick-select.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-quick-select.js
index 93e9b1e45e..775808393a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-quick-select.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-quick-select.js
@@ -51,7 +51,7 @@
         /**
          * Attempts to show configuration or details dialog for the specified slection.
          */
-        quickSelect: function () {
+        quickSelect: function (event) {
             var selection = nfCanvasUtils.getSelection();
 
             if (nfCanvasUtils.isConfigurable(selection)) {
@@ -61,8 +61,8 @@
             }
 
             // stop propagation and prevent default
-            d3.event.preventDefault();
-            d3.event.stopPropagation();
+            event.preventDefault();
+            event.stopPropagation();
         },
 
         /**
@@ -71,9 +71,9 @@
          * @param {selection} components
          */
         activate: function (components) {
-            components.on('dblclick', function () {
+            components.on('dblclick', function (event) {
                 // get the clicked component to update selection
-                nfQuickSelect.quickSelect();
+                nfQuickSelect.quickSelect(event);
             });
         }
     };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
index 61d2d4cbee..e63b28cbd2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
@@ -24,9 +24,10 @@
                 'nf.Connection',
                 'nf.Common',
                 'nf.Client',
-                'nf.CanvasUtils'],
-            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils) {
-                return (nf.RemoteProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'nf.ng.D3Helpers'],
+            function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
+                return (nf.RemoteProcessGroup = factory($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.RemoteProcessGroup =
@@ -35,16 +36,18 @@
                 require('nf.Connection'),
                 require('nf.Common'),
                 require('nf.Client'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('nf.ng.D3Helpers')));
     } else {
         nf.RemoteProcessGroup = factory(root.$,
             root.d3,
             root.nf.Connection,
             root.nf.Common,
             root.nf.Client,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root.nf.ng.D3Helpers);
     }
-}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils) {
+}(this, function ($, d3, nfConnection, nfCommon, nfClient, nfCanvasUtils, d3Helpers) {
     'use strict';
 
     var nfConnectable;
@@ -87,7 +90,7 @@
      * Selects the remote process group elements against the current remote process group map.
      */
     var select = function () {
-        return remoteProcessGroupContainer.selectAll('g.remote-process-group').data(remoteProcessGroupMap.values(), function (d) {
+        return remoteProcessGroupContainer.selectAll('g.remote-process-group').data(Array.from(remoteProcessGroupMap.values()), function (d) {
             return d.id;
         });
     };
@@ -104,8 +107,9 @@
             return entered;
         }
 
-        var remoteProcessGroup = entered.append('g')
-            .attrs({
+        var remoteProcessGroup = d3Helpers.multiAttr(
+            entered.append('g'),
+            {
                 'id': function (d) {
                     return 'id-' + d.id;
                 },
@@ -119,8 +123,9 @@
         // ----
 
         // remote process group border
-        remoteProcessGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            remoteProcessGroup.append('rect'),
+            {
                 'class': 'border',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -133,8 +138,9 @@
             });
 
         // remote process group body
-        remoteProcessGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            remoteProcessGroup.append('rect'),
+            {
                 'class': 'body',
                 'width': function (d) {
                     return d.dimensions.width;
@@ -147,8 +153,9 @@
             });
 
         // remote process group name background
-        remoteProcessGroup.append('rect')
-            .attrs({
+        d3Helpers.multiAttr(
+            remoteProcessGroup.append('rect'),
+            {
                 'width': function (d) {
                     return d.dimensions.width;
                 },
@@ -157,8 +164,9 @@
             });
 
         // remote process group name
-        remoteProcessGroup.append('text')
-            .attrs({
+        d3Helpers.multiAttr(
+            remoteProcessGroup.append('text'),
+            {
                 'x': 30,
                 'y': 20,
                 'width': 305,
@@ -207,8 +215,9 @@
                     details = remoteProcessGroup.append('g').attr('class', 'remote-process-group-details');
 
                     // remote process group transmission status
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'remote-process-group-transmission-status',
                             'x': 10,
                             'y': 20
@@ -218,8 +227,9 @@
                     // details background
                     // ------------------
 
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'x': 0,
                             'y': 32,
                             'width': function () {
@@ -234,16 +244,18 @@
                     // -------
 
                     // remote process group secure transfer
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'remote-process-group-transmission-secure',
                             'x': 10,
                             'y': 48
                         });
 
                     // remote process group uri
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'x': 30,
                             'y': 48,
                             'width': 305,
@@ -256,8 +268,9 @@
                     // ----------------
 
                     // sent
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return remoteProcessGroupData.dimensions.width;
                             },
@@ -268,8 +281,9 @@
                         });
 
                     // border
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return remoteProcessGroupData.dimensions.width;
                             },
@@ -280,8 +294,9 @@
                         });
 
                     // received
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'width': function () {
                                 return remoteProcessGroupData.dimensions.width;
                             },
@@ -296,14 +311,16 @@
                     // -----
 
                     // stats label container
-                    var remoteProcessGroupStatsLabel = details.append('g')
-                        .attrs({
+                    var remoteProcessGroupStatsLabel = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(6, 75)'
                         });
 
                     // sent label
-                    remoteProcessGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        remoteProcessGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -313,8 +330,9 @@
                         .text('Sent');
 
                     // received label
-                    remoteProcessGroupStatsLabel.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        remoteProcessGroupStatsLabel.append('text'),
+                        {
                             'width': 73,
                             'height': 10,
                             'x': 4,
@@ -324,14 +342,16 @@
                         .text('Received');
 
                     // stats value container
-                    var remoteProcessGroupStatsValue = details.append('g')
-                        .attrs({
+                    var remoteProcessGroupStatsValue = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(95, 75)'
                         });
 
                     // sent value
-                    var sentText = remoteProcessGroupStatsValue.append('text')
-                        .attrs({
+                    var sentText = d3Helpers.multiAttr(
+                        remoteProcessGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -340,26 +360,30 @@
                         });
 
                     // sent count
-                    sentText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        sentText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // sent size
-                    sentText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        sentText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // sent ports
-                    sentText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        sentText.append('tspan'),
+                        {
                             'class': 'ports'
                         });
 
                     // received value
-                    var receivedText = remoteProcessGroupStatsValue.append('text')
-                        .attrs({
+                    var receivedText = d3Helpers.multiAttr(
+                        remoteProcessGroupStatsValue.append('text'),
+                        {
                             'width': 180,
                             'height': 10,
                             'x': 4,
@@ -368,32 +392,37 @@
                         });
 
                     // received ports
-                    receivedText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        receivedText.append('tspan'),
+                        {
                             'class': 'ports'
                         });
 
                     // received count
-                    receivedText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        receivedText.append('tspan'),
+                        {
                             'class': 'count'
                         });
 
                     // received size
-                    receivedText.append('tspan')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        receivedText.append('tspan'),
+                        {
                             'class': 'size'
                         });
 
                     // stats value container
-                    var processGroupStatsInfo = details.append('g')
-                        .attrs({
+                    var processGroupStatsInfo = d3Helpers.multiAttr(
+                        details.append('g'),
+                        {
                             'transform': 'translate(335, 75)'
                         });
 
                     // sent info
-                    processGroupStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'x': 4,
@@ -403,8 +432,9 @@
                         .text('5 min');
 
                     // received info
-                    processGroupStatsInfo.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        processGroupStatsInfo.append('text'),
+                        {
                             'width': 25,
                             'height': 10,
                             'x': 4,
@@ -417,8 +447,9 @@
                     // last refreshed time
                     // -------------------
 
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'x': 0,
                             'y': function () {
                                 return remoteProcessGroupData.dimensions.height - 24;
@@ -430,8 +461,9 @@
                             'fill': '#e3e8eb'
                         });
 
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'x': 10,
                             'y': 168,
                             'class': 'remote-process-group-last-refresh'
@@ -441,8 +473,9 @@
                     // comments
                     // --------
 
-                    details.append('path')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('path'),
+                        {
                             'class': 'component-comments',
                             'transform': 'translate(' + (remoteProcessGroupData.dimensions.width - 2) + ', ' + (remoteProcessGroupData.dimensions.height - 10) + ')',
                             'd': 'm0,0 l0,8 l-8,0 z'
@@ -453,16 +486,18 @@
                     // -------------------
 
                     // active thread count
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count-icon',
                             'y': 20
                         })
                         .text('\ue83f');
 
                     // active thread icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'active-thread-count',
                             'y': 20
                         });
@@ -472,8 +507,9 @@
                     // ---------
 
                     // bulletin background
-                    details.append('rect')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('rect'),
+                        {
                             'class': 'bulletin-background',
                             'x': function () {
                                 return remoteProcessGroupData.dimensions.width - 24;
@@ -484,8 +520,9 @@
                         });
 
                     // bulletin icon
-                    details.append('text')
-                        .attrs({
+                    d3Helpers.multiAttr(
+                        details.append('text'),
+                        {
                             'class': 'bulletin-icon',
                             'x': function () {
                                 return remoteProcessGroupData.dimensions.width - 17;
@@ -859,13 +896,14 @@
             nfContextMenu = nfContextMenuRef;
             nfQuickSelect = nfQuickSelectRef;
 
-            remoteProcessGroupMap = d3.map();
-            removedCache = d3.map();
-            addedCache = d3.map();
+            remoteProcessGroupMap = new Map();
+            removedCache = new Map();
+            addedCache = new Map();
 
             // create the process group container
-            remoteProcessGroupContainer = d3.select('#canvas').append('g')
-                .attrs({
+            remoteProcessGroupContainer = d3Helpers.multiAttr(
+                d3.select('#canvas').append('g'),
+                {
                     'pointer-events': 'all',
                     'class': 'remote-process-groups'
                 });
@@ -945,7 +983,7 @@
 
             // determine how to handle the specified remote process groups
             if ($.isArray(remoteProcessGroupEntities)) {
-                $.each(remoteProcessGroupMap.keys(), function (_, key) {
+                $.each(Array.from(remoteProcessGroupMap.keys()), function (_, key) {
                     var currentRemoteProcessGroupEntity = remoteProcessGroupMap.get(key);
                     var isPresent = $.grep(remoteProcessGroupEntities, function (proposedRemoteProcessGroupEntity) {
                         return proposedRemoteProcessGroupEntity.id === currentRemoteProcessGroupEntity.id;
@@ -953,7 +991,7 @@
 
                     // if the current remote process group is not present and was not recently added, remove it
                     if (isPresent.length === 0 && !addedCache.has(key)) {
-                        remoteProcessGroupMap.remove(key);
+                        remoteProcessGroupMap['delete'](key);
                     }
                 });
                 $.each(remoteProcessGroupEntities, function (_, remoteProcessGroupEntity) {
@@ -985,7 +1023,7 @@
          */
         get: function (id) {
             if (nfCommon.isUndefined(id)) {
-                return remoteProcessGroupMap.values();
+                return Array.from(remoteProcessGroupMap.values());
             } else {
                 return remoteProcessGroupMap.get(id);
             }
@@ -1059,11 +1097,11 @@
             if ($.isArray(remoteProcessGroupIds)) {
                 $.each(remoteProcessGroupIds, function (_, remoteProcessGroupId) {
                     removedCache.set(remoteProcessGroupId, now);
-                    remoteProcessGroupMap.remove(remoteProcessGroupId);
+                    remoteProcessGroupMap['delete'](remoteProcessGroupId);
                 });
             } else {
                 removedCache.set(remoteProcessGroupIds, now);
-                remoteProcessGroupMap.remove(remoteProcessGroupIds);
+                remoteProcessGroupMap['delete'](remoteProcessGroupIds);
             }
 
             // apply the selection and handle all removed remote process groups
@@ -1074,7 +1112,7 @@
          * Removes all remote process groups.
          */
         removeAll: function () {
-            nfRemoteProcessGroup.remove(remoteProcessGroupMap.keys());
+            nfRemoteProcessGroup.remove(Array.from(remoteProcessGroupMap.keys()));
         },
 
         /**
@@ -1084,9 +1122,9 @@
          */
         expireCaches: function (timestamp) {
             var expire = function (cache) {
-                cache.each(function (entryTimestamp, id) {
+                cache.forEach(function (entryTimestamp, id) {
                     if (timestamp > entryTimestamp) {
-                        cache.remove(id);
+                        cache['delete'](id);
                     }
                 });
             };
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
index c24457d110..12e48bc680 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
@@ -43,14 +43,14 @@
 
     var nfSelectable = {
 
-        select: function (g) {
+        select: function (event, g) {
             // hide any context menus as necessary
             nfContextMenu.hide();
 
             // only need to update selection if necessary
             if (!g.classed('selected')) {
                 // since we're not appending, deselect everything else
-                if (!d3.event.shiftKey) {
+                if (!event.shiftKey) {
                     d3.selectAll('g.selected').classed('selected', false);
                 }
 
@@ -58,7 +58,7 @@
                 g.classed('selected', true);
             } else {
                 // we are currently selected, if shift key the deselect
-                if (d3.event.shiftKey) {
+                if (event.shiftKey) {
                     g.classed('selected', false);
                 }
             }
@@ -68,7 +68,7 @@
             nfNgBridge.digest();
 
             // stop propagation
-            d3.event.stopPropagation();
+            event.stopPropagation();
         },
 
         /**
@@ -77,9 +77,9 @@
          * @param {selection} components
          */
         activate: function (components) {
-            components.on('mousedown.selection', function () {
+            components.on('mousedown.selection', function (event) {
                 // get the clicked component to update selection
-                nfSelectable.select(d3.select(this));
+                nfSelectable.select(event, d3.select(this));
 
                 // update URL deep linking params
                 nfCanvasUtils.setURLParameters();
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
index 290ce4dce6..45a35bc274 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
@@ -960,9 +960,9 @@
         }).done(function (response) {
             var id = 0;
             var tags = [];
-            var groups = d3.set();
-            var restrictedUsage = d3.map();
-            var requiredPermissions = d3.map();
+            var groups = new Set();
+            var restrictedUsage = new Map();
+            var requiredPermissions = new Map();
 
             // begin the update
             reportingTaskTypesData.beginUpdate();
@@ -1056,7 +1056,7 @@
                 text: 'all groups',
                 value: ''
             }];
-            groups.each(function (group) {
+            groups.forEach(function (group) {
                 options.push({
                     text: group,
                     value: group
@@ -1362,9 +1362,9 @@
         }).done(function (response) {
             var id = 0;
             var tags = [];
-            var groups = d3.set();
-            var restrictedUsage = d3.map();
-            var requiredPermissions = d3.map();
+            var groups = new Set();
+            var restrictedUsage = new Map();
+            var requiredPermissions = new Map();
 
             // begin the update
             parameterProviderTypesData.beginUpdate();
@@ -1458,7 +1458,7 @@
                 text: 'all groups',
                 value: ''
             }];
-            groups.each(function (group) {
+            groups.forEach(function (group) {
                 options.push({
                     text: group,
                     value: group
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
index 6f6f4a7e18..a1b1a43853 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
@@ -356,8 +356,8 @@
 
             // add status history details
             var detailsContainer = buildDetailsContainer('Status History');
-            d3.map(statusHistory.details).each(function (value, label) {
-                addDetailItem(detailsContainer, label, value);
+            Object.entries(statusHistory.details).forEach(function(key, value) {
+                addDetailItem(detailsContainer, key, value);
             });
 
             var margin = {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
index b67e474f47..09dc4ddb4e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
@@ -203,8 +203,8 @@
             var maxMillis;
 
             // data lookups
-            var nodeLookup = d3.map();
-            var linkLookup = d3.map();
+            var nodeLookup = new Map();
+            var linkLookup = new Map();
 
             var locateDescendants = function (nodeIds, descendants, depth) {
                 $.each(nodeIds, function (_, nodeId) {
@@ -225,9 +225,9 @@
             };
 
             var positionNodes = function (nodeIds, depth, parents, levelDifference) {
-                var immediateSet = d3.set(nodeIds);
-                var childSet = d3.set();
-                var descendantSet = d3.set();
+                var immediateSet = new Set(nodeIds);
+                var childSet = new Set();
+                var descendantSet = new Set();
 
                 // locate children
                 locateDescendants(nodeIds, childSet, 1);
@@ -238,16 +238,16 @@
                 // push off processing a node until its deepest point
                 // by removing any descendants from the immediate nodes.
                 // in this case, a link is panning multiple levels
-                descendantSet.each(function (d) {
-                    immediateSet.remove(d);
+                descendantSet.forEach(function (d) {
+                    immediateSet['delete'](d);
                 });
 
                 // convert the children to an array to ensure consistent
                 // order when performing index of checks below
-                var children = childSet.values().sort(d3.descending);
+                var children = Array.from(childSet.values()).sort(d3.descending);
 
                 // convert the immediate to allow for sorting below
-                var immediate = immediateSet.values();
+                var immediate = Array.from(immediateSet.values());
 
                 // attempt to identify fan in/out cases
                 var nodesWithTwoParents = 0;
@@ -475,10 +475,10 @@
 
             var refresh = function (provenanceTableCtrl) {
                 // consider all nodes as starting points
-                var startNodes = d3.set(nodeLookup.keys());
+                var startNodes = new Set(Array.from(nodeLookup.keys()));
 
                 // go through the nodes to reset their outgoing links
-                nodeLookup.each(function (node, id) {
+                nodeLookup.forEach(function (node, id) {
                     node.outgoing = [];
                     node.incoming = [];
 
@@ -493,17 +493,17 @@
                 });
 
                 // go through the links in order to compute the new layout
-                linkLookup.each(function (link, id) {
+                linkLookup.forEach(function (link, id) {
                     // updating the nodes connections
                     link.source.outgoing.push(link);
                     link.target.incoming.push(link);
 
                     // remove the target from being a potential starting node
-                    startNodes.remove(link.target.id);
+                    startNodes['delete'](link.target.id);
                 });
 
                 // position the nodes
-                positionNodes(startNodes.values(), 1, [], 50);
+                positionNodes(Array.from(startNodes.values()), 1, [], 50);
 
                 // update the slider min/max/step values
                 var step = (maxMillis - minMillis) / config.sliderTickCount;
@@ -537,9 +537,9 @@
             // handle zoom behavior
             var lineageZoom = d3.zoom()
                 .scaleExtent([0.2, 8])
-                .on('zoom', function () {
+                .on('zoom', function (event) {
                     d3.select('g.lineage').attr('transform', function () {
-                        return 'translate(' + d3.event.transform.x + ', ' + d3.event.transform.y + ') scale(' + d3.event.transform.k + ')';
+                        return 'translate(' + event.transform.x + ', ' + event.transform.y + ') scale(' + event.transform.k + ')';
                     });
                 });
 
@@ -549,20 +549,20 @@
                 .attr('height', '100%')
                 .call(lineageZoom)
                 .on('dblclick.zoom', null)
-                .on('mousedown', function (d) {
+                .on('mousedown', function (event, d) {
                     // hide the context menu if necessary
                     d3.selectAll('circle.context').classed('context', false);
                     $('#provenance-lineage-context-menu').hide().empty();
 
                     // prevents browser from using text cursor
-                    d3.event.preventDefault();
+                    event.preventDefault();
                 })
-                .on('contextmenu', function () {
+                .on('contextmenu', function (event) {
                     var contextMenu = $('#provenance-lineage-context-menu');
 
                     // if there is something to show in the context menu
                     if (!contextMenu.is(':empty')) {
-                        var position = d3.mouse(this);
+                        var position = d3.pointer(event, this);
 
                         // show the context menu
                         contextMenu.css({
@@ -572,41 +572,37 @@
                     }
 
                     // prevent the native default context menu
-                    d3.event.preventDefault();
+                    event.preventDefault();
                 });
 
             svg.append('rect')
-                .attrs({
-                    'width': '100%',
-                    'height': '100%',
-                    'fill': '#f9fafb'
-                });
+                .attr('width', '100%')
+                .attr('height', '100%')
+                .attr('fill', '#f9fafb');
 
             svg.append('defs').selectAll('marker')
                 .data(['FLOWFILE', 'FLOWFILE-SELECTED', 'EVENT', 'EVENT-SELECTED'])
                 .enter().append('marker')
-                .attrs({
-                    'id': function (d) {
-                        return d;
-                    },
-                    'viewBox': '0 -3 6 6',
-                    'refX': function (d) {
-                        if (d.indexOf('FLOWFILE') >= 0) {
-                            return 16;
-                        } else {
-                            return 11;
-                        }
-                    },
-                    'refY': 0,
-                    'markerWidth': 6,
-                    'markerHeight': 6,
-                    'orient': 'auto',
-                    'fill': function (d) {
-                        if (d.indexOf('SELECTED') >= 0) {
-                            return '#ba554a';
-                        } else {
-                            return '#000000';
-                        }
+                .attr('id', function (d) {
+                    return d;
+                })
+                .attr('viewBox', '0 -3 6 6')
+                .attr('refX', function (d) {
+                    if (d.indexOf('FLOWFILE') >= 0) {
+                        return 16;
+                    } else {
+                        return 11;
+                    }
+                })
+                .attr('refY', 0)
+                .attr('markerWidth', 6)
+                .attr('markerHeight', 6)
+                .attr('orient', 'auto')
+                .attr('fill', function (d) {
+                    if (d.indexOf('SELECTED') >= 0) {
+                        return '#ba554a';
+                    } else {
+                        return '#000000';
                     }
                 })
                 .append('path')
@@ -614,11 +610,9 @@
 
             // group everything together
             var lineageContainer = svg.append('g')
-                .attrs({
-                    'transform': 'translate(0, 0) scale(1)',
-                    'pointer-events': 'all',
-                    'class': 'lineage'
-                });
+                .attr('transform', 'translate(0, 0) scale(1)')
+                .attr('pointer-events', 'all')
+                .attr('class', 'lineage');
 
             // select the nodes and links
             var nodes = lineageContainer.selectAll('g.node');
@@ -673,19 +667,17 @@
             var renderFlowFile = function (flowfiles) {
                 flowfiles
                     .classed('flowfile', true)
-                    .on('mousedown', function (d) {
-                        d3.event.stopPropagation();
+                    .on('mousedown', function (event, d) {
+                        event.stopPropagation();
                     });
 
                 // node
                 flowfiles.append('circle')
-                    .attrs({
-                        'r': 16,
-                        'fill': '#fff',
-                        'stroke': '#000',
-                        'stroke-width': 1.0
-                    })
-                    .on('mouseover', function (d) {
+                    .attr('r', 16)
+                    .attr('fill', '#fff')
+                    .attr('stroke', '#000')
+                    .attr('stroke-width', 1.0)
+                    .on('mouseover', function (event, d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
                         })
@@ -694,7 +686,7 @@
                                 return 'url(#' + d.target.type + '-SELECTED)';
                             });
                     })
-                    .on('mouseout', function (d) {
+                    .on('mouseout', function (event, d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
                         }).classed('selected', false)
@@ -704,21 +696,17 @@
                     });
 
                 var icon = flowfiles.append('g')
-                    .attrs({
-                        'class': 'flowfile-icon',
-                        'transform': function (d) {
-                            return 'translate(-9,-9)';
-                        }
+                    .attr('class', 'flowfile-icon')
+                    .attr('transform', function (d) {
+                        return 'translate(-9,-9)';
                     }).append('text')
-                    .attrs({
-                        'font-family': 'flowfont',
-                        'font-size': '18px',
-                        'fill': '#ad9897',
-                        'transform': function (d) {
-                            return 'translate(0,15)';
-                        }
+                    .attr('font-family', 'flowfont')
+                    .attr('font-size', '18px')
+                    .attr('fill', '#ad9897')
+                    .attr('transform', function (d) {
+                        return 'translate(0,15)';
                     })
-                    .on('mouseover', function (d) {
+                    .on('mouseover', function (event, d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
                         })
@@ -727,7 +715,7 @@
                                 return 'url(#' + d.target.type + '-SELECTED)';
                             });
                     })
-                    .on('mouseout', function (d) {
+                    .on('mouseout', function (event, d) {
                         links.filter(function (linkDatum) {
                             return d.id === linkDatum.flowFileUuid;
                         }).classed('selected', false)
@@ -890,14 +878,14 @@
                         provenanceTableCtrl.getEventDetails(eventId, clusterNodeId).done(function (response) {
                             var provenanceEvent = response.provenanceEvent;
                             var eventUuid = provenanceEvent.flowFileUuid;
-                            var eventUuids = d3.set(provenanceEvent.childUuids);
+                            var eventUuids = new Set(provenanceEvent.childUuids);
 
                             // determines if the specified event should be removable based on if the collapsing is fanning in/out
                             var allowEventRemoval = function (fanIn, node) {
                                 if (fanIn) {
                                     return node.id !== eventId;
                                 } else {
-                                    return node.flowFileUuid !== eventUuid && $.inArray(eventUuid, node.parentUuids) === -1;
+                                    return node.flowFileUuid !== eventUuid && $.inArray(Array.from(eventUuid), node.parentUuids) === -1;
                                 }
                             };
 
@@ -918,11 +906,11 @@
                                 var newUuids = false;
 
                                 // consider each node for being collapsed
-                                $.each(nodeLookup.values(), function (_, node) {
+                                $.each(Array.from(nodeLookup.values()), function (_, node) {
                                     // if this node is in the uuids remove it unless its the original event or is part of this and another lineage
                                     if (uuids.has(node.flowFileUuid) && allowEventRemoval(fanIn, node)) {
                                         // remove it from the look lookup
-                                        nodeLookup.remove(node.id);
+                                        nodeLookup['delete'](node.id);
 
                                         // include all related outgoing flow file uuids
                                         $.each(node.outgoing, function (_, outgoing) {
@@ -935,11 +923,11 @@
                                 });
 
                                 // update the link data
-                                $.each(linkLookup.values(), function (_, link) {
+                                $.each(Array.from(linkLookup.values()), function (_, link) {
                                     // if this link is in the uuids remove it
                                     if (uuids.has(link.flowFileUuid) && allowLinkRemoval(fanIn, link)) {
                                         // remove it from the link lookup
-                                        linkLookup.remove(link.id);
+                                        linkLookup['delete'](link.id);
 
                                         // add a related uuid that needs to be collapse
                                         var next = link.target;
@@ -1002,17 +990,17 @@
             // renders event nodes
             var renderEvent = function (events, provenanceTableCtrl) {
                 events
-                    .on('contextmenu', function (d) {
+                    .on('contextmenu', function (event, d) {
                         // select the current node for a visible cue
                         d3.select('#event-node-' + d.id).classed('context', true);
 
                         // show the context menu
                         showContextMenu(d, provenanceTableCtrl);
                     })
-                    .on('mousedown', function (d) {
-                        d3.event.stopPropagation();
+                    .on('mousedown', function (event, d) {
+                        event.stopPropagation();
                     })
-                    .on('dblclick', function (d) {
+                    .on('dblclick', function (event, d) {
                         // show the event details
                         provenanceTableCtrl.showEventDetails(d.id, clusterNodeId);
                     });
@@ -1021,15 +1009,13 @@
                     .classed('event', true)
                     // join node to its label
                     .append('rect')
-                    .attrs({
-                        'x': 0,
-                        'y': -8,
-                        'height': 16,
-                        'width': 14,
-                        'opacity': 0,
-                        'id': function (d) {
-                            return 'event-filler-' + d.id;
-                        }
+                    .attr('x', 0)
+                    .attr('y', -8)
+                    .attr('height', 16)
+                    .attr('width', 1)
+                    .attr('opacity', 0)
+                    .attr('id', function (d) {
+                        return 'event-filler-' + d.id;
                     });
 
                 events    
@@ -1037,24 +1023,20 @@
                     .classed('selected', function (d) {
                         return d.id === eventId;
                     })
-                    .attrs({
-                        'r': 8,
-                        'fill': '#aabbc3',
-                        'stroke': '#000',
-                        'stroke-width': 1.0,
-                        'id': function (d) {
-                            return 'event-node-' + d.id;
-                        }
+                    .attr('r', 8)
+                    .attr('fill', '#aabbc3')
+                    .attr('stroke', '#000')
+                    .attr('stroke-width', 1.0)
+                    .attr('id', function (d) {
+                        return 'event-node-' + d.id;
                     });
 
                 events
                     .append('text')
-                    .attrs({
-                        'id': function (d) {
-                            return 'event-text-' + d.id;
-                        },
-                        'class': 'event-type'
+                    .attr('id', function (d) {
+                        return 'event-text-' + d.id;
                     })
+                    .attr('class', 'event-type')
                     .classed('expand-parents', function (d) {
                         return d.eventType === 'SPAWN';
                     })
@@ -1083,10 +1065,9 @@
                             });
                             label.attr('transform', 'translate(10,-14)');
                         } else {
-                            label.text(d.eventType).attrs({
-                                'x': 10,
-                                'y': 4
-                            });
+                            label.text(d.eventType)
+                                .attr('x', 10)
+                                .attr('y', 4);
                         }
                     });
             };
@@ -1094,7 +1075,7 @@
             // updates the ui
             var update = function (provenanceTableCtrl) {
                 // update the node data
-                nodes = nodes.data(nodeLookup.values(), function (d) {
+                nodes = nodes.data(Array.from(nodeLookup.values()), function (d) {
                     return d.id;
                 });
 
@@ -1149,7 +1130,7 @@
                     .style('opacity', 1);
 
                 // update the link data
-                links = links.data(linkLookup.values(), function (d) {
+                links = links.data(Array.from(linkLookup.values()), function (d) {
                     return d.id;
                 });
 
@@ -1167,14 +1148,12 @@
                 // add new links
                 var linksEntered = links.enter()
                     .insert('path', '.node')
-                    .attrs({
-                        'class': 'link',
-                        'stroke-width': 1.5,
-                        'stroke': '#000',
-                        'fill': 'none',
-                        'd': function (d) {
-                            return 'M' + d.source.x + ',' + d.source.y + 'L' + d.source.x + ',' + d.source.y;
-                        }
+                    .attr('class', 'link')
+                    .attr('stroke-width', 1.5)
+                    .attr('stroke', '#000')
+                    .attr('fill', 'none')
+                    .attr('d', function (d) {
+                        return 'M' + d.source.x + ',' + d.source.y + 'L' + d.source.x + ',' + d.source.y;
                     })
                     .style('opacity', 0);
 
@@ -1186,13 +1165,11 @@
                 links.transition()
                     .delay(200)
                     .duration(400)
-                    .attrs({
-                        'marker-end': function (d) {
-                            return 'url(#' + d.target.type + ')';
-                        },
-                        'd': function (d) {
-                            return 'M' + d.source.x + ',' + d.source.y + 'L' + d.target.x + ',' + d.target.y;
-                        }
+                    .attr('marker-end', function (d) {
+                        return 'url(#' + d.target.type + ')';
+                    })
+                    .attr('d', function (d) {
+                        return 'M' + d.source.x + ',' + d.source.y + 'L' + d.target.x + ',' + d.target.y;
                     })
                     .style('opacity', 1);
             };