From b15015ad854d9758b473fd6c64574bcf298ffae3 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 25 Jul 2018 12:23:35 -0400 Subject: [PATCH] v4.18.4 --- bower.json | 2 +- dist/angular-patternfly.js | 21427 ++++++++++++++++ dist/angular-patternfly.min.js | 8 + dist/imgs/canvas-dot-grid.png | Bin 0 -> 100 bytes dist/less/angular-patternfly.less | 19 + dist/less/canvas-editor.less | 201 + dist/less/canvas.less | 240 + dist/less/card.less | 161 + dist/less/charts.less | 319 + dist/less/datepicker.less | 120 + .../patternfly/color-variables.less | 85 + dist/less/filters.less | 110 + dist/less/list-view.less | 5 + dist/less/misc.less | 19 + dist/less/modal-overlay.less | 7 + dist/less/notification.less | 8 + dist/less/pagination.less | 6 + dist/less/sort.less | 12 + dist/less/table.less | 46 + dist/less/toolbars.less | 32 + dist/less/vertical-navigation.less | 39 + dist/less/views.less | 120 + dist/less/wizard.less | 30 + dist/sass/_angular-patternfly.scss | 16 + dist/sass/_canvas-editor.scss | 201 + dist/sass/_canvas.scss | 240 + dist/sass/_card.scss | 161 + dist/sass/_charts.scss | 319 + dist/sass/_datepicker.scss | 120 + dist/sass/_filters.scss | 110 + dist/sass/_list-view.scss | 5 + dist/sass/_misc.scss | 19 + dist/sass/_modal-overlay.scss | 7 + dist/sass/_notification.scss | 8 + dist/sass/_pagination.scss | 6 + dist/sass/_sort.scss | 12 + dist/sass/_table.scss | 46 + dist/sass/_toolbars.scss | 32 + dist/sass/_vertical-navigation.scss | 39 + dist/sass/_views.scss | 120 + dist/sass/_wizard.scss | 30 + dist/sass/angular-patternfly.css | 1449 ++ dist/styles/angular-patternfly.css | 1308 + dist/styles/angular-patternfly.min.css | 1 + package.json | 2 +- 45 files changed, 27265 insertions(+), 2 deletions(-) create mode 100644 dist/angular-patternfly.js create mode 100644 dist/angular-patternfly.min.js create mode 100644 dist/imgs/canvas-dot-grid.png create mode 100644 dist/less/angular-patternfly.less create mode 100644 dist/less/canvas-editor.less create mode 100644 dist/less/canvas.less create mode 100644 dist/less/card.less create mode 100644 dist/less/charts.less create mode 100644 dist/less/datepicker.less create mode 100644 dist/less/dependencies/patternfly/color-variables.less create mode 100644 dist/less/filters.less create mode 100644 dist/less/list-view.less create mode 100644 dist/less/misc.less create mode 100644 dist/less/modal-overlay.less create mode 100644 dist/less/notification.less create mode 100644 dist/less/pagination.less create mode 100644 dist/less/sort.less create mode 100644 dist/less/table.less create mode 100644 dist/less/toolbars.less create mode 100644 dist/less/vertical-navigation.less create mode 100644 dist/less/views.less create mode 100644 dist/less/wizard.less create mode 100644 dist/sass/_angular-patternfly.scss create mode 100644 dist/sass/_canvas-editor.scss create mode 100644 dist/sass/_canvas.scss create mode 100644 dist/sass/_card.scss create mode 100644 dist/sass/_charts.scss create mode 100644 dist/sass/_datepicker.scss create mode 100644 dist/sass/_filters.scss create mode 100644 dist/sass/_list-view.scss create mode 100644 dist/sass/_misc.scss create mode 100644 dist/sass/_modal-overlay.scss create mode 100644 dist/sass/_notification.scss create mode 100644 dist/sass/_pagination.scss create mode 100644 dist/sass/_sort.scss create mode 100644 dist/sass/_table.scss create mode 100644 dist/sass/_toolbars.scss create mode 100644 dist/sass/_vertical-navigation.scss create mode 100644 dist/sass/_views.scss create mode 100644 dist/sass/_wizard.scss create mode 100644 dist/sass/angular-patternfly.css create mode 100644 dist/styles/angular-patternfly.css create mode 100644 dist/styles/angular-patternfly.min.css diff --git a/bower.json b/bower.json index 78866a663..43a2f4394 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-patternfly", - "version": "0.0.0-semantically-released", + "version": "4.18.4", "authors": [ "Red Hat" ], diff --git a/dist/angular-patternfly.js b/dist/angular-patternfly.js new file mode 100644 index 000000000..25f44ddaf --- /dev/null +++ b/dist/angular-patternfly.js @@ -0,0 +1,21427 @@ +/** + * @name patternfly canvas + * + * @description + * Canvas module for patternfly. + * + */ +angular.module('patternfly.canvas', ['dragging', 'ngDragDrop', 'ui.bootstrap']); +;/** + * @name patternfly card + * + * @description + * Card module for patternfly. + * + */ +angular.module('patternfly.card', ['ui.bootstrap']); +;/** + * @name patternfly + * + * @description + * Charts module for patternfly. Must Include d3.js and c3.js to use + * + */ +angular.module('patternfly.charts', ['patternfly.utils', 'ui.bootstrap', 'ngSanitize']); + +;/** + * @name patternfly datepicker + * + * @description + * Datepicker module for patternfly. + * + */ +angular.module('patternfly.datepicker', ['ui.bootstrap']); + +;/** + * @name patternfly card + * + * @description + * Filters module for patternfly. + * + */ +angular.module('patternfly.filters', ['ui.bootstrap']); +;/** + * @name patternfly.form + * + * @description + * Module for formting related functionality, primarily filters. + */ +angular.module('patternfly.form', []); +;/** + * @name patternfly + * + * @description + * Modal module for patternfly. + * + */ +angular.module('patternfly.modals', ['ui.bootstrap.modal', 'ui.bootstrap.tpls', 'ngSanitize', 'ngAnimate']); +;/** + * @name patternfly navigation + * + * @description + * Navigation module for patternfly. + * + */ +angular.module('patternfly.navigation', ['ui.bootstrap']); +;/** + * @name patternfly notification + * + * @description + * Notification module for patternfly. + * + */ +angular.module('patternfly.notification', ['patternfly.utils', 'ui.bootstrap']); +;/** + * @name patternfly pagination + * + * @description + * Pagination module for patternfly. + * + */ +angular.module('patternfly.pagination', ['ui.bootstrap']) + .filter('startFrom', function () { + 'use strict'; + return function (input, start) { + start = parseInt(start, 10); + return input.slice(start); + }; + }); +;/** + * @name patternfly + * + * @description + * Base module for patternfly. + */ +angular.module('patternfly', [ + 'patternfly.autofocus', + 'patternfly.card', + 'patternfly.datepicker', + 'patternfly.filters', + 'patternfly.form', + 'patternfly.modals', + 'patternfly.navigation', + 'patternfly.notification', + 'patternfly.pagination', + 'patternfly.select', + 'patternfly.sort', + 'patternfly.toolbars', + 'patternfly.utils', + 'patternfly.validation', + 'patternfly.views', + 'patternfly.wizard' +]); + +;/** + * @name patternfly card + * + * @description + * Select module for patternfly. + * + */ +angular.module('patternfly.select', ['ui.bootstrap']); +;/** + * @name patternfly card + * + * @description + * Sort module for patternfly. + * + */ +angular.module('patternfly.sort', ['ui.bootstrap']); +;/** + * @name patternfly + * + * @description + * Table module for patternfly. + * + */ +angular.module('patternfly.table', ['datatables', 'patternfly.pagination', 'patternfly.utils', 'patternfly.filters', 'patternfly.sort']); +;/** + * @name patternfly toolbars + * + * @description + * Filters module for patternfly. + * + */ +angular.module('patternfly.toolbars', [ + 'patternfly.utils', + 'patternfly.filters', + 'patternfly.sort', + 'patternfly.views']); +; +angular.module( 'patternfly.utils', ['ui.bootstrap'] ); +;/** + * @name patternfly + * + * @description + * Views module for patternfly. + * + */ +angular.module('patternfly.views', ['patternfly.utils', 'patternfly.filters', 'patternfly.sort', 'patternfly.charts', + 'dndLists', 'patternfly.pagination']); +;/** + * @name PatternFly Wizard + * + * @description + * Wizard module. + * + */ +angular.module('patternfly.wizard', ['ui.bootstrap.modal', + 'ui.bootstrap', + 'patternfly.form']); + + +;/** + * @ngdoc directive + * @name patternfly.autofocus:pfFocused + * @restrict A + * @element ANY + * @param {expression=} pfFocused If the expression is true, the element is focused and selected (if possible). + * + * @description + * The focus on element is evaluated from given expression. If the expression provided as an attribute to this directive + * is evaluated as true, the element is selected (and focused). + * + * @example + + + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+ */ + +angular.module('patternfly.autofocus', []).directive('pfFocused', ["$timeout", function ($timeout) { + 'use strict'; + + return { + restrict: 'A', + link: function (scope, element, attrs) { + scope.$watch(attrs.pfFocused, function (newValue) { + $timeout(function () { + if (newValue) { + element[0].focus(); + if (element[0].select) { + element[0].select(); + } + } + }); + }); + } + }; +}]); +;(function () { + 'use strict'; + + angular.module('patternfly.canvas').component('pfCanvasEditor', { + + bindings: { + chartDataModel: '=', + chartViewModel: '=?', + toolboxTabs: '=', + readOnly: 'u').addClass('nav-tabs-pf'); + angular.element('#filterFld').focus(); + }); + }; + + ctrl.hideToolbox = function () { + ctrl.toolboxVisible = false; + }; + + ctrl.toggleToolbox = function () { + if (!ctrl.readOnly && !ctrl.chartViewModel.inConnectingMode) { + if (ctrl.toolboxVisible === true) { + ctrl.hideToolbox(); + } else { + ctrl.showToolbox(); + } + } + }; + + ctrl.tabClicked = function () { + angular.element('#filterFld').focus(); + }; + + /*** Toolbox ***/ + + ctrl.startCallback = function (event, ui, item) { + ctrl.draggedItem = item; + }; + + ctrl.dropCallback = function (event, ui) { + var newNode = angular.copy(ctrl.draggedItem); + newNodeCount++; + newNode.x = event.clientX - 600; + newNode.y = event.clientY - 200; + newNode.backgroundColor = newNode.backgroundColor ? newNode.backgroundColor : '#fff'; + + ctrl.chartViewModel.addNode(newNode); + }; + + ctrl.addNodeByClick = function (item) { + var newNode = angular.copy(item); + newNodeCount++; + newNode.x = 250 + (newNodeCount * 4 + 160); + newNode.y = 200 + (newNodeCount * 4 + 160); + newNode.backgroundColor = newNode.backgroundColor ? newNode.backgroundColor : '#fff'; + + ctrl.chartViewModel.addNode(newNode); + }; + + ctrl.tabClicked = function () { + angular.element('#filterFld').focus(); + }; + + ctrl.activeTab = function () { + return ctrl.toolboxTabs.filter(function (tab) { + return tab.active; + })[0]; + }; + + ctrl.activeSubTab = function () { + var activeTab = ctrl.activeTab(); + if (activeTab && activeTab.subtabs) { + return activeTab.subtabs.filter(function (subtab) { + return subtab.active; + })[0]; + } + }; + + ctrl.activeSubSubTab = function () { + var activeSubTab = ctrl.activeSubTab(); + if (activeSubTab && activeSubTab.subtabs) { + return activeSubTab.subtabs.filter(function (subsubtab) { + return subsubtab.active; + })[0]; + } + }; + + /*** Zoom ***/ + + ctrl.maxZoom = function () { + if (ctrl.chartViewModel && ctrl.chartViewModel.zoom) { + return ctrl.chartViewModel.zoom.isMax(); + } + + return false; + }; + + ctrl.minZoom = function () { + if (ctrl.chartViewModel && ctrl.chartViewModel.zoom) { + return ctrl.chartViewModel.zoom.isMin(); + } + + return false; + }; + + ctrl.zoomIn = function () { + ctrl.chartViewModel.zoom.in(); + }; + + ctrl.zoomOut = function () { + ctrl.chartViewModel.zoom.out(); + }; + }] // controller + }); // module +})(); +;(function () { + 'use strict'; + + angular.module('patternfly.canvas') + .component('toolboxItems', { + templateUrl: 'canvas-view/canvas-editor/toolbox-items.html', + bindings: { + items: '=', + startDragCallback: '<', + clickCallback: '<', + searchText: '=' + }, + controller: function toolboxItemsController () { + var ctrl = this; + + ctrl.itemClicked = function (item) { + if (angular.isFunction(ctrl.clickCallback) && !item.disableInToolbox) { + ctrl.clickCallback(item); + } + }; + + ctrl.startItemDrag = function (event, ui, item) { + if (angular.isFunction(ctrl.startDragCallback)) { + ctrl.startDragCallback(event, ui, item); + } + }; + } + }); +})(); +;(function () { + 'use strict'; + + angular.module('patternfly.canvas') + .filter('trustAsResourceUrl', ['$sce', function ($sce) { + return function (val) { + return $sce.trustAsResourceUrl(val); + }; + }]) + + // + // Component that generates the rendered chart from the data model. + // + .component('pfCanvas', { + templateUrl: 'canvas-view/canvas/canvas.html', + bindings: { + chartDataModel:'=', + chartViewModel: '=?', + readOnly: ' startPoint.x ? startPoint.x : curPoint.x, + y: curPoint.y > startPoint.y ? startPoint.y : curPoint.y, + width: curPoint.x > startPoint.x ? curPoint.x - startPoint.x : startPoint.x - curPoint.x, + height: curPoint.y > startPoint.y ? curPoint.y - startPoint.y : startPoint.y - curPoint.y, + }; + }, + + // + // Dragging has ended... select all that are within the drag selection rect. + // + dragEnded: function () { + ctrl.dragSelecting = false; + ctrl.chart.applySelectionRect(ctrl.dragSelectionRect); + delete ctrl.dragSelectionStartPoint; + delete ctrl.dragSelectionRect; + } + }); + }; + + // + // Handle nodeMouseOver on an node. + // + ctrl.nodeMouseOver = function (evt, node) { + if (!ctrl.readOnly) { + ctrl.mouseOverNode = node; + } + }; + + // + // Handle nodeMouseLeave on an node. + // + ctrl.nodeMouseLeave = function () { + ctrl.mouseOverNode = null; + }; + + // + // Handle mousedown on a node. + // + ctrl.nodeMouseDown = function (evt, node) { + var chart = ctrl.chart; + var lastMouseCoords; + + if (ctrl.readOnly) { + return; + } + + dragging.startDrag(evt, { + + // + // Node dragging has commenced. + // + dragStarted: function (x, y) { + lastMouseCoords = ctrl.translateCoordinates(x, y, evt); + + // + // If nothing is selected when dragging starts, + // at least select the node we are dragging. + // + if (!node.selected()) { + chart.deselectAll(); + node.select(); + } + }, + + // + // Dragging selected nodes... update their x,y coordinates. + // + dragging: function (x, y) { + var curCoords = ctrl.translateCoordinates(x, y, evt); + var deltaX = curCoords.x - lastMouseCoords.x; + var deltaY = curCoords.y - lastMouseCoords.y; + + chart.updateSelectedNodesLocation(deltaX, deltaY); + + lastMouseCoords = curCoords; + }, + + // + // The node wasn't dragged... it was clicked. + // + clicked: function () { + chart.handleNodeClicked(node, evt.ctrlKey); + } + + }); + }; + + // + // Listen for node action + // + ctrl.nodeClickHandler = function (action, node) { + if (action === 'nodeActionConnect') { + ctrl.startConnectingMode(node); + } + }; + + ctrl.nodeCloseHandler = function () { + ctrl.mouseOverNode = null; + }; + + ctrl.connectingModeOutputConnector = null; + ctrl.connectingModeSourceNode = null; + + ctrl.startConnectingMode = function (node) { + ctrl.chart.inConnectingMode = true; + ctrl.hideConnectors = false; + ctrl.connectingModeSourceNode = node; + ctrl.connectingModeSourceNode.select(); + ctrl.connectingModeOutputConnector = node.getOutputConnector(); + ctrl.chart.updateValidNodesAndConnectors(ctrl.connectingModeSourceNode); + }; + + ctrl.cancelConnectingMode = function () { + // if output connector not connected to something, remove it + if (!ctrl.connectingModeOutputConnector.connected()) { + ctrl.chart.removeOutputConnector(ctrl.connectingModeOutputConnector); + } + ctrl.stopConnectingMode(); + }; + + ctrl.stopConnectingMode = function () { + ctrl.chart.inConnectingMode = false; + ctrl.chart.resetValidNodesAndConnectors(); + }; + + // + // Handle connectionMouseOver on an connection. + // + ctrl.connectionMouseOver = function (evt, connection) { + if (!ctrl.draggingConnection && !ctrl.readOnly) { // Only allow 'connection mouse over' when not dragging out a connection. + ctrl.mouseOverConnection = connection; + } + }; + + // + // Handle connectionMouseLeave on an connection. + // + ctrl.connectionMouseLeave = function () { + ctrl.mouseOverConnection = null; + }; + + // + // Handle mousedown on a connection. + // + ctrl.connectionMouseDown = function (evt, connection) { + var chart = ctrl.chart; + if (!ctrl.readOnly) { + chart.handleConnectionMouseDown(connection, evt.ctrlKey); + } + // Don't let the chart handle the mouse down. + evt.stopPropagation(); + evt.preventDefault(); + }; + + // + // Handle connectorMouseOver on an connector. + // + ctrl.connectorMouseOver = function (evt, node, connector) { + if (!ctrl.readOnly) { + ctrl.mouseOverConnector = connector; + } + }; + + // + // Handle connectorMouseLeave on an connector. + // + ctrl.connectorMouseLeave = function () { + ctrl.mouseOverConnector = null; + }; + + // + // Handle mousedown on an input connector. + // + ctrl.connectorMouseDown = function (evt, node) { + if (ctrl.chart.inConnectingMode && node !== ctrl.connectingModeSourceNode) { + ctrl.chart.createNewConnection(ctrl.connectingModeOutputConnector, ctrl.mouseOverConnector); + ctrl.stopConnectingMode(); + } + }; + + // + // zoom. + // + $scope.$on('zoomIn', function () { + ctrl.chart.zoom.in(); + }); + + $scope.$on('zoomOut', function () { + ctrl.chart.zoom.out(); + }); + + $scope.maxZoom = function () { + return (ctrl.chart.chartViewModel && ctrl.chart.chartViewModel.zoom) ? ctrl.chart.chartViewModel.zoom.isMax() : false; + }; + $scope.minZoom = function () { + return (ctrl.chart.chartViewModel && ctrl.chart.chartViewModel.zoom) ? ctrl.chart.chartViewModel.zoom.isMin() : false; + }; + + ctrl.zoomLevel = function () { + return ctrl.chart.zoom.getLevel(); + }; + + ctrl.$onInit = function () { + var backspaceKeyCode = 8; + var deleteKeyCode = 46; + var aKeyCode = 65; + var escKeyCode = 27; + + $document.find('body').keydown(function (evt) { + + if (evt.keyCode === aKeyCode && evt.ctrlKey === true) { + // + // Ctrl + A + // + ctrl.selectAll(); + $scope.$digest(); + evt.stopPropagation(); + evt.preventDefault(); + } + + if (evt.keyCode === deleteKeyCode || evt.keyCode === backspaceKeyCode) { + ctrl.deleteSelected(); + $scope.$digest(); + } + + if (evt.keyCode === escKeyCode) { + ctrl.deselectAll(); + $scope.$digest(); + } + }); + }; + }] + }); +})(); +;/* eslint-disable */ +// +// Global accessor. +// +var pfCanvas = {}; + +// Module. +(function() { + // + // Height of flow chart. + // + pfCanvas.defaultHeight = 756; + + // + // Width of flow chart. + // + pfCanvas.defaultWidth = 1396; + + pfCanvas.defaultBgImageSize = 24; + + // + // Width of a node. + // + pfCanvas.defaultNodeWidth = 150; + + // + // Height of a node. + // + pfCanvas.defaultNodeHeight = 150; + + // + // Amount of space reserved for displaying the node's name. + // + pfCanvas.nodeNameHeight = 40; + + // + // Height of a connector in a node. + // + pfCanvas.connectorHeight = 25; + + // + // Compute the Y coordinate of a connector, given its index. + // + pfCanvas.computeConnectorY = function(connectorIndex) { + return pfCanvas.defaultNodeHeight / 2 + connectorIndex * pfCanvas.connectorHeight; + }; + + // + // Compute the position of a connector in the graph. + // + pfCanvas.computeConnectorPos = function(node, connectorIndex, inputConnector) { + return { + x: node.x() + (inputConnector ? 0 : node.width ? node.width() : pfCanvas.defaultNodeWidth), + y: node.y() + pfCanvas.computeConnectorY(connectorIndex) + }; + }; + + // + // View model for a connector. + // + pfCanvas.ConnectorViewModel = function(connectorDataModel, x, y, parentNode) { + this.data = connectorDataModel; + + this._parentNode = parentNode; + this._x = x; + this._y = y; + + // + // The name of the connector. + // + this.name = function() { + return this.data.name; + }; + + // + // X coordinate of the connector. + // + this.x = function() { + return this._x; + }; + + // + // Y coordinate of the connector. + // + this.y = function() { + return this._y; + }; + + // + // The parent node that the connector is attached to. + // + this.parentNode = function() { + return this._parentNode; + }; + + // + // Is this connector connected? + // + this.connected = function() { + return this.data.connected; + }; + + // + // set connector connected + // + this.setConnected = function(value) { + this.data.connected = value; + }; + + // + // Is this connector invalid for a connecton? + // + this.invalid = function() { + return this.data.invalid; + }; + + // + // set connector invalid + // + this.setInvalid = function(value) { + this.data.invalid = value; + }; + + // + // Font Family for the the node. + // + this.fontFamily = function() { + return this.data.fontFamily || ""; + }; + + // + // Font Content for the the node. + // + this.fontContent = function() { + return this.data.fontContent || ""; + }; + }; + + // + // Create view model for a list of data models. + // + var createConnectorsViewModel = function(connectorDataModels, x, parentNode) { + var viewModels = []; + + if (connectorDataModels) { + for (var i = 0; i < connectorDataModels.length; ++i) { + var connectorViewModel = new pfCanvas.ConnectorViewModel(connectorDataModels[i], x, pfCanvas.computeConnectorY(i), parentNode); + viewModels.push(connectorViewModel); + } + } + + return viewModels; + }; + + // + // View model for a node. + // + pfCanvas.NodeViewModel = function(nodeDataModel) { + this.data = nodeDataModel; + + // set the default width value of the node + if (!this.data.width || this.data.width < 0) { + this.data.width = pfCanvas.defaultNodeWidth; + } + this.inputConnectors = createConnectorsViewModel(this.data.inputConnectors, 0, this); + this.outputConnectors = createConnectorsViewModel(this.data.outputConnectors, this.data.width, this); + + // Set to true when the node is selected. + this._selected = false; + + // + // Name of the node. + // + this.name = function() { + return this.data.name || ""; + }; + + // + // id of the node. + // + this.id = function() { + return this.data.id || -1; + }; + + // + // Image for the the node. + // + this.image = function() { + return this.data.image || ""; + }; + + // + // Icon for the the node. + // + this.icon = function() { + return this.data.icon || ""; + }; + + // + // Is node a bundle + // + this.bundle = function() { + return this.data.bundle || ""; + }; + + // + // background color for the node. + // + this.backgroundColor = function() { + return this.data.backgroundColor; + }; + + // + // X coordinate of the node. + // + this.x = function() { + return this.data.x; + }; + + // + // Y coordinate of the node. + // + this.y = function() { + return this.data.y; + }; + + // + // Width of the node. + // + this.width = function() { + return this.data.width; + }; + + // + // Font Family for the the node. + // + this.fontFamily = function() { + return this.data.fontFamily || ""; + }; + + // + // Font size for the the icon + // + this.fontSize = function() { + return this.data.fontSize || ""; + }; + + // + // Font Content for the the node. + // + this.fontContent = function() { + return this.data.fontContent || ""; + }; + + // + // Returns valid connection types for the node. + // + this.validConnectionTypes = function() { + return this.data.validConnectionTypes || []; + }; + + // + // Is this node valid for current connection? + // + this.invalid = function() { + return this.data.invalid; + }; + + // + // set node valid + // + this.setInvalid = function(value) { + this.data.invalid = value; + }; + + // + // Height of the node. + // + this.height = function() { + /* + var numConnectors = + Math.max( + this.inputConnectors.length, + this.outputConnectors.length); + + return pfCanvas.computeConnectorY(numConnectors); + */ + + return pfCanvas.defaultNodeHeight; + }; + + // + // Select the node. + // + this.select = function() { + this._selected = true; + }; + + // + // Deselect the node. + // + this.deselect = function() { + this._selected = false; + }; + + // + // Toggle the selection state of the node. + // + this.toggleSelected = function() { + this._selected = !this._selected; + }; + + // + // Returns true if the node is selected. + // + this.selected = function() { + return this._selected; + }; + + // + // Internal function to add a connector. + this._addConnector = function(connectorDataModel, x, connectorsDataModel, connectorsViewModel) { + var connectorViewModel = new pfCanvas.ConnectorViewModel(connectorDataModel, x, + pfCanvas.computeConnectorY(connectorsViewModel.length), this); + + connectorsDataModel.push(connectorDataModel); + + // Add to node's view model. + connectorsViewModel.push(connectorViewModel); + + return connectorViewModel; + }; + + // + // Internal function to remove a connector. + this._removeConnector = function(connectorDataModel, connectorsDataModel, connectorsViewModel) { + var connectorIndex = connectorsDataModel.indexOf(connectorDataModel); + connectorsDataModel.splice(connectorIndex, 1); + connectorsViewModel.splice(connectorIndex, 1); + }; + + // + // Add an input connector to the node. + // + this.addInputConnector = function(connectorDataModel) { + if (!this.data.inputConnectors) { + this.data.inputConnectors = []; + } + this._addConnector(connectorDataModel, 0, this.data.inputConnectors, this.inputConnectors); + }; + + // + // Get the single ouput connector for the node. + // + this.getOutputConnector = function() { + if (!this.data.outputConnectors) { + this.data.outputConnectors = []; + } + + if (this.data.outputConnectors.length === 0) { + var connectorDataModel = {name: 'out'}; + + return this._addConnector(connectorDataModel, this.data.width, this.data.outputConnectors, this.outputConnectors); + } else { + return this.outputConnectors[0]; + } + }; + + // + // Remove an ouput connector from the node. + // + this.removeOutputConnector = function(connectorDataModel) { + if (this.data.outputConnectors) { + this._removeConnector(connectorDataModel, this.data.outputConnectors, this.outputConnectors); + } + }; + + this.tags = function() { + return this.data.tags; + }; + }; + + // + // Wrap the nodes data-model in a view-model. + // + var createNodesViewModel = function(nodesDataModel) { + var nodesViewModel = []; + + if (nodesDataModel) { + for (var i = 0; i < nodesDataModel.length; ++i) { + nodesViewModel.push(new pfCanvas.NodeViewModel(nodesDataModel[i])); + } + } + + return nodesViewModel; + }; + + // + // View model for a node action. + // + pfCanvas.NodeActionViewModel = function(nodeActionDataModel) { + this.data = nodeActionDataModel; + + // + // id of the node action. + // + this.id = function() { + return this.data.id || ""; + }; + + // + // Name of the node action. + // + this.name = function() { + return this.data.name || ""; + }; + + // + // Font Family for the the node. + // + this.iconClass = function() { + return this.data.iconClass || ""; + }; + + // + // Font Content for the the node. + // + this.action = function() { + return this.data.action || ""; + }; + }; + + // + // Wrap the node actions data-model in a view-model. + // + var createNodeActionsViewModel = function(nodeActionsDataModel) { + var nodeActionsViewModel = []; + + if (nodeActionsDataModel) { + for (var i = 0; i < nodeActionsDataModel.length; ++i) { + nodeActionsViewModel.push(new pfCanvas.NodeActionViewModel(nodeActionsDataModel[i])); + } + } + + return nodeActionsViewModel; + }; + + // + // View model for a connection. + // + pfCanvas.ConnectionViewModel = function(connectionDataModel, sourceConnector, destConnector) { + this.data = connectionDataModel; + this.source = sourceConnector; + this.dest = destConnector; + + // Set to true when the connection is selected. + this._selected = false; + + this.name = function() { + return destConnector.name() || ""; + }; + + this.sourceCoordX = function() { + return this.source.parentNode().x() + this.source.x(); + }; + + this.sourceCoordY = function() { + return this.source.parentNode().y() + this.source.y(); + }; + + this.sourceCoord = function() { + return { + x: this.sourceCoordX(), + y: this.sourceCoordY() + }; + }; + + this.sourceTangentX = function() { + return pfCanvas.computeConnectionSourceTangentX(this.sourceCoord(), this.destCoord()); + }; + + this.sourceTangentY = function() { + return pfCanvas.computeConnectionSourceTangentY(this.sourceCoord(), this.destCoord()); + }; + + this.destCoordX = function() { + return this.dest.parentNode().x() + this.dest.x(); + }; + + this.destCoordY = function() { + return this.dest.parentNode().y() + this.dest.y(); + }; + + this.destCoord = function() { + return { + x: this.destCoordX(), + y: this.destCoordY() + }; + }; + + this.destTangentX = function() { + return pfCanvas.computeConnectionDestTangentX(this.sourceCoord(), this.destCoord()); + }; + + this.destTangentY = function() { + return pfCanvas.computeConnectionDestTangentY(this.sourceCoord(), this.destCoord()); + }; + + this.middleX = function(scale) { + if (angular.isUndefined(scale)) { + scale = 0.5; + } + + return this.sourceCoordX() * (1 - scale) + this.destCoordX() * scale; + }; + + this.middleY = function(scale) { + if (angular.isUndefined(scale)) { + scale = 0.5; + } + + return this.sourceCoordY() * (1 - scale) + this.destCoordY() * scale; + }; + + // + // Select the connection. + // + this.select = function() { + this._selected = true; + }; + + // + // Deselect the connection. + // + this.deselect = function() { + this._selected = false; + }; + + // + // Toggle the selection state of the connection. + // + this.toggleSelected = function() { + this._selected = !this._selected; + }; + + // + // Returns true if the connection is selected. + // + this.selected = function() { + return this._selected; + }; + }; + + // + // Helper function. + // + var computeConnectionTangentOffset = function(pt1, pt2) { + return (pt2.x - pt1.x) / 2; + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionSourceTangentX = function(pt1, pt2) { + return pt1.x + computeConnectionTangentOffset(pt1, pt2); + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionSourceTangentY = function(pt1, pt2) { + return pt1.y; + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionSourceTangent = function(pt1, pt2) { + return { + x: pfCanvas.computeConnectionSourceTangentX(pt1, pt2), + y: pfCanvas.computeConnectionSourceTangentY(pt1, pt2) + }; + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionDestTangentX = function(pt1, pt2) { + return pt2.x - computeConnectionTangentOffset(pt1, pt2); + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionDestTangentY = function(pt1, pt2) { + return pt2.y; + }; + + // + // Compute the tangent for the bezier curve. + // + pfCanvas.computeConnectionDestTangent = function(pt1, pt2) { + return { + x: pfCanvas.computeConnectionDestTangentX(pt1, pt2), + y: pfCanvas.computeConnectionDestTangentY(pt1, pt2) + }; + }; + + // + // View model for the chart. + // + pfCanvas.ChartViewModel = function(chartDataModel) { + // + // Find a specific node within the chart. + // + this.findNode = function(nodeID) { + for (var i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + if (node.data.id === nodeID) { + return node; + } + } + + throw new Error("Failed to find node " + nodeID); + }; + + // + // Find a specific input connector within the chart. + // + this.findInputConnector = function(nodeID, connectorIndex) { + var node = this.findNode(nodeID); + + if (!node.inputConnectors || node.inputConnectors.length <= connectorIndex) { + throw new Error("Node " + nodeID + " has invalid input connectors."); + } + + return node.inputConnectors[connectorIndex]; + }; + + // + // Find a specific output connector within the chart. + // + this.findOutputConnector = function(nodeID, connectorIndex) { + var node = this.findNode(nodeID); + + /*if (!node.outputConnectors || node.outputConnectors.length < connectorIndex) { + throw new Error("Node " + nodeID + " has invalid output connectors."); + } + + return node.outputConnectors[connectorIndex];*/ + return node.getOutputConnector(); + }; + + // + // Create a view model for connection from the data model. + // + this._createConnectionViewModel = function(connectionDataModel) { + var sourceConnector = this.findOutputConnector(connectionDataModel.source.nodeID, connectionDataModel.source.connectorIndex); + var destConnector = this.findInputConnector(connectionDataModel.dest.nodeID, connectionDataModel.dest.connectorIndex); + + sourceConnector.setConnected(true); + destConnector.setConnected(true); + return new pfCanvas.ConnectionViewModel(connectionDataModel, sourceConnector, destConnector); + }; + + // + // Wrap the connections data-model in a view-model. + // + this._createConnectionsViewModel = function(connectionsDataModel) { + var connectionsViewModel = []; + + if (connectionsDataModel) { + for (var i = 0; i < connectionsDataModel.length; ++i) { + connectionsViewModel.push(this._createConnectionViewModel(connectionsDataModel[i])); + } + } + + return connectionsViewModel; + }; + + // Reference to the underlying data. + this.data = chartDataModel; + + // Create a view-model for nodes. + this.nodes = createNodesViewModel(this.data.nodes); + + // Create a view-model for nodes. + this.nodeActions = createNodeActionsViewModel(this.data.nodeActions); + + // Create a view-model for connections. + this.connections = this._createConnectionsViewModel(this.data.connections); + + // Are there any valid connections (used in connection mode) ? + this.validConnections = true; + + // Create a view-model for zoom. + this.zoom = new pfCanvas.ZoomViewModel(); + + // Flag to indicate in connecting mode + this.inConnectingMode = false; + + // Flag to indicate whether the chart was just clicked on. + this.clickedOnChart = false; + + // + // Create a view model for a new connection. + // + this.createNewConnection = function(startConnector, endConnector) { + var connectionsDataModel = this.data.connections; + if (!connectionsDataModel) { + connectionsDataModel = this.data.connections = []; + } + + var connectionsViewModel = this.connections; + if (!connectionsViewModel) { + connectionsViewModel = this.connections = []; + } + + var startNode = startConnector.parentNode(); + var startConnectorIndex = startNode.outputConnectors.indexOf(startConnector); + startConnector = startNode.outputConnectors[startConnectorIndex]; + var startConnectorType = 'output'; + if (startConnectorIndex === -1) { + startConnectorIndex = startNode.inputConnectors.indexOf(startConnector); + startConnectorType = 'input'; + if (startConnectorIndex === -1) { + throw new Error("Failed to find source connector within either inputConnectors or outputConnectors of source node."); + } + } + + var endNode = endConnector.parentNode(); + var endConnectorIndex = endNode.inputConnectors.indexOf(endConnector); + endConnector = endNode.inputConnectors[endConnectorIndex]; + var endConnectorType = 'input'; + if (endConnectorIndex === -1) { + endConnectorIndex = endNode.outputConnectors.indexOf(endConnector); + endConnectorType = 'output'; + if (endConnectorIndex === -1) { + throw new Error("Failed to find dest connector within inputConnectors or outputConnectors of dest node."); + } + } + + if (startConnectorType === endConnectorType) { + throw new Error("Failed to create connection. Only output to input connections are allowed."); + } + + if (startNode === endNode) { + throw new Error("Failed to create connection. Cannot link a node with itself."); + } + + startNode = { + nodeID: startNode.data.id, + connectorIndex: startConnectorIndex + }; + + endNode = { + nodeID: endNode.data.id, + connectorIndex: endConnectorIndex + }; + + var connectionDataModel = { + source: startConnectorType === 'output' ? startNode : endNode, + dest: startConnectorType === 'output' ? endNode : startNode + }; + connectionsDataModel.push(connectionDataModel); + + var outputConnector = startConnectorType === 'output' ? startConnector : endConnector; + var inputConnector = startConnectorType === 'output' ? endConnector : startConnector; + + var connectionViewModel = new pfCanvas.ConnectionViewModel(connectionDataModel, outputConnector, inputConnector); + connectionsViewModel.push(connectionViewModel); + + startConnector.setConnected(true); + endConnector.setConnected(true); + }; + + // + // Add a node to the view model. + // + this.addNode = function(nodeDataModel) { + if (!this.data.nodes) { + this.data.nodes = []; + } + + // + // Update the data model. + // + this.data.nodes.push(nodeDataModel); + + // + // Update the view model. + // + this.nodes.push(new pfCanvas.NodeViewModel(nodeDataModel)); + }; + + // + // Select all nodes and connections in the chart. + // + this.selectAll = function() { + var nodes = this.nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.select(); + } + + var connections = this.connections; + for (i = 0; i < connections.length; ++i) { + var connection = connections[i]; + connection.select(); + } + }; + + // + // Deselect all nodes and connections in the chart. + // + this.deselectAll = function() { + var nodes = this.nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.deselect(); + // close any/all open toolbar dialogs + node.toolbarDlgOpen = false; + } + + var connections = this.connections; + for (i = 0; i < connections.length; ++i) { + var connection = connections[i]; + connection.deselect(); + } + }; + + // + // Mark nodes & connectors as valid/invalid based on source node's + // valid connection types + // + this.updateValidNodesAndConnectors = function(sourceNode) { + this.validConnections = false; + var validConnectionTypes = sourceNode.validConnectionTypes(); + for (var i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + node.setInvalid(true); + for (var c = 0; c < node.inputConnectors.length; c++) { + var inputConnector = node.inputConnectors[c]; + inputConnector.setInvalid(validConnectionTypes.indexOf(inputConnector.data.type) === -1); + if (!inputConnector.invalid() && node !== sourceNode && !inputConnector.connected()) { + node.setInvalid(false); + this.validConnections = true; + } + } + } + }; + + // + // Mark nodes & connectors as valid + // + this.resetValidNodesAndConnectors = function() { + for (var i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + node.setInvalid(false); + for (var c = 0; c < node.inputConnectors.length; c++) { + var inputConnector = node.inputConnectors[c]; + inputConnector.setInvalid(false); + } + } + }; + + this.removeOutputConnector = function(connectorViewModel) { + var parentNode = connectorViewModel.parentNode(); + parentNode.removeOutputConnector(connectorViewModel.data); + }; + + // + // Update the location of the node and its connectors. + // + this.updateSelectedNodesLocation = function(deltaX, deltaY) { + var selectedNodes = this.getSelectedNodes(); + + for (var i = 0; i < selectedNodes.length; ++i) { + var node = selectedNodes[i]; + node.data.x += deltaX; + node.data.y += deltaY; + } + }; + + // + // Handle mouse click on a particular node. + // + this.handleNodeClicked = function(node, ctrlKey) { + if (ctrlKey) { + node.toggleSelected(); + } else { + this.deselectAll(); + node.select(); + } + + // Move node to the end of the list so it is rendered after all the other. + // This is the way Z-order is done in SVG. + + var nodeIndex = this.nodes.indexOf(node); + if (nodeIndex === -1) { + throw new Error("Failed to find node in view model!"); + } + this.nodes.splice(nodeIndex, 1); + this.nodes.push(node); + }; + + // + // Handle mouse down on a connection. + // + this.handleConnectionMouseDown = function(connection, ctrlKey) { + if (ctrlKey) { + connection.toggleSelected(); + } else { + this.deselectAll(); + connection.select(); + } + }; + + // + // Delete all nodes and connections that are selected. + // + this.duplicateSelectedNode = function() { + var duplicatedNode = angular.copy(this.getSelectedNodes()[0]); + delete duplicatedNode.data.outputConnectors; + return duplicatedNode.data; + }; + + // + // Delete all nodes and connections that are selected. + // + this.deleteSelected = function() { + var newNodeViewModels = []; + var newNodeDataModels = []; + + var deletedNodeIds = []; + + // + /* Sort nodes into: + * nodes to keep and + * nodes to delete. + */ + + for (var nodeIndex = 0; nodeIndex < this.nodes.length; ++nodeIndex) { + var node = this.nodes[nodeIndex]; + if (!node.selected()) { + // Only retain non-selected nodes. + newNodeViewModels.push(node); + newNodeDataModels.push(node.data); + } else { + // Keep track of nodes that were deleted, so their connections can also + // be deleted. + deletedNodeIds.push(node.data.id); + } + } + + var newConnectionViewModels = []; + var newConnectionDataModels = []; + + // + // Remove connections that are selected. + // Also remove connections for nodes that have been deleted. + // + for (var connectionIndex = 0; connectionIndex < this.connections.length; ++connectionIndex) { + var connection = this.connections[connectionIndex]; + if (!connection.selected()) { + if (deletedNodeIds.indexOf(connection.data.source.nodeID) === -1 + && deletedNodeIds.indexOf(connection.data.dest.nodeID) === -1) { + // + // The nodes this connection is attached to, where not deleted, + // so keep the connection. + // + newConnectionViewModels.push(connection); + newConnectionDataModels.push(connection.data); + } + } else { + // connection selected, so it will be deleted (ie. not included in the 'newConnection models) + // also delete the connection's source node's output connector (if source node hasn't been deleteed + if (deletedNodeIds.indexOf(connection.data.source.nodeID) === -1) { + var sourceConnectorViewModel = connection.source; + if (sourceConnectorViewModel) { + sourceConnectorViewModel._parentNode.removeOutputConnector(sourceConnectorViewModel.data); + // also set connected to false on the dest node + var destConnectorViewModel = connection.dest; + if (destConnectorViewModel) { + destConnectorViewModel.setConnected(false); + } else { + throw new Error("Failed to find dest node of deleted connection!"); + } + } else { + throw new Error("Failed to find source node of deleted connection!"); + } + } + } + } + + // + // Update nodes and connections. + // + this.nodes = newNodeViewModels; + this.data.nodes = newNodeDataModels; + this.connections = newConnectionViewModels; + this.data.connections = newConnectionDataModels; + }; + + // + // Select nodes and connections that fall within the selection rect. + // + this.applySelectionRect = function(selectionRect) { + this.deselectAll(); + + for (var i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + if (node.x() >= selectionRect.x + && node.y() >= selectionRect.y + && node.x() + node.width() <= selectionRect.x + selectionRect.width + && node.y() + node.height() <= selectionRect.y + selectionRect.height) { + // Select nodes that are within the selection rect. + node.select(); + } + } + + for (i = 0; i < this.connections.length; ++i) { + var connection = this.connections[i]; + if (connection.source.parentNode().selected() + && connection.dest.parentNode().selected()) { + // Select the connection if both its parent nodes are selected. + connection.select(); + } + } + }; + + // + // Get the array of nodes that are currently selected. + // + this.getSelectedNodes = function() { + var selectedNodes = []; + + for (var i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + if (node.selected()) { + selectedNodes.push(node); + } + } + + return selectedNodes; + }; + + // + // Is only one node selected + // + this.isOnlyOneNodeSelected = function() { + return this.getSelectedNodes().length === 1; + }; + + // + // Are any nodes selected + // + this.areAnyNodesSelected = function() { + return this.getSelectedNodes().length > 0; + }; + + // + // Get the array of connections that are currently selected. + // + this.getSelectedConnections = function() { + var selectedConnections = []; + + for (var i = 0; i < this.connections.length; ++i) { + var connection = this.connections[i]; + if (connection.selected()) { + selectedConnections.push(connection); + } + } + + return selectedConnections; + }; + }; + + // + // Zoom view model + // + pfCanvas.ZoomViewModel = function() { + this.max = 1; // Max zoom level + this.min = parseFloat(".5"); // Min zoom level + this.inc = parseFloat(".25"); // Zoom level increment + this.level = this.max; // Zoom level + + // + // Is max zoom + // + this.isMax = function() { + return (this.level === this.max); + }; + + // + // Is min zoom + // + this.isMin = function() { + return (this.level === this.min); + }; + + // + // Get background image size + // + this.getBackgroundSize = function() { + var size = pfCanvas.defaultBgImageSize * this.getLevel(); + + return size; + }; + + // + // Get height to accomodate flow chart + // + this.getChartHeight = function() { + var height = (pfCanvas.defaultHeight / this.min) * this.getLevel(); + + return height; + }; + + // + // Get width to accomodate flow chart + // + this.getChartWidth = function() { + var width = (pfCanvas.defaultWidth / this.min) * this.getLevel(); + + return width; + }; + + // + // Zoom level + // + this.getLevel = function() { + return this.level; + }; + + // + // Zoom in + // + this.in = function() { + if (!this.isMax()) { + this.level = (this.level * 10 + this.inc * 10) / 10; + } + }; + + // + // Zoom out + // + this.out = function() { + if (!this.isMin()) { + this.level = (this.level * 10 - this.inc * 10) / 10; + } + }; + }; +})(); +;(function () { + 'use strict'; + + // Service used to help with dragging and clicking on elements. + angular.module('dragging', ['mouseCapture']) + .factory('dragging', ['mouseCapture', Factory]); + + function Factory (mouseCapture) { + // + // Threshold for dragging. + // When the mouse moves by at least this amount dragging starts. + // + var threshold = 5; + + return { + + // + // Called by users of the service to register a mousedown event and start dragging. + // Acquires the 'mouse capture' until the mouseup event. + // + startDrag: function (evt, config) { + var dragging = false; + var x = evt.pageX; + var y = evt.pageY; + + // + // Handler for mousemove events while the mouse is 'captured'. + // + var mouseMove = function (evt) { + if (!dragging) { + if (Math.abs(evt.pageX - x) > threshold + || Math.abs(evt.pageY - y) > threshold) { + dragging = true; + + if (config.dragStarted) { + config.dragStarted(x, y, evt); + } + + if (config.dragging) { + // First 'dragging' call to take into account that we have + // already moved the mouse by a 'threshold' amount. + config.dragging(evt.pageX, evt.pageY, evt); + } + } + } else { + if (config.dragging) { + config.dragging(evt.pageX, evt.pageY, evt); + } + + x = evt.pageX; + y = evt.pageY; + } + }; + + // + // Handler for when mouse capture is released. + // + var released = function () { + if (dragging) { + if (config.dragEnded) { + config.dragEnded(); + } + } else { + if (config.clicked) { + config.clicked(); + } + } + }; + + // + // Handler for mouseup event while the mouse is 'captured'. + // Mouseup releases the mouse capture. + // + var mouseUp = function (evt) { + mouseCapture.release(); + + evt.stopPropagation(); + evt.preventDefault(); + }; + + // + // Acquire the mouse capture and start handling mouse events. + // + mouseCapture.acquire(evt, { + mouseMove: mouseMove, + mouseUp: mouseUp, + released: released + }); + + evt.stopPropagation(); + evt.preventDefault(); + } + }; + } +})(); + +;(function () { + "use strict"; + + // Service used to acquire 'mouse capture' then receive dragging events while the mouse is captured. + angular.module('mouseCapture', []) + .factory('mouseCapture', ['$rootScope', Factory]) + .directive('mouseCapture', [ComponentDirective]); + + function Factory ($rootScope) { + // + // Element that the mouse capture applies to, defaults to 'document' + // unless the 'mouse-capture' directive is used. + // + var $element = document; + + // + // Set when mouse capture is acquired to an object that contains + // handlers for 'mousemove' and 'mouseup' events. + // + var mouseCaptureConfig = null; + + // + // Handler for mousemove events while the mouse is 'captured'. + // + var mouseMove = function (evt) { + if (mouseCaptureConfig && mouseCaptureConfig.mouseMove) { + mouseCaptureConfig.mouseMove(evt); + + $rootScope.$digest(); + } + }; + + // + // Handler for mouseup event while the mouse is 'captured'. + // + var mouseUp = function (evt) { + if (mouseCaptureConfig && mouseCaptureConfig.mouseUp) { + mouseCaptureConfig.mouseUp(evt); + + $rootScope.$digest(); + } + }; + + return { + + // + // Register an element to use as the mouse capture element instead of + // the default which is the document. + // + registerElement: function (element) { + $element = element; + }, + + // + // Acquire the 'mouse capture'. + // After acquiring the mouse capture mousemove and mouseup events will be + // forwarded to callbacks in 'config'. + // + acquire: function (evt, config) { + // + // Release any prior mouse capture. + // + this.release(); + + mouseCaptureConfig = config; + + // + // In response to the mousedown event register handlers for mousemove and mouseup + // during 'mouse capture'. + // + $element.mousemove(mouseMove); + $element.mouseup(mouseUp); + }, + + // + // Release the 'mouse capture'. + // + release: function () { + if (mouseCaptureConfig) { + if (mouseCaptureConfig.released) { + // + // Let the client know that their 'mouse capture' has been released. + // + mouseCaptureConfig.released(); + } + + mouseCaptureConfig = null; + } + + $element.unbind("mousemove", mouseMove); + $element.unbind("mouseup", mouseUp); + } + }; + } + + function ComponentDirective () { + return { + restrict: 'A', + controller: ['$scope', '$element', '$attrs', 'mouseCapture', + function ($scope, $element, $attrs, mouseCapture) { + // + // Register the directives element as the mouse capture element. + // + mouseCapture.registerElement($element); + }] + + }; + } +})(); + +;(function () { + 'use strict'; + + angular.module('patternfly.canvas') + .component('nodeToolbar', { + templateUrl: 'canvas-view/canvas/node-toolbar.html', + bindings: { + node: '=', + nodeActions: '=', + nodeClickHandler: '<', + nodeCloseHandler: '<' + }, + controller: ["$scope", function NodeToolbarController ($scope) { + var ctrl = this; + ctrl.selectedAction = 'none'; + + ctrl.actionIconClicked = function (action) { + ctrl.selectedAction = action; + $scope.$emit('nodeActionClicked', {'action': action, 'node': ctrl.node}); + if (angular.isFunction(ctrl.nodeClickHandler)) { + ctrl.nodeClickHandler(action, ctrl.node); + } + }; + + ctrl.close = function () { + ctrl.selectedAction = 'none'; + $scope.$emit('nodeActionClosed'); + if (angular.isFunction(ctrl.nodeCloseHandler)) { + ctrl.nodeCloseHandler(); + } + }; + }] + }); +})(); +;/** + * @ngdoc directive + * @name patternfly.canvas.component:pfCanvas + * @restrict E + * + * @description + * Component for core operations and rendering of a canvas. Does not work in IE 11 or lower because they do not support + * latest svg specification's 'foreignObject' api. Tested in FireFox, Chrome, and MS-Edge. + * @param {object} chartDataModel Chart data object which defines the nodes and connections on the canvas + * + * @param {object} chartViewModel (Optional) The chartViewModel is initialized from the chartDataModel and contains additional helper methods such as chartViewModel.isOnlyOneNodeSelected() and + * chartViewModel.getSelectedNodes(). You only need to specify a chartViewModel object if you plan on using advanced canvas operations. + * @param {boolean} readOnly A flag indicating whether the canvas is in 'read-only' mode. When in 'read-only' mode nodes cannot be moved, selected, or deleted, and the node action toolbar is hidden. + * @param {boolean} hideConnectors A flag indicating whether connections should be hidden or shown on the canvas + * @example + + + +
+
+ + +
+
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.canvas.demo', ['patternfly.canvas']); + + + + angular.module( 'patternfly.canvas.demo' ).controller( 'CanvasDemoCtrl', function( $scope, $window ) { + var imagePath = $window.IMAGE_PATH || "img"; + $scope.chartDataModel = { + "nodes": [ + { + "name": "Nuage", + "x": 345, + "y": 67, + "id": 1, + "image": imagePath + "/OpenShift-logo.svg", + "width": 150, + "bundle": true, + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network", "container"] + }, + { + "name": "Vmware", + "x": 100, + "y": 290, + "id": 2, + "image": imagePath + "/kubernetes.svg", + "width": 150, + "backgroundColor": "#fff", + "validConnectionTypes": ["storage"], + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Storage", + "type": "storage", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue90e" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ] + }, + { + "name": "NetApp", + "x": 350, + "y": 291, + "id": 3, + "width": 150, + "icon": "pf pficon-service", + "fontSize": "76px", + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network"] + }, + { + "name": "OpenShift", + "x": 105, + "y": 67, + "id": 4, + "width": 150, + "fontFamily": "fontawesome", + "fontContent": "\uf0c2", + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Storage", + "type": "storage", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue90e" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network", "container", "storage"] + } + ], + "nodeActions" : [ + { + "id": 1, + "name": "connect", + "iconClass": "fa fa-share-alt", + "action": "nodeActionConnect" + }, + { + "id": 2, + "name": "edit", + "iconClass": "pf pficon-edit", + "action": "nodeActionEdit" + }, + { + "id": 3, + "name": "tag", + "iconClass": "fa fa-tag", + "action": "nodeActionTag" + } + ], + "connections": [ + { + "source": { + "nodeID": 4, + "connectorIndex": 0 + }, + "dest": { + "nodeID": 1, + "connectorIndex": 1 + } + }, + { + "source": { + "nodeID": 4, + "connectorIndex": 0 + }, + "dest": { + "nodeID": 3, + "connectorIndex": 0 + } + } + ] + }; + + $scope.newNode = { + "name": "NetApp", + "id": 1000, + "width": 150, + "icon": "pf pficon-service", + "fontSize": "76px", + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network"] + }; + + $scope.readOnly = false; + $scope.hideConnectors = false; + $scope.eventText = ""; + + var numNewNodes = 1; + + $scope.addNode = function() { + var newNode = angular.copy($scope.newNode); + newNode.id ++; + newNode.name = newNode.name + " " + numNewNodes++; + newNode.x = 250 + (numNewNodes * 4 + 160); + newNode.y = 200 + (numNewNodes * 4 + 160); + + $scope.chartViewModel.addNode(newNode); + }; + + $scope.$on('nodeActionClicked', function(evt, args) { + var action = args.action; + var node = args.node; + $scope.eventText = node.name() + ' ' + action + '\r\n' + $scope.eventText; + }); + + $scope.zoomIn = function() { + $scope.$broadcast('zoomIn'); + }; + $scope.zoomOut = function() { + $scope.$broadcast('zoomOut'); + }; + + $scope.selectAll = function() { + $scope.$broadcast('selectAll'); + }; + $scope.deselectAll = function() { + $scope.$broadcast('deselectAll'); + }; + $scope.deleteSelected = function() { + $scope.$broadcast('deleteSelected'); + }; + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.canvas.component:pfCanvasEditor + * @restrict E + * + * @description + * Component for canvas editor which adds a toolbox where items can be dragged and dropped onto canvas, as well as other canvas + * operations such as: Zoom In, Zoom Out, Hide Connections, Remove Node, and Duplicate Node. Does not work in IE 11 or lower because they do not support + * latest svg specification's 'foreignObject' api. Tested in FireFox, Chrome, and MS-Edge. + * + * @param {object} chartDataModel Chart data object which defines the nodes and connections on the canvas. See {@link patternfly.canvas.component:pfCanvas} for detailed information. + * @param {object} chartViewModel (Optional) The chartViewModel is initialized from the chartDataModel and contains additional helper methods such as chartViewModel.isOnlyOneNodeSelected() and + * chartViewModel.getSelectedNodes(). + * @param {boolean} toolboxTabs An array of Tab objects used in the Toolbox. Each Tab object many contain 'subtabs' and/or 'items'. Items may be dragged onto the canvas. + * + * @param {boolean} readOnly (Optional) A flag indicating whether the canvas is in 'read-only' mode. When in 'read-only' mode nodes cannot be moved, selected, or deleted, and the node action toolbar is hidden. + * @example + + + +
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.canvaseditor.demo', ['patternfly.canvas']); + + + + angular.module( 'patternfly.canvaseditor.demo' ).controller( 'CanvasEditorDemoCtrl', function( $scope, $filter, $window ) { + var imagePath = $window.IMAGE_PATH || "img"; + $scope.chartDataModel = { + "nodes": [ + { + "name": "Nuage", + "x": 345, + "y": 67, + "id": 1, + "image": imagePath + "/OpenShift-logo.svg", + "width": 150, + "bundle": true, + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network", "container"] + }, + { + "name": "Vmware", + "x": 100, + "y": 290, + "id": 2, + "image": imagePath + "/kubernetes.svg", + "width": 150, + "backgroundColor": "#fff", + "validConnectionTypes": ["storage"], + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Storage", + "type": "storage", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue90e" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ] + }, + { + "name": "NetApp", + "x": 350, + "y": 291, + "id": 3, + "width": 150, + "icon": "pf pficon-service", + "fontSize": "76px", + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Network", + "type": "network", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue909" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network"] + }, + { + "name": "OpenShift", + "x": 105, + "y": 67, + "id": 4, + "width": 150, + "fontFamily": "fontawesome", + "fontContent": "\uf0c2", + "backgroundColor": "#fff", + "inputConnectors": [ + { + "name": "Storage", + "type": "storage", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue90e" + }, + { + "name": "Container", + "type": "container", + "fontFamily": "PatternFlyIcons-webfont", + "fontContent": "\ue621" + } + ], + "validConnectionTypes": ["network", "container", "storage"] + } + ], + "nodeActions" : [ + { + "id": 1, + "name": "connect", + "iconClass": "fa fa-share-alt", + "action": "nodeActionConnect" + }, + { + "id": 2, + "name": "edit", + "iconClass": "pf pficon-edit", + "action": "nodeActionEdit" + }, + { + "id": 3, + "name": "tag", + "iconClass": "fa fa-tag", + "action": "nodeActionTag" + } + ], + "connections": [ + { + "source": { + "nodeID": 4, + "connectorIndex": 0 + }, + "dest": { + "nodeID": 1, + "connectorIndex": 1 + } + }, + { + "source": { + "nodeID": 4, + "connectorIndex": 0 + }, + "dest": { + "nodeID": 3, + "connectorIndex": 0 + } + } + ] + }; + + $scope.toolboxTabs = [ + { + "preTitle": "Toolbox", + "title": "Items A", + "active": true, + "items": [ + { + "name": "Nuage", + "id": 10000000000004, + "image": imagePath + "/OpenShift-logo.svg" + }, + { + "name": "Vmware", + "id": 10000000000010, + "image": imagePath + "/kubernetes.svg" + } + ] + }, + { + "preTitle": "Toolbox", + "title": "Items B", + "active": true, + "items": [ + { + "name": "NetApp", + "id": 10000000000014, + "icon": "pf pficon-service" + }, + { + "name": "OpenShift", + "id": 10000000000021, + "icon": "fa fa-cloud" + }, + { + "name": "OpenStack", + "id": 10000000000022, + "icon": "pf pficon-network" + }, + { + "name": "Storage", + "id": 10000000000026, + "icon": "pf pficon-storage-domain" + }, + { + "name": "VM", + "id": 10000000000023, + "icon": "pf pficon-virtual-machine" + }, + { + "name": "Replicatore", + "id": 10000000000027, + "icon": "pf pficon-replicator" + } + ] + } + ]; + + $scope.readOnly = false; + $scope.eventText = ""; + + $scope.$on('nodeActionClicked', function(evt, args) { + var action = args.action; + var node = args.node; + $scope.eventText = node.name() + ' ' + action + '\r\n' + $scope.eventText; + }); + + $scope.deleteNodes = function() { + if ($scope.chartViewModel.inConnectingMode) { + return; + } + + $scope.chartViewModel.deleteSelected(); + + angular.element("#deleteNodes").blur(); + }; + + $scope.duplicateNode = function() { + if ($scope.chartViewModel.inConnectingMode) { + return; + } + + var duplicatedNode = $scope.chartViewModel.duplicateSelectedNode(); + + // Note: node id will be used in connections to/from this duplicated node + // If id changes, connections array/obj will need to be updated as well + duplicatedNode.id = Math.floor((Math.random() * 600) + 1); // random number between 1 and 600 + duplicatedNode.name = getCopyName(duplicatedNode.name); + duplicatedNode.backgroundColor = '#fff'; + + duplicatedNode.x = duplicatedNode.x + 15 * $scope.chartDataModel.nodes.length; + duplicatedNode.y = duplicatedNode.y + 15 * $scope.chartDataModel.nodes.length; + + $scope.chartViewModel.addNode(duplicatedNode); + + angular.element("#duplicateItem").blur(); + }; + + function getCopyName(baseName) { + // Test to see if we are duplicating an existing 'Copy' + var baseNameLength = baseName.indexOf(' Copy'); + if (baseNameLength === -1) { + baseNameLength = baseName.length; + } + baseName = baseName.substr(0, baseNameLength); + var filteredArray = $filter('filter')($scope.chartDataModel.nodes, {name: baseName}, false); + var copyName = baseName + " Copy" + ((filteredArray.length === 1) ? "" : " " + filteredArray.length); + + return copyName; + } + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.card.component:pfAggregateStatusCard + * @restrict E + * + * @param {object} status Status configuration information
+ * + * When layout='mini', only one notification can be specified:
+ * + * @param {boolean=} show-top-border Show/hide the top border, true shows top border, false (default) hides top border + * @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner + * @param {string=} spinnerText Text for the card spinner + * @param {string=} spinnerCardHeight Height to set for the card when data is loading and spinner is shown + * @param {string=} layout Various alternative layouts the aggregate status card may have:
+ * + * @deprecated {boolean=} alt-layout Display the aggregate status card in a 'alternate tall' layout. false (default) displays normal layout, true displays tall layout + * + * @description + * Component for easily displaying status information + * + * @example + + + +
+
+
+ + +
+
+ + +
+
+
+
+ + + +
+
+ + +
+
+
+
+ +
+ (depreciated, use layout = 'tall' instead) + +
+
+ +
+
+ +
+
+
+
+ + + angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window, $timeout ) { + var imagePath = $window.IMAGE_PATH || "img"; + + $scope.dataLoading = true; + + $scope.status = { + "title":"Nodes", + "href":"#", + "iconClass": "fa fa-shield", + "notifications":[] + }; + + $scope.status2 = { + "title":"Nodes", + "count":793, + "href":"#", + "iconClass": "fa fa-shield", + "notifications":[ + { + "iconClass":"pficon pficon-error-circle-o", + "count":4, + "href":"#" + }, + { + "iconClass":"pficon pficon-warning-triangle-o", + "count":1 + } + ] + }; + $scope.aggStatusAlt = { + "title":"Providers", + "count":3, + "notifications":[ + { + "iconImage": imagePath + "/kubernetes.svg", + "count":1, + "href":"#" + }, + { + "iconImage": imagePath + "/OpenShift-logo.svg", + "count":2, + "href":"#" + } + ] + }; + + $scope.aggStatusAlt2 = { + "title":"Providers", + "notifications":[] + }; + + $timeout(function () { + $scope.dataLoading = false; + + $scope.status = { + "title":"Nodes", + "count":793, + "href":"#", + "iconClass": "fa fa-shield", + "notifications":[ + { + "iconClass":"pficon pficon-error-circle-o", + "count":4, + "href":"#" + }, + { + "iconClass":"pficon pficon-warning-triangle-o", + "count":1 + } + ] + }; + + $scope.aggStatusAlt2 = { + "title":"Providers", + "count":3, + "notifications":[ + { + "iconImage": imagePath + "/kubernetes.svg", + "count":1, + "href":"#" + }, + { + "iconImage": imagePath + "/OpenShift-logo.svg", + "count":2, + "href":"#" + } + ] + }; + }, 6000 ); + + $scope.miniAggStatus = { + "iconClass":"pficon pficon-container-node", + "title":"Nodes", + "count":52, + "href":"#", + "notification": { + "iconClass":"pficon pficon-error-circle-o", + "count":3 + } + }; + + $scope.miniAggStatus2 = { + "iconClass":"pficon pficon-cluster", + "title":"Adipiscing", + "count":9, + "href":"#", + "notification":{ + "iconClass":"pficon pficon-ok" + } + }; + }); + + +
+ */ + +angular.module( 'patternfly.card' ).component('pfAggregateStatusCard', { + bindings: { + status: '=', + showTopBorder: '@?', + showSpinner: ' + * + * *Note: If a href link and a callBackFn are specified, the href link will be called + * @param {object=} filter filter configuration properties:
+ * + * @description + * Component for easily displaying a card with html content + * + * @example + + + +
+ + + + + + + +
+
+ + angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) { + + $scope.dataLoading = true; + + $scope.title2 = 'Memory'; + $scope.units2 = 'GB'; + + $scope.title3 = 'CPU Usage'; + $scope.units3 = 'MHz'; + + $scope.title4 = 'Disk Usage'; + $scope.units4 = 'TB'; + + $scope.title5 = 'Disk I/O'; + $scope.units5 = 'I/Ops'; + + $timeout(function () { + $scope.dataLoading = false; + + $scope.data2 = { + 'used': '25', + 'total': '100' + }; + + $scope.data3 = { + 'used': '420', + 'total': '500' + }; + + $scope.data4 = { + 'used': '350', + 'total': '500' + }; + $scope.data5 = { + 'used': '450', + 'total': '500' + }; + }, 3000 ); + + $scope.layoutInline = { + 'type': 'inline' + }; + }); + +
+ */ +angular.module('patternfly.card').component('pfCard', { + transclude: true, + templateUrl: 'card/basic/card.html', + bindings: { + headTitle: '@', + subTitle: '@?', + showTopBorder: '@?', + showTitlesSeparator: '@?', + showSpinner: ' + * + * *Note: If a href link and a callBackFn are specified, the href link will be called + * @param {object=} filter filter configuration properties:
+ * + * @description + * Component for easily displaying a card with html content + * + * @example + + + +
+ + + Card Contents + + + + Card Contents + + + + Card Contents + +
+
+ + angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) { + + $scope.dataLoading = true; + + $timeout(function () { + $scope.dataLoading = false; + }, 3000 ); + + $scope.footerConfig = { + 'iconClass' : 'fa fa-flag', + 'text' : 'View All Events', + 'callBackFn': function () { + alert("Footer Callback Fn Called"); + } + }; + + $scope.filterConfigHeader = { + 'filters' : [{label:'Last 30 Days', value:'30'}, + {label:'Last 15 Days', value:'15'}, + {label:'Today', value:'today'}], + 'callBackFn': function (f) { + alert("Header Filter Callback Fn Called for '" + f.label + "' value = " + f.value); + }, + 'position' : 'header' + }; + + $scope.filterConfig = { + 'filters' : [{label:'Last 30 Days', value:'30'}, + {label:'Last 15 Days', value:'15'}, + {label:'Today', value:'today'}], + 'callBackFn': function (f) { + alert("Filter Callback Fn Called for '" + f.label + "' value = " + f.value); + }, + 'defaultFilter' : '1' + }; + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.card.component:pfCard - Trends + * @restrict E + * + * @param {string} headTitle Title for the card + * @param {string=} subTitle Sub-Title for the card + * @param {string=} spinnerText Text for the card spinner + * @param {string=} spinnerCardHeight Height to set for the card when data is loading and spinner is shown + * @param {boolean=} showTopBorder Show/Hide the blue top border. True shows top border, false (default) hides top border + * @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner + * @param {boolean=} showTitlesSeparator Show/Hide the grey line between the title and sub-title. + * True (default) shows the line, false hides the line + * @param {object=} footer footer configuration properties:
+ * + * *Note: If a href link and a callBackFn are specified, the href link will be called + * @param {object=} filter filter configuration properties:
+ * + * @description + * Component for easily displaying a card with html content + * + * @example + + + +
+ + + + + + + + + + + + + +
+ +
+ + angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) { + + $scope.dataLoading = true; + + $timeout(function () { + $scope.dataLoading = false; + }, 3000 ); + + $scope.footerConfig = { + 'iconClass' : 'fa fa-flag', + 'text' : 'View All Events', + 'callBackFn': function () { + alert("Footer Callback Fn Called"); + } + }; + + $scope.filterConfig = { + 'filters' : [{label:'Last 30 Days', value:'30'}, + {label:'Last 15 Days', value:'15'}, + {label:'Today', value:'today'}], + 'callBackFn': function (f) { + alert("Filter Callback Fn Called for '" + f.label + "' value = " + f.value); + }, + 'defaultFilter' : '1' + }; + + var today = new Date(); + var dates = ['dates']; + for (var d = 20 - 1; d >= 0; d--) { + dates.push(new Date(today.getTime() - (d * 24 * 60 * 60 * 1000))); + } + + $scope.configSingle = { + 'chartId' : 'example2TrendsChart', + 'title' : 'Storage Capacity', + 'layout' : 'compact', + 'valueType' : 'actual', + 'units' : 'TB', + 'tooltipType' : 'percentage' + }; + + $scope.configRightLabel = { + 'chartId' : 'exampleRightLabelTrendsChart', + 'title' : 'Storage Capacity', + 'layout' : 'compact', + 'valueType' : 'actual', + 'units' : 'TB', + 'tooltipType' : 'percentage', + 'compactLabelPosition' : 'right' + }; + + $scope.dataSingle = { + 'total': '250', + 'xData': dates, + 'yData': ['used', '90', '20', '30', '20', '20', '10', '14', '20', '25', '68', '44', '56', '78', '56', '67', '88', '76', '65', '87', '76'] + }; + + $scope.configVirtual = { + 'chartId' : 'virtualTrendsChart', + 'layout' : 'inline', + 'trendLabel' : 'Virtual Disk I/O', + 'units' : 'GB', + 'tooltipType' : 'percentage' + }; + + $scope.dataVirtual = { + 'total': '250', + 'xData': dates, + 'yData': ['used', '90', '20', '30', '20', '20', '10', '14', '20', '25', '68', '44', '56', '78', '56', '67', '88', '76', '65', '87', '76'] + }; + + $scope.configPhysical = { + 'chartId' : 'physicalTrendsChart', + 'layout' : 'inline', + 'trendLabel' : 'Physical Disk I/O', + 'units' : 'MHz', + 'tooltipType' : 'percentage' + }; + + $scope.dataPhysical = { + 'total': '250', + 'xData': dates, + 'yData': ['used', '20', '20', '35', '20', '20', '87', '14', '20', '25', '28', '44', '56', '78', '56', '67', '88', '76', '65', '87', '16'] + }; + + $scope.configMemory = { + 'chartId' : 'memoryTrendsChart', + 'layout' : 'inline', + 'trendLabel' : 'Memory Utilization', + 'units' : 'GB', + 'tooltipType' : 'percentage' + }; + + $scope.dataMemory = { + 'total': '250', + 'xData': dates, + 'yData': ['used', '20', '20', '35', '70', '20', '87', '14', '95', '25', '28', '44', '56', '66', '16', '67', '88', '76', '65', '87', '56'] + }; + + $scope.actionBarConfig = { + 'iconClass' : 'fa fa-plus-circle', + 'text' : 'Add New Cluster', + 'callBackFn': function () { + alert("Footer Callback Fn Called"); + } + } + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.card.component:pfInfoStatusCard + * @restrict E + * + * @param {object} status Status configuration information
+ * + * @param {boolean=} show-top-border Show/hide the top border, true shows top border, false (default) hides top border + * @param {boolean} htmlContent Flag to allow HTML content within the info options + * @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner + * @param {string=} spinnerText Text for the card spinner + * @param {string=} spinnerCardHeight Height to set for the card when data is loading and spinner is shown + * + * @description + * Component for easily displaying textual information + * + * @example + + + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + + angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window, $timeout ) { + var imagePath = $window.IMAGE_PATH || "img"; + + $scope.dataLoading = true; + + $scope.infoStatus = { + "title":"TinyCore-local", + "href":"#", + "iconClass": "fa fa-shield", + "info":[ + "VM Name: aapdemo002", + "Host Name: localhost.localdomian", + "IP Address: 10.9.62.100", + "Power status: on" + ] + }; + + $scope.infoStatus2 = { + "title":"TinyCore-local", + "iconClass": "fa fa-shield" + }; + + $scope.infoStatusTitless = { + "iconImage": imagePath + "/OpenShift-logo.svg", + "info":[ + "Infastructure: VMware", + "Vmware: 1 CPU (1 socket x 1 core), 1024 MB", + "12 Snapshots", + "Drift History: 1" + ] + }; + + $scope.infoStatusAlt = {}; + + $timeout(function () { + $scope.dataLoading = false; + + $scope.infoStatus2 = { + "title":"TinyCore-local", + "href":"#", + "iconClass": "fa fa-shield", + "info":[ + "VM Name: aapdemo002", + "Host Name: localhost.localdomian", + "IP Address: 10.9.62.100", + "Power status: on" + ] + }; + + $scope.infoStatusAlt = { + "title":"Favorite Things", + "iconClass":"fa fa-heart", + "info":[ + "", + "", + "Tacos" + ] + }; + }, 6000 ); + }); + + +
+ */ + +angular.module( 'patternfly.card' ).component('pfInfoStatusCard', { + bindings: { + status: '=', + showTopBorder: '@?', + showSpinner: ' + +
+ + +
+ Total = {{total}}, Used = {{used}}, Available = {{available}} +
+ + +
+ + +
+
+
+ + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope ) { + $scope.used = 950; + $scope.total = 1000; + $scope.available = $scope.total - $scope.used; + + $scope.chartConfig = patternfly.c3ChartDefaults().getDefaultDonutConfig('MHz Used'); + $scope.chartConfig.data = { + type: "donut", + columns: [ + ["Used", $scope.used], + ["Available", $scope.total - $scope.used] + ], + groups: [ + ["used", "available"] + ], + order: null + }; + + $scope.getChart = function (chart) { + $scope.chart = chart; + }; + + $scope.focusUsed = function () { + $scope.chart.focus("Used"); + }; + + $scope.updateAvailable = function (val) { + $scope.available = $scope.total - $scope.used; + }; + + $scope.submitform = function (val) { + console.log("submitform"); + $scope.used = val; + $scope.updateAvailable(); + $scope.chartConfig.data.columns = [["Used",$scope.used],["Available",$scope.available]]; + }; + }); + + + */ +(function () { + 'use strict'; + + angular.module('patternfly.charts').component('pfC3Chart', { + bindings: { + config: '<', + getChartCallback: '<' + }, + template: '
', + controller: ["$timeout", "$attrs", function ($timeout, $attrs) { + var ctrl = this, prevConfig; + + // store the chart object + var chart; + ctrl.generateChart = function () { + var chartData; + + // Need to deep watch changes in chart config + prevConfig = angular.copy(ctrl.config); + + $timeout(function () { + chartData = ctrl.config; + if (chartData) { + chartData.bindto = '#' + $attrs.id; + // only re-generate donut pct chart if it has a threshold object + // because it's colors will change based on data and thresholds + if (!chart || ($attrs.id.indexOf('donutPctChart') !== -1 && chartData.thresholds)) { + chart = c3.generate(chartData); + } else { + //if chart is already created, then we only need to re-load data + chart.load(ctrl.config.data); + } + if (ctrl.getChartCallback) { + ctrl.getChartCallback(chart); + } + prevConfig = angular.copy(ctrl.config); + } + }); + }; + + ctrl.$doCheck = function () { + // do a deep compare on config + if (!angular.equals(ctrl.config, prevConfig)) { + ctrl.generateChart(); + } + }; + }] + }); +}()); +;angular.module('patternfly.charts').component('pfDonutChart', { + bindings: { + config: '<', + data: '<', + chartHeight: '= thresholds.error) { + threshold = "error"; + color = pfUtils.colorPalette.red; + } else if (used >= thresholds.warning) { + threshold = "warning"; + color = pfUtils.colorPalette.orange; + } + } + + if (!ctrl.threshold || ctrl.threshold !== threshold) { + ctrl.threshold = threshold; + ctrl.onThresholdChange({ threshold: ctrl.threshold }); + } + + return color; + }; + + ctrl.statusDonutColor = function () { + var color, percentUsed; + + color = { pattern: [] }; + percentUsed = ctrl.data.used / ctrl.data.total * 100.0; + color.pattern[0] = ctrl.getStatusColor(percentUsed, ctrl.config.thresholds); + color.pattern[1] = pfUtils.colorPalette.black300; + return color; + }; + + ctrl.donutTooltip = function () { + return { + contents: function (d) { + // Default to percent format + var tooltipContent = + '' + + Math.round(d[0].ratio * 100) + '% ' + d[0].name + + ''; + if (ctrl.config.tooltipFn) { + tooltipContent = + '' + + ctrl.config.tooltipFn(d) + + ''; + } else if (ctrl.tooltip === "amount") { + tooltipContent = + '' + + d[0].value + ' ' + ctrl.config.units + ' ' + d[0].name + + ''; + } else if (ctrl.tooltip === "both") { + tooltipContent = + '' + + '
' + + d[0].value + ' ' + ctrl.config.units + ' ' + d[0].name + + '' + + Math.round(d[0].ratio * 100) + '%' + + '
'; + } + return tooltipContent; + } + }; + }; + + ctrl.getDonutData = function () { + return { + columns: [ + ['Used', ctrl.data.used], + ['Available', ctrl.data.available] + ], + type: 'donut', + donut: { + label: { + show: false + } + }, + groups: [ + ['used', 'available'] + ], + order: null + }; + }; + + ctrl.getCenterLabelText = function () { + var centerLabelText; + + // default to 'used' info. + centerLabelText = { bigText: ctrl.data.used, + smText: ctrl.config.units + ' Used' }; + + if (ctrl.config.centerLabelFn) { + centerLabelText.bigText = ctrl.config.centerLabelFn(); + centerLabelText.smText = ''; + } else if (ctrl.centerLabel === 'none') { + centerLabelText.bigText = ''; + centerLabelText.smText = ''; + } else if (ctrl.centerLabel === 'available') { + centerLabelText.bigText = ctrl.data.available; + centerLabelText.smText = ctrl.config.units + ' Available'; + } else if (ctrl.centerLabel === 'percent') { + centerLabelText.bigText = Math.round(ctrl.data.used / ctrl.data.total * 100.0) + '%'; + centerLabelText.smText = 'of ' + ctrl.data.total + ' ' + ctrl.config.units; + } + + return centerLabelText; + }; + + ctrl.updateAll = function () { + // Need to deep watch changes in chart data + prevData = angular.copy(ctrl.data); + + ctrl.config = pfUtils.merge(patternfly.c3ChartDefaults().getDefaultDonutConfig(), ctrl.config); + ctrl.updateAvailable(); + ctrl.updatePercentage(); + ctrl.config.data = pfUtils.merge(ctrl.config.data, ctrl.getDonutData()); + ctrl.config.color = ctrl.statusDonutColor(ctrl); + ctrl.config.tooltip = ctrl.donutTooltip(); + ctrl.config.data.onclick = ctrl.config.onClickFn; + }; + + ctrl.setupDonutChartTitle = function () { + var donutChartTitle, centerLabelText; + + if (angular.isUndefined(ctrl.chart)) { + return; + } + + donutChartTitle = d3.select(ctrl.chart.element).select('text.c3-chart-arcs-title'); + if (!donutChartTitle) { + return; + } + + centerLabelText = ctrl.getCenterLabelText(); + + // Remove any existing title. + donutChartTitle.selectAll('*').remove(); + if (centerLabelText.bigText && !centerLabelText.smText) { + donutChartTitle.text(centerLabelText.bigText); + } else { + donutChartTitle.insert('tspan').text(centerLabelText.bigText).classed('donut-title-big-pf', true).attr('dy', 0).attr('x', 0); + donutChartTitle.insert('tspan').text(centerLabelText.smText).classed('donut-title-small-pf', true).attr('dy', 20).attr('x', 0); + } + }; + + ctrl.setChart = function (chart) { + ctrl.chart = chart; + ctrl.setupDonutChartTitle(); + }; + + ctrl.$onChanges = function (changesObj) { + if (changesObj.config || changesObj.data) { + ctrl.updateAll(); + } + if (changesObj.chartHeight) { + ctrl.config.size.height = changesObj.chartHeight.currentValue; + } + if (changesObj.centerLabel) { + ctrl.setupDonutChartTitle(); + } + }; + + ctrl.$doCheck = function () { + // do a deep compare on data + if (!angular.equals(ctrl.data, prevData)) { + ctrl.updateAll(); + } + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.component:pfDonutChart + * @restrict E + * + * @description + * Component for rendering a donut chart which shows the relationships of a set of values to a whole. When using a + * Donut Chart to show the relationship of a set of values to a whole, there should be no more than six + * categories. + * + *

+ * See http://c3js.org/reference.html for a full list of C3 chart options. + * + * @param {object} config configuration properties for the donut chart:
+ *
    + *
  • .chartId - the unique id of the donut chart + *
  • .centerLabelFn - user defined function to customize the text of the center label (optional) + *
  • .onClickFn(d,i) - user defined function to handle when donut arc is clicked upon. + *
+ * + * @param {object} data an array of values for the donut chart.
+ *
    + *
  • .key - string representing an arc within the donut chart + *
  • .value - number representing the value of the arc + *
+ * + * @param {number} chartHeight height of the donut chart + + * @example + + +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope, $interval ) { + $scope.config = { + 'chartId': 'chartOne', + 'legend': {"show":true}, + 'colors' : { + 'Cats': '#0088ce', // blue + 'Hamsters': '#3f9c35', // green + 'Fish': '#ec7a08', // orange + 'Dogs': '#cc0000' // red + }, + donut: { + title: "Animals" + }, + 'onClickFn': function (d, i) { + alert("You clicked on donut arc: " + d.id); + } + }; + + $scope.custConfig = angular.copy($scope.config); + $scope.custConfig.chartId = 'chartTwo'; + $scope.custConfig.legend.position = 'right'; + $scope.custConfig.centerLabelFn = function () { + return "Pets"; + }; + $scope.chartHeight = 120; + + $scope.data = [ + ['Cats', 2], + ['Hamsters', 1], + ['Fish', 3], + ['Dogs', 2] + ]; + + + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.charts.component:pfDonutPctChart + * @restrict E + * + * @description + * Component for rendering a percentage used donut/radial chart. The Used Percentage fill starts at 12 o’clock and + * moves clockwise. Whatever portion of the donut not Used, will be represented as Available, and rendered as a + * gray fill. + * There are three possible fill colors for Used Percentage, dependent on whether or not there are thresholds:
+ *
    + *
  • When no thresholds exist, or if the used percentage has not surpassed any thresholds, the indicator is blue. + *
  • When the used percentage has surpassed the warning threshold, but not the error threshold, the indicator is orange. + *
  • When the used percentage has surpassed the error threshold, the indicator is is red. + *
+ * The directive will calculate the Available Percentage (Total - Used), and display it as a grey radial fill. + * + *

+ * See http://c3js.org/reference.html for a full list of C3 chart options. + * + * @param {object} config configuration properties for the donut chart:
+ *
    + *
  • .chartId - the unique id of the donut chart + *
  • .units - unit label for values, ex: 'MHz','GB', etc.. + *
  • .thresholds - warning and error percentage thresholds used to determine the Usage Percentage fill color (optional) + *
  • .tooltipFn(d) - user defined function to customize the tool tip (optional) + *
  • .centerLabelFn - user defined function to customize the text of the center label (optional) + *
  • .onClickFn(d,i) - user defined function to handle when donut arc is clicked upon. + *
  • .labelConfig - object containing properties for external label (optional) - default: undefined + *
      + *
    • .orientation - string with possible values: 'left', 'right' (optional) - default: 'center' + *
    • .title - string representing a prefix or title (optional) - default: empty string + *
    • .label - the wording format to display, possible values: 'used', 'available', 'percent', 'none' (optional) - default: 'used' + *
    • .units - unit label for values, ex: 'MHz','GB', etc.. (optional) - default: empty string + *
    • .labelFn - function to customize the text of the external label. This callback returns no data. Updated display data can be accessed through the passed and updated parameter 'data'. (optional) - default: undefined + *
    + *
  • + *
+ * + * @param {object} data the Total and Used values for the donut chart. Available is calculated as Total - Used.
+ *
    + *
  • .used - number representing the amount used + *
  • .percent - number representing the percentage used + *
  • .total - number representing the total amount + *
  • .dataAvailable - Flag if there is data available - default: true + *
+ * + * @param {string=} center-label specifies the contents of the donut's center label.
+ * Values: + *
    + *
  • 'used' - displays the Used amount in the center label (default) + *
  • 'available' - displays the Available amount in the center label + *
  • 'percent' - displays the Usage Percent of the Total amount in the center label + *
  • 'none' - does not display the center label + *
+ * + * @param {string=} tooltip specifies the value to show in the tooltip when hovering Used or Available chart segments + * Values: + *
    + *
  • 'percent' - displays the Used or Available percentage of the Total in the tooltop (default) + *
  • 'amount' - displays the Used or Available amount and units in the tooltip + *
  • 'both' - displays both the percentage and amount in the tooltip + *
+ * + * @param {int=} chartHeight height of the donut chart + * @param {function (threshold)} on-threshold-change user defined function to handle when thresolds change
+ * 'threshold' Values: + *
    + *
  • 'ok' - when ok threshold is set + *
  • 'warning' - when warning threshold is set + *
  • 'error' - when error threshold is set + *
+ + + * @example + + +
+
+
+
+ +

+ +

+
+
+ + +
+
+ +

+ + +

+
+
+ + +
+
+ +
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ +
+
+ +

+ +

+
+
+ + +
+
+ +

+ +

+
+
+ +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+ + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope, $interval ) { + // start demo 1st row + $scope.configErr = { + 'chartId': 'chartErr', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.dataErr = { + 'used': '950', + 'total': '1000' + }; + + $scope.ChartErr = {}; + + $scope.configWarn = { + 'chartId': 'chartWarn', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.dataWarn = { + 'used': '650', + 'total': '1000' + }; + + $scope.configDynamic = { + 'chartId': 'chartOk', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.dataDynamic = { + 'used': '550', + 'total': '1000' + }; + + $scope.labelDynamic = "used"; + + $scope.thresholdChanged = function(threshold) { + $scope.threshLabel = threshold; + }; + + $interval(function () { + $scope.dataDynamic.used = Number($scope.dataDynamic.used) + 40; + if ($scope.dataDynamic.used > 1000) { + $scope.dataDynamic.used = 10; + } + + if ($scope.dataDynamic.used < 500) { + $scope.labelDynamic = "used"; + } else { + $scope.labelDynamic = "percent"; + } + }, 1000); + + $scope.configNoThresh = { + 'chartId': 'chartNoThresh', + 'units': 'GB' + }; + + $scope.dataNoThresh = { + 'used': '750', + 'total': '1000' + }; + + //start demo 2nd row + $scope.usedConfig = { + 'chartId': 'usedChart', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.usedData = { + 'used': '350', + 'total': '1000' + }; + + $scope.usedLabel = "used"; + + $scope.availConfig = { + 'chartId': 'availChart', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.availData = { + 'used': '350', + 'total': '1000' + }; + + $scope.availLabel = "available"; + + $scope.pctConfig = { + 'chartId': 'pctChart', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.pctData = { + 'used': '350', + 'total': '1000' + }; + + $scope.pctLabel = "percent"; + + $scope.noneConfig = { + 'chartId': 'noneChart', + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'} + }; + + $scope.noneData = { + 'used': '350', + 'total': '1000' + }; + + $scope.noLabel = "none"; + + //start demo 3rd row + $scope.tooltipAmount = "amount"; + $scope.tooltipBoth = "both"; + + //start demo 4th row + $scope.configOrientationLeft = { + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'}, + 'labelConfig': { + 'orientation': 'left', + 'labelFn': function () { + return "Lorem ipsum
" + $scope.dataOrientationLeft.used + " GB used"; + } + }, + 'size': { + 'height': 85, + 'width': 85 + }, + 'centerLabelFn': function () { + return $scope.dataOrientationLeft.used + "GB"; + } + }; + + $scope.dataOrientationLeft = { + 'used': '350', + 'total': '1000' + }; + + $scope.configOrientationCenter = { + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'}, + 'labelConfig': { + 'label': 'available', + 'units': 'GB', + 'title': 'Lorem ipsum,' + }, + 'size': { + 'height': 115, + 'width': 115 + }, + 'centerLabelFn': function () { + return $scope.dataOrientationCenter.used + "GB"; + } + }; + + $scope.dataOrientationCenter = { + 'used': '350', + 'total': '1000' + }; + + $scope.configOrientationRight = { + 'units': 'GB', + 'thresholds':{'warning':'60','error':'90'}, + 'labelConfig': { + 'orientation': 'right', + 'labelFn': function () { + return "Lorem ipsum
" + $scope.dataOrientationRight.percent + "% used"; + } + }, + 'size': { + 'height': 85, + 'width': 85 + }, + 'centerLabelFn': function () { + return $scope.dataOrientationRight.percent + "%"; + } + }; + + $scope.dataOrientationRight = { + 'used': '350', + 'total': '1000' + }; + + //start demo 5th row + $scope.custConfig = { + 'chartId': 'custChart', + 'units': 'MHz', + 'thresholds':{'warning':'60','error':'90'}, + "legend":{"show":true}, + 'tooltipFn': function (d) { + return '' + + d[0].value + ' ' + d[0].name + + ''; + }, + 'centerLabelFn': function () { + return $scope.custData.available + " GB"; + }, + 'onClickFn': function (d, i) { + alert("You Clicked On The Donut!"); + } + }; + + $scope.custData = { + 'dataAvailable': true, + 'used': '670', + 'total': '1000' + }; + + $scope.custChartHeight = 200; + }); +
+
+ */ + +;/** + * + * @description + * Directive for rendering an empty chart. This is used by chart directives when the data + * available flag is set to false. + * + * @param {string=} chartHeight height of the chart (no units) - default: 40 + */ +angular.module('patternfly.charts').component('pfEmptyChart', { + bindings: { + chartHeight: ' 90%']; + + ctrl.$onInit = function () { + ctrl.updateAll(); + }; + + ctrl.updateAll = function () { + var items = []; + var index; + + //Allow overriding of defaults + if (!ctrl.legendColors) { + ctrl.legendColors = heatmapColorPatternDefaults; + } + if (!ctrl.legend) { + ctrl.legend = legendLabelDefaults; + } + for (index = ctrl.legend.length - 1; index >= 0; index--) { + items.push({ + text: ctrl.legend[index], + color: ctrl.legendColors[index] + }); + } + ctrl.legendItems = items; + }; + + ctrl.$onChanges = function (changesObj) { + if (changesObj.legend && !changesObj.legend.isFirstChange()) { + ctrl.updateAll(); + } + if (changesObj.legendColors && !changesObj.legendColors.isFirstChange()) { + ctrl.updateAll(); + } + }; + } +}); +;/** + * @ngdoc directive + * @name patternfly.charts.directive:pfHeatMap + * @restrict E + * + * @description + * Component for rendering a heatmap chart. + * + * @param {object} data data for the chart:
+ *
    + *
  • .id - the id of the measurement + *
  • .value - the value of the measurement + *
  • .tooltip - message to be displayed on hover + *
+ * + * @param {boolean=} chartDataAvailable flag if the chart data is available - default: true + * @param {number=} height height of the chart (no units) - default: 200 + * @param {string=} chartTitle title of the chart + * @param {boolean=} showLegend flag to show the legend, defaults to true + * @param {array=} legendLabels the labels for the legend - defaults: ['< 70%', '70-80%' ,'80-90%', '> 90%'] + * @param {number=} maxBlockSize the maximum size for blocks in the heatmap. Default: 50, Range: 5 - 50 + * @param {number=} minBlockSize the minimum size for blocks in the heatmap. Default: 2 + * @param {number=} blockPadding the padding in pixels between blocks (default: 2) + * @param {array=} thresholds the threshold values for the heapmap - defaults: [0.7, 0.8, 0.9] + * @param {array=} heatmapColorPattern the colors that correspond to the various threshold values (lowest to hightest value ex: <70& to >90%) - defaults: ['#d4f0fa', '#F9D67A', '#EC7A08', '#CE0000'] + * @param {function=} clickAction function(block) function to call when a block is clicked on + * @param {number=} rangeHoverSize the maximum size for highlighting blocks in the same range. Default: 15 + * @param {boolean=} rangeOnHover flag to highlight blocks in the same range on hover, defaults to true + * @param {array=} rangeTooltips the tooltips for blocks in the same range - defaults: ['< 70%', '70-80%' ,'80-90%', '> 90%'] + * @example + + +
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope) { + $scope.data = [ + {'id': 9,'value': 0.96,'tooltip': 'Node 8 : My OpenShift Provider96% : 96 Used of 100 Total4 Available'}, + {'id': 44, 'value': 0.94, 'tooltip': 'Node 19 : My Kubernetes Provider94% : 94 Used of 100 Total6 Available'}, + {'id': 0, 'value': 0.91, 'tooltip': 'Node 9 : My OpenShift Provider91% : 91 Used of 100 Total9 Available'}, + {'id': 43, 'value': 0.9, 'tooltip': 'Node 18 : My Kubernetes Provider90% : 90 Used of 100 Total10 Available'}, + {'id': 7, 'value': 0.89, 'tooltip': 'Node 12 : My OpenShift Provider89% : 89 Used of 100 Total11 Available'}, + {'id': 41, 'value': 0.82, 'tooltip': 'Node 16 : My Kubernetes Provider82% : 82 Used of 100 Total18 Available'}, + {'id': 21, 'value': 0.81, 'tooltip': 'Node 21 : My OpenShift Provider81% : 81 Used of 100 Total19 Available'}, + {'id': 26, 'value': 0.8, 'tooltip': 'Node 1 : My Kubernetes Provider80% : 80 Used of 100 Total20 Available'}, + {'id': 48, 'value': 0.74, 'tooltip': 'Node 23 : My Kubernetes Provider74% : 74 Used of 100 Total26 Available'}, + {'id': 27, 'value': 0.72, 'tooltip': 'Node 2 : My Kubernetes Provider72% : 72 Used of 100 Total28 Available'}, + {'id': 42, 'value': 0.71, 'tooltip': 'Node 17 : My Kubernetes Provider71% : 71 Used of 100 Total29 Available'}, + {'id': 23, 'value': 0.71, 'tooltip': 'Node 23 : My OpenShift Provider71% : 71 Used of 100 Total29 Available'}, + {'id': 22, 'value': 0.69, 'tooltip': 'Node 22 : My OpenShift Provider69% : 69 Used of 100 Total31 Available'}, + {'id': 2, 'value': 0.66, 'tooltip': 'Node 2 : M8y OpenShift Provider66% : 66 Used of 100 Total34 Available'}, + {'id': 39, 'value': 0.66, 'tooltip': 'Node 14 : My Kubernetes Provider66% : 66 Used of 100 Total34 Available'}, + {'id': 3, 'value': 0.65, 'tooltip': 'Node 39 : My OpenShift Provider65% : 65 Used of 100 Total35 Available'}, + {'id': 29, 'value': 0.65, 'tooltip': 'Node 4 : My Kubernetes Provider65% : 65 Used of 100 Total35 Available'}, + {'id': 32, 'value': 0.56, 'tooltip': 'Node 7 : My Kubernetes Provider56% : 56 Used of 100 Total44 Available'}, + {'id': 13, 'value': 0.56, 'tooltip': 'Node 13 : My OpenShift Provider56% : 56 Used of 100 Total44 Available'}, + {'id': 49, 'value': 0.52, 'tooltip': 'Node 24 : My Kubernetes Provider52% : 52 Used of 100 Total48 Available'}, + {'id': 36, 'value': 0.5, 'tooltip': 'Node 11 : My Kubernetes Provider50% : 50 Used of 100 Total50 Available'}, + {'id': 6, 'value': 0.5, 'tooltip': 'Node 5 : My OpenShift Provider50% : 50 Used of 100 Total50 Available'}, + {'id': 38, 'value': 0.49, 'tooltip': 'Node 13 : My Kubernetes Provider49% : 49 Used of 100 Total51 Available'}, + {'id': 15, 'value': 0.48, 'tooltip': 'Node 15 : My OpenShift Provider48% : 48 Used of 100 Total52 Available'}, + {'id': 30, 'value': 0.48, 'tooltip': 'Node 5 : My Kubernetes Provider48% : 48 Used of 100 Total52 Available'}, + {'id': 11, 'value': 0.47, 'tooltip': 'Node 11 : My OpenShift Provider47% : 47 Used of 100 Total53 Available'}, + {'id': 17, 'value': 0.46, 'tooltip': 'Node 17 : My OpenShift Provider46% : 46 Used of 100 Total54 Available'}, + {'id': 25, 'value': 0.45, 'tooltip': 'Node 0 : My Kubernetes Provider45% : 45 Used of 100 Total55 Available'}, + {'id': 50, 'value': 0.45, 'tooltip': 'Node 25 : My Kubernetes Provider45% : 45 Used of 100 Total55 Available'}, + {'id': 46, 'value': 0.45, 'tooltip': 'Node 21 : My Kubernetes Provider45% : 45 Used of 100 Total55 Available'}, + {'id': 47, 'value': 0.45, 'tooltip': 'Node 22 : My Kubernetes Provider45% : 45 Used of 100 Total55 Available'}, + {'id': 1, 'value': 0.44, 'tooltip': 'Node 1 : My OpenShift Provider44% : 44 Used of 100 Total56 Available'}, + {'id': 31, 'value': 0.44, 'tooltip': 'Node 6 : My Kubernetes Provider44% : 44 Used of 100 Total56 Available'}, + {'id': 37, 'value': 0.44, 'tooltip': 'Node 12 : My Kubernetes Provider44% : 44 Used of 100 Total56 Available'}, + {'id': 24, 'value': 0.44, 'tooltip': 'Node 24 : My OpenShift Provider44% : 44 Used of 100 Total56 Available'}, + {'id': 40, 'value': 0.43, 'tooltip': 'Node 40 : My Kubernetes Provider43% : 43 Used of 100 Total57 Available'}, + {'id': 20, 'value': 0.39, 'tooltip': 'Node 20 : My OpenShift Provider39% : 39 Used of 100 Total61 Available'}, + {'id': 8, 'value': 0.39, 'tooltip': 'Node 8 : My OpenShift Provider39% : 39 Used of 100 Total61 Available'}, + {'id': 5, 'value': 0.38, 'tooltip': 'Node 5 : My OpenShift Provider38% : 38 Used of 100 Total62 Available'}, + {'id': 45, 'value': 0.37, 'tooltip': 'Node 20 : My Kubernetes Provider37% : 37 Used of 100 Total63 Available'}, + {'id': 12, 'value': 0.37, 'tooltip': 'Node 12 : My OpenShift Provider37% : 37 Used of 100 Total63 Available'}, + {'id': 34, 'value': 0.37, 'tooltip': 'Node 9 : My Kubernetes Provider37% : 37 Used of 100 Total63 Available'}, + {'id': 33, 'value': 0.33, 'tooltip': 'Node 8 : My Kubernetes Provider33% : 33 Used of 100 Total67 Available'}, + {'id': 16, 'value': 0.32, 'tooltip': 'Node 16 : My OpenShift Provider32% : 32 Used of 100 Total68 Available'}, + {'id': 10, 'value': 0.29, 'tooltip': 'Node 10 : My OpenShift Provider28% : 29 Used of 100 Total71 Available'}, + {'id': 35, 'value': 0.28, 'tooltip': 'Node 35 : My Kubernetes Provider28% : 28 Used of 100 Total72 Available'}, + {'id': 18, 'value': 0.27, 'tooltip': 'Node 18 : My OpenShift Provider27% : 27 Used of 100 Total73 Available'}, + {'id': 4, 'value': 0.26, 'tooltip': 'Node 4 : My OpenShift Provider26% : 26 Used of 100 Total74 Available'}, + {'id': 19, 'value': 0.25, 'tooltip': 'Node 19 : My OpenShift Provider25% : 25 Used of 100 Total75 Available'}, + {'id': 28, 'value': 0.25, 'tooltip': 'Node 3 : My Kubernetes Provider25% : 25 Used of 100 Total75 Available'}, + {'id': 51, 'value': 0.22, 'tooltip': 'Node 26 : My Kubernetes Provider22% : 22 Used of 100 Total78 Available'}, + {'id': 14, 'value': 0.2, 'tooltip': 'Node 14 : My OpenShift Provider20% : 20 Used of 100 Total80 Available'}]; + + $scope.dataAvailable = true; + $scope.title = 'Utilization - Using Defaults'; + $scope.titleAlt = 'Utilization - Overriding Defaults'; + $scope.titleSmall = 'Utilization - Small Blocks'; + $scope.legendLabels = ['< 60%','70%', '70-80%' ,'80-90%', '> 90%']; + $scope.rangeTooltips = ['Memory Utilization < 70%40 Nodes', 'Memory Utilization 70-80%4 Nodes', 'Memory Utilization 80-90%4 Nodes', 'Memory Utilization > 90%4 Nodes']; + $scope.thresholds = [0.6, 0.7, 0.8, 0.9]; + $scope.heatmapColorPattern = ['#d4f0fa', '#F9D67A', '#EC7A08', '#CE0000', '#f00']; + + $scope.showLegends = true; + var clickAction = function (block) { + console.log(block); + }; + $scope.clickAction = clickAction; + }); + +
+ */ +angular.module('patternfly.charts').component('pfHeatmap', { + bindings: { + data: '<', + chartDataAvailable: ' 90%']; + var rangeTooltipDefaults = ['< 70%', '70-80%' ,'80-90%', '> 90%']; + var heightDefault = 200; + + var setStyles = function () { + ctrl.containerStyles = { + height: ctrl.height + 'px', + display: ctrl.chartDataAvailable === false ? 'none' : 'block' + }; + }; + + var setSizes = function () { + var parentContainer = $element[0].querySelector('.heatmap-container'); + containerWidth = parentContainer.clientWidth; + containerHeight = parentContainer.clientHeight; + blockSize = determineBlockSize(); + + if ((blockSize - ctrl.padding) > ctrl.maxSize) { + blockSize = ctrl.padding + ctrl.maxSize; + + // Attempt to square off the area, check if square fits + numberOfRows = Math.ceil(Math.sqrt(ctrl.data.length)); + if (blockSize * numberOfRows > containerWidth || + blockSize * numberOfRows > containerHeight) { + numberOfRows = (blockSize === 0) ? 0 : Math.floor(containerHeight / blockSize); + } + } else if ((blockSize - ctrl.padding) < ctrl.minSize) { + blockSize = ctrl.padding + ctrl.minSize; + + // Attempt to square off the area, check if square fits + numberOfRows = Math.ceil(Math.sqrt(ctrl.data.length)); + if (blockSize * numberOfRows > containerWidth || + blockSize * numberOfRows > containerHeight) { + numberOfRows = (blockSize === 0) ? 0 : Math.floor(containerHeight / blockSize); + } + } else { + numberOfRows = (blockSize === 0) ? 0 : Math.floor(containerHeight / blockSize); + } + }; + + var determineBlockSize = function () { + var x = containerWidth; + var y = containerHeight; + var n = ctrl.data ? ctrl.data.length : 0; + var px = Math.ceil(Math.sqrt(n * x / y)); + var py = Math.ceil(Math.sqrt(n * y / x)); + var sx, sy; + + if (Math.floor(px * y / x) * px < n) { + sx = y / Math.ceil(px * y / x); + } else { + sx = x / px; + } + + if (Math.floor(py * x / y) * py < n) { + sy = x / Math.ceil(x * py / y); + } else { + sy = y / py; + } + return Math.max(sx, sy); + }; + + var redraw = function () { + var data = ctrl.data; + var color = d3.scale.threshold().domain(ctrl.thresholds).range(ctrl.heatmapColorPattern); + var rangeTooltip = d3.scale.threshold().domain(ctrl.thresholds).range(ctrl.rangeTooltips); + var blocks; + var fillSize = blockSize - ctrl.padding; + var highlightBlock = function (block, active) { + block.style('fill-opacity', active ? 1 : 0.4); + }; + var highlightBlockColor = function (block, fillColor) { + // Get fill color from given block + var blockColor = color(block.map(function (d) { + return d[0].__data__.value; + })); + // If given color matches, apply highlight + if (blockColor === fillColor) { + block.style('fill-opacity', 1); + } + }; + + var svg = window.d3.select(ctrl.thisComponent); + svg.selectAll('*').remove(); + blocks = svg.selectAll('rect').data(data).enter().append('rect'); + blocks.attr('x', function (d, i) { + return Math.floor(i / numberOfRows) * blockSize; + }).attr('y', function (d, i) { + return i % numberOfRows * blockSize; + }).attr('width', fillSize).attr('height', fillSize).style('fill', function (d) { + return color(d.value); + }).attr('uib-tooltip-html', function (d, i) { //tooltip-html is throwing an exception + if (ctrl.rangeOnHover && fillSize <= ctrl.rangeHoverSize) { + return '"' + rangeTooltip(d.value) + '"'; + } + return "'" + d.tooltip + "'"; + }).attr('tooltip-append-to-body', function (d, i) { + return true; + }).attr('tooltip-animation', function (d, i) { + return false; + }); + + //Adding events + blocks.on('mouseover', function () { + var fillColor; + blocks.call(highlightBlock, false); + if (ctrl.rangeOnHover && fillSize <= ctrl.rangeHoverSize) { + // Get fill color for current block + fillColor = color(d3.select(this).map(function (d) { + return d[0].__data__.value; + })); + // Highlight all blocks matching fill color + blocks[0].forEach(function (block) { + highlightBlockColor(d3.select(block), fillColor); + }); + } else { + d3.select(this).call(highlightBlock, true); + } + }); + blocks.on('click', function (d) { + if (ctrl.clickAction) { + ctrl.clickAction(d); + } + }); + + //Compiles the tooltips + angular.forEach(angular.element(blocks), function (block) { + var el = angular.element(block); + // TODO: get heatmap tooltips to work without using $compile or $scope + $compile(el)($scope); + }); + + svg.on('mouseleave', function () { + blocks.call(highlightBlock, true); + }); + }; + + var updateDisplay = function () { + setStyles(); + + if (ctrl.chartDataAvailable !== false && ctrl.data) { + ctrl.loadingDone = true; + + // Allow the style change to take effect to update the container size + $timeout(function () { + setSizes(); + redraw(); + }); + } + }; + + var handleDataUpdate = function () { + prevData = angular.copy(ctrl.data); + updateDisplay(); + }; + + var debounceResize = _.debounce(function () { + updateDisplay(); + }, 250, 500); + + var updateConfig = function () { + //Allow overriding of defaults + if (ctrl.maxBlockSize === undefined || isNaN(ctrl.maxBlockSize)) { + ctrl.maxSize = 64; + } else { + ctrl.maxSize = parseInt(ctrl.maxBlockSize); + if (ctrl.maxSize < 5) { + ctrl.maxSize = 5; + } else if (ctrl.maxSize > 50) { + ctrl.maxSize = 50; + } + } + + if (ctrl.minBlockSize === undefined || isNaN(ctrl.minBlockSize)) { + ctrl.minSize = 2; + } else { + ctrl.minSize = parseInt(ctrl.minBlockSize); + } + + if (ctrl.blockPadding === undefined || isNaN(ctrl.blockPadding)) { + ctrl.padding = 2; + } else { + ctrl.padding = parseInt(ctrl.blockPadding); + } + + if (ctrl.rangeHoverSize === undefined || isNaN(ctrl.rangeHoverSize)) { + ctrl.rangeHoverSize = 15; + } else { + ctrl.rangeHoverSize = parseInt(ctrl.rangeHoverSize); + } + + ctrl.rangeOnHover = (ctrl.rangeOnHover === undefined || ctrl.rangeOnHover) ? true : false; + + if (!ctrl.rangeTooltips) { + ctrl.rangeTooltips = rangeTooltipDefaults; + } + + if (!ctrl.thresholds) { + ctrl.thresholds = thresholdDefaults; + } + + if (!ctrl.heatmapColorPattern) { + ctrl.heatmapColorPattern = heatmapColorPatternDefaults; + } + + if (!ctrl.legendLabels) { + ctrl.legendLabels = legendLabelDefaults; + } + ctrl.height = ctrl.height || heightDefault; + ctrl.showLegend = ctrl.showLegend || (ctrl.showLegend === undefined); + }; + + ctrl.loadingDone = false; + + ctrl.$onChanges = function (changesObj) { + if (changesObj.chartDataAvailable && !changesObj.chartDataAvailable.isFirstChange()) { + setStyles(); + } else if (!changesObj.data) { + updateConfig(); + updateDisplay(); + } + }; + + ctrl.$doCheck = function () { + // do a deep compare on chartData and config + if (!angular.equals(ctrl.data, prevData)) { + handleDataUpdate(); + } + }; + + ctrl.$postLink = function () { + ctrl.thisComponent = $element[0].querySelector('.heatmap-pf-svg'); + updateConfig(); + handleDataUpdate(); + + angular.element($window).on('resize', debounceResize); + }; + + ctrl.$onDestroy = function () { + angular.element($window).off('resize', debounceResize); + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.component:pfLineChart + * @restrict E + * + * @description + * Component for rendering a line chart. + *

+ * See http://c3js.org/reference.html for a full list of C3 chart options. + * + * @param {object} config configuration settings for the line chart:
+ *
    + *
  • .chartId - the ID of the container that the chart should bind to + *
  • .units - unit label for values, ex: 'MHz','GB', etc.. + *
  • .tooltipFn - (optional) override the tooltip contents generation functions. Should take a data point and + * return HTML markup for the tooltip contents. Setting this overrides the tooltipType value. + *
  • .area - (optional) overrides the default Area properties of the C3 chart + *
  • .size - (optional) overrides the default Size properties of the C3 chart + *
  • .axis - (optional) overrides the default Axis properties of the C3 chart + *
  • .color - (optional) overrides the default Color properties of the C3 chart + *
  • .legend - (optional) overrides the default Legend properties of the C3 chart + *
  • .point - (optional) overrides the default Point properties of the C3 chart + *
+ * + * @param {object} chartData the data to be shown as an area chart
+ * First and second Array elements, xData and yData, must exist, next data arrays are optional.
+ *
    + *
  • .xData - Array, X values for the data points, first element must be the name of the data + *
  • .yData - Array, Y Values for the data points, first element must be the name of the data + *
  • .yData1 - Array, Y Values for the data points, first element must be the name of the data + *
  • .[...] - Array, Y Values for the data points, first element must be the name of the data + *
+ * + * @param {boolean=} showXAxis override config settings for showing the X Axis + * @param {boolean=} showYAxis override config settings for showing the Y Axis + * @param {boolean=} setAreaChart override config settings for showing area type chart + + * @example + + +
+
+ +
+
+
+
+
+
+
+ + + +
+
+
+
+ + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope, pfUtils ) { + + $scope.config = { + chartId: 'exampleLine', + grid: {y: {show: false}}, + point: {r: 1}, + color: {pattern: [pfUtils.colorPalette.blue, pfUtils.colorPalette.green]} + }; + + var today = new Date(); + var dates = ['dates']; + for (var d = 20 - 1; d >= 0; d--) { + dates.push(new Date(today.getTime() - (d * 24 * 60 * 60 * 1000))); + } + + $scope.data = { + dataAvailable: true, + xData: dates, + yData0: ['Created', 12, 10, 10, 62, 17, 10, 15, 13, 17, 10, 12, 10, 10, 12, 17, 16, 15, 13, 17, 10], + yData1: ['Deleted', 10, 17, 76, 14, 10, 10, 10, 10, 10, 10, 10, 17, 17, 14, 10, 10, 10, 10, 10, 10] + }; + + $scope.custShowXAxis = false; + $scope.custShowYAxis = false; + $scope.custAreaChart = false; + + $scope.addDataPoint = function () { + $scope.data.xData.push(new Date($scope.data.xData[$scope.data.xData.length - 1].getTime() + (24 * 60 * 60 * 1000))); + $scope.data.yData0.push(Math.round(Math.random() * 100)); + $scope.data.yData1.push(Math.round(Math.random() * 100)); + }; + + $scope.resetData = function () { + $scope.data = { + xData: dates, + yData0: ['Created', 12, 10, 10, 62], + yData1: ['Deleted', 10, 17, 76, 14] + }; + }; + }); + +
+ */ +angular.module('patternfly.charts').component('pfLineChart', { + bindings: { + config: '<', + chartData: '<', + showXAxis: '
+ * See http://c3js.org/reference.html for a full list of C3 chart options. + * + * @param {object} config configuration settings for the sparkline chart:
+ *
    + *
  • .chartId - the ID of the container that the chart should bind to + *
  • .units - unit label for values, ex: 'MHz','GB', etc.. + *
  • .tooltipType - (optional) set the type of tooltip, valid values: + *
      + *
    • 'default' - show the data point value and the data point name. + *
    • 'usagePerDay' - show the date, percent used, and used value for the data point. + *
    • 'valuePerDay' - show the date and value for the data point. + *
    • 'percentage' - show the current data point as a percentage. + *
    + *
  • .tooltipFn - (optional) override the tooltip contents generation functions. Should take a data point and + * return HTML markup for the tooltip contents. Setting this overrides the tooltipType value. + *
  • .area - (optional) overrides the default Area properties of the C3 chart + *
  • .size - (optional) overrides the default Size properties of the C3 chart + *
  • .axis - (optional) overrides the default Axis properties of the C3 chart + *
  • .color - (optional) overrides the default Color properties of the C3 chart + *
  • .legend - (optional) overrides the default Legend properties of the C3 chart + *
  • .point - (optional) overrides the default Point properties of the C3 chart + *
+ * + * @param {object} chartData the data to be shown as an area chart
+ *
    + *
  • .xData - Array, X values for the data points, first element must be the name of the data + *
  • .yData - Array, Y Values for the data points, first element must be the name of the data + *
  • .total - (optional) The Total amount, used when determining percentages + *
  • .dataAvailable - Flag if there is data available - default: true + *
+ * + * @param {int=} chartHeight height of the sparkline chart + * @param {boolean=} showXAxis override config settings for showing the X Axis + * @param {boolean=} showYAxis override config settings for showing the Y Axis + + * @example + + +
+
+ +
+
+
+
+
+ +
+ + + + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope ) { + + $scope.config = { + chartId: 'exampleSparkline', + tooltipType: 'default' + }; + + var today = new Date(); + var dates = ['dates']; + for (var d = 20 - 1; d >= 0; d--) { + dates.push(new Date(today.getTime() - (d * 24 * 60 * 60 * 1000))); + } + + $scope.data = { + dataAvailable: true, + total: 100, + xData: dates, + yData: ['used', 10, 20, 30, 20, 30, 10, 14, 20, 25, 68, 54, 56, 78, 56, 67, 88, 76, 65, 87, 76] + }; + + $scope.custShowXAxis = false; + $scope.custShowYAxis = false; + $scope.custChartHeight = 60; + + $scope.addDataPoint = function () { + $scope.data.xData.push(new Date($scope.data.xData[$scope.data.xData.length - 1].getTime() + (24 * 60 * 60 * 1000))); + $scope.data.yData.push(Math.round(Math.random() * 100)); + }; + }); + +
+ */ +angular.module('patternfly.charts').component('pfSparklineChart', { + bindings: { + config: '<', + chartData: '<', + chartHeight: '' + + ' ' + + ' ' + + tipRows + + ' ' + + '
' + + ''; + }; + + ctrl.sparklineTooltip = function () { + return { + contents: function (d) { + var tipRows; + var percentUsed = 0; + + if (ctrl.config.tooltipFn) { + tipRows = ctrl.config.tooltipFn(d); + } else { + switch (ctrl.config.tooltipType) { + case 'usagePerDay': + if (ctrl.chartData.dataAvailable !== false && ctrl.chartData.total > 0) { + percentUsed = Math.round(d[0].value / ctrl.chartData.total * 100.0); + } + tipRows = + '' + + ' ' + d[0].x.toLocaleDateString() + '' + + '' + + '' + + ' ' + percentUsed + '%:' + '' + + ' ' + d[0].value + ' ' + (ctrl.config.units ? ctrl.config.units + ' ' : '') + d[0].name + '' + + ''; + break; + case 'valuePerDay': + tipRows = + '' + + ' ' + d[0].x.toLocaleDateString() + '' + + ' ' + d[0].value + ' ' + d[0].name + '' + + ''; + break; + case 'percentage': + percentUsed = Math.round(d[0].value / ctrl.chartData.total * 100.0); + tipRows = + '' + + ' ' + percentUsed + '%' + '' + + ''; + break; + default: + tipRows = patternfly.c3ChartDefaults().getDefaultSparklineTooltip().contents(d); + } + } + return ctrl.getTooltipTableHTML(tipRows); + }, + position: function (data, width, height, element) { + var center; + var top; + var chartBox; + var graphOffsetX; + var x; + + try { + center = parseInt(element.getAttribute('x')); + top = parseInt(element.getAttribute('y')); + chartBox = document.querySelector('#' + ctrl.sparklineChartId).getBoundingClientRect(); + graphOffsetX = document.querySelector('#' + ctrl.sparklineChartId + ' g.c3-axis-y').getBoundingClientRect().right; + x = Math.max(0, center + graphOffsetX - chartBox.left - Math.floor(width / 2)); + + return { + top: top - height, + left: Math.min(x, chartBox.width - width) + }; + } catch (e) { + } + } + }; + }; + + ctrl.$onChanges = function (changesObj) { + ctrl.updateAll(); + }; + + ctrl.$doCheck = function () { + // do a deep compare on chartData + if (!angular.equals(ctrl.chartData, prevChartData)) { + ctrl.updateAll(); + } + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.component:pfTopologyMap + * @restrict E + * + * @param {array} nodes array containing objects representing graph nodes. Each node has these attributes. Only node id is mandatory parameter. + *
    + *
  • id: unique node identifier
  • + *
  • title: node title
  • + *
  • size: node radius; default value is 17
  • + *
  • fileicon: this attribute specifies path to image file. eg: '/some/path/to/image/image.png'. File icon has higher priority than fonticon.
  • + *
  • fonticon: css class of node icon eg: 'fa fa-info' File icon has higher priority than fonticon.
  • + *
  • fill: string containing color code (basic, RGB, HEX) of node background.
  • + *
  • borderColor: string containing color code (basic, RGB, HEX) of node border.
  • + *
  • iconColor: string containing color code (basic, RGB, HEX) of node icon, if iconType is fonticon.
  • + *
  • opacity: number from 〈0,1〉range, representing node opacity
  • + *
  • utilization: number from〈0,100〉range, representing node utilization percentage
  • + *
+ * @param {array} edges array of objects. Each object represents one edge between two nodes. Source and target are mandatory attributes. + *
    + *
  • source: id of source node
  • + *
  • target: id of target node
  • + *
  • lineStyle: stroke style of edge; currently only 'dashed' is avaliable
  • + *
  • title: label of edge
  • + *
+ * + * @param {boolean=} show-node-labels show/hide all node tooltips + * + * @param {boolean=} show-edge-labels show/hode all edge tooltips + * + * @param {object=} tooltip-style object used for tooltip styling. This is an optional parameter. + *
    + *
  • size: text size in px
  • + *
  • font: font name. eg: 'Arial'
  • + *
  • textColor: string containing color code (basic, RGB, HEX) of title text.
  • + *
  • background: string containing color code (basic, RGB, HEX) of title background
  • + *
+ * @param {function (node) =} select-node function that return selected(clicked) node from graph + * @param {function (array) =} multi-select-nodes function that returns array of selected nodes. Multiple nodes are selected while holding the ctrl/shift key and clicking + * @param {function (edge) =} select-edge function that return selected(clicked) edge from graph + * @param {function (array) =} multi-select-edges function that returns array of selected edges. Multiple edges are selected while holding the ctrl/shift key and clicking + * + * @description + * Component for rendering topology chart on Canvas element. This is just a simple component. It has no searching/filtering or other methods. Will only render given data and return data about selected objects.
+ * Component also supports semantic zooming. Only distance between nodes is growing/shrinking, but node size remains the same. Canvas will zoom around the mouse cursor. + * @example + + +
+ + +

Selected node:

+
{{selectedNode}}
+

Selected nodes:

+
{{selectedNodes}}
+

Selected edge:

+
{{selectedEdge}}
+

Selected edges:

+
{{selectedEdges}}
+
+
+
+
+
+ + angular.module( 'patternfly.charts' ).controller( 'TopologyMapCtrl', function( $scope, $rootScope ) { + $scope.tooltipStyle = { + size: 12, + font: 'Arial', + textColor: 'white', + background: 'rgba(0, 0, 0, .5)', + } + $scope.showEdgeLabels = false; + $scope.showNodeLabels = false; + $scope.selectedNode = {}; + $scope.selectedNodes = []; + $scope.selectedEdge = {}; + $scope.selectedEdges = []; + $scope.selectEdge = function(edge){ + $scope.selectedEdge = edge; + $scope.$apply(); + } + $scope.multiSelectEdges = function(array) { + $scope.selectedEdges = array; + $scope.$apply(); + } + $scope.selectNode = function(node){ + $scope.selectedNode = node; + $scope.$apply() + }; + $scope.multiSelect = function(array){ + $scope.selectedNodes = array; + $scope.$apply(); + } + $scope.changeData = function(){ + $scope.nodes = [ + { + id: '1', + title: 'testNode', + utilization: 50, + opacity: 0.5, + size: 32, + fonticon: "fa fa-cog", + iconType: "fonticon", + }, + { + id: '2', + title: 'testNode2', + fill: '#FF0000', + utilization: 75, + fonticon: "fa fa-info", + iconType: "fonticon", + borderColor: '#AD1457' + },{ + id: 'blabla', + title: 'testNode3', + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + iconType: "fileicon", + size: 64, + utilization: 25, + } + ]; + $scope.edges = [ + {source: '1', target: 'blabla', title: 'edge title'}, + {source: 'blabla', target: '2'}, + {source: '1', target: '2', title: '1596'} + ]; + } + $scope.nodes = [ + { + "id": 0, + "title": "Levy", + "size": 25, + fonticon: "fa fa-cog", + }, + { + "id": 1, + "title": "Celina", + "size": 24, + fonticon: "fa fa-cog", + }, + { + "id": 2, + "title": "Nancy", + "size": 15, + fonticon: "fa fa-cog", + }, + { + "id": 3, + "title": "Yang", + "size": 25 + }, + { + "id": 4, + "title": "Gray", + "size": 28, + fonticon: "fa fa-cog", + }, + { + "id": 5, + "title": "Maddox", + "size": 16, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 6, + "title": "Wallace", + "size": 27, + utilization: 50, + fonticon: "fa fa-cog", + }, + { + "id": 7, + "title": "Bettie", + "size": 19, + fonticon: "fa fa-cog", + }, + { + "id": 8, + "title": "Watkins", + "size": 20, + fonticon: "fa fa-cog", + }, + { + "id": 9, + "title": "Stanton", + "size": 12, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 10, + "title": "Lindsay", + "size": 18 + }, + { + "id": 11, + "title": "Harrell", + "size": 27 + }, + { + "id": 12, + "title": "Stephanie", + "size": 30, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 13, + "title": "Mona", + "size": 18, + fonticon: "fa fa-cog", + }, + { + "id": 14, + "title": "Natalia", + "size": 21, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 15, + "title": "Rose", + "size": 26, + fonticon: "fa fa-cog", + }, + { + "id": 16, + "title": "Robles", + "size": 12, + fonticon: "fa fa-cog", + }, + { + "id": 17, + "title": "Parker", + "size": 20, + fonticon: "fa fa-cog", + }, + { + "id": 18, + "title": "Decker", + "size": 21, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 19, + "title": "Helen", + "size": 18 + }, + { + "id": 20, + "title": "Coleman", + "size": 30, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 21, + "title": "Wolf", + "size": 25, + fonticon: "fa fa-cog", + }, + { + "id": 22, + "title": "Morton", + "size": 28, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 23, + "title": "Fitzpatrick", + "size": 16, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 24, + "title": "Hayden", + "size": 21 + }, + { + "id": 25, + "title": "Kaufman", + "size": 30, + fileicon: "http://www.patternfly.org/angular-patternfly/img/patternfly-orb.svg", + }, + { + "id": 26, + "title": "Julia", + "size": 15 + }, + { + "id": 27, + "title": "Louella", + "size": 16 + }, + { + "id": 28, + "title": "Rocha", + "size": 21 + }, + { + "id": 29, + "title": "Kimberly", + "size": 31 + }, + { + "id": 30, + "title": "Benita", + "size": 20 + }, + { + "id": 31, + "title": "Harrington", + "size": 27 + }, + { + "id": 32, + "title": "Hobbs", + "size": 17 + }, + { + "id": 33, + "title": "Fuentes", + "size": 26 + }, + { + "id": 34, + "title": "Alyce", + "size": 22 + }, + { + "id": 35, + "title": "Frank", + "size": 27 + }, + { + "id": 36, + "title": "Lawrence", + "size": 21 + }, + { + "id": 37, + "title": "Harper", + "size": 19 + }, + { + "id": 38, + "title": "Woods", + "size": 29 + }, + { + "id": 39, + "title": "Hopper", + "size": 25 + }, + { + "id": 40, + "title": "Lesley", + "size": 28 + }, + { + "id": 41, + "title": "Arlene", + "size": 30 + }, + { + "id": 42, + "title": "Mcdowell", + "size": 13 + }, + { + "id": 43, + "title": "Kristy", + "size": 12 + }, + { + "id": 44, + "title": "Janette", + "size": 21 + }, + { + "id": 45, + "title": "Charles", + "size": 20 + }, + { + "id": 46, + "title": "Houston", + "size": 30 + }, + { + "id": 47, + "title": "Renee", + "size": 32 + }, + { + "id": 48, + "title": "Leah", + "size": 16 + }, + { + "id": 49, + "title": "Marguerite", + "size": 31 + }, + { + "id": 50, + "title": "Moreno", + "size": 18 + }, + { + "id": 51, + "title": "Washington", + "size": 21 + }, + { + "id": 52, + "title": "Davis", + "size": 14 + }, + { + "id": 53, + "title": "Potts", + "size": 29 + }, + { + "id": 54, + "title": "Holly", + "size": 22 + }, + { + "id": 55, + "title": "Phillips", + "size": 18 + }, + { + "id": 56, + "title": "Santiago", + "size": 32 + }, + { + "id": 57, + "title": "Suarez", + "size": 24 + }, + { + "id": 58, + "title": "Lynnette", + "size": 12 + }, + { + "id": 59, + "title": "Gates", + "size": 18 + }, + { + "id": 60, + "title": "Aurelia", + "size": 14 + }, + { + "id": 61, + "title": "Horn", + "size": 30 + }, + { + "id": 62, + "title": "Annabelle", + "size": 19 + }, + { + "id": 63, + "title": "Snyder", + "size": 19 + }, + { + "id": 64, + "title": "Tanner", + "size": 24 + }, + { + "id": 65, + "title": "Walker", + "size": 26 + }, + { + "id": 66, + "title": "Ruthie", + "size": 26 + }, + { + "id": 67, + "title": "Beverly", + "size": 20 + }, + { + "id": 68, + "title": "Ines", + "size": 16 + }, + { + "id": 69, + "title": "Dudley", + "size": 17 + }, + { + "id": 70, + "title": "Hays", + "size": 19 + }, + { + "id": 71, + "title": "Russell", + "size": 32 + }, + { + "id": 72, + "title": "Lolita", + "size": 30 + }, + { + "id": 73, + "title": "Kasey", + "size": 25 + }, + { + "id": 74, + "title": "Abby", + "size": 26 + }, + { + "id": 75, + "title": "Mason", + "size": 16 + }, + { + "id": 76, + "title": "Wilcox", + "size": 12 + }, + { + "id": 77, + "title": "Talley", + "size": 27 + }, + { + "id": 78, + "title": "Hull", + "size": 31 + }, + { + "id": 79, + "title": "Harrison", + "size": 15 + }, + { + "id": 80, + "title": "Cooke", + "size": 23 + }, + { + "id": 81, + "title": "Sparks", + "size": 15 + }, + { + "id": 82, + "title": "Calhoun", + "size": 23 + }, + { + "id": 83, + "title": "Deborah", + "size": 18 + }, + { + "id": 84, + "title": "Glass", + "size": 26 + }, + { + "id": 85, + "title": "Butler", + "size": 30 + }, + { + "id": 86, + "title": "Barton", + "size": 16 + }, + { + "id": 87, + "title": "Carpenter", + "size": 19 + }, + { + "id": 88, + "title": "Roberta", + "size": 16 + }, + { + "id": 89, + "title": "Lester", + "size": 23 + }, + { + "id": 90, + "title": "Sonya", + "size": 30 + }, + { + "id": 91, + "title": "Newman", + "size": 24 + }, + { + "id": 92, + "title": "Barron", + "size": 12 + }, + { + "id": 93, + "title": "Jackie", + "size": 18 + }, + { + "id": 94, + "title": "Margarita", + "size": 20 + }, + { + "id": 95, + "title": "Taylor", + "size": 15 + }, + { + "id": 96, + "title": "Rose", + "size": 21 + }, + { + "id": 97, + "title": "Hammond", + "size": 29 + }, + { + "id": 98, + "title": "Berg", + "size": 27 + }, + { + "id": 99, + "title": "Dennis", + "size": 22 + } + ] + $scope.edges = [ + { + "source": 5, + "target": 6, + "title": "edge title" + }, + { + "source": 42, + "target": 89 + }, + { + "source": 84, + "target": 11 + }, + { + "source": 60, + "target": 10 + }, + { + "source": 17, + "target": 74 + }, + { + "source": 28, + "target": 79 + }, + { + "source": 37, + "target": 84 + }, + { + "source": 32, + "target": 46 + }, + { + "source": 2, + "target": 24 + }, + { + "source": 33, + "target": 24 + }, + { + "source": 76, + "target": 78 + }, + { + "source": 19, + "target": 70 + }, + { + "source": 45, + "target": 36 + }, + { + "source": 74, + "target": 87 + }, + { + "source": 59, + "target": 33 + }, + { + "source": 14, + "target": 66 + }, + { + "source": 10, + "target": 35 + }, + { + "source": 77, + "target": 57 + }, + { + "source": 13, + "target": 37 + }, + { + "source": 39, + "target": 18 + }, + { + "source": 37, + "target": 3 + }, + { + "source": 14, + "target": 47 + }, + { + "source": 85, + "target": 20 + }, + { + "source": 83, + "target": 84 + }, + { + "source": 65, + "target": 98 + }, + { + "source": 96, + "target": 62 + }, + { + "source": 15, + "target": 98 + }, + { + "source": 89, + "target": 8 + }, + { + "source": 26, + "target": 49 + }, + { + "source": 14, + "target": 94 + }, + { + "source": 47, + "target": 14 + }, + { + "source": 68, + "target": 84 + }, + { + "source": 52, + "target": 3 + }, + { + "source": 66, + "target": 47 + }, + { + "source": 53, + "target": 16 + }, + { + "source": 46, + "target": 73 + }, + { + "source": 99, + "target": 27 + }, + { + "source": 84, + "target": 10 + }, + { + "source": 14, + "target": 28 + }, + { + "source": 74, + "target": 50 + }, + { + "source": 30, + "target": 37 + }, + { + "source": 49, + "target": 32 + }, + { + "source": 5, + "target": 1 + }, + { + "source": 4, + "target": 61 + }, + { + "source": 25, + "target": 45 + }, + { + "source": 29, + "target": 8 + }, + { + "source": 14, + "target": 76 + }, + { + "source": 9, + "target": 71 + }, + { + "source": 58, + "target": 47 + }, + { + "source": 72, + "target": 95 + }, + { + "source": 44, + "target": 81 + }, + { + "source": 33, + "target": 57 + }, + { + "source": 61, + "target": 87 + }, + { + "source": 56, + "target": 65 + }, + { + "source": 80, + "target": 79 + }, + { + "source": 24, + "target": 54 + }, + { + "source": 94, + "target": 67 + }, + { + "source": 5, + "target": 66 + }, + { + "source": 55, + "target": 85 + }, + { + "source": 33, + "target": 53 + }, + { + "source": 32, + "target": 39 + }, + { + "source": 41, + "target": 20 + }, + { + "source": 7, + "target": 47 + }, + { + "source": 71, + "target": 6 + }, + { + "source": 23, + "target": 61 + }, + { + "source": 41, + "target": 34 + }, + { + "source": 5, + "target": 42 + }, + { + "source": 90, + "target": 72 + }, + { + "source": 70, + "target": 49 + }, + { + "source": 99, + "target": 25 + }, + { + "source": 69, + "target": 46 + }, + { + "source": 65, + "target": 90 + }, + { + "source": 93, + "target": 72 + }, + { + "source": 7, + "target": 26 + }, + { + "source": 88, + "target": 39 + }, + { + "source": 77, + "target": 52 + }, + { + "source": 86, + "target": 38 + }, + { + "source": 47, + "target": 38 + }, + { + "source": 67, + "target": 51 + }, + { + "source": 41, + "target": 12 + }, + { + "source": 29, + "target": 71 + }, + { + "source": 86, + "target": 42 + }, + { + "source": 80, + "target": 89 + }, + { + "source": 54, + "target": 53 + }, + { + "source": 93, + "target": 25 + }, + { + "source": 28, + "target": 72 + }, + { + "source": 70, + "target": 59 + }, + { + "source": 18, + "target": 88 + }, + { + "source": 60, + "target": 4 + }, + { + "source": 19, + "target": 0 + }, + { + "source": 78, + "target": 37 + }, + { + "source": 85, + "target": 54 + }, + { + "source": 83, + "target": 44 + }, + { + "source": 84, + "target": 12 + }, + { + "source": 43, + "target": 70 + }, + { + "source": 27, + "target": 81 + }, + { + "source": 85, + "target": 55 + }, + { + "source": 53, + "target": 69 + }, + { + "source": 46, + "target": 87 + }, + { + "source": 80, + "target": 98 + }, + { + "source": 31, + "target": 10 + }, + { + "source": 49, + "target": 13 + }, + { + "source": 61, + "target": 39 + }, + { + "source": 64, + "target": 10 + }, + { + "source": 96, + "target": 58 + }, + { + "source": 43, + "target": 23 + }, + { + "source": 99, + "target": 53 + }, + { + "source": 52, + "target": 21 + }, + { + "source": 12, + "target": 88 + }, + { + "source": 5, + "target": 11 + }, + { + "source": 34, + "target": 42 + }, + { + "source": 31, + "target": 97 + }, + { + "source": 56, + "target": 49 + }, + { + "source": 27, + "target": 74 + }, + { + "source": 11, + "target": 30 + } + ] + }); + +
+ */ +;angular.module('patternfly.charts').component('pfTopologyMap', { + bindings: { + nodes: '<', + edges: '<', + selectNode: '&', + multiSelectNodes: '&', + tooltipStyle: ' tolerance) { + return false; + } + //test if the point c is between a and b + if (dotproduct < 0) { + return false; + } + + if (dotproduct > squaredlengthba) { + return false; + } + return true; + }); + }; + + ctrl.getRealCoordinates = function (x, y) { + return [ + (x - ctrl.transform.x) / ctrl.transform.k, + (y - ctrl.transform.y) / ctrl.transform.k, + ]; + }; + + ctrl.setUpDrag = function () { + var drag = d3.behavior.drag(); + var canvas = d3.select(ctrl.canvas); + canvas.call(drag.on('dragstart', onDragStart).on('drag', onDrag).on('dragend', onDragEnd)); + function onDragStart () { + ctrl.tooltip = undefined; + d3.event.sourceEvent.stopPropagation(); + ctrl.draggedNode = ctrl.findNode.apply(this, ctrl.getRealCoordinates(d3.event.sourceEvent.offsetX, d3.event.sourceEvent.offsetY)); + } + function onDrag () { + var newCoordinates = ctrl.getRealCoordinates(d3.event.sourceEvent.offsetX, d3.event.sourceEvent.offsetY); + if (ctrl.draggedNode) { + d3.event.sourceEvent.stopPropagation(); + ctrl.draggedNode.px = newCoordinates[0]; + ctrl.draggedNode.py = newCoordinates[1]; + ctrl.draggedNode.fixed = true; + ctrl.force.start(); + } + } + function onDragEnd () { + d3.event.sourceEvent.stopPropagation(); + ctrl.draggedNode = undefined; + } + }; + + ctrl.setUpSemanticZoom = function () { + var canvas = d3.select(ctrl.canvas) + .call(ctrl.zoom.on("zoom", semanticZoom)); + function semanticZoom () { + var translateX; + var translateY; + if (ctrl.draggedNode) { + return; + } + if (ctrl.zoom.scale() === 1) { + translateY = 0; + translateX = 0; + } else { + translateX = Math.min(0, Math.max(ctrl.zoom.translate()[0], ctrl.canvasW - ctrl.canvasW * ctrl.zoom.scale())); + translateY = Math.min(0, Math.max(ctrl.zoom.translate()[1], ctrl.canvasH - ctrl.canvasH * ctrl.zoom.scale())); + } + ctrl.zoom.translate([translateX, translateY]); + ctrl.transform = { + x: translateX, + y: translateY, + k: ctrl.zoom.scale(), + }; + ctrl.draw(); + } + }; + + //return coordinates after canvas transformation + ctrl.transformApply = function (x, y) { + return { + x: x * ctrl.transform.k + ctrl.transform.x, + y: y * ctrl.transform.k + ctrl.transform.y, + }; + }; + + ctrl.draw = function () { + ctrl.context.clearRect(0, 0, ctrl.canvasW, ctrl.canvasH, d3.scale.linear()); + ctrl.drawEdges(); + ctrl.drawNodes(); + if (!ctrl.showNodeLabels && ctrl.tooltip && ctrl.tooltip.title) { + ctrl.drawNodeTooltip(ctrl.tooltip); + } + if (!ctrl.showEdgeLabels && ctrl.highlightEdge && ctrl.highlightEdge.title) { + ctrl.drawEdgeTooltip(ctrl.highlightEdge); + } + if (ctrl.transform.scale !== 1) { + ctrl.drawMiniMap(); + } + }; + + ctrl.setUpForce = function () { + ctrl.force = d3.layout.force() + .charge(function (d, i) { + return i ? -500 : -2500; + }) + .friction(0.5) + .chargeDistance(400) + .gravity(0) + .linkDistance(100) + .linkStrength(1) + .size([ctrl.canvasW, ctrl.canvasH]); + + ctrl.edges.forEach(function (edge) { + edge.source = _.findIndex(ctrl.nodes, function (node) { + return node.id === edge.source; + }); + edge.target = _.findIndex(ctrl.nodes, function (node) { + return node.id === edge.target; + }); + }); + + ctrl.force.nodes(ctrl.nodes) + .links(ctrl.edges) + .on('tick', tick) + .start(); + + function tick () { + ctrl.draw(); + } + }; + + ctrl.shouldHighlightEdge = function(edge) { + return _.isEqual(edge, ctrl.highlightEdge) || _.find(ctrl.edgeMultiSelect, function(e) { + return _.isEqual(edge, e); + }); + }; + + ctrl.drawEdges = function () { + var quadtree = d3.geom.quadtree(ctrl.force.nodes()); + ctrl.context.strokeStyle = 'rgba(150, 150, 150, 0.6)'; + ctrl.context.lineWidth = 1; + ctrl.edges.forEach(function (d) { + var sourceCoords; + var targetCoords; + var highlight = ctrl.shouldHighlightEdge(d); + ctrl.context.strokeStyle = highlight ? 'rgba(0, 0, 0, .5)' : 'rgba(150, 150, 150, 0.6)'; + quadtree.visit(ctrl.collide(d.source)); + quadtree.visit(ctrl.collide(d.target)); + sourceCoords = ctrl.transformApply(d.source.x, d.source.y); + targetCoords = ctrl.transformApply(d.target.x, d.target.y); + ctrl.context.beginPath(); + ctrl.context.setLineDash([]); + if (d.lineStyle === 'dashed') { + ctrl.context.setLineDash([10, 5]); + } + ctrl.context.moveTo(sourceCoords.x, sourceCoords.y); + ctrl.context.lineTo(targetCoords.x, targetCoords.y); + if (highlight) { + ctrl.context.shadowBlur = 15; + ctrl.context.shadowOffsetX = 3; + ctrl.context.shadowOffsetY = 3; + ctrl.context.shadowColor = "rgba(0, 0, 0, 0.5)"; + } else { + ctrl.context.shadowColor = 'transparent'; + } + ctrl.context.stroke(); + ctrl.context.shadowColor = 'transparent'; + if (ctrl.showEdgeLabels && d.title) { + ctrl.drawEdgeTooltip(d); + } + }); + }; + + ctrl.normalizeNode = function (node) { + node.size = node.size || 17; + node.x = Math.max(node.size + 1, Math.min(ctrl.canvasW - node.size - 1, node.x)); + node.y = Math.max(node.size + 1, Math.min(ctrl.canvasH - node.size - 1, node.y)); + }; + + ctrl.shouldHighlightNode = function(node) { + return (ctrl.tooltip && ctrl.tooltip.id === node.id) || _.find(ctrl.nodeMultiSelect, function(n) { + return n.id === node.id; + }); + }; + + ctrl.drawNodes = function () { + var coordinates; + ctrl.nodes.forEach(function (node) { + var imgR = node.size * 0.7; + var highlight = ctrl.shouldHighlightNode(node); + ctrl.normalizeNode(node); + ctrl.context.globalAlpha = node.opacity || 1; + coordinates = ctrl.transformApply(node.x, node.y); + ctrl.context.beginPath(); + ctrl.context.fillStyle = node.fill || "#FFFFFF"; + ctrl.context.strokeStyle = node.borderColor || '#000000'; + ctrl.context.lineWidth = highlight ? 3 : 1; + ctrl.context.arc(coordinates.x, coordinates.y, node.size, 0, 2 * Math.PI); + if (highlight) { + ctrl.context.shadowBlur = 20; + ctrl.context.shadowOffsetX = 5; + ctrl.context.shadowOffsetY = 5; + ctrl.context.shadowColor = "rgba(0, 0, 0, 0.5)"; + } else { + ctrl.context.shadowColor = 'transparent'; + } + ctrl.context.fill(); + ctrl.context.shadowColor = 'transparent'; + ctrl.context.stroke(); + + if (node.utilization) { + ctrl.context.beginPath(); + ctrl.context.lineWidth = 5; + ctrl.context.arc(coordinates.x, coordinates.y, node.size, 0, ((node.utilization / 100) * 2) * Math.PI); + ctrl.context.strokeStyle = pfUtils.utilizationToColor(node.utilization); + ctrl.context.stroke(); + } + + //draw icon + ctrl.context.beginPath(); + ctrl.context.fillStyle = node.iconColor || '#000000'; + ctrl.context.textAlign = 'center'; + ctrl.context.textBaseline = 'middle'; + if (node.fonticon) { + ctrl.context.font = 'normal normal normal ' + node.size + 'px FontAwesome'; + ctrl.context.fillText(ctrl.cachedIcons[node.fonticon].char, coordinates.x, coordinates.y); + } else if (node.fileicon) { + ctrl.context.drawImage(ctrl.cachedIcons[node.fileicon].img, coordinates.x - imgR, coordinates.y - imgR, 2 * imgR, 2 * imgR); + } else { + ctrl.context.font = 2 * imgR + 'px ' + ctrl.cachedIcons.unknown.font; + ctrl.context.fillText(ctrl.cachedIcons.unknown.char, coordinates.x, coordinates.y); + } + ctrl.context.globalAlpha = 1; + if (ctrl.showNodeLabels && node.title) { + ctrl.drawNodeTooltip(node); + } + }); + }; + + ctrl.drawMiniMap = function () { + var mapX = 0.9 * ctrl.canvasW - 10, + mapY = 10, + mapW = 0.1 * ctrl.canvasW, + mapH = 0.1 * ctrl.canvasH; + ctrl.context.lineWidth = 1; + ctrl.context.beginPath(); + ctrl.context.strokeStyle = 'rgba(0, 0, 0, 0.3)'; + ctrl.context.fillStyle = 'rgba(252, 252, 252, 0.3)'; + + ctrl.context.rect(mapX, mapY, mapW, mapH); + ctrl.context.stroke(); + ctrl.context.fill(); + + ctrl.context.beginPath(); + ctrl.context.fillStyle = 'rgba(224, 224, 224, 0.3)'; + ctrl.context.rect( + mapX - ctrl.transform.x / ctrl.transform.k * 0.1, + mapY - ctrl.transform.y / ctrl.transform.k * 0.1, + mapW / ctrl.transform.k, + mapH / ctrl.transform.k + ); + ctrl.context.stroke(); + ctrl.context.fill(); + }; + + ctrl.drawEdgeTooltip = function (edge) { + var sourceCoordinates = ctrl.transformApply(edge.source.x, edge.source.y); + var targetCoordinates = ctrl.transformApply(edge.target.x, edge.target.y); + var midX = sourceCoordinates.x + (targetCoordinates.x - sourceCoordinates.x) * 0.50; + var midY = sourceCoordinates.y + (targetCoordinates.y - sourceCoordinates.y) * 0.50; + var tmp = document.createElement('span'); + var tooltipWidth; + + tmp.innerHTML = edge.title; + tmp.style.padding = '10px'; + tmp.style.visibility = 'hidden'; + tmp.style.display = 'inline-block'; + //to measure width, element must be in the actual DOM + document.body.appendChild(tmp); + tooltipWidth = tmp.clientWidth;ctrl.context.beginPath(); + + ctrl.context.fillStyle = "#000000"; + ctrl.context.font = ctrl.tooltipStyle.size + 'px ' + ctrl.tooltipStyle.font; + ctrl.context.fillText(edge.title, midX, midY + 5); + + //clean the temp element + document.body.removeChild(tmp); + }; + + ctrl.drawNodeTooltip = function (node) { + var coordinates = ctrl.transformApply(node.x, node.y); + var tooltipWidth = 0; + var offsetY = coordinates.y + node.size + ctrl.tooltipStyle.size + 5; + //to determine the width of tooltip, we will use actual widt of html element, because the measureText function return inconsistent values + var tmp = document.createElement('span'); + tmp.innerHTML = node.title; + tmp.style.padding = '10px'; + tmp.style.visibility = 'hidden'; + tmp.style.display = 'inline-block'; + //to measure width, element must be in the actual DOM + document.body.appendChild(tmp); + tooltipWidth = tmp.clientWidth; + + ctrl.context.beginPath(); + ctrl.context.rect(coordinates.x - tooltipWidth / 2 - 5, offsetY - ctrl.tooltipStyle.size, tooltipWidth + 10, ctrl.tooltipStyle.size + 10); + ctrl.context.fillStyle = ctrl.tooltipStyle.background; + ctrl.context.fill(); + + ctrl.context.lineWidth = ctrl.tooltipStyle.borderWidth; + ctrl.context.strokeStyle = ctrl.tooltipStyle.borderColor; + ctrl.context.stroke(); + + ctrl.context.fillStyle = ctrl.tooltipStyle.textColor; + ctrl.context.font = ctrl.tooltipStyle.size + 'px ' + ctrl.tooltipStyle.font; + ctrl.context.fillText(node.title, coordinates.x, offsetY); + + //clean the temp element + document.body.removeChild(tmp); + }; + + ctrl.loadIcons = function () { + var tmp = document.createElement('i'); + var char = ''; + var promises = []; + var questionCode = ctrl.findIconUnicode('fa fa-question'); + var q = $q.defer(); + var code = ''; + document.body.appendChild(tmp); + ctrl.cachedIcons.unknown = {}; + tmp.className = 'hidden fa fa-question'; + char = window.getComputedStyle(tmp, ':before').content.replace(/'|"/g, ''); + if (ctrl.IE11 && questionCode) { + ctrl.cachedIcons.unknown.char = String.fromCharCode(questionCode.toUpperCase().replace('\\', '0x').replace(/'|"/g, '')); + } else { + ctrl.cachedIcons.unknown.char = char; + } + ctrl.cachedIcons.unknown.font = window.getComputedStyle(tmp, ':before').fontFamily; + ctrl.nodes.forEach(function (node) { + if (node.fileicon && !ctrl.cachedIcons[node.fileicon]) { + ctrl.cachedIcons[node.fileicon] = {}; + promises.push(q.promise); + ctrl.cachedIcons[node.fileicon].img = new Image(); + ctrl.cachedIcons[node.fileicon].img.src = node.fileicon; + ctrl.cachedIcons[node.fileicon].img.onload = function () { + return q.resolve(); + }; + } else if (node.fonticon && !ctrl.cachedIcons[node.fonticon]) { + ctrl.cachedIcons[node.fonticon] = {}; + tmp.className = 'hidden ' + node.fonticon; + char = window.getComputedStyle(tmp, ':before').content; + ctrl.cachedIcons[node.fonticon].char = char.replace(/'|"/g, ''); + if (ctrl.IE11) { + code = ctrl.findIconUnicode(node.fonticon).toUpperCase().replace('\\', '0x'); + ctrl.cachedIcons[node.fonticon].char = String.fromCharCode(code.replace(/'|"/g, '')); + } + ctrl.cachedIcons[node.fonticon].font = window.getComputedStyle(tmp, ':before').fontFamily; + } + }); + document.body.removeChild(tmp); + }; + + this.collide = function (node) { + var r = node.size + 22, + nx1 = node.x - r, + nx2 = node.x + r, + ny1 = node.y - r, + ny2 = node.y + r; + return function (quad, x1, y1, x2, y2) { + var x, l, y, r; + if (quad.point && (quad.point !== node)) { + x = node.x - quad.point.x; + y = node.y - quad.point.y; + l = Math.sqrt(x * x + y * y); + r = 30 * node.size + quad.point.radius; + if (l < r) { + l = (l - r) / l * 2.5; + node.x -= x *= l; + node.y -= y *= l; + quad.point.x += x; + quad.point.y += y; + } + } + return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; + }; + }; + + ctrl.findRules = function () { + var href; + var index; + var styleSheet = _.find(document.styleSheets, function(sheet) { + if (sheet && sheet.href) { + href = sheet.href; + index = sheet.href.lastIndexOf('/'); + return href.substring(index) === '/patternfly.css'; + } + }); + return styleSheet ? styleSheet.rules : undefined; + }; + + ctrl.findIconUnicode = function(fonticon) { + var rule; + var className = fonticon.substring(fonticon.indexOf(' ') + 1); + if (ctrl.rules) { + rule = _.find(ctrl.rules, function(rule) { + if (rule && rule.selectorText) { + return rule.selectorText.indexOf(className + '::before') !== -1; + } + }); + } + return rule ? rule.style.content : undefined; + }; + }], +}); +;/** + * @ngdoc directive + * @name patternfly.charts.component:pfTopology + * @restrict E + * + * @description + * Component for rendering a topology chart. Individual nodes and relationships can be represented with this view. CSS is especially important for rendering the nodes and lines. The example inline contains specific examples that can be used to change the icon size and the line type of the relationships. + * + * In addition; searching, filtering and label visibility is also supported.
+ * + * @param {object} items items to display in the topology chart, each is represented as an individual node. The keys of this object are used in the relations attribute. The items should have a item.kind attribute, as well as the usual item.metadata and so on.:
+ *
    + *
  • .name - name of the item the node represents + *
  • .status - optional status of the node (can be used to differentiate the circle color) + *
  • .kind - the kind of node - this is a general key that needs to be unique for grouping the nodes Filtering and styles use this value as well to correctly select the nodes. + *
+ * + * @param {object} relations the object containing all of the node relationships:
+ *
    + *
  • .source - the key of the source node + *
  • .target - the key of the target node + *
+ * + * @param {object} icons The different icons to be used in the node representations + * @param {object} selection The item to be selected + * @param {object} force Optional. A D3 force layout to use instead of creating one by default. The force layout size will be updated, and layout will be started as appropriate. + * @param {object} nodes The node configuration for the various types of nodes
+ * @param {string} searchText Search text which is watched for changes and highlights the nodes matching the search text + * @param {object} kinds The different kinds of nodes represented in the topology chart + * @param {function (vertices, added) } chartRendered The argument will be D3 selection of elements that correspond to items. Each item has its data set to one of the items. The default implementation of this event sets the title from Kubernetes metadata and tweaks the look of for certain statuses. Use event.preventDefault() to prevent this default behavior. + * @param {boolean} itemSelected A function that is dispatched when an item is selected (along with the node data associated with the function + * @param {boolean} showLabels A watched boolean that determines whether or not lables should be displayed beneath the nodes + * @param {function (node) } tooltipFunction A passed in tooltip function which can be used to overwrite the default tooltip behavior + * + * @example + + +
+ + + +
+ + + + + + +
+
+
+ + + angular.module( 'patternfly.charts' ).controller( 'TopologyCtrl', function( $scope, $rootScope ) { + var index = 0; + var datasets = []; + + function sink(dataset) { + datasets.push(dataset); + } + + sink({ + "items": { + "ContainerManager10r20": { + "name": "ocp-master.example.com", + "kind": "ContainerManager", + "miq_id": 10000000000020, + "status": "Valid", + "display_kind": "OpenshiftEnterprise" + }, + "ContainerNode10r14": { + "name": "ocp-master.example.com", + "kind": "ContainerNode", + "miq_id": 10000000000014, + "status": "Ready", + "display_kind": "Node" + }, + "ContainerGroup10r240": { + "name": "docker-registry-2-vrguw", + "kind": "ContainerGroup", + "miq_id": 10000000000240, + "status": "Running", + "display_kind": "Pod" + }, + "Container10r235": { + "name": "registry", + "kind": "Container", + "miq_id": 10000000000235, + "status": "Error", + "display_kind": "Container" + }, + "ContainerReplicator10r56": { + "name": "docker-registry-2", + "kind": "ContainerReplicator", + "miq_id": 10000000000056, + "status": "OK", + "display_kind": "Replicator" + }, + "ContainerService10r61": { + "name": "docker-registry", + "kind": "ContainerService", + "miq_id": 10000000000061, + "status": "Unknown", + "display_kind": "Service" + } + }, + "relations": [ + { + "source": "ContainerManager10r20", + "target": "ContainerNode10r14" + }, { + "source": "ContainerNode10r14", + "target": "ContainerGroup10r240" + }, { + "source": "ContainerGroup10r240", + "target": "Container10r235" + }, { + "source": "ContainerGroup10r240", + "target": "ContainerReplicator10r56" + }, { + "source": "ContainerGroup10r240", + "target": "ContainerService10r61" + }, { + "source": "ContainerNode10r14", + "target": "ContainerGroup10r241" + }, { + "source": "ContainerGroup10r241", + "target": "Container10r236" + }, { + "source": "ContainerGroup10r241", + "target": "ContainerReplicator10r57" + } + ], + "icons": { + "AvailabilityZone": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "ContainerReplicator": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "ContainerGroup": { + "type": "glyph", + "icon": "", + "fontfamily": "FontAwesome" + }, + "ContainerNode": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "ContainerService": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "ContainerRoute": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "Container": { + "type": "glyph", + "icon": "", + "fontfamily": "FontAwesome" + }, + "Host": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "Vm": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + }, + "ContainerManager": { + "type": "glyph", + "icon": "", + "fontfamily": "PatternFlyIcons-webfont" + } + } + }); + + $rootScope.data = datasets[index]; + + var nodeKinds = { + "ContainerReplicator": true, + "ContainerGroup": true, + "Container": true, + "ContainerNode": true, + "ContainerService": true, + "Host": true, + "Vm": true, + "ContainerRoute": true, + "ContainerManager": true + }; + + $rootScope.kinds = nodeKinds; + + var icons = $rootScope.data.icons; + $scope.nodes = {}; + for(var kind in nodeKinds) { + var icon = icons[kind]; + $scope.nodes[kind] = { + "name": kind, + "enabled": nodeKinds[kind], + "radius": 16, + "textX": 0, + "textY": 5, + "height": 18, + "width": 18, + "icon": icon.icon, + "fontFamily": icon.fontfamily + }; + } + + // Individual values can also be set for specific icons + $scope.nodes.ContainerService.textY = 9; + $scope.nodes.ContainerService.textX = -1; + + $scope.nodes.ContainerGroup.height = 30; + $scope.nodes.ContainerGroup.width = 30; + $scope.nodes.ContainerGroup.radius = 28; + $scope.nodes.ContainerGroup.textY = 8; + + $scope.itemSelected = function (item) { + var text = ""; + if (item) { + text = "Selected: " + item.name; + } + angular.element(document.getElementById("selected")).text(text); + }; + + $scope.removeKind = function () { + if($rootScope.kinds.ContainerNode) { + delete $rootScope.kinds.ContainerNode; + } + }; + + $scope.tooltip = function (node) { + var status = [ + 'Name: ' + node.item.name, + 'Type: ' + node.item.kind, + 'Status: ' + node.item.status + ]; + return status; + } + }); + + + + .pf-topology-svg g.Pod text { + font-family: FontAwesome; + font-size: 16px; + fill: #1186C1; + } + + .pf-topology-svg g.Node text { + fill: #636363; + } + + .pf-topology-svg g.Service text { + fill: #ff7f0e; + } + + .pf-topology-svg g.ReplicationController text { + fill: #9467bd; + font-size: 20px; + } + + .pf-topology-svg line.ReplicationControllerPod { + stroke-linecap: round; + stroke-dasharray: 5, 2; + } + + + .pf-topology-svg line.ContainerServiceContainerGroup, .pf-topology-svg line.ContainerReplicatorContainerGroup, .pf-topology-svg line.ContainerServiceContainerRoute, + .pf-topology-svg line.ContainerGroupContainerService, .pf-topology-svg line.ContainerGroupContainerReplicator { + stroke-linecap: round; + stroke-dasharray: 5.5; + } + + + .pf-topology-svg g.Container text.glyph { + font-size: 18px; + } + + .pf-topology-svg g.ContainerGroup text.glyph { + font-size: 28px; + } + + .pf-topology-svg g.Vm text.glyph, .pf-topology-svg g.Host text.glyph { + fill: #636363; + } + + .pf-topology-svg g.ContainerNode text.glyph { + font-size: 18px; + } + + .pf-topology-svg g.ContainerManager text.glyph { + font-size: 18px; + } + + +
+ */ +;angular.module('patternfly.charts').component('pfTopology', { + bindings: { + items: '<', + relations: '<', + kinds: '<', + icons: '<', + selection: '<', + force: '<', + radius: '<', + nodes: '<', + searchText: ' d.floatpoint[0] + 5) || + (d.y < d.floatpoint[1] - 5 || d.y > d.floatpoint[1] + 5); + delete d.floatpoint; + } + d.fixed = moved && d.x > 3 && d.x < (width - 3) && d.y >= 3 && d.y < (height - 3); + d3.select(this).classed("fixed", d.fixed); + }); + + svg + .on("dblclick", function () { + svg.selectAll("g") + .classed("fixed", false) + .each(function (d) { + d.fixed = false; + }); + force.start(); + }) + .on("click", function (ev) { + if (!d3.select(d3.event.target).datum()) { + notify(null); + } + }); + + function select (item) { + if (item !== undefined) { + selection = item; + } + svg.selectAll("g") + .classed("selected", function (d) { + return d.item === selection; + }); + } + + function adjust () { + timeout = null; + width = outer.node().clientWidth; + height = outer.node().clientHeight; + force.size([width, height]); + svg.attr("viewBox", "0 0 " + width + " " + height); + update(); + } + + function update () { + var added; + + edges = svg.selectAll("line") + .data(links); + + edges.exit().remove(); + edges.enter().insert("line", ":first-child"); + + edges.attr("class", function (d) { + return d.kinds; + }); + + vertices = svg.selectAll("g") + .data(nodes, function (d) { + return d.id; + }); + + vertices.exit().remove(); + + added = vertices.enter().append("g") + .call(drag); + + select(selection); + + force + .nodes(nodes) + .links(links) + .start(); + + return added; + } + + function digest () { + var pnodes = nodes; + var plookup = lookup; + var item, id, kind, node; + var i, len, relation, s, t; + /* The actual data for the graph */ + nodes = []; + links = []; + lookup = {}; + + for (id in items) { + if (id) { + item = items[id]; + kind = item.kind; + + if (kinds && !kinds[kind]) { + continue; + } + + /* Prevents flicker */ + node = pnodes[plookup[id]]; + if (!node) { + node = cache[id]; + delete cache[id]; + if (!node) { + node = {}; + } + } + + node.id = id; + node.item = item; + + lookup[id] = nodes.length; + nodes.push(node); + } + } + for (i = 0, len = relations.length; i < len; i++) { + relation = relations[i]; + + s = lookup[relation.source]; + t = lookup[relation.target]; + if (s === undefined || t === undefined) { + continue; + } + + links.push({source: s, target: t, kinds: nodes[s].item.kind + nodes[t].item.kind}); + } + + if (width && height) { + return update(); + } + return d3.select(); + } + + function resized () { + window.clearTimeout(timeout); + timeout = window.setTimeout(adjust, 1); + } + + window.addEventListener('resize', resized); + + adjust(); + resized(); + + return { + select: select, + kinds: function (value) { + var added; + kinds = value; + added = digest(); + return [vertices, added]; + }, + data: function (newItems, newRelations) { + var added; + items = newItems || {}; + relations = newRelations || []; + added = digest(); + return [vertices, added]; + }, + close: function () { + var id, node; + window.removeEventListener('resize', resized); + window.clearTimeout(timeout); + /* + * Keep the positions of these items cached, + * in case we are asked to make the same graph again. + */ + cache = {}; + for (id in lookup) { + if (id) { + node = nodes[lookup[id]]; + delete node.item; + cache[id] = node; + } + } + + nodes = []; + lookup = {}; + } + }; + } + + function search (query) { + var svg = getSVG(); + var nodes = svg.selectAll("g"); + var selected, links; + if (query !== "") { + selected = nodes.filter(function (d) { + return d.item.name.indexOf(query) === -1; + }); + selected.style("opacity", "0.2"); + links = svg.selectAll("line"); + links.style("opacity", "0.2"); + } + } + + function resetSearch (d3) { + // Display all topology nodes and links + d3.selectAll("g, line").transition() + .duration(2000) + .style("opacity", 1); + } + + function toggleLabelVisibility () { + if (ctrl.showLabels) { + vs.selectAll("text.attached-label") + .classed("visible", true); + } else { + vs.selectAll("text.attached-label") + .classed("visible", false); + } + } + + function getSVG () { + var graph = d3.select("pf-topology"); + var svg = graph.select('svg'); + return svg; + } + + function notify (item) { + ctrl.itemSelected({item: item}); + if ($attrs.selection === undefined) { + graph.select(item); + } + } + + function icon (d) { + return '#' + d.item.kind; + } + + function title (d) { + return d.item.name; + } + + function render (args) { + var vertices = args[0]; + var added = args[1]; + var event; + + // allow custom rendering of chart + if (angular.isFunction(ctrl.chartRendered)) { + event = ctrl.chartRendered({vertices: vertices, added: added}); + } + + if (!event || !event.defaultPrevented) { + added.attr("class", function (d) { + return d.item.kind; + }); + + added.append("circle") + .attr("r", function (d) { + return getDimensions(d).r; + }) + .attr('class', function (d) { + return getItemStatusClass(d); + }) + .on("contextmenu", function (d) { + contextMenu(ctrl, d); + }); + + added.append("title"); + + added.on("dblclick", function (d) { + return dblclick(d); + }); + + added.append("image") + .attr("xlink:href", function (d) { + // overwrite this . . . + var iconInfo = ctrl.icons[d.item.kind]; + switch (iconInfo.type) { + case 'image': + return iconInfo.icon; + case "glyph": + return null; + } + }) + .attr("height", function (d) { + var iconInfo = ctrl.icons[d.item.kind]; + if (iconInfo.type !== 'image') { + return 0; + } + return 40; + }) + .attr("width", function (d) { + var iconInfo = ctrl.icons[d.item.kind]; + if (iconInfo.type !== 'image') { + return 0; + } + return 40; + }) + .attr("y", function (d) { + return getDimensions(d).y; + }) + .attr("x", function (d) { + return getDimensions(d).x; + }) + .on("contextmenu", function (d) { + contextMenu(ctrl, d); + }); + + added.append("text") + .each(function (d) { + var iconInfo = ctrl.icons[d.item.kind]; + if (iconInfo.type !== 'glyph') { + return; + } + d3.select(this).text(iconInfo.icon) + .attr("class", "glyph") + .attr('font-family', iconInfo.fontfamily); + }) + + .attr("y", function (d) { + return getDimensions(d).y; + }) + .attr("x", function (d) { + return getDimensions(d).x; + }) + .on("contextmenu", function (d) { + contextMenu(this, d); + }); + + + added.append("text") + .attr("x", 26) + .attr("y", 24) + .text(function (d) { + return d.item.name; + }) + .attr('class', function () { + var className = "attached-label"; + if (ctrl.showLabels) { + return className + ' visible'; + } + return className; + }); + + added.selectAll("title").text(function (d) { + return tooltip(d).join("\n"); + }); + + vs = vertices; + } + graph.select(); + } + + function tooltip (d) { + if (ctrl.tooltipFunction) { + return ctrl.tooltipFunction({node: d}); + } + return 'Name: ' + d.item.name; + } + + function removeContextMenu () { + d3.event.preventDefault(); + d3.select('.popup').remove(); + contextMenuShowing = false; + } + + function contextMenu (that, data) { + var canvasSize, popupSize, canvas, mousePosition, popup; + + if (contextMenuShowing) { + removeContextMenu(); + } else { + d3.event.preventDefault(); + + canvas = d3.select('pf-topology'); + mousePosition = d3.mouse(canvas.node()); + + popup = canvas.append('div') + .attr('class', 'popup') + .style('left', mousePosition[0] + 'px') + .style('top', mousePosition[1] + 'px'); + popup.append('h5').text('Actions on ' + data.item.kind); + + buildContextMenuOptions(popup, data); + + canvasSize = [ + canvas.node().offsetWidth, + canvas.node().offsetHeight + ]; + + popupSize = [ + popup.node().offsetWidth, + popup.node().offsetHeight + ]; + + if (popupSize[0] + mousePosition[0] > canvasSize[0]) { + popup.style('left', 'auto'); + popup.style('right', 0); + } + + if (popupSize[1] + mousePosition[1] > canvasSize[1]) { + popup.style('top', 'auto'); + popup.style('bottom', 0); + } + contextMenuShowing = !contextMenuShowing; + } + } + + function buildContextMenuOptions (popup, data) { + if (data.item.kind === 'Tag') { + return false; + } + addContextMenuOption(popup, 'Go to summary page', data, dblclick); + } + + function dblclick (d) { + window.location.assign(d.url); + } + + function addContextMenuOption (popup, text, data, callback) { + popup.append('p').text(text).on('click', function () { + callback(data); + }); + } + + function getDimensions (d) { + var nodeEntry = ctrl.nodes[d.item.kind]; + var defaultDimensions = defaultElementDimensions(); + if (nodeEntry) { + if (nodeEntry.textX) { + defaultDimensions.x = nodeEntry.textX; + } + if (nodeEntry.textY) { + defaultDimensions.y = nodeEntry.textY; + } + + if (nodeEntry.radius) { + defaultDimensions.r = nodeEntry.radius; + } + } + return defaultDimensions; + } + + function defaultElementDimensions () { + return { x: 0, y: 9, r: 17 }; + } + + function getItemStatusClass (d) { + switch (d.item.status.toLowerCase()) { + case "ok": + case "active": + case "available": + case "on": + case "ready": + case "running": + case "succeeded": + case "valid": + return "success"; + case "notready": + case "failed": + case "error": + case "unreachable": + return "error"; + case 'warning': + case 'waiting': + case 'pending': + return "warning"; + case 'unknown': + case 'terminated': + return "unknown"; + } + } + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.directive:pfTrendsChart + * @restrict E + * + * @description + * Component for rendering a trend chart. The trend chart combines overall data with a + * pfSparklineChart. + *

+ * See http://c3js.org/reference.html for a full list of C3 chart options.
+ * See also: {@link patternfly.charts.component:pfSparklineChart} + * + * @param {object} config configuration settings for the trends chart:
+ *
    + *
  • .chartId - the unique id of this trends chart + *
  • .title - (optional) title of the Trends chart + *
  • .layout - (optional) the layout and sizes of titles and chart. Values are 'large' (default), 'small', 'compact', and 'inline' + *
  • .compactLabelPosition - (optional) the trend label positioning when the layout value is 'compact'. Values are 'left' (default) or 'right' + *
  • .trendLabel - (optional) the trend label used in the 'inline' layout + *
  • .timeFrame - (optional) the time frame for the data in the pfSparklineChart, ex: 'Last 30 Days' + *
  • .units - unit label for values, ex: 'MHz','GB', etc.. + *
  • .valueType - (optional) the format of the latest data point which is shown in the title. Values are 'actual'(default) or 'percentage' + *
+ * + * @param {object} chartData the data to be shown in the sparkline charts
+ *
    + *
  • .total - number representing the total amount + *
  • .xData - Array, X values for the data points, first element must be the name of the data + *
  • .yData - Array, Y Values for the data points, first element must be the name of the data + *
  • .dataAvailable - Flag if there is data available - default: true + *
+ * + * @param {int=} chartHeight height of the sparkline chart + * @param {boolean=} showXAxis override sparkline config settings for showing the X Axis + * @param {boolean=} showYAxis override sparkline config settings for showing the Y Axis + * @example + + +
+
+ +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ + angular.module( 'demo', ['patternfly.charts', 'patternfly.card', 'ui.bootstrap'] ).controller( 'ChartCtrl', function( $scope ) { + + $scope.config = { + chartId : 'exampleTrendsChart', + title : 'Network Utilization Trends', + layout : 'large', + trendLabel : 'Virtual Disk I/O', + valueType : 'actual', + timeFrame : 'Last 15 Minutes', + units : 'MHz', + tooltipType : 'percentage', + compactLabelPosition : 'left' + }; + + $scope.footerConfig = { + iconClass : 'fa fa-plus-circle', + text : 'Add New Cluster', + callBackFn: function () { + alert("Footer Callback Fn Called"); + } + }; + + $scope.filterConfig = { + filters : [{label:'Last 30 Days', value:'30'}, + {label:'Last 15 Days', value:'15'}, + {label:'Today', value:'today'}], + callBackFn: function (f) { + alert("Filter Callback Fn Called for '" + f.label + "' value = " + f.value); + } + }; + + $scope.layouts = [ + { + title: "Large", + value: "large" + }, + { + title: "Small", + value: "small" + }, + { + title: "Compact", + value: "compact" + }, + { + title: "Inline", + value: "inline" + } + ]; + + $scope.layout = $scope.layouts[0]; + + $scope.updateLayout = function(item) { + $scope.layout = item; + $scope.config.layout = item.value; + }; + + $scope.valueTypes = [ + { + title: "Actual", + value: "actual" + }, + { + title: "Percentage", + value: "percentage" + } + ]; + + $scope.valueType = $scope.valueTypes[0]; + + $scope.updateValueType = function(item) { + $scope.valueType = item; + $scope.config.valueType = item.value; + }; + + var today = new Date(); + var dates = ['dates']; + for (var d = 20 - 1; d >= 0; d--) { + dates.push(new Date(today.getTime() - (d * 24 * 60 * 60 * 1000))); + } + + $scope.data = { + dataAvailable: true, + total: 250, + xData: dates, + yData: ['used', 10, 20, 30, 20, 30, 10, 14, 20, 25, 68, 54, 56, 78, 56, 67, 88, 76, 65, 87, 76] + }; + + $scope.custShowXAxis = false; + $scope.custShowYAxis = false; + + $scope.addDataPoint = function () { + $scope.data.xData.push(new Date($scope.data.xData[$scope.data.xData.length - 1].getTime() + (24 * 60 * 60 * 1000))); + $scope.data.yData.push(Math.round(Math.random() * 100)); + }; + }); + +
+ */ +angular.module('patternfly.charts').component('pfTrendsChart', { + bindings: { + config: '<', + chartData: '<', + chartHeight: ' 0) { + pctValue = Math.round(ctrl.getLatestValue() / ctrl.chartData.total * 100.0); + } + return pctValue; + }; + ctrl.getLatestValue = function () { + var latestValue = 0; + if (ctrl.chartData.yData && ctrl.chartData.yData.length > 0) { + latestValue = ctrl.chartData.yData[ctrl.chartData.yData.length - 1]; + } + return latestValue; + }; + ctrl.getChartHeight = function () { + var retValue = LARGE; + if (ctrl.chartHeight) { + retValue = ctrl.chartHeight; + } else if (ctrl.config.layout === 'small') { + retValue = SMALL; + } + return retValue; + }; + + ctrl.$onChanges = function (changesObj) { + ctrl.updateAll(); + }; + + ctrl.$doCheck = function () { + // do a deep compare on chartData and config + if (!angular.equals(ctrl.chartData, prevChartData) || !angular.equals(ctrl.config, prevConfig)) { + ctrl.updateAll(); + } + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.directive:pfUtilizationBarChart + * @restrict E + * + * @description + * Component for rendering a utilization bar chart + * There are three possible fill colors for Used Percentage, dependent on whether or not there are thresholds:
+ *
    + *
  • When no thresholds exist, or if the used percentage has not surpassed any thresholds, the indicator is blue. + *
  • When the used percentage has surpassed the warning threshold, but not the error threshold, the indicator is orange. + *
  • When the used percentage has surpassed the error threshold, the indicator is is red. + *
+ * + * @param {object} chartData the data to be shown in the utilization bar chart
+ *
    + *
  • .used - number representing the amount used + *
  • .total - number representing the total amount + *
  • .dataAvailable - Flag if there is data available - default: true + *
+ * + * @param {object=} chart-title The title displayed on the left-hand side of the chart + * @param {object=} chart-footer The label displayed on the right-hand side of the chart. If chart-footer is not + * specified, the automatic footer-label-format will be used. + * @param {object=} layout Various alternative layouts the utilization bar chart may have:
+ *
    + *
  • .type - The type of layout to use. Valid values are 'regular' (default) displays the standard chart layout, + * and 'inline' displays a smaller, inline layout.
  • + *
  • .titleLabelWidth - Width of the left-hand title label when using 'inline' layout. Example values are "120px", "20%", "10em", etc..
  • + *
  • .footerLabelWidth - Width of the right-hand used label when using 'inline' layout. Example values are "120px", "20%", "10em", etc..
  • + *
+ * @param {string=} footer-label-format The auto-format of the label on the right side of the bar chart when chart-footer + * has not been specified. Values may be:
+ *
    + *
  • 'actual' - (default) displays the standard label of '(n) of (m) (units) Used'. + *
  • 'percent' - displays a percentage label of '(n)% Used'.
  • + *
+ * @param {object=} units to be displayed on the chart. Examples: "GB", "MHz", "I/Ops", etc... + * @param {string=} threshold-error The percentage used, when reached, denotes an error. Valid values are 1-100. When the error threshold + * has been reached, the used donut arc will be red. + * @param {string=} threshold-warning The percentage usage, when reached, denotes a warning. Valid values are 1-100. When the warning threshold + * has been reached, the used donut arc will be orange. + * @param {function(items)} avaliableTooltipFunction A passed in tooltip function which can be used to overwrite the default available tooltip behavior + * @param {function(items)} usedTooltipFunction A passed in tooltip function which can be used to overwrite the default usedtooltip behavior + * + * @example + + +
+ + + +
+ + +
+ + ../utilization-trend/utilization-trend-chart-directive.js + + +
+ + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + angular.module( 'patternfly.example', ['patternfly.charts', 'patternfly.card']); + + angular.module( 'patternfly.example' ).controller( 'ChartCtrl', function( $scope, $interval ) { + + $scope.title1 = 'RAM Usage'; + $scope.units1 = 'MB'; + + $scope.data1 = { + 'dataAvailable': true, + 'used': '8', + 'total': '24' + }; + + $scope.title2 = 'Memory'; + $scope.units2 = 'GB'; + + $scope.data2 = { + 'used': '25', + 'total': '100' + }; + + $scope.title3 = 'CPU Usage'; + $scope.units3 = 'MHz'; + + $scope.data3 = { + 'used': '420', + 'total': '500' + }; + + $scope.title4 = 'Disk Usage'; + $scope.units4 = 'TB'; + $scope.data4 = { + 'used': '350', + 'total': '500' + }; + + $scope.title5 = 'Disk I/O'; + $scope.units5 = 'I/Ops'; + $scope.data5 = { + 'used': '450', + 'total': '500' + }; + + $interval(function () { + $scope.data5.used = Number($scope.data5.used) + 40; + if ($scope.data5.used > 500) { + $scope.data5.used = 10; + } + }, 1000); + + $scope.layoutInline = { + 'type': 'inline' + }; + + $scope.footer1 = '500 TB Total'; + $scope.footer2 = '450 of 500 Total'; + $scope.availableTooltip = function (title, data){ + return '
Title: ' + title + '
Available: ' + data.total + '
'; + }; + $scope.usedTooltip = function (title, data){ + return '
Title: ' + title + '
Usage: ' + data.used + 'MB
'; + }; + }); +
+
+*/ + +angular.module('patternfly.charts').component('pfUtilizationBarChart', { + bindings: { + chartData: '=', + chartTitle: '=', + chartFooter: '=', + units: '=', + thresholdError: '=?', + thresholdWarning: '=?', + footerLabelFormat: '@?', + layout: '=?', + usedTooltipFunction: '&?', + availableTooltipFunction: '&?' + }, + + templateUrl: 'charts/utilization-bar/utilization-bar-chart.html', + controller: ["$timeout", function ($timeout) { + 'use strict'; + var ctrl = this, prevChartData, prevLayout; + + ctrl.updateAll = function () { + // Need to deep watch changes + prevChartData = angular.copy(ctrl.chartData); + prevLayout = angular.copy(ctrl.layout); + + if (!ctrl.chartData) { + return; + } + + //Calculate the percentage used + if (!isNaN(ctrl.chartData.used) && !isNaN(ctrl.chartData.total) && (ctrl.chartData.total > 0)) { + ctrl.chartData.percentageUsed = Math.round(100 * (ctrl.chartData.used / ctrl.chartData.total)); + } else { + ctrl.chartData.percentageUsed = 0; + } + + if (ctrl.thresholdError || ctrl.thresholdWarning) { + ctrl.isError = (ctrl.chartData.percentageUsed >= ctrl.thresholdError); + ctrl.isWarn = (ctrl.chartData.percentageUsed >= ctrl.thresholdWarning && + ctrl.chartData.percentageUsed < ctrl.thresholdError); + ctrl.isOk = (ctrl.chartData.percentageUsed < ctrl.thresholdWarning); + } + + //Animate in the chart load. + ctrl.animate = true; + $timeout(function () { + ctrl.animate = false; + }, 0); + }; + + ctrl.$onChanges = function (changesObj) { + ctrl.updateAll(); + }; + + ctrl.$doCheck = function () { + // do a deep compare on chartData and layout + if (!angular.equals(ctrl.chartData, prevChartData) || !angular.equals(ctrl.layout, prevLayout)) { + ctrl.updateAll(); + } + }; + + ctrl.usedTooltipMessage = function () { + return ctrl.usedTooltipFunction ? ctrl.usedTooltipFunction() : _.get(ctrl.chartData, 'percentageUsed', 'N/A') + '% Used'; + }; + + ctrl.availableTooltipMessage = function () { + return ctrl.availableTooltipFunction ? ctrl.availableTooltipFunction() : (100 - _.get(ctrl.chartData, 'percentageUsed', 0)) + '% Available'; + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.charts.directive:pfUtilizationTrendChart + * @restrict E + * + * @description + * Component for rendering a utilization trend chart. The utilization trend chart combines overall + * data with a pfDonutPctChart and a pfSparklineChart. Add the options for the pfDonutChart via + * the donutConfig parameter. Add the options for the pfSparklineChart via the sparklineConfig + * parameter. + *

+ * See http://c3js.org/reference.html for a full list of C3 chart options. + * + * @param {object} config configuration settings for the utilization trend chart:
+ *
    + *
  • .title - title of the Utilization chart + *
  • .units - unit label for values, ex: 'MHz','GB', etc.. + *
+ * + * @param {object} donutConfig configuration settings for the donut pct chart, see pfDonutPctChart for specifics
+ * @param {object} sparklineConfig configuration settings for the sparkline chart, see pfSparklineChart for specifics
+ * + * @param {object} chartData the data to be shown in the donut and sparkline charts
+ *
    + *
  • .used - number representing the amount used + *
  • .total - number representing the total amount + *
  • .xData - Array, X values for the data points, first element must be the name of the data + *
  • .yData - Array, Y Values for the data points, first element must be the name of the data + *
  • .dataAvailable - Flag if there is data available - default: true + *
+ * + * @param {string=} donutCenterLabel specifies the contents of the donut's center label.
+ * Values: + *
    + *
  • 'used' - displays the Used amount in the center label (default) + *
  • 'available' - displays the Available amount in the center label + *
  • 'percent' - displays the Usage Percent of the Total amount in the center label + *
  • 'none' - does not display the center label + *
+ * @param {int=} sparklineChartHeight height of the sparkline chart + * @param {boolean=} showSparklineXAxis override sparkline config settings for showing the X Axis + * @param {boolean=} showSparklineYAxis override sparkline config settings for showing the Y Axis + + * @example + + +
+
+ + +
+
+
+
+
+ +
+ + + + +
+
+
+
+ +
+ + + + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + angular.module( 'patternfly.charts' ).controller( 'ChartCtrl', function( $scope ) { + $scope.config = { + title: 'Memory', + units: 'GB' + }; + $scope.donutConfig = { + chartId: 'chartA', + thresholds: {'warning':'60','error':'90'} + }; + $scope.sparklineConfig = { + 'chartId': 'exampleSparkline', + 'tooltipType': 'default', + 'units': 'GB' + }; + + var today = new Date(); + var dates = ['dates']; + for (var d = 20 - 1; d >= 0; d--) { + dates.push(new Date(today.getTime() - (d * 24 * 60 * 60 * 1000))); + } + + $scope.data = { + dataAvailable: true, + used: 76, + total: 100, + xData: dates, + yData: ['used', '10', '20', '30', '20', '30', '10', '14', '20', '25', '68', '54', '56', '78', '56', '67', '88', '76', '65', '87', '76'] + }; + + $scope.centerLabel = 'used'; + + $scope.custShowXAxis = false; + $scope.custShowYAxis = false; + $scope.custChartHeight = 60; + + $scope.addDataPoint = function () { + var newData = Math.round(Math.random() * 100); + var newDate = new Date($scope.data.xData[$scope.data.xData.length - 1].getTime() + (24 * 60 * 60 * 1000)); + + $scope.data.used = newData; + $scope.data.xData.push(newDate); + $scope.data.yData.push(newData); + }; + }); + + + */ +angular.module('patternfly.charts').component('pfUtilizationTrendChart', { + bindings: { + chartData: '<', + config: '<', + centerLabel: ' + * @param {boolean} isOpen Optional boolean for determining whether or not to have the datepicker default to open (false by default). + * @param {string} popupPlacement Optional configuration string used to position the popup datepicker relative to the input element. See {@link https://angular-ui.github.io/bootstrap/#datepickerPopup Angular UI Datepicker Popup}. + * @param {object} dateOptions Optional uib-datepicker configuration object. See {@link https://angular-ui.github.io/bootstrap/#datepicker Angular UI Datepicker}. + * + * @description + * A wrapper for the Angular UI {@link http://angular-ui.github.io/bootstrap/#!#datepickerPopup datepicker}. + * + * @example + + + +
+
+ + + + + + +
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.datepicker').controller('DemoBootstrapDatepicker', function( $scope ) { + $scope.eventText = ''; + + // first datepicker + $scope.dateOne = new Date("Jan 1, 2000"); + $scope.firstDateChanged = function(date) { + $scope.eventText = 'Date One Updated to: ' + date + '\r\n' + $scope.eventText; + }; + + // second datepicker + $scope.dateTwo = new Date("Feb 1, 2000"); + $scope.format1 = "MM/dd/yy"; + $scope.dateOptions = { + showWeeks : true + }; + $scope.isOpen = true; + $scope.popupPlacement = "bottom-left"; + + $scope.secondDateChanged = function(date) { + $scope.eventText = 'Date Two Updated to: ' + date + '\r\n' + $scope.eventText; + }; + }); + + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.filters.component:pfFilterPanel + * @restrict E + * + * @description + * The Filter Panel is opened and closed by clicking on the Filter button. It can contain any HTML desired by + * the application developer. As such, the application developer is repsonsible for constructing the appliedFilters array which is passed to + * the filter results tags. + * The application developer is responsible for filtering items based on the appliedFilters array; both when a + * filter is changed on the panel, or when a filter tag is 'cleared' (by user clicking on 'x' in the filter results tag). + *
+ * + * @param {object} config configuration settings for the filters:
+ *
    + *
  • .appliedFilters - (Array) List of the currently applied filters. Used to render the filter results tags and returned + * in the onFilterChange function where it can be used to filter a set of items. + *
      + *
    • .id - (String) Id for the filter, useful for comparisons + *
    • .title - (String) The title to display for the filter results tag + *
    • .values - (Array) The value(s) to display for the filter results tag. Ie. [title: [value1 x] [value2 x]] + *
    + *
  • .resultsCount - (int) The number of results returned after the current applied filters have been applied + *
  • .totalCount - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm' + *
  • .resultsLabel - (String) Optional label for the result units. Default is "Results". Ex: "'n' of 'm' Results" + *
  • .onFilterChange - ( function(appliedFilters, changedFilterId, changedFilterValue) ) Function to call when the applied + * filters list changes. Triggered by user clicking on 'x' or 'Clear All Filters' in the filter result tags. + * changedFilterId and changedFilterValue are returned after user clicks on an 'x' in a tag to + * denote which filter and filter value was cleared. changedFilterId and changedFilterValue are + * not returned when 'Clear All Filters' link is clicked. + *
+ * + * @example + + +
+
+ +
+ +
{{filter.title}} +
    +
  • + + {{value.title}} +
  • +
+
+
+
+
+
+
+ +
+
+
+
+
ID
+
Keyword
+
Category One
+
Category Two
+
+
+
+
+
+ {{item.id}} +
+
+ {{item.keyword}} +
+
+ {{item.categoryOne}} +
+
+ {{item.categoryTwo}} +
+
+
+
+
+
+ + + angular.module('patternfly.filters').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.filterPanelModel = [ + { + id: 'keyword', + title: 'Keyword', + placeholder: 'Filter by Keyword', + filterType: 'input', + values: [] + }, + { + id: 'category1', + title: 'Category One', + filterType: 'checkbox', + values: [ + { + id: 'val1', + title: 'Value 1', + value: 'Value 1', + selected: false + }, + { + id: 'val2', + title: 'Value 2', + value: 'Value 2', + selected: false + }, + { + id: 'val3', + title: 'Value 3', + value: 'Value 3', + selected: false + } + ] + }, + { + id: 'category2', + title: 'Category Two', + filterType: 'checkbox', + values: [ + { + id: 'val1', + title: 'Value 1', + value: 'Value 1', + selected: false + }, + { + id: 'val2', + title: 'Value 2', + value: 'Value 2', + selected: false + }, + { + id: 'val3', + title: 'Value 3', + value: 'Value 3', + selected: false + } + ] + } + ]; + + $scope.filtersText = ''; + + $scope.allItems = [ + { + id: "1", + keyword: "Foo", + categoryOne: "Value 1", + categoryTwo: "Value 1" + }, + { + id: "2", + keyword: "Bar", + categoryOne: "Value 2", + categoryTwo: "Value 1" + }, + { + id: "3", + keyword: "Foo", + categoryOne: "Value 3", + categoryTwo: "Value 1" + }, + { + id: "4", + keyword: "Bar", + categoryOne: "Value 1", + categoryTwo: "Value 2" + }, + { + id: "5", + keyword: "Foo", + categoryOne: "Value 2", + categoryTwo: "Value 2" + }, + { + id: "6", + keyword: "Bar", + categoryOne: "Value 3", + categoryTwo: "Value 2" + }, + { + id: "7", + keyword: "Foo", + categoryOne: "Value 1", + categoryTwo: "Value 3" + }, + { + id: "8", + keyword: "Bar", + categoryOne: "Value 2", + categoryTwo: "Value 3" + }, + { + id: "9", + keyword: "Foo", + categoryOne: "Value 3", + categoryTwo: "Value 3" + } + ]; + $scope.items = $scope.allItems; + + // called when filter cleared by hitting 'x' in filter results tag, or 'Clear All Filters' link + var onFilterChange = function (appliedFilters, changedFilterId, changedFilterValue) { + if (angular.isDefined(changedFilterId) && angular.isDefined(changedFilterValue)) { + updateFilterPanelModel(changedFilterId, changedFilterValue); + } else { + // the 'Clear All Filters' link was clicked + resetFilterPanelModel(); + } + // filter items + filterItems(appliedFilters); + }; + + $scope.filterConfig = { + resultsLabel: "Items", + resultsCount: $scope.items.length, + totalCount: $scope.allItems.length, + appliedFilters: [], + onFilterChange: onFilterChange + }; + + // called when filter is changed in the filter panel + $scope.filterChanged = function() { + applyFilters(); + }; + + $scope.onKeywordKeyPress = function(keyEvent) { + if (keyEvent.which === 13 && $scope.filterPanelModel[0].value.length > 0) { + var currentKeyword = $scope.filterPanelModel[0].value; + if(!keywordFilterExists(currentKeyword)) { + // store new keywoard filter value in values array + $scope.filterPanelModel[0].values.push(currentKeyword); + applyFilters(); + } + // remove the keyword value to show placeholder text + delete $scope.filterPanelModel[0].value; + } + }; + + var keywordFilterExists = function (keyword) { + return _.some( $scope.filterPanelModel[0].values, function(existingKeyword) { + // case sensitive + // return keyword === existingKeyword; + // case insensitive: + return keyword.toLowerCase() === existingKeyword.toLowerCase(); + }); + } + + var applyFilters = function () { + var newAppliedFilters = []; + _.forEach($scope.filterPanelModel, function(filter) { + var filterValues = []; + if (angular.isDefined(filter.values) && filter.values.length > 0) { + if(filter.filterType === "checkbox") { + // the values of the selected checkboxes are stored in a single new appliedFilter + _.forEach(filter.values, function(value) { + if(value.selected) { + filterValues.push(value.value) + } + }); + if (filterValues.length > 0) { + newAppliedFilters.push( createAppliedFilter (filter, filterValues) ); + } + } else { + // each keyword value gets a new appliedFilter + _.forEach(filter.values, function(value) { + filterValues = [value]; + newAppliedFilters.push( createAppliedFilter (filter, filterValues) ); + }); + } + } + }); + + // sets the filter result tags + $scope.filterConfig.appliedFilters = newAppliedFilters; + // filter items + filterItems($scope.filterConfig.appliedFilters); + }; + + var createAppliedFilter = function(filter, values) { + return { + id: filter.id, + title: filter.title, + values: values + }; + }; + + var updateFilterPanelModel = function (changedFilterId, changedFilterValue) { + var changedFilter = _.find($scope.filterPanelModel, function(f) { return f.id === changedFilterId}); + if(changedFilter.filterType === "checkbox") { + // unselect the checkbox + _.find(changedFilter.values, function(v) {return v.value == changedFilterValue}).selected = false; + } else { + // remove keyword from values array + _.remove(changedFilter.values, function(v) {return v == changedFilterValue}); + } + }; + + var resetFilterPanelModel = function () { + _.forEach($scope.filterPanelModel, function(filter) { + if (angular.isDefined(filter.values) && filter.values.length > 0) { + if(filter.filterType === "checkbox") { + // unselect all checkboxes + filter.values.forEach(function (value) { + value.selected = false; + }); + } else { + // clear all keyword filter values + filter.values = []; + } + } + }); + }; + + var filterItems = function (filters) { + $scope.items = []; + if (filters && filters.length > 0) { + _.forEach($scope.allItems, function(item) { + if (matchesFilters(item, filters)) { + $scope.items.push(item); + } + }); + } else { + $scope.items = $scope.allItems; + } + $scope.filterConfig.resultsCount = $scope.items.length; + }; + + var matchesFilters = function (item, filters) { + var matches = true; + + _.forEach(filters, function(filter) { + if (!matchesFilter(item, filter)) { + matches = false; + return false; + } + }); + return matches; + }; + + var matchesFilter = function (item, filter) { + var match = true; + + if (filter.id === 'keyword') { + var re = new RegExp(filter.values[0], 'i'); + match = item.keyword.match(re) !== null; + } else if (filter.id === 'category1') { + // values are OR'ed + _.forEach(filter.values, function(value) { + match = item.categoryOne === value; + return !match; + }); + } else if (filter.id === 'category2') { + // values are OR'ed + _.forEach(filter.values, function(value) { + match = item.categoryTwo === value; + return !match; + }); + } + return match; + }; + } + ]); + +
+*/ +;/** + * @ngdoc directive + * @name patternfly.filters.component:pfFilter + * @restrict E + * + * @description + * Component for a filter bar + *

+ * + * @param {object} config configuration settings for the filters:
+ *
    + *
  • .fields - (Array) List of filterable fields containing: + *
      + *
    • .id - (String) Optional unique Id for the filter field, useful for comparisons + *
    • .title - (String) The title to display for the filter field + *
    • .placeholder - (String) Text to display when no filter value has been entered + *
    • .filterMultiselect - (Boolean) In `complex-select`, allow selection of multiple categories and values. Optional, default is `false` + *
    • .filterType - (String) The filter input field type (any html input type, or 'select' for a single select box or 'complex-select' for a category select box) + *
    • .filterValues - (Array) List of valid select values used when filterType is 'select' or 'complex-select' (in where these values serve as case insensitve keys for .filterCategories objects) + *
    • .filterCategories - (Array of (Objects)) For 'complex-select' only, array of objects whoes keys (case insensitive) match the .filterValues, these objects include each of the filter fields above (sans .placeholder) + *
    • .filterCategoriesPlaceholder - (String) Text to display in `complex-select` category value select when no filter value has been entered, Optional + *
    • .filterDelimiter - (String) Delimiter separating 'complex-select' category and value. Optional, default is a space, ' ' + *
    + *
  • .inlineResults - (Boolean) Flag to show results inline with the filter selection (default: false) + *
  • .appliedFilters - (Array) List of the currently applied filters + *
  • .resultsCount - (int) The number of results returned after the current applied filters have been applied + *
  • .totalCount - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm' selected + *
  • .showTotalCountResults - (Boolean) Optional, flag to show the total count in the filter results as well (ie. 'n' of 'm' Results) + *
  • .itemsLabel - (String) Optional label to use for the items in the results count (default: Result) + *
  • .itemsLabelPlural - (String) Optional label to use for the items in the resuults count when plural (default: Results) + *
  • .onFilterChange - ( function(array of filters) ) Function to call when the applied filters list changes + *
+ * + * @example + + +
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ {{item.name}} +
+
+ {{item.address}} +
+ + +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ + + angular.module('patternfly.filters').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.filtersText = ''; + + $scope.allItems = [ + { + name: "Fred Flintstone", + address: "20 Dinosaur Way, Bedrock, Washingstone", + birthMonth: 'February', + car: 'Toyota-Echo' + }, + { + name: "John Smith", + address: "415 East Main Street, Norfolk, Virginia", + birthMonth: 'October', + car: 'subie-out' + + }, + { + name: "Frank Livingston", + address: "234 Elm Street, Pittsburgh, Pennsylvania", + birthMonth: 'March', + car: 'Toyota-pri' + }, + { + name: "Judy Green", + address: "2 Apple Boulevard, Cincinatti, Ohio", + birthMonth: 'December', + car: 'subie-Impreza' + }, + { + name: "Pat Thomas", + address: "50 Second Street, New York, New York", + birthMonth: 'jan', + car: 'subie-Crosstrek' + } + ]; + $scope.items = $scope.allItems; + + var matchesFilter = function (item, filter) { + var match = true; + var re = new RegExp(filter.value, 'i'); + + if (filter.id === 'name') { + match = item.name.match(re) !== null; + } else if (filter.id === 'address') { + match = item.address.match(re) !== null; + } else if (filter.id === 'birthMonth') { + match = item.birthMonth === filter.value.id || item.birthMonth === filter.value; + } else if (filter.id === 'car') { + match = item.car === ((filter.value.filterCategory.id || filter.value.filterCategory) + + filter.value.filterDelimiter + (filter.value.filterValue.id || filter.value.filterValue)); + } + return match; + }; + + var matchesFilters = function (item, filters) { + var matches = true; + + filters.forEach(function(filter) { + if (!matchesFilter(item, filter)) { + matches = false; + return false; + } + }); + return matches; + }; + + var applyFilters = function (filters) { + $scope.items = []; + if (filters && filters.length > 0) { + $scope.allItems.forEach(function (item) { + if (matchesFilters(item, filters)) { + $scope.items.push(item); + } + }); + } else { + $scope.items = $scope.allItems; + } + $scope.filterConfig.resultsCount = $scope.items.length; + }; + + var filterChange = function (filters) { + $scope.filtersText = ""; + filters.forEach(function (filter) { + $scope.filtersText += filter.title + " : "; + if (filter.value.filterCategory) { + $scope.filtersText += ((filter.value.filterCategory.title || filter.value.filterCategory) + + filter.value.filterDelimiter + (filter.value.filterValue.title || filter.value.filterValue)); + } else if (filter.value.title){ + $scope.filtersText += filter.value.title; + } else { + $scope.filtersText += filter.value; + } + $scope.filtersText += "\n"; + }); + applyFilters(filters); + }; + + $scope.filterConfig = { + fields: [ + { + id: 'name', + title: 'Name', + placeholder: 'Filter by Name', + filterType: 'text' + }, + { + id: 'address', + title: 'Address', + placeholder: 'Filter by Address', + filterType: 'text' + }, + { + id: 'birthMonth', + title: 'Birth Month', + placeholder: 'Filter by Birth Month', + filterType: 'select', + filterValues: [{title:'January', id:'jan'}, {title:'Feb', id:'February'}, 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + }, + { + id: 'car', + title: 'Car', + placeholder: 'Filter by Car Make', + filterType: 'complex-select', + filterValues: [{title:'Subaru', id:'subie'}, 'Toyota'], + filterDelimiter: '-', + filterCategoriesPlaceholder: 'Filter by Car Model', + filterCategories: {subie: { + id: 'subie', + title: 'Subaru', + filterValues: [{title:'Outback', id:'out'}, 'Crosstrek', 'Impreza']}, + toyota: { + id: 'toyota', + title: 'Toyota', + filterValues: [{title:'Prius', id:'pri'}, 'Corolla', 'Echo']} + } + } + ], + resultsCount: $scope.items.length, + totalCount: $scope.allItems.length, + appliedFilters: [], + onFilterChange: filterChange + }; + } + ]); + +
+*/ +;angular.module('patternfly.filters').component('pfFilterPanel', { + bindings: { + config: '=' + }, + transclude: true, + templateUrl: 'filters/filter-panel/filter-panel.html', + controller: function () { + 'use strict'; + + var ctrl = this; + } +}); +;/** + * @ngdoc directive + * @name patternfly.filters.component:pfFilterPanelResults + * @restrict E + * + * @description + * Component for the filter panel results + *

+ * + * @param {object} config configuration settings for the filter panel results:
+ *
    + *
  • .appliedFilters - (Array) List of the currently applied filters. Used to render the filter results tags and returned + * in the onFilterChange function where it can be used to filter a set of items. + *
      + *
    • .id - (String) Id for the filter, useful for comparisons + *
    • .title - (String) The title to display for the filter results tag + *
    • .values - (Array) The value(s) to display for the filter results tag. Ie. [title: [value1 x] [value2 x]] + *
    + *
  • .resultsCount - (int) The number of results returned after the current applied filters have been applied + *
  • .totalCount - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm' + *
  • .resultsLabel - (String) Optional label for the result units. Default is "Results". Ex: "'n' of 'm' Results" + *
  • .onFilterChange - ( function(appliedFilters, changedFilterId, changedFilterValue) ) Function to call when the applied + * filters list changes. Triggered by user clicking on 'x' or 'Clear All Filters' in the filter result tags. + * changedFilterId and changedFilterValue are returned after user clicks on an 'x' in a tag to + * denote which filter and filter value was cleared. changedFilterId and changedFilterValue are + * not returned when 'Clear All Filters' link is clicked. + *
+ * + */ +angular.module('patternfly.filters').component('pfFilterPanelResults', { + bindings: { + config: '=' + }, + templateUrl: 'filters/filter-panel/filter-panel-results.html', + controller: function () { + 'use strict'; + + var ctrl = this; + + ctrl.$onInit = function () { + angular.extend(ctrl, { + clearFilter: clearFilter, + clearAllFilters: clearAllFilters + }); + }; + + ctrl.$onChanges = function () { + setupConfig (); + }; + + function setupConfig () { + if (!ctrl.config.appliedFilters) { + ctrl.config.appliedFilters = []; + } + if (ctrl.config.resultsCount === undefined) { + ctrl.config.resultsCount = 0; + } + } + + function clearFilter (evt, filter, value) { + var changedFilterId = filter.id; + evt.preventDefault(); + + _.pull(filter.values, value); + + if (filter.values.length === 0) { + _.pull(ctrl.config.appliedFilters, filter); + } + + if (ctrl.config.onFilterChange) { + ctrl.config.onFilterChange(ctrl.config.appliedFilters, changedFilterId, value); + } + } + + function clearAllFilters (evt) { + evt.preventDefault(); + + ctrl.config.appliedFilters = []; + + if (ctrl.config.onFilterChange) { + ctrl.config.onFilterChange(ctrl.config.appliedFilters); + } + } + } +}); +;angular.module('patternfly.filters').component('pfFilter', { + bindings: { + config: '=' + }, + templateUrl: 'filters/simple-filter/filter.html', + controller: function () { + 'use strict'; + + var ctrl = this; + + ctrl.$onInit = function () { + + angular.extend(ctrl, + { + addFilter: addFilter + } + ); + }; + + function filterExists (filter) { + return angular.isDefined(_.find(ctrl.config.appliedFilters, {title: filter.title, value: filter.value})); + } + + function findDuplicateComplexSelect (item) { + return item.value.filterCategory; + } + + function enforceSingleSelect (filter) { + _.remove(ctrl.config.appliedFilters, {title: filter.title}); + } + + function addFilter (field, value) { + var newFilter = { + id: field.id, + title: field.title, + type: field.filterType, + value: value + }; + + if (!filterExists(newFilter)) { + + if (newFilter.type === 'select') { + enforceSingleSelect(newFilter); + } + + if (field.filterType === 'complex-select' && !field.filterMultiselect) { + _.remove(ctrl.config.appliedFilters, findDuplicateComplexSelect); + } + + ctrl.config.appliedFilters.push(newFilter); + + if (ctrl.config.onFilterChange) { + ctrl.config.onFilterChange(ctrl.config.appliedFilters); + } + } + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.filters.component:pfFilterFields + * @restrict E + * + * @description + * Directive for the filter bar's filter entry components + *

+ * + * @param {object} config configuration settings for the filters:
+ *
    + *
  • .fields - (Array) List of filterable fields containing: + *
      + *
    • .id - (String) Optional unique Id for the filter field, useful for comparisons + *
    • .title - (String) The title to display for the filter field + *
    • .placeholder - (String) Text to display when no filter value has been entered + *
    • .filterMultiselect - (Boolean) In `complex-select`, allow selection of multiple categories and values. Optional, default is `false` + *
    • .filterType - (String) The filter input field type (any html input type, or 'select' for a single select box or 'complex-select' for a category select box) + *
    • .filterValues - (Array) List of valid select values used when filterType is 'select' or 'complex-select' (in where these values serve as case insensitve keys for .filterCategories objects) + *
    • .filterCategories - (Array of (Objects)) For 'complex-select' only, array of objects whoes keys (case insensitive) match the .filterValues, these objects include each of the filter fields above (sans .placeholder) + *
    • .filterCategoriesPlaceholder - (String) Text to display in `complex-select` category value select when no filter value has been entered, Optional + *
    • .filterDelimiter - (String) Delimiter separating 'complex-select' category and value. Optional, default is a space, ' ' + *
    + *
  • .appliedFilters - (Array) List of the currently applied filters + *
+ * + */ +angular.module('patternfly.filters').component('pfFilterFields', { + bindings: { + config: '=', + addFilterFn: '<' + }, + templateUrl: 'filters/simple-filter/filter-fields.html', + controller: function () { + 'use strict'; + + var ctrl = this; + var prevConfig; + + ctrl.$onInit = function () { + angular.extend(ctrl, { + selectField: selectField, + selectValue: selectValue, + onValueKeyPress: onValueKeyPress + }); + }; + + ctrl.$onChanges = function () { + setupConfig (); + }; + + ctrl.$doCheck = function () { + // do a deep compare on config + if (!angular.equals(ctrl.config, prevConfig)) { + setupConfig(); + } + }; + + function selectField (item) { + ctrl.currentField = item; + ctrl.currentField.filterDelimiter = ctrl.currentField.filterDelimiter || ' '; + ctrl.currentValue = null; + } + + function selectValue (filterValue, valueType) { + if (angular.isDefined (filterValue)) { + if (ctrl.currentField.filterType === 'complex-select') { + switch (valueType) { + case 'filter-category': + ctrl.filterCategory = filterValue; + ctrl.filterValue = null; + break; + case 'filter-value': + ctrl.filterValue = filterValue; + break; + } + if (ctrl.filterCategory && ctrl.filterValue) { + ctrl.addFilterFn(ctrl.currentField, { + filterCategory: ctrl.filterCategory, + filterDelimiter: ctrl.currentField.filterDelimiter, + filterValue: ctrl.filterValue + }); + } + } else { + ctrl.addFilterFn(ctrl.currentField, filterValue); + ctrl.currentValue = filterValue; + } + } + } + + function onValueKeyPress (keyEvent) { + if (keyEvent.which === 13 && ctrl.currentValue && ctrl.currentValue.length > 0) { + ctrl.addFilterFn(ctrl.currentField, ctrl.currentValue); + ctrl.currentValue = undefined; + keyEvent.stopPropagation(); + keyEvent.preventDefault(); + } + } + + function setupConfig () { + var fieldFound = false; + + prevConfig = angular.copy(ctrl.config); + + if (ctrl.config.fields === undefined) { + ctrl.config.fields = []; + } + + if (ctrl.currentField) { + fieldFound = _.find(ctrl.config.fields, function (nextField) { + return nextField.id === ctrl.currentField.id; + }); + } + + if (!fieldFound) { + ctrl.currentField = ctrl.config.fields[0]; + ctrl.currentValue = null; + } + + if (ctrl.currentValue === undefined) { + ctrl.currentValue = null; + } + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.filters.component:pfFilterResults + * @restrict E + * + * @description + * Component for the filter results + *

+ * + * @param {object} config configuration settings for the filter results:
+ *
    + *
  • .fields - (Array) List of filterable fields containing: + *
      + *
    • .id - (String) Optional unique Id for the filter field, useful for comparisons + *
    • .title - (String) The title to display for the filter field + *
    • .placeholder - (String) Text to display when no filter value has been entered + *
    • .filterType - (String) The filter input field type (any html input type, or 'select' for a select box) + *
    • .filterValues - (Array) List of valid select values used when filterType is 'select' + *
    + *
  • .appliedFilters - (Array) List of the currently applied filters + *
  • .resultsCount - (int) The number of results returned after the current applied filters have been applied + *
  • .selectedCount - (int) The number selected items, The 'n' in the label: 'n' of 'm' selected + *
  • .totalCount - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm' selected + *
  • .showTotalCountResults - (Boolean) Optional, flag to show the total count in the filter results (ie. x of y Results) + *
  • .itemsLabel - (String) Optional label to use for the items (default: Result) + *
  • .itemsLabelPlural - (String) Optional label to use for the items when plural (default: Results) + *
  • .onFilterChange - ( function(array of filters) ) Function to call when the applied filters list changes + *
+ * + */ +angular.module('patternfly.filters').component('pfFilterResults', { + bindings: { + config: '=' + }, + templateUrl: 'filters/simple-filter/filter-results.html', + controller: function () { + 'use strict'; + + var ctrl = this; + var prevConfig; + + ctrl.$onInit = function () { + angular.extend(ctrl, { + clearFilter: clearFilter, + clearAllFilters: clearAllFilters + }); + }; + + ctrl.$onChanges = function () { + setupConfig (); + }; + + ctrl.$doCheck = function () { + // do a deep compare on config + if (!angular.equals(ctrl.config, prevConfig)) { + setupConfig(); + } + }; + + function setupConfig () { + prevConfig = angular.copy(ctrl.config); + + if (!ctrl.config.appliedFilters) { + ctrl.config.appliedFilters = []; + } + if (ctrl.config.resultsCount === undefined) { + ctrl.config.resultsCount = 0; + } + + ctrl.config.itemsLabel = ctrl.config.itemsLabel || 'Result'; + ctrl.config.itemsLabelPlural = ctrl.config.itemsLabelPlural || 'Results'; + } + + function clearFilter (evt, item) { + var newFilters = []; + evt.preventDefault(); + + ctrl.config.appliedFilters.forEach(function (filter) { + if (item.title !== filter.title || item.value !== filter.value) { + newFilters.push(filter); + } + }); + ctrl.config.appliedFilters = newFilters; + + if (ctrl.config.onFilterChange) { + ctrl.config.onFilterChange(ctrl.config.appliedFilters); + } + } + + function clearAllFilters (evt) { + evt.preventDefault(); + + ctrl.config.appliedFilters = []; + + if (ctrl.config.onFilterChange) { + ctrl.config.onFilterChange(ctrl.config.appliedFilters); + } + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.form.component:pfFormButtons + * @restrict E + * + * @description + * Encapsulates the standard structure and styling for create and cancel buttons + * when used with a form. + * + * This directive creates new scope. + * + * @param {function} pfHandleCancel function to call when the user clicks cancel. + * @param {function} pfHandleSave function to call when the user clicks save. + * @param {expression} pfWorking the model to store the working status in. + * @param {string} pfButtonClass the class of the button container. + * + * @example + + + +
+

Saved?

+

{{ status }}

+
+
Input + +
+ +
+
+
+ + + angular.module( 'patternfly.form' ).controller( 'FormButtonCtrl', function( $scope, $timeout, $element ) { + $scope.status = 'Not yet Saved'; + $scope.working = false; + + $scope.save = function (item) { + $scope.status = 'saved'; + $scope.working = true; + + $timeout(function () { + $scope.working = false; + }, 1000); + }; + + $scope.cancel = function () { + $scope.status = 'cancelled'; + $scope.input = null; + }; + }); + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.form.directive:pfFormGroup + * @restrict E + * + * @description + * Encapsulates the structure and styling for a label + input used within a + * Bootstrap3 based form. + * + * This directive creates new scope. + * + * @param {string} pfLabel the text for the
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + +

Your preferred pet is {{pet}}.

+

Your preferred fruit is {{fruit}}.

+

Your preferred drink is {{drink || 'No drink selected'}}.

+ + +
+ + + angular.module( 'patternfly.select' ).controller( 'SelectDemoCtrl', function( $scope ) { + $scope.drinks = ['tea', 'coffee', 'water']; + $scope.pets = ['Dog', 'Cat', 'Chicken']; + $scope.pet = $scope.pets[0]; + $scope.fruit = 'orange'; + }); + + +
+ */ +;/** + * @ngdoc directive + * @name patternfly.select.component:pfSelect + * @restrict E + * + * @param {object} selected Curently selected value + * @param {object} options Array of valid selections + * @param {string} displayField Field from the object in the array to display for selection (optional) + * @param {string} emptyValue value to display when nothing is selected + * @param {function(item)} onSelect Function to call upon user selection of an item. + * + * @description + * The pfSelect component provides a wrapper for the angular ui bootstrap dropdown. + * + * @example + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+

Your preferred pet is {{pet || noPet}}.

+

Your preferred fruit is {{fruit.name}}.

+

Your preferred drink is {{drink ? drink.name : noDrink}}.

+
+
+ + angular.module( 'patternfly.select' ).controller( 'SelectDemoCtrl', function( $scope ) { + $scope.pets = ['Dog', 'Cat', 'Chicken']; + $scope.noPet = "No pet selected"; + + $scope.fruits = [ + { id: 1, name:'orange', title: 'Oranges - fresh from Florida'}, + { id: 2, name:'apple', title: 'Apples - Macintosh, great for pies.'}, + { id: 3, name:'banana', title: 'Bananas - you will go ape for them!' } + ]; + $scope.fruit = $scope.fruits[0]; + + $scope.drinks = [ + { id: 1, name:'tea'}, + { id: 2, name:'coffee'}, + { id: 3, name:'water'}, + { id: 4, name:'wine'}, + { id: 5, name:'beer'} + ]; + $scope.drink = $scope.drinks[0]; + $scope.noDrink = "No drink selected"; + }); + +
+ */ +;angular.module('patternfly.select').component('pfSelect', { + + bindings: { + selected: '=', + options: '<', + displayField: '@', + emptyValue: '@', + onSelect: '<' + }, + templateUrl: 'select/select.html', + controller: function () { + 'use strict'; + + var ctrl = this; + + ctrl.$onInit = function () { + angular.extend(ctrl, { + showEmpty: angular.isDefined(ctrl.emptyValue), + getDisplayValue: getDisplayValue, + selectItem: selectItem + }); + }; + + function getDisplayValue (item) { + var value; + + if (item !== ctrl.emptyValue && angular.isString(ctrl.displayField)) { + value = item[ctrl.displayField]; + } else { + value = item; + } + + return value; + } + + function selectItem (evt, item) { + evt.preventDefault(); + + ctrl.selected = item; + if (angular.isFunction(ctrl.onSelect)) { + ctrl.onSelect(item); + } + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.sort.component:pfSort + * @restrict E + * + * @description + * Sort component + *

+ * + * @param {object} config configuration settings for the sort:
+ *
    + *
  • .fields - (Array) List of sortable fields containing: + *
      + *
    • .id - (String) Unique Id for the sort field + *
    • .title - (String) The title to display for the sort field + *
    • .sortType - (String) The sort type, 'alpha' or 'numeric' + *
    + *
  • .currentField - (Object) Currently selected field + *
  • .isAscending - (boolean) Current sort direction is ascending. True for ascending, False for descending + *
  • .onSortChange - ( function(sortId, sortDirection ) Function to call when the current sort params change + *
+ * + * @example + + +
+
+ +
+
+
+ +
+
+
+
+
+ {{item.name}} +
+
+ {{item.count}} +
+
+ {{item.description}} +
+
+
+
+
+
+ + + angular.module('patternfly.sort').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.items = [ + { + name: "Item 7", + count: 432, + description: 'Very nice item' + }, + { + name: "Item 6", + count: 22, + description: 'It lasts forever' + }, + { + name: "Item 3", + count: 632, + description: 'Good stuff cheap' + }, + { + name: "Item 2", + count: 12, + description: 'Fantastic' + }, + { + name: "Item 9", + count: 99, + description: 'It does alright' + }, + { + name: "Item 4", + count: 442, + description: 'Horrible' + }, + { + name: "Item 1", + count: 42, + description: 'Most excellent' + }, + { + name: "Item 8", + count: 2, + description: 'Get it while it lasts' + }, + { + name: "Item 5", + count: 321, + description: 'Beautiful style' + } + ]; + + var compareFn = function(item1, item2) { + var compValue = 0; + if ($scope.sortConfig.currentField.id === 'name') { + compValue = item1.name.localeCompare(item2.name); + } else if ($scope.sortConfig.currentField.id === 'count') { + compValue = item1.count - item2.count; + } else if ($scope.sortConfig.currentField.id === 'description') { + compValue = item1.description.localeCompare(item2.description); + } + + if (!$scope.sortConfig.isAscending) { + compValue = compValue * -1; + } + + return compValue; + }; + + var sortChange = function (sortId, isAscending) { + $scope.items.sort(compareFn); + }; + + $scope.sortConfig = { + fields: [ + { + id: 'name', + title: 'Name', + sortType: 'alpha' + }, + { + id: 'count', + title: 'Count', + sortType: 'numeric' + }, + { + id: 'description', + title: 'Description', + sortType: 'alpha' + } + ], + onSortChange: sortChange + }; + } + ]); + +
+ */ +;angular.module('patternfly.sort').component('pfSort', { + bindings: { + config: '=' + }, + templateUrl: 'sort/sort.html', + controller: function () { + 'use strict'; + + var ctrl = this; + var prevConfig; + + ctrl.$onInit = function () { + if (angular.isDefined(ctrl.config) && angular.isUndefined(ctrl.config.show)) { + // default to true + ctrl.config.show = true; + } + + angular.extend(ctrl, { + selectField: selectField, + changeDirection: changeDirection, + getSortIconClass: getSortIconClass + }); + }; + + ctrl.$onChanges = function () { + setupConfig(); + }; + + ctrl.$doCheck = function () { + // do a deep compare on config + if (!angular.equals(ctrl.config, prevConfig)) { + setupConfig(); + } + }; + + function setupConfig () { + var updated = false; + + prevConfig = angular.copy(ctrl.config); + + if (ctrl.config.fields === undefined) { + ctrl.config.fields = []; + } + + if (ctrl.config.fields.length > 0) { + if (ctrl.config.currentField === undefined) { + ctrl.config.currentField = ctrl.config.fields[0]; + updated = true; + } + if (ctrl.config.isAscending === undefined) { + ctrl.config.isAscending = true; + updated = true; + } + } + + if (updated === true && ctrl.config.onSortChange) { + ctrl.config.onSortChange(ctrl.config.currentField, ctrl.config.isAscending); + } + } + + function selectField (evt, field) { + evt.preventDefault(); + + ctrl.config.currentField = field; + + if (ctrl.config.onSortChange) { + ctrl.config.onSortChange(ctrl.config.currentField, ctrl.config.isAscending); + } + } + + function changeDirection () { + ctrl.config.isAscending = !ctrl.config.isAscending; + + if (ctrl.config.onSortChange) { + ctrl.config.onSortChange(ctrl.config.currentField, ctrl.config.isAscending); + } + } + + function getSortIconClass () { + var iconClass; + + if (ctrl.config.currentField.sortType === 'numeric') { + if (ctrl.config.isAscending) { + iconClass = 'fa fa-sort-numeric-asc'; + } else { + iconClass = 'fa fa-sort-numeric-desc'; + } + } else { + if (ctrl.config.isAscending) { + iconClass = 'fa fa-sort-alpha-asc'; + } else { + iconClass = 'fa fa-sort-alpha-desc'; + } + } + + return iconClass; + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.table.component:pfTableView-Basic + * + * @description + * Component for rendering a simple table view.

+ * See {@link patternfly.table.component:pfTableView-with-Toolbar pfTableView - with Toolbar} for use with a Toolbar
+ * See {@link patternfly.toolbars.component:pfToolbar pfToolbar} for use in Toolbar View Switcher + * + * @param {object} config Optional configuration object + *
    + *
  • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
  • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. + *
  • .showCheckboxes - (boolean) Show checkboxes for row selection, default is true + *
+ * @param {object} dtOptions Optional angular-datatables DTOptionsBuilder configuration object. See {@link http://l-lin.github.io/angular-datatables/archives/#/api angular-datatables: DTOptionsBuilder} + * @param {array} items Array of items to display in the table view. + * @param {array} columns Array of table column information to display in the table's header row and optionaly render the cells of a column. + *
    + *
  • .header - (string) Text label for a column header + *
  • .itemField - (string) Item field to associate with a particular column. + *
  • .templateFn - (function(value, item)) (optional) Template function used to render each cell of the column. Pro: more performant than `htmlTemplate`. Con: doesn't support AngularJS directives in the template, therefore it doesn't support things like ng-click. Example:
    templateFn: (value, item) => `${value}`
    + *
  • .htmlTemplate - (string) (optional) id/name of an embedded ng/html template. Pro: supports AngularJS directives in the template. Con: poor performance on large tables. Ex: htmlTemplate="name_template.html". The template will be used to render each cell of the column. + * Use handleColAction(key, value) in the template to call the colActionFn callback function you specify. 'key' is the item attribute name; which should equal the itemFld of a column. + * 'value' is the item[key] value. + *
    +  *     
    +  *   
    + *
  • .colActionFn - (function) (optional) Callback function used for the column. 'value' is passed as a paramenter to the + * callback function. + *
+ *

Tip: For templating, use `tempateFn` unless you really need to use AngularJS directives. `templateFn` performs better than `htmlTemplate`.

+ * @param {array} actionButtons List of action buttons in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
+ * @param {array} menuActions List of actions for dropdown menu in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
+ * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @param {array} emptyStateActionButtons Optional buttons to display under the icon, title, and informational paragraph in the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @example + + +
+
+ + +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ + + + + + angular.module('patternfly.tableview.demo', ['patternfly.views','patternfly.table']); + + + + angular.module('patternfly.tableview.demo').controller('TableCtrl', ['$scope', '$timeout', 'itemsService', + function ($scope, $timeout, itemsService) { + $scope.dtOptions = { + // order column(s) should NOT account for 1st checkbox column, table component will adjust col. numbers accordingly + // Sort by City, then Name + order: [[3, "asc"], [1, "desc"]], + }; + + $scope.columns = [ + { header: "Status", itemField: "status", htmlTemplate: "status_template.html" }, + { header: "Name", itemField: "name", htmlTemplate: "name_template.html", colActionFn: onNameClick }, + { header: "Address", itemField: "address"}, + { header: "City", itemField: "city", templateFn: function(value, item) { return '' + value + '' } }, + { header: "State", itemField: "state"} + ]; + + $scope.items = null; + + $scope.eventText = ""; + + $scope.config = { + onCheckBoxChange: handleCheckBoxChange, + selectionMatchProp: "name", + itemsAvailable: true, + showCheckboxes: true + }; + + var performEmptyStateAction = function (action) { + $scope.eventText = action.name + "\r\n" + $scope.eventText; + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + $scope.emptyStateActionButtons = [ + { + name: 'Main Action', + title: 'Perform an action', + actionFn: performEmptyStateAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 2', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 3', + title: 'Perform an action', + actionFn: performEmptyStateAction + } + ]; + + function handleCheckBoxChange (item) { + $scope.eventText = item.name + ' checked: ' + item.selected + '\r\n' + $scope.eventText; + }; + + var performAction = function (action, item) { + $scope.eventText = item.name + " : " + action.name + "\r\n" + $scope.eventText; + }; + + function onNameClick (name) { + $scope.eventText = "You clicked on " + name + "\n" + $scope.eventText; + } + + $scope.actionButtons = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + } + ]; + + $scope.menuActions = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + title: 'Do something similar', + actionFn: performAction + } + ]; + + $scope.showComponent = true; + + $scope.addNewComponentToDOM = function () { + $scope.showComponent = false; + $timeout(() => $scope.showComponent = true); + }; + + (function init() { + itemsService.getItems() + .then(items => $scope.items = items); + })(); + } + ]); + + + + angular.module('patternfly.tableview.demo').service('itemsService', ['$q', function($q) { + + this.getItems = function() { + return $q((resolve, reject) => { + setTimeout(function() { + let items = [ + { + status: "error", + name: "Fred Flintstone", + address: "20 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + status: "error", + name: "John Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia", + }, + { + status: "warning", + name: "Frank Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + status: "ok", + name: "Linda McGovern", + address: "22 Oak Street", + city: "Denver", + state: "Colorado" + }, + { + status: "error", + name: "Jim Brown", + address: "72 Bourbon Way", + city: "Nashville", + state: "Tennessee" + }, + { + status: "ok", + name: "Holly Nichols", + address: "21 Jump Street", + city: "Hollywood", + state: "California" + }, + { + status: "error", + name: "Marie Edwards", + address: "17 Cross Street", + city: "Boston", + state: "Massachusetts" + }, + { + status: "ok", + name: "Pat Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + }, + { + status: "warning", + name: "Mike Bird", + address: "50 Forth Street", + city: "New York", + state: "New York" + }, + { + status: "error", + name: "Cheryl Taylor", + address: "2 Main Street", + city: "New York", + state: "New York" + }, + { + status: "ok", + name: "Ren DiLorenzo", + address: "10 Chase Lane", + city: "Boston", + state: "Massacusetts" + }, + { + status: "ok", + name: "Kim Livingston", + address: "5 Tree Hill Lane", + city: "Boston", + state: "Massacusetts" + } + ]; + resolve(items); + }, 10); + }); + } + + }]); + + + +*/ +;/** + * @ngdoc directive + * @name patternfly.table.component:pfTableView-with-Toolbar + * + * @description + * Example configuring a table view with a toolbar.

+ * Please see {@link patternfly.toolbars.component:pfToolbar pfToolbar} for use in Toolbar View Switcher + * + * @param {object} config Optional configuration object + *
    + *
  • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
  • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. + *
  • .showCheckboxes - (boolean) Show checkboxes for row selection, default is true + *
+ * @param {object} dtOptions Optional angular-datatables DTOptionsBuilder configuration object. Note: paginationType, displayLength, and dom:"p" are no longer being used for pagination, but are allowed for backward compatibility. + * Please switch to prefered 'pageConfig' settings for pf pagination controls. + * Other dtOptions can still be used, such as 'order' See {@link http://l-lin.github.io/angular-datatables/archives/#/api angular-datatables: DTOptionsBuilder} + * @param {object} pageConfig Optional pagination configuration object. Since all properties are optional it is ok to specify: 'pageConfig = {}' to indicate that you want to + * use pagination with the default parameters. + *
    + *
  • .pageNumber - (number) Optional Initial page number to display. Default is page 1. + *
  • .pageSize - (number) Optional Initial page size/display length to use. Ie. Number of "Items per Page". Default is 10 items per page + *
  • .pageSizeIncrements - (Array[Number]) Optional Page size increments for the 'per page' dropdown. If not specified, the default values are: [5, 10, 20, 40, 80, 100] + *
+ * @param {array} items Array of items to display in the table view. + * @param {array} columns Array of table column information to display in the table's header row and optionaly render the cells of a column. + *
+ *

Tip: For templating, use `tempateFn` unless you really need to use AngularJS directives. `templateFn` performs better than `htmlTemplate`.

+ * @param {array} actionButtons List of action buttons in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
+ * @param {array} menuActions List of actions for dropdown menu in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
+ * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @param {array} emptyStateActionButtons Optional buttons to display under the icon, title, and informational paragraph. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @example + + + +
+
+ +
+
+ + +
+
+
+
+ + +
+
+ +
+
+
+ +
+
+ +
+ + + +
+
+ + + angular.module('patternfly.tableview.demo', ['patternfly.toolbars','patternfly.table']); + + + + angular.module('patternfly.tableview.demo').controller('ViewCtrl', ['$scope', '$timeout', 'pfViewUtils', '$filter', + function ($scope, $timeout, pfViewUtils, $filter) { + $scope.actionsText = ""; + + $scope.columns = [ + { + header: "Status", + itemField: "status", + htmlTemplate: "status_template.html" + }, + { + header: "Name", + itemField: "name", + htmlTemplate: "name_template.html", + colActionFn: onNameClick + }, + { + header: "Age", + itemField: "age", + templateFn: function(value) { + var className = value > 30 ? 'text-success' : 'text-warning'; + return '' + value + ''; + } + }, + { + header: "Address", + itemField: "address", + htmlTemplate: "address_template.html" + }, + { + header: "BirthMonth", + itemField: "birthMonth" + } + ]; + + // dtOptions paginationType, displayLength, and dom:"p" are no longer being + // used, but are allowed for backward compatibility. + // Please switch to prefered 'pageConfig' settings for pf pagination controls + // Other dtOptions can still be used, such as 'order' + // $scope.dtOptions = { + // paginationType: 'full', + // displayLength: 10, + // dom: "tp" + // } + + // New pagination config settings + $scope.pageConfig = { + pageNumber: 1, + pageSize: 10, + pageSizeIncrements: [5, 10, 15] + }; + + $scope.allItems = [ + { + status: "error", + name: "Fred Flintstone", + age: 57, + address: "20 Dinosaur Way, Bedrock, Washingstone", + birthMonth: 'February' + }, + { + status: "ok", + name: "John Smith", + age: 23, + address: "415 East Main Street, Norfolk, Virginia", + birthMonth: 'October' + }, + { + status: "warning", + name: "Frank Livingston", + age: 71, + address: "234 Elm Street, Pittsburgh, Pennsylvania", + birthMonth: 'March' + }, + { + status: "ok", + name: "Judy Green", + age: 21, + address: "2 Apple Boulevard, Cincinatti, Ohio", + birthMonth: 'December' + }, + { + status: "ok", + name: "Pat Thomas", + age: 19, + address: "50 Second Street, New York, New York", + birthMonth: 'February' + }, + { + status: "error", + name: "Linda McGovern", + age: 32, + address: "22 Oak Stree, Denver, Colorado", + birthMonth: 'March' + }, + { + status: "warning", + name: "Jim Brown", + age: 55, + address: "72 Bourbon Way. Nashville. Tennessee", + birthMonth: 'March' + }, + { + status: "ok", + name: "Holly Nichols", + age: 34, + address: "21 Jump Street, Hollywood, California", + birthMonth: 'March' + }, + { + status: "ok", + name: "Wilma Flintstone", + age: 47, + address: "20 Dinosaur Way, Bedrock, Washingstone", + birthMonth: 'February' + }, + { + status: "warning", + name: "Jane Smith", + age: 22, + address: "415 East Main Street, Norfolk, Virginia", + birthMonth: 'April' + }, + { + status: "error", + name: "Liz Livingston", + age: 65, + address: "234 Elm Street, Pittsburgh, Pennsylvania", + birthMonth: 'November' + }, + { + status: "ok", + name: "Jim Green", + age: 23, + address: "2 Apple Boulevard, Cincinatti, Ohio", + birthMonth: 'January' + }, + { + status: "ok", + name: "Chris Thomas", + age: 21, + address: "50 Second Street, New York, New York", + birthMonth: 'October' + }, + { + status: "error", + name: "Larry McGovern", + age: 34, + address: "22 Oak Stree, Denver, Colorado", + birthMonth: 'September' + }, + { + status: "warning", + name: "July Brown", + age: 51, + address: "72 Bourbon Way. Nashville. Tennessee", + birthMonth: 'May' + }, + { + status: "error", + name: "Henry Nichols", + age: 36, + address: "21 Jump Street, Hollywood, California", + birthMonth: 'March' + } + ]; + + $scope.items = $scope.allItems; + + var matchesFilter = function (item, filter) { + var match = true; + + if (filter.id === 'name') { + match = item.name.match(filter.value) !== null; + } else if (filter.id === 'age') { + match = item.age === parseInt(filter.value); + } else if (filter.id === 'address') { + match = item.address.match(filter.value) !== null; + } else if (filter.id === 'birthMonth') { + match = item.birthMonth === filter.value; + } else if (filter.id === 'status') { + match = item.status === filter.value; + } + return match; + }; + + var matchesFilters = function (item, filters) { + var matches = true; + + filters.forEach(function(filter) { + if (!matchesFilter(item, filter)) { + matches = false; + return false; + } + }); + return matches; + }; + + var applyFilters = function (filters) { + $scope.items = []; + if (filters && filters.length > 0) { + $scope.allItems.forEach(function (item) { + if (matchesFilters(item, filters)) { + $scope.items.push(item); + } + }); + } else { + $scope.items = $scope.allItems; + } + if (filters && (filters.length === 0 || filters.length > 1)) { + $scope.addNewComponentToDOM(); + } + }; + + var filterChange = function (filters) { + applyFilters(filters); + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + }; + + var performAction = function (action) { + var selectedItems = $filter('filter')($scope.allItems, {selected: true}); + if(!selectedItems) { + selectedItems = []; + } + $scope.actionsText = "Toolbar Action: " + action.name + " on " + selectedItems.length + " selected items\n" + $scope.actionsText; + }; + + var performTableAction = function (action, item) { + $scope.actionsText = "Table Row Action on '" + item.name + "' : " + action.name + "\r\n" + $scope.actionsText; + }; + + function handleCheckBoxChange (item) { + var selectedItems = $filter('filter')($scope.allItems, {selected: true}); + if (selectedItems) { + $scope.toolbarConfig.filterConfig.selectedCount = selectedItems.length; + } + } + + function onNameClick (name) { + $scope.actionsText = "You clicked on " + name + "\n" + $scope.actionsText; + } + + $scope.filterConfig = { + fields: [ + { + id: 'status', + title: 'Status', + placeholder: 'Filter by Status...', + filterType: 'select', + filterValues: ['error', 'warning', 'ok'] + }, + { + id: 'name', + title: 'Name', + placeholder: 'Filter by Name...', + filterType: 'text' + }, + { + id: 'age', + title: 'Age', + placeholder: 'Filter by Age...', + filterType: 'text' + }, + { + id: 'address', + title: 'Address', + placeholder: 'Filter by Address...', + filterType: 'text' + }, + { + id: 'birthMonth', + title: 'Birth Month', + placeholder: 'Filter by Birth Month...', + filterType: 'select', + filterValues: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + } + ], + resultsCount: $scope.items.length, + totalCount: $scope.allItems.length, + appliedFilters: [], + onFilterChange: filterChange + }; + + var monthVals = { + 'January': 1, + 'February': 2, + 'March': 3, + 'April': 4, + 'May': 5, + 'June': 6, + 'July': 7, + 'August': 8, + 'September': 9, + 'October': 10, + 'November': 11, + 'December': 12 + }; + + $scope.toolbarActionsConfig = { + primaryActions: [ + { + name: 'Action 1', + title: 'Do the first thing', + actionFn: performAction + }, + { + name: 'Action 2', + title: 'Do something else', + actionFn: performAction + } + ], + moreActions: [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + actionFn: performAction, + title: 'Do something similar' + } + ], + actionsInclude: true + }; + + $scope.toolbarConfig = { + filterConfig: $scope.filterConfig, + sortConfig: $scope.sortConfig, + actionsConfig: $scope.toolbarActionsConfig, + isTableView: true + }; + + $scope.tableConfig = { + onCheckBoxChange: handleCheckBoxChange, + selectionMatchProp: "name", + itemsAvailable: true, + showCheckboxes: true + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + $scope.tableActionButtons = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performTableAction + } + ]; + + $scope.tableMenuActions = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performTableAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performTableAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performTableAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performTableAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performTableAction + }, + { + name: 'Grouped Action 2', + title: 'Do something similar', + actionFn: performTableAction + } + ]; + + $scope.updateItemsAvailable = function () { + if(!$scope.tableConfig.itemsAvailable) { + $scope.toolbarConfig.filterConfig.resultsCount = 0; + $scope.toolbarConfig.filterConfig.totalCount = 0; + $scope.toolbarConfig.filterConfig.selectedCount = 0; + } else { + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + $scope.toolbarConfig.filterConfig.totalCount = $scope.allItems.length; + handleCheckBoxChange(); + } + }; + + $scope.showComponent = true; + + $scope.addNewComponentToDOM = function () { + $scope.showComponent = false; + $timeout(() => $scope.showComponent = true); + + }; + } + ]); + +
+ */ +;angular.module('patternfly.table').component('pfTableView', { + bindings: { + config: ' dtInstanceCallback"); + } + + ctrl.dtInstance = _dtInstance; + listenForDraw(); + selectRowsByChecked(); + }; + + ctrl.$onChanges = function (changesObj) { + if (ctrl.debug) { + $log.debug("$onChanges"); + } + if ((changesObj.config && !changesObj.config.isFirstChange()) ) { + if (ctrl.debug) { + $log.debug("...updateConfigOptions"); + } + ctrl.updateConfigOptions(); + } + if (changesObj.items && changesObj.items.currentValue) { + ctrl.config.itemsAvailable = changesObj.items.currentValue.length > 0; + } + }; + + ctrl.updatePageSize = function (event) { + ctrl.pageConfig.pageSize = event.pageSize; + ctrl.dtOptions.displayLength = ctrl.pageConfig.pageSize; + ctrl.pageConfig.pageNumber = 1; // goto first page after pageSize/displayLength change + }; + + ctrl.updatePageNumber = function (event) { + if (ctrl.dtInstance) { + ctrl.pageConfig.pageNumber = event.pageNumber; + if (ctrl.dtInstance && ctrl.dtInstance.dataTable) { + ctrl.dtInstance.dataTable.fnPageChange(ctrl.pageConfig.pageNumber - 1); + } + } + }; + + ctrl.$doCheck = function () { + if (ctrl.debug) { + $log.debug("$doCheck"); + } + // do a deep compare on dtOptions and items + if (!angular.equals(ctrl.dtOptions, prevDtOptions) || + !angular.equals(ctrl.pageConfig, prevPageConfig)) { + if (ctrl.debug) { + $log.debug(" dtOptions !== prevDtOptions"); + } + ctrl.updateConfigOptions(); + } + if (!angular.equals(ctrl.items, prevItems)) { + if (ctrl.debug) { + $log.debug(" items !== prevItems"); + } + if (ctrl.items) { + ctrl.config.itemsAvailable = ctrl.items.length > 0; + } + if (angular.isDefined(ctrl.pageConfig) && angular.isDefined(ctrl.pageConfig.numTotalItems)) { + ctrl.pageConfig.numTotalItems = ctrl.items.length; + } + prevItems = angular.copy(ctrl.items); + } + }; + + ctrl.$postLink = function () { + if (ctrl.debug) { + $log.debug(" $postLink"); + } + }; + + ctrl.$onDestroy = function () { + if (ctrl.debug) { + $log.debug(" $onDestroy"); + } + ctrl.dtInstance = {}; + }; + + function setColumnDefs () { + var i = 0, actnBtns = 1; + var offset; + ctrl.dtColumnDefs = []; + + // add checkbox col, not sortable + if (ctrl.config.showCheckboxes) { + ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++).notSortable()); + } + + // add column definitions + _.forEach(ctrl.columns, function (column) { + ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++)); + }); + + // Determine selectionMatchProp column number (add offset due to the checkbox column) + offset = ctrl.config.showCheckboxes ? 1 : 0; + ctrl.selectionMatchPropColNum = _.findIndex(ctrl.columns, ['itemField', ctrl.config.selectionMatchProp]) + offset; + + // add actions col. + if (ctrl.actionButtons && ctrl.actionButtons.length > 0) { + for (actnBtns = 1; actnBtns <= ctrl.actionButtons.length; actnBtns++) { + ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++).notSortable()); + } + } + if (ctrl.menuActions && ctrl.menuActions.length > 0) { + ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++).notSortable()); + } + } + + function listenForDraw () { + var oTable; + var dtInstance = ctrl.dtInstance; + + if (dtInstance && dtInstance.dataTable) { + oTable = dtInstance.dataTable; + if (angular.isDefined(ctrl.pageConfig) && angular.isDefined(ctrl.pageConfig.pageNumber)) { + oTable.fnPageChange(ctrl.pageConfig.pageNumber - 1); + } + ctrl.tableId = oTable[0].id; + oTable.off('draw.dt'); + oTable.on('draw.dt', function () { + if (ctrl.debug) { + $log.debug("--> redraw"); + } + selectRowsByChecked(); + }); + } + } + + function validSelectionMatchProp () { + return _.find(ctrl.columns, ['itemField', ctrl.config.selectionMatchProp]) !== undefined; + } + + /* + * Checkbox Selections + */ + + ctrl.toggleAll = function () { + var item; + var visibleRows = getVisibleRows(); + angular.forEach(visibleRows, function (row) { + item = getItemFromRow(row); + if (item.selected !== ctrl.selectAll) { + item.selected = ctrl.selectAll; + if (ctrl.config && ctrl.config.onCheckBoxChange) { + ctrl.config.onCheckBoxChange(item); + } + } + }); + selectRowsByChecked(); + }; + + ctrl.toggleOne = function (item) { + if (ctrl.config && ctrl.config.onCheckBoxChange) { + ctrl.config.onCheckBoxChange(item); + } + }; + + function getItemFromRow (matchPropValue) { + return _.find(ctrl.items, function (item) { + return _.toString(item[ctrl.config.selectionMatchProp]) === _.toString(matchPropValue); + }); + } + + function selectRowsByChecked () { + if (ctrl.config.showCheckboxes) { + $timeout(function () { + var oTable, rows, checked; + + oTable = ctrl.dtInstance.DataTable; + + if (ctrl.debug) { + $log.debug(" selectRowsByChecked"); + } + + if (angular.isUndefined(oTable)) { + return; + } + + if (ctrl.debug) { + $log.debug(" ...oTable defined"); + } + + // deselect all + rows = oTable.rows(); + rows.deselect(); + + // select those with checked checkboxes + rows = oTable.rows( function ( idx, data, node ) { + // row td input type=checkbox + checked = node.children[0].children[0].checked; + return checked; + }); + + if (ctrl.debug) { + $log.debug(" ... #checkedRows = " + rows[0].length); + } + + if (rows[0].length > 0) { + rows.select(); + } + setSelectAllCheckbox(); + }); + } + } + + function setSelectAllCheckbox () { + var numVisibleRows, numCheckedRows; + + if (ctrl.debug) { + $log.debug(" setSelectAllCheckbox"); + } + + numVisibleRows = getVisibleRows().length; + numCheckedRows = document.querySelectorAll("#" + ctrl.tableId + " tbody tr.even.selected").length + + document.querySelectorAll("#" + ctrl.tableId + " tbody tr.odd.selected").length; + ctrl.selectAll = (numVisibleRows === numCheckedRows); + } + + function getVisibleRows () { + // Returns an array of visible 'selectionMatchProp' values + // Ex. if selectionMatchProp === 'name' & selectionMatchPropColNum === 1 & + // page length === 3 + // returns ['Mary Jane', 'Fred Flinstone', 'Frank Livingston'] + // + var i, rowData, visibleRows = []; + + var anNodes = document.querySelectorAll("#" + ctrl.tableId + " tbody tr"); + + for (i = 0; i < anNodes.length; ++i) { + rowData = anNodes[i].cells; + if (rowData !== null && rowData.length > ctrl.selectionMatchPropColNum) { + visibleRows.push(_.trim(rowData[ctrl.selectionMatchPropColNum].innerText)); + } + } + + if (ctrl.debug) { + $log.debug(" getVisibleRows (" + visibleRows.length + ")"); + } + + return visibleRows; + } + + /* + * Action Buttons and Menus + */ + + ctrl.handleButtonAction = function (action, item) { + if (action && action.actionFn) { + action.actionFn(action, item); + } + }; + + ctrl.handleColAction = function (key, value) { + var tableCol = $filter('filter')(ctrl.columns, {itemField: key}); + + if (tableCol && tableCol.length === 1 && tableCol[0].hasOwnProperty('colActionFn')) { + tableCol[0].colActionFn(value); + } + }; + + ctrl.areActions = function () { + return (ctrl.actionButtons && ctrl.actionButtons.length > 0) || + (ctrl.menuActions && ctrl.menuActions.length > 0); + }; + + ctrl.calcActionsColspan = function () { + var colspan = 0; + + if (ctrl.actionButtons && ctrl.actionButtons.length > 0) { + colspan += ctrl.actionButtons.length; + } + + if (ctrl.menuActions && ctrl.menuActions.length > 0) { + colspan += 1; + } + + return colspan; + }; + + ctrl.handleMenuAction = function (action, item) { + if (!ctrl.checkDisabled(item) && action && action.actionFn && (action.isDisabled !== true)) { + action.actionFn(action, item); + } + }; + + ctrl.setupActions = function (item, event) { + /* Ignore disabled items completely + if (ctrl.checkDisabled(item)) { + return; + }*/ + + // update the actions based on the current item + // $scope.updateActions(item); + + $timeout(function () { + var parentDiv = undefined; + var nextElement; + + nextElement = event.target; + while (nextElement && !parentDiv) { + if (nextElement.className.indexOf('dropdown-kebab-pf') !== -1) { + parentDiv = nextElement; + if (nextElement.className.indexOf('open') !== -1) { + setDropMenuLocation (parentDiv); + } + } + nextElement = nextElement.parentElement; + } + }); + }; + + ctrl.checkDisabled = function () { + //TODO: implement checkDisabled + return false; + }; + + function setDropMenuLocation (parentDiv) { + var dropButton = parentDiv.querySelector('.dropdown-toggle'); + var dropMenu = parentDiv.querySelector('.dropdown-menu'); + var parentRect = $element[0].getBoundingClientRect(); + var buttonRect = dropButton.getBoundingClientRect(); + var menuRect = dropMenu.getBoundingClientRect(); + var menuTop = buttonRect.top - menuRect.height; + var menuBottom = buttonRect.top + buttonRect.height + menuRect.height; + + if ((menuBottom <= parentRect.top + parentRect.height) || (menuTop < parentRect.top)) { + ctrl.dropdownClass = 'dropdown'; + } else { + ctrl.dropdownClass = 'dropup'; + } + } + + ctrl.trustAsHtml = function (html) { + return $sce.trustAsHtml(html); + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.toolbars.component:pfToolbar + * @restrict E + * + * @description + * Standard toolbar component. Includes filtering and view selection capabilities + *

+ * + * @param {object} config configuration settings for the toolbar:
+ *
    + *
  • .filterConfig - (Object) Optional filter config. If undefined, no filtering capabilities are shown. + * See pfSimpleFilter for filter config options. + *
  • .sortConfig - (Object) Optional sort config. If undefined, no sort capabilities are shown. + * See pfSort for sort config options. + *
  • .viewsConfig - (Object) Optional configuration settings for view type selection + *
      + *
    • .views - (Array) List of available views for selection. See pfViewUtils for standard available views + *
        + *
      • .id - (String) Unique id for the view, used for comparisons + *
      • .title - (String) Optional title, uses as a tooltip for the view selector + *
      • .iconClass - (String) Icon class to use for the view selector + *
      + *
    • .onViewSelect - ( function(view) ) Function to call when a view is selected + *
    • .currentView - the id of the currently selected view + *
    + *
  • .actionsConfig - (Object) Optional configuration settings for toolbar actions + *
      + *
    • .primaryActions - (Array) List of primary actions to display on the toolbar + *
        + *
      • .name - (String) The name of the action, displayed on the button + *
      • .title - (String) Optional title, used for the tooltip + *
      • .actionFn - (function(action)) Function to invoke when the action selected + *
      • .isDisabled - (Boolean) set to true to disable the action + *
      + *
    • .moreActions - (Array) List of secondary actions to display on the toolbar action pulldown menu + *
        + *
      • .name - (String) The name of the action, displayed on the button + *
      • .title - (String) Optional title, used for the tooltip + *
      • .actionFn - (function(action)) Function to invoke when the action selected + *
      • .isDisabled - (Boolean) set to true to disable the action + *
      • .isSeparator - (Boolean) set to true if this is a placehodler for a separator rather than an action + *
      + *
    • .actionsInclude - (Boolean) set to true if using the actions transclude to add custom action buttons (only available if using Angular 1.5 or later) + *
    + *
  • .isTableView - (Boolean) set to true if toolbar is only being used with a table view and viewsConfig is not defined. + *
+ * + * @example + + +
+
+ + + + + + + + + +
+
+ +
+
+ {{item.name}} +
+
+ {{item.address}} +
+
+
+
+ {{item.age}} +
+
+ {{item.birthMonth}} +
+
+
+
+
+ +
+ {{item.name}} +
+
+ {{item.address}} +
+
+ {{item.birthMonth}} +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.toolbars.demo', ['patternfly.toolbars','patternfly.table']); + + + + angular.module('patternfly.toolbars.demo').controller('ViewCtrl', ['$scope', '$timeout', 'pfViewUtils', '$filter', + function ($scope, $timeout, pfViewUtils, $filter) { + $scope.filtersText = ''; + $scope.showPagination = false; + + $scope.columns = [ + { header: "Name", itemField: "name" }, + { header: "Age", itemField: "age"}, + { header: "Address", itemField: "address" }, + { header: "BirthMonth", itemField: "birthMonth"} + ]; + + $scope.allItems = [ + { + name: "Fred Flintstone", + age: 57, + address: "20 Dinosaur Way, Bedrock, Washingstone", + birthMonth: 'February' + }, + { + name: "John Smith", + age: 23, + address: "415 East Main Street, Norfolk, Virginia", + birthMonth: 'October' + }, + { + name: "Frank Livingston", + age: 71, + address: "234 Elm Street, Pittsburgh, Pennsylvania", + birthMonth: 'March' + }, + { + name: "Judy Green", + age: 21, + address: "2 Apple Boulevard, Cincinatti, Ohio", + birthMonth: 'December' + }, + { + name: "Pat Thomas", + age: 19, + address: "50 Second Street, New York, New York", + birthMonth: 'February' + }, + { + name: "Linda McGovern", + age: 32, + address: "22 Oak Stree, Denver, Colorado", + birthMonth: 'March' + }, + { + name: "Jim Brown", + age: 55, + address: "72 Bourbon Way. Nashville. Tennessee", + birthMonth: 'March' + }, + { + name: "Holly Nichols", + age: 34, + address: "21 Jump Street, Hollywood, California", + birthMonth: 'March' + }, + { + name: "Chris Thomas", + age: 21, + address: "50 Second Street, New York, New York", + birthMonth: 'April' + }, + { + name: "Jeff McGovern", + age: 30, + address: "22 Oak Stree, Denver, Colorado", + birthMonth: 'November' + }, + { + name: "Jessica Brown", + age: 50, + address: "72 Bourbon Way. Nashville. Tennessee", + birthMonth: 'January' + }, + { + name: "Dave Nichols", + age: 32, + address: "21 Jump Street, Hollywood, California", + birthMonth: 'June' + } + ]; + $scope.items = $scope.allItems; + + var matchesFilter = function (item, filter) { + var match = true; + var re = new RegExp(filter.value, 'i'); + + if (filter.id === 'name') { + match = item.name.match(re) !== null; + } else if (filter.id === 'age') { + match = item.age === parseInt(filter.value); + } else if (filter.id === 'address') { + match = item.address.match(re) !== null; + } else if (filter.id === 'birthMonth') { + match = item.birthMonth === filter.value; + } + return match; + }; + + var matchesFilters = function (item, filters) { + var matches = true; + + filters.forEach(function(filter) { + if (!matchesFilter(item, filter)) { + matches = false; + return false; + } + }); + return matches; + }; + + var applyFilters = function (filters) { + $scope.items = []; + if (filters && filters.length > 0) { + $scope.allItems.forEach(function (item) { + if (matchesFilters(item, filters)) { + $scope.items.push(item); + } + }); + } else { + $scope.items = $scope.allItems; + } + }; + + var clearFilters = function() { + filterChange([]); + $scope.filterConfig.appliedFilters = []; + }; + + var filterChange = function (filters) { + $scope.filtersText = ""; + filters.forEach(function (filter) { + $scope.filtersText += filter.title + " : " + filter.value + "\n"; + }); + applyFilters(filters); + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + }; + + $scope.filterConfig = { + fields: [ + { + id: 'name', + title: 'Name', + placeholder: 'Filter by Name...', + filterType: 'text' + }, + { + id: 'age', + title: 'Age', + placeholder: 'Filter by Age...', + filterType: 'text' + }, + { + id: 'address', + title: 'Address', + placeholder: 'Filter by Address...', + filterType: 'text' + }, + { + id: 'birthMonth', + title: 'Birth Month', + placeholder: 'Filter by Birth Month...', + filterType: 'select', + filterValues: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + } + ], + resultsCount: $scope.items.length, + totalCount: $scope.allItems.length, + appliedFilters: [], + onFilterChange: filterChange + }; + + var viewSelected = function(viewId) { + $scope.viewType = viewId; + $scope.sortConfig.show = ($scope.viewType !== "tableView"); + }; + + $scope.viewsConfig = { + views: [pfViewUtils.getListView(), pfViewUtils.getCardView(), pfViewUtils.getTableView()], + onViewSelect: viewSelected + }; + + $scope.viewsConfig.currentView = $scope.viewsConfig.views[0].id; + $scope.viewType = $scope.viewsConfig.currentView; + + var monthVals = { + 'January': 1, + 'February': 2, + 'March': 3, + 'April': 4, + 'May': 5, + 'June': 6, + 'July': 7, + 'August': 8, + 'September': 9, + 'October': 10, + 'November': 11, + 'December': 12 + }; + var compareFn = function(item1, item2) { + var compValue = 0; + if ($scope.sortConfig.currentField.id === 'name') { + compValue = item1.name.localeCompare(item2.name); + } else if ($scope.sortConfig.currentField.id === 'age') { + compValue = item1.age - item2.age; + } else if ($scope.sortConfig.currentField.id === 'address') { + compValue = item1.address.localeCompare(item2.address); + } else if ($scope.sortConfig.currentField.id === 'birthMonth') { + compValue = monthVals[item1.birthMonth] - monthVals[item2.birthMonth]; + } + + if (!$scope.sortConfig.isAscending) { + compValue = compValue * -1; + } + + return compValue; + }; + + var sortChange = function (sortId, isAscending) { + $scope.items.sort(compareFn); + }; + + $scope.sortConfig = { + fields: [ + { + id: 'name', + title: 'Name', + sortType: 'alpha' + }, + { + id: 'age', + title: 'Age', + sortType: 'numeric' + }, + { + id: 'address', + title: 'Address', + sortType: 'alpha' + }, + { + id: 'birthMonth', + title: 'Birth Month', + sortType: 'alpha' + } + ], + onSortChange: sortChange + }; + + $scope.actionsText = ""; + var performAction = function (action) { + $scope.actionsText = action.name + "\n" + $scope.actionsText; + }; + + $scope.actionsConfig = { + primaryActions: [ + { + name: 'Action 1', + title: 'Do the first thing', + actionFn: performAction + }, + { + name: 'Action 2', + title: 'Do something else', + actionFn: performAction + } + ], + moreActions: [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + actionFn: performAction, + title: 'Do something similar' + } + ], + actionsInclude: true + }; + + $scope.toolbarConfig = { + viewsConfig: $scope.viewsConfig, + filterConfig: $scope.filterConfig, + sortConfig: $scope.sortConfig, + actionsConfig: $scope.actionsConfig + }; + + $scope.listConfig = { + selectionMatchProp: 'name', + checkDisabled: false, + itemsAvailable: true, + onCheckBoxChange: handleCheckBoxChange + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + $scope.noItemsConfig = { + title: 'No Results Match the Filter Criteria', + info: 'The active filters are hiding all items.', + helpLink: { + urlLabel: 'Clear All Filters', + urlAction: clearFilters + } + }; + + $scope.tableConfig = { + onCheckBoxChange: handleCheckBoxChange, + selectionMatchProp: "name", + itemsAvailable: true + }; + + $scope.doAdd = function () { + $scope.actionsText = "Add Action\n" + $scope.actionsText; + }; + + $scope.optionSelected = function (option) { + $scope.actionsText = "Option " + option + " selected\n" + $scope.actionsText; + }; + + $scope.updateItemsAvailable = function () { + $scope.tableConfig.itemsAvailable = $scope.listConfig.itemsAvailable; + if(!$scope.listConfig.itemsAvailable) { + $scope.toolbarConfig.filterConfig.resultsCount = 0; + $scope.toolbarConfig.filterConfig.totalCount = 0; + $scope.toolbarConfig.filterConfig.selectedCount = 0; + } else { + $scope.toolbarConfig.filterConfig.resultsCount = $scope.items.length; + $scope.toolbarConfig.filterConfig.totalCount = $scope.allItems.length; + handleCheckBoxChange(); + } + }; + + function handleCheckBoxChange (item) { + var selectedItems = $filter('filter')($scope.allItems, {selected: true}); + if (selectedItems) { + $scope.toolbarConfig.filterConfig.selectedCount = selectedItems.length; + } + } + + $scope.togglePagination = function () { + if ($scope.showPagination) { + $scope.pageConfig = { + pageSize: 5 + } + } else { + delete $scope.pageConfig; + } + $scope.addNewComponentToDOM(); + }; + + $scope.showComponent = true; + + $scope.addNewComponentToDOM = function () { + $scope.showComponent = false; + $timeout(() => $scope.showComponent = true); + }; + } + ]); + +
+ */ +;angular.module('patternfly.toolbars').component('pfToolbar', { + bindings: { + config: '=' + }, + transclude: { + 'actions': '?' + }, + templateUrl: 'toolbars/toolbar.html', + controller: function () { + 'use strict'; + + var ctrl = this; + var prevConfig; + + ctrl.$onInit = function () { + if (angular.isDefined(ctrl.config.sortConfig) && angular.isUndefined(ctrl.config.sortConfig.show)) { + // default to true + ctrl.config.sortConfig.show = true; + } + + angular.extend(ctrl, { + viewSelected: viewSelected, + isViewSelected: isViewSelected, + isTableViewSelected: isTableViewSelected, + checkViewDisabled: checkViewDisabled, + addFilter: addFilter, + handleAction: handleAction + }); + }; + + ctrl.$onChanges = function () { + setupConfig (); + }; + + ctrl.$doCheck = function () { + // do a deep compare on config + if (!angular.equals(ctrl.config, prevConfig)) { + setupConfig(); + } + }; + + function setupConfig () { + prevConfig = angular.copy(ctrl.config); + + if (ctrl.config && ctrl.config.viewsConfig && ctrl.config.viewsConfig.views) { + ctrl.config.viewsConfig.viewsList = angular.copy(ctrl.config.viewsConfig.views); + + if (!ctrl.config.viewsConfig.currentView) { + ctrl.config.viewsConfig.currentView = ctrl.config.viewsConfig.viewsList[0].id; + } + } + } + + function viewSelected (viewId) { + ctrl.config.viewsConfig.currentView = viewId; + if (ctrl.config.viewsConfig.onViewSelect && !ctrl.checkViewDisabled(viewId)) { + ctrl.config.viewsConfig.onViewSelect(viewId); + } + } + + function isViewSelected (viewId) { + return ctrl.config.viewsConfig && (ctrl.config.viewsConfig.currentView === viewId); + } + + function isTableViewSelected () { + return ctrl.config.viewsConfig ? (ctrl.config.viewsConfig.currentView === 'tableView') : ctrl.config.isTableView; + } + + function checkViewDisabled (view) { + return ctrl.config.viewsConfig.checkViewDisabled && ctrl.config.viewsConfig.checkViewDisabled(view); + } + + function filterExists (filter) { + var foundFilter = _.find(ctrl.config.filterConfig.appliedFilters, {title: filter.title, value: filter.value}); + return foundFilter !== undefined; + } + + function enforceSingleSelect (filter) { + _.remove(ctrl.config.filterConfig.appliedFilters, {title: filter.title}); + } + + function addFilter (field, value) { + var newFilter = { + id: field.id, + title: field.title, + value: value + }; + if (!filterExists(newFilter)) { + if (field.filterType === 'select') { + enforceSingleSelect(newFilter); + } + ctrl.config.filterConfig.appliedFilters.push(newFilter); + + if (ctrl.config.filterConfig.onFilterChange) { + ctrl.config.filterConfig.onFilterChange(ctrl.config.filterConfig.appliedFilters); + } + } + } + + function handleAction (action) { + if (action && action.actionFn && (action.isDisabled !== true)) { + action.actionFn(action); + } + } + } +}); +;/** + * @ngdoc directive + * @name patternfly.utils:pfFixedAccordion + * @restrict A + * @element ANY + * @param {string} scrollSelector specifies the selector to be used to find the element that should scroll (optional, the entire collapse area scrolls by default) + * @param {string} groupHeight Height to set for uib-accordion group (optional) + * @param {string} groupClass Class to set for uib-accordion group (optional) + * + * @description + * Directive for setting a ui-bootstrap uib-accordion to use a fixed height (collapse elements scroll when necessary) + * + * @example + + +
+
+ +
+ {{item.content}} +
+
+
+
+
+ + + angular.module('patternfly.utils').controller( 'AccordionCntrl', function ($scope) { + $scope.items = [ + { heading: 'Lorem ipsum', isOpen: false, content: 'Praesent sagittis est et arcu fringilla placerat. Cras erat ante, dapibus non mauris ac, volutpat sollicitudin ligula. Morbi gravida nisl vel risus tempor, sit amet luctus erat tempus. Curabitur blandit sem non pretium bibendum. Donec eleifend non turpis vitae vestibulum. Vestibulum ut sem ac nunc posuere blandit sed porta lorem. Cras rutrum velit vel leo iaculis imperdiet.' }, + { heading: 'Dolor sit amet', isOpen: false, content: 'Donec consequat dignissim neque, sed suscipit quam egestas in. Fusce bibendum laoreet lectus commodo interdum. Vestibulum odio ipsum, tristique et ante vel, iaculis placerat nulla. Suspendisse iaculis urna feugiat lorem semper, ut iaculis risus tempus.' }, + { heading: 'Consectetur', isOpen: false, content: 'Curabitur nisl quam, interdum a venenatis a, consequat a ligula. Nunc nec lorem in erat rhoncus lacinia at ac orci. Sed nec augue congue, vehicula justo quis, venenatis turpis. Nunc quis consectetur purus. Nam vitae viverra lacus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eu augue felis. Maecenas in dignissim purus, quis pulvinar lectus. Vivamus euismod ultrices diam, in mattis nibh.' }, + { heading: 'Adipisicing elit', isOpen: false, content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' }, + { heading: 'Suspendisse lectus tortor', isOpen: false, content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.' }, + { heading: 'Velit mauris', isOpen: false, content: 'Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna.' }, + { heading: 'Aliquam convallis', isOpen: false, content: 'Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.' }, + { heading: 'Vulputate dictum', isOpen: false, content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at ante. Mauris eleifend, quam a vulputate dictum, massa quam dapibus leo, eget vulputate orci purus ut lorem. In fringilla mi in ligula. Pellentesque aliquam quam vel dolor. Nunc adipiscing. Sed quam odio, tempus ac, aliquam molestie, varius ac, tellus. Vestibulum ut nulla aliquam risus rutrum interdum. Pellentesque lorem. Curabitur sit amet erat quis risus feugiat viverra. Pellentesque augue justo, sagittis et, lacinia at, venenatis non, arcu. Nunc nec libero. In cursus dictum risus. Etiam tristique nisl a nulla. Ut a orci. Curabitur dolor nunc, egestas at, accumsan at, malesuada nec, magna.' } + ]; + }); + +
+ */ +angular.module('patternfly.utils').directive('pfFixedAccordion', ["$window", "$timeout", function ($window, $timeout) { + 'use strict'; + return { + restrict: 'A', + scope: { + scrollSelector: '@', + groupHeight: '@', + groupClass: '@' + }, + link: function ($scope, $element, $attrs) { + var contentElementHeight = function (contentElement) { + var contentHeight = contentElement.offsetHeight; + contentHeight += parseInt(getComputedStyle(contentElement).marginTop); + contentHeight += parseInt(getComputedStyle(contentElement).marginBottom); + + return contentHeight; + }; + + var setBodyScrollHeight = function (parentElement, bodyHeight) { + // Set the max-height for the fixed height components + var collapsePanels = parentElement[0].querySelectorAll('.panel-collapse'); + var collapsePanel; + var scrollElement; + var panelContents; + var innerHeight; + var scroller; + + angular.forEach(collapsePanels, function (collapseElement) { + collapsePanel = angular.element(collapseElement); + scrollElement = collapsePanel; + innerHeight = 0; + + if (angular.isDefined($scope.scrollSelector)) { + scroller = angular.element(collapsePanel[0].querySelector($scope.scrollSelector)); + if (scroller.length) { + scrollElement = angular.element(scroller[0]); + + panelContents = collapsePanel.children(); + angular.forEach(panelContents, function (contentElement) { + // Get the height of all the non-scroll element contents + if (contentElement !== scrollElement[0]) { + innerHeight += contentElementHeight(contentElement); + } + }); + } + } + + // Make sure we have enough height to be able to scroll the contents if necessary + angular.element(scrollElement).css('max-height', Math.max((bodyHeight - innerHeight), 25) + 'px'); + angular.element(scrollElement).css('overflow-y', 'auto'); + }); + }; + + var setCollapseHeights = function () { + var height, openPanel, contentHeight, bodyHeight, overflowY = 'hidden'; + var parentElement = angular.element($element[0].querySelector('.panel-group')); + var headings = angular.element(parentElement).children(); + + height = parentElement[0].clientHeight; + + // Close any open panel + openPanel = parentElement[0].querySelectorAll('.collapse.in'); + if (openPanel && openPanel.length > 0) { + angular.element(openPanel).removeClass('in'); + } + + // Determine the necessary height for the closed content + contentHeight = 0; + + angular.forEach(headings, function (heading) { + contentHeight += contentElementHeight(heading); + }); + + // Determine the height remaining for opened collapse panels + bodyHeight = height - contentHeight; + + // Make sure we have enough height to be able to scroll the contents if necessary + if (bodyHeight < 25) { + bodyHeight = 25; + + // Allow the parent to scroll so the child elements are accessible + overflowY = 'auto'; + } + + // Reopen the initially opened panel + if (openPanel && openPanel.length > 0) { + angular.element(openPanel).addClass("in"); + } + + angular.element(parentElement).css('overflow-y', overflowY); + + // Update body scroll element's height after allowing these changes to set in + $timeout(function () { + setBodyScrollHeight(parentElement, bodyHeight); + }); + }; + + var debounceResize = _.debounce(setCollapseHeights, 150, { + maxWait: 250 + }); + + if ($scope.groupHeight) { + angular.element($element[0].querySelector('.panel-group')).css('height', $scope.groupHeight); + } + if ($scope.groupClass) { + angular.element($element[0].querySelector(".panel-group")).addClass($scope.groupClass); + } + + $timeout(function () { + setCollapseHeights(); + }, 100); + + // Update on window resizing + $element.on('resize', function () { + debounceResize(); + }); + + angular.element($window).on('resize', function () { + debounceResize(); + }); + } + }; +}]); +; +/** + * @ngdoc directive + * @name patternfly.utils.directive:pfTransclude + * @restrict A + * @element ANY + * @param {string} pfTransclude specifies the type of transclusion to use.
+ * Values: + *
    + *
  • 'sibling' - The transcluded contents scope is a sibling one to the element where transclusion happens (default) + *
  • 'parent' - The transcluded contents scope is that of the element where transclusion happens. + *
  • 'child' - The transcluded contents scope is child scope to the scope of the element where transclusion happens. + *
+ * + * @description + * Directive for transcluding in directives and setting up scope of children of parent directives. This is a workaround + * for https://github.com/angular/angular.js/issues/5489 + * + * @example + + +
+ Here the scope id is: {{$id}} + + +
This content was transcluded using pf-transclude or pf-transclude="sibling".
Its scope is: {{$id}} the parent of which is {{$parent.$id}}
+
+ + +
This content was transcluded using pf-transclude="parent".
Its scope is: {{$id}} the parent of which is {{$parent.$id}}
+
+ + +
This content was transcluded using pf-transclude="child".
Its scope is: {{$id}} the parent of which is {{$parent.$id}}
+
+
+
+ + + angular.module('patternfly.utils') + .controller( 'UtilCtrl', function($scope) { + + }) + + .config(function($provide){ + $provide.decorator('ngTranscludeDirective', ['$delegate', function($delegate) { + // Remove the original directive + $delegate.shift(); + return $delegate; + }]); + }) + + .directive( 'transcludeSibling', function() { + return { + restrict: 'E', + transclude: true, + scope: {}, + template: + '
' + + '

I am a directive with scope {{$id}}

' + + '' + + '
' + } + }) + + .directive( 'transcludeParent', function() { + return { + restrict: 'E', + transclude: true, + scope: {}, + template: + '
' + + '

I am a directive with scope {{$id}}

' + + '' + + '
' + } + }) + + .directive( 'transcludeChild', function() { + return { + restrict: 'E', + transclude: true, + scope: {}, + template: + '
' + + '

I am a directive with scope {{$id}}

' + + '' + + '
' + } + }) + ; +
+
+ */ +angular + .module('patternfly.utils').directive('pfTransclude', function () { + 'use strict'; + return { + restrict: 'A', + link: function ($scope, $element, $attrs, controller, $transclude) { + var iChildScope; + var iScopeType; + + if (!$transclude) { + throw new Error('pfTransclude - ' + + 'Illegal use of pfTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}'); + } + + iScopeType = $attrs.pfTransclude || 'sibling'; + + switch (iScopeType) { + case 'sibling': + $transclude(function (clone) { + $element.empty(); + $element.append(clone); + }); + break; + case 'parent': + $transclude($scope, function (clone) { + $element.empty(); + $element.append( clone ); + }); + break; + case 'child': + iChildScope = $scope.$new(); + $transclude( iChildScope, function (clone) { + $element.empty(); + $element.append( clone ); + $element.on( '$destroy', function () { + iChildScope.$destroy(); + }); + }); + break; + } + } + }; + }); +;(function () { + 'use strict'; + + angular.module('patternfly.utils').constant('pfUtils', { + merge: function (source1, source2) { + var retValue; + + if (typeof angular.merge === 'function') { + retValue = this.angularMerge(source1, source2); + } else if (typeof _.merge === 'function') { + retValue = this._merge(source1, source2); + } else if (typeof $.extend === 'function') { + retValue = this.$extend(source1, source2); + } else { + retValue = this.mergeDeep(source1, source2); + } + + return retValue; + }, + angularMerge: function (source1, source2) { + return angular.merge({}, source1, source2); + }, + _merge: function (source1, source2) { + return _.merge({}, source1, source2); + }, + $extend: function (source1, source2) { + return $.extend(true, angular.copy(source1), source2); + }, + mergeDeep: function (source1, source2) { + return mergeDeep({}, angular.copy(source1), angular.copy(source2)); + }, + utilizationToColor: function (utilization) { + return utilizationToColor(utilization); + }, + colorPalette: patternfly.pfPaletteColors + }); +})(); + +/* This function does not merge/concat Arrays. + * It replaces the earlier Array with any latter Array. + */ +function mergeDeep (dst) { + 'use strict'; + angular.forEach(arguments, function (obj) { + if (obj !== dst) { + angular.forEach(obj, function (value, key) { + if (dst[key] && dst[key].constructor && dst[key].constructor === Object) { + mergeDeep(dst[key], value); + } else { + dst[key] = value; + } + }); + } + }); + return dst; +} + +function utilizationToColor (number) { + 'use strict'; + // as the function expects a value between 0 and 1, and green = 0° and red = 120° + // we convert the input to the appropriate hue value + var invert = 100 - number; + var hue = invert * 1.2 / 360; + // we convert hsl to rgb (saturation 100%, lightness 50%) + var rgb = hslToRgb(hue, 1, .5); + // we format to css value and return + return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'; +} + +function hue2rgb (p, q, t) { + 'use strict'; + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; +} + +function hslToRgb (h, s, l) { + 'use strict'; + var r, g, b, q, p; + if (s === 0) { + r = g = b = l; // achromatic + } else { + q = l < 0.5 ? l * (1 + s) : l + s - l * s; + p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]; +} +;/** + * @ngdoc directive + * @name patternfly.validation:pfValidation + * @restrict E + * @element INPUT + * @scope + * + * @description + * Directive used for input validation based on custom function. + * + * @param {expression=} pfValidationDisabled If true, the validation is disabled, it is enabled otherwise. + * + * @example + + + +
+
+ +
+ +
+ + The value you typed is not a number. +
+
+ +
+ +
+ + The value you typed is not a number. +
+
+ +
+ +
+ + The value you typed is not a number. +
+
+ +
+ +
+ +
+
+
+
+
+ + + angular.module( 'patternfly.validation' ).controller( 'ValidationDemoCtrl', function( $scope ) { + $scope.myValue = "Change this value to be a number"; + $scope.myValueValid = 42; + $scope.isValidationDisabled = false; + + $scope.isNumber = function (value) { + if (isNaN(value)) { + return false; + } + + return true; + } + }); + + +
+ */ +angular.module('patternfly.validation', []).directive('pfValidation', ["$timeout", function ($timeout) { + 'use strict'; + + return { + restrict: 'A', + require: 'ngModel', + scope: { + pfValidation: '&', + pfValidationDisabled: '=' + }, + link: function (scope, element, attrs, ctrl) { + + scope.inputCtrl = ctrl; + scope.valEnabled = !attrs.pfValidationDisabled; + + scope.$watch('pfValidationDisabled', function (newVal) { + scope.valEnabled = !newVal; + if (newVal) { + scope.inputCtrl.$setValidity('pfValidation', true); + toggleErrorClass(false); + } else { + validate(); + } + }); + + // If validation function is set + if (attrs.pfValidation) { + // using $timeout(0) to get the actual $modelValue + $timeout(function () { + validate(); + }, 0); + } else if (!scope.inputCtrl.$valid && scope.inputCtrl.$dirty) { + toggleErrorClass(true); + } + + scope.$watch('inputCtrl.$valid', function (isValid) { + if (isValid) { + toggleErrorClass(false); + } else { + toggleErrorClass(true); + } + }); + + scope.$watch('inputCtrl.$modelValue', function () { + validate(); + }); + + function validate () { + var valid; + + var val = scope.inputCtrl.$modelValue; + + var valFunc = scope.pfValidation({'input': val}); + + if (!attrs.pfValidation) { + valFunc = true; + } + + valid = !val || valFunc || val === ''; + + if (scope.valEnabled && !valid) { + toggleErrorClass(true); + } else { + toggleErrorClass(false); + } + } + + function toggleErrorClass (add) { + var messageElement = element.next(); + var parentElement = element.parent(); + var hasErrorM = parentElement.hasClass('has-error'); + var wasHidden = messageElement.hasClass('ng-hide'); + + scope.inputCtrl.$setValidity('pf-validation', !add); + + if (add) { + if (!hasErrorM) { + parentElement.addClass('has-error'); + } + if (wasHidden) { + messageElement.removeClass('ng-hide'); + } + } + + if (!add) { + if (hasErrorM) { + parentElement.removeClass('has-error'); + } + if (!wasHidden) { + messageElement.addClass('ng-hide'); + } + } + } + } + }; +}]); +;/** + * @ngdoc directive + * @name patternfly.views.component:pfCardView + * @restrict E + * + * @description + * Component for rendering cards in a view + *

+ * + * @param {object} config configuration settings for the cards:
+ *
    + *
  • .showSelectBox - (boolean) Show item selection boxes for each item, default is true + *
  • .selectItems - (boolean) Allow card selection, default is false + *
  • .dlbClick - (boolean) Handle double clicking (item remains selected on a double click). Default is false. + *
  • .multiSelect - (boolean) Allow multiple card selections, selectItems must also be set, not applicable when dblClick is true. Default is false + *
  • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
  • .selectedItems - (array) Current set of selected items + *
  • .checkDisabled - ( function(item) ) Function to call to determine if an item is disabled, default is none + *
  • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
  • .onSelect - ( function(item, event) ) Called to notify of item selection, default is none + *
  • .onSelectionChange - ( function(items) ) Called to notify when item selections change, default is none + *
  • .onClick - ( function(item, event) ) Called to notify when an item is clicked, default is none + *
  • .onDblClick - ( function(item, event) ) Called to notify when an item is double clicked, default is none + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. + *
+ * @param {object} pageConfig Optional pagination configuration object. Since all properties are optional it is ok to specify: 'pageConfig = {}' to indicate that you want to + * use pagination with the default parameters. + *
    + *
  • .pageNumber - (number) Optional Initial page number to display. Default is page 1. + *
  • .pageSize - (number) Optional Initial page size/display length to use. Ie. Number of "Items per Page". Default is 10 items per page + *
  • .pageSizeIncrements - (Array[Number]) Optional Page size increments for the 'per page' dropdown. If not specified, the default values are: [5, 10, 20, 40, 80, 100] + *
+ * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @param {array} emptyStateActionButtons Optional buttons to display under the icon, title, and informational paragraph in the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @param {Array} items the data to be shown in the cards
+ * + * @example + + + +
+
+ +
+ {{item.name}} +
+
+ {{item.address}} +
+
+ {{item.city}}, {{item.state}} +
+
+
+
+
+
+
+ +
+ + + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + + +
+
+
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.views').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.showPagination = false; + $scope.eventText = ''; + var handleSelect = function (item, e) { + $scope.eventText = item.name + ' selected\n' + $scope.eventText; + }; + var handleSelectionChange = function (selectedItems, e) { + $scope.eventText = selectedItems.length + ' items selected\n' + $scope.eventText; + }; + var handleClick = function (item, e) { + $scope.eventText = item.name + ' clicked\n' + $scope.eventText; + }; + var handleDblClick = function (item, e) { + $scope.eventText = item.name + ' double clicked\n' + $scope.eventText; + }; + var handleCheckBoxChange = function (item, selected, e) { + $scope.eventText = item.name + ' checked: ' + item.selected + '\n' + $scope.eventText; + }; + $scope.togglePagination = function () { + if ($scope.showPagination) { + $scope.pageConfig = { + pageSize: 5 + } + } else { + delete $scope.pageConfig; + } + }; + + var checkDisabledItem = function(item) { + return $scope.showDisabled && (item.name === "John Smith"); + }; + + $scope.selectType = 'checkbox'; + $scope.updateSelectionType = function() { + if ($scope.selectType === 'checkbox') { + $scope.config.selectItems = false; + $scope.config.showSelectBox = true; + } else if ($scope.selectType === 'card') { + $scope.config.selectItems = true; + $scope.config.showSelectBox = false; + } else { + $scope.config.selectItems = false; + $scope.config.showSelectBox = false; + } + }; + + $scope.showDisabled = false; + + $scope.config = { + selectItems: false, + itemsAvailable: true, + multiSelect: false, + dblClick: false, + selectionMatchProp: 'name', + selectedItems: [], + checkDisabled: checkDisabledItem, + showSelectBox: true, + onSelect: handleSelect, + onSelectionChange: handleSelectionChange, + onCheckBoxChange: handleCheckBoxChange, + onClick: handleClick, + onDblClick: handleDblClick + }; + + $scope.items = [ + { + name: "Fred Flintstone", + address: "20 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + name: "John Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia" + }, + { + name: "Frank Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + name: "Judy Green", + address: "2 Apple Boulevard", + city: "Cincinatti", + state: "Ohio" + }, + { + name: "Pat Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + }, + { + name: "Betty Rubble", + address: "30 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + name: "Martha Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia" + }, + { + name: "Liz Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + name: "Howard McGovern", + address: "22 Oak Street", + city: "Denver", + state: "Colorado" + }, + { + name: "Joyce Brown", + address: "72 Bourbon Way", + city: "Nashville", + state: "Tennessee" + }, + { + name: "Mike Nichols", + address: "21 Jump Street", + city: "Hollywood", + state: "California" + }, + { + name: "Mark Edwards", + address: "17 Cross Street", + city: "Boston", + state: "Massachusetts" + }, + { + name: "Chris Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + } + ]; + + var performEmptyStateAction = function (action) { + $scope.eventText = action.name + "\r\n" + $scope.eventText; + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + $scope.emptyStateActionButtons = [ + { + name: 'Main Action', + title: 'Perform an action', + actionFn: performEmptyStateAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 2', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 3', + title: 'Perform an action', + actionFn: performEmptyStateAction + } + ]; + } + ]); + +
+ */ +angular.module('patternfly.views').component('pfCardView', { + bindings: { + config: '=?', + pageConfig: '=?', + emptyStateConfig: '=?', + emptyStateActionButtons: '=?', + items: '=', + eventId: '@id' + }, + transclude: true, + templateUrl: 'views/cardview/card-view.html', + controller: function () { + 'use strict'; + var ctrl = this; + var prevPageConfig, prevItems; + + ctrl.defaultConfig = { + selectItems: false, + multiSelect: false, + dblClick: false, + selectionMatchProp: 'uuid', + selectedItems: [], + checkDisabled: false, + showSelectBox: true, + onSelect: null, + onSelectionChange: null, + onCheckBoxChange: null, + onClick: null, + onDblClick: null, + itemsAvailable: true + }; + + ctrl.itemClick = function (e, item) { + var alreadySelected; + var selectionChanged = false; + var continueEvent = true; + + // Ignore disabled item clicks completely + if (ctrl.checkDisabled(item)) { + return continueEvent; + } + + if (ctrl.config && ctrl.config.selectItems && item) { + if (ctrl.config.multiSelect && !ctrl.config.dblClick) { + + alreadySelected = _.find(ctrl.config.selectedItems, function (itemObj) { + return itemObj === item; + }); + + if (alreadySelected) { + // already selected so deselect + ctrl.config.selectedItems = _.without(ctrl.config.selectedItems, item); + } else { + // add the item to the selected items + ctrl.config.selectedItems.push(item); + selectionChanged = true; + } + } else { + if (ctrl.config.selectedItems[0] === item) { + if (!ctrl.config.dblClick) { + ctrl.config.selectedItems = []; + selectionChanged = true; + } + continueEvent = false; + } else { + ctrl.config.selectedItems = [item]; + selectionChanged = true; + } + } + + if (selectionChanged && ctrl.config.onSelect) { + ctrl.config.onSelect(item, e); + } + if (selectionChanged && ctrl.config.onSelectionChange) { + ctrl.config.onSelectionChange(ctrl.config.selectedItems, e); + } + } + if (ctrl.config.onClick) { + ctrl.config.onClick(item, e); + } + + return continueEvent; + }; + + ctrl.dblClick = function (e, item) { + if (ctrl.config.onDblClick) { + ctrl.config.onDblClick(item, e); + } + }; + + ctrl.checkBoxChange = function (item) { + if (ctrl.config.onCheckBoxChange) { + ctrl.config.onCheckBoxChange(item); + } + }; + + ctrl.isSelected = function (item) { + var matchProp = ctrl.config.selectionMatchProp; + var selected = false; + + if (ctrl.config.showSelectBox) { + selected = item.selected; + } else { + if (ctrl.config.selectedItems.length) { + return _.find(ctrl.config.selectedItems, function (itemObj) { + return itemObj[matchProp] === item[matchProp]; + }); + } + } + return selected; + }; + + ctrl.checkDisabled = function (item) { + return ctrl.config.checkDisabled && ctrl.config.checkDisabled(item); + }; + + function setPagination () { + if (angular.isUndefined(ctrl.pageConfig)) { + ctrl.pageConfig = { + pageNumber: 1, + pageSize: ctrl.items.length, + numTotalItems: ctrl.items.length, + showPaginationControls: false + }; + } else { + if (angular.isUndefined(ctrl.pageConfig.showPaginationControls)) { + ctrl.pageConfig.showPaginationControls = true; + } + if (!angular.isNumber(ctrl.pageConfig.pageNumber)) { + ctrl.pageConfig.pageNumber = 1; + } + if (!angular.isNumber(ctrl.pageConfig.pageSize)) { + ctrl.pageConfig.pageSize = 10; + } + if (!angular.isNumber(ctrl.pageConfig.numTotalItems)) { + ctrl.pageConfig.numTotalItems = ctrl.items.length; + } + // if not showing pagination, keep pageSize equal to numTotalItems + if (!ctrl.pageConfig.showPaginationControls) { + ctrl.pageConfig.pageSize = ctrl.pageConfig.numTotalItems; + } + } + prevPageConfig = angular.copy(ctrl.pageConfig); + } + + ctrl.$onInit = function () { + + _.defaults(ctrl.config, ctrl.defaultConfig); + + if (ctrl.config.selectItems && ctrl.config.showSelectBox) { + throw new Error('pfCardView - ' + + 'Illegal use of pfCardView component! ' + + 'Cannot allow both select box and click selection in the same card view.'); + } + + prevItems = angular.copy(ctrl.items); + setPagination(); + }; + + + ctrl.$doCheck = function () { + if (!angular.equals(ctrl.pageConfig, prevPageConfig)) { + setPagination(); + } + if (!angular.equals(ctrl.items, prevItems)) { + if (ctrl.items) { + ctrl.config.itemsAvailable = ctrl.items.length > 0; + } + if (angular.isDefined(ctrl.pageConfig)) { + ctrl.pageConfig.numTotalItems = ctrl.items.length; + } + prevItems = angular.copy(ctrl.items); + } + }; + } +}); +;/** + * @ngdoc directive + * @name patternfly.views.component:pfEmptyState + * @restrict E + * + * @description + * Component for rendering an empty state. + * + * @param {object} config Optional configuration object + *
    + *
  • .icon - (string) class for main icon. Ex. 'pficon pficon-add-circle-o' + *
  • .title - (string) Text for the main title + *
  • .info - (string) Text for the main informational paragraph + *
  • .helpLink - (object) Contains url specific properties and actions + *
      + *
    • .label - (string) Optional text label which appears before the urlLabel + *
    • .urlLabel - (string) Optional text for the clickable portion of the link + *
    • .url - (string) Optional text for url path + *
    • .urlAction - (function) Optional function to invoke a url action when a callback method is specified. + * When both urlAction and url are specified the component will first execute urlAction then nagivate to the url. + *
    + *
+ * @param {array} actionButtons Buttons to display under the icon, title, and informational paragraph. + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
  • .type - (String) Optional type property. Set to 'main' to be displayed as a main action button. + * If unspecified, action button will be displayed as a secondary action button. + *
+ * @example + + +
+
+ +
+
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.views').controller('ViewCtrl', ['$scope', + function ($scope) { + + $scope.eventText = ''; + + var performEmptyStateAction = function () { + $scope.eventText += 'Empty State Action Executed \r\n'; + }; + + $scope.config = { + icon: 'pficon-add-circle-o', + title: 'Empty State Title', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url: '#/api/patternfly.views.component:pfEmptyState', + urlAction: performEmptyStateAction + } + }; + + var performAction = function (action) { + $scope.eventText = action.name + " executed. \r\n" + $scope.eventText; + }; + + $scope.actionButtons = [ + { + name: 'Main Action', + title: 'Perform an action', + actionFn: performAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Secondary Action 2', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Secondary Action 3', + title: 'Perform an action', + actionFn: performAction + } + ]; + } + ]); + +
+*/ +angular.module('patternfly.views').component('pfEmptyState', { + bindings: { + config: '
+ * If using expanding rows, use a list-expanded-content element containing expandable content for each row. Item data can be accessed inside list-expanded-content by using $parent.item.property. For each item in the items array, the expansion can be disabled by setting disableRowExpansion to true on the item. + * Setting compoundExpanion requires the applicatiot to set/unset the items' isExpanded field and to handle the contents in the list-expanded-content element based on what is expanded. + * + * @param {array} items Array of items to display in the list view. If an item in the array has a 'rowClass' field, the value of this field will be used as a class specified on the row (list-group-item). + * @param {object} config Configuration settings for the list view: + *
    + *
  • .showSelectBox - (boolean) Show item selection boxes for each item, default is true + *
  • .selectItems - (boolean) Allow row selection, default is false + *
  • .dlbClick - (boolean) Handle double clicking (item remains selected on a double click). Default is false. + *
  • .dragEnabled - (boolean) Enable drag and drop. Default is false. + *
  • .dragEnd - ( function() ) Function to call when the drag operation ended, default is none + *
  • .dragMoved - ( function() ) Function to call when the drag operation moved an element, default is none + *
  • .dragStart - ( function(item) ) Function to call when the drag operation started, default is none + *
  • .multiSelect - (boolean) Allow multiple row selections, selectItems must also be set, not applicable when dblClick is true. Default is false + *
  • .useExpandingRows - (boolean) Allow row expansion for each list item. + *
  • .compoundExpansionOnly - (boolean) Use compound row expansion only. Hides the row expander and pointer cursor on the row while allowing the row to expand via transcluded items functionality, only valid if useExpandRows is true. + *
  • .selectionMatchProp - (string) Property of the items to use for determining matching, default is 'uuid' + *
  • .selectedItems - (array) Current set of selected items + *
  • .itemsAvailable - (boolean) If 'false', displays the {@link patternfly.views.component:pfEmptyState Empty State} component. + *
  • .checkDisabled - ( function(item) ) Function to call to determine if an item is disabled, default is none + *
  • .onCheckBoxChange - ( function(item) ) Called to notify when a checkbox selection changes, default is none + *
  • .onSelect - ( function(item, event) ) Called to notify of item selection, default is none + *
  • .onSelectionChange - ( function(items) ) Called to notify when item selections change, default is none + *
  • .onClick - ( function(item, event) ) Called to notify when an item is clicked, default is none. Note: row expansion is the default behavior after onClick performed, but user can stop such default behavior by adding the sentence "return false;" to the end of onClick function body + *
  • .onDblClick - ( function(item, event) ) Called to notify when an item is double clicked, default is none + *
+ * @param {object} pageConfig Optional pagination configuration object. Since all properties are optional it is ok to specify: 'pageConfig = {}' to indicate that you want to + * use pagination with the default parameters. + *
    + *
  • .pageNumber - (number) Optional Initial page number to display. Default is page 1. + *
  • .pageSize - (number) Optional Initial page size/display length to use. Ie. Number of "Items per Page". Default is 10 items per page + *
  • .pageSizeIncrements - (Array[Number]) Optional Page size increments for the 'per page' dropdown. If not specified, the default values are: [5, 10, 20, 40, 80, 100] + *
+ * @param {array} actionButtons List of action buttons in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .class - (String) Optional class to add to the action button + *
  • .include - (String) Optional include src for the button. Used for custom button layouts (icons, dropdowns, etc) + *
  • .includeClass - (String) Optional class to set on the include src div (only relevant when include is set). + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
+ * @param {function (action, item))} enableButtonForItemFn function(action, item) Used to enabled/disable an action button based on the current item + * @param {array} menuActions List of actions for dropdown menu in each row + *
    + *
  • .name - (String) The name of the action, displayed on the button + *
  • .title - (String) Optional title, used for the tooltip + *
  • .actionFn - (function(action)) Function to invoke when the action selected + *
  • .isVisible - (Boolean) set to false to hide the action + *
  • .isDisabled - (Boolean) set to true to disable the action + *
  • .isSeparator - (Boolean) set to true if this is a placeholder for a separator rather than an action + *
+ * @param {function (item))} hideMenuForItemFn function(item) Used to hide all menu actions for a particular item + * @param {function (item))} menuClassForItemFn function(item) Used to specify a class for an item's dropdown kebab + * @param {function (action, item))} updateMenuActionForItemFn function(action, item) Used to update a menu action based on the current item + * @param {object} customScope Object containing any variables/functions used by the transcluded html, access via $ctrl.customScope. + * @param {object} emptyStateConfig Optional configuration settings for the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @param {array} emptyStateActionButtons Optional buttons to display under the icon, title, and informational paragraph in the empty state component. See the {@link patternfly.views.component:pfEmptyState Empty State} component + * @example + + + + + + angular.module('patternfly.views').controller('ViewCtrl', ['$scope', + function ($scope) { + $scope.viewType = 'basic'; + + $scope.setView = function(viewType) { + $scope.viewType = viewType; + }; + } + ]); + + +
+
+ +
+
+ {{item.name}} +
+
+ {{item.address}} +
+
+
+
+ {{item.city}} +
+
+ {{item.state}} +
+
+ +
+
+ +
+
+
+
Host
+
{{$parent.item.city}}
+
Admin
+
{{$parent.item.name}}
+
Time
+
January 15, 2016 10:45:11 AM
+
Severity
+
Warning
+
Cluster
+
Cluster 1
+
+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non + proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+
+
+
+
+
+
+
+
+
+ +
+ + + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ + + angular.module('patternfly.views').controller('BasicCtrl', ['$scope', '$templateCache', + function ($scope, $templateCache) { + $scope.eventText = ''; + var handleSelect = function (item, e) { + $scope.eventText = item.name + ' selected\r\n' + $scope.eventText; + }; + var handleSelectionChange = function (selectedItems, e) { + $scope.eventText = selectedItems.length + ' items selected\r\n' + $scope.eventText; + }; + var handleClick = function (item, e) { + $scope.eventText = item.name + ' clicked\r\n' + $scope.eventText; + }; + var handleDblClick = function (item, e) { + $scope.eventText = item.name + ' double clicked\r\n' + $scope.eventText; + }; + var handleCheckBoxChange = function (item, selected, e) { + $scope.eventText = item.name + ' checked: ' + item.selected + '\r\n' + $scope.eventText; + }; + + var checkDisabledItem = function(item) { + return $scope.showDisabled && (item.name === "John Smith"); + }; + + var dragEnd = function() { + $scope.eventText = 'drag end\r\n' + $scope.eventText; + }; + var dragMoved = function() { + var index = -1; + + for (var i = 0; i < $scope.items.length; i++) { + if ($scope.items[i] === $scope.dragItem) { + index = i; + break; + } + } + if (index >= 0) { + $scope.items.splice(index, 1); + } + $scope.eventText = 'drag moved\r\n' + $scope.eventText; + }; + var dragStart = function(item) { + $scope.dragItem = item; + $scope.eventText = item.name + ': drag start\r\n' + $scope.eventText; + }; + + $scope.enableButtonForItemFn = function(action, item) { + return !((action.name ==='Action 2') && (item.name === "Frank Livingston")) && + !(action.name === 'Start' && item.started); + }; + + $scope.updateMenuActionForItemFn = function(action, item) { + if (action.name === 'Another Action') { + action.isVisible = (item.name !== "John Smith"); + } + }; + + $scope.exampleChartConfig = { + 'chartId': 'pctChart', + 'units': 'GB', + 'thresholds': { + 'warning':'60', + 'error':'90' + } + }; + + $scope.selectType = 'checkbox'; + $scope.updateSelectionType = function() { + if ($scope.selectType === 'checkbox') { + $scope.config.selectItems = false; + $scope.config.showSelectBox = true; + } else if ($scope.selectType === 'row') { + $scope.config.selectItems = true; + $scope.config.showSelectBox = false; + } else { + $scope.config.selectItems = false; + $scope.config.showSelectBox = false; + } + }; + + $scope.showDisabled = false; + + $scope.config = { + selectItems: false, + multiSelect: false, + dblClick: false, + dragEnabled: false, + dragEnd: dragEnd, + dragMoved: dragMoved, + dragStart: dragStart, + selectionMatchProp: 'name', + selectedItems: [], + itemsAvailable: true, + checkDisabled: checkDisabledItem, + showSelectBox: true, + useExpandingRows: false, + onSelect: handleSelect, + onSelectionChange: handleSelectionChange, + onCheckBoxChange: handleCheckBoxChange, + onClick: handleClick, + onDblClick: handleDblClick + }; + + var performEmptyStateAction = function (action) { + $scope.eventText = action.name + "\r\n" + $scope.eventText; + }; + + $scope.emptyStateConfig = { + icon: 'pficon-warning-triangle-o', + title: 'No Items Available', + info: "This is the Empty State component. The goal of a empty state pattern is to provide a good first impression that helps users to achieve their goals. It should be used when a view is empty because no objects exists and you want to guide the user to perform specific actions.", + helpLink: { + label: 'For more information please see', + urlLabel: 'pfExample', + url : '#/api/patternfly.views.component:pfEmptyState' + } + }; + + $scope.emptyStateActionButtons = [ + { + name: 'Main Action', + title: 'Perform an action', + actionFn: performEmptyStateAction, + type: 'main' + }, + { + name: 'Secondary Action 1', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 2', + title: 'Perform an action', + actionFn: performEmptyStateAction + }, + { + name: 'Secondary Action 3', + title: 'Perform an action', + actionFn: performEmptyStateAction + } + ]; + + $scope.items = [ + { + name: "Fred Flintstone", + address: "20 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + name: "John Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia", + disableRowExpansion: true + }, + { + name: "Frank Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + name: "Linda McGovern", + address: "22 Oak Street", + city: "Denver", + state: "Colorado" + }, + { + name: "Jim Brown", + address: "72 Bourbon Way", + city: "Nashville", + state: "Tennessee" + }, + { + name: "Holly Nichols", + address: "21 Jump Street", + city: "Hollywood", + state: "California" + }, + { + name: "Marie Edwards", + address: "17 Cross Street", + city: "Boston", + state: "Massachusetts" + }, + { + name: "Pat Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + } + ]; + + $scope.getMenuClass = function (item) { + var menuClass = ""; + if (item.name === "Jim Brown") { + menuClass = 'red'; + } + return menuClass; + }; + + $scope.hideMenuActions = function (item) { + return (item.name === "Marie Edwards"); + }; + + var performAction = function (action, item) { + $scope.eventText = item.name + " : " + action.name + "\r\n" + $scope.eventText; + }; + + var startServer = function (action, item) { + $scope.eventText = item.name + " : " + action.name + "\r\n" + $scope.eventText; + item.started = true; + }; + + var buttonInclude = '{{actionButton.name}}'; + $templateCache.put('my-button-template', buttonInclude); + + var startButtonInclude = '{{item.started ? "Starting" : "Start"}}'; + $templateCache.put('start-button-template', startButtonInclude); + + $scope.actionButtons = [ + { + name: 'Start', + class: 'btn-primary', + include: 'start-button-template', + title: 'Start the server', + actionFn: startServer + }, + { + name: 'Action 1', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Action 2', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Action 3', + include: 'my-button-template', + title: 'Do something special', + actionFn: performAction + } + ]; + $scope.menuActions = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + title: 'Do something similar', + actionFn: performAction + } + ]; + } + ]); + + +
+
+ +
+ +
+
+
+
+ {{item.name}} +
+
+ The following snippet of text is rendered as link text. +
+
+
+
+
+ + + {{item.hostCount}} Hosts +
+
+
+
+ + + {{item.clusterCount}} Clusters +
+
+
+
+ + + {{item.nodeCount}} Nodes +
+
+
+
+ + + {{item.imageCount}} Images +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+
+ +
+
+
+ + angular.module('patternfly.views').controller('CompoundExanspansionCtrl', ['$scope', '$templateCache', + function ($scope, $templateCache) { + $scope.eventText = ''; + var handleCheckBoxChange = function (item, selected, e) { + $scope.eventText = item.name + ' checked: ' + item.selected + '\r\n' + $scope.eventText; + }; + + $scope.enableButtonForItemFn = function(action, item) { + return !((action.name ==='Action 2') && (item.name === "Frank Livingston")) && + !(action.name === 'Start' && item.started); + }; + + $scope.updateMenuActionForItemFn = function(action, item) { + if (action.name === 'Another Action') { + action.isVisible = (item.name !== "John Smith"); + } + }; + + $scope.customScope = { + toggleExpandItemField: function(item, field) { + if (item.isExpanded && item.expandField === field) { + item.isExpanded = false; + } else { + item.isExpanded = true; + item.expandField = field; + } + }, + collapseItem: function(item) { + item.isExpanded = false; + }, + isItemExpanded: function(item, field) { + return item.isExpanded && item.expandField === field; + } + }; + + $scope.selectType = 'checkbox'; + $scope.showDisabled = false; + + $scope.config = { + selectionMatchProp: 'name', + selectedItems: [], + itemsAvailable: true, + showSelectBox: true, + useExpandingRows: true, + compoundExpansionOnly: true, + onCheckBoxChange: handleCheckBoxChange + }; + + $scope.items = [ + { + name: "Event One", + typeIcon: "fa fa-plane ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + }, + { + name: "Event Two", + typeIcon: "fa fa-magic ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + }, + { + name: "Event Three", + typeIcon: "fa fa-gamepad ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + }, + { + name: "Event Four", + typeIcon: "fa fa-linux ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + }, + { + name: "Event Five", + typeIcon: "fa fa-briefcase ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + }, + { + name: "Event Six", + typeIcon: "fa fa-coffee ", + hostCount: 8, + clusterCount: 6, + nodeCount: 10, + imageCount: 8 + } + ]; + + $scope.getMenuClass = function (item) { + var menuClass = ""; + if (item.name === "Jim Brown") { + menuClass = 'red'; + } + return menuClass; + }; + + $scope.hideMenuActions = function (item) { + return (item.name === "Marie Edwards"); + }; + + var performAction = function (action, item) { + $scope.eventText = item.name + " : " + action.name + "\r\n" + $scope.eventText; + }; + + var startServer = function (action, item) { + $scope.eventText = item.name + " : " + action.name + "\r\n" + $scope.eventText; + item.started = true; + }; + + var buttonInclude = '{{actionButton.name}}'; + $templateCache.put('my-button-template', buttonInclude); + + var startButtonInclude = '{{item.started ? "Starting" : "Start"}}'; + $templateCache.put('start-button-template', startButtonInclude); + + $scope.actionButtons = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + } + ]; + $scope.menuActions = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + title: 'Do something similar', + actionFn: performAction + } + ]; + } + ]); + + + angular.module('patternfly.views').component('itemExpansion', { + bindings: { + item: '<' + }, + templateUrl: 'itemExpansion.html', + controller: function () { + 'use strict'; + var ctrl = this; + } + }); + + +
+
+
+
+
+ + +
+
+ +
+
+ {{item.name}} +
+
+ {{item.address}} +
+
+
+
+ {{item.city}} +
+
+ {{item.state}} +
+
+
+
+
+
+ + + angular.module('patternfly.views').controller('PaginationCtrl', ['$scope', '$templateCache', + function ($scope, $templateCache) { + + $scope.pageConfig = { + pageSize: 5 + }; + + var startServer = function (action, item) { + console.log(item.name + " : " + action.name); + }; + + var performAction = function (action, item) { + console.log(item.name + " : " + action.name); + }; + + $scope.items = [ + { + name: "Fred Flintstone", + address: "20 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + name: "John Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia" + }, + { + name: "Frank Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + name: "Linda McGovern", + address: "22 Oak Street", + city: "Denver", + state: "Colorado" + }, + { + name: "Jim Brown", + address: "72 Bourbon Way", + city: "Nashville", + state: "Tennessee" + }, + { + name: "Holly Nichols", + address: "21 Jump Street", + city: "Hollywood", + state: "California" + }, + { + name: "Marie Edwards", + address: "17 Cross Street", + city: "Boston", + state: "Massachusetts" + }, + { + name: "Pat Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + }, + { + name: "Betty Rubble", + address: "30 Dinosaur Way", + city: "Bedrock", + state: "Washingstone" + }, + { + name: "Martha Smith", + address: "415 East Main Street", + city: "Norfolk", + state: "Virginia" + }, + { + name: "Liz Livingston", + address: "234 Elm Street", + city: "Pittsburgh", + state: "Pennsylvania" + }, + { + name: "Howard McGovern", + address: "22 Oak Street", + city: "Denver", + state: "Colorado" + }, + { + name: "Joyce Brown", + address: "72 Bourbon Way", + city: "Nashville", + state: "Tennessee" + }, + { + name: "Mike Nichols", + address: "21 Jump Street", + city: "Hollywood", + state: "California" + }, + { + name: "Mark Edwards", + address: "17 Cross Street", + city: "Boston", + state: "Massachusetts" + }, + { + name: "Chris Thomas", + address: "50 Second Street", + city: "New York", + state: "New York" + } + ]; + + $scope.actionButtons = [ + { + name: 'Start', + class: 'btn-primary', + include: 'start-button-template', + title: 'Start the server', + actionFn: startServer + }, + { + name: 'Action 1', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Action 2', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Action 3', + include: 'my-button-template', + title: 'Do something special', + actionFn: performAction + } + ]; + $scope.menuActions = [ + { + name: 'Action', + title: 'Perform an action', + actionFn: performAction + }, + { + name: 'Another Action', + title: 'Do something else', + actionFn: performAction + }, + { + name: 'Disabled Action', + title: 'Unavailable action', + actionFn: performAction, + isDisabled: true + }, + { + name: 'Something Else', + title: '', + actionFn: performAction + }, + { + isSeparator: true + }, + { + name: 'Grouped Action 1', + title: 'Do something', + actionFn: performAction + }, + { + name: 'Grouped Action 2', + title: 'Do something similar', + actionFn: performAction + } + ]; + } + ]); + +
+ */ +;angular.module('patternfly.views').component('pfListView', { + bindings: { + config: '=?', + pageConfig: '=?', + items: '=', + actionButtons: '=?', + enableButtonForItemFn: '=?', + menuActions: '=?', + hideMenuForItemFn: '=?', + menuClassForItemFn: '=?', + updateMenuActionForItemFn: '=?', + actions: '=?', + updateActionForItemFn: '=?', + customScope: '=?', + emptyStateConfig: '=?', + emptyStateActionButtons: '=?' + }, + transclude: { + expandedContent: '?listExpandedContent' + }, + templateUrl: 'views/listview/list-view.html', + controller: ["$window", "$element", "$timeout", function ($window, $element, $timeout) { + 'use strict'; + var ctrl = this; + var prevPageConfig, prevItems; + + var setDropMenuLocation = function (parentDiv) { + var dropButton = parentDiv.querySelector('.dropdown-toggle'); + var dropMenu = parentDiv.querySelector('.dropdown-menu'); + + var parentRect = $element[0].querySelector('.list-view-pf.list-view-pf-view').getBoundingClientRect(); + var buttonRect = dropButton.getBoundingClientRect(); + var menuRect = dropMenu.getBoundingClientRect(); + + var buttonTop = buttonRect.top - parentRect.top; + var buttonBottom = buttonTop + buttonRect.height; + var menuTop = buttonTop - menuRect.height; + var menuBottom = buttonBottom + menuRect.height; + + if ((menuBottom <= parentRect.height) || (menuTop < 0)) { + ctrl.dropdownClass = 'dropdown'; + } else { + ctrl.dropdownClass = 'dropup'; + } + + // OK to display the menu now + ctrl.kebabMenuReady = true; + }; + + ctrl.defaultConfig = { + selectItems: false, + multiSelect: false, + dblClick: false, + dragEnabled: false, + dragEnd: null, + dragMoved: null, + dragStart: null, + selectionMatchProp: 'uuid', + selectedItems: [], + checkDisabled: false, + useExpandingRows: false, + showSelectBox: true, + onSelect: null, + onSelectionChange: null, + onCheckBoxChange: null, + onClick: null, + onDblClick: null, + itemsAvailable: true + }; + + + ctrl.dropdownClass = 'dropdown'; + + ctrl.handleButtonAction = function (action, item) { + if (!ctrl.checkDisabled(item) && action && action.actionFn && ctrl.enableButtonForItem(action, item)) { + action.actionFn(action, item); + } + }; + + ctrl.handleMenuAction = function (action, item) { + if (!ctrl.checkDisabled(item) && action && action.actionFn && (action.isDisabled !== true)) { + action.actionFn(action, item); + } + }; + + ctrl.enableButtonForItem = function (action, item) { + var enable = true; + if (typeof ctrl.enableButtonForItemFn === 'function') { + return ctrl.enableButtonForItemFn(action, item); + } + return enable; + }; + + ctrl.updateActions = function (item) { + if (typeof ctrl.updateMenuActionForItemFn === 'function') { + ctrl.menuActions.forEach(function (action) { + ctrl.updateMenuActionForItemFn(action, item); + }); + } + }; + + ctrl.getMenuClassForItem = function (item) { + var menuClass = ''; + if (angular.isFunction(ctrl.menuClassForItemFn)) { + menuClass = ctrl.menuClassForItemFn(item); + } + + return menuClass; + }; + + ctrl.hideMenuForItem = function (item) { + var hideMenu = false; + if (angular.isFunction(ctrl.hideMenuForItemFn)) { + hideMenu = ctrl.hideMenuForItemFn(item); + } + + return hideMenu; + }; + + ctrl.toggleItemExpansion = function (item) { + item.isExpanded = !item.isExpanded; + }; + + ctrl.setupActions = function (item, event) { + // Ignore disabled items completely + if (ctrl.checkDisabled(item)) { + return; + } + + // update the actions based on the current item + ctrl.updateActions(item); + + // Hide the kebab until we determine dropup or dropdown to avoid flicker + ctrl.kebabMenuReady = false; + + $timeout(function() { + var parentDiv = undefined; + var nextElement; + + nextElement = event.target; + while (nextElement && !parentDiv) { + if (nextElement.className.indexOf('dropdown-kebab-pf') !== -1) { + parentDiv = nextElement; + if (nextElement.className.indexOf('open') !== -1) { + setDropMenuLocation(parentDiv); + } + } + nextElement = nextElement.parentElement; + } + }); + }; + + ctrl.itemClick = function (e, item) { + var alreadySelected; + var selectionChanged = false; + var continueEvent = true; + var enableRowExpansion = ctrl.config && ctrl.config.useExpandingRows && !ctrl.config.compoundExpansionOnly && item && !item.disableRowExpansion; + + // Ignore disabled item clicks completely + if (ctrl.checkDisabled(item)) { + return continueEvent; + } + + if (ctrl.config && ctrl.config.selectItems && item) { + if (ctrl.config.multiSelect && !ctrl.config.dblClick) { + + alreadySelected = _.find(ctrl.config.selectedItems, function (itemObj) { + return itemObj === item; + }); + + if (alreadySelected) { + // already selected so deselect + ctrl.config.selectedItems = _.without(ctrl.config.selectedItems, item); + } else { + // add the item to the selected items + ctrl.config.selectedItems.push(item); + selectionChanged = true; + } + } else { + if (ctrl.config.selectedItems[0] === item) { + if (!ctrl.config.dblClick) { + ctrl.config.selectedItems = []; + selectionChanged = true; + } + continueEvent = false; + } else { + ctrl.config.selectedItems = [item]; + selectionChanged = true; + } + } + + if (selectionChanged && ctrl.config.onSelect) { + ctrl.config.onSelect(item, e); + } + if (selectionChanged && ctrl.config.onSelectionChange) { + ctrl.config.onSelectionChange(ctrl.config.selectedItems, e); + } + } + if (ctrl.config.onClick) { + if (ctrl.config.onClick(item, e) !== false && enableRowExpansion) { + ctrl.toggleItemExpansion(item); + } + } else if (enableRowExpansion) { + ctrl.toggleItemExpansion(item); + } + + return continueEvent; + }; + + ctrl.dblClick = function (e, item) { + // Ignore disabled item clicks completely + if (ctrl.checkDisabled(item)) { + return continueEvent; + } + + if (ctrl.config.onDblClick) { + ctrl.config.onDblClick(item, e); + } + }; + + ctrl.checkBoxChange = function (item) { + if (ctrl.config.onCheckBoxChange) { + ctrl.config.onCheckBoxChange(item); + } + }; + + ctrl.isSelected = function (item) { + var matchProp = ctrl.config.selectionMatchProp; + var selected = false; + + if (ctrl.config.showSelectBox) { + selected = item.selected; + } else if (ctrl.config.selectItems && ctrl.config.selectedItems.length) { + selected = _.find(ctrl.config.selectedItems, function (itemObj) { + return itemObj[matchProp] === item[matchProp]; + }); + } + return selected; + }; + + ctrl.checkDisabled = function (item) { + return ctrl.config.checkDisabled && ctrl.config.checkDisabled(item); + }; + + function setPagination () { + if (angular.isUndefined(ctrl.pageConfig)) { + ctrl.pageConfig = { + pageNumber: 1, + pageSize: ctrl.items.length, + numTotalItems: ctrl.items.length, + showPaginationControls: false + }; + } else { + if (angular.isUndefined(ctrl.pageConfig.showPaginationControls)) { + ctrl.pageConfig.showPaginationControls = true; + } + if (!angular.isNumber(ctrl.pageConfig.pageNumber)) { + ctrl.pageConfig.pageNumber = 1; + } + if (!angular.isNumber(ctrl.pageConfig.pageSize)) { + ctrl.pageConfig.pageSize = 10; + } + if (!angular.isNumber(ctrl.pageConfig.numTotalItems)) { + ctrl.pageConfig.numTotalItems = ctrl.items.length; + } + // if not showing pagination, keep pageSize equal to numTotalItems + if (!ctrl.pageConfig.showPaginationControls) { + ctrl.pageConfig.pageSize = ctrl.pageConfig.numTotalItems; + } + } + prevPageConfig = angular.copy(ctrl.pageConfig); + } + + ctrl.$onInit = function () { + if (angular.isUndefined(ctrl.config)) { + ctrl.config = {}; + } + + _.defaults(ctrl.config, ctrl.defaultConfig); + + if (!ctrl.config.selectItems) { + ctrl.config.selectedItems = []; + } + if (!ctrl.config.multiSelect && ctrl.config.selectedItems && ctrl.config.selectedItems.length > 0) { + ctrl.config.selectedItems = [ctrl.config.selectedItems[0]]; + } + if (ctrl.config.selectItems && ctrl.config.showSelectBox) { + throw new Error('pfListView - ' + + 'Illegal use of pListView component! ' + + 'Cannot allow both select box and click selection in the same list view.'); + } + prevItems = angular.copy(ctrl.items); + setPagination(); + }; + + ctrl.$doCheck = function () { + if (!angular.equals(ctrl.pageConfig, prevPageConfig)) { + setPagination(); + } + if (!angular.equals(ctrl.items, prevItems)) { + if (ctrl.items) { + ctrl.config.itemsAvailable = ctrl.items.length > 0; + } + if (angular.isDefined(ctrl.pageConfig)) { + ctrl.pageConfig.numTotalItems = ctrl.items.length; + } + prevItems = angular.copy(ctrl.items); + } + }; + + ctrl.dragEnd = function () { + if (angular.isFunction(ctrl.config.dragEnd)) { + ctrl.config.dragEnd(); + } + }; + + ctrl.dragMoved = function () { + if (angular.isFunction(ctrl.config.dragMoved)) { + ctrl.config.dragMoved(); + } + }; + + ctrl.isDragOriginal = function (item) { + return (item === ctrl.dragItem); + }; + + ctrl.dragStart = function (item) { + ctrl.dragItem = item; + + if (angular.isFunction(ctrl.config.dragStart)) { + ctrl.config.dragStart(item); + } + }; + }] +}); +;(function () { + 'use strict'; + + /** + * @ngdoc object + * @name patternfly.views.pfViewUtils + * @description + * A utility constant to return view objects used for Dashboard, Card, Table, List, and Topology view switcher types. + * @example + *
+   * $scope.viewsConfig = {
+   *   views: [pfViewUtils.getListView(), pfViewUtils.getCardView(), pfViewUtils.getTableView()]
+   * };
+   * 
+ * Each getXxxxView() returns an object: + *
    + *
  • .id - (String) Unique id for the view, used for comparisons + *
  • .title - (String) Optional title, uses as a tooltip for the view selector + *
  • .iconClass - (String) Icon class to use for the view selector + *
+ * Please see {@link patternfly.toolbars.component:pfToolbar pfToolbar} for use in Toolbar View Switcher + */ + angular.module('patternfly.views').constant('pfViewUtils', { + getDashboardView: function (title) { + return { + id: 'dashboardView', + title: title || 'Dashboard View', + iconClass: 'fa fa-dashboard' + }; + }, + getCardView: function (title) { + return { + id: 'cardView', + title: title || 'Card View', + iconClass: 'fa fa-th' + }; + }, + getListView: function (title) { + return { + id: 'listView', + title: title || 'List View', + iconClass: 'fa fa-th-list' + }; + }, + getTableView: function (title) { + return { + id: 'tableView', + title: title || 'Table View', + iconClass: 'fa fa-table' + }; + }, + getTopologyView: function (title) { + return { + id: 'topologyView', + title: title || 'Topology View', + iconClass: 'fa fa-sitemap' + }; + } + }); +})(); +;/** + * @ngdoc directive + * @name patternfly.wizard.component:pfWizard + * @restrict E + * + * @description + * Component for rendering a Wizard modal. Each wizard dynamically creates the step navigation both in the header and the left-hand side based on nested steps. + * Use pf-wizard-step to define individual steps within a wizard and pf-wizard-substep to define portions of pf-wizard-steps if so desired. For instance, Step one can have two substeps - 1A and 1B when it is logical to group those together. + *

+ * The basic structure should be: + *
+  * 
+  *   
+  *     
+  *     
+  *   
+  *   
+  *   
+  * 
+  * 
+ * + * @param {string} wizardTitle The wizard title displayed in the header + * @param {boolean=} hideIndicators Hides the step indicators in the header of the wizard + * @param {boolean=} activeStepTitleOnly Shows the title only for the active step in the step indicators, optional, default is false. + * @param {boolean=} hideSidebar Hides page navigation sidebar on the wizard pages + * @param {boolean=} hideHeader Optional value to hide the title bar. Default is false. + * @param {boolean=} hideBackButton Optional value to hide the back button, useful in 2 step wizards. Default is false. + * @param {string=} stepClass Optional CSS class to be given to the steps page container. Used for the sidebar panel as well unless a sidebarClass is provided. + * @param {string=} sidebarClass Optional CSS class to be give to the sidebar panel. Only used if the stepClass is also provided. + * @param {string=} contentHeight The height the wizard content should be set to. This is used ONLY if the stepClass is not given. This defaults to 300px if the property is not supplied. + * @param {boolean=} hideIndicators Hides the step indicators in the header of the wizard + * @param {string=} currentStep The current step can be changed externally - this is the title of the step to switch the wizard to + * @param {string=} cancelTitle The text to display on the cancel button + * @param {string=} backTitle The text to display on the back button + * @param {string=} nextTitle The text to display on the next button + * @param {function(step)=} backCallback Called to notify when the back button is clicked + * @param {function(step)=} nextCallback Called to notify when the next button is clicked + * @param {function()=} onFinish Called to notify when when the wizard is complete. Returns a boolean value to indicate if the finish operation is complete + * @param {function()=} onCancel Called when the wizard is canceled, returns a boolean value to indicate if cancel is successful + * @param {boolean} wizardReady Value that is set when the wizard is ready + * @param {boolean=} wizardDone Value that is set when the wizard is done + * @param {string} loadingWizardTitle The text displayed when the wizard is loading + * @param {string=} loadingSecondaryInformation Secondary descriptive information to display when the wizard is loading + * @param {boolean=} embedInPage Value that indicates wizard is embedded in a page (not a modal). This moves the navigation buttons to the left hand side of the footer and removes the close button. + * @param {function(step, index)=} onStepChanged Called when the wizard step is changed, passes in the step and the step index of the step changed to + * @deprecated {string} title The wizard title displayed in the head (use wizardTitle instead) + * @example + + +
+ +
+
+ + + +
+
+ +
+ + + + + + +
+
+
+
+
+ +
+
+
+
+
+ +
+ +
+ + + + + + +
+
+
+
+ +
+
+
+ Name: + {{data.name}} +
+
+ Description: + {{data.description}} +
+
+
+
+ +
+
+
+ Lorem: + {{data.lorem}} +
+
+ Ipsum: + {{data.ipsum}} +
+
+
+
+ +
+ +
+

Wizards should make use of substeps consistently throughout (either using them or not using them). This is an example only.

+ + + + + + +
+
+
+
+ +
+ + + +
+
+ +
+ +
+
+
+

Deployment in progress

+

Lorem ipsum dolor sit amet, porta at suspendisse ac, ut wisi vivamus, lorem sociosqu eget nunc amet.

+
+
+
+

Deployment was successful

+

Lorem ipsum dolor sit amet, porta at suspendisse ac, ut wisi vivamus, lorem sociosqu eget nunc amet.

+ +
+
+
+
+
+ + angular.module('patternfly.wizard').controller('WizardModalController', ['$scope', '$timeout', '$uibModal', '$rootScope', + function ($scope, $timeout, $uibModal, $rootScope) { + $scope.openWizardModel = function () { + var wizardDoneListener, + modalInstance = $uibModal.open({ + animation: true, + backdrop: 'static', + templateUrl: 'wizard-container.html', + controller: 'WizardController', + size: 'lg' + }); + + var closeWizard = function (e, reason) { + modalInstance.dismiss(reason); + wizardDoneListener(); + }; + + modalInstance.result.then(function () { }, function () { }); + + wizardDoneListener = $rootScope.$on('wizard.done', closeWizard); + }; + } + ]); + angular.module('patternfly.wizard').controller('WizardController', ['$scope', '$timeout', '$rootScope', + function ($scope, $timeout, $rootScope) { + + + var initializeWizard = function () { + $scope.data = { + name: '', + description: '', + lorem: 'default setting', + ipsum: '' + }; + $scope.secondaryLoadInformation = 'ipsum dolor sit amet, porta at suspendisse ac, ut wisi vivamus, lorem sociosqu eget nunc amet.'; + + $scope.wizardReady = false; + $timeout(function () { + $scope.wizardReady = true; + }, 1000); + + $scope.nextButtonTitle = "Next >"; + }; + + var startDeploy = function () { + $timeout(function() { }, 10000); + $scope.deployInProgress = true; + }; + + $scope.data = {}; + + $scope.firstStepNextTooltip = "First step next"; + $scope.firstStepPrevTooltip = "First step back"; + $scope.secondStepNextTooltip = "Second step next"; + $scope.secondStepPrevTooltip = "Second step back"; + $scope.reviewStepNextTooltip = "Review step next"; + $scope.reviewStepPrevTooltip = "Review step back"; + + $scope.nextCallback = function (step) { + // call startdeploy after deploy button is clicked on review-summary tab + if (step.stepId === 'review-summary') { + startDeploy(); + } + return true; + }; + $scope.backCallback = function (step) { + return true; + }; + + $scope.stepChanged = function (step, index) { + if (step.stepId === 'review-summary') { + $scope.nextButtonTitle = "Deploy"; + } else if (step.stepId === 'review-progress') { + $scope.nextButtonTitle = "Close"; + } else { + $scope.nextButtonTitle = "Next >"; + } + }; + + $scope.cancelDeploymentWizard = function () { + $rootScope.$emit('wizard.done', 'cancel'); + }; + + $scope.finishedWizard = function () { + $rootScope.$emit('wizard.done', 'done'); + return true; + }; + + initializeWizard(); + } + ]); + + angular.module('patternfly.wizard').controller('DetailsGeneralController', ['$rootScope', '$scope', + function ($rootScope, $scope) { + 'use strict'; + + $scope.reviewTemplate = "review-template.html"; + $scope.detailsGeneralComplete = false; + $scope.focusSelectors = ['#new-name']; + $scope.onShow = function() { }; + + $scope.updateName = function() { + $scope.detailsGeneralComplete = angular.isDefined($scope.data.name) && $scope.data.name.length > 0; + }; + } + ]); + + angular.module('patternfly.wizard').controller('DetailsReviewController', ['$rootScope', '$scope', + function ($rootScope, $scope) { + 'use strict'; + + // Find the data! + var next = $scope; + while (angular.isUndefined($scope.data)) { + next = next.$parent; + if (angular.isUndefined(next)) { + $scope.data = {}; + } else { + $scope.data = next.$ctrl.wizardData; + } + } + } + ]); + + angular.module('patternfly.wizard').controller('SecondStepController', ['$rootScope', '$scope', + function ($rootScope, $scope) { + 'use strict'; + + $scope.focusSelectors = ['.invalid-classname', '#step-two-new-lorem']; + } + ]); + + angular.module('patternfly.wizard').controller('SummaryController', ['$rootScope', '$scope', '$timeout', + function ($rootScope, $scope, $timeout) { + 'use strict'; + $scope.pageShown = false; + + $scope.onShow = function () { + $scope.pageShown = true; + $timeout(function () { + $scope.pageShown = false; // done so the next time the page is shown it updates + }); + } + } + ]); + + angular.module('patternfly.wizard').controller('DeploymentController', ['$rootScope', '$scope', '$timeout', + function ($rootScope, $scope, $timeout) { + 'use strict'; + + $scope.onShow = function() { + $scope.deploymentComplete = false; + $timeout(function() { + $scope.deploymentComplete = true; + }, 2500); + }; + } + ]); + +
+*/ +;(function () { + 'use strict'; + + var findWizard = function (scope) { + var wizard; + + if (scope) { + if (angular.isDefined(scope.wizard)) { + wizard = scope.wizard; + } else { + wizard = findWizard(scope.$parent); + } + } + + return wizard; + }; + + var setupCallback = function (scope, button, fnName, callback) { + button.on("click", function (e) { + e.preventDefault(); + scope.$apply(function () { + // scope apply in button module + scope.wizard[fnName](callback); + }); + }); + }; + + angular.module('patternfly.wizard').component('pfWizNext', { + bindings: { + callback: "=?" + }, + controller: ["$element", "$scope", function ($element, $scope) { + var ctrl = this; + + ctrl.$onInit = function () { + $scope.wizard = findWizard($scope); + }; + + ctrl.$postLink = function () { + setupCallback($scope, $element, 'next', ctrl.callback); + }; + }] + }); + + angular.module('patternfly.wizard').component('pfWizPrevious', { + bindings: { + callback: "=?" + }, + controller: ["$element", "$scope", function ($element, $scope) { + var ctrl = this; + + ctrl.$onInit = function () { + $scope.wizard = findWizard($scope); + }; + + ctrl.$postLink = function () { + setupCallback($scope, $element, 'previous', ctrl.callback); + }; + }] + }); + + angular.module('patternfly.wizard').component('pfWizFinish', { + bindings: { + callback: "=?" + }, + controller: ["$element", "$scope", function ($element, $scope) { + var ctrl = this; + + ctrl.$onInit = function () { + $scope.wizard = findWizard($scope); + }; + + ctrl.$postLink = function () { + setupCallback($scope, $element, 'finish', ctrl.callback); + }; + }] + }); + + angular.module('patternfly.wizard').component('pfWizCancel', { + bindings: { + callback: "=?" + }, + controller: ["$element", "$scope", function ($element, $scope) { + var ctrl = this; + + ctrl.$onInit = function () { + $scope.wizard = findWizard($scope); + }; + + ctrl.$postLink = function () { + setupCallback($scope, $element, 'cancel', ctrl.callback); + }; + }] + }); + + angular.module('patternfly.wizard').component('pfWizReset', { + bindings: { + callback: "=?" + }, + controller: ["$element", "$scope", function ($element, $scope) { + var ctrl = this; + + ctrl.$onInit = function () { + $scope.wizard = findWizard($scope); + }; + + ctrl.$postLink = function () { + setupCallback($scope, $element, 'reset', ctrl.callback); + }; + }] + }); +})(); +;/** + * @ngdoc directive + * @name patternfly.wizard.component:pfWizardReviewPage + * @restrict E + * + * @description + * Component for rendering a Wizard Review Page - should only be used within a wizard. + * + * @param {boolean} shown Value watched internally by the wizard review page to know when it is visible. + * @param {object} wizardData Sets the internal content of the review page to apply wizard data to the review templates. + * + */ +angular.module('patternfly.wizard').component('pfWizardReviewPage', { + bindings: { + shown: '<', + wizardData: "<" + }, + templateUrl: 'wizard/wizard-review-page.html', + controller: ["$scope", function ($scope) { + 'use strict'; + var ctrl = this; + + var findWizard = function (scope) { + var wizard; + if (scope) { + if (angular.isDefined(scope.wizard)) { + wizard = scope.wizard; + } else { + wizard = findWizard(scope.$parent); + } + } + + return wizard; + }; + + ctrl.$onInit = function () { + ctrl.reviewSteps = []; + ctrl.wizard = findWizard($scope.$parent); + }; + + ctrl.$onChanges = function (changesObj) { + if (changesObj.shown) { + if (changesObj.shown.currentValue) { + ctrl.updateReviewSteps(); + } + } + }; + + ctrl.toggleShowReviewDetails = function (step) { + if (step.showReviewDetails === true) { + step.showReviewDetails = false; + } else { + step.showReviewDetails = true; + } + }; + + ctrl.getSubStepNumber = function (step, substep) { + return step.getStepDisplayNumber(substep); + }; + + ctrl.getReviewSubSteps = function (reviewStep) { + return reviewStep.getReviewSteps(); + }; + + ctrl.updateReviewSteps = function () { + ctrl.reviewSteps = ctrl.wizard.getReviewSteps(); + }; + }] +}); +;/** + * @ngdoc directive + * @name patternfly.wizard.component:pfWizardStep + * @restrict E + * + * @description + * Component for rendering a Wizard step. Each step can stand alone or have substeps. This directive can only be used as a child of pf-wizard. + * + * @param {string} stepTitle The step title displayed in the header and used for the review screen when displayed + * @param {string} stepId Sets the text identifier of the step + * @param {number} stepPriority This sets the priority of this wizard step relative to other wizard steps. They should be numbered sequentially in the order they should be viewed. + * @param {boolean} substeps Sets whether this step has substeps + * @param {boolean=} nextEnabled Sets whether the next button should be enabled when this step is first displayed + * @param {boolean=} prevEnabled Sets whether the back button should be enabled when this step is first displayed + * @param {string=} nextTooltip The text to display as a tooltip on the next button + * @param {string=} prevTooltip The text to display as a tooltip on the back button + * @param {boolean=} wzDisabled Hides the step when set to True + * @param {boolean} okToNavAway Sets whether or not it's ok for the user to leave this page + * @param {boolean} allowClickNav Sets whether the user can click on the numeric step indicators to navigate directly to this step + * @param {string=} description The step description (optional) + * @param {object} wizardData Data passed to the step that is shared by the entire wizard + * @param {function()=} onShow The function called when the wizard shows this step + * @param {object=} focusSelectors Array of selectors to be used (in the order given) to find the initial focus component for the page + * @param {boolean=} showReview Indicates whether review information should be displayed for this step when the review step is reached + * @param {boolean=} showReviewDetails Indicators whether the review information should be expanded by default when the review step is reached + * @param {string=} reviewTemplate The template that should be used for the review details screen + */ +angular.module('patternfly.wizard').component('pfWizardStep', { + transclude: true, + bindings: { + stepTitle: '@', + stepId: '@', + stepPriority: '@', + substeps: '=?', + nextEnabled: ' step.stepPriority; + }); + if (insertBefore) { + ctrl.steps.splice(ctrl.steps.indexOf(insertBefore), 0, step); + } else { + ctrl.steps.push(step); + } + }; + + ctrl.currentStepTitle = function () { + return ctrl.selectedStep.stepTitle; + }; + + ctrl.currentStepDescription = function () { + return ctrl.selectedStep.description; + }; + + ctrl.currentStep = function () { + return ctrl.selectedStep; + }; + + ctrl.totalStepCount = function () { + return ctrl.getEnabledSteps().length; + }; + + // Method used for next button within step + ctrl.next = function (callback) { + var enabledSteps = ctrl.getEnabledSteps(); + + // Save the step you were on when next() was invoked + var index = stepIdx(ctrl.selectedStep); + + // Check if callback is a function + if (angular.isFunction (callback)) { + if (callback(ctrl.selectedStep)) { + if (index === enabledSteps.length - 1) { + return false; + } + // Go to the next step + ctrl.goTo(enabledSteps[index + 1]); + return true; + } + return true; + } + + // Completed property set on scope which is used to add class/remove class from progress bar + ctrl.selectedStep.completed = true; + + // Check to see if this is the last step. If it is next behaves the same as finish() + if (index === enabledSteps.length - 1) { + return false; + } + // Go to the next step + ctrl.goTo(enabledSteps[index + 1]); + return true; + }; + + ctrl.previous = function (callback) { + var index = stepIdx(ctrl.selectedStep); + var goPrev = false; + + // Check if callback is a function + if (!angular.isFunction (callback) || callback(ctrl.selectedStep)) { + if (index !== 0) { + ctrl.goTo(ctrl.getEnabledSteps()[index - 1]); + goPrev = true; + } + } + return goPrev; + }; + }] +}); +;/** @ngdoc directive +* @name patternfly.wizard.component:pfWizardSubstep +* @restrict E +* +* @description +* Component for rendering a Wizard substep. Each substep must be a child of a pf-wizardstep in a pf-wizard directive. +* +* @param {string} stepTitle The step title displayed in the header and used for the review screen when displayed +* @param {string} stepId Sets the text identifier of the step +* @param {number} stepPriority This sets the priority of this wizard step relative to other wizard steps. They should be numbered sequentially in the order they should be viewed. +* @param {boolean=} nextEnabled Sets whether the next button should be enabled when this step is first displayed +* @param {boolean=} prevEnabled Sets whether the back button should be enabled when this step is first displayed +* @param {boolean=} wzDisabled Hides the step when set to True +* @param {boolean} okToNavAway Sets whether or not it's ok for the user to leave this page +* @param {boolean=} allowClickNav Sets whether the user can click on the numeric step indicators to navigate directly to this step +* @param {string=} description The step description +* @param {object} wizardData Data passed to the step that is shared by the entire wizard +* @param {function()=} onShow The function called when the wizard shows this step +* @param {object=} focusSelectors Array of selectors to be used (in the order given) to find the initial focus component for the page +* @param {boolean=} showReviewDetails Indicators whether the review information should be expanded by default when the review step is reached +* @param {string=} reviewTemplate The template that should be used for the review details screen +*/ +angular.module('patternfly.wizard').component('pfWizardSubstep', { + transclude: true, + bindings: { + stepTitle: '@', + stepId: '@', + stepPriority: '@', + nextEnabled: '"; + } + }; + + ctrl.$onChanges = function (changesObj) { + var step; + + if (changesObj.hideHeader) { + ctrl.hideHeader = ctrl.hideHeader === 'true'; + } + + if (changesObj.hideSidebar) { + ctrl.hideSidebar = ctrl.hideSidebar === 'true'; + } + + if (changesObj.hideBackButton) { + ctrl.hideBackButton = ctrl.hideBackButton === 'true'; + } + + if (changesObj.wizardReady && changesObj.wizardReady.currentValue) { + ctrl.goTo(ctrl.getEnabledSteps()[0]); + } + + if (changesObj.currentStep) { + //checking to make sure currentStep is truthy value + step = changesObj.currentStep.currentValue; + if (!step) { + return; + } + + //setting stepTitle equal to current step title or default title + if (ctrl.selectedStep && ctrl.selectedStep.title !== step) { + ctrl.goTo(stepByTitle(step)); + } + } + }; + + ctrl.getEnabledSteps = function () { + return ctrl.steps.filter(function (step) { + return step.disabled !== 'true'; + }); + }; + + ctrl.getReviewSteps = function () { + return ctrl.steps.filter(function (step) { + return !step.disabled && + (!angular.isUndefined(step.reviewTemplate) || step.getReviewSteps().length > 0); + }); + }; + + ctrl.currentStepNumber = function () { + //retrieve current step number + return stepIdx(ctrl.selectedStep) + 1; + }; + + ctrl.getStepNumber = function (step) { + return stepIdx(step) + 1; + }; + + ctrl.goTo = function (step, resetStepNav) { + var focusElement = null; + + if (ctrl.wizardDone || (ctrl.selectedStep && !ctrl.selectedStep.okToNavAway) || step === ctrl.selectedStep) { + return; + } + + if (firstRun || (ctrl.getStepNumber(step) < ctrl.currentStepNumber() && ctrl.selectedStep.isPrevEnabled()) || ctrl.selectedStep.isNextEnabled()) { + unselectAll(); + + if (!firstRun && resetStepNav && step.substeps) { + step.resetNav(); + } + + ctrl.selectedStep = step; + step.selected = true; + + $timeout(function () { + if (angular.isFunction(step.onShow)) { + step.onShow(); + } + // Give time for onShow to do its thing (maybe update the selectors), then time to display the elements + $timeout(function () { + if (step.focusSelectors) { + _.find(step.focusSelectors, function (selector) { + return focusElement = document.querySelector(selector); + }); + } + + // Default to next button if it is enabled + if (!focusElement && step.nextEnabled) { + focusElement = document.querySelector('.wizard-pf-next'); + } + + // Use cancel button if we haven't found anything else to set focus on + if (!focusElement) { + focusElement = document.querySelector('.wizard-pf-cancel'); + } + + if (focusElement) { + focusElement.focus(); + } + }, 300); + }, 100); + + // Make sure current step is not undefined + ctrl.currentStep = step.title; + + ctrl.nextTooltip = step.nextTooltip; + ctrl.prevTooltip = step.prevTooltip; + + //emit event upwards with data on goTo() invocation + if (!step.substeps) { + ctrl.setPageSelected(step); + } + firstRun = false; + } + + if (!ctrl.selectedStep.substeps) { + ctrl.firstStep = stepIdx(ctrl.selectedStep) === 0; + } else { + ctrl.firstStep = stepIdx(ctrl.selectedStep) === 0 && ctrl.selectedStep.currentStepNumber() === 1; + } + }; + + ctrl.allowStepIndicatorClick = function (step) { + return step.allowClickNav && + !ctrl.wizardDone && + ctrl.selectedStep && + ctrl.selectedStep.okToNavAway && + (ctrl.selectedStep.nextEnabled || (step.stepPriority < ctrl.selectedStep.stepPriority)) && + (ctrl.selectedStep.prevEnabled || (step.stepPriority > ctrl.selectedStep.stepPriority)); + }; + + ctrl.stepClick = function (step) { + if (step.allowClickNav && + ctrl.selectedStep && + !ctrl.wizardDone && + ctrl.selectedStep.okToNavAway && + (ctrl.selectedStep.nextEnabled || (step.stepPriority < ctrl.selectedStep.stepPriority)) && + (ctrl.selectedStep.prevEnabled || (step.stepPriority > ctrl.selectedStep.stepPriority))) { + ctrl.goTo(step, true); + } + }; + + ctrl.setPageSelected = function (step) { + if (angular.isFunction(ctrl.onStepChanged)) { + ctrl.onStepChanged({step: step, index: stepIdx(step)}); + } + }; + + ctrl.addStep = function (step) { + // Insert the step into step array + var insertBefore = _.find(ctrl.steps, function (nextStep) { + return nextStep.stepPriority > step.stepPriority; + }); + if (insertBefore) { + ctrl.steps.splice(ctrl.steps.indexOf(insertBefore), 0, step); + } else { + ctrl.steps.push(step); + } + + if (ctrl.wizardReady && (ctrl.getEnabledSteps().length > 0) && (step === ctrl.getEnabledSteps()[0])) { + ctrl.goTo(ctrl.getEnabledSteps()[0]); + } + }; + + ctrl.isWizardDone = function () { + return ctrl.wizardDone; + }; + + ctrl.updateSubStepNumber = function (value) { + ctrl.firstStep = stepIdx(ctrl.selectedStep) === 0 && value === 0; + }; + + ctrl.currentStepTitle = function () { + return ctrl.selectedStep.title; + }; + + ctrl.currentStepDescription = function () { + return ctrl.selectedStep.description; + }; + + ctrl.currentStep = function () { + return ctrl.selectedStep; + }; + + ctrl.totalStepCount = function () { + return ctrl.getEnabledSteps().length; + }; + + // Allow access to any step + ctrl.goToStep = function (step, resetStepNav) { + var enabledSteps = ctrl.getEnabledSteps(); + var stepTo; + + if (angular.isNumber(step)) { + stepTo = enabledSteps[step]; + } else { + stepTo = stepByTitle(step); + } + + ctrl.goTo(stepTo, resetStepNav); + }; + + // Method used for next button within step + ctrl.next = function (callback) { + var enabledSteps = ctrl.getEnabledSteps(); + + // Save the step you were on when next() was invoked + var index = stepIdx(ctrl.selectedStep); + + callback = callback || ctrl.nextCallback; + + if (ctrl.selectedStep.substeps) { + if (ctrl.selectedStep.next(callback)) { + return; + } + } + + // Check if callback is a function + if (angular.isFunction(callback)) { + if (callback(ctrl.selectedStep)) { + if (index < enabledSteps.length - 1) { + // Go to the next step + if (enabledSteps[index + 1].substeps) { + enabledSteps[index + 1].resetNav(); + } + } else { + ctrl.finish(); + } + } else { + return; + } + } + + // Completed property set on ctrl which is used to add class/remove class from progress bar + ctrl.selectedStep.completed = true; + + // Check to see if this is the last step. If it is next behaves the same as finish() + if (index === enabledSteps.length - 1) { + ctrl.finish(); + } else { + // Go to the next step + ctrl.goTo(enabledSteps[index + 1]); + } + }; + + ctrl.previous = function (callback) { + var index = stepIdx(ctrl.selectedStep); + callback = callback || ctrl.backCallback; + + if (ctrl.selectedStep.substeps) { + if (ctrl.selectedStep.previous(callback)) { + return; + } + } + + // Check if callback is a function + if (!angular.isFunction(callback) || callback(ctrl.selectedStep)) { + + if (index === 0) { + throw new Error("Can't go back. It's already in step 0"); + } else { + ctrl.goTo(ctrl.getEnabledSteps()[index - 1]); + } + } + }; + + ctrl.finish = function () { + if (ctrl.onFinish) { + if (ctrl.onFinish() !== false) { + ctrl.reset(); + } + } + }; + + ctrl.cancel = function () { + if (ctrl.onCancel) { + if (ctrl.onCancel() !== false) { + ctrl.reset(); + } + } + }; + + //reset + ctrl.reset = function () { + //traverse steps array and set each "completed" property to false + angular.forEach(ctrl.getEnabledSteps(), function (step) { + step.completed = false; + }); + //go to first step + ctrl.goToStep(0); + }; + + // Provide wizard controls to steps and sub-steps + $scope.wizard = this; + }] +}); +;angular.module('patternfly.canvas').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('canvas-view/canvas-editor/canvas-editor.html', + "
Hide Connectors
{{tab.preTitle}}
{{tab.title}}
" + ); + + + $templateCache.put('canvas-view/canvas-editor/toolbox-items.html', + "
  • \"{{item.name}}\" {{ item.name }}
" + ); + + + $templateCache.put('canvas-view/canvas/canvas.html', + "Select a second item to complete the connection or click on the canvas to cancelNo available connections! Click on the canvas to cancel{{node.name()}}

{{node.name()}}

{{node.fontContent()}}{{'\\ue918'}}{{connector.fontContent()}}{{connector.name()}}
{{connection.name()}}
" + ); + + + $templateCache.put('canvas-view/canvas/node-toolbar.html', + "
" + ); + +}]); +;angular.module('patternfly.card').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('card/aggregate-status/aggregate-status-card.html', + "

\"\" {{$ctrl.status.count}} {{$ctrl.status.title}} \"\" {{$ctrl.status.count}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}

\"\"{{ notification.count }} \"\"{{ notification.count }}

\"\" {{$ctrl.status.count}} {{$ctrl.status.title}} {{$ctrl.status.count}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}

\"\"{{$ctrl.status.notification.count}} \"\"{{$ctrl.status.notification.count}}

" + ); + + + $templateCache.put('card/basic/card-filter.html', + "
" + ); + + + $templateCache.put('card/basic/card.html', + "

{{$ctrl.headTitle}}

{{$ctrl.subTitle}}
{{$ctrl.spinnerText}}
" + ); + + + $templateCache.put('card/info-status/info-status-card.html', + "
\"\"

{{$ctrl.status.title}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}
" + ); + +}]); +;angular.module('patternfly.charts').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('charts/donut/donut-chart.html', + "" + ); + + + $templateCache.put('charts/donut/donut-pct-chart.html', + " {{$ctrl.config.labelConfig.title}} {{$ctrl.data.available}} {{$ctrl.config.labelConfig.units}} available {{$ctrl.data.percent}}% used {{$ctrl.data.used}} {{$ctrl.config.labelConfig.units}} of {{$ctrl.data.total}} {{$ctrl.config.labelConfig.units}} used " + ); + + + $templateCache.put('charts/empty-chart.html', + "
No data available
" + ); + + + $templateCache.put('charts/heatmap/heatmap-legend.html', + "
  • {{item.text}}
" + ); + + + $templateCache.put('charts/heatmap/heatmap.html', + "

{{$ctrl.chartTitle}}

" + ); + + + $templateCache.put('charts/line/line-chart.html', + "" + ); + + + $templateCache.put('charts/sparkline/sparkline-chart.html', + "" + ); + + + $templateCache.put('charts/topology-map/topology-map.html', + "
" + ); + + + $templateCache.put('charts/trends/trends-chart.html', + "
{{$ctrl.config.title}} {{$ctrl.getLatestValue()}} {{$ctrl.config.units}} {{$ctrl.getPercentageValue() + '%'}} of {{$ctrl.chartData.total + ' ' + $ctrl.config.units}}{{$ctrl.config.timeFrame}}
{{$ctrl.getLatestValue()}} {{$ctrl.config.units}} {{$ctrl.getPercentageValue() + '%'}} of {{$ctrl.chartData.total + ' ' + $ctrl.config.units}} {{$ctrl.config.title}}
{{$ctrl.getPercentageValue() + '%'}}
{{$ctrl.config.trendLabel}} {{$ctrl.getLatestValue()}} of {{$ctrl.chartData.total + ' ' + $ctrl.config.units}}
" + ); + + + $templateCache.put('charts/utilization-bar/utilization-bar-chart.html', + "
{{$ctrl.chartTitle}}
{{$ctrl.chartData.used}} of {{$ctrl.chartData.total}} {{$ctrl.units}} Used {{$ctrl.chartData.percentageUsed}}% Used
{{$ctrl.chartTitle}}
{{$ctrl.chartData.used}} {{$ctrl.units}} Used {{$ctrl.chartData.percentageUsed}}% Used
" + ); + + + $templateCache.put('charts/utilization-trend/utilization-trend-chart.html', + "

{{$ctrl.config.title}}

{{$ctrl.currentValue}}

{{$ctrl.currentText}}
of {{$ctrl.chartData.total}} {{$ctrl.config.units}}
{{$ctrl.legendLeftText}} {{$ctrl.legendRightText}}
" + ); + +}]); +;angular.module('patternfly.datepicker').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('datepicker/datepicker.html', + "

" + ); + +}]); +;angular.module('patternfly.filters').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('filters/filter-panel/filter-panel-results.html', + "
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.resultsLabel === undefined ? \"Results\" : $ctrl.config.resultsLabel}}

Active filters:

  • 1}\">{{filter.title}}:
    • {{value}}

0\">Clear All Filters

" + ); + + + $templateCache.put('filters/filter-panel/filter-panel.html', + "
" + ); + + + $templateCache.put('filters/simple-filter/filter-fields.html', + "
" + ); + + + $templateCache.put('filters/simple-filter/filter-results.html', + "
{{$ctrl.config.resultsCount}} {{$ctrl.config.itemsLabel}}
{{$ctrl.config.resultsCount}} {{$ctrl.config.itemsLabelPlural}}
0\">
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.itemsLabel}}
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.itemsLabelPlural}}

0\">Active Filters:

    0\">
  • {{filter.title}}: {{((filter.value.filterCategory.title || filter.value.filterCategory) + filter.value.filterDelimiter + (filter.value.filterValue.title || filter.value.filterValue)) || filter.value.title || filter.value}}

0\">Clear All Filters

{{$ctrl.config.selectedCount}} of {{$ctrl.config.totalCount}} selected
" + ); + + + $templateCache.put('filters/simple-filter/filter.html', + "
" + ); + +}]); +;angular.module('patternfly.form').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('form/form-buttons/form-buttons.html', + "
" + ); + + + $templateCache.put('form/form-group/form-group.html', + "
  • {{ message }}
" + ); + +}]); +;angular.module('patternfly.modals').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('modals/about-modal/about-modal.html', + "" + ); + + + $templateCache.put('modals/modal-overlay/modal-overlay.html', + "" + ); + +}]); +;angular.module('patternfly.navigation').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('navigation/application-launcher.html', + "" + ); + + + $templateCache.put('navigation/vertical-navigation.html', + "
" + ); + +}]); +;angular.module('patternfly.notification').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('notification/inline-notification.html', + "
{{$ctrl.pfNotificationHeader}} {{$ctrl.pfNotificationMessage}}
" + ); + + + $templateCache.put('notification/notification-drawer.html', + "

{{$ctrl.drawerTitle}}

Loading More
" + ); + + + $templateCache.put('notification/notification-list.html', + "
0\">
" + ); + + + $templateCache.put('notification/toast-notification-list.html', + "
0\">
0)}}\" html-content=$ctrl.htmlContent close-callback=$ctrl.handleClose action-title={{notification.actionTitle}} action-callback=notification.actionCallback menu-actions=notification.menuActions update-viewing=$ctrl.handleViewingChange data=notification>
" + ); + + + $templateCache.put('notification/toast-notification.html', + "" + ); + +}]); +;angular.module('patternfly.pagination').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('pagination/pagination.html', + "
per page
{{$ctrl.getStartIndex()}}-{{$ctrl.getEndIndex()}} of {{$ctrl.numTotalItems}}
of {{$ctrl.lastPageNumber}}
" + ); + +}]); +;angular.module('patternfly.select').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('select/select.html', + "
" + ); + +}]); +;angular.module('patternfly.sort').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('sort/sort.html', + "
" + ); + +}]); +;angular.module('patternfly.table').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('table/tableview/table-view.html', + "
{{col.header}}Actions
{{value}} 0\" class=table-view-pf-actions ng-repeat=\"actionButton in $ctrl.actionButtons\">
0\" class=\"table-view-pf-actions list-group-item-header\">
" + ); + +}]); +;angular.module('patternfly.toolbars').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('toolbars/toolbar.html', + "
0) ||\n" + + " ($ctrl.config.actionsConfig.moreActions && $ctrl.config.actionsConfig.moreActions.length > 0) ||\n" + + " $ctrl.config.actionsConfig.actionsInclude)\">
" + ); + +}]); +;angular.module('patternfly.views').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('views/cardview/card-view.html', + "
" + ); + + + $templateCache.put('views/empty-state.html', + "

{{$ctrl.config.title}}

{{$ctrl.config.info}}

" + ); + + + $templateCache.put('views/listview/examples/clusters-content.html', + "
Clusters for {{$ctrl.item.name}}
  • Cluster 1
  • Cluster 2
  • Cluster 3
  • Cluster 4
  • Cluster 5
  • Cluster 6
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

" + ); + + + $templateCache.put('views/listview/examples/hosts-content.html', + "
Hosts for {{$ctrl.item.name}}
  • Host 1
  • Host 2
  • Host 3
  • Host 4
  • Host 5
  • Host 6
  • Host 7
  • Host 8
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

" + ); + + + $templateCache.put('views/listview/examples/images-content.html', + "
Images for {{$ctrl.item.name}}
  • Image 1
  • Image 2
  • Image 3
  • Image 4
  • Image 5
  • Image 6
  • Image 7
  • Image 8
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

" + ); + + + $templateCache.put('views/listview/examples/nodes-content.html', + "
Nodes for {{$ctrl.item.name}}
  • Node 1
  • Node 2
  • Node 3
  • Node 4
  • Node 5
  • Node 6
  • Node 7
  • Node 8
  • Node 9
  • Node 10
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

" + ); + + + $templateCache.put('views/listview/list-view.html', + "
0) || ($ctrl.menuActions && $ctrl.menuActions.length > 0)\">
" + ); + +}]); +;angular.module('patternfly.wizard').run(['$templateCache', function($templateCache) { + 'use strict'; + + $templateCache.put('wizard/wizard-review-page.html', + "" + ); + + + $templateCache.put('wizard/wizard-step.html', + "
" + ); + + + $templateCache.put('wizard/wizard-substep.html', + "" + ); + + + $templateCache.put('wizard/wizard.html', + "

{{$ctrl.loadingWizardTitle}}

{{$ctrl.loadingSecondaryInformation}}

" + ); + +}]); diff --git a/dist/angular-patternfly.min.js b/dist/angular-patternfly.min.js new file mode 100644 index 000000000..9d25348fa --- /dev/null +++ b/dist/angular-patternfly.min.js @@ -0,0 +1,8 @@ +function mergeDeep(dst){"use strict";return angular.forEach(arguments,function(obj){obj!==dst&&angular.forEach(obj,function(value,key){dst[key]&&dst[key].constructor&&dst[key].constructor===Object?mergeDeep(dst[key],value):dst[key]=value})}),dst}function utilizationToColor(number){"use strict";var invert=100-number,hue=1.2*invert/360,rgb=hslToRgb(hue,1,.5);return"rgb("+rgb[0]+","+rgb[1]+","+rgb[2]+")"}function hue2rgb(p,q,t){"use strict";return 0>t&&(t+=1),t>1&&(t-=1),1/6>t?p+6*(q-p)*t:.5>t?q:2/3>t?p+(q-p)*(2/3-t)*6:p}function hslToRgb(h,s,l){"use strict";var r,g,b,q,p;return 0===s?r=g=b=l:(q=.5>l?l*(1+s):l+s-l*s,p=2*l-q,r=hue2rgb(p,q,h+1/3),g=hue2rgb(p,q,h),b=hue2rgb(p,q,h-1/3)),[Math.floor(255*r),Math.floor(255*g),Math.floor(255*b)]}angular.module("patternfly.canvas",["dragging","ngDragDrop","ui.bootstrap"]),angular.module("patternfly.card",["ui.bootstrap"]),angular.module("patternfly.charts",["patternfly.utils","ui.bootstrap","ngSanitize"]),angular.module("patternfly.datepicker",["ui.bootstrap"]),angular.module("patternfly.filters",["ui.bootstrap"]),angular.module("patternfly.form",[]),angular.module("patternfly.modals",["ui.bootstrap.modal","ui.bootstrap.tpls","ngSanitize","ngAnimate"]),angular.module("patternfly.navigation",["ui.bootstrap"]),angular.module("patternfly.notification",["patternfly.utils","ui.bootstrap"]),angular.module("patternfly.pagination",["ui.bootstrap"]).filter("startFrom",function(){"use strict";return function(input,start){return start=parseInt(start,10),input.slice(start)}}),angular.module("patternfly",["patternfly.autofocus","patternfly.card","patternfly.datepicker","patternfly.filters","patternfly.form","patternfly.modals","patternfly.navigation","patternfly.notification","patternfly.pagination","patternfly.select","patternfly.sort","patternfly.toolbars","patternfly.utils","patternfly.validation","patternfly.views","patternfly.wizard"]),angular.module("patternfly.select",["ui.bootstrap"]),angular.module("patternfly.sort",["ui.bootstrap"]),angular.module("patternfly.table",["datatables","patternfly.pagination","patternfly.utils","patternfly.filters","patternfly.sort"]),angular.module("patternfly.toolbars",["patternfly.utils","patternfly.filters","patternfly.sort","patternfly.views"]),angular.module("patternfly.utils",["ui.bootstrap"]),angular.module("patternfly.views",["patternfly.utils","patternfly.filters","patternfly.sort","patternfly.charts","dndLists","patternfly.pagination"]),angular.module("patternfly.wizard",["ui.bootstrap.modal","ui.bootstrap","patternfly.form"]),angular.module("patternfly.autofocus",[]).directive("pfFocused",["$timeout",function($timeout){"use strict";return{restrict:"A",link:function(scope,element,attrs){scope.$watch(attrs.pfFocused,function(newValue){$timeout(function(){newValue&&(element[0].focus(),element[0].select&&element[0].select())})})}}}]),function(){"use strict";angular.module("patternfly.canvas").component("pfCanvasEditor",{bindings:{chartDataModel:"=",chartViewModel:"=?",toolboxTabs:"=",readOnly:"u").addClass("nav-tabs-pf"),angular.element("#filterFld").focus()})},ctrl.hideToolbox=function(){ctrl.toolboxVisible=!1},ctrl.toggleToolbox=function(){ctrl.readOnly||ctrl.chartViewModel.inConnectingMode||(ctrl.toolboxVisible===!0?ctrl.hideToolbox():ctrl.showToolbox())},ctrl.tabClicked=function(){angular.element("#filterFld").focus()},ctrl.startCallback=function(event,ui,item){ctrl.draggedItem=item},ctrl.dropCallback=function(event,ui){var newNode=angular.copy(ctrl.draggedItem);newNodeCount++,newNode.x=event.clientX-600,newNode.y=event.clientY-200,newNode.backgroundColor=newNode.backgroundColor?newNode.backgroundColor:"#fff",ctrl.chartViewModel.addNode(newNode)},ctrl.addNodeByClick=function(item){var newNode=angular.copy(item);newNodeCount++,newNode.x=250+(4*newNodeCount+160),newNode.y=200+(4*newNodeCount+160),newNode.backgroundColor=newNode.backgroundColor?newNode.backgroundColor:"#fff",ctrl.chartViewModel.addNode(newNode)},ctrl.tabClicked=function(){angular.element("#filterFld").focus()},ctrl.activeTab=function(){return ctrl.toolboxTabs.filter(function(tab){return tab.active})[0]},ctrl.activeSubTab=function(){var activeTab=ctrl.activeTab();return activeTab&&activeTab.subtabs?activeTab.subtabs.filter(function(subtab){return subtab.active})[0]:void 0},ctrl.activeSubSubTab=function(){var activeSubTab=ctrl.activeSubTab();return activeSubTab&&activeSubTab.subtabs?activeSubTab.subtabs.filter(function(subsubtab){return subsubtab.active})[0]:void 0},ctrl.maxZoom=function(){return ctrl.chartViewModel&&ctrl.chartViewModel.zoom?ctrl.chartViewModel.zoom.isMax():!1},ctrl.minZoom=function(){return ctrl.chartViewModel&&ctrl.chartViewModel.zoom?ctrl.chartViewModel.zoom.isMin():!1},ctrl.zoomIn=function(){ctrl.chartViewModel.zoom["in"]()},ctrl.zoomOut=function(){ctrl.chartViewModel.zoom.out()}}]})}(),function(){"use strict";angular.module("patternfly.canvas").component("toolboxItems",{templateUrl:"canvas-view/canvas-editor/toolbox-items.html",bindings:{items:"=",startDragCallback:"<",clickCallback:"<",searchText:"="},controller:function(){var ctrl=this;ctrl.itemClicked=function(item){angular.isFunction(ctrl.clickCallback)&&!item.disableInToolbox&&ctrl.clickCallback(item)},ctrl.startItemDrag=function(event,ui,item){angular.isFunction(ctrl.startDragCallback)&&ctrl.startDragCallback(event,ui,item)}}})}(),function(){"use strict";angular.module("patternfly.canvas").filter("trustAsResourceUrl",["$sce",function($sce){return function(val){return $sce.trustAsResourceUrl(val)}}]).component("pfCanvas",{templateUrl:"canvas-view/canvas/canvas.html",bindings:{chartDataModel:"=",chartViewModel:"=?",readOnly:"startPoint.x?startPoint.x:curPoint.x,y:curPoint.y>startPoint.y?startPoint.y:curPoint.y,width:curPoint.x>startPoint.x?curPoint.x-startPoint.x:startPoint.x-curPoint.x,height:curPoint.y>startPoint.y?curPoint.y-startPoint.y:startPoint.y-curPoint.y}},dragEnded:function(){ctrl.dragSelecting=!1,ctrl.chart.applySelectionRect(ctrl.dragSelectionRect),delete ctrl.dragSelectionStartPoint,delete ctrl.dragSelectionRect}}))},ctrl.nodeMouseOver=function(evt,node){ctrl.readOnly||(ctrl.mouseOverNode=node)},ctrl.nodeMouseLeave=function(){ctrl.mouseOverNode=null},ctrl.nodeMouseDown=function(evt,node){var lastMouseCoords,chart=ctrl.chart;ctrl.readOnly||dragging.startDrag(evt,{dragStarted:function(x,y){lastMouseCoords=ctrl.translateCoordinates(x,y,evt),node.selected()||(chart.deselectAll(),node.select())},dragging:function(x,y){var curCoords=ctrl.translateCoordinates(x,y,evt),deltaX=curCoords.x-lastMouseCoords.x,deltaY=curCoords.y-lastMouseCoords.y;chart.updateSelectedNodesLocation(deltaX,deltaY),lastMouseCoords=curCoords},clicked:function(){chart.handleNodeClicked(node,evt.ctrlKey)}})},ctrl.nodeClickHandler=function(action,node){"nodeActionConnect"===action&&ctrl.startConnectingMode(node)},ctrl.nodeCloseHandler=function(){ctrl.mouseOverNode=null},ctrl.connectingModeOutputConnector=null,ctrl.connectingModeSourceNode=null,ctrl.startConnectingMode=function(node){ctrl.chart.inConnectingMode=!0,ctrl.hideConnectors=!1,ctrl.connectingModeSourceNode=node,ctrl.connectingModeSourceNode.select(),ctrl.connectingModeOutputConnector=node.getOutputConnector(),ctrl.chart.updateValidNodesAndConnectors(ctrl.connectingModeSourceNode)},ctrl.cancelConnectingMode=function(){ctrl.connectingModeOutputConnector.connected()||ctrl.chart.removeOutputConnector(ctrl.connectingModeOutputConnector),ctrl.stopConnectingMode()},ctrl.stopConnectingMode=function(){ctrl.chart.inConnectingMode=!1,ctrl.chart.resetValidNodesAndConnectors()},ctrl.connectionMouseOver=function(evt,connection){ctrl.draggingConnection||ctrl.readOnly||(ctrl.mouseOverConnection=connection)},ctrl.connectionMouseLeave=function(){ctrl.mouseOverConnection=null},ctrl.connectionMouseDown=function(evt,connection){var chart=ctrl.chart;ctrl.readOnly||chart.handleConnectionMouseDown(connection,evt.ctrlKey),evt.stopPropagation(),evt.preventDefault()},ctrl.connectorMouseOver=function(evt,node,connector){ctrl.readOnly||(ctrl.mouseOverConnector=connector)},ctrl.connectorMouseLeave=function(){ctrl.mouseOverConnector=null},ctrl.connectorMouseDown=function(evt,node){ctrl.chart.inConnectingMode&&node!==ctrl.connectingModeSourceNode&&(ctrl.chart.createNewConnection(ctrl.connectingModeOutputConnector,ctrl.mouseOverConnector),ctrl.stopConnectingMode())},$scope.$on("zoomIn",function(){ctrl.chart.zoom["in"]()}),$scope.$on("zoomOut",function(){ctrl.chart.zoom.out()}),$scope.maxZoom=function(){return ctrl.chart.chartViewModel&&ctrl.chart.chartViewModel.zoom?ctrl.chart.chartViewModel.zoom.isMax():!1},$scope.minZoom=function(){return ctrl.chart.chartViewModel&&ctrl.chart.chartViewModel.zoom?ctrl.chart.chartViewModel.zoom.isMin():!1},ctrl.zoomLevel=function(){return ctrl.chart.zoom.getLevel()},ctrl.$onInit=function(){var backspaceKeyCode=8,deleteKeyCode=46,aKeyCode=65,escKeyCode=27;$document.find("body").keydown(function(evt){evt.keyCode===aKeyCode&&evt.ctrlKey===!0&&(ctrl.selectAll(),$scope.$digest(),evt.stopPropagation(),evt.preventDefault()),(evt.keyCode===deleteKeyCode||evt.keyCode===backspaceKeyCode)&&(ctrl.deleteSelected(),$scope.$digest()),evt.keyCode===escKeyCode&&(ctrl.deselectAll(),$scope.$digest())})}}]})}();var pfCanvas={};!function(){pfCanvas.defaultHeight=756,pfCanvas.defaultWidth=1396,pfCanvas.defaultBgImageSize=24,pfCanvas.defaultNodeWidth=150,pfCanvas.defaultNodeHeight=150,pfCanvas.nodeNameHeight=40,pfCanvas.connectorHeight=25,pfCanvas.computeConnectorY=function(connectorIndex){return pfCanvas.defaultNodeHeight/2+connectorIndex*pfCanvas.connectorHeight},pfCanvas.computeConnectorPos=function(node,connectorIndex,inputConnector){return{x:node.x()+(inputConnector?0:node.width?node.width():pfCanvas.defaultNodeWidth),y:node.y()+pfCanvas.computeConnectorY(connectorIndex)}},pfCanvas.ConnectorViewModel=function(connectorDataModel,x,y,parentNode){this.data=connectorDataModel,this._parentNode=parentNode,this._x=x,this._y=y,this.name=function(){return this.data.name},this.x=function(){return this._x},this.y=function(){return this._y},this.parentNode=function(){return this._parentNode},this.connected=function(){return this.data.connected},this.setConnected=function(value){this.data.connected=value},this.invalid=function(){return this.data.invalid},this.setInvalid=function(value){this.data.invalid=value},this.fontFamily=function(){return this.data.fontFamily||""},this.fontContent=function(){return this.data.fontContent||""}};var createConnectorsViewModel=function(connectorDataModels,x,parentNode){var viewModels=[];if(connectorDataModels)for(var i=0;i=selectionRect.x&&node.y()>=selectionRect.y&&node.x()+node.width()<=selectionRect.x+selectionRect.width&&node.y()+node.height()<=selectionRect.y+selectionRect.height&&node.select()}for(i=0;i0},this.getSelectedConnections=function(){for(var selectedConnections=[],i=0;ithreshold||Math.abs(evt.pageY-y)>threshold)&&(dragging=!0,config.dragStarted&&config.dragStarted(x,y,evt),config.dragging&&config.dragging(evt.pageX,evt.pageY,evt))},released=function(){dragging?config.dragEnded&&config.dragEnded():config.clicked&&config.clicked()},mouseUp=function(evt){mouseCapture.release(),evt.stopPropagation(),evt.preventDefault()};mouseCapture.acquire(evt,{mouseMove:mouseMove,mouseUp:mouseUp,released:released}),evt.stopPropagation(),evt.preventDefault()}}}angular.module("dragging",["mouseCapture"]).factory("dragging",["mouseCapture",Factory])}(),function(){"use strict";function Factory($rootScope){var $element=document,mouseCaptureConfig=null,mouseMove=function(evt){mouseCaptureConfig&&mouseCaptureConfig.mouseMove&&(mouseCaptureConfig.mouseMove(evt),$rootScope.$digest())},mouseUp=function(evt){mouseCaptureConfig&&mouseCaptureConfig.mouseUp&&(mouseCaptureConfig.mouseUp(evt),$rootScope.$digest())};return{registerElement:function(element){$element=element},acquire:function(evt,config){this.release(),mouseCaptureConfig=config,$element.mousemove(mouseMove),$element.mouseup(mouseUp)},release:function(){mouseCaptureConfig&&(mouseCaptureConfig.released&&mouseCaptureConfig.released(),mouseCaptureConfig=null),$element.unbind("mousemove",mouseMove),$element.unbind("mouseup",mouseUp)}}}function ComponentDirective(){return{restrict:"A",controller:["$scope","$element","$attrs","mouseCapture",function($scope,$element,$attrs,mouseCapture){mouseCapture.registerElement($element)}]}}angular.module("mouseCapture",[]).factory("mouseCapture",["$rootScope",Factory]).directive("mouseCapture",[ComponentDirective])}(),function(){"use strict";angular.module("patternfly.canvas").component("nodeToolbar",{templateUrl:"canvas-view/canvas/node-toolbar.html",bindings:{node:"=",nodeActions:"=",nodeClickHandler:"<",nodeCloseHandler:"<"},controller:["$scope",function($scope){var ctrl=this;ctrl.selectedAction="none",ctrl.actionIconClicked=function(action){ctrl.selectedAction=action,$scope.$emit("nodeActionClicked",{action:action,node:ctrl.node}),angular.isFunction(ctrl.nodeClickHandler)&&ctrl.nodeClickHandler(action,ctrl.node)},ctrl.close=function(){ +ctrl.selectedAction="none",$scope.$emit("nodeActionClosed"),angular.isFunction(ctrl.nodeCloseHandler)&&ctrl.nodeCloseHandler()}}]})}(),angular.module("patternfly.card").component("pfAggregateStatusCard",{bindings:{status:"=",showTopBorder:"@?",showSpinner:"
',controller:["$timeout","$attrs",function($timeout,$attrs){var prevConfig,chart,ctrl=this;ctrl.generateChart=function(){var chartData;prevConfig=angular.copy(ctrl.config),$timeout(function(){chartData=ctrl.config,chartData&&(chartData.bindto="#"+$attrs.id,!chart||-1!==$attrs.id.indexOf("donutPctChart")&&chartData.thresholds?chart=c3.generate(chartData):chart.load(ctrl.config.data),ctrl.getChartCallback&&ctrl.getChartCallback(chart),prevConfig=angular.copy(ctrl.config))})},ctrl.$doCheck=function(){angular.equals(ctrl.config,prevConfig)||ctrl.generateChart()}}]})}(),angular.module("patternfly.charts").component("pfDonutChart",{bindings:{config:"<",data:"<",chartHeight:"=thresholds.error?(threshold="error",color=pfUtils.colorPalette.red):used>=thresholds.warning&&(threshold="warning",color=pfUtils.colorPalette.orange)),ctrl.threshold&&ctrl.threshold===threshold||(ctrl.threshold=threshold,ctrl.onThresholdChange({threshold:ctrl.threshold})),color},ctrl.statusDonutColor=function(){var color,percentUsed;return color={pattern:[]},percentUsed=ctrl.data.used/ctrl.data.total*100,color.pattern[0]=ctrl.getStatusColor(percentUsed,ctrl.config.thresholds),color.pattern[1]=pfUtils.colorPalette.black300,color},ctrl.donutTooltip=function(){return{contents:function(d){var tooltipContent=''+Math.round(100*d[0].ratio)+"% "+d[0].name+"";return ctrl.config.tooltipFn?tooltipContent=''+ctrl.config.tooltipFn(d)+"":"amount"===ctrl.tooltip?tooltipContent=''+d[0].value+" "+ctrl.config.units+" "+d[0].name+"":"both"===ctrl.tooltip&&(tooltipContent='
'+d[0].value+" "+ctrl.config.units+" "+d[0].name+""+Math.round(100*d[0].ratio)+"%
"),tooltipContent}}},ctrl.getDonutData=function(){return{columns:[["Used",ctrl.data.used],["Available",ctrl.data.available]],type:"donut",donut:{label:{show:!1}},groups:[["used","available"]],order:null}},ctrl.getCenterLabelText=function(){var centerLabelText;return centerLabelText={bigText:ctrl.data.used,smText:ctrl.config.units+" Used"},ctrl.config.centerLabelFn?(centerLabelText.bigText=ctrl.config.centerLabelFn(),centerLabelText.smText=""):"none"===ctrl.centerLabel?(centerLabelText.bigText="",centerLabelText.smText=""):"available"===ctrl.centerLabel?(centerLabelText.bigText=ctrl.data.available,centerLabelText.smText=ctrl.config.units+" Available"):"percent"===ctrl.centerLabel&&(centerLabelText.bigText=Math.round(ctrl.data.used/ctrl.data.total*100)+"%",centerLabelText.smText="of "+ctrl.data.total+" "+ctrl.config.units),centerLabelText},ctrl.updateAll=function(){prevData=angular.copy(ctrl.data),ctrl.config=pfUtils.merge(patternfly.c3ChartDefaults().getDefaultDonutConfig(),ctrl.config),ctrl.updateAvailable(),ctrl.updatePercentage(),ctrl.config.data=pfUtils.merge(ctrl.config.data,ctrl.getDonutData()),ctrl.config.color=ctrl.statusDonutColor(ctrl),ctrl.config.tooltip=ctrl.donutTooltip(),ctrl.config.data.onclick=ctrl.config.onClickFn},ctrl.setupDonutChartTitle=function(){var donutChartTitle,centerLabelText;angular.isUndefined(ctrl.chart)||(donutChartTitle=d3.select(ctrl.chart.element).select("text.c3-chart-arcs-title"),donutChartTitle&&(centerLabelText=ctrl.getCenterLabelText(),donutChartTitle.selectAll("*").remove(),centerLabelText.bigText&&!centerLabelText.smText?donutChartTitle.text(centerLabelText.bigText):(donutChartTitle.insert("tspan").text(centerLabelText.bigText).classed("donut-title-big-pf",!0).attr("dy",0).attr("x",0),donutChartTitle.insert("tspan").text(centerLabelText.smText).classed("donut-title-small-pf",!0).attr("dy",20).attr("x",0))))},ctrl.setChart=function(chart){ctrl.chart=chart,ctrl.setupDonutChartTitle()},ctrl.$onChanges=function(changesObj){(changesObj.config||changesObj.data)&&ctrl.updateAll(),changesObj.chartHeight&&(ctrl.config.size.height=changesObj.chartHeight.currentValue),changesObj.centerLabel&&ctrl.setupDonutChartTitle()},ctrl.$doCheck=function(){angular.equals(ctrl.data,prevData)||ctrl.updateAll()}}]}),angular.module("patternfly.charts").component("pfEmptyChart",{bindings:{chartHeight:" 90%"];ctrl.$onInit=function(){ctrl.updateAll()},ctrl.updateAll=function(){var index,items=[];for(ctrl.legendColors||(ctrl.legendColors=heatmapColorPatternDefaults),ctrl.legend||(ctrl.legend=legendLabelDefaults),index=ctrl.legend.length-1;index>=0;index--)items.push({text:ctrl.legend[index],color:ctrl.legendColors[index]});ctrl.legendItems=items},ctrl.$onChanges=function(changesObj){changesObj.legend&&!changesObj.legend.isFirstChange()&&ctrl.updateAll(),changesObj.legendColors&&!changesObj.legendColors.isFirstChange()&&ctrl.updateAll()}}}),angular.module("patternfly.charts").component("pfHeatmap",{bindings:{data:"<",chartDataAvailable:" 90%"],rangeTooltipDefaults=["< 70%","70-80%","80-90%","> 90%"],heightDefault=200,setStyles=function(){ctrl.containerStyles={height:ctrl.height+"px",display:ctrl.chartDataAvailable===!1?"none":"block"}},setSizes=function(){var parentContainer=$element[0].querySelector(".heatmap-container");containerWidth=parentContainer.clientWidth,containerHeight=parentContainer.clientHeight,blockSize=determineBlockSize(),blockSize-ctrl.padding>ctrl.maxSize?(blockSize=ctrl.padding+ctrl.maxSize,numberOfRows=Math.ceil(Math.sqrt(ctrl.data.length)),(blockSize*numberOfRows>containerWidth||blockSize*numberOfRows>containerHeight)&&(numberOfRows=0===blockSize?0:Math.floor(containerHeight/blockSize))):blockSize-ctrl.paddingcontainerWidth||blockSize*numberOfRows>containerHeight)&&(numberOfRows=0===blockSize?0:Math.floor(containerHeight/blockSize))):numberOfRows=0===blockSize?0:Math.floor(containerHeight/blockSize)},determineBlockSize=function(){var sx,sy,x=containerWidth,y=containerHeight,n=ctrl.data?ctrl.data.length:0,px=Math.ceil(Math.sqrt(n*x/y)),py=Math.ceil(Math.sqrt(n*y/x));return sx=Math.floor(px*y/x)*px50&&(ctrl.maxSize=50)),void 0===ctrl.minBlockSize||isNaN(ctrl.minBlockSize)?ctrl.minSize=2:ctrl.minSize=parseInt(ctrl.minBlockSize),void 0===ctrl.blockPadding||isNaN(ctrl.blockPadding)?ctrl.padding=2:ctrl.padding=parseInt(ctrl.blockPadding),void 0===ctrl.rangeHoverSize||isNaN(ctrl.rangeHoverSize)?ctrl.rangeHoverSize=15:ctrl.rangeHoverSize=parseInt(ctrl.rangeHoverSize),ctrl.rangeOnHover=void 0===ctrl.rangeOnHover||ctrl.rangeOnHover?!0:!1,ctrl.rangeTooltips||(ctrl.rangeTooltips=rangeTooltipDefaults),ctrl.thresholds||(ctrl.thresholds=thresholdDefaults),ctrl.heatmapColorPattern||(ctrl.heatmapColorPattern=heatmapColorPatternDefaults),ctrl.legendLabels||(ctrl.legendLabels=legendLabelDefaults),ctrl.height=ctrl.height||heightDefault,ctrl.showLegend=ctrl.showLegend||void 0===ctrl.showLegend};ctrl.loadingDone=!1,ctrl.$onChanges=function(changesObj){changesObj.chartDataAvailable&&!changesObj.chartDataAvailable.isFirstChange()?setStyles():changesObj.data||(updateConfig(),updateDisplay())},ctrl.$doCheck=function(){angular.equals(ctrl.data,prevData)||handleDataUpdate()},ctrl.$postLink=function(){ctrl.thisComponent=$element[0].querySelector(".heatmap-pf-svg"),updateConfig(),handleDataUpdate(),angular.element($window).on("resize",debounceResize)},ctrl.$onDestroy=function(){angular.element($window).off("resize",debounceResize)}}]}),angular.module("patternfly.charts").component("pfLineChart",{bindings:{config:"<",chartData:"<",showXAxis:" '+tipRows+"
"},ctrl.sparklineTooltip=function(){return{contents:function(d){var tipRows,percentUsed=0;if(ctrl.config.tooltipFn)tipRows=ctrl.config.tooltipFn(d);else switch(ctrl.config.tooltipType){case"usagePerDay":ctrl.chartData.dataAvailable!==!1&&ctrl.chartData.total>0&&(percentUsed=Math.round(d[0].value/ctrl.chartData.total*100)),tipRows=' '+d[0].x.toLocaleDateString()+' '+percentUsed+'%: '+d[0].value+" "+(ctrl.config.units?ctrl.config.units+" ":"")+d[0].name+"";break;case"valuePerDay":tipRows=' '+d[0].x.toLocaleDateString()+' '+d[0].value+" "+d[0].name+"";break;case"percentage":percentUsed=Math.round(d[0].value/ctrl.chartData.total*100),tipRows=' '+percentUsed+"%";break;default:tipRows=patternfly.c3ChartDefaults().getDefaultSparklineTooltip().contents(d)}return ctrl.getTooltipTableHTML(tipRows)},position:function(data,width,height,element){var center,top,chartBox,graphOffsetX,x;try{return center=parseInt(element.getAttribute("x")),top=parseInt(element.getAttribute("y")),chartBox=document.querySelector("#"+ctrl.sparklineChartId).getBoundingClientRect(),graphOffsetX=document.querySelector("#"+ctrl.sparklineChartId+" g.c3-axis-y").getBoundingClientRect().right,x=Math.max(0,center+graphOffsetX-chartBox.left-Math.floor(width/2)),{top:top-height,left:Math.min(x,chartBox.width-width)}}catch(e){}}}},ctrl.$onChanges=function(changesObj){ctrl.updateAll()},ctrl.$doCheck=function(){angular.equals(ctrl.chartData,prevChartData)||ctrl.updateAll()}}]}),angular.module("patternfly.charts").component("pfTopologyMap",{bindings:{nodes:"<",edges:"<",selectNode:"&",multiSelectNodes:"&",tooltipStyle:"tolerance?!1:0>dotproduct?!1:dotproduct>squaredlengthba?!1:!0})},ctrl.getRealCoordinates=function(x,y){return[(x-ctrl.transform.x)/ctrl.transform.k,(y-ctrl.transform.y)/ctrl.transform.k]},ctrl.setUpDrag=function(){function onDragStart(){ctrl.tooltip=void 0,d3.event.sourceEvent.stopPropagation(),ctrl.draggedNode=ctrl.findNode.apply(this,ctrl.getRealCoordinates(d3.event.sourceEvent.offsetX,d3.event.sourceEvent.offsetY))}function onDrag(){var newCoordinates=ctrl.getRealCoordinates(d3.event.sourceEvent.offsetX,d3.event.sourceEvent.offsetY);ctrl.draggedNode&&(d3.event.sourceEvent.stopPropagation(),ctrl.draggedNode.px=newCoordinates[0],ctrl.draggedNode.py=newCoordinates[1],ctrl.draggedNode.fixed=!0,ctrl.force.start())}function onDragEnd(){d3.event.sourceEvent.stopPropagation(),ctrl.draggedNode=void 0}var drag=d3.behavior.drag(),canvas=d3.select(ctrl.canvas);canvas.call(drag.on("dragstart",onDragStart).on("drag",onDrag).on("dragend",onDragEnd))},ctrl.setUpSemanticZoom=function(){function semanticZoom(){var translateX,translateY;ctrl.draggedNode||(1===ctrl.zoom.scale()?(translateY=0,translateX=0):(translateX=Math.min(0,Math.max(ctrl.zoom.translate()[0],ctrl.canvasW-ctrl.canvasW*ctrl.zoom.scale())),translateY=Math.min(0,Math.max(ctrl.zoom.translate()[1],ctrl.canvasH-ctrl.canvasH*ctrl.zoom.scale()))),ctrl.zoom.translate([translateX,translateY]),ctrl.transform={x:translateX,y:translateY,k:ctrl.zoom.scale()},ctrl.draw())}d3.select(ctrl.canvas).call(ctrl.zoom.on("zoom",semanticZoom))},ctrl.transformApply=function(x,y){return{x:x*ctrl.transform.k+ctrl.transform.x,y:y*ctrl.transform.k+ctrl.transform.y}},ctrl.draw=function(){ctrl.context.clearRect(0,0,ctrl.canvasW,ctrl.canvasH,d3.scale.linear()),ctrl.drawEdges(),ctrl.drawNodes(),!ctrl.showNodeLabels&&ctrl.tooltip&&ctrl.tooltip.title&&ctrl.drawNodeTooltip(ctrl.tooltip),!ctrl.showEdgeLabels&&ctrl.highlightEdge&&ctrl.highlightEdge.title&&ctrl.drawEdgeTooltip(ctrl.highlightEdge),1!==ctrl.transform.scale&&ctrl.drawMiniMap()},ctrl.setUpForce=function(){function tick(){ctrl.draw()}ctrl.force=d3.layout.force().charge(function(d,i){return i?-500:-2500}).friction(.5).chargeDistance(400).gravity(0).linkDistance(100).linkStrength(1).size([ctrl.canvasW,ctrl.canvasH]),ctrl.edges.forEach(function(edge){edge.source=_.findIndex(ctrl.nodes,function(node){return node.id===edge.source}),edge.target=_.findIndex(ctrl.nodes,function(node){return node.id===edge.target})}),ctrl.force.nodes(ctrl.nodes).links(ctrl.edges).on("tick",tick).start()},ctrl.shouldHighlightEdge=function(edge){return _.isEqual(edge,ctrl.highlightEdge)||_.find(ctrl.edgeMultiSelect,function(e){return _.isEqual(edge,e)})},ctrl.drawEdges=function(){var quadtree=d3.geom.quadtree(ctrl.force.nodes());ctrl.context.strokeStyle="rgba(150, 150, 150, 0.6)",ctrl.context.lineWidth=1,ctrl.edges.forEach(function(d){var sourceCoords,targetCoords,highlight=ctrl.shouldHighlightEdge(d);ctrl.context.strokeStyle=highlight?"rgba(0, 0, 0, .5)":"rgba(150, 150, 150, 0.6)",quadtree.visit(ctrl.collide(d.source)),quadtree.visit(ctrl.collide(d.target)),sourceCoords=ctrl.transformApply(d.source.x,d.source.y),targetCoords=ctrl.transformApply(d.target.x,d.target.y),ctrl.context.beginPath(),ctrl.context.setLineDash([]),"dashed"===d.lineStyle&&ctrl.context.setLineDash([10,5]),ctrl.context.moveTo(sourceCoords.x,sourceCoords.y),ctrl.context.lineTo(targetCoords.x,targetCoords.y),highlight?(ctrl.context.shadowBlur=15,ctrl.context.shadowOffsetX=3,ctrl.context.shadowOffsetY=3,ctrl.context.shadowColor="rgba(0, 0, 0, 0.5)"):ctrl.context.shadowColor="transparent",ctrl.context.stroke(),ctrl.context.shadowColor="transparent",ctrl.showEdgeLabels&&d.title&&ctrl.drawEdgeTooltip(d)})},ctrl.normalizeNode=function(node){node.size=node.size||17,node.x=Math.max(node.size+1,Math.min(ctrl.canvasW-node.size-1,node.x)),node.y=Math.max(node.size+1,Math.min(ctrl.canvasH-node.size-1,node.y))},ctrl.shouldHighlightNode=function(node){return ctrl.tooltip&&ctrl.tooltip.id===node.id||_.find(ctrl.nodeMultiSelect,function(n){return n.id===node.id})},ctrl.drawNodes=function(){ +var coordinates;ctrl.nodes.forEach(function(node){var imgR=.7*node.size,highlight=ctrl.shouldHighlightNode(node);ctrl.normalizeNode(node),ctrl.context.globalAlpha=node.opacity||1,coordinates=ctrl.transformApply(node.x,node.y),ctrl.context.beginPath(),ctrl.context.fillStyle=node.fill||"#FFFFFF",ctrl.context.strokeStyle=node.borderColor||"#000000",ctrl.context.lineWidth=highlight?3:1,ctrl.context.arc(coordinates.x,coordinates.y,node.size,0,2*Math.PI),highlight?(ctrl.context.shadowBlur=20,ctrl.context.shadowOffsetX=5,ctrl.context.shadowOffsetY=5,ctrl.context.shadowColor="rgba(0, 0, 0, 0.5)"):ctrl.context.shadowColor="transparent",ctrl.context.fill(),ctrl.context.shadowColor="transparent",ctrl.context.stroke(),node.utilization&&(ctrl.context.beginPath(),ctrl.context.lineWidth=5,ctrl.context.arc(coordinates.x,coordinates.y,node.size,0,node.utilization/100*2*Math.PI),ctrl.context.strokeStyle=pfUtils.utilizationToColor(node.utilization),ctrl.context.stroke()),ctrl.context.beginPath(),ctrl.context.fillStyle=node.iconColor||"#000000",ctrl.context.textAlign="center",ctrl.context.textBaseline="middle",node.fonticon?(ctrl.context.font="normal normal normal "+node.size+"px FontAwesome",ctrl.context.fillText(ctrl.cachedIcons[node.fonticon]["char"],coordinates.x,coordinates.y)):node.fileicon?ctrl.context.drawImage(ctrl.cachedIcons[node.fileicon].img,coordinates.x-imgR,coordinates.y-imgR,2*imgR,2*imgR):(ctrl.context.font=2*imgR+"px "+ctrl.cachedIcons.unknown.font,ctrl.context.fillText(ctrl.cachedIcons.unknown["char"],coordinates.x,coordinates.y)),ctrl.context.globalAlpha=1,ctrl.showNodeLabels&&node.title&&ctrl.drawNodeTooltip(node)})},ctrl.drawMiniMap=function(){var mapX=.9*ctrl.canvasW-10,mapY=10,mapW=.1*ctrl.canvasW,mapH=.1*ctrl.canvasH;ctrl.context.lineWidth=1,ctrl.context.beginPath(),ctrl.context.strokeStyle="rgba(0, 0, 0, 0.3)",ctrl.context.fillStyle="rgba(252, 252, 252, 0.3)",ctrl.context.rect(mapX,mapY,mapW,mapH),ctrl.context.stroke(),ctrl.context.fill(),ctrl.context.beginPath(),ctrl.context.fillStyle="rgba(224, 224, 224, 0.3)",ctrl.context.rect(mapX-ctrl.transform.x/ctrl.transform.k*.1,mapY-ctrl.transform.y/ctrl.transform.k*.1,mapW/ctrl.transform.k,mapH/ctrl.transform.k),ctrl.context.stroke(),ctrl.context.fill()},ctrl.drawEdgeTooltip=function(edge){var tooltipWidth,sourceCoordinates=ctrl.transformApply(edge.source.x,edge.source.y),targetCoordinates=ctrl.transformApply(edge.target.x,edge.target.y),midX=sourceCoordinates.x+.5*(targetCoordinates.x-sourceCoordinates.x),midY=sourceCoordinates.y+.5*(targetCoordinates.y-sourceCoordinates.y),tmp=document.createElement("span");tmp.innerHTML=edge.title,tmp.style.padding="10px",tmp.style.visibility="hidden",tmp.style.display="inline-block",document.body.appendChild(tmp),tooltipWidth=tmp.clientWidth,ctrl.context.beginPath(),ctrl.context.fillStyle="#000000",ctrl.context.font=ctrl.tooltipStyle.size+"px "+ctrl.tooltipStyle.font,ctrl.context.fillText(edge.title,midX,midY+5),document.body.removeChild(tmp)},ctrl.drawNodeTooltip=function(node){var coordinates=ctrl.transformApply(node.x,node.y),tooltipWidth=0,offsetY=coordinates.y+node.size+ctrl.tooltipStyle.size+5,tmp=document.createElement("span");tmp.innerHTML=node.title,tmp.style.padding="10px",tmp.style.visibility="hidden",tmp.style.display="inline-block",document.body.appendChild(tmp),tooltipWidth=tmp.clientWidth,ctrl.context.beginPath(),ctrl.context.rect(coordinates.x-tooltipWidth/2-5,offsetY-ctrl.tooltipStyle.size,tooltipWidth+10,ctrl.tooltipStyle.size+10),ctrl.context.fillStyle=ctrl.tooltipStyle.background,ctrl.context.fill(),ctrl.context.lineWidth=ctrl.tooltipStyle.borderWidth,ctrl.context.strokeStyle=ctrl.tooltipStyle.borderColor,ctrl.context.stroke(),ctrl.context.fillStyle=ctrl.tooltipStyle.textColor,ctrl.context.font=ctrl.tooltipStyle.size+"px "+ctrl.tooltipStyle.font,ctrl.context.fillText(node.title,coordinates.x,offsetY),document.body.removeChild(tmp)},ctrl.loadIcons=function(){var tmp=document.createElement("i"),char="",promises=[],questionCode=ctrl.findIconUnicode("fa fa-question"),q=$q.defer(),code="";document.body.appendChild(tmp),ctrl.cachedIcons.unknown={},tmp.className="hidden fa fa-question",char=window.getComputedStyle(tmp,":before").content.replace(/'|"/g,""),ctrl.IE11&&questionCode?ctrl.cachedIcons.unknown["char"]=String.fromCharCode(questionCode.toUpperCase().replace("\\","0x").replace(/'|"/g,"")):ctrl.cachedIcons.unknown["char"]=char,ctrl.cachedIcons.unknown.font=window.getComputedStyle(tmp,":before").fontFamily,ctrl.nodes.forEach(function(node){node.fileicon&&!ctrl.cachedIcons[node.fileicon]?(ctrl.cachedIcons[node.fileicon]={},promises.push(q.promise),ctrl.cachedIcons[node.fileicon].img=new Image,ctrl.cachedIcons[node.fileicon].img.src=node.fileicon,ctrl.cachedIcons[node.fileicon].img.onload=function(){return q.resolve()}):node.fonticon&&!ctrl.cachedIcons[node.fonticon]&&(ctrl.cachedIcons[node.fonticon]={},tmp.className="hidden "+node.fonticon,char=window.getComputedStyle(tmp,":before").content,ctrl.cachedIcons[node.fonticon]["char"]=char.replace(/'|"/g,""),ctrl.IE11&&(code=ctrl.findIconUnicode(node.fonticon).toUpperCase().replace("\\","0x"),ctrl.cachedIcons[node.fonticon]["char"]=String.fromCharCode(code.replace(/'|"/g,""))),ctrl.cachedIcons[node.fonticon].font=window.getComputedStyle(tmp,":before").fontFamily)}),document.body.removeChild(tmp)},this.collide=function(node){var r=node.size+22,nx1=node.x-r,nx2=node.x+r,ny1=node.y-r,ny2=node.y+r;return function(quad,x1,y1,x2,y2){var x,l,y,r;return quad.point&&quad.point!==node&&(x=node.x-quad.point.x,y=node.y-quad.point.y,l=Math.sqrt(x*x+y*y),r=30*node.size+quad.point.radius,r>l&&(l=(l-r)/l*2.5,node.x-=x*=l,node.y-=y*=l,quad.point.x+=x,quad.point.y+=y)),x1>nx2||nx1>x2||y1>ny2||ny1>y2}},ctrl.findRules=function(){var href,index,styleSheet=_.find(document.styleSheets,function(sheet){return sheet&&sheet.href?(href=sheet.href,index=sheet.href.lastIndexOf("/"),"/patternfly.css"===href.substring(index)):void 0});return styleSheet?styleSheet.rules:void 0},ctrl.findIconUnicode=function(fonticon){var rule,className=fonticon.substring(fonticon.indexOf(" ")+1);return ctrl.rules&&(rule=_.find(ctrl.rules,function(rule){return rule&&rule.selectorText?-1!==rule.selectorText.indexOf(className+"::before"):void 0})),rule?rule.style.content:void 0}}]}),angular.module("patternfly.charts").component("pfTopology",{bindings:{items:"<",relations:"<",kinds:"<",icons:"<",selection:"<",force:"<",radius:"<",nodes:"<",searchText:"i;i++)relation=relations[i],s=lookup[relation.source],t=lookup[relation.target],void 0!==s&&void 0!==t&&links.push({source:s,target:t,kinds:nodes[s].item.kind+nodes[t].item.kind});return width&&height?update():d3.select()}function resized(){window.clearTimeout(timeout),timeout=window.setTimeout(adjust,1)}var width,height,timeout,drag,svg,vertices,edges,outer=d3.select(selector),kinds=null,items={},relations=[],radius=20,nodes=[],links=[],lookup={},selection=null,force=options.force;return options.radius&&(radius=options.radius),force||(force=d3.layout.force().charge(-800).gravity(.2).linkDistance(80)),drag=force.drag(),svg=outer.append("svg").attr("viewBox","0 0 1600 1200").attr("preserveAspectRatio","xMidYMid meet").attr("class","pf-topology-svg"),vertices=d3.select(),edges=d3.select(),force.on("tick",function(){edges.attr("x1",function(d){return d.source.x}).attr("y1",function(d){return d.source.y}).attr("x2",function(d){return d.target.x}).attr("y2",function(d){return d.target.y}),vertices.attr("cx",function(d){return d.x=d.fixed?d.x:Math.max(radius,Math.min(width-radius,d.x)),d.x}).attr("cy",function(d){return d.y=d.fixed?d.y:Math.max(radius,Math.min(height-radius,d.y)),d.y}).attr("transform",function(d){return"translate("+d.x+","+d.y+")"})}),drag.on("dragstart",function(d){notify(d.item),d.fixed!==!0&&(d.floatpoint=[d.x,d.y]),d.fixed=!0,d3.select(this).classed("fixed",!0)}).on("dragend",function(d){var moved=!0;d.floatpoint&&(moved=d.xd.floatpoint[0]+5||d.yd.floatpoint[1]+5,delete d.floatpoint),d.fixed=moved&&d.x>3&&d.x=3&&d.ycanvasSize[0]&&(popup.style("left","auto"),popup.style("right",0)),popupSize[1]+mousePosition[1]>canvasSize[1]&&(popup.style("top","auto"),popup.style("bottom",0)),contextMenuShowing=!contextMenuShowing)}function buildContextMenuOptions(popup,data){return"Tag"===data.item.kind?!1:void addContextMenuOption(popup,"Go to summary page",data,dblclick)}function dblclick(d){window.location.assign(d.url)}function addContextMenuOption(popup,text,data,callback){popup.append("p").text(text).on("click",function(){callback(data)})}function getDimensions(d){var nodeEntry=ctrl.nodes[d.item.kind],defaultDimensions=defaultElementDimensions();return nodeEntry&&(nodeEntry.textX&&(defaultDimensions.x=nodeEntry.textX),nodeEntry.textY&&(defaultDimensions.y=nodeEntry.textY),nodeEntry.radius&&(defaultDimensions.r=nodeEntry.radius)),defaultDimensions}function defaultElementDimensions(){return{x:0,y:9,r:17}}function getItemStatusClass(d){switch(d.item.status.toLowerCase()){case"ok":case"active":case"available":case"on":case"ready":case"running":case"succeeded":case"valid":return"success";case"notready":case"failed":case"error":case"unreachable":return"error";case"warning":case"waiting":case"pending":return"warning";case"unknown":case"terminated":return"unknown"}}var options,graph,previousItems,previousRelations,previousKinds,contextMenuShowing,vs,ctrl=this,cache={};ctrl.$onInit=function(){$element.css("display","block"),options={force:ctrl.force,radius:ctrl.radius},ctrl.showLabels=!1,$element.on("$destroy",function(){graph.close()}),d3.select("body").on("click",function(){contextMenuShowing&&removeContextMenu()})},ctrl.$onChanges=function(changesObj){changesObj.searchText&&graph&&search(changesObj.searchText.currentValue),changesObj.showLabels&&vs&&toggleLabelVisibility(),changesObj.selection&&graph&&graph.select(changesObj.selection.currentValue||null)},ctrl.$doCheck=function(){graph&&(angular.equals(ctrl.kinds,previousKinds)||(previousKinds=angular.copy(ctrl.kinds),render(graph.kinds(ctrl.kinds))),angular.equals(ctrl.items,previousItems)&&angular.equals(ctrl.relations,previousRelations)||(previousItems=angular.copy(ctrl.items),previousRelations=angular.copy(ctrl.relations),render(graph.data(ctrl.items,ctrl.relations))))},ctrl.$postLink=function(){options={force:ctrl.force,radius:ctrl.radius},graph=topologyGraph($element[0],notify,options)}}]}),angular.module("patternfly.charts").component("pfTrendsChart",{bindings:{config:"<",chartData:"<",chartHeight:"0&&(pctValue=Math.round(ctrl.getLatestValue()/ctrl.chartData.total*100)),pctValue},ctrl.getLatestValue=function(){var latestValue=0;return ctrl.chartData.yData&&ctrl.chartData.yData.length>0&&(latestValue=ctrl.chartData.yData[ctrl.chartData.yData.length-1]),latestValue},ctrl.getChartHeight=function(){var retValue=LARGE;return ctrl.chartHeight?retValue=ctrl.chartHeight:"small"===ctrl.config.layout&&(retValue=SMALL),retValue},ctrl.$onChanges=function(changesObj){ctrl.updateAll()},ctrl.$doCheck=function(){angular.equals(ctrl.chartData,prevChartData)&&angular.equals(ctrl.config,prevConfig)||ctrl.updateAll()}}]}),angular.module("patternfly.charts").component("pfUtilizationBarChart",{bindings:{chartData:"=",chartTitle:"=",chartFooter:"=",units:"=",thresholdError:"=?",thresholdWarning:"=?",footerLabelFormat:"@?",layout:"=?",usedTooltipFunction:"&?",availableTooltipFunction:"&?"},templateUrl:"charts/utilization-bar/utilization-bar-chart.html",controller:["$timeout",function($timeout){"use strict";var prevChartData,prevLayout,ctrl=this;ctrl.updateAll=function(){prevChartData=angular.copy(ctrl.chartData),prevLayout=angular.copy(ctrl.layout),ctrl.chartData&&(!isNaN(ctrl.chartData.used)&&!isNaN(ctrl.chartData.total)&&ctrl.chartData.total>0?ctrl.chartData.percentageUsed=Math.round(100*(ctrl.chartData.used/ctrl.chartData.total)):ctrl.chartData.percentageUsed=0,(ctrl.thresholdError||ctrl.thresholdWarning)&&(ctrl.isError=ctrl.chartData.percentageUsed>=ctrl.thresholdError,ctrl.isWarn=ctrl.chartData.percentageUsed>=ctrl.thresholdWarning&&ctrl.chartData.percentageUsed0&&(ctrl.addFilterFn(ctrl.currentField,ctrl.currentValue),ctrl.currentValue=void 0,keyEvent.stopPropagation(),keyEvent.preventDefault())}function setupConfig(){var fieldFound=!1;prevConfig=angular.copy(ctrl.config),void 0===ctrl.config.fields&&(ctrl.config.fields=[]),ctrl.currentField&&(fieldFound=_.find(ctrl.config.fields,function(nextField){return nextField.id===ctrl.currentField.id})),fieldFound||(ctrl.currentField=ctrl.config.fields[0],ctrl.currentValue=null),void 0===ctrl.currentValue&&(ctrl.currentValue=null)}var prevConfig,ctrl=this;ctrl.$onInit=function(){angular.extend(ctrl,{selectField:selectField,selectValue:selectValue,onValueKeyPress:onValueKeyPress})},ctrl.$onChanges=function(){setupConfig()},ctrl.$doCheck=function(){angular.equals(ctrl.config,prevConfig)||setupConfig()}}}),angular.module("patternfly.filters").component("pfFilterResults",{bindings:{config:"="},templateUrl:"filters/simple-filter/filter-results.html",controller:function(){"use strict";function setupConfig(){prevConfig=angular.copy(ctrl.config),ctrl.config.appliedFilters||(ctrl.config.appliedFilters=[]),void 0===ctrl.config.resultsCount&&(ctrl.config.resultsCount=0),ctrl.config.itemsLabel=ctrl.config.itemsLabel||"Result",ctrl.config.itemsLabelPlural=ctrl.config.itemsLabelPlural||"Results"}function clearFilter(evt,item){var newFilters=[];evt.preventDefault(),ctrl.config.appliedFilters.forEach(function(filter){(item.title!==filter.title||item.value!==filter.value)&&newFilters.push(filter)}),ctrl.config.appliedFilters=newFilters,ctrl.config.onFilterChange&&ctrl.config.onFilterChange(ctrl.config.appliedFilters)}function clearAllFilters(evt){evt.preventDefault(),ctrl.config.appliedFilters=[],ctrl.config.onFilterChange&&ctrl.config.onFilterChange(ctrl.config.appliedFilters)}var prevConfig,ctrl=this;ctrl.$onInit=function(){angular.extend(ctrl,{clearFilter:clearFilter,clearAllFilters:clearAllFilters})},ctrl.$onChanges=function(){setupConfig()},ctrl.$doCheck=function(){angular.equals(ctrl.config,prevConfig)||setupConfig()}}}),angular.module("patternfly.form").component("pfFormButtons",{bindings:{pfHandleCancel:"&pfOnCancel",pfHandleSave:"&pfOnSave",pfWorking:"=",pfButtonContainerClass:"@"},require:{form:"^form"},templateUrl:"form/form-buttons/form-buttons.html",controller:function(){"use strict";function isInvalid(){var invalid=ctrl.form.$invalid;return ctrl.form&&ctrl.form.name&&ctrl.form.name.$error&&ctrl.form.name.$error.server&&(invalid=!1),invalid}var ctrl=this;ctrl.$onInit=function(){void 0===ctrl.pfWorking&&(ctrl.pfWorking=!1),angular.extend(ctrl,{isInvalid:isInvalid})}}}),angular.module("patternfly.form").component("pfFormGroup",{bindings:{pfLabel:"@",pfField:"@",pfLabelClass:"@",pfInputClass:"@"},require:{form:"^form"},transclude:!0,templateUrl:"form/form-group/form-group.html",controller:["$element",function($element){"use strict";function hasErrors(){return ctrl.form[ctrl.pfField]&&ctrl.form[ctrl.pfField].$invalid&&ctrl.form[ctrl.pfField].$dirty}function getInput(element){var input=element.find("table");return 0===input.length&&(input=element.find("input"),0===input.length&&(input=element.find("select"),0===input.length&&(input=element.find("textarea")))),input}var ctrl=this;ctrl.$onInit=function(){angular.extend(ctrl,{hasErrors:hasErrors})},ctrl.$postLink=function(){var input=getInput($element),type=input.attr("type");-1===["checkbox","radio","time"].indexOf(type)&&input.addClass("form-control"),ctrl.pfField||(ctrl.pfField=input.attr("id")),input.attr("required")&&$element.addClass("required"),ctrl.form[ctrl.pfField]&&(ctrl.error=ctrl.form[ctrl.pfField].$error)}}]}),angular.module("patternfly.form").directive("pfRemainingCharsCount",["$timeout",function($timeout){"use strict";return{restrict:"A",require:"ngModel",scope:{ngModel:"="},link:function($scope,$element,$attributes){var charsMaxLimit=$attributes.charsMaxLimit,charsWarnRemaining=$attributes.charsWarnRemaining,countRemainingFld=angular.element(document.getElementById($attributes.countFld)),blockInputAtMaxLimit="true"===$attributes.blockInputAtMaxLimit,checkCharactersRemaining=function(){var charsLength=$scope.ngModel.length,remainingChars=charsMaxLimit-charsLength;blockInputAtMaxLimit&&charsLength>charsMaxLimit&&($scope.ngModel=$scope.ngModel.substring(0,charsMaxLimit),charsLength=$scope.ngModel.length,remainingChars=charsMaxLimit-charsLength),$scope.remainingChars=remainingChars,$scope.remainingCharsWarning=charsWarnRemaining>=remainingChars,countRemainingFld.text(remainingChars),countRemainingFld.toggleClass("chars-warn-remaining-pf",charsWarnRemaining>=remainingChars),0>remainingChars?$scope.$emit("overCharsMaxLimit",$attributes.id):$scope.$emit("underCharsMaxLimit",$attributes.id)};$scope.$watch("ngModel",function(){checkCharactersRemaining()}),$element.on("keypress",function(event){blockInputAtMaxLimit&&$element.val().length>=charsMaxLimit&&8!==event.keyCode&&event.preventDefault()})}}}]),angular.module("patternfly.modals").directive("pfAboutModalTransclude",["$parse",function($parse){"use strict";return{link:function(scope,element,attrs){element.append($parse(attrs.pfAboutModalTransclude)(scope))}}}]).component("pfModalContent",{templateUrl:"about-modal-template.html",bindings:{resolve:"<",close:"&",dismiss:"&"},controller:function(){"use strict";var $ctrl=this;$ctrl.$onInit=function(){$ctrl.additionalInfo=$ctrl.resolve.additionalInfo,$ctrl.copyright=$ctrl.resolve.copyright,$ctrl.imgAlt=$ctrl.resolve.imgAlt,$ctrl.imgSrc=$ctrl.resolve.imgSrc,$ctrl.isOpen=$ctrl.resolve.isOpen,$ctrl.productInfo=$ctrl.resolve.productInfo,$ctrl.title=$ctrl.resolve.title,$ctrl.template=$ctrl.resolve.content}}}).component("pfAboutModal",{bindings:{additionalInfo:"=?",copyright:"=?",close:"&onClose",imgAlt:"=?",imgSrc:"=?",isOpen:"0&&item.children.forEach(function(secondaryItem){secondaryItem.isHover&&(hover=!0)})}),hover},updateSecondaryCollapsedState=function(setCollapsed,collapsedItem){var bodyContentElement=getBodyContentElement();collapsedItem&&(collapsedItem.secondaryCollapsed=setCollapsed),setCollapsed?(ctrl.collapsedSecondaryNav=!0,bodyContentElement.addClass("collapsed-secondary-nav-pf")):(ctrl.items&&ctrl.items.forEach(function(item){item.secondaryCollasped=!1}),ctrl.collapsedSecondaryNav=!1,bodyContentElement.removeClass("collapsed-secondary-nav-pf"))},updateTertiaryCollapsedState=function(setCollapsed,collapsedItem){var bodyContentElement=getBodyContentElement();collapsedItem&&(collapsedItem.tertiaryCollapsed=setCollapsed),setCollapsed?(ctrl.collapsedTertiaryNav=!0,bodyContentElement.addClass("collapsed-tertiary-nav-pf"),updateSecondaryCollapsedState(!1)):(ctrl.items&&ctrl.items.forEach(function(item){item.children&&item.children.length>0&&item.children.forEach(function(secondaryItem){secondaryItem.tertiaryCollasped=!1})}),ctrl.collapsedTertiaryNav=!1,bodyContentElement.removeClass("collapsed-tertiary-nav-pf"))};ctrl.showBadges="true"===ctrl.showBadges,ctrl.persistentSecondary="true"===ctrl.persistentSecondary,ctrl.pinnableMenus="true"===ctrl.pinnableMenus,ctrl.hiddenIcons="true"===ctrl.hiddenIcons,ctrl.updateActiveItemsOnClick="true"===ctrl.updateActiveItemsOnClick,ctrl.ignoreMobile="true"===ctrl.ignoreMobile,ctrl.activeSecondary=!1,ctrl.showMobileNav=!1,ctrl.showMobileSecondary=!1,ctrl.showMobileTertiary=!1,ctrl.hoverSecondaryNav=!1,ctrl.hoverTertiaryNav=!1,ctrl.collapsedSecondaryNav=!1,ctrl.collapsedTertiaryNav=!1,ctrl.navCollapsed=!1,ctrl.forceHidden=!1,ctrl.clearActiveItems=function(){ctrl.items.forEach(function(item){item.isActive=!1,item.children&&item.children.forEach(function(secondary){secondary.isActive=!1,secondary.children&&secondary.children.forEach(function(tertiary){tertiary.isActive=!1})})})},ctrl.setActiveItems=function(){var updatedRoute="#"+$location.path();ctrl.items.forEach(function(topLevel){updatedRoute.indexOf(topLevel.href)>-1&&(topLevel.isActive=!0),topLevel.children&&topLevel.children.forEach(function(secondLevel){updatedRoute.indexOf(secondLevel.href)>-1&&(secondLevel.isActive=!0,topLevel.isActive=!0),secondLevel.children&&secondLevel.children.forEach(function(thirdLevel){updatedRoute.indexOf(thirdLevel.href)>-1&&(thirdLevel.isActive=!0,secondLevel.isActive=!0,topLevel.isActive=!0)})})})},ctrl.handleNavBarToggleClick=function(){ctrl.inMobileState?ctrl.showMobileNav?ctrl.showMobileNav=!1:(updateMobileMenu(),ctrl.showMobileNav=!0):ctrl.navCollapsed?expandMenu():collapseMenu()},ctrl.handlePrimaryClick=function(item,event){ctrl.inMobileState?item.children&&item.children.length>0?updateMobileMenu(item):(updateMobileMenu(),navigateToItem(item)):navigateToItem(item)},ctrl.handleSecondaryClick=function(primary,secondary,event){ctrl.inMobileState?secondary.children&&secondary.children.length>0?updateMobileMenu(primary,secondary):(updateMobileMenu(),navigateToItem(secondary)):navigateToItem(secondary)},ctrl.handleTertiaryClick=function(primary,secondary,tertiary,event){ctrl.inMobileState&&updateMobileMenu(),navigateToItem(tertiary)},ctrl.handlePrimaryHover=function(item){item.children&&item.children.length>0&&(ctrl.inMobileState||(void 0!==item.navUnHoverTimeout?($timeout.cancel(item.navUnHoverTimeout),item.navUnHoverTimeout=void 0):void 0!==ctrl.navHoverTimeout||item.isHover||(item.navHoverTimeout=$timeout(function(){ctrl.hoverSecondaryNav=!0,item.isHover=!0,item.navHoverTimeout=void 0},hoverDelay))))},ctrl.handlePrimaryUnHover=function(item){item.children&&item.children.length>0&&(void 0!==item.navHoverTimeout?($timeout.cancel(item.navHoverTimeout),item.navHoverTimeout=void 0):void 0===item.navUnHoverTimeout&&item.isHover&&(item.navUnHoverTimeout=$timeout(function(){item.isHover=!1,primaryHover()||(ctrl.hoverSecondaryNav=!1),item.navUnHoverTimeout=void 0},hideDelay)))},ctrl.handleSecondaryHover=function(item){item.children&&item.children.length>0&&(ctrl.inMobileState||(void 0!==item.navUnHoverTimeout?($timeout.cancel(item.navUnHoverTimeout),item.navUnHoverTimeout=void 0):void 0===ctrl.navHoverTimeout&&(item.navHoverTimeout=$timeout(function(){ctrl.hoverTertiaryNav=!0,item.isHover=!0,item.navHoverTimeout=void 0},hoverDelay))))},ctrl.handleSecondaryUnHover=function(item){item.children&&item.children.length>0&&(void 0!==item.navHoverTimeout?($timeout.cancel(item.navHoverTimeout),item.navHoverTimeout=void 0):void 0===item.navUnHoverTimeout&&(item.navUnHoverTimeout=$timeout(function(){item.isHover=!1,secondaryHover()||(ctrl.hoverTertiaryNav=!1),item.navUnHoverTimeout=void 0},hideDelay)))},ctrl.collapseSecondaryNav=function(item,event){ctrl.inMobileState?updateMobileMenu():item.secondaryCollapsed?(updateSecondaryCollapsedState(!1,item),forceHideSecondaryMenu()):updateSecondaryCollapsedState(!0,item),ctrl.hoverSecondaryNav=!1,event.stopImmediatePropagation()},ctrl.collapseTertiaryNav=function(item,event){ctrl.inMobileState?ctrl.items.forEach(function(primaryItem){primaryItem.children&&primaryItem.children.forEach(function(secondaryItem){secondaryItem===item&&updateMobileMenu(primaryItem)})}):item.tertiaryCollapsed?(updateTertiaryCollapsedState(!1,item),forceHideSecondaryMenu()):updateTertiaryCollapsedState(!0,item),ctrl.hoverSecondaryNav=!1,ctrl.hoverTertiaryNav=!1,event.stopImmediatePropagation()},ctrl.$onInit=function(){$injector.has("$state")&&($state=$injector.get("$state")),ctrl.updateActiveItemsOnClick||$rootScope&&(routeChangeListener=$rootScope.$on("$routeChangeSuccess",function(event,next,current){ctrl.clearActiveItems(),ctrl.setActiveItems()})),initBodyElement(),checkNavState(),angular.element($window).on("resize",function(){checkNavState(),$scope.$digest()})},ctrl.$onDestroy=function(){_.isFunction(routeChangeListener)&&routeChangeListener()}}]}),angular.module("patternfly.notification").component("pfInlineNotification",{bindings:{pfNotificationType:"=",pfNotificationMessage:"=",pfNotificationHeader:"=",pfNotificationPersistent:"=",pfNotificationIndex:"=",pfNotificationRemove:"&?"},templateUrl:"notification/inline-notification.html"}),angular.module("patternfly.notification").component("pfNotificationDrawer",{bindings:{drawerHidden:"<",allowExpand:"=?",drawerExpanded:"=?",drawerTitle:"@",notificationGroups:"<",notificationTrackField:"@",onClose:"=?",showMarkAllRead:"0},ctrl.hasUnread=function(notificationGroup){return _.size(_.filter(_.get(notificationGroup,"notifications"),{unread:!0}))>0}}]}),angular.module("patternfly.notification").provider("Notifications",function(){"use strict";this.delay=8e3,this.verbose=!0,this.notifications={},this.notifications.data=[],this.persist={error:!0,httpError:!0},this.setDelay=function(delay){return this.delay=delay,this},this.setVerbose=function(verbose){return this.verbose=verbose,this},this.setPersist=function(persist){this.persist=persist},this.$get=["$timeout","$log",function($timeout,$log){function createNotifyMethod(mode){return function(message,header,persistent,closeCallback,actionTitle,actionCallback,menuActions){angular.isUndefined(header)&&(header=modes[mode].header),angular.isUndefined(persistent)&&(persistent=persist[mode]),notifications.message(modes[mode].type,header,message,persistent,closeCallback,actionTitle,actionCallback,menuActions),verbose&&$log[modes[mode].log](message)}}var delay=this.delay,notifications=this.notifications,verbose=this.verbose,persist=this.persist,modes={info:{type:"info",header:"Info!",log:"info"},success:{type:"success",header:"Success!",log:"info"},error:{type:"danger",header:"Error!",log:"error"},warn:{type:"warning",header:"Warning!",log:"warn"}};return notifications||(notifications.data=[]),notifications.message=function(type,header,message,isPersistent,closeCallback,actionTitle,actionCallback,menuActions){var notification={type:type,header:header,message:message,isPersistent:isPersistent,closeCallback:closeCallback,actionTitle:actionTitle,actionCallback:actionCallback,menuActions:menuActions};notification.show=!0,notifications.data.push(notification),notification.isPersistent||(notification.viewing=!1,$timeout(function(){notification.show=!1,notification.viewing||notifications.remove(notification)},delay))},angular.forEach(modes,function(mode,index){notifications[index]=createNotifyMethod(index)}),notifications.httpError=function(message,httpResponse){message+=" ("+(httpResponse.data.message||httpResponse.data.cause||httpResponse.data.cause||httpResponse.data.errorMessage)+")",notifications.message("danger","Error!",message,persist.httpError),verbose&&$log.error(message)},notifications.remove=function(notification){var index=notifications.data.indexOf(notification);-1!==index&¬ifications.removeIndex(index)},notifications.removeIndex=function(index){notifications.data.splice(index,1)},notifications.setViewing=function(notification,viewing){notification.viewing=viewing,viewing||notification.show||notifications.remove(notification)},notifications}]}),angular.module("patternfly.notification").component("pfNotificationList",{templateUrl:"notification/notification-list.html",controller:["Notifications",function(Notifications){"use strict";var ctrl=this;ctrl.$onInit=function(){ctrl.notifications=Notifications}}]}),angular.module("patternfly.notification").component("pfToastNotificationList",{bindings:{notifications:"=",showClose:"=?",htmlContent:"ctrl.lastPageNumber?ctrl.lastPageNumber:1>newPageNumber||isNaN(ctrl.pageNumber)?1:newPageNumber)},ctrl.gotoFirstPage=function(){1!==ctrl.pageNumber&&updatePageNumber(1)},ctrl.gotoPreviousPage=function(){1!==ctrl.pageNumber&&updatePageNumber(ctrl.pageNumber-1)},ctrl.gotoNextPage=function(){ctrl.pageNumber0&&(void 0===ctrl.config.currentField&&(ctrl.config.currentField=ctrl.config.fields[0],updated=!0),void 0===ctrl.config.isAscending&&(ctrl.config.isAscending=!0,updated=!0)),updated===!0&&ctrl.config.onSortChange&&ctrl.config.onSortChange(ctrl.config.currentField,ctrl.config.isAscending)}function selectField(evt,field){evt.preventDefault(),ctrl.config.currentField=field,ctrl.config.onSortChange&&ctrl.config.onSortChange(ctrl.config.currentField,ctrl.config.isAscending)}function changeDirection(){ctrl.config.isAscending=!ctrl.config.isAscending,ctrl.config.onSortChange&&ctrl.config.onSortChange(ctrl.config.currentField,ctrl.config.isAscending)}function getSortIconClass(){var iconClass;return iconClass="numeric"===ctrl.config.currentField.sortType?ctrl.config.isAscending?"fa fa-sort-numeric-asc":"fa fa-sort-numeric-desc":ctrl.config.isAscending?"fa fa-sort-alpha-asc":"fa fa-sort-alpha-desc"}var prevConfig,ctrl=this;ctrl.$onInit=function(){angular.isDefined(ctrl.config)&&angular.isUndefined(ctrl.config.show)&&(ctrl.config.show=!0),angular.extend(ctrl,{selectField:selectField,changeDirection:changeDirection,getSortIconClass:getSortIconClass})},ctrl.$onChanges=function(){setupConfig()},ctrl.$doCheck=function(){angular.equals(ctrl.config,prevConfig)||setupConfig()}}}),angular.module("patternfly.table").component("pfTableView",{bindings:{config:"0)for(actnBtns=1;actnBtns<=ctrl.actionButtons.length;actnBtns++)ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++).notSortable());ctrl.menuActions&&ctrl.menuActions.length>0&&ctrl.dtColumnDefs.push(DTColumnDefBuilder.newColumnDef(i++).notSortable())}function listenForDraw(){var oTable,dtInstance=ctrl.dtInstance;dtInstance&&dtInstance.dataTable&&(oTable=dtInstance.dataTable,angular.isDefined(ctrl.pageConfig)&&angular.isDefined(ctrl.pageConfig.pageNumber)&&oTable.fnPageChange(ctrl.pageConfig.pageNumber-1),ctrl.tableId=oTable[0].id,oTable.off("draw.dt"),oTable.on("draw.dt",function(){ctrl.debug&&$log.debug("--> redraw"),selectRowsByChecked()}))}function validSelectionMatchProp(){return void 0!==_.find(ctrl.columns,["itemField",ctrl.config.selectionMatchProp])}function getItemFromRow(matchPropValue){return _.find(ctrl.items,function(item){return _.toString(item[ctrl.config.selectionMatchProp])===_.toString(matchPropValue)})}function selectRowsByChecked(){ctrl.config.showCheckboxes&&$timeout(function(){var oTable,rows,checked;oTable=ctrl.dtInstance.DataTable,ctrl.debug&&$log.debug(" selectRowsByChecked"),angular.isUndefined(oTable)||(ctrl.debug&&$log.debug(" ...oTable defined"),rows=oTable.rows(),rows.deselect(),rows=oTable.rows(function(idx,data,node){return checked=node.children[0].children[0].checked}),ctrl.debug&&$log.debug(" ... #checkedRows = "+rows[0].length),rows[0].length>0&&rows.select(),setSelectAllCheckbox())})}function setSelectAllCheckbox(){var numVisibleRows,numCheckedRows;ctrl.debug&&$log.debug(" setSelectAllCheckbox"),numVisibleRows=getVisibleRows().length,numCheckedRows=document.querySelectorAll("#"+ctrl.tableId+" tbody tr.even.selected").length+document.querySelectorAll("#"+ctrl.tableId+" tbody tr.odd.selected").length,ctrl.selectAll=numVisibleRows===numCheckedRows}function getVisibleRows(){var i,rowData,visibleRows=[],anNodes=document.querySelectorAll("#"+ctrl.tableId+" tbody tr");for(i=0;ictrl.selectionMatchPropColNum&&visibleRows.push(_.trim(rowData[ctrl.selectionMatchPropColNum].innerText));return ctrl.debug&&$log.debug(" getVisibleRows ("+visibleRows.length+")"),visibleRows}function setDropMenuLocation(parentDiv){var dropButton=parentDiv.querySelector(".dropdown-toggle"),dropMenu=parentDiv.querySelector(".dropdown-menu"),parentRect=$element[0].getBoundingClientRect(),buttonRect=dropButton.getBoundingClientRect(),menuRect=dropMenu.getBoundingClientRect(),menuTop=buttonRect.top-menuRect.height,menuBottom=buttonRect.top+buttonRect.height+menuRect.height;menuBottom<=parentRect.top+parentRect.height||menuTop dtInstanceCallback"),ctrl.dtInstance=_dtInstance,listenForDraw(),selectRowsByChecked()},ctrl.$onChanges=function(changesObj){ctrl.debug&&$log.debug("$onChanges"),changesObj.config&&!changesObj.config.isFirstChange()&&(ctrl.debug&&$log.debug("...updateConfigOptions"),ctrl.updateConfigOptions()),changesObj.items&&changesObj.items.currentValue&&(ctrl.config.itemsAvailable=changesObj.items.currentValue.length>0)},ctrl.updatePageSize=function(event){ctrl.pageConfig.pageSize=event.pageSize,ctrl.dtOptions.displayLength=ctrl.pageConfig.pageSize,ctrl.pageConfig.pageNumber=1},ctrl.updatePageNumber=function(event){ctrl.dtInstance&&(ctrl.pageConfig.pageNumber=event.pageNumber,ctrl.dtInstance&&ctrl.dtInstance.dataTable&&ctrl.dtInstance.dataTable.fnPageChange(ctrl.pageConfig.pageNumber-1)); +},ctrl.$doCheck=function(){ctrl.debug&&$log.debug("$doCheck"),angular.equals(ctrl.dtOptions,prevDtOptions)&&angular.equals(ctrl.pageConfig,prevPageConfig)||(ctrl.debug&&$log.debug(" dtOptions !== prevDtOptions"),ctrl.updateConfigOptions()),angular.equals(ctrl.items,prevItems)||(ctrl.debug&&$log.debug(" items !== prevItems"),ctrl.items&&(ctrl.config.itemsAvailable=ctrl.items.length>0),angular.isDefined(ctrl.pageConfig)&&angular.isDefined(ctrl.pageConfig.numTotalItems)&&(ctrl.pageConfig.numTotalItems=ctrl.items.length),prevItems=angular.copy(ctrl.items))},ctrl.$postLink=function(){ctrl.debug&&$log.debug(" $postLink")},ctrl.$onDestroy=function(){ctrl.debug&&$log.debug(" $onDestroy"),ctrl.dtInstance={}},ctrl.toggleAll=function(){var item,visibleRows=getVisibleRows();angular.forEach(visibleRows,function(row){item=getItemFromRow(row),item.selected!==ctrl.selectAll&&(item.selected=ctrl.selectAll,ctrl.config&&ctrl.config.onCheckBoxChange&&ctrl.config.onCheckBoxChange(item))}),selectRowsByChecked()},ctrl.toggleOne=function(item){ctrl.config&&ctrl.config.onCheckBoxChange&&ctrl.config.onCheckBoxChange(item)},ctrl.handleButtonAction=function(action,item){action&&action.actionFn&&action.actionFn(action,item)},ctrl.handleColAction=function(key,value){var tableCol=$filter("filter")(ctrl.columns,{itemField:key});tableCol&&1===tableCol.length&&tableCol[0].hasOwnProperty("colActionFn")&&tableCol[0].colActionFn(value)},ctrl.areActions=function(){return ctrl.actionButtons&&ctrl.actionButtons.length>0||ctrl.menuActions&&ctrl.menuActions.length>0},ctrl.calcActionsColspan=function(){var colspan=0;return ctrl.actionButtons&&ctrl.actionButtons.length>0&&(colspan+=ctrl.actionButtons.length),ctrl.menuActions&&ctrl.menuActions.length>0&&(colspan+=1),colspan},ctrl.handleMenuAction=function(action,item){!ctrl.checkDisabled(item)&&action&&action.actionFn&&action.isDisabled!==!0&&action.actionFn(action,item)},ctrl.setupActions=function(item,event){$timeout(function(){var nextElement,parentDiv=void 0;for(nextElement=event.target;nextElement&&!parentDiv;)-1!==nextElement.className.indexOf("dropdown-kebab-pf")&&(parentDiv=nextElement,-1!==nextElement.className.indexOf("open")&&setDropMenuLocation(parentDiv)),nextElement=nextElement.parentElement})},ctrl.checkDisabled=function(){return!1},ctrl.trustAsHtml=function(html){return $sce.trustAsHtml(html)}}]}),angular.module("patternfly.toolbars").component("pfToolbar",{bindings:{config:"="},transclude:{actions:"?"},templateUrl:"toolbars/toolbar.html",controller:function(){"use strict";function setupConfig(){prevConfig=angular.copy(ctrl.config),ctrl.config&&ctrl.config.viewsConfig&&ctrl.config.viewsConfig.views&&(ctrl.config.viewsConfig.viewsList=angular.copy(ctrl.config.viewsConfig.views),ctrl.config.viewsConfig.currentView||(ctrl.config.viewsConfig.currentView=ctrl.config.viewsConfig.viewsList[0].id))}function viewSelected(viewId){ctrl.config.viewsConfig.currentView=viewId,ctrl.config.viewsConfig.onViewSelect&&!ctrl.checkViewDisabled(viewId)&&ctrl.config.viewsConfig.onViewSelect(viewId)}function isViewSelected(viewId){return ctrl.config.viewsConfig&&ctrl.config.viewsConfig.currentView===viewId}function isTableViewSelected(){return ctrl.config.viewsConfig?"tableView"===ctrl.config.viewsConfig.currentView:ctrl.config.isTableView}function checkViewDisabled(view){return ctrl.config.viewsConfig.checkViewDisabled&&ctrl.config.viewsConfig.checkViewDisabled(view)}function filterExists(filter){var foundFilter=_.find(ctrl.config.filterConfig.appliedFilters,{title:filter.title,value:filter.value});return void 0!==foundFilter}function enforceSingleSelect(filter){_.remove(ctrl.config.filterConfig.appliedFilters,{title:filter.title})}function addFilter(field,value){var newFilter={id:field.id,title:field.title,value:value};filterExists(newFilter)||("select"===field.filterType&&enforceSingleSelect(newFilter),ctrl.config.filterConfig.appliedFilters.push(newFilter),ctrl.config.filterConfig.onFilterChange&&ctrl.config.filterConfig.onFilterChange(ctrl.config.filterConfig.appliedFilters))}function handleAction(action){action&&action.actionFn&&action.isDisabled!==!0&&action.actionFn(action)}var prevConfig,ctrl=this;ctrl.$onInit=function(){angular.isDefined(ctrl.config.sortConfig)&&angular.isUndefined(ctrl.config.sortConfig.show)&&(ctrl.config.sortConfig.show=!0),angular.extend(ctrl,{viewSelected:viewSelected,isViewSelected:isViewSelected,isTableViewSelected:isTableViewSelected,checkViewDisabled:checkViewDisabled,addFilter:addFilter,handleAction:handleAction})},ctrl.$onChanges=function(){setupConfig()},ctrl.$doCheck=function(){angular.equals(ctrl.config,prevConfig)||setupConfig()}}}),angular.module("patternfly.utils").directive("pfFixedAccordion",["$window","$timeout",function($window,$timeout){"use strict";return{restrict:"A",scope:{scrollSelector:"@",groupHeight:"@",groupClass:"@"},link:function($scope,$element,$attrs){var contentElementHeight=function(contentElement){var contentHeight=contentElement.offsetHeight;return contentHeight+=parseInt(getComputedStyle(contentElement).marginTop),contentHeight+=parseInt(getComputedStyle(contentElement).marginBottom)},setBodyScrollHeight=function(parentElement,bodyHeight){var collapsePanel,scrollElement,panelContents,innerHeight,scroller,collapsePanels=parentElement[0].querySelectorAll(".panel-collapse");angular.forEach(collapsePanels,function(collapseElement){collapsePanel=angular.element(collapseElement),scrollElement=collapsePanel,innerHeight=0,angular.isDefined($scope.scrollSelector)&&(scroller=angular.element(collapsePanel[0].querySelector($scope.scrollSelector)),scroller.length&&(scrollElement=angular.element(scroller[0]),panelContents=collapsePanel.children(),angular.forEach(panelContents,function(contentElement){contentElement!==scrollElement[0]&&(innerHeight+=contentElementHeight(contentElement))}))),angular.element(scrollElement).css("max-height",Math.max(bodyHeight-innerHeight,25)+"px"),angular.element(scrollElement).css("overflow-y","auto")})},setCollapseHeights=function(){var height,openPanel,contentHeight,bodyHeight,overflowY="hidden",parentElement=angular.element($element[0].querySelector(".panel-group")),headings=angular.element(parentElement).children();height=parentElement[0].clientHeight,openPanel=parentElement[0].querySelectorAll(".collapse.in"),openPanel&&openPanel.length>0&&angular.element(openPanel).removeClass("in"),contentHeight=0,angular.forEach(headings,function(heading){contentHeight+=contentElementHeight(heading)}),bodyHeight=height-contentHeight,25>bodyHeight&&(bodyHeight=25,overflowY="auto"),openPanel&&openPanel.length>0&&angular.element(openPanel).addClass("in"),angular.element(parentElement).css("overflow-y",overflowY),$timeout(function(){setBodyScrollHeight(parentElement,bodyHeight)})},debounceResize=_.debounce(setCollapseHeights,150,{maxWait:250});$scope.groupHeight&&angular.element($element[0].querySelector(".panel-group")).css("height",$scope.groupHeight),$scope.groupClass&&angular.element($element[0].querySelector(".panel-group")).addClass($scope.groupClass),$timeout(function(){setCollapseHeights()},100),$element.on("resize",function(){debounceResize()}),angular.element($window).on("resize",function(){debounceResize()})}}}]),angular.module("patternfly.utils").directive("pfTransclude",function(){"use strict";return{restrict:"A",link:function($scope,$element,$attrs,controller,$transclude){var iChildScope,iScopeType;if(!$transclude)throw new Error("pfTransclude - Illegal use of pfTransclude directive in the template! No parent directive that requires a transclusion found. Element: {0}");switch(iScopeType=$attrs.pfTransclude||"sibling"){case"sibling":$transclude(function(clone){$element.empty(),$element.append(clone)});break;case"parent":$transclude($scope,function(clone){$element.empty(),$element.append(clone)});break;case"child":iChildScope=$scope.$new(),$transclude(iChildScope,function(clone){$element.empty(),$element.append(clone),$element.on("$destroy",function(){iChildScope.$destroy()})})}}}}),function(){"use strict";angular.module("patternfly.utils").constant("pfUtils",{merge:function(source1,source2){var retValue;return retValue="function"==typeof angular.merge?this.angularMerge(source1,source2):"function"==typeof _.merge?this._merge(source1,source2):"function"==typeof $.extend?this.$extend(source1,source2):this.mergeDeep(source1,source2)},angularMerge:function(source1,source2){return angular.merge({},source1,source2)},_merge:function(source1,source2){return _.merge({},source1,source2)},$extend:function(source1,source2){return $.extend(!0,angular.copy(source1),source2)},mergeDeep:function(source1,source2){return mergeDeep({},angular.copy(source1),angular.copy(source2))},utilizationToColor:function(utilization){return utilizationToColor(utilization)},colorPalette:patternfly.pfPaletteColors})}(),angular.module("patternfly.validation",[]).directive("pfValidation",["$timeout",function($timeout){"use strict";return{restrict:"A",require:"ngModel",scope:{pfValidation:"&",pfValidationDisabled:"="},link:function(scope,element,attrs,ctrl){function validate(){var valid,val=scope.inputCtrl.$modelValue,valFunc=scope.pfValidation({input:val});attrs.pfValidation||(valFunc=!0),valid=!val||valFunc||""===val,toggleErrorClass(scope.valEnabled&&!valid?!0:!1)}function toggleErrorClass(add){var messageElement=element.next(),parentElement=element.parent(),hasErrorM=parentElement.hasClass("has-error"),wasHidden=messageElement.hasClass("ng-hide");scope.inputCtrl.$setValidity("pf-validation",!add),add&&(hasErrorM||parentElement.addClass("has-error"),wasHidden&&messageElement.removeClass("ng-hide")),add||(hasErrorM&&parentElement.removeClass("has-error"),wasHidden||messageElement.addClass("ng-hide"))}scope.inputCtrl=ctrl,scope.valEnabled=!attrs.pfValidationDisabled,scope.$watch("pfValidationDisabled",function(newVal){scope.valEnabled=!newVal,newVal?(scope.inputCtrl.$setValidity("pfValidation",!0),toggleErrorClass(!1)):validate()}),attrs.pfValidation?$timeout(function(){validate()},0):!scope.inputCtrl.$valid&&scope.inputCtrl.$dirty&&toggleErrorClass(!0),scope.$watch("inputCtrl.$valid",function(isValid){toggleErrorClass(isValid?!1:!0)}),scope.$watch("inputCtrl.$modelValue",function(){validate()})}}}]),angular.module("patternfly.views").component("pfCardView",{bindings:{config:"=?",pageConfig:"=?",emptyStateConfig:"=?",emptyStateActionButtons:"=?",items:"=",eventId:"@id"},transclude:!0,templateUrl:"views/cardview/card-view.html",controller:function(){"use strict";function setPagination(){angular.isUndefined(ctrl.pageConfig)?ctrl.pageConfig={pageNumber:1,pageSize:ctrl.items.length,numTotalItems:ctrl.items.length,showPaginationControls:!1}:(angular.isUndefined(ctrl.pageConfig.showPaginationControls)&&(ctrl.pageConfig.showPaginationControls=!0),angular.isNumber(ctrl.pageConfig.pageNumber)||(ctrl.pageConfig.pageNumber=1),angular.isNumber(ctrl.pageConfig.pageSize)||(ctrl.pageConfig.pageSize=10),angular.isNumber(ctrl.pageConfig.numTotalItems)||(ctrl.pageConfig.numTotalItems=ctrl.items.length),ctrl.pageConfig.showPaginationControls||(ctrl.pageConfig.pageSize=ctrl.pageConfig.numTotalItems)),prevPageConfig=angular.copy(ctrl.pageConfig)}var prevPageConfig,prevItems,ctrl=this;ctrl.defaultConfig={selectItems:!1,multiSelect:!1,dblClick:!1,selectionMatchProp:"uuid",selectedItems:[],checkDisabled:!1,showSelectBox:!0,onSelect:null,onSelectionChange:null,onCheckBoxChange:null,onClick:null,onDblClick:null,itemsAvailable:!0},ctrl.itemClick=function(e,item){var alreadySelected,selectionChanged=!1,continueEvent=!0;return ctrl.checkDisabled(item)?continueEvent:(ctrl.config&&ctrl.config.selectItems&&item&&(ctrl.config.multiSelect&&!ctrl.config.dblClick?(alreadySelected=_.find(ctrl.config.selectedItems,function(itemObj){return itemObj===item}),alreadySelected?ctrl.config.selectedItems=_.without(ctrl.config.selectedItems,item):(ctrl.config.selectedItems.push(item),selectionChanged=!0)):ctrl.config.selectedItems[0]===item?(ctrl.config.dblClick||(ctrl.config.selectedItems=[],selectionChanged=!0),continueEvent=!1):(ctrl.config.selectedItems=[item],selectionChanged=!0),selectionChanged&&ctrl.config.onSelect&&ctrl.config.onSelect(item,e),selectionChanged&&ctrl.config.onSelectionChange&&ctrl.config.onSelectionChange(ctrl.config.selectedItems,e)),ctrl.config.onClick&&ctrl.config.onClick(item,e),continueEvent)},ctrl.dblClick=function(e,item){ctrl.config.onDblClick&&ctrl.config.onDblClick(item,e)},ctrl.checkBoxChange=function(item){ctrl.config.onCheckBoxChange&&ctrl.config.onCheckBoxChange(item)},ctrl.isSelected=function(item){var matchProp=ctrl.config.selectionMatchProp,selected=!1;if(ctrl.config.showSelectBox)selected=item.selected;else if(ctrl.config.selectedItems.length)return _.find(ctrl.config.selectedItems,function(itemObj){return itemObj[matchProp]===item[matchProp]});return selected},ctrl.checkDisabled=function(item){return ctrl.config.checkDisabled&&ctrl.config.checkDisabled(item)},ctrl.$onInit=function(){if(_.defaults(ctrl.config,ctrl.defaultConfig),ctrl.config.selectItems&&ctrl.config.showSelectBox)throw new Error("pfCardView - Illegal use of pfCardView component! Cannot allow both select box and click selection in the same card view.");prevItems=angular.copy(ctrl.items),setPagination()},ctrl.$doCheck=function(){angular.equals(ctrl.pageConfig,prevPageConfig)||setPagination(),angular.equals(ctrl.items,prevItems)||(ctrl.items&&(ctrl.config.itemsAvailable=ctrl.items.length>0),angular.isDefined(ctrl.pageConfig)&&(ctrl.pageConfig.numTotalItems=ctrl.items.length),prevItems=angular.copy(ctrl.items))}}}),angular.module("patternfly.views").component("pfEmptyState",{bindings:{config:"menuTop?ctrl.dropdownClass="dropdown":ctrl.dropdownClass="dropup",ctrl.kebabMenuReady=!0};ctrl.defaultConfig={selectItems:!1,multiSelect:!1,dblClick:!1,dragEnabled:!1,dragEnd:null,dragMoved:null,dragStart:null,selectionMatchProp:"uuid",selectedItems:[],checkDisabled:!1,useExpandingRows:!1,showSelectBox:!0,onSelect:null,onSelectionChange:null,onCheckBoxChange:null,onClick:null,onDblClick:null,itemsAvailable:!0},ctrl.dropdownClass="dropdown",ctrl.handleButtonAction=function(action,item){!ctrl.checkDisabled(item)&&action&&action.actionFn&&ctrl.enableButtonForItem(action,item)&&action.actionFn(action,item)},ctrl.handleMenuAction=function(action,item){!ctrl.checkDisabled(item)&&action&&action.actionFn&&action.isDisabled!==!0&&action.actionFn(action,item)},ctrl.enableButtonForItem=function(action,item){var enable=!0;return"function"==typeof ctrl.enableButtonForItemFn?ctrl.enableButtonForItemFn(action,item):enable},ctrl.updateActions=function(item){"function"==typeof ctrl.updateMenuActionForItemFn&&ctrl.menuActions.forEach(function(action){ctrl.updateMenuActionForItemFn(action,item)})},ctrl.getMenuClassForItem=function(item){var menuClass="";return angular.isFunction(ctrl.menuClassForItemFn)&&(menuClass=ctrl.menuClassForItemFn(item)),menuClass},ctrl.hideMenuForItem=function(item){var hideMenu=!1;return angular.isFunction(ctrl.hideMenuForItemFn)&&(hideMenu=ctrl.hideMenuForItemFn(item)),hideMenu},ctrl.toggleItemExpansion=function(item){item.isExpanded=!item.isExpanded},ctrl.setupActions=function(item,event){ctrl.checkDisabled(item)||(ctrl.updateActions(item),ctrl.kebabMenuReady=!1,$timeout(function(){var nextElement,parentDiv=void 0;for(nextElement=event.target;nextElement&&!parentDiv;)-1!==nextElement.className.indexOf("dropdown-kebab-pf")&&(parentDiv=nextElement,-1!==nextElement.className.indexOf("open")&&setDropMenuLocation(parentDiv)),nextElement=nextElement.parentElement}))},ctrl.itemClick=function(e,item){var alreadySelected,selectionChanged=!1,continueEvent=!0,enableRowExpansion=ctrl.config&&ctrl.config.useExpandingRows&&!ctrl.config.compoundExpansionOnly&&item&&!item.disableRowExpansion;return ctrl.checkDisabled(item)?continueEvent:(ctrl.config&&ctrl.config.selectItems&&item&&(ctrl.config.multiSelect&&!ctrl.config.dblClick?(alreadySelected=_.find(ctrl.config.selectedItems,function(itemObj){return itemObj===item}),alreadySelected?ctrl.config.selectedItems=_.without(ctrl.config.selectedItems,item):(ctrl.config.selectedItems.push(item),selectionChanged=!0)):ctrl.config.selectedItems[0]===item?(ctrl.config.dblClick||(ctrl.config.selectedItems=[],selectionChanged=!0),continueEvent=!1):(ctrl.config.selectedItems=[item],selectionChanged=!0),selectionChanged&&ctrl.config.onSelect&&ctrl.config.onSelect(item,e),selectionChanged&&ctrl.config.onSelectionChange&&ctrl.config.onSelectionChange(ctrl.config.selectedItems,e)),ctrl.config.onClick?ctrl.config.onClick(item,e)!==!1&&enableRowExpansion&&ctrl.toggleItemExpansion(item):enableRowExpansion&&ctrl.toggleItemExpansion(item),continueEvent)},ctrl.dblClick=function(e,item){return ctrl.checkDisabled(item)?continueEvent:void(ctrl.config.onDblClick&&ctrl.config.onDblClick(item,e))},ctrl.checkBoxChange=function(item){ctrl.config.onCheckBoxChange&&ctrl.config.onCheckBoxChange(item)},ctrl.isSelected=function(item){var matchProp=ctrl.config.selectionMatchProp,selected=!1;return ctrl.config.showSelectBox?selected=item.selected:ctrl.config.selectItems&&ctrl.config.selectedItems.length&&(selected=_.find(ctrl.config.selectedItems,function(itemObj){return itemObj[matchProp]===item[matchProp]})),selected},ctrl.checkDisabled=function(item){return ctrl.config.checkDisabled&&ctrl.config.checkDisabled(item)},ctrl.$onInit=function(){if(angular.isUndefined(ctrl.config)&&(ctrl.config={}),_.defaults(ctrl.config,ctrl.defaultConfig),ctrl.config.selectItems||(ctrl.config.selectedItems=[]),!ctrl.config.multiSelect&&ctrl.config.selectedItems&&ctrl.config.selectedItems.length>0&&(ctrl.config.selectedItems=[ctrl.config.selectedItems[0]]),ctrl.config.selectItems&&ctrl.config.showSelectBox)throw new Error("pfListView - Illegal use of pListView component! Cannot allow both select box and click selection in the same list view.");prevItems=angular.copy(ctrl.items),setPagination()},ctrl.$doCheck=function(){angular.equals(ctrl.pageConfig,prevPageConfig)||setPagination(),angular.equals(ctrl.items,prevItems)||(ctrl.items&&(ctrl.config.itemsAvailable=ctrl.items.length>0),angular.isDefined(ctrl.pageConfig)&&(ctrl.pageConfig.numTotalItems=ctrl.items.length),prevItems=angular.copy(ctrl.items))},ctrl.dragEnd=function(){angular.isFunction(ctrl.config.dragEnd)&&ctrl.config.dragEnd()},ctrl.dragMoved=function(){angular.isFunction(ctrl.config.dragMoved)&&ctrl.config.dragMoved()},ctrl.isDragOriginal=function(item){return item===ctrl.dragItem},ctrl.dragStart=function(item){ctrl.dragItem=item,angular.isFunction(ctrl.config.dragStart)&&ctrl.config.dragStart(item)}}]}),function(){"use strict";angular.module("patternfly.views").constant("pfViewUtils",{getDashboardView:function(title){return{id:"dashboardView",title:title||"Dashboard View",iconClass:"fa fa-dashboard"}},getCardView:function(title){return{id:"cardView",title:title||"Card View",iconClass:"fa fa-th"}},getListView:function(title){return{id:"listView",title:title||"List View",iconClass:"fa fa-th-list"}},getTableView:function(title){return{id:"tableView",title:title||"Table View",iconClass:"fa fa-table"}},getTopologyView:function(title){return{id:"topologyView",title:title||"Topology View",iconClass:"fa fa-sitemap"}}})}(),function(){"use strict";var findWizard=function(scope){var wizard;return scope&&(wizard=angular.isDefined(scope.wizard)?scope.wizard:findWizard(scope.$parent)),wizard},setupCallback=function(scope,button,fnName,callback){button.on("click",function(e){e.preventDefault(),scope.$apply(function(){scope.wizard[fnName](callback)})})};angular.module("patternfly.wizard").component("pfWizNext",{bindings:{callback:"=?"},controller:["$element","$scope",function($element,$scope){var ctrl=this;ctrl.$onInit=function(){$scope.wizard=findWizard($scope)},ctrl.$postLink=function(){setupCallback($scope,$element,"next",ctrl.callback)}}]}),angular.module("patternfly.wizard").component("pfWizPrevious",{bindings:{callback:"=?"},controller:["$element","$scope",function($element,$scope){var ctrl=this;ctrl.$onInit=function(){$scope.wizard=findWizard($scope)},ctrl.$postLink=function(){setupCallback($scope,$element,"previous",ctrl.callback)}}]}),angular.module("patternfly.wizard").component("pfWizFinish",{bindings:{callback:"=?"},controller:["$element","$scope",function($element,$scope){var ctrl=this;ctrl.$onInit=function(){$scope.wizard=findWizard($scope)},ctrl.$postLink=function(){setupCallback($scope,$element,"finish",ctrl.callback)}}]}),angular.module("patternfly.wizard").component("pfWizCancel",{bindings:{callback:"=?"},controller:["$element","$scope",function($element,$scope){var ctrl=this;ctrl.$onInit=function(){$scope.wizard=findWizard($scope)},ctrl.$postLink=function(){setupCallback($scope,$element,"cancel",ctrl.callback)}}]}),angular.module("patternfly.wizard").component("pfWizReset",{bindings:{callback:"=?"},controller:["$element","$scope",function($element,$scope){var ctrl=this;ctrl.$onInit=function(){$scope.wizard=findWizard($scope)},ctrl.$postLink=function(){setupCallback($scope,$element,"reset",ctrl.callback)}}]})}(),angular.module("patternfly.wizard").component("pfWizardReviewPage",{bindings:{shown:"<",wizardData:"<"},templateUrl:"wizard/wizard-review-page.html",controller:["$scope",function($scope){"use strict";var ctrl=this,findWizard=function(scope){var wizard;return scope&&(wizard=angular.isDefined(scope.wizard)?scope.wizard:findWizard(scope.$parent)),wizard};ctrl.$onInit=function(){ctrl.reviewSteps=[],ctrl.wizard=findWizard($scope.$parent)},ctrl.$onChanges=function(changesObj){changesObj.shown&&changesObj.shown.currentValue&&ctrl.updateReviewSteps()},ctrl.toggleShowReviewDetails=function(step){step.showReviewDetails===!0?step.showReviewDetails=!1:step.showReviewDetails=!0},ctrl.getSubStepNumber=function(step,substep){return step.getStepDisplayNumber(substep)},ctrl.getReviewSubSteps=function(reviewStep){return reviewStep.getReviewSteps()},ctrl.updateReviewSteps=function(){ctrl.reviewSteps=ctrl.wizard.getReviewSteps()}}]}),angular.module("patternfly.wizard").component("pfWizardStep",{transclude:!0,bindings:{stepTitle:"@",stepId:"@",stepPriority:"@",substeps:"=?",nextEnabled:"stepIndex&&(complete=complete&&step.nextEnabled)}),complete},ctrl.goTo=function(step){var focusElement=null;!ctrl.wizard.isWizardDone()&&step.okToNavAway&&step!==ctrl.selectedStep&&(firstRun||ctrl.getStepNumber(step)step.stepPriority});insertBefore?ctrl.steps.splice(ctrl.steps.indexOf(insertBefore),0,step):ctrl.steps.push(step)},ctrl.currentStepTitle=function(){return ctrl.selectedStep.stepTitle},ctrl.currentStepDescription=function(){return ctrl.selectedStep.description},ctrl.currentStep=function(){return ctrl.selectedStep},ctrl.totalStepCount=function(){return ctrl.getEnabledSteps().length},ctrl.next=function(callback){var enabledSteps=ctrl.getEnabledSteps(),index=stepIdx(ctrl.selectedStep);return angular.isFunction(callback)?callback(ctrl.selectedStep)?index===enabledSteps.length-1?!1:(ctrl.goTo(enabledSteps[index+1]),!0):!0:(ctrl.selectedStep.completed=!0,index===enabledSteps.length-1?!1:(ctrl.goTo(enabledSteps[index+1]),!0))},ctrl.previous=function(callback){var index=stepIdx(ctrl.selectedStep),goPrev=!1;return(!angular.isFunction(callback)||callback(ctrl.selectedStep))&&0!==index&&(ctrl.goTo(ctrl.getEnabledSteps()[index-1]),goPrev=!0),goPrev}}]}),angular.module("patternfly.wizard").component("pfWizardSubstep",{transclude:!0,bindings:{stepTitle:"@",stepId:"@",stepPriority:"@",nextEnabled:"")},ctrl.$onChanges=function(changesObj){var step;if(changesObj.hideHeader&&(ctrl.hideHeader="true"===ctrl.hideHeader),changesObj.hideSidebar&&(ctrl.hideSidebar="true"===ctrl.hideSidebar),changesObj.hideBackButton&&(ctrl.hideBackButton="true"===ctrl.hideBackButton),changesObj.wizardReady&&changesObj.wizardReady.currentValue&&ctrl.goTo(ctrl.getEnabledSteps()[0]),changesObj.currentStep){if(step=changesObj.currentStep.currentValue,!step)return;ctrl.selectedStep&&ctrl.selectedStep.title!==step&&ctrl.goTo(stepByTitle(step))}},ctrl.getEnabledSteps=function(){return ctrl.steps.filter(function(step){return"true"!==step.disabled})},ctrl.getReviewSteps=function(){return ctrl.steps.filter(function(step){return!step.disabled&&(!angular.isUndefined(step.reviewTemplate)||step.getReviewSteps().length>0)})},ctrl.currentStepNumber=function(){return stepIdx(ctrl.selectedStep)+1},ctrl.getStepNumber=function(step){return stepIdx(step)+1},ctrl.goTo=function(step,resetStepNav){var focusElement=null;ctrl.wizardDone||ctrl.selectedStep&&!ctrl.selectedStep.okToNavAway||step===ctrl.selectedStep||((firstRun||ctrl.getStepNumber(step)ctrl.selectedStep.stepPriority)},ctrl.stepClick=function(step){step.allowClickNav&&ctrl.selectedStep&&!ctrl.wizardDone&&ctrl.selectedStep.okToNavAway&&(ctrl.selectedStep.nextEnabled||step.stepPriorityctrl.selectedStep.stepPriority)&&ctrl.goTo(step,!0)},ctrl.setPageSelected=function(step){angular.isFunction(ctrl.onStepChanged)&&ctrl.onStepChanged({step:step,index:stepIdx(step)})},ctrl.addStep=function(step){var insertBefore=_.find(ctrl.steps,function(nextStep){return nextStep.stepPriority>step.stepPriority});insertBefore?ctrl.steps.splice(ctrl.steps.indexOf(insertBefore),0,step):ctrl.steps.push(step),ctrl.wizardReady&&ctrl.getEnabledSteps().length>0&&step===ctrl.getEnabledSteps()[0]&&ctrl.goTo(ctrl.getEnabledSteps()[0])},ctrl.isWizardDone=function(){return ctrl.wizardDone},ctrl.updateSubStepNumber=function(value){ctrl.firstStep=0===stepIdx(ctrl.selectedStep)&&0===value},ctrl.currentStepTitle=function(){return ctrl.selectedStep.title},ctrl.currentStepDescription=function(){return ctrl.selectedStep.description},ctrl.currentStep=function(){return ctrl.selectedStep},ctrl.totalStepCount=function(){return ctrl.getEnabledSteps().length},ctrl.goToStep=function(step,resetStepNav){var stepTo,enabledSteps=ctrl.getEnabledSteps();stepTo=angular.isNumber(step)?enabledSteps[step]:stepByTitle(step),ctrl.goTo(stepTo,resetStepNav)},ctrl.next=function(callback){var enabledSteps=ctrl.getEnabledSteps(),index=stepIdx(ctrl.selectedStep);if(callback=callback||ctrl.nextCallback,!ctrl.selectedStep.substeps||!ctrl.selectedStep.next(callback)){if(angular.isFunction(callback)){if(!callback(ctrl.selectedStep))return;index
Hide Connectors
{{tab.preTitle}}
{{tab.title}}
'),$templateCache.put("canvas-view/canvas-editor/toolbox-items.html",'
  • {{item.name}} {{ item.name }}
'),$templateCache.put("canvas-view/canvas/canvas.html",'Select a second item to complete the connection or click on the canvas to cancelNo available connections! Click on the canvas to cancel{{node.name()}}

{{node.name()}}

{{node.fontContent()}}{{\'\\ue918\'}}{{connector.fontContent()}}{{connector.name()}}
{{connection.name()}}
'),$templateCache.put("canvas-view/canvas/node-toolbar.html",'
')}]),angular.module("patternfly.card").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("card/aggregate-status/aggregate-status-card.html",'

{{$ctrl.status.count}} {{$ctrl.status.title}} {{$ctrl.status.count}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}

{{ notification.count }} {{ notification.count }}

{{$ctrl.status.count}} {{$ctrl.status.title}} {{$ctrl.status.count}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}

{{$ctrl.status.notification.count}} {{$ctrl.status.notification.count}}

'),$templateCache.put("card/basic/card-filter.html",'
'),$templateCache.put("card/basic/card.html",'

{{$ctrl.headTitle}}

{{$ctrl.subTitle}}
{{$ctrl.spinnerText}}
'),$templateCache.put("card/info-status/info-status-card.html",'

{{$ctrl.status.title}} {{$ctrl.status.title}}

{{$ctrl.spinnerText}}
')}]),angular.module("patternfly.charts").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("charts/donut/donut-chart.html",''),$templateCache.put("charts/donut/donut-pct-chart.html",' {{$ctrl.config.labelConfig.title}} {{$ctrl.data.available}} {{$ctrl.config.labelConfig.units}} available {{$ctrl.data.percent}}% used {{$ctrl.data.used}} {{$ctrl.config.labelConfig.units}} of {{$ctrl.data.total}} {{$ctrl.config.labelConfig.units}} used '),$templateCache.put("charts/empty-chart.html",'
No data available
'),$templateCache.put("charts/heatmap/heatmap-legend.html",'
  • {{item.text}}
'),$templateCache.put("charts/heatmap/heatmap.html",'

{{$ctrl.chartTitle}}

'), +$templateCache.put("charts/line/line-chart.html",''),$templateCache.put("charts/sparkline/sparkline-chart.html",''),$templateCache.put("charts/topology-map/topology-map.html",'
'),$templateCache.put("charts/trends/trends-chart.html",'
{{$ctrl.config.title}} {{$ctrl.getLatestValue()}} {{$ctrl.config.units}} {{$ctrl.getPercentageValue() + \'%\'}} of {{$ctrl.chartData.total + \' \' + $ctrl.config.units}}{{$ctrl.config.timeFrame}}
{{$ctrl.getLatestValue()}} {{$ctrl.config.units}} {{$ctrl.getPercentageValue() + \'%\'}} of {{$ctrl.chartData.total + \' \' + $ctrl.config.units}} {{$ctrl.config.title}}
{{$ctrl.getPercentageValue() + \'%\'}}
{{$ctrl.config.trendLabel}} {{$ctrl.getLatestValue()}} of {{$ctrl.chartData.total + \' \' + $ctrl.config.units}}
'),$templateCache.put("charts/utilization-bar/utilization-bar-chart.html","
{{$ctrl.chartTitle}}
{{$ctrl.chartData.used}} of {{$ctrl.chartData.total}} {{$ctrl.units}} Used {{$ctrl.chartData.percentageUsed}}% Used
{{$ctrl.chartTitle}}
{{$ctrl.chartData.used}} {{$ctrl.units}} Used {{$ctrl.chartData.percentageUsed}}% Used
"),$templateCache.put("charts/utilization-trend/utilization-trend-chart.html",'

{{$ctrl.config.title}}

{{$ctrl.currentValue}}

{{$ctrl.currentText}}
of {{$ctrl.chartData.total}} {{$ctrl.config.units}}
{{$ctrl.legendLeftText}} {{$ctrl.legendRightText}}
')}]),angular.module("patternfly.datepicker").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("datepicker/datepicker.html",'

')}]),angular.module("patternfly.filters").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("filters/filter-panel/filter-panel-results.html",'
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.resultsLabel === undefined ? "Results" : $ctrl.config.resultsLabel}}

Active filters:

  • {{filter.title}}:
    • {{value}}

Clear All Filters

'),$templateCache.put("filters/filter-panel/filter-panel.html",'
'),$templateCache.put("filters/simple-filter/filter-fields.html",'
'),$templateCache.put("filters/simple-filter/filter-results.html",'
{{$ctrl.config.resultsCount}} {{$ctrl.config.itemsLabel}}
{{$ctrl.config.resultsCount}} {{$ctrl.config.itemsLabelPlural}}
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.itemsLabel}}
{{$ctrl.config.resultsCount}} of {{$ctrl.config.totalCount}} {{$ctrl.config.itemsLabelPlural}}

Active Filters:

  • {{filter.title}}: {{((filter.value.filterCategory.title || filter.value.filterCategory) + filter.value.filterDelimiter + (filter.value.filterValue.title || filter.value.filterValue)) || filter.value.title || filter.value}}

Clear All Filters

{{$ctrl.config.selectedCount}} of {{$ctrl.config.totalCount}} selected
'),$templateCache.put("filters/simple-filter/filter.html","
")}]),angular.module("patternfly.form").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("form/form-buttons/form-buttons.html",'
'),$templateCache.put("form/form-group/form-group.html",'
  • {{ message }}
')}]),angular.module("patternfly.modals").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("modals/about-modal/about-modal.html",''),$templateCache.put("modals/modal-overlay/modal-overlay.html",'')}]),angular.module("patternfly.navigation").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("navigation/application-launcher.html",''),$templateCache.put("navigation/vertical-navigation.html","
')}]),angular.module("patternfly.notification").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("notification/inline-notification.html",'
{{$ctrl.pfNotificationHeader}} {{$ctrl.pfNotificationMessage}}
'),$templateCache.put("notification/notification-drawer.html",'

{{$ctrl.drawerTitle}}

Loading More
'),$templateCache.put("notification/notification-list.html",'
'),$templateCache.put("notification/toast-notification-list.html",'
'), +$templateCache.put("notification/toast-notification.html",'')}]),angular.module("patternfly.pagination").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("pagination/pagination.html",'
per page
{{$ctrl.getStartIndex()}}-{{$ctrl.getEndIndex()}} of {{$ctrl.numTotalItems}}
of {{$ctrl.lastPageNumber}}
')}]),angular.module("patternfly.select").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("select/select.html",'
')}]),angular.module("patternfly.sort").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("sort/sort.html",'
')}]),angular.module("patternfly.table").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("table/tableview/table-view.html",'
{{col.header}}Actions
{{value}}
')}]),angular.module("patternfly.toolbars").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("toolbars/toolbar.html",'
')}]),angular.module("patternfly.views").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("views/cardview/card-view.html",'
'),$templateCache.put("views/empty-state.html",'

{{$ctrl.config.title}}

{{$ctrl.config.info}}

'),$templateCache.put("views/listview/examples/clusters-content.html","
Clusters for {{$ctrl.item.name}}
  • Cluster 1
  • Cluster 2
  • Cluster 3
  • Cluster 4
  • Cluster 5
  • Cluster 6
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

"),$templateCache.put("views/listview/examples/hosts-content.html","
Hosts for {{$ctrl.item.name}}
  • Host 1
  • Host 2
  • Host 3
  • Host 4
  • Host 5
  • Host 6
  • Host 7
  • Host 8
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

"),$templateCache.put("views/listview/examples/images-content.html","
Images for {{$ctrl.item.name}}
  • Image 1
  • Image 2
  • Image 3
  • Image 4
  • Image 5
  • Image 6
  • Image 7
  • Image 8
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

"),$templateCache.put("views/listview/examples/nodes-content.html","
Nodes for {{$ctrl.item.name}}
  • Node 1
  • Node 2
  • Node 3
  • Node 4
  • Node 5
  • Node 6
  • Node 7
  • Node 8
  • Node 9
  • Node 10
Host Name
file1.nay.redhat.com
Device Path
/dev/disk/pci-0000.05:00-sas-0.2-part1
Time
January 15, 2016 10:45:11 AM
Severity
Warning
Cluster
Cluster 1

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

"),$templateCache.put("views/listview/list-view.html",'
')}]),angular.module("patternfly.wizard").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("wizard/wizard-review-page.html",''),$templateCache.put("wizard/wizard-step.html",'
'),$templateCache.put("wizard/wizard-substep.html",''),$templateCache.put("wizard/wizard.html",'
')}]); \ No newline at end of file diff --git a/dist/imgs/canvas-dot-grid.png b/dist/imgs/canvas-dot-grid.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd2a6857ee65cfd2c0f5183a187d58d15e587fd GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9+MX_sAr`&Ke|~+{W;SII4)u{V x&A754u-T1Q$~416@`%$;!Q*|h$qN>BGces{P5!sMvK*+5!PC{xWt~$(69AT592Ecn literal 0 HcmV?d00001 diff --git a/dist/less/angular-patternfly.less b/dist/less/angular-patternfly.less new file mode 100644 index 000000000..0bea83991 --- /dev/null +++ b/dist/less/angular-patternfly.less @@ -0,0 +1,19 @@ +@import "dependencies/patternfly/color-variables.less"; +@import "../node_modules/bootstrap/less/variables.less"; +@import "misc.less"; +@import "card.less"; +@import "charts.less"; +@import "views.less"; +@import "toolbars.less"; +@import "filters.less"; +@import "sort.less"; +@import "notification.less"; +@import "list-view.less"; +@import "table.less"; +@import "wizard.less"; +@import "canvas.less"; +@import "canvas-editor.less"; +@import "pagination.less"; +@import "datepicker.less"; +@import "modal-overlay.less"; +@import "vertical-navigation.less"; diff --git a/dist/less/canvas-editor.less b/dist/less/canvas-editor.less new file mode 100644 index 000000000..71ff3d1be --- /dev/null +++ b/dist/less/canvas-editor.less @@ -0,0 +1,201 @@ +.canvas-editor-container { + background-color: @color-pf-white; + height: 100%; + width: 100%; + .canvas-editor-toolbar { + padding: 15px; + a { + color: @color-pf-black-600; + cursor: pointer; + } + a.disabled, a:hover.disabled { + color: @color-pf-black-400; + cursor: not-allowed; + } + a:hover { + color: @color-pf-black-800; + } + button { + margin-right: 6px; + } + .more-actions { + border-left: solid 2px @color-pf-black-300; + margin-left: 6px; + vertical-align: middle; + } + .pficon { + font-size: 20px; + margin-left: 15px; + margin-top: -4px; + vertical-align: middle; + } + .right-aligned-controls { + display: block; + float: right; + font-size: 12px; + font-weight: 600; + padding-right: 5px; + padding-top: 2px; + vertical-align: middle; + } + .show-hide-connectors-label { + vertical-align: 2px; + } + } + .canvas-editor-toolbox-container { + -moz-placeholder-font-style: italic; + -moz-placeholder-padding-left: 10px; + -ms-input-placeholder-font-style: italic; + -ms-input-placeholder-padding-left: 10px; + height: 100%; + position: relative; + width: 100%; + .canvas-editor-toolbox { + background-color: rgba(255, 255, 255, 0.94); + border: 1.5px solid @color-pf-black-300; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + padding-bottom: 10px; + padding-left: 26px; + padding-right: 26px; + padding-top: 26px; + position: absolute; + top: -1px; + width: 100%; + .draggable-item-icon { + font-size: 28px; + padding-right: 14px; + padding-top: 6px; + vertical-align: middle; + } + .tab-pre-title { + font-size: 12px; + margin-bottom: -6px; + } + .tab-title { + font-size: 14px; + } + .tab-single-line { + padding-bottom: 8px; + padding-top: 8px; + } + .toolbox-items-list { + list-style: none; + max-height: 300px; + overflow-y: auto; + padding-bottom: 4px; + padding-left: 15px; + } + .toolbox-item { + background-color: rgba(255, 255, 255, 0.94); + border-radius: 1px; + border: 1px solid @color-pf-black-400; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + cursor: pointer; + float: left; + height: 36px; + margin-right: 16px; + margin-top: 16px; + min-height: 52px; + min-width: 325px; + padding: 6px 28px; + position: relative; + vertical-align: middle; + .fa { + font-size: 26px; + margin-right: 8px; + margin-top: 2px; + vertical-align: middle; + } + span { + position: relative; + top: 4px; + } + img { + height: 100%; + margin-right: 8px; + object-fit: contain; + } + &::before { + background-image: linear-gradient(to bottom, @color-pf-blue-400 60%, @color-pf-white 0%); + background-position: left; + background-repeat: repeat-y; + background-size: 2px 5px; + border: 4px solid @color-pf-blue-400; + bottom: 4px; + content: ""; + left: 4px; + position: absolute; + top: 3px; + width: 10px; + } + } + .not-draggable { + background-color: @color-pf-black-200; + cursor: auto; + opacity: 0.4; + } + .toolbox-filter { + margin-top: 10px; + .search-text { + border: 1px solid @color-pf-black-400; + float: right; + position: relative; + text-decoration: none; + width: 250px; + padding-left: 6px; + } + .clear-search-text { + bottom: -6px; + color: @color-pf-black-400; + cursor: pointer; + float: right; + position: relative; + right: -242px; + span { + font-size: 20px; + padding-right: 14px; + vertical-align: middle; + } + } + } + .close-toolbox { + font-size: 16px; + float: right; + position: relative; + cursor: pointer; + top: -17px; + right: -12px; + color: @color-pf-black-600; + cursor: pointer; + :hover { + color: @color-pf-black-800; + } + } + } + .canvas-container { + height: 756px; + margin-left: 15px; + overflow: auto; + width: 98%; + } + a { + color: #000000; + } + &::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; + } + &::-moz-placeholder { + font-style: italic; + padding-left: 10px; + } + } +} +.subtabs { + .nav-tabs-pf { + padding-left: 18px; + li { + font-size: 14px; + } + } +} diff --git a/dist/less/canvas.less b/dist/less/canvas.less new file mode 100644 index 000000000..2ebcf5385 --- /dev/null +++ b/dist/less/canvas.less @@ -0,0 +1,240 @@ +.canvas { + height: 756px; + width: 1396px; +} + +.read-only { + background-image: none !important; + background-repeat: unset !important; +} + +.canvas-in-connection-mode { + background-image: none !important; + background-repeat: unset !important; + background-color: @color-pf-black-400; +} + +.node-header { + display: table; + font-weight: 600; + p { + display: table-cell; + text-align: center; + vertical-align: middle; + } +} +.invalid-node-header { + fill: @color-pf-black-400; + color: @color-pf-black-400; +} +.node-center-icon { + font-size: 72px; +} +.node-rect { + fill: @color-pf-white; + stroke-width: 2; + stroke: @color-pf-black-400; +} +.invalid-node-rect { + fill: @color-pf-black-300; +} +.invalid-node-img { + opacity: 0.2; +} +.node-toolbar { + background-color: @color-pf-black-150; + border-bottom: solid 1px @color-pf-black-400; + border-left: solid 1px @color-pf-black-400; + border-radius: 5px; + border-right: solid 1px @color-pf-black-400; + height: 28px; + padding-left: 8px; +} +.node-toolbar-icons { + color: @color-pf-blue-400; + cursor: pointer; + font-size: 16px; + padding-right: 16px; + padding-top: 4px; +} +.svg-triangle { + polyline { + fill: @color-pf-black-150; + stroke-width: 2; + stroke: @color-pf-black-400; + } +} +.connecting-mode-rec { + fill: #8b8d8f; +} +.connecting-mode-label { + fill: @color-pf-white; + font-size: 16px; +} +.connecting-mode-label-warning { + fill: @color-pf-red; + font-size: 16px; +} +.connector-icons { + cursor: pointer; + fill: @color-pf-blue-400; + font-size: 16px; + &:hover { + fill: #063451; + } +} +.mouseover-node-rect { + stroke-width: 3; + stroke: @color-pf-black-400; +} +.selected-node-rect { + stroke-width: 3; + stroke: @color-pf-blue-400; +} +.connector-tooltip { + fill: @color-pf-black-150; + stroke-width: 1; + stroke: @color-pf-black; +} +.connector-tooltip-text { + font-size: 10px; +} +.connector-circle { + fill: @color-pf-white; + stroke-width: 2; + stroke: @color-pf-black; +} +.mouseover-connector-circle { + fill: @color-pf-white; + stroke-width: 3; + stroke: @color-pf-black; +} +.unconnected-circle { + fill: @color-pf-green-500; + stroke-opacity: "0.8"; + stroke-width: 2; + stroke: @color-pf-black; +} +.mouseover-unconnected-circle { + fill: @color-pf-green-500; + stroke-opacity: "0.8"; + stroke-width: 3; + stroke: @color-pf-black; +} +.connection-line { + fill: transparent; + stroke-width: 4; + stroke: @color-pf-black-500; +} +.mouseover-connection-line { + fill: transparent; + stroke-width: 6; + stroke: @color-pf-black-500; +} +.selected-connection-line { + fill: transparent; + stroke-width: 4; + stroke: @color-pf-red; +} +.connection-endpoint { + fill: @color-pf-black-500; +} +.selected-connection-endpoint { + fill: @color-pf-red; +} +.mouseover-connection-endpoint { + fill: @color-pf-black-500; +} +.connection-name { + fill: @color-pf-black; +} +.selected-connection-name { + fill: @color-pf-red; +} +.mouseover-connection-name { + fill: @color-pf-black; +} +.dragging-connection { + pointer-events: none; +} +.dragging-connection-line { + fill: transparent; + stroke-width: 3; + stroke: @color-pf-black-500; +} +.dragging-connection-endpoint { + fill: @color-pf-black-500; +} +.draggable-container { + border: solid 1px @color-pf-black-300; +} +.drag-selection-rect { + fill: transparent; + stroke-width: 2; + stroke:@color-pf-blue-400; +} +.node-dialog-close { + font-size: 12px; + padding-top: 7px; +} +.nodetoolbar-dialog { + left: -19px; + margin-top: 4px; + padding: 8px; + position: fixed; + top: 19px; + width: 300px; + .tag-list-action { + color: @color-pf-white; + cursor: pointer; + } +} +.tag-dialog { + left: 30px; + width: 353px; +} +.node-tag-title { + font-weight: 600; + padding-bottom: 12px; + padding-top: 12px; +} +.edit-dialog { + left: 5px; +} +.arrow-box { + background: @color-pf-white; + border-radius: 5px; + border: 1px solid @color-pf-black-700; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + position: relative; + &::after { + border-bottom-color: @color-pf-white; + border-color: @color-pf-black-800; + border-width: 8px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41%; + pointer-events: none; + position: absolute; + width: 0; + } + &::before { + border-bottom-color: @color-pf-black-700; + border-color: rgba(81, 77, 82, 0); + border-width: 10px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41.5%; + pointer-events: none; + position: absolute; + width: 0; + } +} diff --git a/dist/less/card.less b/dist/less/card.less new file mode 100644 index 000000000..35f722943 --- /dev/null +++ b/dist/less/card.less @@ -0,0 +1,161 @@ +.card-pf-aggregate-status-alt { + .card-pf-body { + padding-bottom: 20px; + } + .card-pf-title { + font-weight: 300; + line-height: 22px; + margin: 20px 0 10px 0; + } + .card-pf-aggregate-status-count { + font-size: 24px; + } + .card-pf-aggregate-status-title { + display: block; + font-size: 12px; + } + .card-pf-aggregate-status-notifications { + .card-pf-aggregate-status-notification { + border-left: none; + } + .fa { + position: relative; + top: -1px; + } + .pficon { + position: relative; + top: -1px; + } + } +} + +.card-pf.card-pf-aggregate-status .card-pf-body .spinner-container { + position: static; +} + +.card-pf { + .hide-for-spinner { + opacity: 0; + } + .card-pf-body { + &.show-spinner { + position: relative; + } + .spinner-container { + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: -20px; + bottom: 0; + + .loading-indicator { + align-items: center; + justify-content: center; + display: flex; + + .loading-text { + font-size: 16px; + margin-left: 10px; + } + + .spinner.spinner-lg { + display: inline-block; + } + } + } + } +} + +.card-pf-info-status .card-pf-body .spinner-container{ + top: 0px; + height: 100%; + &.with-title { + top: 12px; + } +} + +.card-pf-aggregate-status-mini { + .card-pf-body { + &.show-spinner { + height: 40px; + } + .spinner-container { + top: -22px; + } + } +} + +.card-pf-heading-no-bottom { + margin: 0 -20px 0px; + padding: 0 20px 0; +} + +.card-pf-icon-image { + height: 18px; + margin: 0 5px 5px; +} + +.trend-card-large-pf { + .trend-header-pf { + display: block; + font-size: 16px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-big-pf { + font-size: 26px; + font-weight: 300; + margin-left: 10px; + } + .trend-title-small-pf { + font-size: 12px; + font-weight: 400; + } +} + +.trend-card-small-pf { + .trend-header-pf { + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-big-pf { + font-size: 17px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-small-pf { + font-size: 10px; + font-weight: 400; + } +} + +.card-pf-info-status { + display: flex; + .card-pf-info-image { + display: flex; + align-items: center; + justify-content: center; + flex-direction:column; + margin-right: 15px; + .info-icon { + font-size: 50px; + } + .info-img { + max-height: 50px; + } + } + .card-pf-info-content { + margin: 10px 0; + .card-pf-title{ + margin-top: 10px; + margin-bottom: 15px; + } + } + .show-spinner { + width: 100%; + } +} diff --git a/dist/less/charts.less b/dist/less/charts.less new file mode 100644 index 000000000..be696542f --- /dev/null +++ b/dist/less/charts.less @@ -0,0 +1,319 @@ +pf-c3-chart { + display: block; +} +.empty-chart-content { + text-align: center; + .pficon { + font-size: 24px; + } + span { + vertical-align: middle; + width: 100%; + } +} +.utilization-trend-chart-pf { + .donut-chart-pf { + float: left; + padding-top: 15px; + width: 100%; + } + h3 { + font-weight: 400; + } + .current-values { + border-bottom: 1px solid @color-pf-black-300; + float: left; + padding: 0 5px 10px 0; + width: 100%; + } + .available-count { + margin: 3px 0; + padding-left: 0; + padding-right: 5px; + } + .available-text { + font-size: 12px; + font-weight: 400; + line-height: 14px; + margin: 2px 0; + padding: 0 5px; + } + .radial-chart { + float: left; + padding-top: 10px; + width: 100%; + } + .sparkline-chart { + float: left; + margin-left: -5px; + margin-right: -5px; + width: 100%; + } + .legend-text { + color: inherit; + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 0; + } +} +.utilization-trend-chart-pf.data-unavailable-pf { + .current-values { + color: transparent; + } +} + +.trend-card-compact-pf { + .col-sm-2:not(.col-sm-push-10) { + padding-right: 0; + } + .col-sm-10:not(.col-sm-pull-2) { + padding-left: 0; + } +} + +.trend-flat-details { + display: table; + margin-top: 5px; +} + +@media (min-width: 768px) { + .trend-flat-details { + margin-top: 25px; + } +} + +.trend-flat-details-cell { + display: table-cell; + min-width: 70px; + vertical-align: bottom; +} +.trend-header-compact-pf { + display: block; + font-size: 12px; + font-weight: 400; +} +.trend-title-compact-big-pf { + font-size: 36px; + font-weight: 300; + line-height: 1; +} +.trend-title-compact-small-pf { + font-size: 12px; + font-weight: 400; +} +.trend-title-flat-big-pf { + font-size: 26px; + font-weight: 300; + line-height: 1; + margin-right: 15px; +} +.trend-label-flat-pf { + font-size: 12px; + font-weight: 400; + line-height: 1; +} +.trend-label-flat-strong-pf { + display: block; + font-size: 12px; + font-weight: 700; + line-height: 1; +} +.trend-footer-pf { + color: #333333; + font-size: 10px; + font-weight: 400; + margin-left: 10px; +} + +.data-unavailable-pf[class*="trend-title-"] { + color: transparent; +} +.data-unavailable-pf[class*="trend-label-"] { + color: transparent; +} +.data-unavailable-pf { + .trend-footer-pf { + color: transparent; + } +} + +.utilization-bar-chart-pf { + .progress-bar { + -moz-transition: width .75s ease-in-out; + -o-transition: width .75s ease-in-out; + -webkit-transition: width .75s ease-in-out; + transition: width .75s ease-in-out; + } + .progress-bar.animate { + width: 0% !important; + } +} +.heatmap-pf-container { + position: relative; + .heatmap-container { + margin-left: -1px; + } +} +.heatmap-pf-container-pf { + .loading { + position: absolute; + right: 50%; + top: 100px; + z-index: 10; + } +} +.heatmap-pf-svg { + height: 100%; + width: 100%; +} +.heatmap-pf-legend-container { + list-style-type: none; + margin-top: 5px; + overflow: auto; + padding: 0; +} +.heatmap-pf-legend-items { + float: left; +} +.legend-pf-color-box { + display: inline-block; + height: 11px; + margin-left: 5px; + margin-right: 5px; + width: 11px; + &:first-of-type { + margin-left: 0px; + } +} +.legend-pf-text { + font-size: 11px; + font-weight: 400; + line-height: 11px; + padding-right: 5px; +} +.camelcase { + text-transform: capitalize; +} + +pf-topology { + display: block; + user-select: none; +} + +.container-topology pf-topology { + height: 500px; + position: relative; +} +.container-topology .canvas { + position: absolute; +} + +.container-topology .popup { + position: absolute; + left: 0; + top: 0; + background-color: #fff; + width: 180px; + border: 1px #ccc solid; + border-radius: 6px; + box-shadow: #333 2px 2px 4px; + padding: 6px; + font-size: 14px; +} + +.container-topology .popup h5 { + font-weight: bold; +} + +.container-topology .popup p { + margin: 0 0 4px; +} + +.container-topology .popup p:hover { + color : #0099cc; + cursor: pointer; +} + + +.container-topology label.checkbox-inline { + font-size: 14px; +} + +.pf-topology-svg g { + font-family: PatternFlyIcons-webfont; + font-size: 18px; + text-anchor: middle; + cursor: pointer; +} + +.pf-topology-svg g text { + stroke: none; + stroke-width: 0px; +} + +.pf-topology-svg g.weak use { + opacity: .6; +} + +.pf-topology-svg g circle { + stroke: #aaa; + fill: #fff; +} + +.pf-topology-svg g.fixed use { + stroke-width: 2px; +} + +.pf-topology-svg g.selected use, +.pf-topology-svg g.selected circle { + stroke-width: 4px; +} + +.pf-topology-svg line { + stroke: #aaa; + stroke-width: 1; +} + +.pf-topology-svg g text.attached-label { + display: none; +} + +.pf-topology-svg g text.attached-label.visible { + font-size: 12px; + fill: black; + display: block; +} + +.pf-topology-svg g.selected { + stroke-width: 4px; +} + +.pf-topology-svg g circle { + stroke-width: 2px; +} + +.pf-topology-svg g circle.success { + stroke: #3F9C35; +} + +.pf-topology-svg g circle.error { + stroke: #CC0000; +} + +.pf-topology-svg g circle.warning { + stroke: #EC7A08; +} + +.pf-topology-svg g circle.unknown { + stroke: #bbb; +} + +.pf-topology-svg g text.glyph { + font-size: 20px; + fill: #1186C1; +} + +.pf-topology-map canvas.topology-graph { + width: 100%; +} diff --git a/dist/less/datepicker.less b/dist/less/datepicker.less new file mode 100644 index 000000000..41cf7c64f --- /dev/null +++ b/dist/less/datepicker.less @@ -0,0 +1,120 @@ +.uib-datepicker-popup { + padding: 4px; + + *:focus { + outline: none; + } + + button { + background: @color-pf-white; + border: none; + box-shadow: none; + } + + th { + height: 30px; + } + + .uib-title { + font-size: 14px; + font-weight: 500; + padding: 5px; + + strong { + font-weight: normal; + } + } + + .uib-left { + height: 30px; + + .glyphicon { + display: none; + } + + &::before { + content: "\00AB"; + } + } + + .uib-right { + height: 30px; + + .glyphicon { + display: none; + } + + &::before { + content: "\00BB"; + } + } + + .uib-day { + + button.btn.btn-default { + height: 30px; + width: 30px; + + &:hover { + background: @color-pf-blue-50; + } + + &.active { + background: @color-pf-blue-400; + border-color: @color-pf-blue-600; + color: @color-pf-white; + box-shadow: none; + padding: 0; + + .text-info { // this is today's date + color: @color-pf-white; + } + } + + &:not(.active){ + .text-info { // this is today's date + color: @color-pf-black-800; + background: @color-pf-gold-200; + height: 30px; + width: 30px; + padding: 7px; + display: inline-block; + margin: -1px -7px -2px -7px; + + &:hover { + background: @color-pf-blue-50; + } + } + } + } + + } + + .uib-button-bar { + padding: 0; + + .btn-group { + width: 100%; + + .uib-clear { + display: none; + } + + .uib-datepicker-current { + color: @color-pf-black; + width: 100%; + font-size: 14px; + font-weight: 500; + height: 30px; + + &:hover { + background: @color-pf-blue-50; + } + } + } + + .uib-close { + display: none; + } + } +} diff --git a/dist/less/dependencies/patternfly/color-variables.less b/dist/less/dependencies/patternfly/color-variables.less new file mode 100644 index 000000000..9db2cd22a --- /dev/null +++ b/dist/less/dependencies/patternfly/color-variables.less @@ -0,0 +1,85 @@ +@color-pf-black-100: #fafafa; +@color-pf-black-150: #f5f5f5; +@color-pf-black-200: #ededed; +@color-pf-black-300: #d1d1d1; +@color-pf-black-400: #bbb; +@color-pf-black-500: #8b8d8f; +@color-pf-black-600: #72767b; +@color-pf-black-700: #4d5258; +@color-pf-black-800: #393f44; +@color-pf-black-900: #292e34; +@color-pf-blue-25: #edf8ff; +@color-pf-blue-50: #def3ff; +@color-pf-blue-100: #bee1f4; +@color-pf-blue-200: #7dc3e8; +@color-pf-blue-300: #39a5dc; +@color-pf-blue-400: #0088ce; +@color-pf-blue-500: #00659c; +@color-pf-blue-600: #004368; +@color-pf-blue-700: #002235; +@color-pf-cyan-100: #bedee1; +@color-pf-cyan-200: #7dbdc3; +@color-pf-cyan-300: #3a9ca6; +@color-pf-cyan-400: #007a87; +@color-pf-cyan-500: #005c66; +@color-pf-cyan-600: #003d44; +@color-pf-cyan-700: #001f22; +@color-pf-gold-100: #fbeabc; +@color-pf-gold-200: #f9d67a; +@color-pf-gold-300: #f5c12e; +@color-pf-gold-400: #f0ab00; +@color-pf-gold-500: #b58100; +@color-pf-gold-600: #795600; +@color-pf-gold-700: #3d2c00; +@color-pf-green-100: #cfe7cd; +@color-pf-green-200: #9ecf99; +@color-pf-green-300: #6ec664; +@color-pf-green-400: #3f9c35; +@color-pf-green-500: #2d7623; +@color-pf-green-600: #1e4f18; +@color-pf-green-700: #0f280d; +@color-pf-light-blue-100: #beedf9; +@color-pf-light-blue-200: #7cdbf3; +@color-pf-light-blue-300: #35caed; +@color-pf-light-blue-400: #00b9e4; +@color-pf-light-blue-500: #008bad; +@color-pf-light-blue-600: #005c73; +@color-pf-light-blue-700: #002d39; +@color-pf-light-green-100: #e4f5bc; +@color-pf-light-green-200: #c8eb79; +@color-pf-light-green-300: #ace12e; +@color-pf-light-green-400: #92d400; +@color-pf-light-green-500: #6ca100; +@color-pf-light-green-600: #486b00; +@color-pf-light-green-700: #253600; +@color-pf-orange-100: #fbdebf; +@color-pf-orange-200: #f7bd7f; +@color-pf-orange-300: #f39d3c; +@color-pf-orange-400: #ec7a08; +@color-pf-orange-500: #b35c00; +@color-pf-orange-600: #773d00; +@color-pf-orange-700: #3b1f00; +@color-pf-purple-100: #c7bfff; +@color-pf-purple-200: #a18fff; +@color-pf-purple-300: #8461f7; +@color-pf-purple-400: #703fec; +@color-pf-purple-500: #582fc0; +@color-pf-purple-600: #40199a; +@color-pf-purple-700: #1f0066; +@color-pf-red-100: #cc0000; +@color-pf-red-200: #a30000; +@color-pf-red-300: #8b0000; +@color-pf-red-400: #470000; +@color-pf-red-500: #2c0000; + +@color-pf-black: #030303; +@color-pf-blue: @color-pf-blue-400; +@color-pf-cyan: @color-pf-cyan-400; +@color-pf-gold: @color-pf-gold-400; +@color-pf-green: @color-pf-green-400; +@color-pf-light-blue: @color-pf-light-blue-400; +@color-pf-light-green: @color-pf-light-green-400; +@color-pf-orange: @color-pf-orange-400; +@color-pf-purple: @color-pf-purple-400; +@color-pf-red: @color-pf-red-100; +@color-pf-white: #fff; diff --git a/dist/less/filters.less b/dist/less/filters.less new file mode 100644 index 000000000..f55587239 --- /dev/null +++ b/dist/less/filters.less @@ -0,0 +1,110 @@ +.filter-pf { + .category-select { + display: flex; + } + .category-select-value { + border-left-width: 0 !important; + } +} +.filter-pf.filter-fields { + .form-group { + width: 275px; + } +} +.filter-select { + .btn-default { + background-color: @color-pf-white; + background-image: none; + color: @color-pf-black-500; + font-size: 12px; + font-style: italic; + font-weight: 400; + } +} + +pf-filter-panel { + .dropdown > .dropdown-menu, .input-group-btn > .dropdown-menu { + margin-top: 5px; + opacity: .95; + } + input { + &:-moz-placeholder { font-style: italic; padding-left: 10px; } // Firefox 4-18 + &::-moz-placeholder { font-style: italic; padding-left: 10px; } // Firefox 19+ + &:-ms-input-placeholder { font-style: italic; padding-left: 10px; } // Internet Explorer 10+ + &::-webkit-input-placeholder { font-style: italic; padding-left: 10px;} // Safari and Chrome + } + .inline-filter-pf > .dropdown { + margin-right: 10px; + } + .filter-panel-container { + padding: 10px; + .keyword-filter { + margin-bottom: 10px; + } + ul { + list-style-type: none; + padding-left: 16px; + } + .category { + font-size: 15px; + font-weight: 400; + + .category-option-label { + font-size: 12px; + vertical-align: text-bottom; + } + } + } +} + +.filter-pf-active-label { + margin-right: 5px; +} + +.filter-pf-category-item { + margin-bottom: 5px; +} + +.pf-filter-category-label { + font-weight: 700; + padding: 5px 0 6px 5px; + margin-right: 5px; + + &.multiples { + background-color: @color-pf-blue-300; + padding-right: 5px; + } +} + +.filter-pf-category-values.list-inline { + margin-left: 0; + > li { + margin-left: 5px; + padding: 0; + } +} + + +.filter-pf { + &.inline-filter-pf { + pf-filter-fields, + pf-filter-results, + pf-filter-panel-results { + display: inline-block; + } + pf-filter-fields { + vertical-align: middle; + } + + .filter-fields { + > .form-group { + margin-bottom: 3px; + margin-right: 15px; + } + } + .toolbar-pf-results { + border-top: none; + margin-top: 0; + } + } +} diff --git a/dist/less/list-view.less b/dist/less/list-view.less new file mode 100644 index 000000000..5f0fe07b8 --- /dev/null +++ b/dist/less/list-view.less @@ -0,0 +1,5 @@ +.list-group-item-header { + &.list-group-item-not-selectable { + cursor: inherit; + } +} diff --git a/dist/less/misc.less b/dist/less/misc.less new file mode 100644 index 000000000..4f97408b0 --- /dev/null +++ b/dist/less/misc.less @@ -0,0 +1,19 @@ +.navbar-brand-txt { + line-height: 34px; +} +uib-accordion > .panel-group, accordion > .panel-group { + .panel-default { + .panel-title > a { + &:before { + content: "\f105"; + } + } + } + .panel-open { + .panel-title > a { + &:before { + content: "\f107"; + } + } + } +} diff --git a/dist/less/modal-overlay.less b/dist/less/modal-overlay.less new file mode 100644 index 000000000..fb716d042 --- /dev/null +++ b/dist/less/modal-overlay.less @@ -0,0 +1,7 @@ +.modal-overlay { + + .modal-body { + max-height: 70vh; + overflow-x: hidden; + } +} diff --git a/dist/less/notification.less b/dist/less/notification.less new file mode 100644 index 000000000..c65f6e197 --- /dev/null +++ b/dist/less/notification.less @@ -0,0 +1,8 @@ +.toast-pf-action>a { + cursor: pointer; +} +.toast-pf { + .dropdown-menu>li>a { + cursor: pointer; + } +} diff --git a/dist/less/pagination.less b/dist/less/pagination.less new file mode 100644 index 000000000..4b8e631aa --- /dev/null +++ b/dist/less/pagination.less @@ -0,0 +1,6 @@ +.content-view-pf-pagination .form-group { + align-items: center; + .per-page-label { + padding-left: 10px; + } +} diff --git a/dist/less/sort.less b/dist/less/sort.less new file mode 100644 index 000000000..b94038fb7 --- /dev/null +++ b/dist/less/sort.less @@ -0,0 +1,12 @@ +.sort-pf { + .btn-link { + color: #252525; + font-size: 16px; + line-height: 1; + margin-left: 10px; + padding: 4px 0; + &:hover { + color: @color-pf-blue; + } + } +} diff --git a/dist/less/table.less b/dist/less/table.less new file mode 100644 index 000000000..d34a66a14 --- /dev/null +++ b/dist/less/table.less @@ -0,0 +1,46 @@ +.pf-table-view-selected-label { + float: right; + line-height: 40px; +} +.table-view-container { + background-color: white; +} +table.dataTable.no-footer { + border-bottom: none; +} +.dataTables_wrapper { + border: none; + margin: 0px; + padding: 0px; +} +table.dataTable { + margin: 0; + border-collapse: collapse; + thead { + .sorting_asc { + background-image: none !important; + } + .sorting_desc { + background-image: none !important; + } + .sorting { + background-image: none !important; + } + } + tbody { + tr.selected { + span a { + color: @color-pf-white; + } + } + th { + padding: 2px 10px 3px; + } + td { + padding: 2px 10px 3px; + } + } +} +.table-view-pf-select { + width: 13px; +} diff --git a/dist/less/toolbars.less b/dist/less/toolbars.less new file mode 100644 index 000000000..67f962c32 --- /dev/null +++ b/dist/less/toolbars.less @@ -0,0 +1,32 @@ +@media (min-width: 768px) { + .toolbar-pf-actions .toolbar-apf-filter { + padding-left: 0; + } +} +.toolbar-pf-actions { + .toolbar-pf-view-selector { + a { + cursor: pointer; + } + } + .dropdown-menu { + a { + cursor: pointer; + } + } + .dropdown-kebab-pf { + float: right; + } +} +.toolbar-pf-include-actions { + display: inline-block; + margin: 0 5px; +} +.dropdown-kebab-pf.invisible { + opacity: 0; + pointer-events: none; +} +.toolbar-pf-actions.no-filter { + margin-left: -30px; + margin-bottom: 10px; +} diff --git a/dist/less/vertical-navigation.less b/dist/less/vertical-navigation.less new file mode 100644 index 000000000..f429bc1eb --- /dev/null +++ b/dist/less/vertical-navigation.less @@ -0,0 +1,39 @@ +.navbar-header { + &.ignore-mobile { + float: left; + } +} + +.navbar-collapse { + &.ignore-mobile { + width: auto; + border-top: 0; + box-shadow: none; + display: block; + + .navbar-nav { + float: left; + margin: 0; + + &.navbar-right { + float: right; + .dropdown-menu { + left: auto; + right: 0; + } + } + + > li { + float: left; + } + + .open .dropdown-menu { + position: absolute; + margin-top: 0; + background-color: @color-pf-white; + border: 1px solid @color-pf-black-400; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + } + } + } +} diff --git a/dist/less/views.less b/dist/less/views.less new file mode 100644 index 000000000..f552814db --- /dev/null +++ b/dist/less/views.less @@ -0,0 +1,120 @@ +.card-view-pf { + overflow: auto; + padding-left: 2px; + padding-top: 20px; + .card { + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .175); + background: @color-pf-white; + border-top: 2px solid transparent; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + display: block; + float: left; + height: 290px; + margin-bottom: 20px; + margin-right: 20px; + padding: 10px; + position: relative; + text-align: center; + width: 260px; + .card-check-box { + left: 10px; + position: absolute; + top: 8px; + visibility: hidden; + width: 20px; + z-index: 3; + } + &:hover { + -moz-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + border: 1px solid @color-pf-black-300; + box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + .card-check-box { + visibility: visible; + } + } + &:focus { + -moz-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + border: 1px solid @color-pf-black-300; + box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + } + } + .card-content { + height: 100%; + margin: 2px 0 10px 0; + overflow: auto; + width: 100%; + } + .card-title { + color: #1186C1; + font-size: 16px; + font-weight: 500; + line-height: 1.1; + margin-top: 0px; + } + .card.active { + border: solid 3px @color-pf-blue-300; + &:hover { + border: solid 3px @color-pf-blue-300; + .pficon { + color: @color-pf-white; + } + } + &:focus { + border: solid 3px @color-pf-blue-300; + .pficon { + color: @color-pf-white; + } + } + .pficon { + color: @color-pf-white; + } + .card-check-box { + visibility: visible; + } + } + .card.disabled { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid @color-pf-black-200; + box-shadow: none; + color: @color-pf-black-500; + cursor: not-allowed; + } +} +.card.disabled { + &:hover { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid @color-pf-black-200; + box-shadow: none; + color: @color-pf-black-500; + cursor: not-allowed; + } + &:focus { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid @color-pf-black-200; + box-shadow: none; + color: @color-pf-black-500; + cursor: not-allowed; + } +} +/* overriding pf base so that blank slate fills entire parent container */ +.blank-slate-pf { + height:100%; + margin-bottom: 0px; + button { + margin-right: 4px; + } + .blank-state-pf-helpbutton { + padding-left: 0; + padding-right: 0; + vertical-align: baseline; + } +} +/* overriding pf base so that buttons have spaces between them */ +.pf-expand-placeholder { + margin-right: 15px; +} diff --git a/dist/less/wizard.less b/dist/less/wizard.less new file mode 100644 index 000000000..ab8f12234 --- /dev/null +++ b/dist/less/wizard.less @@ -0,0 +1,30 @@ +.wizard-pf-footer { + .btn-cancel { + &.wizard-pf-cancel-no-back { + margin-right: 0; + } + } +} +.wizard-pf-singlestep { + margin-left: 0; +} +.wizard-pf-position-override { + position: relative; +} +.wizard-pf-footer-inline { + text-align: left; +} +.wizard-pf-cancel-inline { + margin-left: 25px; +} + +.wizard-pf-steps-indicator li a.disabled { + cursor: default; + &:hover { + .wizard-pf-step-number { + background-color: @color-pf-white; + border-color: @color-pf-black-400; + color: @color-pf-black-400; + } + } +} diff --git a/dist/sass/_angular-patternfly.scss b/dist/sass/_angular-patternfly.scss new file mode 100644 index 000000000..068e0d86c --- /dev/null +++ b/dist/sass/_angular-patternfly.scss @@ -0,0 +1,16 @@ +@import 'patternfly/color-variables'; +@import 'misc'; +@import 'card'; +@import 'charts'; +@import 'views'; +@import 'toolbars'; +@import 'filters'; +@import 'sort'; +@import 'notification'; +@import 'list-view'; +@import 'table'; +@import 'wizard'; +@import 'canvas'; +@import 'canvas-editor'; +@import 'pagination'; +@import 'modal-overlay'; diff --git a/dist/sass/_canvas-editor.scss b/dist/sass/_canvas-editor.scss new file mode 100644 index 000000000..5f74cbf5b --- /dev/null +++ b/dist/sass/_canvas-editor.scss @@ -0,0 +1,201 @@ +.canvas-editor-container { + background-color: $color-pf-white; + height: 100%; + width: 100%; + .canvas-editor-toolbar { + padding: 15px; + a { + color: $color-pf-black-600; + cursor: pointer; + } + a.disabled, a:hover.disabled { + color: $color-pf-black-400; + cursor: not-allowed; + } + a:hover { + color: $color-pf-black-800; + } + button { + margin-right: 6px; + } + .more-actions { + border-left: solid 2px $color-pf-black-300; + margin-left: 6px; + vertical-align: middle; + } + .pficon { + font-size: 20px; + margin-left: 15px; + margin-top: -4px; + vertical-align: middle; + } + .right-aligned-controls { + display: block; + float: right; + font-size: 12px; + font-weight: 600; + padding-right: 5px; + padding-top: 2px; + vertical-align: middle; + } + .show-hide-connectors-label { + vertical-align: 2px; + } + } + .canvas-editor-toolbox-container { + -moz-placeholder-font-style: italic; + -moz-placeholder-padding-left: 10px; + -ms-input-placeholder-font-style: italic; + -ms-input-placeholder-padding-left: 10px; + height: 100%; + position: relative; + width: 100%; + .canvas-editor-toolbox { + background-color: rgba(255, 255, 255, 0.94); + border: 1.5px solid $color-pf-black-300; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + padding-bottom: 10px; + padding-left: 26px; + padding-right: 26px; + padding-top: 26px; + position: absolute; + top: -1px; + width: 100%; + .draggable-item-icon { + font-size: 28px; + padding-right: 14px; + padding-top: 6px; + vertical-align: middle; + } + .tab-pre-title { + font-size: 12px; + margin-bottom: -6px; + } + .tab-title { + font-size: 14px; + } + .tab-single-line { + padding-bottom: 8px; + padding-top: 8px; + } + .toolbox-items-list { + list-style: none; + max-height: 300px; + overflow-y: auto; + padding-bottom: 4px; + padding-left: 15px; + } + .toolbox-item { + background-color: rgba(255, 255, 255, 0.94); + border-radius: 1px; + border: 1px solid $color-pf-black-400; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + cursor: pointer; + float: left; + height: 36px; + margin-right: 16px; + margin-top: 16px; + min-height: 52px; + min-width: 325px; + padding: 6px 28px; + position: relative; + vertical-align: middle; + .fa { + font-size: 26px; + margin-right: 8px; + margin-top: 2px; + vertical-align: middle; + } + span { + position: relative; + top: 4px; + } + img { + height: 100%; + margin-right: 8px; + object-fit: contain; + } + &::before { + background-image: linear-gradient(to bottom, $color-pf-blue-400 60%, $color-pf-white 0%); + background-position: left; + background-repeat: repeat-y; + background-size: 2px 5px; + border: 4px solid $color-pf-blue-400; + bottom: 4px; + content: ""; + left: 4px; + position: absolute; + top: 3px; + width: 10px; + } + } + .not-draggable { + background-color: $color-pf-black-200; + cursor: auto; + opacity: 0.4; + } + .toolbox-filter { + margin-top: 10px; + .search-text { + border: 1px solid $color-pf-black-400; + float: right; + position: relative; + text-decoration: none; + width: 250px; + padding-left: 6px; + } + .clear-search-text { + bottom: -6px; + color: $color-pf-black-400; + cursor: pointer; + float: right; + position: relative; + right: -242px; + span { + font-size: 20px; + padding-right: 14px; + vertical-align: middle; + } + } + } + .close-toolbox { + font-size: 16px; + float: right; + position: relative; + cursor: pointer; + top: -17px; + right: -12px; + color: $color-pf-black-600; + cursor: pointer; + :hover { + color: $color-pf-black-800; + } + } + } + .canvas-container { + height: 756px; + margin-left: 15px; + overflow: auto; + width: 98%; + } + a { + color: #000000; + } + &::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; + } + &::-moz-placeholder { + font-style: italic; + padding-left: 10px; + } + } +} +.subtabs { + .nav-tabs-pf { + padding-left: 18px; + li { + font-size: 14px; + } + } +} diff --git a/dist/sass/_canvas.scss b/dist/sass/_canvas.scss new file mode 100644 index 000000000..dc2e7c6c6 --- /dev/null +++ b/dist/sass/_canvas.scss @@ -0,0 +1,240 @@ +.canvas { + height: 756px; + width: 1396px; +} + +.read-only { + background-image: none !important; + background-repeat: unset !important; +} + +.canvas-in-connection-mode { + background-image: none !important; + background-repeat: unset !important; + background-color: $color-pf-black-400; +} + +.node-header { + display: table; + font-weight: 600; + p { + display: table-cell; + text-align: center; + vertical-align: middle; + } +} +.invalid-node-header { + fill: $color-pf-black-400; + color: $color-pf-black-400; +} +.node-center-icon { + font-size: 72px; +} +.node-rect { + fill: $color-pf-white; + stroke-width: 2; + stroke: $color-pf-black-400; +} +.invalid-node-rect { + fill: $color-pf-black-300; +} +.invalid-node-img { + opacity: 0.2; +} +.node-toolbar { + background-color: $color-pf-black-150; + border-bottom: solid 1px $color-pf-black-400; + border-left: solid 1px $color-pf-black-400; + border-radius: 5px; + border-right: solid 1px $color-pf-black-400; + height: 28px; + padding-left: 8px; +} +.node-toolbar-icons { + color: $color-pf-blue-400; + cursor: pointer; + font-size: 16px; + padding-right: 16px; + padding-top: 4px; +} +.svg-triangle { + polyline { + fill: $color-pf-black-150; + stroke-width: 2; + stroke: $color-pf-black-400; + } +} +.connecting-mode-rec { + fill: #8b8d8f; +} +.connecting-mode-label { + fill: $color-pf-white; + font-size: 16px; +} +.connecting-mode-label-warning { + fill: $color-pf-red; + font-size: 16px; +} +.connector-icons { + cursor: pointer; + fill: $color-pf-blue-400; + font-size: 16px; + &:hover { + fill: #063451; + } +} +.mouseover-node-rect { + stroke-width: 3; + stroke: $color-pf-black-400; +} +.selected-node-rect { + stroke-width: 3; + stroke: $color-pf-blue-400; +} +.connector-tooltip { + fill: $color-pf-black-150; + stroke-width: 1; + stroke: $color-pf-black; +} +.connector-tooltip-text { + font-size: 10px; +} +.connector-circle { + fill: $color-pf-white; + stroke-width: 2; + stroke: $color-pf-black; +} +.mouseover-connector-circle { + fill: $color-pf-white; + stroke-width: 3; + stroke: $color-pf-black; +} +.unconnected-circle { + fill: $color-pf-green-500; + stroke-opacity: "0.8"; + stroke-width: 2; + stroke: $color-pf-black; +} +.mouseover-unconnected-circle { + fill: $color-pf-green-500; + stroke-opacity: "0.8"; + stroke-width: 3; + stroke: $color-pf-black; +} +.connection-line { + fill: transparent; + stroke-width: 4; + stroke: $color-pf-black-500; +} +.mouseover-connection-line { + fill: transparent; + stroke-width: 6; + stroke: $color-pf-black-500; +} +.selected-connection-line { + fill: transparent; + stroke-width: 4; + stroke: $color-pf-red; +} +.connection-endpoint { + fill: $color-pf-black-500; +} +.selected-connection-endpoint { + fill: $color-pf-red; +} +.mouseover-connection-endpoint { + fill: $color-pf-black-500; +} +.connection-name { + fill: $color-pf-black; +} +.selected-connection-name { + fill: $color-pf-red; +} +.mouseover-connection-name { + fill: $color-pf-black; +} +.dragging-connection { + pointer-events: none; +} +.dragging-connection-line { + fill: transparent; + stroke-width: 3; + stroke: $color-pf-black-500; +} +.dragging-connection-endpoint { + fill: $color-pf-black-500; +} +.draggable-container { + border: solid 1px $color-pf-black-300; +} +.drag-selection-rect { + fill: transparent; + stroke-width: 2; + stroke:$color-pf-blue-400; +} +.node-dialog-close { + font-size: 12px; + padding-top: 7px; +} +.nodetoolbar-dialog { + left: -19px; + margin-top: 4px; + padding: 8px; + position: fixed; + top: 19px; + width: 300px; + .tag-list-action { + color: $color-pf-white; + cursor: pointer; + } +} +.tag-dialog { + left: 30px; + width: 353px; +} +.node-tag-title { + font-weight: 600; + padding-bottom: 12px; + padding-top: 12px; +} +.edit-dialog { + left: 5px; +} +.arrow-box { + background: $color-pf-white; + border-radius: 5px; + border: 1px solid $color-pf-black-700; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + position: relative; + &::after { + border-bottom-color: $color-pf-white; + border-color: $color-pf-black-800; + border-width: 8px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41%; + pointer-events: none; + position: absolute; + width: 0; + } + &::before { + border-bottom-color: $color-pf-black-700; + border-color: rgba(81, 77, 82, 0); + border-width: 10px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41.5%; + pointer-events: none; + position: absolute; + width: 0; + } +} diff --git a/dist/sass/_card.scss b/dist/sass/_card.scss new file mode 100644 index 000000000..35f722943 --- /dev/null +++ b/dist/sass/_card.scss @@ -0,0 +1,161 @@ +.card-pf-aggregate-status-alt { + .card-pf-body { + padding-bottom: 20px; + } + .card-pf-title { + font-weight: 300; + line-height: 22px; + margin: 20px 0 10px 0; + } + .card-pf-aggregate-status-count { + font-size: 24px; + } + .card-pf-aggregate-status-title { + display: block; + font-size: 12px; + } + .card-pf-aggregate-status-notifications { + .card-pf-aggregate-status-notification { + border-left: none; + } + .fa { + position: relative; + top: -1px; + } + .pficon { + position: relative; + top: -1px; + } + } +} + +.card-pf.card-pf-aggregate-status .card-pf-body .spinner-container { + position: static; +} + +.card-pf { + .hide-for-spinner { + opacity: 0; + } + .card-pf-body { + &.show-spinner { + position: relative; + } + .spinner-container { + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: -20px; + bottom: 0; + + .loading-indicator { + align-items: center; + justify-content: center; + display: flex; + + .loading-text { + font-size: 16px; + margin-left: 10px; + } + + .spinner.spinner-lg { + display: inline-block; + } + } + } + } +} + +.card-pf-info-status .card-pf-body .spinner-container{ + top: 0px; + height: 100%; + &.with-title { + top: 12px; + } +} + +.card-pf-aggregate-status-mini { + .card-pf-body { + &.show-spinner { + height: 40px; + } + .spinner-container { + top: -22px; + } + } +} + +.card-pf-heading-no-bottom { + margin: 0 -20px 0px; + padding: 0 20px 0; +} + +.card-pf-icon-image { + height: 18px; + margin: 0 5px 5px; +} + +.trend-card-large-pf { + .trend-header-pf { + display: block; + font-size: 16px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-big-pf { + font-size: 26px; + font-weight: 300; + margin-left: 10px; + } + .trend-title-small-pf { + font-size: 12px; + font-weight: 400; + } +} + +.trend-card-small-pf { + .trend-header-pf { + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-big-pf { + font-size: 17px; + font-weight: 400; + margin-left: 10px; + } + .trend-title-small-pf { + font-size: 10px; + font-weight: 400; + } +} + +.card-pf-info-status { + display: flex; + .card-pf-info-image { + display: flex; + align-items: center; + justify-content: center; + flex-direction:column; + margin-right: 15px; + .info-icon { + font-size: 50px; + } + .info-img { + max-height: 50px; + } + } + .card-pf-info-content { + margin: 10px 0; + .card-pf-title{ + margin-top: 10px; + margin-bottom: 15px; + } + } + .show-spinner { + width: 100%; + } +} diff --git a/dist/sass/_charts.scss b/dist/sass/_charts.scss new file mode 100644 index 000000000..ca3961bc7 --- /dev/null +++ b/dist/sass/_charts.scss @@ -0,0 +1,319 @@ +pf-c3-chart { + display: block; +} +.empty-chart-content { + text-align: center; + .pficon { + font-size: 24px; + } + span { + vertical-align: middle; + width: 100%; + } +} +.utilization-trend-chart-pf { + .donut-chart-pf { + float: left; + padding-top: 15px; + width: 100%; + } + h3 { + font-weight: 400; + } + .current-values { + border-bottom: 1px solid $color-pf-black-300; + float: left; + padding: 0 5px 10px 0; + width: 100%; + } + .available-count { + margin: 3px 0; + padding-left: 0; + padding-right: 5px; + } + .available-text { + font-size: 12px; + font-weight: 400; + line-height: 14px; + margin: 2px 0; + padding: 0 5px; + } + .radial-chart { + float: left; + padding-top: 10px; + width: 100%; + } + .sparkline-chart { + float: left; + margin-left: -5px; + margin-right: -5px; + width: 100%; + } + .legend-text { + color: inherit; + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 0; + } +} +.utilization-trend-chart-pf.data-unavailable-pf { + .current-values { + color: transparent; + } +} + +.trend-card-compact-pf { + .col-sm-2:not(.col-sm-push-10) { + padding-right: 0; + } + .col-sm-10:not(.col-sm-pull-2) { + padding-left: 0; + } +} + +.trend-flat-details { + display: table; + margin-top: 5px; +} + +@media (min-width: 768px) { + .trend-flat-details { + margin-top: 25px; + } +} + +.trend-flat-details-cell { + display: table-cell; + min-width: 70px; + vertical-align: bottom; +} +.trend-header-compact-pf { + display: block; + font-size: 12px; + font-weight: 400; +} +.trend-title-compact-big-pf { + font-size: 36px; + font-weight: 300; + line-height: 1; +} +.trend-title-compact-small-pf { + font-size: 12px; + font-weight: 400; +} +.trend-title-flat-big-pf { + font-size: 26px; + font-weight: 300; + line-height: 1; + margin-right: 15px; +} +.trend-label-flat-pf { + font-size: 12px; + font-weight: 400; + line-height: 1; +} +.trend-label-flat-strong-pf { + display: block; + font-size: 12px; + font-weight: 700; + line-height: 1; +} +.trend-footer-pf { + color: #333333; + font-size: 10px; + font-weight: 400; + margin-left: 10px; +} + +.data-unavailable-pf[class*="trend-title-"] { + color: transparent; +} +.data-unavailable-pf[class*="trend-label-"] { + color: transparent; +} +.data-unavailable-pf { + .trend-footer-pf { + color: transparent; + } +} + +.utilization-bar-chart-pf { + .progress-bar { + -moz-transition: width .75s ease-in-out; + -o-transition: width .75s ease-in-out; + -webkit-transition: width .75s ease-in-out; + transition: width .75s ease-in-out; + } + .progress-bar.animate { + width: 0% !important; + } +} +.heatmap-pf-container { + position: relative; + .heatmap-container { + margin-left: -1px; + } +} +.heatmap-pf-container-pf { + .loading { + position: absolute; + right: 50%; + top: 100px; + z-index: 10; + } +} +.heatmap-pf-svg { + height: 100%; + width: 100%; +} +.heatmap-pf-legend-container { + list-style-type: none; + margin-top: 5px; + overflow: auto; + padding: 0; +} +.heatmap-pf-legend-items { + float: left; +} +.legend-pf-color-box { + display: inline-block; + height: 11px; + margin-left: 5px; + margin-right: 5px; + width: 11px; + &:first-of-type { + margin-left: 0px; + } +} +.legend-pf-text { + font-size: 11px; + font-weight: 400; + line-height: 11px; + padding-right: 5px; +} +.camelcase { + text-transform: capitalize; +} + +pf-topology { + display: block; + user-select: none; +} + +.container-topology pf-topology { + height: 500px; + position: relative; +} +.container-topology .canvas { + position: absolute; +} + +.container-topology .popup { + position: absolute; + left: 0; + top: 0; + background-color: #fff; + width: 180px; + border: 1px #ccc solid; + border-radius: 6px; + box-shadow: #333 2px 2px 4px; + padding: 6px; + font-size: 14px; +} + +.container-topology .popup h5 { + font-weight: bold; +} + +.container-topology .popup p { + margin: 0 0 4px; +} + +.container-topology .popup p:hover { + color : #0099cc; + cursor: pointer; +} + + +.container-topology label.checkbox-inline { + font-size: 14px; +} + +.pf-topology-svg g { + font-family: PatternFlyIcons-webfont; + font-size: 18px; + text-anchor: middle; + cursor: pointer; +} + +.pf-topology-svg g text { + stroke: none; + stroke-width: 0px; +} + +.pf-topology-svg g.weak use { + opacity: .6; +} + +.pf-topology-svg g circle { + stroke: #aaa; + fill: #fff; +} + +.pf-topology-svg g.fixed use { + stroke-width: 2px; +} + +.pf-topology-svg g.selected use, +.pf-topology-svg g.selected circle { + stroke-width: 4px; +} + +.pf-topology-svg line { + stroke: #aaa; + stroke-width: 1; +} + +.pf-topology-svg g text.attached-label { + display: none; +} + +.pf-topology-svg g text.attached-label.visible { + font-size: 12px; + fill: black; + display: block; +} + +.pf-topology-svg g.selected { + stroke-width: 4px; +} + +.pf-topology-svg g circle { + stroke-width: 2px; +} + +.pf-topology-svg g circle.success { + stroke: #3F9C35; +} + +.pf-topology-svg g circle.error { + stroke: #CC0000; +} + +.pf-topology-svg g circle.warning { + stroke: #EC7A08; +} + +.pf-topology-svg g circle.unknown { + stroke: #bbb; +} + +.pf-topology-svg g text.glyph { + font-size: 20px; + fill: #1186C1; +} + +.pf-topology-map canvas.topology-graph { + width: 100%; +} diff --git a/dist/sass/_datepicker.scss b/dist/sass/_datepicker.scss new file mode 100644 index 000000000..2fdde60ec --- /dev/null +++ b/dist/sass/_datepicker.scss @@ -0,0 +1,120 @@ +.uib-datepicker-popup { + padding: 4px; + + *:focus { + outline: none; + } + + button { + background: $color-pf-white; + border: none; + box-shadow: none; + } + + th { + height: 30px; + } + + .uib-title { + font-size: 14px; + font-weight: 500; + padding: 5px; + + strong { + font-weight: normal; + } + } + + .uib-left { + height: 30px; + + .glyphicon { + display: none; + } + + &::before { + content: "\00AB"; + } + } + + .uib-right { + height: 30px; + + .glyphicon { + display: none; + } + + &::before { + content: "\00BB"; + } + } + + .uib-day { + + button.btn.btn-default { + height: 30px; + width: 30px; + + &:hover { + background: $color-pf-blue-50; + } + + &.active { + background: $color-pf-blue-400; + border-color: $color-pf-blue-600; + color: $color-pf-white; + box-shadow: none; + padding: 0; + + .text-info { // this is today's date + color: $color-pf-white; + } + } + + &:not(.active){ + .text-info { // this is today's date + color: $color-pf-black-800; + background: $color-pf-gold-200; + height: 30px; + width: 30px; + padding: 7px; + display: inline-block; + margin: -1px -7px -2px -7px; + + &:hover { + background: $color-pf-blue-50; + } + } + } + } + + } + + .uib-button-bar { + padding: 0; + + .btn-group { + width: 100%; + + .uib-clear { + display: none; + } + + .uib-datepicker-current { + color: $color-pf-black; + width: 100%; + font-size: 14px; + font-weight: 500; + height: 30px; + + &:hover { + background: $color-pf-blue-50; + } + } + } + + .uib-close { + display: none; + } + } +} diff --git a/dist/sass/_filters.scss b/dist/sass/_filters.scss new file mode 100644 index 000000000..51d274214 --- /dev/null +++ b/dist/sass/_filters.scss @@ -0,0 +1,110 @@ +.filter-pf { + .category-select { + display: flex; + } + .category-select-value { + border-left-width: 0 !important; + } +} +.filter-pf.filter-fields { + .form-group { + width: 275px; + } +} +.filter-select { + .btn-default { + background-color: $color-pf-white; + background-image: none; + color: $color-pf-black-500; + font-size: 12px; + font-style: italic; + font-weight: 400; + } +} + +pf-filter-panel { + .dropdown > .dropdown-menu, .input-group-btn > .dropdown-menu { + margin-top: 5px; + opacity: .95; + } + input { + &:-moz-placeholder { font-style: italic; padding-left: 10px; } // Firefox 4-18 + &::-moz-placeholder { font-style: italic; padding-left: 10px; } // Firefox 19+ + &:-ms-input-placeholder { font-style: italic; padding-left: 10px; } // Internet Explorer 10+ + &::-webkit-input-placeholder { font-style: italic; padding-left: 10px;} // Safari and Chrome + } + .inline-filter-pf > .dropdown { + margin-right: 10px; + } + .filter-panel-container { + padding: 10px; + .keyword-filter { + margin-bottom: 10px; + } + ul { + list-style-type: none; + padding-left: 16px; + } + .category { + font-size: 15px; + font-weight: 400; + + .category-option-label { + font-size: 12px; + vertical-align: text-bottom; + } + } + } +} + +.filter-pf-active-label { + margin-right: 5px; +} + +.filter-pf-category-item { + margin-bottom: 5px; +} + +.pf-filter-category-label { + font-weight: 700; + padding: 5px 0 6px 5px; + margin-right: 5px; + + &.multiples { + background-color: $color-pf-blue-300; + padding-right: 5px; + } +} + +.filter-pf-category-values.list-inline { + margin-left: 0; + > li { + margin-left: 5px; + padding: 0; + } +} + + +.filter-pf { + &.inline-filter-pf { + pf-filter-fields, + pf-filter-results, + pf-filter-panel-results { + display: inline-block; + } + pf-filter-fields { + vertical-align: middle; + } + + .filter-fields { + > .form-group { + margin-bottom: 3px; + margin-right: 15px; + } + } + .toolbar-pf-results { + border-top: none; + margin-top: 0; + } + } +} diff --git a/dist/sass/_list-view.scss b/dist/sass/_list-view.scss new file mode 100644 index 000000000..5f0fe07b8 --- /dev/null +++ b/dist/sass/_list-view.scss @@ -0,0 +1,5 @@ +.list-group-item-header { + &.list-group-item-not-selectable { + cursor: inherit; + } +} diff --git a/dist/sass/_misc.scss b/dist/sass/_misc.scss new file mode 100644 index 000000000..4f97408b0 --- /dev/null +++ b/dist/sass/_misc.scss @@ -0,0 +1,19 @@ +.navbar-brand-txt { + line-height: 34px; +} +uib-accordion > .panel-group, accordion > .panel-group { + .panel-default { + .panel-title > a { + &:before { + content: "\f105"; + } + } + } + .panel-open { + .panel-title > a { + &:before { + content: "\f107"; + } + } + } +} diff --git a/dist/sass/_modal-overlay.scss b/dist/sass/_modal-overlay.scss new file mode 100644 index 000000000..fb716d042 --- /dev/null +++ b/dist/sass/_modal-overlay.scss @@ -0,0 +1,7 @@ +.modal-overlay { + + .modal-body { + max-height: 70vh; + overflow-x: hidden; + } +} diff --git a/dist/sass/_notification.scss b/dist/sass/_notification.scss new file mode 100644 index 000000000..c65f6e197 --- /dev/null +++ b/dist/sass/_notification.scss @@ -0,0 +1,8 @@ +.toast-pf-action>a { + cursor: pointer; +} +.toast-pf { + .dropdown-menu>li>a { + cursor: pointer; + } +} diff --git a/dist/sass/_pagination.scss b/dist/sass/_pagination.scss new file mode 100644 index 000000000..4b8e631aa --- /dev/null +++ b/dist/sass/_pagination.scss @@ -0,0 +1,6 @@ +.content-view-pf-pagination .form-group { + align-items: center; + .per-page-label { + padding-left: 10px; + } +} diff --git a/dist/sass/_sort.scss b/dist/sass/_sort.scss new file mode 100644 index 000000000..e0ac5c448 --- /dev/null +++ b/dist/sass/_sort.scss @@ -0,0 +1,12 @@ +.sort-pf { + .btn-link { + color: #252525; + font-size: 16px; + line-height: 1; + margin-left: 10px; + padding: 4px 0; + &:hover { + color: $color-pf-blue; + } + } +} diff --git a/dist/sass/_table.scss b/dist/sass/_table.scss new file mode 100644 index 000000000..4cac98faf --- /dev/null +++ b/dist/sass/_table.scss @@ -0,0 +1,46 @@ +.pf-table-view-selected-label { + float: right; + line-height: 40px; +} +.table-view-container { + background-color: white; +} +table.dataTable.no-footer { + border-bottom: none; +} +.dataTables_wrapper { + border: none; + margin: 0px; + padding: 0px; +} +table.dataTable { + margin: 0; + border-collapse: collapse; + thead { + .sorting_asc { + background-image: none !important; + } + .sorting_desc { + background-image: none !important; + } + .sorting { + background-image: none !important; + } + } + tbody { + tr.selected { + span a { + color: $color-pf-white; + } + } + th { + padding: 2px 10px 3px; + } + td { + padding: 2px 10px 3px; + } + } +} +.table-view-pf-select { + width: 13px; +} diff --git a/dist/sass/_toolbars.scss b/dist/sass/_toolbars.scss new file mode 100644 index 000000000..67f962c32 --- /dev/null +++ b/dist/sass/_toolbars.scss @@ -0,0 +1,32 @@ +@media (min-width: 768px) { + .toolbar-pf-actions .toolbar-apf-filter { + padding-left: 0; + } +} +.toolbar-pf-actions { + .toolbar-pf-view-selector { + a { + cursor: pointer; + } + } + .dropdown-menu { + a { + cursor: pointer; + } + } + .dropdown-kebab-pf { + float: right; + } +} +.toolbar-pf-include-actions { + display: inline-block; + margin: 0 5px; +} +.dropdown-kebab-pf.invisible { + opacity: 0; + pointer-events: none; +} +.toolbar-pf-actions.no-filter { + margin-left: -30px; + margin-bottom: 10px; +} diff --git a/dist/sass/_vertical-navigation.scss b/dist/sass/_vertical-navigation.scss new file mode 100644 index 000000000..d2fd795ff --- /dev/null +++ b/dist/sass/_vertical-navigation.scss @@ -0,0 +1,39 @@ +.navbar-header { + &.ignore-mobile { + float: left; + } +} + +.navbar-collapse { + &.ignore-mobile { + width: auto; + border-top: 0; + box-shadow: none; + display: block; + + .navbar-nav { + float: left; + margin: 0; + + &.navbar-right { + float: right; + .dropdown-menu { + left: auto; + right: 0; + } + } + + > li { + float: left; + } + + .open .dropdown-menu { + position: absolute; + margin-top: 0; + background-color: $color-pf-white; + border: 1px solid $color-pf-black-400; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + } + } + } +} diff --git a/dist/sass/_views.scss b/dist/sass/_views.scss new file mode 100644 index 000000000..95931dd79 --- /dev/null +++ b/dist/sass/_views.scss @@ -0,0 +1,120 @@ +.card-view-pf { + overflow: auto; + padding-left: 2px; + padding-top: 20px; + .card { + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .175); + background: $color-pf-white; + border-top: 2px solid transparent; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + display: block; + float: left; + height: 290px; + margin-bottom: 20px; + margin-right: 20px; + padding: 10px; + position: relative; + text-align: center; + width: 260px; + .card-check-box { + left: 10px; + position: absolute; + top: 8px; + visibility: hidden; + width: 20px; + z-index: 3; + } + &:hover { + -moz-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + border: 1px solid $color-pf-black-300; + box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + .card-check-box { + visibility: visible; + } + } + &:focus { + -moz-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + border: 1px solid $color-pf-black-300; + box-shadow: 0px 3px 10px -2px rgba(0,0,0,0.24); + } + } + .card-content { + height: 100%; + margin: 2px 0 10px 0; + overflow: auto; + width: 100%; + } + .card-title { + color: #1186C1; + font-size: 16px; + font-weight: 500; + line-height: 1.1; + margin-top: 0px; + } + .card.active { + border: solid 3px $color-pf-blue-300; + &:hover { + border: solid 3px $color-pf-blue-300; + .pficon { + color: $color-pf-white; + } + } + &:focus { + border: solid 3px $color-pf-blue-300; + .pficon { + color: $color-pf-white; + } + } + .pficon { + color: $color-pf-white; + } + .card-check-box { + visibility: visible; + } + } + .card.disabled { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid $color-pf-black-200; + box-shadow: none; + color: $color-pf-black-500; + cursor: not-allowed; + } +} +.card.disabled { + &:hover { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid $color-pf-black-200; + box-shadow: none; + color: $color-pf-black-500; + cursor: not-allowed; + } + &:focus { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid $color-pf-black-200; + box-shadow: none; + color: $color-pf-black-500; + cursor: not-allowed; + } +} +/* overriding pf base so that blank slate fills entire parent container */ +.blank-slate-pf { + height:100%; + margin-bottom: 0px; + button { + margin-right: 4px; + } + .blank-state-pf-helpbutton { + padding-left: 0; + padding-right: 0; + vertical-align: baseline; + } +} +/* overriding pf base so that buttons have spaces between them */ +.pf-expand-placeholder { + margin-right: 15px; +} diff --git a/dist/sass/_wizard.scss b/dist/sass/_wizard.scss new file mode 100644 index 000000000..4bf122659 --- /dev/null +++ b/dist/sass/_wizard.scss @@ -0,0 +1,30 @@ +.wizard-pf-footer { + .btn-cancel { + &.wizard-pf-cancel-no-back { + margin-right: 0; + } + } +} +.wizard-pf-singlestep { + margin-left: 0; +} +.wizard-pf-position-override { + position: relative; +} +.wizard-pf-footer-inline { + text-align: left; +} +.wizard-pf-cancel-inline { + margin-left: 25px; +} + +.wizard-pf-steps-indicator li a.disabled { + cursor: default; + &:hover { + .wizard-pf-step-number { + background-color: $color-pf-white; + border-color: $color-pf-black-400; + color: $color-pf-black-400; + } + } +} diff --git a/dist/sass/angular-patternfly.css b/dist/sass/angular-patternfly.css new file mode 100644 index 000000000..02287c671 --- /dev/null +++ b/dist/sass/angular-patternfly.css @@ -0,0 +1,1449 @@ +.navbar-brand-txt { + line-height: 34px; +} + +uib-accordion > .panel-group .panel-default .panel-title > a:before, accordion > .panel-group .panel-default .panel-title > a:before { + content: "\f105"; +} + +uib-accordion > .panel-group .panel-open .panel-title > a:before, accordion > .panel-group .panel-open .panel-title > a:before { + content: "\f107"; +} + +.card-pf-aggregate-status-alt .card-pf-body { + padding-bottom: 20px; +} + +.card-pf-aggregate-status-alt .card-pf-title { + font-weight: 300; + line-height: 22px; + margin: 20px 0 10px 0; +} + +.card-pf-aggregate-status-alt .card-pf-aggregate-status-count { + font-size: 24px; +} + +.card-pf-aggregate-status-alt .card-pf-aggregate-status-title { + display: block; + font-size: 12px; +} + +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .card-pf-aggregate-status-notification { + border-left: none; +} + +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .fa { + position: relative; + top: -1px; +} + +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .pficon { + position: relative; + top: -1px; +} + +.card-pf.card-pf-aggregate-status .card-pf-body .spinner-container { + position: static; +} + +.card-pf .hide-for-spinner { + opacity: 0; +} + +.card-pf .card-pf-body.show-spinner { + position: relative; +} + +.card-pf .card-pf-body .spinner-container { + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: -20px; + bottom: 0; +} + +.card-pf .card-pf-body .spinner-container .loading-indicator { + align-items: center; + justify-content: center; + display: flex; +} + +.card-pf .card-pf-body .spinner-container .loading-indicator .loading-text { + font-size: 16px; + margin-left: 10px; +} + +.card-pf .card-pf-body .spinner-container .loading-indicator .spinner.spinner-lg { + display: inline-block; +} + +.card-pf-info-status .card-pf-body .spinner-container { + top: 0px; + height: 100%; +} + +.card-pf-info-status .card-pf-body .spinner-container.with-title { + top: 12px; +} + +.card-pf-aggregate-status-mini .card-pf-body.show-spinner { + height: 40px; +} + +.card-pf-aggregate-status-mini .card-pf-body .spinner-container { + top: -22px; +} + +.card-pf-heading-no-bottom { + margin: 0 -20px 0px; + padding: 0 20px 0; +} + +.card-pf-icon-image { + height: 18px; + margin: 0 5px 5px; +} + +.trend-card-large-pf .trend-header-pf { + display: block; + font-size: 16px; + font-weight: 400; + margin-left: 10px; +} + +.trend-card-large-pf .trend-title-big-pf { + font-size: 26px; + font-weight: 300; + margin-left: 10px; +} + +.trend-card-large-pf .trend-title-small-pf { + font-size: 12px; + font-weight: 400; +} + +.trend-card-small-pf .trend-header-pf { + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 10px; +} + +.trend-card-small-pf .trend-title-big-pf { + font-size: 17px; + font-weight: 400; + margin-left: 10px; +} + +.trend-card-small-pf .trend-title-small-pf { + font-size: 10px; + font-weight: 400; +} + +.card-pf-info-status { + display: flex; +} + +.card-pf-info-status .card-pf-info-image { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-right: 15px; +} + +.card-pf-info-status .card-pf-info-image .info-icon { + font-size: 50px; +} + +.card-pf-info-status .card-pf-info-image .info-img { + max-height: 50px; +} + +.card-pf-info-status .card-pf-info-content { + margin: 10px 0; +} + +.card-pf-info-status .card-pf-info-content .card-pf-title { + margin-top: 10px; + margin-bottom: 15px; +} + +.card-pf-info-status .show-spinner { + width: 100%; +} + +pf-c3-chart { + display: block; +} + +.empty-chart-content { + text-align: center; +} + +.empty-chart-content .pficon { + font-size: 24px; +} + +.empty-chart-content span { + vertical-align: middle; + width: 100%; +} + +.utilization-trend-chart-pf .donut-chart-pf { + float: left; + padding-top: 15px; + width: 100%; +} + +.utilization-trend-chart-pf h3 { + font-weight: 400; +} + +.utilization-trend-chart-pf .current-values { + border-bottom: 1px solid #d1d1d1; + float: left; + padding: 0 5px 10px 0; + width: 100%; +} + +.utilization-trend-chart-pf .available-count { + margin: 3px 0; + padding-left: 0; + padding-right: 5px; +} + +.utilization-trend-chart-pf .available-text { + font-size: 12px; + font-weight: 400; + line-height: 14px; + margin: 2px 0; + padding: 0 5px; +} + +.utilization-trend-chart-pf .radial-chart { + float: left; + padding-top: 10px; + width: 100%; +} + +.utilization-trend-chart-pf .sparkline-chart { + float: left; + margin-left: -5px; + margin-right: -5px; + width: 100%; +} + +.utilization-trend-chart-pf .legend-text { + color: inherit; + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 0; +} + +.utilization-trend-chart-pf.data-unavailable-pf .current-values { + color: transparent; +} + +.trend-card-compact-pf .col-sm-2:not(.col-sm-push-10) { + padding-right: 0; +} + +.trend-card-compact-pf .col-sm-10:not(.col-sm-pull-2) { + padding-left: 0; +} + +.trend-flat-details { + display: table; + margin-top: 5px; +} + +@media (min-width: 768px) { + .trend-flat-details { + margin-top: 25px; + } +} + +.trend-flat-details-cell { + display: table-cell; + min-width: 70px; + vertical-align: bottom; +} + +.trend-header-compact-pf { + display: block; + font-size: 12px; + font-weight: 400; +} + +.trend-title-compact-big-pf { + font-size: 36px; + font-weight: 300; + line-height: 1; +} + +.trend-title-compact-small-pf { + font-size: 12px; + font-weight: 400; +} + +.trend-title-flat-big-pf { + font-size: 26px; + font-weight: 300; + line-height: 1; + margin-right: 15px; +} + +.trend-label-flat-pf { + font-size: 12px; + font-weight: 400; + line-height: 1; +} + +.trend-label-flat-strong-pf { + display: block; + font-size: 12px; + font-weight: 700; + line-height: 1; +} + +.trend-footer-pf { + color: #333333; + font-size: 10px; + font-weight: 400; + margin-left: 10px; +} + +.data-unavailable-pf[class*="trend-title-"] { + color: transparent; +} + +.data-unavailable-pf[class*="trend-label-"] { + color: transparent; +} + +.data-unavailable-pf .trend-footer-pf { + color: transparent; +} + +.utilization-bar-chart-pf .progress-bar { + -moz-transition: width .75s ease-in-out; + -o-transition: width .75s ease-in-out; + -webkit-transition: width .75s ease-in-out; + transition: width .75s ease-in-out; +} + +.utilization-bar-chart-pf .progress-bar.animate { + width: 0% !important; +} + +.heatmap-pf-container { + position: relative; +} + +.heatmap-pf-container .heatmap-container { + margin-left: -1px; +} + +.heatmap-pf-container-pf .loading { + position: absolute; + right: 50%; + top: 100px; + z-index: 10; +} + +.heatmap-pf-svg { + height: 100%; + width: 100%; +} + +.heatmap-pf-legend-container { + list-style-type: none; + margin-top: 5px; + overflow: auto; + padding: 0; +} + +.heatmap-pf-legend-items { + float: left; +} + +.legend-pf-color-box { + display: inline-block; + height: 11px; + margin-left: 5px; + margin-right: 5px; + width: 11px; +} + +.legend-pf-color-box:first-of-type { + margin-left: 0px; +} + +.legend-pf-text { + font-size: 11px; + font-weight: 400; + line-height: 11px; + padding-right: 5px; +} + +.camelcase { + text-transform: capitalize; +} + +pf-topology { + display: block; + user-select: none; +} + +.container-topology pf-topology { + height: 500px; + position: relative; +} + +.container-topology .canvas { + position: absolute; +} + +.container-topology .popup { + position: absolute; + left: 0; + top: 0; + background-color: #fff; + width: 180px; + border: 1px #ccc solid; + border-radius: 6px; + box-shadow: #333 2px 2px 4px; + padding: 6px; + font-size: 14px; +} + +.container-topology .popup h5 { + font-weight: bold; +} + +.container-topology .popup p { + margin: 0 0 4px; +} + +.container-topology .popup p:hover { + color: #0099cc; + cursor: pointer; +} + +.container-topology label.checkbox-inline { + font-size: 14px; +} + +.pf-topology-svg g { + font-family: PatternFlyIcons-webfont; + font-size: 18px; + text-anchor: middle; + cursor: pointer; +} + +.pf-topology-svg g text { + stroke: none; + stroke-width: 0px; +} + +.pf-topology-svg g.weak use { + opacity: .6; +} + +.pf-topology-svg g circle { + stroke: #aaa; + fill: #fff; +} + +.pf-topology-svg g.fixed use { + stroke-width: 2px; +} + +.pf-topology-svg g.selected use, +.pf-topology-svg g.selected circle { + stroke-width: 4px; +} + +.pf-topology-svg line { + stroke: #aaa; + stroke-width: 1; +} + +.pf-topology-svg g text.attached-label { + display: none; +} + +.pf-topology-svg g text.attached-label.visible { + font-size: 12px; + fill: black; + display: block; +} + +.pf-topology-svg g.selected { + stroke-width: 4px; +} + +.pf-topology-svg g circle { + stroke-width: 2px; +} + +.pf-topology-svg g circle.success { + stroke: #3F9C35; +} + +.pf-topology-svg g circle.error { + stroke: #CC0000; +} + +.pf-topology-svg g circle.warning { + stroke: #EC7A08; +} + +.pf-topology-svg g circle.unknown { + stroke: #bbb; +} + +.pf-topology-svg g text.glyph { + font-size: 20px; + fill: #1186C1; +} + +.pf-topology-map canvas.topology-graph { + width: 100%; +} + +.card-view-pf { + overflow: auto; + padding-left: 2px; + padding-top: 20px; +} + +.card-view-pf .card { + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + background: #fff; + border-top: 2px solid transparent; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + display: block; + float: left; + height: 290px; + margin-bottom: 20px; + margin-right: 20px; + padding: 10px; + position: relative; + text-align: center; + width: 260px; +} + +.card-view-pf .card .card-check-box { + left: 10px; + position: absolute; + top: 8px; + visibility: hidden; + width: 20px; + z-index: 3; +} + +.card-view-pf .card:hover { + -moz-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + border: 1px solid #d1d1d1; + box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); +} + +.card-view-pf .card:hover .card-check-box { + visibility: visible; +} + +.card-view-pf .card:focus { + -moz-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + border: 1px solid #d1d1d1; + box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); +} + +.card-view-pf .card-content { + height: 100%; + margin: 2px 0 10px 0; + overflow: auto; + width: 100%; +} + +.card-view-pf .card-title { + color: #1186C1; + font-size: 16px; + font-weight: 500; + line-height: 1.1; + margin-top: 0px; +} + +.card-view-pf .card.active { + border: solid 3px #39a5dc; +} + +.card-view-pf .card.active:hover { + border: solid 3px #39a5dc; +} + +.card-view-pf .card.active:hover .pficon { + color: #fff; +} + +.card-view-pf .card.active:focus { + border: solid 3px #39a5dc; +} + +.card-view-pf .card.active:focus .pficon { + color: #fff; +} + +.card-view-pf .card.active .pficon { + color: #fff; +} + +.card-view-pf .card.active .card-check-box { + visibility: visible; +} + +.card-view-pf .card.disabled { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} + +.card.disabled:hover { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} + +.card.disabled:focus { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} + +/* overriding pf base so that blank slate fills entire parent container */ +.blank-slate-pf { + height: 100%; + margin-bottom: 0px; +} + +.blank-slate-pf button { + margin-right: 4px; +} + +.blank-slate-pf .blank-state-pf-helpbutton { + padding-left: 0; + padding-right: 0; + vertical-align: baseline; +} + +/* overriding pf base so that buttons have spaces between them */ +.pf-expand-placeholder { + margin-right: 15px; +} + +@media (min-width: 768px) { + .toolbar-pf-actions .toolbar-apf-filter { + padding-left: 0; + } +} + +.toolbar-pf-actions .toolbar-pf-view-selector a { + cursor: pointer; +} + +.toolbar-pf-actions .dropdown-menu a { + cursor: pointer; +} + +.toolbar-pf-actions .dropdown-kebab-pf { + float: right; +} + +.toolbar-pf-include-actions { + display: inline-block; + margin: 0 5px; +} + +.dropdown-kebab-pf.invisible { + opacity: 0; + pointer-events: none; +} + +.toolbar-pf-actions.no-filter { + margin-left: -30px; + margin-bottom: 10px; +} + +.filter-pf .category-select { + display: flex; +} + +.filter-pf .category-select-value { + border-left-width: 0 !important; +} + +.filter-pf.filter-fields .form-group { + width: 275px; +} + +.filter-select .btn-default { + background-color: #fff; + background-image: none; + color: #8b8d8f; + font-size: 12px; + font-style: italic; + font-weight: 400; +} + +pf-filter-panel .dropdown > .dropdown-menu, pf-filter-panel .input-group-btn > .dropdown-menu { + margin-top: 5px; + opacity: .95; +} + +pf-filter-panel input:-moz-placeholder { + font-style: italic; + padding-left: 10px; +} + +pf-filter-panel input::-moz-placeholder { + font-style: italic; + padding-left: 10px; +} + +pf-filter-panel input:-ms-input-placeholder { + font-style: italic; + padding-left: 10px; +} + +pf-filter-panel input::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; +} + +pf-filter-panel .inline-filter-pf > .dropdown { + margin-right: 10px; +} + +pf-filter-panel .filter-panel-container { + padding: 10px; +} + +pf-filter-panel .filter-panel-container .keyword-filter { + margin-bottom: 10px; +} + +pf-filter-panel .filter-panel-container ul { + list-style-type: none; + padding-left: 16px; +} + +pf-filter-panel .filter-panel-container .category { + font-size: 15px; + font-weight: 400; +} + +pf-filter-panel .filter-panel-container .category .category-option-label { + font-size: 12px; + vertical-align: text-bottom; +} + +.filter-pf-active-label { + margin-right: 5px; +} + +.filter-pf-category-item { + margin-bottom: 5px; +} + +.pf-filter-category-label { + font-weight: 700; + padding: 5px 0 6px 5px; + margin-right: 5px; +} + +.pf-filter-category-label.multiples { + background-color: #39a5dc; + padding-right: 5px; +} + +.filter-pf-category-values.list-inline { + margin-left: 0; +} + +.filter-pf-category-values.list-inline > li { + margin-left: 5px; + padding: 0; +} + +.filter-pf.inline-filter-pf pf-filter-fields, +.filter-pf.inline-filter-pf pf-filter-results, +.filter-pf.inline-filter-pf pf-filter-panel-results { + display: inline-block; +} + +.filter-pf.inline-filter-pf pf-filter-fields { + vertical-align: middle; +} + +.filter-pf.inline-filter-pf .filter-fields > .form-group { + margin-bottom: 3px; + margin-right: 15px; +} + +.filter-pf.inline-filter-pf .toolbar-pf-results { + border-top: none; + margin-top: 0; +} + +.sort-pf .btn-link { + color: #252525; + font-size: 16px; + line-height: 1; + margin-left: 10px; + padding: 4px 0; +} + +.sort-pf .btn-link:hover { + color: #0088ce; +} + +.toast-pf-action > a { + cursor: pointer; +} + +.toast-pf .dropdown-menu > li > a { + cursor: pointer; +} + +.list-group-item-header.list-group-item-not-selectable { + cursor: inherit; +} + +.pf-table-view-selected-label { + float: right; + line-height: 40px; +} + +.table-view-container { + background-color: white; +} + +table.dataTable.no-footer { + border-bottom: none; +} + +.dataTables_wrapper { + border: none; + margin: 0px; + padding: 0px; +} + +table.dataTable { + margin: 0; + border-collapse: collapse; +} + +table.dataTable thead .sorting_asc { + background-image: none !important; +} + +table.dataTable thead .sorting_desc { + background-image: none !important; +} + +table.dataTable thead .sorting { + background-image: none !important; +} + +table.dataTable tbody tr.selected span a { + color: #fff; +} + +table.dataTable tbody th { + padding: 2px 10px 3px; +} + +table.dataTable tbody td { + padding: 2px 10px 3px; +} + +.table-view-pf-select { + width: 13px; +} + +.wizard-pf-footer .btn-cancel.wizard-pf-cancel-no-back { + margin-right: 0; +} + +.wizard-pf-singlestep { + margin-left: 0; +} + +.wizard-pf-position-override { + position: relative; +} + +.wizard-pf-footer-inline { + text-align: left; +} + +.wizard-pf-cancel-inline { + margin-left: 25px; +} + +.wizard-pf-steps-indicator li a.disabled { + cursor: default; +} + +.wizard-pf-steps-indicator li a.disabled:hover .wizard-pf-step-number { + background-color: #fff; + border-color: #bbb; + color: #bbb; +} + +.canvas { + height: 756px; + width: 1396px; +} + +.read-only { + background-image: none !important; + background-repeat: unset !important; +} + +.canvas-in-connection-mode { + background-image: none !important; + background-repeat: unset !important; + background-color: #bbb; +} + +.node-header { + display: table; + font-weight: 600; +} + +.node-header p { + display: table-cell; + text-align: center; + vertical-align: middle; +} + +.invalid-node-header { + fill: #bbb; + color: #bbb; +} + +.node-center-icon { + font-size: 72px; +} + +.node-rect { + fill: #fff; + stroke-width: 2; + stroke: #bbb; +} + +.invalid-node-rect { + fill: #d1d1d1; +} + +.invalid-node-img { + opacity: 0.2; +} + +.node-toolbar { + background-color: #f5f5f5; + border-bottom: solid 1px #bbb; + border-left: solid 1px #bbb; + border-radius: 5px; + border-right: solid 1px #bbb; + height: 28px; + padding-left: 8px; +} + +.node-toolbar-icons { + color: #0088ce; + cursor: pointer; + font-size: 16px; + padding-right: 16px; + padding-top: 4px; +} + +.svg-triangle polyline { + fill: #f5f5f5; + stroke-width: 2; + stroke: #bbb; +} + +.connecting-mode-rec { + fill: #8b8d8f; +} + +.connecting-mode-label { + fill: #fff; + font-size: 16px; +} + +.connecting-mode-label-warning { + fill: #cc0000; + font-size: 16px; +} + +.connector-icons { + cursor: pointer; + fill: #0088ce; + font-size: 16px; +} + +.connector-icons:hover { + fill: #063451; +} + +.mouseover-node-rect { + stroke-width: 3; + stroke: #bbb; +} + +.selected-node-rect { + stroke-width: 3; + stroke: #0088ce; +} + +.connector-tooltip { + fill: #f5f5f5; + stroke-width: 1; + stroke: #030303; +} + +.connector-tooltip-text { + font-size: 10px; +} + +.connector-circle { + fill: #fff; + stroke-width: 2; + stroke: #030303; +} + +.mouseover-connector-circle { + fill: #fff; + stroke-width: 3; + stroke: #030303; +} + +.unconnected-circle { + fill: #2d7623; + stroke-opacity: "0.8"; + stroke-width: 2; + stroke: #030303; +} + +.mouseover-unconnected-circle { + fill: #2d7623; + stroke-opacity: "0.8"; + stroke-width: 3; + stroke: #030303; +} + +.connection-line { + fill: transparent; + stroke-width: 4; + stroke: #8b8d8f; +} + +.mouseover-connection-line { + fill: transparent; + stroke-width: 6; + stroke: #8b8d8f; +} + +.selected-connection-line { + fill: transparent; + stroke-width: 4; + stroke: #cc0000; +} + +.connection-endpoint { + fill: #8b8d8f; +} + +.selected-connection-endpoint { + fill: #cc0000; +} + +.mouseover-connection-endpoint { + fill: #8b8d8f; +} + +.connection-name { + fill: #030303; +} + +.selected-connection-name { + fill: #cc0000; +} + +.mouseover-connection-name { + fill: #030303; +} + +.dragging-connection { + pointer-events: none; +} + +.dragging-connection-line { + fill: transparent; + stroke-width: 3; + stroke: #8b8d8f; +} + +.dragging-connection-endpoint { + fill: #8b8d8f; +} + +.draggable-container { + border: solid 1px #d1d1d1; +} + +.drag-selection-rect { + fill: transparent; + stroke-width: 2; + stroke: #0088ce; +} + +.node-dialog-close { + font-size: 12px; + padding-top: 7px; +} + +.nodetoolbar-dialog { + left: -19px; + margin-top: 4px; + padding: 8px; + position: fixed; + top: 19px; + width: 300px; +} + +.nodetoolbar-dialog .tag-list-action { + color: #fff; + cursor: pointer; +} + +.tag-dialog { + left: 30px; + width: 353px; +} + +.node-tag-title { + font-weight: 600; + padding-bottom: 12px; + padding-top: 12px; +} + +.edit-dialog { + left: 5px; +} + +.arrow-box { + background: #fff; + border-radius: 5px; + border: 1px solid #4d5258; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + position: relative; +} + +.arrow-box::after { + border-bottom-color: #fff; + border-color: #393f44; + border-width: 8px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41%; + pointer-events: none; + position: absolute; + width: 0; +} + +.arrow-box::before { + border-bottom-color: #4d5258; + border-color: rgba(81, 77, 82, 0); + border-width: 10px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41.5%; + pointer-events: none; + position: absolute; + width: 0; +} + +.canvas-editor-container { + background-color: #fff; + height: 100%; + width: 100%; +} + +.canvas-editor-container .canvas-editor-toolbar { + padding: 15px; +} + +.canvas-editor-container .canvas-editor-toolbar a { + color: #72767b; + cursor: pointer; +} + +.canvas-editor-container .canvas-editor-toolbar a.disabled, .canvas-editor-container .canvas-editor-toolbar a:hover.disabled { + color: #bbb; + cursor: not-allowed; +} + +.canvas-editor-container .canvas-editor-toolbar a:hover { + color: #393f44; +} + +.canvas-editor-container .canvas-editor-toolbar button { + margin-right: 6px; +} + +.canvas-editor-container .canvas-editor-toolbar .more-actions { + border-left: solid 2px #d1d1d1; + margin-left: 6px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbar .pficon { + font-size: 20px; + margin-left: 15px; + margin-top: -4px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbar .right-aligned-controls { + display: block; + float: right; + font-size: 12px; + font-weight: 600; + padding-right: 5px; + padding-top: 2px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbar .show-hide-connectors-label { + vertical-align: 2px; +} + +.canvas-editor-container .canvas-editor-toolbox-container { + -moz-placeholder-font-style: italic; + -moz-placeholder-padding-left: 10px; + -ms-input-placeholder-font-style: italic; + -ms-input-placeholder-padding-left: 10px; + height: 100%; + position: relative; + width: 100%; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox { + background-color: rgba(255, 255, 255, 0.94); + border: 1.5px solid #d1d1d1; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + padding-bottom: 10px; + padding-left: 26px; + padding-right: 26px; + padding-top: 26px; + position: absolute; + top: -1px; + width: 100%; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .draggable-item-icon { + font-size: 28px; + padding-right: 14px; + padding-top: 6px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-pre-title { + font-size: 12px; + margin-bottom: -6px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-title { + font-size: 14px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-single-line { + padding-bottom: 8px; + padding-top: 8px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-items-list { + list-style: none; + max-height: 300px; + overflow-y: auto; + padding-bottom: 4px; + padding-left: 15px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item { + background-color: rgba(255, 255, 255, 0.94); + border-radius: 1px; + border: 1px solid #bbb; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + cursor: pointer; + float: left; + height: 36px; + margin-right: 16px; + margin-top: 16px; + min-height: 52px; + min-width: 325px; + padding: 6px 28px; + position: relative; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item .fa { + font-size: 26px; + margin-right: 8px; + margin-top: 2px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item span { + position: relative; + top: 4px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item img { + height: 100%; + margin-right: 8px; + object-fit: contain; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item::before { + background-image: linear-gradient(to bottom, #0088ce 60%, #fff 0%); + background-position: left; + background-repeat: repeat-y; + background-size: 2px 5px; + border: 4px solid #0088ce; + bottom: 4px; + content: ""; + left: 4px; + position: absolute; + top: 3px; + width: 10px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .not-draggable { + background-color: #ededed; + cursor: auto; + opacity: 0.4; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter { + margin-top: 10px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .search-text { + border: 1px solid #bbb; + float: right; + position: relative; + text-decoration: none; + width: 250px; + padding-left: 6px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text { + bottom: -6px; + color: #bbb; + cursor: pointer; + float: right; + position: relative; + right: -242px; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text span { + font-size: 20px; + padding-right: 14px; + vertical-align: middle; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox { + font-size: 16px; + float: right; + position: relative; + cursor: pointer; + top: -17px; + right: -12px; + color: #72767b; + cursor: pointer; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox :hover { + color: #393f44; +} + +.canvas-editor-container .canvas-editor-toolbox-container .canvas-container { + height: 756px; + margin-left: 15px; + overflow: auto; + width: 98%; +} + +.canvas-editor-container .canvas-editor-toolbox-container a { + color: #000000; +} + +.canvas-editor-container .canvas-editor-toolbox-container::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; +} + +.canvas-editor-container .canvas-editor-toolbox-container::-moz-placeholder { + font-style: italic; + padding-left: 10px; +} + +.subtabs .nav-tabs-pf { + padding-left: 18px; +} + +.subtabs .nav-tabs-pf li { + font-size: 14px; +} + +.content-view-pf-pagination .form-group { + align-items: center; +} + +.content-view-pf-pagination .form-group .per-page-label { + padding-left: 10px; +} + +.modal-overlay .modal-body { + max-height: 70vh; + overflow-x: hidden; +} diff --git a/dist/styles/angular-patternfly.css b/dist/styles/angular-patternfly.css new file mode 100644 index 000000000..966635758 --- /dev/null +++ b/dist/styles/angular-patternfly.css @@ -0,0 +1,1308 @@ +.navbar-brand-txt { + line-height: 34px; +} +uib-accordion > .panel-group .panel-default .panel-title > a:before, +accordion > .panel-group .panel-default .panel-title > a:before { + content: "\f105"; +} +uib-accordion > .panel-group .panel-open .panel-title > a:before, +accordion > .panel-group .panel-open .panel-title > a:before { + content: "\f107"; +} +.card-pf-aggregate-status-alt .card-pf-body { + padding-bottom: 20px; +} +.card-pf-aggregate-status-alt .card-pf-title { + font-weight: 300; + line-height: 22px; + margin: 20px 0 10px 0; +} +.card-pf-aggregate-status-alt .card-pf-aggregate-status-count { + font-size: 24px; +} +.card-pf-aggregate-status-alt .card-pf-aggregate-status-title { + display: block; + font-size: 12px; +} +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .card-pf-aggregate-status-notification { + border-left: none; +} +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .fa { + position: relative; + top: -1px; +} +.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .pficon { + position: relative; + top: -1px; +} +.card-pf.card-pf-aggregate-status .card-pf-body .spinner-container { + position: static; +} +.card-pf .hide-for-spinner { + opacity: 0; +} +.card-pf .card-pf-body.show-spinner { + position: relative; +} +.card-pf .card-pf-body .spinner-container { + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: -20px; + bottom: 0; +} +.card-pf .card-pf-body .spinner-container .loading-indicator { + align-items: center; + justify-content: center; + display: flex; +} +.card-pf .card-pf-body .spinner-container .loading-indicator .loading-text { + font-size: 16px; + margin-left: 10px; +} +.card-pf .card-pf-body .spinner-container .loading-indicator .spinner.spinner-lg { + display: inline-block; +} +.card-pf-info-status .card-pf-body .spinner-container { + top: 0px; + height: 100%; +} +.card-pf-info-status .card-pf-body .spinner-container.with-title { + top: 12px; +} +.card-pf-aggregate-status-mini .card-pf-body.show-spinner { + height: 40px; +} +.card-pf-aggregate-status-mini .card-pf-body .spinner-container { + top: -22px; +} +.card-pf-heading-no-bottom { + margin: 0 -20px 0px; + padding: 0 20px 0; +} +.card-pf-icon-image { + height: 18px; + margin: 0 5px 5px; +} +.trend-card-large-pf .trend-header-pf { + display: block; + font-size: 16px; + font-weight: 400; + margin-left: 10px; +} +.trend-card-large-pf .trend-title-big-pf { + font-size: 26px; + font-weight: 300; + margin-left: 10px; +} +.trend-card-large-pf .trend-title-small-pf { + font-size: 12px; + font-weight: 400; +} +.trend-card-small-pf .trend-header-pf { + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 10px; +} +.trend-card-small-pf .trend-title-big-pf { + font-size: 17px; + font-weight: 400; + margin-left: 10px; +} +.trend-card-small-pf .trend-title-small-pf { + font-size: 10px; + font-weight: 400; +} +.card-pf-info-status { + display: flex; +} +.card-pf-info-status .card-pf-info-image { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-right: 15px; +} +.card-pf-info-status .card-pf-info-image .info-icon { + font-size: 50px; +} +.card-pf-info-status .card-pf-info-image .info-img { + max-height: 50px; +} +.card-pf-info-status .card-pf-info-content { + margin: 10px 0; +} +.card-pf-info-status .card-pf-info-content .card-pf-title { + margin-top: 10px; + margin-bottom: 15px; +} +.card-pf-info-status .show-spinner { + width: 100%; +} +pf-c3-chart { + display: block; +} +.empty-chart-content { + text-align: center; +} +.empty-chart-content .pficon { + font-size: 24px; +} +.empty-chart-content span { + vertical-align: middle; + width: 100%; +} +.utilization-trend-chart-pf .donut-chart-pf { + float: left; + padding-top: 15px; + width: 100%; +} +.utilization-trend-chart-pf h3 { + font-weight: 400; +} +.utilization-trend-chart-pf .current-values { + border-bottom: 1px solid #d1d1d1; + float: left; + padding: 0 5px 10px 0; + width: 100%; +} +.utilization-trend-chart-pf .available-count { + margin: 3px 0; + padding-left: 0; + padding-right: 5px; +} +.utilization-trend-chart-pf .available-text { + font-size: 12px; + font-weight: 400; + line-height: 14px; + margin: 2px 0; + padding: 0 5px; +} +.utilization-trend-chart-pf .radial-chart { + float: left; + padding-top: 10px; + width: 100%; +} +.utilization-trend-chart-pf .sparkline-chart { + float: left; + margin-left: -5px; + margin-right: -5px; + width: 100%; +} +.utilization-trend-chart-pf .legend-text { + color: inherit; + display: block; + font-size: 12px; + font-weight: 400; + margin-left: 0; +} +.utilization-trend-chart-pf.data-unavailable-pf .current-values { + color: transparent; +} +.trend-card-compact-pf .col-sm-2:not(.col-sm-push-10) { + padding-right: 0; +} +.trend-card-compact-pf .col-sm-10:not(.col-sm-pull-2) { + padding-left: 0; +} +.trend-flat-details { + display: table; + margin-top: 5px; +} +@media (min-width: 768px) { + .trend-flat-details { + margin-top: 25px; + } +} +.trend-flat-details-cell { + display: table-cell; + min-width: 70px; + vertical-align: bottom; +} +.trend-header-compact-pf { + display: block; + font-size: 12px; + font-weight: 400; +} +.trend-title-compact-big-pf { + font-size: 36px; + font-weight: 300; + line-height: 1; +} +.trend-title-compact-small-pf { + font-size: 12px; + font-weight: 400; +} +.trend-title-flat-big-pf { + font-size: 26px; + font-weight: 300; + line-height: 1; + margin-right: 15px; +} +.trend-label-flat-pf { + font-size: 12px; + font-weight: 400; + line-height: 1; +} +.trend-label-flat-strong-pf { + display: block; + font-size: 12px; + font-weight: 700; + line-height: 1; +} +.trend-footer-pf { + color: #333333; + font-size: 10px; + font-weight: 400; + margin-left: 10px; +} +.data-unavailable-pf[class*="trend-title-"] { + color: transparent; +} +.data-unavailable-pf[class*="trend-label-"] { + color: transparent; +} +.data-unavailable-pf .trend-footer-pf { + color: transparent; +} +.utilization-bar-chart-pf .progress-bar { + -moz-transition: width 0.75s ease-in-out; + -o-transition: width 0.75s ease-in-out; + -webkit-transition: width 0.75s ease-in-out; + transition: width 0.75s ease-in-out; +} +.utilization-bar-chart-pf .progress-bar.animate { + width: 0% !important; +} +.heatmap-pf-container { + position: relative; +} +.heatmap-pf-container .heatmap-container { + margin-left: -1px; +} +.heatmap-pf-container-pf .loading { + position: absolute; + right: 50%; + top: 100px; + z-index: 10; +} +.heatmap-pf-svg { + height: 100%; + width: 100%; +} +.heatmap-pf-legend-container { + list-style-type: none; + margin-top: 5px; + overflow: auto; + padding: 0; +} +.heatmap-pf-legend-items { + float: left; +} +.legend-pf-color-box { + display: inline-block; + height: 11px; + margin-left: 5px; + margin-right: 5px; + width: 11px; +} +.legend-pf-color-box:first-of-type { + margin-left: 0px; +} +.legend-pf-text { + font-size: 11px; + font-weight: 400; + line-height: 11px; + padding-right: 5px; +} +.camelcase { + text-transform: capitalize; +} +pf-topology { + display: block; + user-select: none; +} +.container-topology pf-topology { + height: 500px; + position: relative; +} +.container-topology .canvas { + position: absolute; +} +.container-topology .popup { + position: absolute; + left: 0; + top: 0; + background-color: #fff; + width: 180px; + border: 1px #ccc solid; + border-radius: 6px; + box-shadow: #333 2px 2px 4px; + padding: 6px; + font-size: 14px; +} +.container-topology .popup h5 { + font-weight: bold; +} +.container-topology .popup p { + margin: 0 0 4px; +} +.container-topology .popup p:hover { + color: #0099cc; + cursor: pointer; +} +.container-topology label.checkbox-inline { + font-size: 14px; +} +.pf-topology-svg g { + font-family: PatternFlyIcons-webfont; + font-size: 18px; + text-anchor: middle; + cursor: pointer; +} +.pf-topology-svg g text { + stroke: none; + stroke-width: 0px; +} +.pf-topology-svg g.weak use { + opacity: .6; +} +.pf-topology-svg g circle { + stroke: #aaa; + fill: #fff; +} +.pf-topology-svg g.fixed use { + stroke-width: 2px; +} +.pf-topology-svg g.selected use, +.pf-topology-svg g.selected circle { + stroke-width: 4px; +} +.pf-topology-svg line { + stroke: #aaa; + stroke-width: 1; +} +.pf-topology-svg g text.attached-label { + display: none; +} +.pf-topology-svg g text.attached-label.visible { + font-size: 12px; + fill: black; + display: block; +} +.pf-topology-svg g.selected { + stroke-width: 4px; +} +.pf-topology-svg g circle { + stroke-width: 2px; +} +.pf-topology-svg g circle.success { + stroke: #3F9C35; +} +.pf-topology-svg g circle.error { + stroke: #CC0000; +} +.pf-topology-svg g circle.warning { + stroke: #EC7A08; +} +.pf-topology-svg g circle.unknown { + stroke: #bbb; +} +.pf-topology-svg g text.glyph { + font-size: 20px; + fill: #1186C1; +} +.pf-topology-map canvas.topology-graph { + width: 100%; +} +.card-view-pf { + overflow: auto; + padding-left: 2px; + padding-top: 20px; +} +.card-view-pf .card { + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + background: #fff; + border-top: 2px solid transparent; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.175); + display: block; + float: left; + height: 290px; + margin-bottom: 20px; + margin-right: 20px; + padding: 10px; + position: relative; + text-align: center; + width: 260px; +} +.card-view-pf .card .card-check-box { + left: 10px; + position: absolute; + top: 8px; + visibility: hidden; + width: 20px; + z-index: 3; +} +.card-view-pf .card:hover { + -moz-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + border: 1px solid #d1d1d1; + box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); +} +.card-view-pf .card:hover .card-check-box { + visibility: visible; +} +.card-view-pf .card:focus { + -moz-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + -webkit-box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); + border: 1px solid #d1d1d1; + box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.24); +} +.card-view-pf .card-content { + height: 100%; + margin: 2px 0 10px 0; + overflow: auto; + width: 100%; +} +.card-view-pf .card-title { + color: #1186C1; + font-size: 16px; + font-weight: 500; + line-height: 1.1; + margin-top: 0px; +} +.card-view-pf .card.active { + border: solid 3px #39a5dc; +} +.card-view-pf .card.active:hover { + border: solid 3px #39a5dc; +} +.card-view-pf .card.active:hover .pficon { + color: #fff; +} +.card-view-pf .card.active:focus { + border: solid 3px #39a5dc; +} +.card-view-pf .card.active:focus .pficon { + color: #fff; +} +.card-view-pf .card.active .pficon { + color: #fff; +} +.card-view-pf .card.active .card-check-box { + visibility: visible; +} +.card-view-pf .card.disabled { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} +.card.disabled:hover { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} +.card.disabled:focus { + -moz-box-shadow: none; + -webkit-box-shadow: none; + border: 1px solid #ededed; + box-shadow: none; + color: #8b8d8f; + cursor: not-allowed; +} +/* overriding pf base so that blank slate fills entire parent container */ +.blank-slate-pf { + height: 100%; + margin-bottom: 0px; +} +.blank-slate-pf button { + margin-right: 4px; +} +.blank-slate-pf .blank-state-pf-helpbutton { + padding-left: 0; + padding-right: 0; + vertical-align: baseline; +} +/* overriding pf base so that buttons have spaces between them */ +.pf-expand-placeholder { + margin-right: 15px; +} +@media (min-width: 768px) { + .toolbar-pf-actions .toolbar-apf-filter { + padding-left: 0; + } +} +.toolbar-pf-actions .toolbar-pf-view-selector a { + cursor: pointer; +} +.toolbar-pf-actions .dropdown-menu a { + cursor: pointer; +} +.toolbar-pf-actions .dropdown-kebab-pf { + float: right; +} +.toolbar-pf-include-actions { + display: inline-block; + margin: 0 5px; +} +.dropdown-kebab-pf.invisible { + opacity: 0; + pointer-events: none; +} +.toolbar-pf-actions.no-filter { + margin-left: -30px; + margin-bottom: 10px; +} +.filter-pf .category-select { + display: flex; +} +.filter-pf .category-select-value { + border-left-width: 0 !important; +} +.filter-pf.filter-fields .form-group { + width: 275px; +} +.filter-select .btn-default { + background-color: #fff; + background-image: none; + color: #8b8d8f; + font-size: 12px; + font-style: italic; + font-weight: 400; +} +pf-filter-panel .dropdown > .dropdown-menu, +pf-filter-panel .input-group-btn > .dropdown-menu { + margin-top: 5px; + opacity: .95; +} +pf-filter-panel input:-moz-placeholder { + font-style: italic; + padding-left: 10px; +} +pf-filter-panel input::-moz-placeholder { + font-style: italic; + padding-left: 10px; +} +pf-filter-panel input:-ms-input-placeholder { + font-style: italic; + padding-left: 10px; +} +pf-filter-panel input::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; +} +pf-filter-panel .inline-filter-pf > .dropdown { + margin-right: 10px; +} +pf-filter-panel .filter-panel-container { + padding: 10px; +} +pf-filter-panel .filter-panel-container .keyword-filter { + margin-bottom: 10px; +} +pf-filter-panel .filter-panel-container ul { + list-style-type: none; + padding-left: 16px; +} +pf-filter-panel .filter-panel-container .category { + font-size: 15px; + font-weight: 400; +} +pf-filter-panel .filter-panel-container .category .category-option-label { + font-size: 12px; + vertical-align: text-bottom; +} +.filter-pf-active-label { + margin-right: 5px; +} +.filter-pf-category-item { + margin-bottom: 5px; +} +.pf-filter-category-label { + font-weight: 700; + padding: 5px 0 6px 5px; + margin-right: 5px; +} +.pf-filter-category-label.multiples { + background-color: #39a5dc; + padding-right: 5px; +} +.filter-pf-category-values.list-inline { + margin-left: 0; +} +.filter-pf-category-values.list-inline > li { + margin-left: 5px; + padding: 0; +} +.filter-pf.inline-filter-pf pf-filter-fields, +.filter-pf.inline-filter-pf pf-filter-results, +.filter-pf.inline-filter-pf pf-filter-panel-results { + display: inline-block; +} +.filter-pf.inline-filter-pf pf-filter-fields { + vertical-align: middle; +} +.filter-pf.inline-filter-pf .filter-fields > .form-group { + margin-bottom: 3px; + margin-right: 15px; +} +.filter-pf.inline-filter-pf .toolbar-pf-results { + border-top: none; + margin-top: 0; +} +.sort-pf .btn-link { + color: #252525; + font-size: 16px; + line-height: 1; + margin-left: 10px; + padding: 4px 0; +} +.sort-pf .btn-link:hover { + color: #0088ce; +} +.toast-pf-action > a { + cursor: pointer; +} +.toast-pf .dropdown-menu > li > a { + cursor: pointer; +} +.list-group-item-header.list-group-item-not-selectable { + cursor: inherit; +} +.pf-table-view-selected-label { + float: right; + line-height: 40px; +} +.table-view-container { + background-color: white; +} +table.dataTable.no-footer { + border-bottom: none; +} +.dataTables_wrapper { + border: none; + margin: 0px; + padding: 0px; +} +table.dataTable { + margin: 0; + border-collapse: collapse; +} +table.dataTable thead .sorting_asc { + background-image: none !important; +} +table.dataTable thead .sorting_desc { + background-image: none !important; +} +table.dataTable thead .sorting { + background-image: none !important; +} +table.dataTable tbody tr.selected span a { + color: #fff; +} +table.dataTable tbody th { + padding: 2px 10px 3px; +} +table.dataTable tbody td { + padding: 2px 10px 3px; +} +.table-view-pf-select { + width: 13px; +} +.wizard-pf-footer .btn-cancel.wizard-pf-cancel-no-back { + margin-right: 0; +} +.wizard-pf-singlestep { + margin-left: 0; +} +.wizard-pf-position-override { + position: relative; +} +.wizard-pf-footer-inline { + text-align: left; +} +.wizard-pf-cancel-inline { + margin-left: 25px; +} +.wizard-pf-steps-indicator li a.disabled { + cursor: default; +} +.wizard-pf-steps-indicator li a.disabled:hover .wizard-pf-step-number { + background-color: #fff; + border-color: #bbb; + color: #bbb; +} +.canvas { + height: 756px; + width: 1396px; +} +.read-only { + background-image: none !important; + background-repeat: unset !important; +} +.canvas-in-connection-mode { + background-image: none !important; + background-repeat: unset !important; + background-color: #bbb; +} +.node-header { + display: table; + font-weight: 600; +} +.node-header p { + display: table-cell; + text-align: center; + vertical-align: middle; +} +.invalid-node-header { + fill: #bbb; + color: #bbb; +} +.node-center-icon { + font-size: 72px; +} +.node-rect { + fill: #fff; + stroke-width: 2; + stroke: #bbb; +} +.invalid-node-rect { + fill: #d1d1d1; +} +.invalid-node-img { + opacity: 0.2; +} +.node-toolbar { + background-color: #f5f5f5; + border-bottom: solid 1px #bbb; + border-left: solid 1px #bbb; + border-radius: 5px; + border-right: solid 1px #bbb; + height: 28px; + padding-left: 8px; +} +.node-toolbar-icons { + color: #0088ce; + cursor: pointer; + font-size: 16px; + padding-right: 16px; + padding-top: 4px; +} +.svg-triangle polyline { + fill: #f5f5f5; + stroke-width: 2; + stroke: #bbb; +} +.connecting-mode-rec { + fill: #8b8d8f; +} +.connecting-mode-label { + fill: #fff; + font-size: 16px; +} +.connecting-mode-label-warning { + fill: #cc0000; + font-size: 16px; +} +.connector-icons { + cursor: pointer; + fill: #0088ce; + font-size: 16px; +} +.connector-icons:hover { + fill: #063451; +} +.mouseover-node-rect { + stroke-width: 3; + stroke: #bbb; +} +.selected-node-rect { + stroke-width: 3; + stroke: #0088ce; +} +.connector-tooltip { + fill: #f5f5f5; + stroke-width: 1; + stroke: #030303; +} +.connector-tooltip-text { + font-size: 10px; +} +.connector-circle { + fill: #fff; + stroke-width: 2; + stroke: #030303; +} +.mouseover-connector-circle { + fill: #fff; + stroke-width: 3; + stroke: #030303; +} +.unconnected-circle { + fill: #2d7623; + stroke-opacity: "0.8"; + stroke-width: 2; + stroke: #030303; +} +.mouseover-unconnected-circle { + fill: #2d7623; + stroke-opacity: "0.8"; + stroke-width: 3; + stroke: #030303; +} +.connection-line { + fill: transparent; + stroke-width: 4; + stroke: #8b8d8f; +} +.mouseover-connection-line { + fill: transparent; + stroke-width: 6; + stroke: #8b8d8f; +} +.selected-connection-line { + fill: transparent; + stroke-width: 4; + stroke: #cc0000; +} +.connection-endpoint { + fill: #8b8d8f; +} +.selected-connection-endpoint { + fill: #cc0000; +} +.mouseover-connection-endpoint { + fill: #8b8d8f; +} +.connection-name { + fill: #030303; +} +.selected-connection-name { + fill: #cc0000; +} +.mouseover-connection-name { + fill: #030303; +} +.dragging-connection { + pointer-events: none; +} +.dragging-connection-line { + fill: transparent; + stroke-width: 3; + stroke: #8b8d8f; +} +.dragging-connection-endpoint { + fill: #8b8d8f; +} +.draggable-container { + border: solid 1px #d1d1d1; +} +.drag-selection-rect { + fill: transparent; + stroke-width: 2; + stroke: #0088ce; +} +.node-dialog-close { + font-size: 12px; + padding-top: 7px; +} +.nodetoolbar-dialog { + left: -19px; + margin-top: 4px; + padding: 8px; + position: fixed; + top: 19px; + width: 300px; +} +.nodetoolbar-dialog .tag-list-action { + color: #fff; + cursor: pointer; +} +.tag-dialog { + left: 30px; + width: 353px; +} +.node-tag-title { + font-weight: 600; + padding-bottom: 12px; + padding-top: 12px; +} +.edit-dialog { + left: 5px; +} +.arrow-box { + background: #fff; + border-radius: 5px; + border: 1px solid #4d5258; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + position: relative; +} +.arrow-box::after { + border-bottom-color: #fff; + border-color: #393f44; + border-width: 8px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41%; + pointer-events: none; + position: absolute; + width: 0; +} +.arrow-box::before { + border-bottom-color: #4d5258; + border-color: rgba(81, 77, 82, 0); + border-width: 10px; + border: solid transparent; + bottom: 100%; + content: " "; + height: 0; + left: 50%; + margin-left: -41.5%; + pointer-events: none; + position: absolute; + width: 0; +} +.canvas-editor-container { + background-color: #fff; + height: 100%; + width: 100%; +} +.canvas-editor-container .canvas-editor-toolbar { + padding: 15px; +} +.canvas-editor-container .canvas-editor-toolbar a { + color: #72767b; + cursor: pointer; +} +.canvas-editor-container .canvas-editor-toolbar a.disabled, +.canvas-editor-container .canvas-editor-toolbar a:hover.disabled { + color: #bbb; + cursor: not-allowed; +} +.canvas-editor-container .canvas-editor-toolbar a:hover { + color: #393f44; +} +.canvas-editor-container .canvas-editor-toolbar button { + margin-right: 6px; +} +.canvas-editor-container .canvas-editor-toolbar .more-actions { + border-left: solid 2px #d1d1d1; + margin-left: 6px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbar .pficon { + font-size: 20px; + margin-left: 15px; + margin-top: -4px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbar .right-aligned-controls { + display: block; + float: right; + font-size: 12px; + font-weight: 600; + padding-right: 5px; + padding-top: 2px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbar .show-hide-connectors-label { + vertical-align: 2px; +} +.canvas-editor-container .canvas-editor-toolbox-container { + -moz-placeholder-font-style: italic; + -moz-placeholder-padding-left: 10px; + -ms-input-placeholder-font-style: italic; + -ms-input-placeholder-padding-left: 10px; + height: 100%; + position: relative; + width: 100%; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox { + background-color: rgba(255, 255, 255, 0.94); + border: 1.5px solid #d1d1d1; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + padding-bottom: 10px; + padding-left: 26px; + padding-right: 26px; + padding-top: 26px; + position: absolute; + top: -1px; + width: 100%; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .draggable-item-icon { + font-size: 28px; + padding-right: 14px; + padding-top: 6px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-pre-title { + font-size: 12px; + margin-bottom: -6px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-title { + font-size: 14px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-single-line { + padding-bottom: 8px; + padding-top: 8px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-items-list { + list-style: none; + max-height: 300px; + overflow-y: auto; + padding-bottom: 4px; + padding-left: 15px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item { + background-color: rgba(255, 255, 255, 0.94); + border-radius: 1px; + border: 1px solid #bbb; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + cursor: pointer; + float: left; + height: 36px; + margin-right: 16px; + margin-top: 16px; + min-height: 52px; + min-width: 325px; + padding: 6px 28px; + position: relative; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item .fa { + font-size: 26px; + margin-right: 8px; + margin-top: 2px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item span { + position: relative; + top: 4px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item img { + height: 100%; + margin-right: 8px; + object-fit: contain; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item::before { + background-image: linear-gradient(to bottom, #0088ce 60%, #fff 0%); + background-position: left; + background-repeat: repeat-y; + background-size: 2px 5px; + border: 4px solid #0088ce; + bottom: 4px; + content: ""; + left: 4px; + position: absolute; + top: 3px; + width: 10px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .not-draggable { + background-color: #ededed; + cursor: auto; + opacity: 0.4; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter { + margin-top: 10px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .search-text { + border: 1px solid #bbb; + float: right; + position: relative; + text-decoration: none; + width: 250px; + padding-left: 6px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text { + bottom: -6px; + color: #bbb; + cursor: pointer; + float: right; + position: relative; + right: -242px; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text span { + font-size: 20px; + padding-right: 14px; + vertical-align: middle; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox { + font-size: 16px; + float: right; + position: relative; + top: -17px; + right: -12px; + color: #72767b; + cursor: pointer; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox :hover { + color: #393f44; +} +.canvas-editor-container .canvas-editor-toolbox-container .canvas-container { + height: 756px; + margin-left: 15px; + overflow: auto; + width: 98%; +} +.canvas-editor-container .canvas-editor-toolbox-container a { + color: #000000; +} +.canvas-editor-container .canvas-editor-toolbox-container::-webkit-input-placeholder { + font-style: italic; + padding-left: 10px; +} +.canvas-editor-container .canvas-editor-toolbox-container::-moz-placeholder { + font-style: italic; + padding-left: 10px; +} +.subtabs .nav-tabs-pf { + padding-left: 18px; +} +.subtabs .nav-tabs-pf li { + font-size: 14px; +} +.content-view-pf-pagination .form-group { + align-items: center; +} +.content-view-pf-pagination .form-group .per-page-label { + padding-left: 10px; +} +.uib-datepicker-popup { + padding: 4px; +} +.uib-datepicker-popup *:focus { + outline: none; +} +.uib-datepicker-popup button { + background: #fff; + border: none; + box-shadow: none; +} +.uib-datepicker-popup th { + height: 30px; +} +.uib-datepicker-popup .uib-title { + font-size: 14px; + font-weight: 500; + padding: 5px; +} +.uib-datepicker-popup .uib-title strong { + font-weight: normal; +} +.uib-datepicker-popup .uib-left { + height: 30px; +} +.uib-datepicker-popup .uib-left .glyphicon { + display: none; +} +.uib-datepicker-popup .uib-left::before { + content: "\00AB"; +} +.uib-datepicker-popup .uib-right { + height: 30px; +} +.uib-datepicker-popup .uib-right .glyphicon { + display: none; +} +.uib-datepicker-popup .uib-right::before { + content: "\00BB"; +} +.uib-datepicker-popup .uib-day button.btn.btn-default { + height: 30px; + width: 30px; +} +.uib-datepicker-popup .uib-day button.btn.btn-default:hover { + background: #def3ff; +} +.uib-datepicker-popup .uib-day button.btn.btn-default.active { + background: #0088ce; + border-color: #004368; + color: #fff; + box-shadow: none; + padding: 0; +} +.uib-datepicker-popup .uib-day button.btn.btn-default.active .text-info { + color: #fff; +} +.uib-datepicker-popup .uib-day button.btn.btn-default:not(.active) .text-info { + color: #393f44; + background: #f9d67a; + height: 30px; + width: 30px; + padding: 7px; + display: inline-block; + margin: -1px -7px -2px -7px; +} +.uib-datepicker-popup .uib-day button.btn.btn-default:not(.active) .text-info:hover { + background: #def3ff; +} +.uib-datepicker-popup .uib-button-bar { + padding: 0; +} +.uib-datepicker-popup .uib-button-bar .btn-group { + width: 100%; +} +.uib-datepicker-popup .uib-button-bar .btn-group .uib-clear { + display: none; +} +.uib-datepicker-popup .uib-button-bar .btn-group .uib-datepicker-current { + color: #030303; + width: 100%; + font-size: 14px; + font-weight: 500; + height: 30px; +} +.uib-datepicker-popup .uib-button-bar .btn-group .uib-datepicker-current:hover { + background: #def3ff; +} +.uib-datepicker-popup .uib-button-bar .uib-close { + display: none; +} +.modal-overlay .modal-body { + max-height: 70vh; + overflow-x: hidden; +} +.navbar-header.ignore-mobile { + float: left; +} +.navbar-collapse.ignore-mobile { + width: auto; + border-top: 0; + box-shadow: none; + display: block; +} +.navbar-collapse.ignore-mobile .navbar-nav { + float: left; + margin: 0; +} +.navbar-collapse.ignore-mobile .navbar-nav.navbar-right { + float: right; +} +.navbar-collapse.ignore-mobile .navbar-nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.navbar-collapse.ignore-mobile .navbar-nav > li { + float: left; +} +.navbar-collapse.ignore-mobile .navbar-nav .open .dropdown-menu { + position: absolute; + margin-top: 0; + background-color: #fff; + border: 1px solid #bbb; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} diff --git a/dist/styles/angular-patternfly.min.css b/dist/styles/angular-patternfly.min.css new file mode 100644 index 000000000..2c71ac328 --- /dev/null +++ b/dist/styles/angular-patternfly.min.css @@ -0,0 +1 @@ +.navbar-brand-txt{line-height:34px}accordion>.panel-group .panel-default .panel-title>a:before,uib-accordion>.panel-group .panel-default .panel-title>a:before{content:"\f105"}accordion>.panel-group .panel-open .panel-title>a:before,uib-accordion>.panel-group .panel-open .panel-title>a:before{content:"\f107"}.card-pf-aggregate-status-alt .card-pf-body{padding-bottom:20px}.card-pf-aggregate-status-alt .card-pf-title{font-weight:300;line-height:22px;margin:20px 0 10px}.card-pf-aggregate-status-alt .card-pf-aggregate-status-count{font-size:24px}.card-pf-aggregate-status-alt .card-pf-aggregate-status-title{display:block;font-size:12px}.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .card-pf-aggregate-status-notification{border-left:none}.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .fa,.card-pf-aggregate-status-alt .card-pf-aggregate-status-notifications .pficon{position:relative;top:-1px}.card-pf.card-pf-aggregate-status .card-pf-body .spinner-container{position:static}.card-pf .hide-for-spinner{opacity:0}.card-pf .card-pf-body.show-spinner{position:relative}.card-pf .card-pf-body .spinner-container{display:flex;justify-content:center;left:0;position:absolute;right:0;top:-20px;bottom:0}.card-pf .card-pf-body .spinner-container .loading-indicator{align-items:center;justify-content:center;display:flex}.card-pf .card-pf-body .spinner-container .loading-indicator .loading-text{font-size:16px;margin-left:10px}.card-pf .card-pf-body .spinner-container .loading-indicator .spinner.spinner-lg{display:inline-block}.card-pf-info-status .card-pf-body .spinner-container{top:0;height:100%}.card-pf-info-status .card-pf-body .spinner-container.with-title{top:12px}.card-pf-aggregate-status-mini .card-pf-body.show-spinner{height:40px}.card-pf-aggregate-status-mini .card-pf-body .spinner-container{top:-22px}.card-pf-heading-no-bottom{margin:0 -20px;padding:0 20px}.card-pf-icon-image{height:18px;margin:0 5px 5px}.trend-card-large-pf .trend-header-pf{display:block;font-size:16px;font-weight:400;margin-left:10px}.trend-card-large-pf .trend-title-big-pf{font-size:26px;font-weight:300;margin-left:10px}.trend-card-large-pf .trend-title-small-pf{font-size:12px;font-weight:400}.trend-card-small-pf .trend-header-pf{display:block;font-size:12px;font-weight:400;margin-left:10px}.trend-card-small-pf .trend-title-big-pf{font-size:17px;font-weight:400;margin-left:10px}.trend-card-small-pf .trend-title-small-pf{font-size:10px;font-weight:400}.card-pf-info-status{display:flex}.card-pf-info-status .card-pf-info-image{display:flex;align-items:center;justify-content:center;flex-direction:column;margin-right:15px}.card-pf-info-status .card-pf-info-image .info-icon{font-size:50px}.card-pf-info-status .card-pf-info-image .info-img{max-height:50px}.card-pf-info-status .card-pf-info-content{margin:10px 0}.card-pf-info-status .card-pf-info-content .card-pf-title{margin-top:10px;margin-bottom:15px}.card-pf-info-status .show-spinner{width:100%}pf-c3-chart{display:block}.empty-chart-content{text-align:center}.empty-chart-content .pficon{font-size:24px}.empty-chart-content span{vertical-align:middle;width:100%}.utilization-trend-chart-pf .donut-chart-pf{float:left;padding-top:15px;width:100%}.utilization-trend-chart-pf h3{font-weight:400}.utilization-trend-chart-pf .current-values{border-bottom:1px solid #d1d1d1;float:left;padding:0 5px 10px 0;width:100%}.utilization-trend-chart-pf .available-count{margin:3px 0;padding-left:0;padding-right:5px}.utilization-trend-chart-pf .available-text{font-size:12px;font-weight:400;line-height:14px;margin:2px 0;padding:0 5px}.utilization-trend-chart-pf .radial-chart{float:left;padding-top:10px;width:100%}.utilization-trend-chart-pf .sparkline-chart{float:left;margin-left:-5px;margin-right:-5px;width:100%}.utilization-trend-chart-pf .legend-text{color:inherit;display:block;font-size:12px;font-weight:400;margin-left:0}.utilization-trend-chart-pf.data-unavailable-pf .current-values{color:transparent}.trend-card-compact-pf .col-sm-2:not(.col-sm-push-10){padding-right:0}.trend-card-compact-pf .col-sm-10:not(.col-sm-pull-2){padding-left:0}.trend-flat-details{display:table;margin-top:5px}@media (min-width:768px){.trend-flat-details{margin-top:25px}}.trend-flat-details-cell{display:table-cell;min-width:70px;vertical-align:bottom}.trend-header-compact-pf{display:block;font-size:12px;font-weight:400}.trend-title-compact-big-pf{font-size:36px;font-weight:300;line-height:1}.trend-title-compact-small-pf{font-size:12px;font-weight:400}.trend-title-flat-big-pf{font-size:26px;font-weight:300;line-height:1;margin-right:15px}.trend-label-flat-pf{font-size:12px;font-weight:400;line-height:1}.trend-label-flat-strong-pf{display:block;font-size:12px;font-weight:700;line-height:1}.trend-footer-pf{color:#333;font-size:10px;font-weight:400;margin-left:10px}.data-unavailable-pf .trend-footer-pf,.data-unavailable-pf[class*=trend-title-],.data-unavailable-pf[class*=trend-label-]{color:transparent}.utilization-bar-chart-pf .progress-bar{-moz-transition:width .75s ease-in-out;-o-transition:width .75s ease-in-out;-webkit-transition:width .75s ease-in-out;transition:width .75s ease-in-out}.utilization-bar-chart-pf .progress-bar.animate{width:0%!important}.heatmap-pf-container{position:relative}.heatmap-pf-container .heatmap-container{margin-left:-1px}.heatmap-pf-container-pf .loading{position:absolute;right:50%;top:100px;z-index:10}.heatmap-pf-svg{height:100%;width:100%}.heatmap-pf-legend-container{list-style-type:none;margin-top:5px;overflow:auto;padding:0}.heatmap-pf-legend-items{float:left}.legend-pf-color-box{display:inline-block;height:11px;margin-left:5px;margin-right:5px;width:11px}.legend-pf-color-box:first-of-type{margin-left:0}.legend-pf-text{font-size:11px;font-weight:400;line-height:11px;padding-right:5px}.camelcase{text-transform:capitalize}pf-topology{display:block;user-select:none}.container-topology pf-topology{height:500px;position:relative}.container-topology .canvas{position:absolute}.container-topology .popup{position:absolute;left:0;top:0;background-color:#fff;width:180px;border:1px solid #ccc;border-radius:6px;box-shadow:#333 2px 2px 4px;padding:6px;font-size:14px}.container-topology .popup h5{font-weight:700}.container-topology .popup p{margin:0 0 4px}.container-topology .popup p:hover{color:#09c;cursor:pointer}.container-topology label.checkbox-inline{font-size:14px}.pf-topology-svg g{font-family:PatternFlyIcons-webfont;font-size:18px;text-anchor:middle;cursor:pointer}.pf-topology-svg g text{stroke:none;stroke-width:0}.pf-topology-svg g.weak use{opacity:.6}.pf-topology-svg g circle{stroke:#aaa;fill:#fff}.pf-topology-svg g.fixed use{stroke-width:2px}.pf-topology-svg g.selected circle,.pf-topology-svg g.selected use{stroke-width:4px}.pf-topology-svg line{stroke:#aaa;stroke-width:1}.pf-topology-svg g text.attached-label{display:none}.pf-topology-svg g text.attached-label.visible{font-size:12px;fill:#000;display:block}.pf-topology-svg g.selected{stroke-width:4px}.pf-topology-svg g circle{stroke-width:2px}.pf-topology-svg g circle.success{stroke:#3F9C35}.pf-topology-svg g circle.error{stroke:#C00}.pf-topology-svg g circle.warning{stroke:#EC7A08}.pf-topology-svg g circle.unknown{stroke:#bbb}.pf-topology-svg g text.glyph{font-size:20px;fill:#1186C1}.pf-topology-map canvas.topology-graph{width:100%}.card-view-pf{overflow:auto;padding-left:2px;padding-top:20px}.card-view-pf .card{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.175);background:#fff;border-top:2px solid transparent;box-shadow:0 1px 1px rgba(0,0,0,.175);display:block;float:left;height:290px;margin-bottom:20px;margin-right:20px;padding:10px;position:relative;text-align:center;width:260px}.canvas-in-connection-mode,.read-only{background-repeat:unset!important}.card-view-pf .card .card-check-box{left:10px;position:absolute;top:8px;visibility:hidden;width:20px;z-index:3}.card-view-pf .card.active .card-check-box,.card-view-pf .card:hover .card-check-box{visibility:visible}.card-view-pf .card:hover{-moz-box-shadow:0 3px 10px -2px rgba(0,0,0,.24);-webkit-box-shadow:0 3px 10px -2px rgba(0,0,0,.24);border:1px solid #d1d1d1;box-shadow:0 3px 10px -2px rgba(0,0,0,.24)}.card-view-pf .card:focus{-moz-box-shadow:0 3px 10px -2px rgba(0,0,0,.24);-webkit-box-shadow:0 3px 10px -2px rgba(0,0,0,.24);border:1px solid #d1d1d1;box-shadow:0 3px 10px -2px rgba(0,0,0,.24)}.card-view-pf .card.active,.card-view-pf .card.active:focus,.card-view-pf .card.active:hover{border:3px solid #39a5dc}.card-view-pf .card-content{height:100%;margin:2px 0 10px;overflow:auto;width:100%}.card-view-pf .card-title{color:#1186C1;font-size:16px;font-weight:500;line-height:1.1;margin-top:0}.card-view-pf .card.active .pficon,.card-view-pf .card.active:focus .pficon,.card-view-pf .card.active:hover .pficon{color:#fff}.card-view-pf .card.disabled,.card.disabled:focus,.card.disabled:hover{-moz-box-shadow:none;-webkit-box-shadow:none;border:1px solid #ededed;box-shadow:none;color:#8b8d8f;cursor:not-allowed}.toast-pf .dropdown-menu>li>a,.toast-pf-action>a,.toolbar-pf-actions .dropdown-menu a,.toolbar-pf-actions .toolbar-pf-view-selector a{cursor:pointer}.blank-slate-pf{height:100%;margin-bottom:0}.blank-slate-pf button{margin-right:4px}.blank-slate-pf .blank-state-pf-helpbutton{padding-left:0;padding-right:0;vertical-align:baseline}.pf-expand-placeholder{margin-right:15px}@media (min-width:768px){.toolbar-pf-actions .toolbar-apf-filter{padding-left:0}}.toolbar-pf-actions .dropdown-kebab-pf{float:right}.toolbar-pf-include-actions{display:inline-block;margin:0 5px}.dropdown-kebab-pf.invisible{opacity:0;pointer-events:none}.toolbar-pf-actions.no-filter{margin-left:-30px;margin-bottom:10px}.filter-pf .category-select{display:flex}.filter-pf .category-select-value{border-left-width:0!important}.filter-pf.filter-fields .form-group{width:275px}.filter-select .btn-default{background-color:#fff;background-image:none;color:#8b8d8f;font-size:12px;font-style:italic;font-weight:400}.canvas-in-connection-mode,.read-only,table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{background-image:none!important}pf-filter-panel .dropdown>.dropdown-menu,pf-filter-panel .input-group-btn>.dropdown-menu{margin-top:5px;opacity:.95}pf-filter-panel input:-moz-placeholder{font-style:italic;padding-left:10px}pf-filter-panel input::-moz-placeholder{font-style:italic;padding-left:10px}pf-filter-panel input:-ms-input-placeholder{font-style:italic;padding-left:10px}pf-filter-panel input::-webkit-input-placeholder{font-style:italic;padding-left:10px}pf-filter-panel .inline-filter-pf>.dropdown{margin-right:10px}pf-filter-panel .filter-panel-container{padding:10px}pf-filter-panel .filter-panel-container .keyword-filter{margin-bottom:10px}pf-filter-panel .filter-panel-container ul{list-style-type:none;padding-left:16px}pf-filter-panel .filter-panel-container .category{font-size:15px;font-weight:400}pf-filter-panel .filter-panel-container .category .category-option-label{font-size:12px;vertical-align:text-bottom}.filter-pf-active-label{margin-right:5px}.filter-pf-category-item{margin-bottom:5px}.pf-filter-category-label{font-weight:700;padding:5px 0 6px 5px;margin-right:5px}.pf-filter-category-label.multiples{background-color:#39a5dc;padding-right:5px}.filter-pf-category-values.list-inline{margin-left:0}.filter-pf-category-values.list-inline>li{margin-left:5px;padding:0}.filter-pf.inline-filter-pf pf-filter-fields,.filter-pf.inline-filter-pf pf-filter-panel-results,.filter-pf.inline-filter-pf pf-filter-results{display:inline-block}.filter-pf.inline-filter-pf pf-filter-fields{vertical-align:middle}.filter-pf.inline-filter-pf .filter-fields>.form-group{margin-bottom:3px;margin-right:15px}.filter-pf.inline-filter-pf .toolbar-pf-results{border-top:none;margin-top:0}.sort-pf .btn-link{color:#252525;font-size:16px;line-height:1;margin-left:10px;padding:4px 0}.sort-pf .btn-link:hover{color:#0088ce}.list-group-item-header.list-group-item-not-selectable{cursor:inherit}.pf-table-view-selected-label{float:right;line-height:40px}.table-view-container{background-color:#fff}table.dataTable.no-footer{border-bottom:none}.dataTables_wrapper{border:none;margin:0;padding:0}table.dataTable{margin:0;border-collapse:collapse}table.dataTable tbody tr.selected span a{color:#fff}table.dataTable tbody td,table.dataTable tbody th{padding:2px 10px 3px}.table-view-pf-select{width:13px}.wizard-pf-footer .btn-cancel.wizard-pf-cancel-no-back{margin-right:0}.wizard-pf-singlestep{margin-left:0}.wizard-pf-position-override{position:relative}.wizard-pf-footer-inline{text-align:left}.wizard-pf-cancel-inline{margin-left:25px}.wizard-pf-steps-indicator li a.disabled{cursor:default}.wizard-pf-steps-indicator li a.disabled:hover .wizard-pf-step-number{background-color:#fff;border-color:#bbb;color:#bbb}.canvas{height:756px;width:1396px}.canvas-in-connection-mode{background-color:#bbb}.node-header{display:table;font-weight:600}.node-header p{display:table-cell;text-align:center;vertical-align:middle}.invalid-node-header{fill:#bbb;color:#bbb}.node-center-icon{font-size:72px}.node-rect{fill:#fff;stroke-width:2;stroke:#bbb}.invalid-node-rect{fill:#d1d1d1}.invalid-node-img{opacity:.2}.node-toolbar{background-color:#f5f5f5;border-bottom:solid 1px #bbb;border-left:solid 1px #bbb;border-radius:5px;border-right:solid 1px #bbb;height:28px;padding-left:8px}.node-toolbar-icons{color:#0088ce;cursor:pointer;font-size:16px;padding-right:16px;padding-top:4px}.svg-triangle polyline{fill:#f5f5f5;stroke-width:2;stroke:#bbb}.connecting-mode-rec{fill:#8b8d8f}.connecting-mode-label{fill:#fff;font-size:16px}.connecting-mode-label-warning{fill:#c00;font-size:16px}.connector-icons{cursor:pointer;fill:#0088ce;font-size:16px}.connector-icons:hover{fill:#063451}.mouseover-node-rect{stroke-width:3;stroke:#bbb}.selected-node-rect{stroke-width:3;stroke:#0088ce}.connector-tooltip{fill:#f5f5f5;stroke-width:1;stroke:#030303}.connector-tooltip-text{font-size:10px}.connector-circle{fill:#fff;stroke-width:2;stroke:#030303}.mouseover-connector-circle{fill:#fff;stroke-width:3;stroke:#030303}.unconnected-circle{fill:#2d7623;stroke-opacity:"0.8";stroke-width:2;stroke:#030303}.mouseover-unconnected-circle{fill:#2d7623;stroke-opacity:"0.8";stroke-width:3;stroke:#030303}.connection-line{fill:transparent;stroke-width:4;stroke:#8b8d8f}.mouseover-connection-line{fill:transparent;stroke-width:6;stroke:#8b8d8f}.selected-connection-line{fill:transparent;stroke-width:4;stroke:#c00}.connection-endpoint{fill:#8b8d8f}.selected-connection-endpoint{fill:#c00}.mouseover-connection-endpoint{fill:#8b8d8f}.connection-name{fill:#030303}.selected-connection-name{fill:#c00}.mouseover-connection-name{fill:#030303}.dragging-connection{pointer-events:none}.dragging-connection-line{fill:transparent;stroke-width:3;stroke:#8b8d8f}.dragging-connection-endpoint{fill:#8b8d8f}.draggable-container{border:1px solid #d1d1d1}.drag-selection-rect{fill:transparent;stroke-width:2;stroke:#0088ce}.node-dialog-close{font-size:12px;padding-top:7px}.nodetoolbar-dialog{left:-19px;margin-top:4px;padding:8px;position:fixed;top:19px;width:300px}.nodetoolbar-dialog .tag-list-action{color:#fff;cursor:pointer}.tag-dialog{left:30px;width:353px}.node-tag-title{font-weight:600;padding-bottom:12px;padding-top:12px}.edit-dialog{left:5px}.arrow-box{background:#fff;border-radius:5px;border:1px solid #4d5258;padding-bottom:10px;padding-left:10px;padding-right:10px;position:relative}.arrow-box::after,.arrow-box::before{border:solid transparent;bottom:100%;content:" ";height:0;left:50%;pointer-events:none;position:absolute;width:0}.arrow-box::after{margin-left:-41%}.arrow-box::before{margin-left:-41.5%}.canvas-editor-container{background-color:#fff;height:100%;width:100%}.canvas-editor-container .canvas-editor-toolbar{padding:15px}.canvas-editor-container .canvas-editor-toolbar a{color:#72767b;cursor:pointer}.canvas-editor-container .canvas-editor-toolbar a.disabled,.canvas-editor-container .canvas-editor-toolbar a:hover.disabled{color:#bbb;cursor:not-allowed}.canvas-editor-container .canvas-editor-toolbar a:hover{color:#393f44}.canvas-editor-container .canvas-editor-toolbar button{margin-right:6px}.canvas-editor-container .canvas-editor-toolbar .more-actions{border-left:solid 2px #d1d1d1;margin-left:6px;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbar .pficon{font-size:20px;margin-left:15px;margin-top:-4px;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbar .right-aligned-controls{display:block;float:right;font-size:12px;font-weight:600;padding-right:5px;padding-top:2px;vertical-align:middle}.uib-datepicker-popup .uib-left .glyphicon,.uib-datepicker-popup .uib-right .glyphicon{display:none}.canvas-editor-container .canvas-editor-toolbar .show-hide-connectors-label{vertical-align:2px}.canvas-editor-container .canvas-editor-toolbox-container{-moz-placeholder-font-style:italic;-moz-placeholder-padding-left:10px;-ms-input-placeholder-font-style:italic;-ms-input-placeholder-padding-left:10px;height:100%;position:relative;width:100%}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox{background-color:rgba(255,255,255,.94);border:1.5px solid #d1d1d1;box-shadow:0 2px 6px rgba(0,0,0,.3);position:absolute;top:-1px;width:100%;padding:26px 26px 10px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .draggable-item-icon{font-size:28px;padding-right:14px;padding-top:6px;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-pre-title{font-size:12px;margin-bottom:-6px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-title{font-size:14px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .tab-single-line{padding-bottom:8px;padding-top:8px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-items-list{list-style:none;max-height:300px;overflow-y:auto;padding-bottom:4px;padding-left:15px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item{background-color:rgba(255,255,255,.94);border-radius:1px;border:1px solid #bbb;box-shadow:0 2px 6px rgba(0,0,0,.2);cursor:pointer;float:left;height:36px;margin-right:16px;margin-top:16px;min-height:52px;min-width:325px;padding:6px 28px;position:relative;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item .fa{font-size:26px;margin-right:8px;margin-top:2px;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item span{position:relative;top:4px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item img{height:100%;margin-right:8px;object-fit:contain}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-item::before{background-image:linear-gradient(to bottom,#0088ce 60%,#fff 0);background-position:left;background-repeat:repeat-y;background-size:2px 5px;border:4px solid #0088ce;bottom:4px;content:"";left:4px;position:absolute;top:3px;width:10px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .not-draggable{background-color:#ededed;cursor:auto;opacity:.4}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter{margin-top:10px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .search-text{border:1px solid #bbb;float:right;position:relative;text-decoration:none;width:250px;padding-left:6px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text{bottom:-6px;color:#bbb;cursor:pointer;float:right;position:relative;right:-242px}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .toolbox-filter .clear-search-text span{font-size:20px;padding-right:14px;vertical-align:middle}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox{font-size:16px;float:right;position:relative;top:-17px;right:-12px;color:#72767b;cursor:pointer}.canvas-editor-container .canvas-editor-toolbox-container .canvas-editor-toolbox .close-toolbox :hover{color:#393f44}.canvas-editor-container .canvas-editor-toolbox-container .canvas-container{height:756px;margin-left:15px;overflow:auto;width:98%}.canvas-editor-container .canvas-editor-toolbox-container a{color:#000}.canvas-editor-container .canvas-editor-toolbox-container::-webkit-input-placeholder{font-style:italic;padding-left:10px}.canvas-editor-container .canvas-editor-toolbox-container::-moz-placeholder{font-style:italic;padding-left:10px}.subtabs .nav-tabs-pf{padding-left:18px}.subtabs .nav-tabs-pf li{font-size:14px}.content-view-pf-pagination .form-group{align-items:center}.content-view-pf-pagination .form-group .per-page-label{padding-left:10px}.uib-datepicker-popup{padding:4px}.uib-datepicker-popup :focus{outline:0}.uib-datepicker-popup button{background:#fff;border:none;box-shadow:none}.uib-datepicker-popup th{height:30px}.uib-datepicker-popup .uib-title{font-size:14px;font-weight:500;padding:5px}.uib-datepicker-popup .uib-title strong{font-weight:400}.uib-datepicker-popup .uib-left{height:30px}.uib-datepicker-popup .uib-left::before{content:"\00AB"}.uib-datepicker-popup .uib-right{height:30px}.uib-datepicker-popup .uib-right::before{content:"\00BB"}.uib-datepicker-popup .uib-day button.btn.btn-default{height:30px;width:30px}.uib-datepicker-popup .uib-day button.btn.btn-default:hover{background:#def3ff}.uib-datepicker-popup .uib-day button.btn.btn-default.active{background:#0088ce;border-color:#004368;color:#fff;box-shadow:none;padding:0}.uib-datepicker-popup .uib-day button.btn.btn-default.active .text-info{color:#fff}.uib-datepicker-popup .uib-day button.btn.btn-default:not(.active) .text-info{color:#393f44;background:#f9d67a;height:30px;width:30px;padding:7px;display:inline-block;margin:-1px -7px -2px}.uib-datepicker-popup .uib-button-bar .btn-group .uib-datepicker-current:hover,.uib-datepicker-popup .uib-day button.btn.btn-default:not(.active) .text-info:hover{background:#def3ff}.uib-datepicker-popup .uib-button-bar .btn-group .uib-clear,.uib-datepicker-popup .uib-button-bar .uib-close{display:none}.uib-datepicker-popup .uib-button-bar{padding:0}.uib-datepicker-popup .uib-button-bar .btn-group{width:100%}.uib-datepicker-popup .uib-button-bar .btn-group .uib-datepicker-current{color:#030303;width:100%;font-size:14px;font-weight:500;height:30px}.modal-overlay .modal-body{max-height:70vh;overflow-x:hidden}.navbar-header.ignore-mobile{float:left}.navbar-collapse.ignore-mobile{width:auto;border-top:0;box-shadow:none;display:block}.navbar-collapse.ignore-mobile .navbar-nav{float:left;margin:0}.navbar-collapse.ignore-mobile .navbar-nav.navbar-right{float:right}.navbar-collapse.ignore-mobile .navbar-nav.navbar-right .dropdown-menu{left:auto;right:0}.navbar-collapse.ignore-mobile .navbar-nav>li{float:left}.navbar-collapse.ignore-mobile .navbar-nav .open .dropdown-menu{position:absolute;margin-top:0;background-color:#fff;border:1px solid #bbb;box-shadow:0 6px 12px rgba(0,0,0,.175)} \ No newline at end of file diff --git a/package.json b/package.json index 69237ea1c..9e2704d90 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Red Hat", "name": "angular-patternfly", - "version": "0.0.0-semantically-released", + "version": "4.18.4", "license": "Apache-2.0", "description": "Angular extension of the PatternFly project.", "keywords": [