http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js
index 1785ce3..8cb96d7 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js
@@ -19,7 +19,7 @@
 
 nf.ng.BreadcrumbsCtrl = (function () {
 
-    function BreadcrumbsCtrl(ServiceProvider, $sanitize) {
+    function BreadcrumbsCtrl(serviceProvider, $sanitize) {
         function BreadcrumbsCtrl() {
             this.breadcrumbs = [];
         };
@@ -27,11 +27,11 @@ nf.ng.BreadcrumbsCtrl = (function () {
             constructor: BreadcrumbsCtrl,
 
             /**
-             *  Initialize the Breadcrumbs controller.
+             *  Register the breadcrumbs controller.
              */
-            init: function () {
-                if (ServiceProvider.BreadcrumbsCtrl === undefined) {
-                    ServiceProvider.register('BreadcrumbsCtrl', 
breadcrumbsCtrl);
+            register: function () {
+                if (serviceProvider.breadcrumbsCtrl === undefined) {
+                    serviceProvider.register('breadcrumbsCtrl', 
breadcrumbsCtrl);
                 }
             },
 
@@ -161,11 +161,11 @@ nf.ng.BreadcrumbsCtrl = (function () {
             }
         };
         var breadcrumbsCtrl = new BreadcrumbsCtrl();
-        breadcrumbsCtrl.init();
+        breadcrumbsCtrl.register();
         return breadcrumbsCtrl;
     }
 
-    BreadcrumbsCtrl.$inject = ['ServiceProvider', '$sanitize'];
+    BreadcrumbsCtrl.$inject = ['serviceProvider', '$sanitize']; //also depends 
on nf.Common
 
     return BreadcrumbsCtrl;
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-app-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-app-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-app-controller.js
index 596852b..a1f7aca 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-app-controller.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-app-controller.js
@@ -19,25 +19,25 @@
 
 nf.ng.Canvas.AppCtrl = (function () {
 
-    function AppCtrl($scope, ServiceProvider) {
-        function AppCtrl(ServiceProvider) {
+    function AppCtrl($scope, serviceProvider) {
+        function AppCtrl(serviceProvider) {
             //global nf namespace for reference throughout angular app
             this.nf = nf;
-            //any registered angular service is available through the 
ServiceProvider
-            this.ServiceProvider = ServiceProvider;
+            //any registered angular service is available through the 
serviceProvider
+            this.serviceProvider = serviceProvider;
         };
         AppCtrl.prototype = {
             constructor: AppCtrl
         };
-        var appCtrl = new AppCtrl(ServiceProvider);
-        $scope.AppCtrl = appCtrl;
+        var appCtrl = new AppCtrl(serviceProvider);
+        $scope.appCtrl = appCtrl;
 
         //For production angular applications .scope() is unavailable so we set
         //the root scope of the bootstrapped app on the bridge
         nf.ng.Bridge.setRootScope($scope);
     }
 
-    AppCtrl.$inject=['$scope', 'ServiceProvider'];
+    AppCtrl.$inject=['$scope', 'serviceProvider', 'headerCtrl', 
'graphControlsCtrl'];
 
     return AppCtrl;
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js
new file mode 100644
index 0000000..e9bdf9d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js
@@ -0,0 +1,344 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.Canvas.GlobalMenuCtrl = (function () {
+
+    function GlobalMenuCtrl(serviceProvider) {
+
+        var config = {
+            urls: {
+                helpDocument: '../nifi-docs/documentation',
+                controllerAbout: '../nifi-api/flow/about'
+            }
+        };
+
+        function GlobalMenuCtrl() {
+        };
+        GlobalMenuCtrl.prototype = {
+            constructor: GlobalMenuCtrl,
+
+            init: function () {
+                this.about.modal.init();
+            },
+
+            /**
+             * The summary menu item.
+             */
+            summary: {
+
+                /**
+                 * The summary menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the summary shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('summary');
+                    }
+                }
+            },
+
+            /**
+             * The counters menu item.
+             */
+            counters: {
+
+                /**
+                 * The counters menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the counters shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('counters');
+                    }
+                }
+            },
+
+            /**
+             * The bulletin board menu item.
+             */
+            bulletinBoard: {
+
+                /**
+                 * The bulletin board menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the bulletin board shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('bulletin-board');
+                    }
+                }
+            },
+
+            /**
+             * The data provenance menu item.
+             */
+            dataProvenance: {
+
+                /**
+                 * Determines if the data provenance menu item is enabled.
+                 *
+                 * @returns {*|boolean}
+                 */
+                enabled: function () {
+                    return nf.Common.canAccessProvenance();
+                },
+
+                /**
+                 * The data provenance menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the data provenance shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('provenance');
+                    }
+                }
+            },
+
+            /**
+             * The controller settings menu item.
+             */
+            controllerSettings: {
+
+                /**
+                 * The controller settings menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the settings shell.
+                     */
+                    launch: function () {
+                        nf.Settings.loadSettings().done(function () {
+                            nf.Settings.showSettings();
+                        });
+                    }
+                }
+            },
+
+            /**
+             * The cluster menu item.
+             */
+            cluster: {
+
+                /**
+                 * Determines if the cluster menu item is enabled.
+                 *
+                 * @returns {*|boolean}
+                 */
+                enabled: function () {
+                    return nf.Canvas.isClustered();
+                },
+
+                /**
+                 * The cluster menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the cluster shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('cluster');
+                    }
+                }
+            },
+
+            /**
+             * The flow config history menu item.
+             */
+            flowConfigHistory: {
+
+                /**
+                 * The flow config history menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the history shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('history');
+                    }
+                }
+            },
+
+            /**
+             * The users menu item.
+             */
+            users: {
+
+                /**
+                 * Determines if the users menu item is enabled.
+                 *
+                 * @returns {*|boolean}
+                 */
+                enabled: function () {
+                    return nf.Common.isAdmin();
+                },
+
+                /**
+                 * The users menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the users shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('users');
+                    }
+                }
+            },
+
+            /**
+             * The templates menu item.
+             */
+            templates: {
+
+                /**
+                 * The templates menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the templates shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('templates?' + $.param({
+                                groupId: nf.Canvas.getGroupId()
+                            }));
+                    }
+                }
+            },
+
+            /**
+             * The help menu item.
+             */
+            help: {
+
+                /**
+                 * The help menu item's shell.
+                 */
+                shell: {
+
+                    /**
+                     * Launch the help documentation shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage(config.urls.helpDocument);
+                    }
+                }
+            },
+
+            /**
+             * The about menu item.
+             */
+            about: {
+
+                /**
+                 * The about menu item's modal.
+                 */
+                modal: {
+                    
+                    /**
+                     * Gets the modal element.
+                     *
+                     * @returns {*|jQuery|HTMLElement}
+                     */
+                    getElement: function () {
+                        return $('#nf-about');
+                    },
+
+                    /**
+                     * Initialize the modal.
+                     */
+                    init: function () {
+                        var self = this;
+                        // get the about details
+                        $.ajax({
+                            type: 'GET',
+                            url: config.urls.controllerAbout,
+                            dataType: 'json'
+                        }).done(function (response) {
+                            var aboutDetails = response.about;
+                            // set the document title and the about title
+                            document.title = aboutDetails.title;
+                            $('#nf-version').text(aboutDetails.version);
+                        }).fail(nf.Common.handleAjaxError);
+
+                        // configure the about dialog
+                        this.getElement().modal({
+                            overlayBackground: true,
+                            buttons: [{
+                                buttonText: 'Ok',
+                                handler: {
+                                    click: function () {
+                                        self.hide();
+                                    }
+                                }
+                            }]
+                        });
+                    },
+
+                    /**
+                     * Updates the modal config.
+                     *
+                     * @param {string} name             The name of the 
property to update.
+                     * @param {object|array} config     The config for the 
`name`.
+                     */
+                    update: function (name, config) {
+                        this.getElement().modal(name, config);
+                    },
+
+                    /**
+                     * Show the modal
+                     */
+                    show: function () {
+                        this.getElement().modal('show');
+                    },
+
+                    /**
+                     * Hide the modal
+                     */
+                    hide: function () {
+                        this.getElement().modal('hide');
+                    }
+                }
+            }
+        };
+        var globalMenuCtrl = new GlobalMenuCtrl();
+        return globalMenuCtrl;
+    }
+
+    GlobalMenuCtrl.$inject = ['serviceProvider'];
+
+    return GlobalMenuCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js
new file mode 100644
index 0000000..8120551
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js
@@ -0,0 +1,185 @@
+/*
+ * 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 nf, d3 */
+
+
+nf.ng.Canvas.GraphControlsCtrl = (function () {
+
+    function GraphControlsCtrl(serviceProvider, navigateCtrl, operateCtrl) {
+
+        var MIN_GRAPH_CONTROL_TOP = 117;
+
+        /**
+         * Positions the graph controls based on the size of the screen.
+         */
+        var positionGraphControls = function () {
+            var windowHeight = $(window).height();
+            var navigationHeight = $('#navigation-control').outerHeight();
+            var operationHeight = $('#operation-control').outerHeight();
+            var graphControlTop = (windowHeight / 2) - ((navigationHeight + 
operationHeight) / 2);
+
+            $('#graph-controls').css('top', Math.max(MIN_GRAPH_CONTROL_TOP, 
graphControlTop));
+        };
+
+        /**
+         * Opens the specified graph control.
+         *
+         * @param {jQuery} graphControl
+         */
+        var openGraphControl = function (graphControl) {
+            // undock if necessary
+            if ($('div.graph-control-content').is(':visible') === false) {
+                $('#graph-controls div.graph-control-docked').hide();
+                $('#graph-controls div.graph-control-header-container').show();
+                $('.graph-control').removeClass('docked');
+            }
+
+            // show the content of the specified graph control
+            graphControl.children('div.graph-control-content').show();
+            
graphControl.find('i.graph-control-expansion').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
+
+            // handle specific actions as necessary
+            if (graphControl.attr('id') === 'navigation-control') {
+                nf.Birdseye.updateBirdseyeVisibility(true);
+            }
+
+            // get the current visibility
+            var graphControlVisibility = 
nf.Storage.getItem('graph-control-visibility');
+            if (graphControlVisibility === null) {
+                graphControlVisibility = {};
+            }
+
+            // update the visibility for this graph control
+            var graphControlId = graphControl.attr('id');
+            graphControlVisibility[graphControlId] = true;
+            nf.Storage.setItem('graph-control-visibility', 
graphControlVisibility);
+
+            // reset the graph control position
+            positionGraphControls();
+        };
+
+        /**
+         * Hides the specified graph control.
+         *
+         * @param {jQuery} graphControl
+         */
+        var hideGraphControl = function (graphControl) {
+            // hide the content of the specified graph control
+            graphControl.children('div.graph-control-content').hide();
+            
graphControl.find('i.graph-control-expansion').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
+
+            // dock if necessary
+            if ($('div.graph-control-content').is(':visible') === false) {
+                $('#graph-controls div.graph-control-header-container').hide();
+                $('#graph-controls div.graph-control-docked').show();
+                $('.graph-control').addClass('docked');
+            }
+
+            // handle specific actions as necessary
+            if (graphControl.attr('id') === 'navigation-control') {
+                nf.Birdseye.updateBirdseyeVisibility(false);
+            }
+
+            // get the current visibility
+            var graphControlVisibility = 
nf.Storage.getItem('graph-control-visibility');
+            if (graphControlVisibility === null) {
+                graphControlVisibility = {};
+            }
+
+            // update the visibility for this graph control
+            var graphControlId = graphControl.attr('id');
+            graphControlVisibility[graphControlId] = false;
+            nf.Storage.setItem('graph-control-visibility', 
graphControlVisibility);
+
+            // reset the graph control position
+            positionGraphControls();
+        };
+
+        function GraphControlsCtrl(navigateCtrl, operateCtrl) {
+            this.navigateCtrl = navigateCtrl;
+            this.operateCtrl = operateCtrl;
+        };
+        GraphControlsCtrl.prototype = {
+            constructor: GraphControlsCtrl,
+
+            /**
+             *  Register the header controller.
+             */
+            register: function () {
+                if (serviceProvider.graphControlsCtrl === undefined) {
+                    serviceProvider.register('graphControlsCtrl', 
graphControlsCtrl);
+                }
+            },
+
+            /**
+             * Initialize the graph controls.
+             */
+            init: function () {
+                this.operateCtrl.init();
+                // initial the graph control visibility
+                var graphControlVisibility = 
nf.Storage.getItem('graph-control-visibility');
+                if (graphControlVisibility !== null) {
+                    $.each(graphControlVisibility, function (id, isVisible) {
+                        var graphControl = $('#' + id);
+                        if (graphControl) {
+                            if (isVisible) {
+                                openGraphControl(graphControl);
+                            } else {
+                                hideGraphControl(graphControl);
+                            }
+                        }
+                    });
+                }
+
+                // listen for browser resize events to reset the graph control 
positioning
+                $(window).resize(positionGraphControls);
+
+                // set the initial position
+                positionGraphControls();
+            },
+
+            /**
+             * Undock the graph control.
+             * @param {jQuery} $event
+             */
+            undock: function($event){
+                openGraphControl($($event.target).parent().parent());
+            },
+
+            /**
+             * Expand the graph control.
+             * @param {jQuery} $event
+             */
+            expand: function($event){
+                var icon = $($event.target);
+                if (icon.hasClass('fa-plus-square-o')) {
+                    openGraphControl(icon.closest('div.graph-control'));
+                } else {
+                    hideGraphControl(icon.closest('div.graph-control'));
+                }
+            }
+        };
+        var graphControlsCtrl = new GraphControlsCtrl(navigateCtrl, 
operateCtrl);
+        graphControlsCtrl.register();
+        return graphControlsCtrl;
+    }
+
+    GraphControlsCtrl.$inject = ['serviceProvider', 'navigateCtrl', 
'operateCtrl'];
+
+    return GraphControlsCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-header-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-header-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-header-controller.js
new file mode 100644
index 0000000..a6096d4
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-header-controller.js
@@ -0,0 +1,119 @@
+/*
+ * 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 nf, d3 */
+
+
+nf.ng.Canvas.HeaderCtrl = (function () {
+
+    function HeaderCtrl(serviceProvider, toolboxCtrl, globalMenuCtrl) {
+
+        var MIN_TOOLBAR_WIDTH = 640;
+
+        function HeaderCtrl(toolboxCtrl, globalMenuCtrl) {
+            this.toolboxCtrl = toolboxCtrl;
+            this.globalMenuCtrl = globalMenuCtrl;
+        };
+        HeaderCtrl.prototype = {
+            constructor: HeaderCtrl,
+
+            /**
+             *  Register the header controller.
+             */
+            register: function () {
+                if (serviceProvider.headerCtrl === undefined) {
+                    serviceProvider.register('headerCtrl', headerCtrl);
+                }
+            },
+
+            /**
+             * Initialize the canvas header.
+             */
+            init: function () {
+                this.toolboxCtrl.init();
+                this.globalMenuCtrl.init();
+
+                // if the user is not anonymous or accessing via http
+                if ($('#current-user').text() !== 
nf.Common.ANONYMOUS_USER_TEXT || location.protocol === 'http:') {
+                    $('#login-link-container').css('display', 'none');
+                }
+
+                // if accessing via http, don't show the current user
+                if (location.protocol === 'http:') {
+                    $('#current-user-container').css('display', 'none');
+                }
+            },
+
+            /**
+             * Reloads and clears any warnings.
+             */
+            reloadAndClearWarnings: function () {
+                nf.Canvas.reload().done(function () {
+                    // update component visibility
+                    nf.Canvas.View.updateVisibility();
+
+                    // refresh the birdseye
+                    nf.Birdseye.refresh();
+
+                    // hide the refresh link on the canvas
+                    $('#stats-last-refreshed').removeClass('alert');
+                    $('#refresh-required-container').hide();
+
+                    // hide the refresh link on the settings
+                    $('#settings-last-refreshed').removeClass('alert');
+                    $('#settings-refresh-required-icon').hide();
+                }).fail(function () {
+                    nf.Dialog.showOkDialog({
+                        dialogContent: 'Unable to refresh the current group.',
+                        overlayBackground: true
+                    });
+                });
+            },
+
+            /**
+             * The login link.
+             */
+            loginLink: {
+                shell: {
+                    /**
+                     * Launch the login shell.
+                     */
+                    launch: function () {
+                        nf.Shell.showPage('login', false);
+                    }
+                }
+            },
+
+            /**
+             * Logout.
+             */
+            logoutLink: {
+                logout: function () {
+                    nf.Storage.removeItem("jwt");
+                    window.location = '/nifi';
+                }
+            }
+        };
+        var headerCtrl = new HeaderCtrl(toolboxCtrl, globalMenuCtrl);
+        headerCtrl.register();
+        return headerCtrl;
+    }
+
+    HeaderCtrl.$inject = ['serviceProvider', 'toolboxCtrl', 'globalMenuCtrl'];
+
+    return HeaderCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-navigate-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-navigate-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-navigate-controller.js
new file mode 100644
index 0000000..37089b0
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-navigate-controller.js
@@ -0,0 +1,85 @@
+/*
+ * 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 nf, d3 */
+
+
+nf.ng.Canvas.NavigateCtrl = (function () {
+
+    function NavigateCtrl() {
+
+        function NavigateCtrl() {
+        };
+        NavigateCtrl.prototype = {
+            constructor: NavigateCtrl,
+            
+            zoomIn: function(){
+                nf.Canvas.View.zoomIn();
+
+                // hide the context menu
+                nf.ContextMenu.hide();
+
+                // refresh the canvas
+                nf.Canvas.View.refresh({
+                    transition: true
+                });
+            },
+            
+            zoomOut: function(){
+                nf.Canvas.View.zoomOut();
+
+                // hide the context menu
+                nf.ContextMenu.hide();
+
+                // refresh the canvas
+                nf.Canvas.View.refresh({
+                    transition: true
+                });
+            },
+            
+            zoomFit: function(){
+                nf.Canvas.View.fit();
+
+                // hide the context menu
+                nf.ContextMenu.hide();
+
+                // refresh the canvas
+                nf.Canvas.View.refresh({
+                    transition: true
+                });
+            },
+            
+            zoomActualSize: function(){
+                nf.Canvas.View.actualSize();
+
+                // hide the context menu
+                nf.ContextMenu.hide();
+
+                // refresh the canvas
+                nf.Canvas.View.refresh({
+                    transition: true
+                });
+            }
+        };
+        var navigateCtrl = new NavigateCtrl();
+        return navigateCtrl;
+    }
+
+    NavigateCtrl.$inject = [];
+
+    return NavigateCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js
new file mode 100644
index 0000000..e60c0f1
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js
@@ -0,0 +1,284 @@
+/*
+ * 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 nf, d3 */
+
+
+nf.ng.Canvas.OperateCtrl = (function () {
+
+    function OperateCtrl() {
+
+        // updates the color if its a valid hex color string
+        var updateColor = function () {
+            var hex = $('#fill-color-value').val();
+
+            // only update the fill color when its a valid hex color string
+            // #[six hex characters|three hex characters] case insensitive
+            if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex)) {
+                $('#fill-color').minicolors('value', hex);
+            }
+        };
+
+        function OperateCtrl() {
+        };
+        OperateCtrl.prototype = {
+            constructor: OperateCtrl,
+
+            /**
+             * The canvas operator's create template component.
+             */
+            template: {
+
+                /**
+                 * The canvas operator's create template component's modal.
+                 */
+                modal: {
+
+                    /**
+                     * Gets the modal element.
+                     *
+                     * @returns {*|jQuery|HTMLElement}
+                     */
+                    getElement: function () {
+                        return $('#new-template-dialog');
+                    },
+
+                    /**
+                     * Initialize the modal.
+                     */
+                    init: function () {
+                        // configure the create template dialog
+                        this.getElement().modal({
+                            headerText: 'Create Template',
+                            overlayBackground: false
+                        });
+                    },
+
+                    /**
+                     * Updates the modal config.
+                     *
+                     * @param {string} name             The name of the 
property to update.
+                     * @param {object|array} config     The config for the 
`name`.
+                     */
+                    update: function (name, config) {
+                        this.getElement().modal(name, config);
+                    },
+
+                    /**
+                     * Show the modal.
+                     */
+                    show: function () {
+                        this.getElement().modal('show');
+                    },
+
+                    /**
+                     * Hide the modal.
+                     */
+                    hide: function () {
+                        this.getElement().modal('hide');
+                    }
+                }
+            },
+
+            /**
+             * The canvas operator's fillcolor component.
+             */
+            fillcolor: {
+
+                /**
+                 * The canvas operator's fillcolor component's modal.
+                 */
+                modal: {
+
+                    /**
+                     * Gets the modal element.
+                     *
+                     * @returns {*|jQuery|HTMLElement}
+                     */
+                    getElement: function () {
+                        return $('#fill-color-dialog');
+                    },
+
+                    /**
+                     * Initialize the modal.
+                     */
+                    init: function () {
+                        // configure the create fillcolor dialog
+                        this.getElement().modal({
+                            headerText: 'Fill',
+                            overlayBackground: false,
+                            buttons: [{
+                                buttonText: 'Apply',
+                                handler: {
+                                    click: function () {
+                                        var selection = 
nf.CanvasUtils.getSelection();
+
+                                        // color the selected components
+                                        selection.each(function (d) {
+                                            var selected = d3.select(this);
+                                            var selectedData = 
selected.datum();
+
+                                            // get the color and update the 
styles
+                                            var color = 
$('#fill-color').minicolors('value');
+
+                                            // ensure the color actually 
changed
+                                            if (color !== 
selectedData.component.style['background-color']) {
+                                                // build the request entity
+                                                var entity = {
+                                                    'revision': 
nf.Client.getRevision(),
+                                                    'component': {
+                                                        'id': selectedData.id,
+                                                        'style': {
+                                                            
'background-color': color
+                                                        }
+                                                    }
+                                                };
+
+                                                // update the style for the 
specified component
+                                                $.ajax({
+                                                    type: 'PUT',
+                                                    url: 
selectedData.component.uri,
+                                                    data: 
JSON.stringify(entity),
+                                                    dataType: 'json',
+                                                    contentType: 
'application/json'
+                                                }).done(function (response) {
+                                                    // update the revision
+                                                    
nf.Client.setRevision(response.revision);
+
+                                                    // update the component
+                                                    
nf[selectedData.type].set(response);
+                                                }).fail(function (xhr, status, 
error) {
+                                                    if (xhr.status === 400 || 
xhr.status === 404 || xhr.status === 409) {
+                                                        
nf.Dialog.showOkDialog({
+                                                            dialogContent: 
nf.Common.escapeHtml(xhr.responseText),
+                                                            overlayBackground: 
true
+                                                        });
+                                                    }
+                                                });
+                                            }
+                                        });
+
+                                        // close the dialog
+                                        $('#fill-color-dialog').modal('hide');
+                                    }
+                                }
+                            }, {
+                                buttonText: 'Cancel',
+                                handler: {
+                                    click: function () {
+                                        // close the dialog
+                                        $('#fill-color-dialog').modal('hide');
+                                    }
+                                }
+                            }],
+                            handler: {
+                                close: function () {
+                                    // clear the current color
+                                    $('#fill-color-value').val('');
+                                    $('#fill-color').minicolors('value', '');
+                                }
+                            }
+                        });
+                    },
+
+                    /**
+                     * Updates the modal config.
+                     *
+                     * @param {string} name             The name of the 
property to update.
+                     * @param {object|array} config     The config for the 
`name`.
+                     */
+                    update: function (name, config) {
+                        this.getElement().modal(name, config);
+                    },
+
+                    /**
+                     * Show the modal.
+                     */
+                    show: function () {
+                        this.getElement().modal('show');
+                    },
+
+                    /**
+                     * Hide the modal.
+                     */
+                    hide: function () {
+                        this.getElement().modal('hide');
+                    },
+
+                    /**
+                     * The canvas operator's fillcolor component modal's 
minicolors.
+                     */
+                    minicolors: {
+
+                        /**
+                         * Gets the minicolors element.
+                         *
+                         * @returns {*|jQuery|HTMLElement}
+                         */
+                        getElement: function () {
+                            return $('#fill-color');
+                        },
+
+                        /**
+                         * Initialize the minicolors.
+                         */
+                        init: function () {
+                            // configure the minicolors
+                            this.getElement().minicolors({
+                                inline: true,
+                                change: function (hex, opacity) {
+                                    // update the value
+                                    $('#fill-color-value').val(hex);
+
+                                    // always update the preview
+                                    $('#fill-color-processor-preview, 
#fill-color-label-preview').css({
+                                        'border-color': hex,
+                                        'background': 'linear-gradient(to 
bottom, #ffffff, ' + hex + ')',
+                                        'filter': 
'progid:DXImageTransform.Microsoft.gradient(gradientType=0, 
startColorstr=#ffffff, endColorstr=' + hex + ')'
+                                    });
+                                }
+                            });
+
+                            // apply fill color from field on blur and enter 
press
+                            $('#fill-color-value').on('blur', 
updateColor).on('keyup', function (e) {
+                                var code = e.keyCode ? e.keyCode : e.which;
+                                if (code === $.ui.keyCode.ENTER) {
+                                    updateColor();
+                                }
+                            });
+                        }
+                    }
+                }
+            },
+
+            /**
+             * Initializes the canvas operate controller.
+             */
+            init: function () {
+                this.template.modal.init();
+                this.fillcolor.modal.init();
+                this.fillcolor.modal.minicolors.init();
+            }
+        };
+        var operateCtrl = new OperateCtrl();
+        return operateCtrl;
+    }
+
+    OperateCtrl.$inject = [];
+
+    return OperateCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js
new file mode 100644
index 0000000..eb27f75
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js
@@ -0,0 +1,169 @@
+/*
+ * 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 nf, d3 */
+
+
+nf.ng.Canvas.ToolboxCtrl = (function () {
+
+    function ToolboxCtrl(processorComponent,
+                         inputPortComponent,
+                         outputPortComponent,
+                         groupComponent,
+                         remoteGroupComponent,
+                         funnelComponent,
+                         templateComponent,
+                         labelComponent) {
+
+        function ToolboxCtrl(processorComponent,
+                             inputPortComponent,
+                             outputPortComponent,
+                             groupComponent,
+                             remoteGroupComponent,
+                             funnelComponent,
+                             templateComponent,
+                             labelComponent) {
+            this.processorComponent = processorComponent;
+            this.inputPortComponent = inputPortComponent;
+            this.outputPortComponent = outputPortComponent;
+            this.groupComponent = groupComponent;
+            this.remoteGroupComponent = remoteGroupComponent;
+            this.funnelComponent = funnelComponent;
+            this.templateComponent = templateComponent;
+            this.labelComponent = labelComponent;
+        };
+        ToolboxCtrl.prototype = {
+            constructor: ToolboxCtrl,
+
+            /**
+             * Config for the toolbox
+             */
+            config: {
+                filterText: 'Filter',
+                type: {
+                    processor: 'Processor',
+                    inputPort: 'Input Port',
+                    outputPort: 'Output Port',
+                    processGroup: 'Process Group',
+                    remoteProcessGroup: 'Remote Process Group',
+                    connection: 'Connection',
+                    funnel: 'Funnel',
+                    template: 'Template',
+                    label: 'Label'
+                },
+                styles: {
+                    filterList: 'filter-list'
+                },
+                urls: {
+                    api: '../nifi-api',
+                    controller: '../nifi-api/controller',
+                    processorTypes: '../nifi-api/flow/processor-types'
+                }
+            },
+
+            /**
+             * Initialize the toolbox controller.
+             */
+            init: function () {
+                // ensure the user can create graph components
+                if (nf.Common.isDFM()) {
+                    // initialize modal dialogs
+                    processorComponent.modal.init();
+                    inputPortComponent.modal.init();
+                    outputPortComponent.modal.init();
+                    groupComponent.modal.init();
+                    remoteGroupComponent.modal.init();
+                    templateComponent.modal.init();
+                } else {
+                    // disable components
+                    processorComponent.disabled();
+                    inputPortComponent.disabled();
+                    outputPortComponent.disabled();
+                    groupComponent.disabled();
+                    remoteGroupComponent.disabled();
+                    funnelComponent.disabled();
+                    templateComponent.disabled();
+                    labelComponent.disabled();
+                }
+            },
+
+            /**
+             * Gets the draggable configuration for a toolbox component.
+             *
+             * @param {object} component        The component responsible for 
handling the stop event.
+             * @returns {object}                The draggable configuration.
+             *
+             * NOTE: The `component` must implement a dropHandler.
+             */
+            draggableComponentConfig: function (component) {
+                return {
+                    zIndex: 1011,
+                    revert: true,
+                    revertDuration: 0,
+                    cancel: false,
+                    helper: "clone",
+                    containment: 'body',
+                    start: function (e, ui) {
+                        // hide the context menu if necessary
+                        nf.ContextMenu.hide();
+                    },
+                    stop: function (e, ui) {
+                        var translate = nf.Canvas.View.translate();
+                        var scale = nf.Canvas.View.scale();
+
+                        var mouseX = e.originalEvent.pageX;
+                        var mouseY = e.originalEvent.pageY - 
nf.Canvas.CANVAS_OFFSET;
+
+                        // invoke the drop handler if we're over the canvas
+                        if (mouseX >= 0 && mouseY >= 0) {
+                            // adjust the x and y coordinates accordingly
+                            var x = (mouseX / scale) - (translate[0] / scale);
+                            var y = (mouseY / scale) - (translate[1] / scale);
+
+                            //each component must implement a dropHandler 
function
+                            component.dropHandler.apply(component, [{
+                                x: x,
+                                y: y
+                            }]);
+                        }
+                    }
+                }
+            }
+        };
+        var toolboxCtrl =
+            new ToolboxCtrl(processorComponent,
+                inputPortComponent,
+                outputPortComponent,
+                groupComponent,
+                remoteGroupComponent,
+                funnelComponent,
+                templateComponent,
+                labelComponent);
+        return toolboxCtrl;
+    }
+
+    ToolboxCtrl.$inject = ['processorComponent',
+        'inputPortComponent',
+        'outputPortComponent',
+        'groupComponent',
+        'remoteGroupComponent',
+        'funnelComponent',
+        'templateComponent',
+        'labelComponent'];
+
+    return ToolboxCtrl;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-breadcrumbs-directive.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-breadcrumbs-directive.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-breadcrumbs-directive.js
index 33c0d8e..f7eb772 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-breadcrumbs-directive.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-breadcrumbs-directive.js
@@ -19,7 +19,7 @@
 
 nf.ng.BreadcrumbsDirective = (function () {
 
-    function BreadcrumbsDirective(BreadcrumbsCtrl) {
+    function BreadcrumbsDirective(breadcrumbsCtrl) {
         return {
             restrict: 'E',
             templateUrl: 'views/nf-ng-breadcrumbs-directive-view.html',
@@ -30,13 +30,12 @@ nf.ng.BreadcrumbsDirective = (function () {
                 'separatorFunc': '='
             },
             link: function (scope, element, attrs) {
-                BreadcrumbsCtrl.registerMouseWheelEvent(element);
-                scope.BreadcrumbsCtrl = BreadcrumbsCtrl;
+                breadcrumbsCtrl.registerMouseWheelEvent(element);
             }
         };
     }
 
-    BreadcrumbsDirective.$inject = ['BreadcrumbsCtrl'];
+    BreadcrumbsDirective.$inject = ['breadcrumbsCtrl'];
 
     return BreadcrumbsDirective;
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-draggable-directive.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-draggable-directive.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-draggable-directive.js
new file mode 100644
index 0000000..5631e9b
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/directives/nf-ng-draggable-directive.js
@@ -0,0 +1,34 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.DraggableDirective = (function () {
+
+    function DraggableDirective() {
+        return {
+            restrict: 'AE',
+            link: function (scope, element, attrs) {
+                element.draggable(scope.$eval(attrs.nfDraggable));
+            }
+        };
+    }
+
+    DraggableDirective.$inject = [];
+
+    return DraggableDirective;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-funnel-component.js
----------------------------------------------------------------------
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-funnel-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-funnel-component.js
new file mode 100644
index 0000000..4854757
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-funnel-component.js
@@ -0,0 +1,107 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.FunnelComponent = (function () {
+
+    function FunnelComponent(serviceProvider) {
+
+        function FunnelComponent() {
+        };
+        FunnelComponent.prototype = {
+            constructor: FunnelComponent,
+
+            /**
+             * Gets the component.
+             *
+             * @returns {*|jQuery|HTMLElement}
+             */
+            getElement: function () {
+                return $('#funnel-component');
+            },
+
+            /**
+             * Enable the component.
+             */
+            enabled: function () {
+                this.getElement().attr('disabled', false);
+            },
+
+            /**
+             * Disable the component.
+             */
+            disabled: function () {
+                this.getElement().attr('disabled', true);
+            },
+
+            /**
+             * Handler function for when component is dropped on the canvas.
+             *
+             * @argument {object} pt        The point that the component was 
dropped.
+             */
+            dropHandler: function (pt) {
+                this.createFunnel(pt);
+            },
+
+            /**
+             * Creates a new funnel at the specified point.
+             *
+             * @argument {object} pt        The point that the funnel was 
dropped.
+             */
+            createFunnel: function (pt) {
+                var outputPortEntity = {
+                    'revision': nf.Client.getRevision(),
+                    'component': {
+                        'position': {
+                            'x': pt.x,
+                            'y': pt.y
+                        }
+                    }
+                };
+
+                // create a new funnel
+                $.ajax({
+                    type: 'POST',
+                    url: 
serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + '/process-groups/' + 
encodeURIComponent(nf.Canvas.getGroupId()) + '/funnels',
+                    data: JSON.stringify(outputPortEntity),
+                    dataType: 'json',
+                    contentType: 'application/json'
+                }).done(function (response) {
+                    if (nf.Common.isDefinedAndNotNull(response.component)) {
+                        // update the revision
+                        nf.Client.setRevision(response.revision);
+
+                        // add the funnel to the graph
+                        nf.Graph.add({
+                            'funnels': [response]
+                        }, true);
+
+                        // update the birdseye
+                        nf.Birdseye.refresh();
+                    }
+                }).fail(nf.Common.handleAjaxError);
+            }
+        };
+        var funnelComponent = new FunnelComponent();
+        return funnelComponent;
+    }
+
+    FunnelComponent.$inject = ['serviceProvider'];
+
+    return FunnelComponent;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js
----------------------------------------------------------------------
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-group-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-group-component.js
new file mode 100644
index 0000000..a6bd99f
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js
@@ -0,0 +1,221 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.GroupComponent = (function () {
+
+    function GroupComponent(serviceProvider) {
+
+        /**
+         * Create the group and add to the graph.
+         *
+         * @argument {string} groupName The name of the group.
+         * @argument {object} pt        The point that the group was dropped.
+         */
+        var createGroup = function (groupName, pt) {
+            var processGroupEntity = {
+                'revision': nf.Client.getRevision(),
+                'component': {
+                    'name': groupName,
+                    'position': {
+                        'x': pt.x,
+                        'y': pt.y
+                    }
+                }
+            };
+
+            // create a new processor of the defined type
+            return $.ajax({
+                type: 'POST',
+                url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + 
'/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + 
'/process-groups',
+                data: JSON.stringify(processGroupEntity),
+                dataType: 'json',
+                contentType: 'application/json'
+            }).done(function (response) {
+                if (nf.Common.isDefinedAndNotNull(response.component)) {
+                    // update the revision
+                    nf.Client.setRevision(response.revision);
+
+                    // add the process group to the graph
+                    nf.Graph.add({
+                        'processGroups': [response]
+                    }, true);
+
+                    // update component visibility
+                    nf.Canvas.View.updateVisibility();
+
+                    // update the birdseye
+                    nf.Birdseye.refresh();
+                }
+            }).fail(nf.Common.handleAjaxError);
+        };
+
+        function GroupComponent() {
+        };
+        GroupComponent.prototype = {
+            constructor: GroupComponent,
+
+            /**
+             * The group component's modal.
+             */
+            modal: {
+                
+                /**
+                 * Gets the modal element.
+                 *
+                 * @returns {*|jQuery|HTMLElement}
+                 */
+                getElement: function () {
+                    return $('#new-process-group-dialog');
+                },
+
+                /**
+                 * Initialize the modal.
+                 */
+                init: function () {
+                    // configure the new process group dialog
+                    this.getElement().modal({
+                        headerText: 'Add Process Group',
+                        overlayBackground: false,
+                        handler: {
+                            close: function () {
+                                $('#new-process-group-name').val('');
+                            }
+                        }
+                    });
+                },
+
+                /**
+                 * Updates the modal config.
+                 *
+                 * @param {string} name             The name of the property 
to update.
+                 * @param {object|array} config     The config for the `name`.
+                 */
+                update: function (name, config) {
+                    this.getElement().modal(name, config);
+                },
+
+                /**
+                 * Show the modal.
+                 */
+                show: function () {
+                    this.getElement().modal('show');
+                },
+
+                /**
+                 * Hide the modal.
+                 */
+                hide: function () {
+                    this.getElement().modal('hide');
+                }
+            },
+
+            /**
+             * Gets the component.
+             *
+             * @returns {*|jQuery|HTMLElement}
+             */
+            getElement: function () {
+                return $('#group-component');
+            },
+
+            /**
+             * Enable the component.
+             */
+            enabled: function () {
+                this.getElement().attr('disabled', false);
+            },
+
+            /**
+             * Disable the component.
+             */
+            disabled: function () {
+                this.getElement().attr('disabled', true);
+            },
+
+            /**
+             * Handler function for when component is dropped on the canvas.
+             *
+             * @argument {object} pt        The point that the component was 
dropped.
+             */
+            dropHandler: function (pt) {
+                this.promptForGroupName(pt);
+            },
+
+            /**
+             * Prompts the user to enter the name for the group.
+             *
+             * @argument {object} pt        The point that the group was 
dropped.
+             */
+            promptForGroupName: function (pt) {
+                var self = this;
+                return $.Deferred(function (deferred) {
+                    var addGroup = function () {
+                        // get the name of the group and clear the textfield
+                        var groupName = $('#new-process-group-name').val();
+
+                        // hide the dialog
+                        self.modal.hide();
+
+                        // create the group and resolve the deferred 
accordingly
+                        createGroup(groupName, pt).done(function (response) {
+                            deferred.resolve(response.component);
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    };
+
+                    self.modal.update('setButtonModel', [{
+                        buttonText: 'Add',
+                        handler: {
+                            click: addGroup
+                        }
+                    }, {
+                        buttonText: 'Cancel',
+                        handler: {
+                            click: function () {
+                                // reject the deferred
+                                deferred.reject();
+
+                                // close the dialog
+                                self.modal.hide();
+                            }
+                        }
+                    }]);
+
+                    // show the dialog
+                    self.modal.show();
+
+                    // set up the focus and key handlers
+                    
$('#new-process-group-name').focus().off('keyup').on('keyup', function (e) {
+                        var code = e.keyCode ? e.keyCode : e.which;
+                        if (code === $.ui.keyCode.ENTER) {
+                            addGroup();
+                        }
+                    });
+                }).promise();
+            }
+        };
+        var groupComponent = new GroupComponent();
+        return groupComponent;
+    }
+
+    GroupComponent.$inject = ['serviceProvider'];
+
+    return GroupComponent;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-input-port-component.js
----------------------------------------------------------------------
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-input-port-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-input-port-component.js
new file mode 100644
index 0000000..ed8f080
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-input-port-component.js
@@ -0,0 +1,214 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.InputPortComponent = (function () {
+
+    function InputPortComponent(serviceProvider) {
+
+        /**
+         * Create the input port and add to the graph.
+         *
+         * @argument {string} portName          The input port name.
+         * @argument {object} pt                The point that the input port 
was dropped.
+         */
+        var createInputPort = function (portName, pt) {
+            var inputPortEntity = {
+                'revision': nf.Client.getRevision(),
+                'component': {
+                    'name': portName,
+                    'position': {
+                        'x': pt.x,
+                        'y': pt.y
+                    }
+                }
+            };
+
+            // create a new processor of the defined type
+            $.ajax({
+                type: 'POST',
+                url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + 
'/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + 
'/input-ports',
+                data: JSON.stringify(inputPortEntity),
+                dataType: 'json',
+                contentType: 'application/json'
+            }).done(function (response) {
+                if (nf.Common.isDefinedAndNotNull(response.component)) {
+                    // update the revision
+                    nf.Client.setRevision(response.revision);
+
+                    // add the port to the graph
+                    nf.Graph.add({
+                        'inputPorts': [response]
+                    }, true);
+
+                    // update component visibility
+                    nf.Canvas.View.updateVisibility();
+
+                    // update the birdseye
+                    nf.Birdseye.refresh();
+                }
+            }).fail(nf.Common.handleAjaxError);
+        };
+
+        function InputPortComponent() {
+        };
+        InputPortComponent.prototype = {
+            constructor: InputPortComponent,
+
+            /**
+             * The input port component's modal.
+             */
+            modal: {
+                
+                /**
+                 * Gets the modal element.
+                 *
+                 * @returns {*|jQuery|HTMLElement}
+                 */
+                getElement: function () {
+                    return $('#new-port-dialog');
+                },
+
+                /**
+                 * Initialize the modal.
+                 */
+                init: function () {
+                    // configure the new port dialog
+                    this.getElement().modal({
+                        headerText: 'Add Port',
+                        overlayBackground: false,
+                        handler: {
+                            close: function () {
+                                $('#new-port-name').val('');
+                            }
+                        }
+                    });
+                },
+
+                /**
+                 * Updates the modal config.
+                 *
+                 * @param {string} name             The name of the property 
to update.
+                 * @param {object|array} config     The config for the `name`.
+                 */
+                update: function (name, config) {
+                    this.getElement().modal(name, config);
+                },
+
+                /**
+                 * Show the modal.
+                 */
+                show: function () {
+                    this.getElement().modal('show');
+                },
+
+                /**
+                 * Hide the modal.
+                 */
+                hide: function () {
+                    this.getElement().modal('hide');
+                }
+            },
+
+            /**
+             * Gets the component.
+             *
+             * @returns {*|jQuery|HTMLElement}
+             */
+            getElement: function () {
+                return $('#port-in-component');
+            },
+
+            /**
+             * Enable the component.
+             */
+            enabled: function () {
+                this.getElement().attr('disabled', false);
+            },
+
+            /**
+             * Disable the component.
+             */
+            disabled: function () {
+                this.getElement().attr('disabled', true);
+            },
+
+            /**
+             * Handler function for when component is dropped on the canvas.
+             *
+             * @argument {object} pt        The point that the component was 
dropped.
+             */
+            dropHandler: function (pt) {
+                this.promptForInputPortName(pt);
+            },
+
+            /**
+             * Prompts the user to enter the name for the input port.
+             *
+             * @argument {object} pt        The point that the input port was 
dropped.
+             */
+            promptForInputPortName: function (pt) {
+                var self = this;
+                var addInputPort = function () {
+                    // get the name of the input port and clear the textfield
+                    var portName = $('#new-port-name').val();
+
+                    // hide the dialog
+                    self.modal.hide();
+
+                    // create the input port
+                    createInputPort(portName, pt);
+                };
+
+                this.modal.update('setButtonModel', [{
+                    buttonText: 'Add',
+                    handler: {
+                        click: addInputPort
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    handler: {
+                        click: function () {
+                            self.modal.hide();
+                        }
+                    }
+                }]);
+
+                // update the port type
+                $('#new-port-type').text('Input');
+
+                // show the dialog
+                this.modal.show();
+
+                // set up the focus and key handlers
+                $('#new-port-name').focus().off('keyup').on('keyup', function 
(e) {
+                    var code = e.keyCode ? e.keyCode : e.which;
+                    if (code === $.ui.keyCode.ENTER) {
+                        addInputPort();
+                    }
+                });
+            }
+        };
+        var inputPortComponent = new InputPortComponent();
+        return inputPortComponent;
+    }
+
+    InputPortComponent.$inject = ['serviceProvider'];
+
+    return InputPortComponent;
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/1df8fe44/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-label-component.js
----------------------------------------------------------------------
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-label-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-label-component.js
new file mode 100644
index 0000000..d111f12
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-label-component.js
@@ -0,0 +1,109 @@
+/*
+ * 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 nf, d3 */
+
+nf.ng.LabelComponent = (function () {
+
+    function LabelComponent(serviceProvider) {
+
+        function LabelComponent() {
+        };
+        LabelComponent.prototype = {
+            constructor: LabelComponent,
+
+            /**
+             * Gets the component.
+             *
+             * @returns {*|jQuery|HTMLElement}
+             */
+            getElement: function () {
+                return $('#label-component');
+            },
+
+            /**
+             * Enable the component.
+             */
+            enabled: function () {
+                this.getElement().attr('disabled', false);
+            },
+
+            /**
+             * Disable the component.
+             */
+            disabled: function () {
+                this.getElement().attr('disabled', true);
+            },
+
+            /**
+             * Handler function for when component is dropped on the canvas.
+             *
+             * @argument {object} pt        The point that the component was 
dropped.
+             */
+            dropHandler: function (pt) {
+                this.createLabel(pt);
+            },
+
+            /**
+             * Create the label and add to the graph.
+             *
+             * @argument {object} pt        The point that the label was 
dropped.
+             */
+            createLabel: function (pt) {
+                var labelEntity = {
+                    'revision': nf.Client.getRevision(),
+                    'component': {
+                        'width': nf.Label.config.width,
+                        'height': nf.Label.config.height,
+                        'position': {
+                            'x': pt.x,
+                            'y': pt.y
+                        }
+                    }
+                };
+
+                // create a new label
+                $.ajax({
+                    type: 'POST',
+                    url: 
serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + '/process-groups/' + 
encodeURIComponent(nf.Canvas.getGroupId()) + '/labels',
+                    data: JSON.stringify(labelEntity),
+                    dataType: 'json',
+                    contentType: 'application/json'
+                }).done(function (response) {
+                    if (nf.Common.isDefinedAndNotNull(response.component)) {
+                        // update the revision
+                        nf.Client.setRevision(response.revision);
+
+                        // add the label to the graph
+                        nf.Graph.add({
+                            'labels': [response]
+                        }, true);
+
+                        // update the birdseye
+                        nf.Birdseye.refresh();
+                    }
+                }).fail(nf.Common.handleAjaxError);
+            }
+        };
+        var labelComponent = new LabelComponent();
+        return labelComponent;
+    }
+
+    LabelComponent.$inject = ['serviceProvider'];
+
+    return LabelComponent;
+}());
\ No newline at end of file

Reply via email to