You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/01/21 14:05:06 UTC
[54/64] incubator-nifi git commit: Merge branch 'develop' into
NIFI-250
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/c5d452c1/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --cc nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 0000000,26db837..8125219
mode 000000,100644..100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@@ -1,0 -1,1530 +1,1531 @@@
+ /*
+ * 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.
+ */
+ $(document).ready(function () {
+ if (nf.Canvas.SUPPORTS_SVG) {
+ // initialize the NiFi
+ nf.Canvas.init();
+ } else {
+ $('#message-title').text('Unsupported Browser');
+ $('#message-content').text('Flow graphs are shown using SVG. Please use a browser that supports rendering SVG.');
+
+ // show the error pane
+ $('#message-pane').show();
+
+ // hide the splash screen
+ nf.Canvas.hideSplash();
+ }
+ });
+
+ nf.Canvas = (function () {
+
+ var SCALE = 1;
+ var TRANSLATE = [0, 0];
+ var INCREMENT = 1.2;
+ var MAX_SCALE = 8;
+ var MIN_SCALE = 0.2;
+ var MIN_SCALE_TO_RENDER = 0.6;
+
+ var revisionPolling = false;
+ var statusPolling = false;
+ var groupId = 'root';
+ var groupName = null;
+ var parentGroupId = null;
+ var secureSiteToSite = false;
+ var clustered = false;
+ var svg = null;
+ var canvas = null;
+
+ var canvasClicked = false;
+ var panning = false;
+
+ var config = {
+ urls: {
+ authorities: '../nifi-api/controller/authorities',
+ revision: '../nifi-api/controller/revision',
+ status: '../nifi-api/controller/status',
+ bulletinBoard: '../nifi-api/controller/bulletin-board',
+ banners: '../nifi-api/controller/banners',
+ controller: '../nifi-api/controller',
+ controllerConfig: '../nifi-api/controller/config',
+ cluster: '../nifi-api/cluster',
+ d3Script: 'js/d3/d3.min.js'
+ }
+ };
+
+ /**
+ * Generates the breadcrumbs.
+ *
+ * @argument {object} processGroup The process group
+ */
+ var generateBreadcrumbs = function (processGroup) {
+ // create the link for loading the correct group
+ var groupLink = $('<span class="link"></span>').text(processGroup.name).click(function () {
+ nf.CanvasUtils.enterGroup(processGroup.id);
+ });
+
+ // make the current group bold
+ if (nf.Canvas.getGroupId() === processGroup.id) {
+ groupLink.css('font-weight', 'bold');
+ }
+
+ // if there is a parent, create the appropriate mark up
+ if (nf.Common.isDefinedAndNotNull(processGroup.parent)) {
+ var separator = $('<span>»</span>').css({
+ 'color': '#598599',
+ 'margin': '0 10px'
+ });
+ $('#data-flow-title-container').append(generateBreadcrumbs(processGroup.parent)).append(separator);
+ }
+
+ // append this link
+ $('#data-flow-title-container').append(groupLink);
+ return groupLink;
+ };
+
+ /**
+ * Loads D3.
+ */
+ var loadD3 = function () {
+ return nf.Common.cachedScript(config.urls.d3Script);
+ };
+
+ /**
+ * Starts polling for the revision.
+ *
+ * @argument {int} autoRefreshInterval The auto refresh interval
+ */
+ var startRevisionPolling = function (autoRefreshInterval) {
+ // set polling flag
+ revisionPolling = true;
+ pollForRevision(autoRefreshInterval);
+ };
+
+ /**
+ * Polls for the revision.
+ *
+ * @argument {int} autoRefreshInterval The auto refresh interval
+ */
+ var pollForRevision = function (autoRefreshInterval) {
+ // ensure we're suppose to poll
+ if (revisionPolling) {
+ // check the revision
+ checkRevision().done(function () {
+ // start the wait to poll again
+ setTimeout(function () {
+ pollForRevision(autoRefreshInterval);
+ }, autoRefreshInterval * 1000);
+ });
+ }
+ };
+
+ /**
+ * Start polling for the status.
+ *
+ * @argument {int} autoRefreshInterval The auto refresh interval
+ */
+ var startStatusPolling = function (autoRefreshInterval) {
+ // set polling flag
+ statusPolling = true;
+ pollForStatus(autoRefreshInterval);
+ };
+
+ /**
+ * Register the status poller.
+ *
+ * @argument {int} autoRefreshInterval The auto refresh interval
+ */
+ var pollForStatus = function (autoRefreshInterval) {
+ // ensure we're suppose to poll
+ if (statusPolling) {
+ // reload the status
+ nf.Canvas.reloadStatus().done(function () {
+ // start the wait to poll again
+ setTimeout(function () {
+ pollForStatus(autoRefreshInterval);
+ }, autoRefreshInterval * 1000);
+ });
+ }
+ };
+
+ /**
+ * Checks the current revision against this version of the flow.
+ */
+ var checkRevision = function () {
+ // get the revision
+ return $.ajax({
+ type: 'GET',
+ url: config.urls.revision,
+ dataType: 'json'
+ }).done(function (response) {
+ if (nf.Common.isDefinedAndNotNull(response.revision)) {
+ var revision = response.revision;
+ var currentRevision = nf.Client.getRevision();
+
+ // if there is a newer revision, there are outstanding
+ // changes that need to be updated
+ if (revision.version > currentRevision.version && revision.clientId !== currentRevision.clientId) {
+ var refreshContainer = $('#refresh-required-container');
+
+ // insert the refresh needed text - if necessary
+ if (!refreshContainer.is(':visible')) {
+ $('#stats-last-refreshed').addClass('alert');
+ refreshContainer.show();
+ }
+ }
+ }
+ }).fail(nf.Common.handleAjaxError);
+ };
+
+ /**
+ * Initializes the canvas.
+ */
+ var initCanvas = function () {
+ var canvasContainer = $('#canvas-container');
+
+ // create the canvas
+ svg = d3.select('#canvas-container').append('svg')
+ .on('contextmenu', function () {
+ // reset the canvas click flag
+ canvasClicked = false;
+
+ // since the context menu event propagated back to the canvas, clear the selection
+ nf.CanvasUtils.getSelection().classed('selected', false);
+
+ // show the context menu on the canvas
+ nf.ContextMenu.show();
+
+ // prevent default browser behavior
+ d3.event.preventDefault();
+ });
+
+ // create the definitions element
+ var defs = svg.append('defs');
+
+ // create arrow definitions for the various line types
+ defs.selectAll('marker')
+ .data(['normal', 'ghost'])
+ .enter().append('marker')
+ .attr({
+ 'id': function (d) {
+ return d;
+ },
+ 'viewBox': '0 0 6 6',
+ 'refX': 5,
+ 'refY': 3,
+ 'markerWidth': 6,
+ 'markerHeight': 6,
+ 'orient': 'auto',
+ 'fill': function (d) {
+ if (d === 'ghost') {
+ return '#aaaaaa';
+ } else {
+ return '#000000';
+ }
+ }
+ })
+ .append('path')
+ .attr('d', 'M2,3 L0,6 L6,3 L0,0 z');
+
+ // define the gradient for the processor stats background
+ var processGroupStatsBackground = defs.append('linearGradient')
+ .attr({
+ 'id': 'process-group-stats-background',
+ 'x1': '0%',
+ 'y1': '100%',
+ 'x2': '0%',
+ 'y2': '0%'
+ });
+
+ processGroupStatsBackground.append('stop')
+ .attr({
+ 'offset': '0%',
+ 'stop-color': '#dedede'
+ });
+
+ processGroupStatsBackground.append('stop')
+ .attr({
+ 'offset': '50%',
+ 'stop-color': '#ffffff'
+ });
+
+ processGroupStatsBackground.append('stop')
+ .attr({
+ 'offset': '100%',
+ 'stop-color': '#dedede'
+ });
+
+ // define the gradient for the processor stats background
+ var processorStatsBackground = defs.append('linearGradient')
+ .attr({
+ 'id': 'processor-stats-background',
+ 'x1': '0%',
+ 'y1': '100%',
+ 'x2': '0%',
+ 'y2': '0%'
+ });
+
+ processorStatsBackground.append('stop')
+ .attr({
+ 'offset': '0%',
+ 'stop-color': '#6f97ac'
+ });
+
+ processorStatsBackground.append('stop')
+ .attr({
+ 'offset': '100%',
+ 'stop-color': '#30505c'
+ });
+
+ // define the gradient for the port background
+ var portBackground = defs.append('linearGradient')
+ .attr({
+ 'id': 'port-background',
+ 'x1': '0%',
+ 'y1': '100%',
+ 'x2': '0%',
+ 'y2': '0%'
+ });
+
+ portBackground.append('stop')
+ .attr({
+ 'offset': '0%',
+ 'stop-color': '#aaaaaa'
+ });
+
+ portBackground.append('stop')
+ .attr({
+ 'offset': '100%',
+ 'stop-color': '#ffffff'
+ });
+
+ // define the gradient for the expiration icon
+ var expirationBackground = defs.append('linearGradient')
+ .attr({
+ 'id': 'expiration',
+ 'x1': '0%',
+ 'y1': '0%',
+ 'x2': '0%',
+ 'y2': '100%'
+ });
+
+ expirationBackground.append('stop')
+ .attr({
+ 'offset': '0%',
+ 'stop-color': '#aeafb1'
+ });
+
+ expirationBackground.append('stop')
+ .attr({
+ 'offset': '100%',
+ 'stop-color': '#87888a'
+ });
+
+ // create the canvas element
+ canvas = svg.append('g')
+ .attr({
+ 'transform': 'translate(' + TRANSLATE + ') scale(' + SCALE + ')',
+ 'pointer-events': 'all',
+ 'id': 'canvas'
+ });
+
+ // handle canvas events
+ svg.on('mousedown.selection', function () {
+ canvasClicked = true;
+
+ if (d3.event.button !== 0) {
+ // prevent further propagation (to parents and others handlers
+ // on the same element to prevent zoom behavior)
+ d3.event.stopImmediatePropagation();
+ return;
+ }
+
+ // show selection box if shift is held down
+ if (d3.event.shiftKey) {
+ var position = d3.mouse(canvas.node());
+ canvas.append('rect')
+ .attr('rx', 6)
+ .attr('ry', 6)
+ .attr('x', position[0])
+ .attr('y', position[1])
+ .attr('class', 'selection')
+ .attr('width', 0)
+ .attr('height', 0)
+ .attr('stroke-width', function () {
+ return 1 / nf.Canvas.View.scale();
+ })
+ .attr('stroke-dasharray', function () {
+ return 4 / nf.Canvas.View.scale();
+ })
+ .datum(position);
+
+ // prevent further propagation (to parents and others handlers
+ // on the same element to prevent zoom behavior)
+ d3.event.stopImmediatePropagation();
+
+ // prevents the browser from changing to a text selection cursor
+ d3.event.preventDefault();
+ }
+ })
+ .on('mousemove.selection', function () {
+ // update selection box if shift is held down
+ if (d3.event.shiftKey) {
+ // get the selection box
+ var selectionBox = d3.select('rect.selection');
+ if (!selectionBox.empty()) {
+ // get the original position
+ var originalPosition = selectionBox.datum();
+ var position = d3.mouse(canvas.node());
+
+ var d = {};
+ if (originalPosition[0] < position[0]) {
+ d.x = originalPosition[0];
+ d.width = position[0] - originalPosition[0];
+ } else {
+ d.x = position[0];
+ d.width = originalPosition[0] - position[0];
+ }
+
+ if (originalPosition[1] < position[1]) {
+ d.y = originalPosition[1];
+ d.height = position[1] - originalPosition[1];
+ } else {
+ d.y = position[1];
+ d.height = originalPosition[1] - position[1];
+ }
+
+ // update the selection box
+ selectionBox.attr(d);
+ }
+
+ // prevent further propagation (to parents)
+ d3.event.stopPropagation();
+ }
+ })
+ .on('mouseup.selection', function () {
+ // ensure this originated from clicking the canvas, not a component.
+ // when clicking on a component, the event propagation is stopped so
+ // it never reaches the canvas. we cannot do this however on up events
+ // since the drag events break down
+ if (canvasClicked === false) {
+ return;
+ }
+
+ // reset the canvas click flag
+ canvasClicked = false;
+
+ // get the selection box
+ var selectionBox = d3.select('rect.selection');
+ if (!selectionBox.empty()) {
+ var selectionBoundingBox = {
+ x: parseInt(selectionBox.attr('x'), 10),
+ y: parseInt(selectionBox.attr('y'), 10),
+ width: parseInt(selectionBox.attr('width'), 10),
+ height: parseInt(selectionBox.attr('height'), 10)
+ };
+
+ // see if a component should be selected or not
+ d3.selectAll('g.component').classed('selected', function (d) {
+ // consider it selected if its already selected or enclosed in the bounding box
+ return d3.select(this).classed('selected') ||
+ d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
+ d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
+ });
+
+ // see if a connection should be selected or not
+ d3.selectAll('g.connection').classed('selected', function (d) {
+ // consider all points
+ var points = [d.start].concat(d.bends, [d.end]);
+
+ // determine the bounding box
+ var x = d3.extent(points, function (pt) {
+ return pt.x;
+ });
+ var y = d3.extent(points, function (pt) {
+ return pt.y;
+ });
+
+ // consider it selected if its already selected or enclosed in the bounding box
+ return d3.select(this).classed('selected') ||
+ x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
+ y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height);
+ });
+
+ // remove the selection box
+ selectionBox.remove();
+ } else if (panning === false) {
+ // deselect as necessary if we are not panning
+ nf.CanvasUtils.getSelection().classed('selected', false);
+ }
+
+ // update the toolbar
+ nf.CanvasToolbar.refresh();
+ });
+
+ // define a function for update the graph dimensions
+ var updateGraphSize = function () {
+ // get the location of the bottom of the graph
+ var footer = $('#banner-footer');
+ var bottom = 0;
+ if (footer.is(':visible')) {
+ bottom = footer.height();
+ }
+
+ // calculate size
+ var top = parseInt(canvasContainer.css('top'), 10);
+ var windowHeight = $(window).height();
+ var canvasHeight = (windowHeight - (bottom + top));
+
+ // canvas/svg
+ canvasContainer.css({
+ 'height': canvasHeight + 'px',
+ 'bottom': bottom + 'px'
+ });
+ svg.attr({
+ 'height': canvasContainer.height(),
+ 'width': canvasContainer.width()
+ });
+
+ // body
+ $('#canvas-body').css({
+ 'height': windowHeight + 'px',
+ 'width': $(window).width() + 'px'
+ });
+ };
+
+ // listen for browser resize events to reset the graph size
+ $(window).on('resize', function () {
+ updateGraphSize();
++ nf.Settings.resetTableSize();
+ }).on('keydown', function (evt) {
+ // if a dialog is open, disable canvas shortcuts
+ if ($('.dialog').is(':visible')) {
+ return;
+ }
+
+ if (evt.ctrlKey || evt.metaKey) {
+ if (evt.keyCode === 82) {
+ // ctrl-r
+ nf.Actions.reloadStatus();
+
+ evt.preventDefault();
+ } else if (evt.keyCode === 65) {
+ // ctrl-a
+ nf.Actions.selectAll();
+ nf.CanvasToolbar.refresh();
+
+ evt.preventDefault();
+ } else if (evt.keyCode === 67) {
+ // ctrl-c
+ nf.Actions.copy(nf.CanvasUtils.getSelection());
+
+ evt.preventDefault();
+ } else if (evt.keyCode === 86) {
+ // ctrl-p
+ nf.Actions.paste();
+
+ evt.preventDefault();
+ }
+ } else {
+ if (evt.keyCode === 46) {
+ // delete
+ nf.Actions['delete'](nf.CanvasUtils.getSelection());
+
+ evt.preventDefault();
+ } else if (evt.keyCode === 27) {
+ // esc
+ nf.Actions.hideDialogs();
+
+ evt.preventDefault();
+ }
+ }
+ });
+
+ // get the banners and update the page accordingly
+ $.ajax({
+ type: 'GET',
+ url: config.urls.banners,
+ dataType: 'json'
+ }).done(function (response) {
+ // ensure the banners response is specified
+ if (nf.Common.isDefinedAndNotNull(response.banners)) {
+ if (nf.Common.isDefinedAndNotNull(response.banners.headerText) && response.banners.headerText !== '') {
+ // update the header text
+ $('#banner-header').addClass('banner-header-background').text(response.banners.headerText);
+ }
+
+ if (nf.Common.isDefinedAndNotNull(response.banners.footerText) && response.banners.footerText !== '') {
+ // update the footer text and show it
+ var bannerFooter = $('#banner-footer').text(response.banners.footerText).show();
+
+ var updateBottom = function (elementId) {
+ var element = $('#' + elementId);
+ element.css('bottom', parseInt(bannerFooter.css('height'), 10) + 'px');
+ };
+
+ // update the position of elements affected by bottom banners
+ updateBottom('graph');
+ }
+ }
+
+ // update the graph dimensions
+ updateGraphSize();
+ }).fail(nf.Common.handleAjaxError);
+ };
+
+ /**
+ * Sets the colors for the specified type.
+ *
+ * @param {array} colors The possible colors
+ * @param {string} type The component type for these colors
+ */
+ var setColors = function (colors, type) {
+ var defs = d3.select('defs');
+
+ // update processors
+ var processorSelection = defs.selectAll('linearGradient.' + type + '-background').data(colors, function (d) {
+ return d;
+ });
+
+ // define the gradient for the processor background
+ var gradient = processorSelection.enter().append('linearGradient')
+ .attr({
+ 'id': function (d) {
+ return type + '-background-' + d;
+ },
+ 'class': type + '-background',
+ 'x1': '0%',
+ 'y1': '100%',
+ 'x2': '0%',
+ 'y2': '0%'
+ });
+
+ gradient.append('stop')
+ .attr({
+ 'offset': '0%',
+ 'stop-color': function (d) {
+ return '#' + d;
+ }
+ });
+
+ gradient.append('stop')
+ .attr({
+ 'offset': '100%',
+ 'stop-color': '#ffffff'
+ });
+
+ // remove old processor colors
+ processorSelection.exit().remove();
+ };
+
+ /**
+ * Reloads the current status of this flow.
+ */
+ var reloadFlowStatus = function () {
+ return $.ajax({
+ type: 'GET',
+ url: config.urls.status,
+ dataType: 'json'
+ }).done(function (response) {
+ // report the updated status
+ if (nf.Common.isDefinedAndNotNull(response.controllerStatus)) {
+ var controllerStatus = response.controllerStatus;
+
+ // update the report values
+ $('#active-thread-count').text(controllerStatus.activeThreadCount);
+ $('#total-queued').text(controllerStatus.queued);
+
+ // update the connected nodes if applicable
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.connectedNodes)) {
+ var connectedNodes = controllerStatus.connectedNodes.split(' / ');
+ if (connectedNodes.length === 2 && connectedNodes[0] !== connectedNodes[1]) {
+ $('#connected-nodes-count').addClass('alert');
+ } else {
+ $('#connected-nodes-count').removeClass('alert');
+ }
+
+ // set the connected nodes
+ $('#connected-nodes-count').text(controllerStatus.connectedNodes);
+ }
+
+ // update the component counts
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.activeRemotePortCount)) {
+ $('#controller-transmitting-count').text(controllerStatus.activeRemotePortCount);
+ } else {
+ $('#controller-transmitting-count').text('-');
+ }
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.inactiveRemotePortCount)) {
+ $('#controller-not-transmitting-count').text(controllerStatus.inactiveRemotePortCount);
+ } else {
+ $('#controller-not-transmitting-count').text('-');
+ }
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.runningCount)) {
+ $('#controller-running-count').text(controllerStatus.runningCount);
+ } else {
+ $('#controller-running-count').text('-');
+ }
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.stoppedCount)) {
+ $('#controller-stopped-count').text(controllerStatus.stoppedCount);
+ } else {
+ $('#controller-stopped-count').text('-');
+ }
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.invalidCount)) {
+ $('#controller-invalid-count').text(controllerStatus.invalidCount);
+ } else {
+ $('#controller-invalid-count').text('-');
+ }
+ if (nf.Common.isDefinedAndNotNull(controllerStatus.disabledCount)) {
+ $('#controller-disabled-count').text(controllerStatus.disabledCount);
+ } else {
+ $('#controller-disabled-count').text('-');
+ }
+
+ // icon for system bulletins
+ var bulletinIcon = $('#controller-bulletins');
+ var currentBulletins = bulletinIcon.data('bulletins');
+
+ // update the bulletins if necessary
+ if (nf.Common.doBulletinsDiffer(currentBulletins, controllerStatus.bulletins)) {
+ bulletinIcon.data('bulletins', controllerStatus.bulletins);
+
+ // get the formatted the bulletins
+ var bulletins = nf.Common.getFormattedBulletins(controllerStatus.bulletins);
+
+ // bulletins for this processor are now gone
+ if (bulletins.length === 0) {
+ if (bulletinIcon.data('qtip')) {
+ bulletinIcon.removeClass('has-bulletins').qtip('destroy');
+ }
+
+ // hide the icon
+ bulletinIcon.hide();
+ } else {
+ var newBulletins = nf.Common.formatUnorderedList(bulletins);
+
+ // different bulletins, refresh
+ if (bulletinIcon.data('qtip')) {
+ bulletinIcon.qtip('option', 'content.text', newBulletins);
+ } else {
+ // no bulletins before, show icon and tips
+ bulletinIcon.addClass('has-bulletins').qtip($.extend({
+ content: newBulletins
+ }, nf.CanvasUtils.config.systemTooltipConfig));
+ }
+
+ // show the icon
+ bulletinIcon.show();
+ }
+ }
+
+ // handle any pending user request
+ if (controllerStatus.hasPendingAccounts === true) {
+ $('#has-pending-accounts').show();
+ } else {
+ $('#has-pending-accounts').hide();
+ }
+ }
+ }).fail(nf.Common.handleAjaxError);
+ };
+
+ /**
+ * Refreshes the graph.
+ *
+ * @argument {string} processGroupId The process group id
+ */
+ var reloadProcessGroup = function (processGroupId) {
+ // load the controller
+ return $.ajax({
+ type: 'GET',
+ url: config.urls.controller + '/process-groups/' + encodeURIComponent(processGroupId),
+ data: {
+ verbose: true
+ },
+ dataType: 'json'
+ }).done(function (processGroupResponse) {
+ // set the revision
+ nf.Client.setRevision(processGroupResponse.revision);
+
+ // get the controller and its contents
+ var processGroup = processGroupResponse.processGroup;
+
+ // set the group details
+ nf.Canvas.setGroupId(processGroup.id);
+ nf.Canvas.setGroupName(processGroup.name);
+
+ // update the breadcrumbs
+ $('#data-flow-title-container').empty();
+ generateBreadcrumbs(processGroup);
+
+ // set the parent id if applicable
+ if (nf.Common.isDefinedAndNotNull(processGroup.parent)) {
+ nf.Canvas.setParentGroupId(processGroup.parent.id);
+ } else {
+ nf.Canvas.setParentGroupId(null);
+ }
+
+ // since we're getting a new group, we want to clear it
+ nf.Graph.removeAll();
+
+ // refresh the graph
+ nf.Graph.add(processGroup.contents, false);
+
+ // update the toolbar
+ nf.CanvasToolbar.refresh();
+ }).fail(nf.Common.handleAjaxError);
+ };
+
+ /**
+ * Refreshes the status for the resources that exist in the specified process group.
+ *
+ * @argument {string} processGroupId The id of the process group
+ */
+ var reloadStatus = function (processGroupId) {
+ // get the stats
+ return $.Deferred(function (deferred) {
+ $.ajax({
+ type: 'GET',
+ url: config.urls.controller + '/process-groups/' + encodeURIComponent(processGroupId) + '/status',
+ data: {
+ recursive: false
+ },
+ dataType: 'json'
+ }).done(function (response) {
+ // report the updated stats
+ if (nf.Common.isDefinedAndNotNull(response.processGroupStatus)) {
+ var processGroupStatus = response.processGroupStatus;
+
+ // update all the stats
+ nf.Graph.setStatus(processGroupStatus);
+
+ // update the timestamp
+ $('#stats-last-refreshed').text(processGroupStatus.statsLastRefreshed);
+ }
+ deferred.resolve();
+ }).fail(function (xhr, status, error) {
+ // if clustered, a 404 likely means the flow status at the ncm is stale
+ if (!nf.Canvas.isClustered() || xhr.status !== 404) {
+ nf.Common.handleAjaxError(xhr, status, error);
+ deferred.reject();
+ } else {
+ deferred.resolve();
+ }
+ });
+ }).promise();
+ };
+
+ return {
+
+ CANVAS_OFFSET: 0,
+
+ /**
+ * Determines if the current broswer supports SVG.
+ */
+ SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
+
+ /**
+ * Hides the splash that is displayed while the application is loading.
+ */
+ hideSplash: function () {
+ $('#splash').fadeOut();
+ },
+
+ /**
+ * Stop polling for revision.
+ */
+ stopRevisionPolling: function () {
+ // set polling flag
+ revisionPolling = false;
+ },
+
+ /**
+ * Remove the status poller.
+ */
+ stopStatusPolling: function () {
+ // set polling flag
+ statusPolling = false;
+ },
+
+ /**
+ * Reloads the flow from the server based on the currently specified group id.
+ * To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload.
+ */
+ reload: function () {
+ return $.Deferred(function (deferred) {
+ // hide the context menu
+ nf.ContextMenu.hide();
+
+ // get the process group to refresh everything
+ var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
+ var statusXhr = reloadFlowStatus();
+ $.when(processGroupXhr, statusXhr).done(function (processGroupResult) {
+ // adjust breadcrumbs if necessary
+ var title = $('#data-flow-title-container');
+ var titlePosition = title.position();
+ var titleWidth = title.outerWidth();
+ var titleRight = titlePosition.left + titleWidth;
+
+ var padding = $('#breadcrumbs-right-border').width();
+ var viewport = $('#data-flow-title-viewport');
+ var viewportWidth = viewport.width();
+ var viewportRight = viewportWidth - padding;
+
+ // if the title's right is past the viewport's right, shift accordingly
+ if (titleRight > viewportRight) {
+ // adjust the position
+ title.css('left', (titlePosition.left - (titleRight - viewportRight)) + 'px');
+ } else {
+ title.css('left', '10px');
+ }
+
+ // don't load the status until the graph is loaded
+ reloadStatus(nf.Canvas.getGroupId()).done(function () {
+ deferred.resolve(processGroupResult);
+ }).fail(function () {
+ deferred.reject();
+ });
+ });
+ }).promise();
+ },
+
+ /**
+ * Reloads the status.
+ */
+ reloadStatus: function () {
+ return $.Deferred(function (deferred) {
+ // refresh the status and check any bulletins
+ $.when(reloadStatus(nf.Canvas.getGroupId()), reloadFlowStatus()).done(function () {
+ deferred.resolve();
+ }).fail(function () {
+ deferred.reject();
+ });
+ }).promise();
+ },
+
+ /**
+ * Initialize NiFi.
+ */
+ init: function () {
+ // init the registration form before performing the first query since
+ // the response could lead to a registration attempt
+ nf.Registration.init();
+
+ // get the controller config to register the status poller
+ var configXhr = $.ajax({
+ type: 'GET',
+ url: config.urls.controllerConfig,
+ dataType: 'json'
+ });
+
+ // create the deferred cluster request
+ var isClusteredRequest = $.Deferred(function (deferred) {
+ $.ajax({
+ type: 'HEAD',
+ url: config.urls.cluster
+ }).done(function (response, status, xhr) {
+ clustered = true;
+ deferred.resolve(response, status, xhr);
+ }).fail(function (xhr, status, error) {
+ if (xhr.status === 404) {
+ clustered = false;
+ deferred.resolve('', 'success', xhr);
+ } else {
+ deferred.reject(xhr, status, error);
+ }
+ });
+ }).promise();
+
+ // load the authorities
+ var authoritiesXhr = $.ajax({
+ type: 'GET',
+ url: config.urls.authorities,
+ dataType: 'json'
+ });
+
+ // ensure the authorities and config request is processed first
+ $.when(authoritiesXhr, configXhr).done(function (authoritiesResult, configResult) {
+ var authoritiesResponse = authoritiesResult[0];
+ var configResponse = configResult[0];
+
+ // set the user's authorities
+ nf.Common.setAuthorities(authoritiesResponse.authorities);
+
+ // calculate the canvas offset
+ var canvasContainer = $('#canvas-container');
+ nf.Canvas.CANVAS_OFFSET = canvasContainer.offset().top;
+
+ // get the config details
+ var configDetails = configResponse.config;
+
+ // when both request complete, load the application
+ isClusteredRequest.done(function () {
+ // get the auto refresh interval
+ var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
+
+ // initialize whether site to site is secure
+ secureSiteToSite = configDetails.siteToSiteSecure;
+
+ // load d3
+ loadD3().done(function () {
+ nf.Storage.init();
+
+ // initialize the application
+ initCanvas();
+ nf.Canvas.View.init();
+ nf.ContextMenu.init();
+ nf.CanvasHeader.init();
+ nf.CanvasToolbox.init();
+ nf.CanvasToolbar.init();
+ nf.GraphControl.init();
+ nf.Search.init();
+ nf.Settings.init();
+
+ // initialize the component behaviors
+ nf.Draggable.init();
+ nf.Selectable.init();
+ nf.Connectable.init();
+
+ // initialize the chart
+ nf.StatusHistory.init(configDetails.timeOffset);
+
+ // initialize the birdseye
+ nf.Birdseye.init();
+
+ // initialize components
+ nf.ConnectionConfiguration.init();
+ nf.ProcessorConfiguration.init();
+ nf.ProcessGroupConfiguration.init();
+ nf.RemoteProcessGroupConfiguration.init();
+ nf.RemoteProcessGroupPorts.init();
+ nf.PortConfiguration.init();
+ nf.SecurePortConfiguration.init();
+ nf.LabelConfiguration.init();
+ nf.ProcessorDetails.init();
+ nf.ProcessGroupDetails.init();
+ nf.PortDetails.init();
+ nf.SecurePortDetails.init();
+ nf.ConnectionDetails.init();
+ nf.RemoteProcessGroupDetails.init();
+ nf.GoTo.init();
+ nf.Graph.init().done(function () {
+ // determine the split between the polling
+ var pollingSplit = autoRefreshIntervalSeconds / 2;
+
+ // register the revision and status polling
+ startRevisionPolling(autoRefreshIntervalSeconds);
+ setTimeout(function () {
+ startStatusPolling(autoRefreshIntervalSeconds);
+ }, pollingSplit * 1000);
+
+ // hide the splash screen
+ nf.Canvas.hideSplash();
+ }).fail(nf.Common.handleAjaxError);
+ }).fail(nf.Common.handleAjaxError);
+ }).fail(nf.Common.handleAjaxError);
+ }).fail(nf.Common.handleAjaxError);
+ },
+
+ /**
+ * Defines the gradient colors used to render processors.
+ *
+ * @param {array} colors The colors
+ */
+ defineProcessorColors: function (colors) {
+ setColors(colors, 'processor');
+ },
+
+ /**
+ * Defines the gradient colors used to render label.
+ *
+ * @param {array} colors The colors
+ */
+ defineLabelColors: function (colors) {
+ setColors(colors, 'label');
+ },
+
+ /**
+ * Return whether this instance of NiFi is clustered.
+ *
+ * @returns {Boolean}
+ */
+ isClustered: function () {
+ return clustered === true;
+ },
+
+ /**
+ * Returns whether site to site communications is secure.
+ */
+ isSecureSiteToSite: function () {
+ return secureSiteToSite;
+ },
+
+ /**
+ * Set the group id.
+ *
+ * @argument {string} gi The group id
+ */
+ setGroupId: function (gi) {
+ groupId = gi;
+ },
+
+ /**
+ * Get the group id.
+ */
+ getGroupId: function () {
+ return groupId;
+ },
+
+ /**
+ * Set the group name.
+ *
+ * @argument {string} gn The group name
+ */
+ setGroupName: function (gn) {
+ groupName = gn;
+ },
+
+ /**
+ * Get the group name.
+ */
+ getGroupName: function () {
+ return groupName;
+ },
+
+ /**
+ * Set the parent group id.
+ *
+ * @argument {string} pgi The id of the parent group
+ */
+ setParentGroupId: function (pgi) {
+ parentGroupId = pgi;
+ },
+
+ /**
+ * Get the parent group id.
+ */
+ getParentGroupId: function () {
+ return parentGroupId;
+ },
+
+ View: (function () {
+
+ /**
+ * Updates component visibility based on their proximity to the screen's viewport.
+ */
+ var updateComponentVisibility = function () {
+ var canvasContainer = $('#canvas-container');
+ var translate = nf.Canvas.View.translate();
+ var scale = nf.Canvas.View.scale();
+
+ // scale the translation
+ translate = [translate[0] / scale, translate[1] / scale];
+
+ // get the normalized screen width and height
+ var screenWidth = canvasContainer.width() / scale;
+ var screenHeight = canvasContainer.height() / scale;
+
+ // calculate the screen bounds one screens worth in each direction
+ var screenLeft = -translate[0] - screenWidth;
+ var screenTop = -translate[1] - screenHeight;
+ var screenRight = screenLeft + (screenWidth * 3);
+ var screenBottom = screenTop + (screenHeight * 3);
+
+ // detects whether a component is visible and should be rendered
+ var isComponentVisible = function (d) {
+ if (!nf.Canvas.View.shouldRenderPerScale()) {
+ return false;
+ }
+
+ var left = d.component.position.x;
+ var top = d.component.position.y;
+ var right = left + d.dimensions.width;
+ var bottom = top + d.dimensions.height;
+
+ // determine if the component is now visible
+ return screenLeft < right && screenRight > left && screenTop < bottom && screenBottom > top;
+ };
+
+ // detects whether a connection is visible and should be rendered
+ var isConnectionVisible = function (d) {
+ if (!nf.Canvas.View.shouldRenderPerScale()) {
+ return false;
+ }
+
+ var x, y;
+ if (d.bends.length > 0) {
+ var i = Math.min(Math.max(0, d.labelIndex), d.bends.length - 1);
+ x = d.bends[i].x;
+ y = d.bends[i].y;
+ } else {
+ x = (d.start.x + d.end.x) / 2;
+ y = (d.start.y + d.end.y) / 2;
+ }
+
+ return screenLeft < x && screenRight > x && screenTop < y && screenBottom > y;
+ };
+
+ // marks the specific component as visible and determines if its entering or leaving visibility
+ var updateVisibility = function (d, isVisible) {
+ var selection = d3.select('#id-' + d.component.id);
+ var visible = isVisible(d);
+ var wasVisible = selection.classed('visible');
+
+ // mark the selection as appropriate
+ selection.classed('visible', visible)
+ .classed('entering', function () {
+ return visible && !wasVisible;
+ }).classed('leaving', function () {
+ return !visible && wasVisible;
+ });
+ };
+
+ // get the all components
+ var graph = nf.Graph.get();
+
+ // update the visibility for each component
+ $.each(graph.processors, function (_, d) {
+ updateVisibility(d, isComponentVisible);
+ });
+ $.each(graph.ports, function (_, d) {
+ updateVisibility(d, isComponentVisible);
+ });
+ $.each(graph.processGroups, function (_, d) {
+ updateVisibility(d, isComponentVisible);
+ });
+ $.each(graph.remoteProcessGroups, function (_, d) {
+ updateVisibility(d, isComponentVisible);
+ });
+ $.each(graph.connections, function (_, d) {
+ updateVisibility(d, isConnectionVisible);
+ });
+ };
+
+ // initialize the zoom behavior
+ var behavior;
+
+ return {
+ init: function () {
+ var refreshed;
+ var zoomed = false;
+
+ // define the behavior
+ behavior = d3.behavior.zoom()
+ .scaleExtent([MIN_SCALE, MAX_SCALE])
+ .translate(TRANSLATE)
+ .scale(SCALE)
+ .on('zoomstart', function () {
+ // hide the context menu
+ nf.ContextMenu.hide();
+ })
+ .on('zoom', function () {
+ // if we have zoomed, indicate that we are panning
+ // to prevent deselection elsewhere
+ if (zoomed) {
+ panning = true;
+ } else {
+ zoomed = true;
+ }
+
+ // see if the scale has changed during this zoom event,
+ // we want to only transition when zooming in/out as running
+ // the transitions during pan events is
+ var transition = d3.event.sourceEvent.type === 'wheel' || d3.event.sourceEvent.type === 'mousewheel';
+
+ // refresh the canvas
+ refreshed = nf.Canvas.View.refresh({
+ persist: false,
+ transition: transition,
+ refreshComponents: false,
+ refreshBirdseye: false
+ });
+ })
+ .on('zoomend', function () {
+ // ensure the canvas was actually refreshed
+ if (nf.Common.isDefinedAndNotNull(refreshed)) {
+ nf.Canvas.View.updateVisibility();
+
+ // refresh the birdseye
+ refreshed.done(function () {
+ nf.Birdseye.refresh();
+ });
+
+ // persist the users view
+ nf.CanvasUtils.persistUserView();
+
+ // reset the refreshed deferred
+ refreshed = null;
+ }
+
+ panning = false;
+ zoomed = false;
+ });
+
+ // add the behavior to the canvas and disable dbl click zoom
+ svg.call(behavior).on('dblclick.zoom', null);
+ },
+
+ /**
+ * Whether or not a component should be rendered based solely on the current scale.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderPerScale: function () {
+ return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER;
+ },
+
+ /**
+ * Updates component visibility based on the current translation/scale.
+ */
+ updateVisibility: function () {
+ updateComponentVisibility();
+ nf.Graph.pan();
+ },
+
+ /**
+ * Sets/gets the current translation.
+ *
+ * @param {array} translate [x, y]
+ */
+ translate: function (translate) {
+ if (nf.Common.isUndefined(translate)) {
+ return behavior.translate();
+ } else {
+ behavior.translate(translate);
+ }
+ },
+
+ /**
+ * Sets/gets the current scale.
+ *
+ * @param {number} scale The new scale
+ */
+ scale: function (scale) {
+ if (nf.Common.isUndefined(scale)) {
+ return behavior.scale();
+ } else {
+ behavior.scale(scale);
+ }
+ },
+
+ /**
+ * Zooms in a single zoom increment.
+ */
+ zoomIn: function () {
+ var translate = nf.Canvas.View.translate();
+ var scale = nf.Canvas.View.scale();
+ var newScale = Math.min(scale * INCREMENT, MAX_SCALE);
+
+ // get the canvas normalized width and height
+ var canvasContainer = $('#canvas-container');
+ var screenWidth = canvasContainer.width() / scale;
+ var screenHeight = canvasContainer.height() / scale;
+
+ // adjust the scale
+ nf.Canvas.View.scale(newScale);
+
+ // center around the center of the screen accounting for the translation accordingly
+ nf.CanvasUtils.centerBoundingBox({
+ x: (screenWidth / 2) - (translate[0] / scale),
+ y: (screenHeight / 2) - (translate[1] / scale),
+ width: 1,
+ height: 1
+ });
+ },
+
+ /**
+ * Zooms out a single zoom increment.
+ */
+ zoomOut: function () {
+ var translate = nf.Canvas.View.translate();
+ var scale = nf.Canvas.View.scale();
+ var newScale = Math.max(scale / INCREMENT, MIN_SCALE);
+
+ // get the canvas normalized width and height
+ var canvasContainer = $('#canvas-container');
+ var screenWidth = canvasContainer.width() / scale;
+ var screenHeight = canvasContainer.height() / scale;
+
+ // adjust the scale
+ nf.Canvas.View.scale(newScale);
+
+ // center around the center of the screen accounting for the translation accordingly
+ nf.CanvasUtils.centerBoundingBox({
+ x: (screenWidth / 2) - (translate[0] / scale),
+ y: (screenHeight / 2) - (translate[1] / scale),
+ width: 1,
+ height: 1
+ });
+ },
+
+ /**
+ * Zooms to fit the entire graph on the canvas.
+ */
+ fit: function () {
+ var translate = nf.Canvas.View.translate();
+ var scale = nf.Canvas.View.scale();
+ var newScale;
+
+ // get the canvas normalized width and height
+ var canvasContainer = $('#canvas-container');
+ var canvasWidth = canvasContainer.width();
+ var canvasHeight = canvasContainer.height();
+
+ // get the bounding box for the graph
+ var graphBox = d3.select('#canvas').node().getBoundingClientRect();
+ var graphWidth = graphBox.width / scale;
+ var graphHeight = graphBox.height / scale;
+ var graphLeft = graphBox.left / scale;
+ var graphTop = (graphBox.top - nf.Canvas.CANVAS_OFFSET) / scale;
+
+
+ // adjust the scale to ensure the entire graph is visible
+ if (graphWidth > canvasWidth || graphHeight > canvasHeight) {
+ newScale = Math.min(canvasWidth / graphWidth, canvasHeight / graphHeight);
+
+ // ensure the scale is within bounds
+ newScale = Math.min(Math.max(newScale, MIN_SCALE), MAX_SCALE);
+ } else {
+ newScale = 1;
+
+ // since the entire graph will fit on the canvas, offset origin appropriately
+ graphLeft -= 100;
+ graphTop -= 50;
+ }
+
+ // set the new scale
+ nf.Canvas.View.scale(newScale);
+
+ // center as appropriate
+ nf.CanvasUtils.centerBoundingBox({
+ x: graphLeft - (translate[0] / scale),
+ y: graphTop - (translate[1] / scale),
+ width: canvasWidth / newScale,
+ height: canvasHeight / newScale
+ });
+ },
+
+ /**
+ * Zooms to the actual size (1 to 1).
+ */
+ actualSize: function () {
+ var translate = nf.Canvas.View.translate();
+ var scale = nf.Canvas.View.scale();
+
+ // get the first selected component
+ var selection = nf.CanvasUtils.getSelection();
+
+ // set the updated scale
+ nf.Canvas.View.scale(1);
+
+ // box to zoom towards
+ var box;
+
+ // if components have been selected position the view accordingly
+ if (!selection.empty()) {
+ // gets the data for the first component
+ var selectionBox = selection.node().getBoundingClientRect();
+
+ // get the bounding box for the selected components
+ box = {
+ x: (selectionBox.left / scale) - (translate[0] / scale),
+ y: ((selectionBox.top - nf.Canvas.CANVAS_OFFSET) / scale) - (translate[1] / scale),
+ width: selectionBox.width / scale,
+ height: selectionBox.height / scale
+ };
+ } else {
+ // get the offset
+ var canvasContainer = $('#canvas-container');
+
+ // get the canvas normalized width and height
+ var screenWidth = canvasContainer.width() / scale;
+ var screenHeight = canvasContainer.height() / scale;
+
+ // center around the center of the screen accounting for the translation accordingly
+ box = {
+ x: (screenWidth / 2) - (translate[0] / scale),
+ y: (screenHeight / 2) - (translate[1] / scale),
+ width: 1,
+ height: 1
+ };
+ }
+
+ // center as appropriate
+ nf.CanvasUtils.centerBoundingBox(box);
+ },
+
+ /**
+ * Refreshes the view based on the configured translation and scale.
+ *
+ * @param {object} options Options for the refresh operation
+ */
+ refresh: function (options) {
+ return $.Deferred(function (deferred) {
+
+ var persist = true;
+ var transition = false;
+ var refreshComponents = true;
+ var refreshBirdseye = true;
+
+ // extract the options if specified
+ if (nf.Common.isDefinedAndNotNull(options)) {
+ persist = nf.Common.isDefinedAndNotNull(options.persist) ? options.persist : persist;
+ transition = nf.Common.isDefinedAndNotNull(options.transition) ? options.transition : transition;
+ refreshComponents = nf.Common.isDefinedAndNotNull(options.refreshComponents) ? options.refreshComponents : refreshComponents;
+ refreshBirdseye = nf.Common.isDefinedAndNotNull(options.refreshBirdseye) ? options.refreshBirdseye : refreshBirdseye;
+ }
+
+ // update component visibility
+ if (refreshComponents) {
+ nf.Canvas.View.updateVisibility();
+ }
+
+ // persist if appropriate
+ if (persist === true) {
+ nf.CanvasUtils.persistUserView();
+ }
+
+ // update the canvas
+ if (transition === true) {
+ canvas.transition()
+ .duration(500)
+ .attr('transform', function () {
+ return 'translate(' + behavior.translate() + ') scale(' + behavior.scale() + ')';
+ })
+ .each('end', function () {
+ // refresh birdseye if appropriate
+ if (refreshBirdseye === true) {
+ nf.Birdseye.refresh();
+ }
+
+ deferred.resolve();
+ });
+ } else {
+ canvas.attr('transform', function () {
+ return 'translate(' + behavior.translate() + ') scale(' + behavior.scale() + ')';
+ });
+
+ // refresh birdseye if appropriate
+ if (refreshBirdseye === true) {
+ nf.Birdseye.refresh();
+ }
+
+ deferred.resolve();
+ }
+ }).promise();
+ }
+ };
+ }())
+ };
+ }());
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/c5d452c1/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
----------------------------------------------------------------------
diff --cc nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
index 0000000,ec64677..4fbcee7
mode 000000,100644..100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
@@@ -1,0 -1,68 +1,68 @@@
+ /*
+ * 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.
+ */
+ nf.Registration = (function () {
+
+ var config = {
+ urls: {
+ users: '../nifi-api/controller/users'
+ }
+ };
+
+ return {
+ /**
+ * Initializes the user account registration form.
+ */
+ init: function () {
+ $('#registration-justification').count({
+ charCountField: '#remaining-characters'
+ });
+
+ // register a click listener to expand/collapse the registration form
+ $('#expand-registration-button, #expand-registration-text').click(function () {
+ var registrationForm = $('#registration-form');
+ if (registrationForm.is(':visible')) {
- $('#expand-registration-button').removeClass('registration-expanded').addClass('registration-collapsed');
++ $('#expand-registration-button').removeClass('registration-expanded').addClass('collapsed');
+ } else {
- $('#expand-registration-button').removeClass('registration-collapsed').addClass('registration-expanded');
++ $('#expand-registration-button').removeClass('registration-collapsed').addClass('expanded');
+ }
+ registrationForm.toggle();
+ });
+
+ // register a click listener for submitting user account requests
+ $('#registration-form-submit').one('click', function () {
+ var justification = $('#registration-justification').val();
+
+ // attempt to create the user account registration
+ $.ajax({
+ type: 'POST',
+ url: config.urls.users,
+ data: {
+ 'justification': justification
+ }
+ }).done(function (response) {
+ // hide the registration pane
+ $('#registration-pane').hide();
+
+ // show the message pane
+ $('#message-pane').show();
+ $('#message-title').text('Thanks');
+ $('#message-content').text('Your request will be processed shortly.');
+ }).fail(nf.Common.handleAjaxError);
+ });
+ }
+ };
+ }());