blob: 25993558a43c552670ecb41db29e30d9c83a0691 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* global define, module, require, exports */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery',
'Slick',
'nf.Client',
'nf.Birdseye',
'nf.Storage',
'nf.Graph',
'nf.CanvasUtils',
'nf.ErrorHandler',
'nf.FilteredDialogCommon',
'nf.Dialog',
'nf.Common'],
function ($, Slick, nfClient, nfBirdseye, nfStorage, nfGraph, nfCanvasUtils, nfErrorHandler, nfFilteredDialogCommon, nfDialog, nfCommon) {
return (nf.ng.ProcessorComponent = factory($, Slick, nfClient, nfBirdseye, nfStorage, nfGraph, nfCanvasUtils, nfErrorHandler, nfFilteredDialogCommon, nfDialog, nfCommon));
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.ng.ProcessorComponent =
factory(require('jquery'),
require('Slick'),
require('nf.Client'),
require('nf.Birdseye'),
require('nf.Storage'),
require('nf.Graph'),
require('nf.CanvasUtils'),
require('nf.ErrorHandler'),
require('nf.FilteredDialogCommon'),
require('nf.Dialog'),
require('nf.Common')));
} else {
nf.ng.ProcessorComponent = factory(root.$,
root.Slick,
root.nf.Client,
root.nf.Birdseye,
root.nf.Storage,
root.nf.Graph,
root.nf.CanvasUtils,
root.nf.ErrorHandler,
root.nf.FilteredDialogCommon,
root.nf.Dialog,
root.nf.Common);
}
}(this, function ($, Slick, nfClient, nfBirdseye, nfStorage, nfGraph, nfCanvasUtils, nfErrorHandler, nfFilteredDialogCommon, nfDialog, nfCommon) {
'use strict';
return function (serviceProvider) {
'use strict';
/**
* Filters the processor type table.
*/
var applyFilter = function () {
// get the dataview
var processorTypesGrid = $('#processor-types-table').data('gridInstance');
// ensure the grid has been initialized
if (nfCommon.isDefinedAndNotNull(processorTypesGrid)) {
var processorTypesData = processorTypesGrid.getData();
// update the search criteria
processorTypesData.setFilterArgs({
searchString: getFilterText()
});
processorTypesData.refresh();
// update the buttons to possibly trigger the disabled state
$('#new-processor-dialog').modal('refreshButtons');
// update the selection if possible
if (processorTypesData.getLength() > 0) {
nfFilteredDialogCommon.choseFirstRow(processorTypesGrid);
// make the first row visible
processorTypesGrid.scrollRowToTop(0);
}
}
};
/**
* Determines if the item matches the filter.
*
* @param {object} item The item to filter.
* @param {object} args The filter criteria.
* @returns {boolean} Whether the item matches the filter.
*/
var matchesRegex = function (item, args) {
if (args.searchString === '') {
return true;
}
try {
// perform the row filtering
var filterExp = new RegExp(args.searchString, 'i');
} catch (e) {
// invalid regex
return false;
}
// determine if the item matches the filter
var matchesLabel = item['label'].search(filterExp) >= 0;
var matchesTags = item['tags'].search(filterExp) >= 0;
return matchesLabel || matchesTags;
};
/**
* Performs the filtering.
*
* @param {object} item The item subject to filtering.
* @param {object} args Filter arguments.
* @returns {Boolean} Whether or not to include the item.
*/
var filter = function (item, args) {
// determine if the item matches the filter
var matchesFilter = matchesRegex(item, args);
// determine if the row matches the selected tags
var matchesTags = true;
if (matchesFilter) {
var tagFilters = $('#processor-tag-cloud').tagcloud('getSelectedTags');
var hasSelectedTags = tagFilters.length > 0;
if (hasSelectedTags) {
matchesTags = matchesSelectedTags(tagFilters, item['tags']);
}
}
// determine if the row matches the selected source group
var matchesGroup = true;
if (matchesFilter && matchesTags) {
var bundleGroup = $('#processor-bundle-group-combo').combo('getSelectedOption');
if (nfCommon.isDefinedAndNotNull(bundleGroup) && bundleGroup.value !== '') {
matchesGroup = (item.bundle.group === bundleGroup.value);
}
}
// determine if this row should be visible
var matches = matchesFilter && matchesTags && matchesGroup;
// if this row is currently selected and its being filtered
if (matches === false && $('#selected-processor-type').text() === item['type']) {
// clear the selected row
$('#processor-type-description').attr('title', '').text('');
$('#processor-type-name').attr('title', '').text('');
$('#processor-type-bundle').attr('title', '').text('');
$('#selected-processor-name').text('');
$('#selected-processor-type').text('').removeData('bundle');
// clear the active cell the it can be reselected when its included
var processTypesGrid = $('#processor-types-table').data('gridInstance');
processTypesGrid.resetActiveCell();
}
return matches;
};
/**
* Determines if the specified tags match all the tags selected by the user.
*
* @argument {string[]} tagFilters The tag filters.
* @argument {string} tags The tags to test.
*/
var matchesSelectedTags = function (tagFilters, tags) {
var selectedTags = [];
$.each(tagFilters, function (_, filter) {
selectedTags.push(filter);
});
// normalize the tags
var normalizedTags = tags.toLowerCase();
var matches = true;
$.each(selectedTags, function (i, selectedTag) {
if (normalizedTags.indexOf(selectedTag) === -1) {
matches = false;
return false;
}
});
return matches;
};
/**
* Get the text out of the filter field. If the filter field doesn't
* have any text it will contain the text 'filter list' so this method
* accounts for that.
*/
var getFilterText = function () {
return $('#processor-type-filter').val();
};
/**
* Resets the filtered processor types.
*/
var resetProcessorDialog = function () {
// clear the selected tag cloud
$('#processor-tag-cloud').tagcloud('clearSelectedTags');
// reset the group combo
$('#processor-bundle-group-combo').combo('setSelectedOption', {
value: ''
});
// clear any filter strings
$('#processor-type-filter').val('');
// reapply the filter
applyFilter();
// clear the selected row
$('#processor-type-description').attr('title', '').text('');
$('#processor-type-name').attr('title', '').text('');
$('#processor-type-bundle').attr('title', '').text('');
$('#selected-processor-name').text('');
$('#selected-processor-type').text('').removeData('bundle');
// unselect any current selection
var processTypesGrid = $('#processor-types-table').data('gridInstance');
processTypesGrid.setSelectedRows([]);
processTypesGrid.resetActiveCell();
};
/**
* Create the processor and add to the graph.
*
* @argument {string} name The processor name.
* @argument {string} processorType The processor type.
* @argument {object} bundle The processor bundle.
* @argument {object} pt The point that the processor was dropped.
*/
var createProcessor = function (name, processorType, bundle, pt) {
var processorEntity = {
'revision': nfClient.getRevision({
'revision': {
'version': 0
}
}),
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
'component': {
'type': processorType,
'bundle': bundle,
'name': name,
'position': {
'x': pt.x,
'y': pt.y
}
}
};
// create a new processor of the defined type
$.ajax({
type: 'POST',
url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + '/process-groups/' + encodeURIComponent(nfCanvasUtils.getGroupId()) + '/processors',
data: JSON.stringify(processorEntity),
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
// add the processor to the graph
nfGraph.add({
'processors': [response]
}, {
'selectAll': true
});
// update component visibility
nfGraph.updateVisibility();
// update the birdseye
nfBirdseye.refresh();
}).fail(nfErrorHandler.handleAjaxError);
};
/**
* Whether the specified item is selectable.
*
* @param item process type
*/
var isSelectable = function (item) {
return item.restricted === false || nfCommon.canAccessComponentRestrictions(item.explicitRestrictions);
};
function ProcessorComponent() {
this.icon = 'icon icon-processor';
this.hoverIcon = 'icon icon-processor-add';
/**
* The processor component's modal.
*/
this.modal = {
/**
* The processor component modal's filter.
*/
filter: {
/**
* Initialize the filter.
*/
init: function () {
// initialize the processor type table
var processorTypesColumns = [
{
id: 'type',
name: 'Type',
field: 'label',
formatter: nfCommon.typeFormatter,
sortable: true,
resizable: true
},
{
id: 'version',
name: 'Version',
field: 'version',
formatter: nfCommon.typeVersionFormatter,
sortable: true,
resizable: true
},
{
id: 'tags',
name: 'Tags',
field: 'tags',
sortable: true,
resizable: true,
formatter: nfCommon.genericValueFormatter
}
];
var processorTypesOptions = {
autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
enableTextSelectionOnCells: true,
enableCellNavigation: true,
enableColumnReorder: false,
autoEdit: false,
multiSelect: false,
rowHeight: 24
};
// initialize the dataview
var processorTypesData = new Slick.Data.DataView({
inlineFilters: false
});
processorTypesData.setItems([]);
processorTypesData.setFilterArgs({
searchString: getFilterText()
});
processorTypesData.setFilter(filter);
// initialize the sort
nfCommon.sortType({
columnId: 'type',
sortAsc: true
}, processorTypesData);
// initialize the grid
var processorTypesGrid = new Slick.Grid('#processor-types-table', processorTypesData, processorTypesColumns, processorTypesOptions);
processorTypesGrid.setSelectionModel(new Slick.RowSelectionModel());
processorTypesGrid.registerPlugin(new Slick.AutoTooltips());
processorTypesGrid.setSortColumn('type', true);
processorTypesGrid.onSort.subscribe(function (e, args) {
nfCommon.sortType({
columnId: args.sortCol.field,
sortAsc: args.sortAsc
}, processorTypesData);
});
processorTypesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
if ($.isArray(args.rows) && args.rows.length === 1) {
var processorTypeIndex = args.rows[0];
var processorType = processorTypesGrid.getDataItem(processorTypeIndex);
// set the processor type description
if (nfCommon.isDefinedAndNotNull(processorType)) {
if (nfCommon.isBlank(processorType.description)) {
$('#processor-type-description')
.attr('title', '')
.html('<span class="unset">No description specified</span>');
} else {
$('#processor-type-description')
.width($('#processor-description-container').innerWidth() - 1)
.html(processorType.description)
.ellipsis();
}
var bundle = nfCommon.formatBundle(processorType.bundle);
var type = nfCommon.formatType(processorType);
// populate the dom
$('#processor-type-name').text(type).attr('title', type);
$('#processor-type-bundle').text(bundle).attr('title', bundle);
$('#selected-processor-name').text(processorType.label);
$('#selected-processor-type').text(processorType.type).data('bundle', processorType.bundle);
// refresh the buttons based on the current selection
$('#new-processor-dialog').modal('refreshButtons');
}
}
});
processorTypesGrid.onViewportChanged.subscribe(function (e, args) {
nfCommon.cleanUpTooltips($('#processor-types-table'), 'div.view-usage-restriction');
});
// wire up the dataview to the grid
processorTypesData.onRowCountChanged.subscribe(function (e, args) {
processorTypesGrid.updateRowCount();
processorTypesGrid.render();
// update the total number of displayed processors
$('#displayed-processor-types').text(args.current);
});
processorTypesData.onRowsChanged.subscribe(function (e, args) {
processorTypesGrid.invalidateRows(args.rows);
processorTypesGrid.render();
});
processorTypesData.syncGridSelection(processorTypesGrid, false);
// hold onto an instance of the grid
$('#processor-types-table').data('gridInstance', processorTypesGrid).on('mouseenter', 'div.slick-cell', function (e) {
var usageRestriction = $(this).find('div.view-usage-restriction');
if (usageRestriction.length && !usageRestriction.data('qtip')) {
var rowId = $(this).find('span.row-id').text();
// get the status item
var item = processorTypesData.getItemById(rowId);
// show the tooltip
if (item.restricted === true) {
var restrictionTip = $('<div></div>');
if (nfCommon.isBlank(item.usageRestriction)) {
restrictionTip.append($('<p style="margin-bottom: 3px;"></p>').text('Requires the following permissions:'));
} else {
restrictionTip.append($('<p style="margin-bottom: 3px;"></p>').text(item.usageRestriction + ' Requires the following permissions:'));
}
var restrictions = [];
if (nfCommon.isDefinedAndNotNull(item.explicitRestrictions)) {
$.each(item.explicitRestrictions, function (_, explicitRestriction) {
var requiredPermission = explicitRestriction.requiredPermission;
restrictions.push("'" + requiredPermission.label + "' - " + nfCommon.escapeHtml(explicitRestriction.explanation));
});
} else {
restrictions.push('Access to restricted components regardless of restrictions.');
}
restrictionTip.append(nfCommon.formatUnorderedList(restrictions));
usageRestriction.qtip($.extend({}, nfCommon.config.tooltipConfig, {
content: restrictionTip,
position: {
container: $('#summary'),
at: 'bottom right',
my: 'top left',
adjust: {
x: 4,
y: 4
}
}
}));
}
}
});
var generalRestriction = nfCommon.getPolicyTypeListing('restricted-components');
// load the available processor types, this select is shown in the
// new processor dialog when a processor is dragged onto the screen
$.ajax({
type: 'GET',
url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.processorTypes,
dataType: 'json'
}).done(function (response) {
var tags = [];
var groups = d3.set();
var restrictedUsage = d3.map();
var requiredPermissions = d3.map();
// begin the update
processorTypesData.beginUpdate();
// go through each processor type
$.each(response.processorTypes, function (i, documentedType) {
var type = documentedType.type;
if (documentedType.restricted === true) {
if (nfCommon.isDefinedAndNotNull(documentedType.explicitRestrictions)) {
$.each(documentedType.explicitRestrictions, function (_, explicitRestriction) {
var requiredPermission = explicitRestriction.requiredPermission;
// update required permissions
if (!requiredPermissions.has(requiredPermission.id)) {
requiredPermissions.set(requiredPermission.id, requiredPermission.label);
}
// update component restrictions
if (!restrictedUsage.has(requiredPermission.id)) {
restrictedUsage.set(requiredPermission.id, []);
}
restrictedUsage.get(requiredPermission.id).push({
type: nfCommon.formatType(documentedType),
bundle: nfCommon.formatBundle(documentedType.bundle),
explanation: nfCommon.escapeHtml(explicitRestriction.explanation)
})
});
} else {
// update required permissions
if (!requiredPermissions.has(generalRestriction.value)) {
requiredPermissions.set(generalRestriction.value, generalRestriction.text);
}
// update component restrictions
if (!restrictedUsage.has(generalRestriction.value)) {
restrictedUsage.set(generalRestriction.value, []);
}
restrictedUsage.get(generalRestriction.value).push({
type: nfCommon.formatType(documentedType),
bundle: nfCommon.formatBundle(documentedType.bundle),
explanation: nfCommon.escapeHtml(documentedType.usageRestriction)
});
}
}
// record the group
groups.add(documentedType.bundle.group);
// create the row for the processor type
processorTypesData.addItem({
id: i,
label: nfCommon.substringAfterLast(type, '.'),
type: type,
bundle: documentedType.bundle,
description: nfCommon.escapeHtml(documentedType.description),
restricted: documentedType.restricted,
usageRestriction: nfCommon.escapeHtml(documentedType.usageRestriction),
explicitRestrictions: documentedType.explicitRestrictions,
tags: documentedType.tags.join(', ')
});
// count the frequency of each tag for this type
$.each(documentedType.tags, function (i, tag) {
tags.push(tag.toLowerCase());
});
});
// end the update
processorTypesData.endUpdate();
// resort
processorTypesData.reSort();
processorTypesGrid.invalidate();
// set the component restrictions and the corresponding required permissions
nfCanvasUtils.addComponentRestrictions(restrictedUsage, requiredPermissions);
// set the total number of processors
$('#total-processor-types, #displayed-processor-types').text(response.processorTypes.length);
// create the tag cloud
$('#processor-tag-cloud').tagcloud({
tags: tags,
select: applyFilter,
remove: applyFilter
});
// build the combo options
var options = [{
text: 'all groups',
value: ''
}];
groups.each(function (group) {
options.push({
text: group,
value: group
});
});
// initialize the bundle group combo
$('#processor-bundle-group-combo').combo({
options: options,
select: applyFilter
});
}).fail(nfErrorHandler.handleAjaxError);
}
},
/**
* Gets the modal element.
*
* @returns {*|jQuery|HTMLElement}
*/
getElement: function () {
return $('#new-processor-dialog');
},
/**
* Initialize the modal.
*/
init: function () {
this.filter.init();
// configure the new processor dialog
this.getElement().modal({
scrollableContentStyle: 'scrollable',
headerText: 'Add Processor',
handler: {
resize: function () {
$('#processor-type-description')
.width($('#processor-description-container').innerWidth() - 1)
.text($('#processor-type-description').attr('title'))
.ellipsis();
}
}
});
},
/**
* Updates the modal config.
*
* @param {string} name The name of the property to update.
* @param {object|array} config The config for the `name`.
*/
update: function (name, config) {
this.getElement().modal(name, config);
},
/**
* Show the modal
*/
show: function () {
this.getElement().modal('show');
},
/**
* Hide the modal
*/
hide: function () {
this.getElement().modal('hide');
}
};
}
ProcessorComponent.prototype = {
constructor: ProcessorComponent,
/**
* Gets the component.
*
* @returns {*|jQuery|HTMLElement}
*/
getElement: function () {
return $('#processor-component');
},
/**
* Enable the component.
*/
enabled: function () {
this.getElement().attr('disabled', false);
},
/**
* Disable the component.
*/
disabled: function () {
this.getElement().attr('disabled', true);
},
/**
* Handler function for when component is dropped on the canvas.
*
* @argument {object} pt The point that the component was dropped
*/
dropHandler: function (pt) {
this.promptForProcessorType(pt);
},
/**
* The drag icon for the toolbox component.
*
* @param event
* @returns {*|jQuery|HTMLElement}
*/
dragIcon: function (event) {
return $('<div class="icon icon-processor-add"></div>');
},
/**
* Prompts the user to select the type of new processor to create.
*
* @argument {object} pt The point that the processor was dropped
*/
promptForProcessorType: function (pt) {
var processorComponent = this;
// handles adding the selected processor at the specified point
var addProcessor = function () {
// get the type of processor currently selected
var name = $('#selected-processor-name').text();
var processorType = $('#selected-processor-type').text();
var bundle = $('#selected-processor-type').data('bundle');
// ensure something was selected
if (name === '' || processorType === '') {
nfDialog.showOkDialog({
headerText: 'Add Processor',
dialogContent: 'The type of processor to create must be selected.'
});
} else {
// create the new processor
createProcessor(name, processorType, bundle, pt);
}
// hide the dialog
processorComponent.modal.hide();
};
// get the grid reference
var grid = $('#processor-types-table').data('gridInstance');
var dataview = grid.getData();
// add the processor when its double clicked in the table
var gridDoubleClick = function (e, args) {
var processorType = grid.getDataItem(args.row);
if (isSelectable(processorType)) {
$('#selected-processor-name').text(processorType.label);
$('#selected-processor-type').text(processorType.type).data('bundle', processorType.bundle);
addProcessor();
}
};
// register a handler for double click events
grid.onDblClick.subscribe(gridDoubleClick);
// update the button model
this.modal.update('setButtonModel', [{
buttonText: 'Add',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
disabled: function () {
var selected = grid.getSelectedRows();
if (selected.length > 0) {
// grid configured with multi-select = false
var item = grid.getDataItem(selected[0]);
return isSelectable(item) === false;
} else {
return dataview.getLength() === 0;
}
},
handler: {
click: addProcessor
}
},
{
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
$('#new-processor-dialog').modal('hide');
}
}
}]);
// set a new handler for closing the the dialog
this.modal.update('setCloseHandler', function () {
// remove the handler
grid.onDblClick.unsubscribe(gridDoubleClick);
// clear the current filters
resetProcessorDialog();
});
// show the dialog
this.modal.show();
var navigationKeys = [$.ui.keyCode.UP, $.ui.keyCode.PAGE_UP, $.ui.keyCode.DOWN, $.ui.keyCode.PAGE_DOWN];
// setup the filter
$('#processor-type-filter').off('keyup').on('keyup', function (e) {
var code = e.keyCode ? e.keyCode : e.which;
// ignore navigation keys
if ($.inArray(code, navigationKeys) !== -1) {
return;
}
if (code === $.ui.keyCode.ENTER) {
var selected = grid.getSelectedRows();
if (selected.length > 0) {
// grid configured with multi-select = false
var item = grid.getDataItem(selected[0]);
if (isSelectable(item)) {
addProcessor();
}
}
} else {
applyFilter();
}
});
// setup row navigation
nfFilteredDialogCommon.addKeydownListener('#processor-type-filter', grid, dataview);
// adjust the grid canvas now that its been rendered
grid.resizeCanvas();
// auto select the first row if possible
if (dataview.getLength() > 0) {
nfFilteredDialogCommon.choseFirstRow(grid);
}
// set the initial focus
$('#processor-type-filter').focus()
}
};
var processorComponent = new ProcessorComponent();
return processorComponent;
};
}));