/*
 * 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 = new Set();
                            var restrictedUsage = new Map();
                            var requiredPermissions = new 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.forEach(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;
    };
}));
