/*
Contains the code for the Files and Links grid panel which is displayed on the Reference Profile page (Panels: related - version),
the Collection Edit and Profile pages, and in the actual DigitalResources Popup.
*/

function holdingGridStore(referenceId, getHoldingsUrl, holdingGrid) {
    return Ext.create('Ext.data.Store', {
        model: 'NPS.DataStore.Models.DigitalResourcesWithCheck',
        proxy: {
            type: 'ajax',
            reader: 'json',
            writer: {
                type: 'json',
                allowSingle: false,   //causes each post to send an array of items
                partialDataOptions: {
                    changes: false,
                    critical: true
                }
            },
            method: 'POST',
            batchActions: false,
            actionMethods: { read: 'POST' },
            url: getHoldingsUrl,
            extraParams: { referenceId: referenceId },
            listeners: {
                exception: function (proxy, data, operation) {
                    Ext.Msg.alert('Error', 'An error occurred retrieving attachment information. Please reload the page to try again.');
                }
            }
        },
        getCheckedFileIds: function () {
            var store = this;
            var selected = store.queryRecords('IsChecked', true);
            var result = [];
            selected.forEach(function (record) { result.push(record.get('Id')); });
            return result;
        },
        listeners: {
            datachanged: function (none) {
                if (holdingGrid !== undefined) {
                    Ext.getCmp(holdingGrid).setGridTitle();
                    Ext.getCmp(holdingGrid).setGridHeight();
                    Ext.getCmp(holdingGrid).showHidePanel();
                }
            }
        },
        autoLoad: true
    });
}

function holdingGridColumns(downloadUrl, downloadability, isUnauthenticated, showDownload, isAdmin) {
    if (isUnauthenticated === undefined) {
        //default to a public user
        isUnauthenticated = false;
    }

    const gridColumns = [];
    var txt = '';

    if (downloadability === 'Restricted') {
        txt = ' Restricted - Access limited to specific individuals<br>';
    }
    else if (downloadability === 'Public') {
        txt = ' Public - Full access to the public and NPS staff<br>';
    }
    else if (downloadability === 'Internal') {
        txt = ' Internal - Access limited to all NPS staff<br>';
    }

    if (showDownload === true) {
        gridColumns.push({
            align: 'center',
            resizable: true, width: 30,
            xtype: 'checkcolumn',
            dataIndex: 'IsChecked',
            renderer: function (val, metaData, record) {                
                const cssPrefix = Ext.baseCSSPrefix;
                var cls = cssPrefix + 'grid-checkcolumn';
                if (val) {
                    cls += ` ${cssPrefix}grid-checkcolumn-checked`;
                }
                switch (record.data.HoldingType) {
                    case 0: //Digital 
                        if (record.data.IsRetractedFile === true) {
                        }
                        else {
                            //disableFilesLinksTab is always false -- logically obsoleted but left just in case.
                            if (record.data.Id > 0) {
                                if (downloadability === 'Public') {
                                    return `<img class="${cls}" src="${Ext.BLANK_IMAGE_URL}"/>`;
                                }
                                else {
                                    //know the user
                                    if (isUnauthenticated === true) {
                                        return `<img class="${cls}" src="${Ext.BLANK_IMAGE_URL}"/>`;
                                    }
                                }
                            }
                        }
                }
                return '';
            }
        });
    }
    gridColumns.push({
        xtype: 'actioncolumn',
        width: 22,
        align: 'center',
        menuDisabled: true,
        draggable: false,
        hideable: true,
        resizable: false,
        tooltip: 'Click here to view more information.',
        icon: Nps.icons.help,
        handler: function (vw, rowIndex, colIndex, item, e, record, row) {
            var info = '',
                header = '';

            //do not show the public any retracted file information
            if (record.data.IsRetractedFile === true && isUnauthenticated === false) {
                return;
            }

            if (record.data.IsRetractedFile === true) {
                header = 'Digital Resource Information';
                info =
                    `<ul><li>File Name: ${record.data.FileDescription}</li>                                                
                        <li>File has been retracted</li>
                        <li>Contact NRSS_DataStore@nps.gov with questions</li>`;
                if (isAdmin === true) {
                    info += `<li>Download Link: ${downloadUrl}/${record.data.Id}</li>`;
                }
                info += '</ul>';
            }
            else {
                //Define all three types of tooltips that will be shown
                switch (record.data.HoldingType) {
                    case 0: //Digital
                        var extension = NPS.DataStore.util.Holding.fileExtension(record.data.FileDescription);
                        header = 'Digital Resource Information';
                        info = `<ul><li>File Name: ${record.data.FileDescription}</li>
                            <li>File Size: ${Ext.util.Format.fileSize(record.data.FileSize)}</li>
                            <li>Extension: ${extension}</li>
                            <li>Access Level: ${txt}${(downloadability === 'Public' || downloadability === 'Internal')
                                ? `</li><li>Download Link: ${downloadUrl}/${record.data.Id}` : ''}</li></ul>`;
                        break;
                    case 1: //External
                        header = 'External Resource Information';
                        info = `<ul><li>Url: ${record.data.Url}</li><li>Last Verified: ${NPS.DataStore.util.dateFormatter(record.data.LastVerified, 'm/d/Y')}</li></ul>`;
                        break;
                    case 2: //WebService
                        var webServiceTypeStore = Ext.getStore('webServiceTypeStore');
                        var serviceType = webServiceTypeStore.findRecord('Key', record.data.WebServiceType);
                        header = 'Web Service Information';
                        info = `<ul><li>Web Service Type: ${serviceType.data.Value}</li><li>EndPoint: ${record.data.Url}</li><li>Accessibility: ${record.data.Accessibility}</li></ul>`;
                        break;
                }
            }

            try {
                //the tooltip
                new Ext.ToolTip({
                    title: `<span class="ttHeaderCss">${header}</span>`,
                    html: `<div>${info}</div>`,
                    autoHide: false,
                    animCollapse: true,
                    trackMouse: false,
                    anchor: 'bottom',
                    shadow: "drop",
                    width: 550,
                    target: row,
                    listeners: {
                        hide: function (tip) {
                            tip.destroy();
                        }
                    }
                }).show();
            }
            catch (e) {
            }
        }
    });

    //show a different color diamond icon if the row is a digital file resource depending on its downloadability
    gridColumns.push({
        xtype: 'actioncolumn',
        width: 22,
        align: 'center',
        menuDisabled: false,
        draggable: false,
        hideable: false,
        resizable: false,
        renderer: function (value, metaData, record) {
            if (record.data.IsRetractedFile === true) {
                metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(record.data.FileDescription)}<br>${Ext.String.htmlEncode(txt)}<br>"`;
                return `<img src="${Nps.icons.greydiamond}" alt="${txt}"></img>`;
            }
            else {
                if (record.data.HoldingType === 0) {
                    if (downloadability === 'Restricted') {
                        metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(record.data.FileDescription)}<br>${Ext.String.htmlEncode(txt)}<br>"`;
                        return `<img src="${Nps.icons.reddiamond}" alt="${txt}"></img>`;
                    }
                    else if (downloadability === 'Public') {
                        metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(record.data.FileDescription)}<br>${Ext.String.htmlEncode(txt)}<br>"`;
                        return `<img src="${Nps.icons.greendiamond}" alt="${txt}"></img>`;
                    }
                    else if (downloadability === 'Internal') {
                        metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(record.data.FileDescription)}<br>${Ext.String.htmlEncode(txt)}<br>"`;
                        return `<img src="${Nps.icons.yellowdiamond}" alt="${txt}"></img>`;
                    }
                }
            }

            metaData.tdAttr = '';
            return '';
        }
    });

    gridColumns.push({ header: 'Code', dataIndex: 'Id', resizable: false, width: 70, hidden: true });

    gridColumns.push({
        header: 'Type', dataIndex: 'HoldingType', resizable: false, width: 75,
        renderer: function (value, metaData, record) {
            return NPS.DataStore.util.Holding.holdingTypeText(value);
        }
    });

    gridColumns.push({
        text: '508 Compliant',
        dataIndex: 'Is508Compliant',
        hidden: true,
        renderer: function (value, metaData, record) {
            if (value) {
                return '508 Compliant';
            }
            return '';
        }
    });

    gridColumns.push({
        header: 'Download Link',
        dataIndex: 'Url',
        align: 'left',
        resizable: true,
        width: 310,
        getSortParam: function () {
            return 'FileDescription';
        },
        renderer: function (value, metaData, record) {
            switch (record.data.HoldingType) {
                case 0: //Digital                   
                    var data = record.data.FileDescription;
                    if (record.data.Id > 0) {
                        if (downloadability === 'Public') {
                            metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(data + '<br>' + txt)}"`;
                        }
                        else if (downloadability === 'Restricted') {
                            metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(data + '<br>' + txt)}"`;
                        }
                        else if (downloadability === 'Internal') {
                            metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(data + '<br>' + txt)}"`;
                        }

                        if (record.data.IsRetractedFile === true) {
                            return data;
                        }
                        else {
                            return `<a href="${downloadUrl}/${record.data.Id}" target="_blank">${data}<br></a>`;
                        }
                    }
                    return data;
                case 1: //External
                    if (value.toLowerCase().indexOf('nps.gov') !== -1) {
                        metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(value)}"`;
                        return `<a href="${value}" target="_blank">${value}</a>`;
                    }
                    else {
                        //non domain links -- true external, so show warning.
                        return `<a onclick=NPS.DataStore.util.ExternalSite.showOffSiteWarning('${encodeURI(value)}') return false;>${value}</a>`;
                    }
                case 2: //WebService
                    metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(value)}"`;
                    //little bit of a hack to allow more sites to be linked
                    var httpValue = 'http://';
                    if (value.indexOf('http') !== 0) {
                        httpValue += value;
                    }
                    else {
                        httpValue = value;
                    }
                    return `<a href="${httpValue}" target="_blank">${value}</a>`;
            }
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(value);
        }
    });

    gridColumns.push({
        header: 'Size', dataIndex: 'FileSize', hidden: false, resizable: false, width: 75,
        renderer: function (value, metaData, record) {
            if (value > 0) {
                return Ext.util.Format.fileSize(value);
            }
            return ''; //make JSLint happy
        }
    });

    gridColumns.push({
        header: 'Description', dataIndex: 'Description', resizable: true, flex: 1,
        renderer: function (value, metaData, record) {
            return NPS.DataStore.util.ToolTip.encodeToolTipSpan(value);
        }
    });


    gridColumns.push({
        header: 'Sort Order', dataIndex: 'DisplayOrder', hidden: true, resizable: false, width: 100,
        renderer: function (value, metaData, record) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(value);
        }
    });

    return gridColumns;
}

showFileDialog = function (referenceId, getHoldingsUrl, downloadUrl, downloadability, isUnauthenticatedStr, isAdmin) {
    const webServiceTypeStore = Ext.getStore('webServiceTypeStore');
    //isUnauthenticatedStr is passed in as a string and must be a bool every where.
    const isUnauthenticated = isUnauthenticatedStr === 'true'
        ? true
        : false;
    if (webServiceTypeStore) {
        if (webServiceTypeStore.count() === 0) {
            webServiceTypeStore.load();
        }
    }

    const popup = Ext.create('Ext.window.Window', {
        title: 'File and Link Information',
        modal: true,
        constrain: true,
        resizable: false,
        closeAction: 'destroy',
        items: [
            {
                xtype: 'form',
                width: 800,
                items: [
                    {
                        xtype: 'grid',
                        minHeight: 200,
                        maxHeight: 500,
                        defaults: {
                            overflowX: 'auto',
                            overflowY: 'auto'
                        },
                        resizable: {
                            pinned: true,
                            wrap: true,
                            handles: 's',
                            dynamic: true,
                            heightIncrement: 20,
                            minHeight: 200,
                            maxHeight: 500
                        },
                        viewConfig: {
                            deferEmptyText: false,
                            emptyText: 'No Files/Links Found',
                            enableTextSelection: true,
                            loadMask: false,
                            preserveScrollOnRefresh: true
                        },
                        store: holdingGridStore(referenceId, getHoldingsUrl),
                        columns: holdingGridColumns(downloadUrl, downloadability, isUnauthenticated, false, isAdmin)
                    }
                ],
                buttons: [
                    '->',
                    {
                        xtype: "button",
                        overCls: '',
                        width: 70,
                        cls: 'mainActionButton',
                        text: 'Close',
                        handler: function () {
                            this.up('window').destroy();
                        }
                    }
                ]
            }
        ]
    }); // end popup definition

    popup.show();
}

function createFilesAndLinksGrid(inputs, getHoldingsUrl, documentDownloadUrl, isUnauthenticated, bundleURL) {
    const referenceId = inputs.ReferenceId;
    const isAdmin = inputs.IsAdmin;

    var panel = Ext.create('Ext.panel.Panel', {
        title: 'Digital Files and Links (0)',
        collapsible: true,
        titleCollapse: true,
        hidden: true,
        border: 1,
        layout: 'fit',
        margin: '10 0 15 0',
        width: 980,
        maxHeight: 1500,
        minHeight: 150,
        height: 250,        
        maxWidth: 980,        
        resizable: {
            pinned: true,
            wrap: true,
            handles: 's',
            dynamic: true,
            heightIncrement: 20
        },
        items: [
            {
                xtype: 'grid',
                itemId: 'digitalResourcesGrid',
                id: 'digitalResourcesGrid',
                defaults: {
                    overflowX: 'auto',
                    overflowY: 'auto'
                },
                viewConfig: {
                    // shows empty text for grids
                    deferEmptyText: false,
                    emptyText: 'No Files/Links Found',
                    enableTextSelection: true,
                    loadMask: false,
                    preserveScrollOnRefresh: true,
                    //show retracted rows as bold
                    stripeRows: false,
                    markDirty: false,
                    getRowClass: function (record) {
                        return record.get('IsRetractedFile') ? 'retracted-row' : 'active-row';
                    }
                },
                store: holdingGridStore(referenceId, getHoldingsUrl, 'digitalResourcesGrid'),
                columns: holdingGridColumns(documentDownloadUrl, inputs.Downloadability, isUnauthenticated, true, isAdmin),
                setGridTitle: function () {
                    this.up('panel').setTitle(`Digital Files and Links (${this.store.getCount()})`);
                },
                setGridHeight: function () {
                    let cnt = this.store.getCount();
                    if (cnt <= 25) {
                        //100 is roughly the header and other panel parts
                        //21 is roughly the row height 
                        let height = (21 * cnt) + 100;
                        this.up('panel').setHeight(height);
                    }
                    else if (cnt > 25) {
                        this.up('panel').setHeight(620);
                    }
                },
                showHidePanel: function () {
                    const show = this.store.getCount() !== 0;
                    this.up('panel').setVisible(show);

                    if (show === false) {
                        //check all the other panels in order of precedence and continue until a panel is expanded or there are no more panels - then stop
                        const rc = Ext.getCmp('refProfile_relatedReferencesId');
                        if (rc && pageModel.RelatedCount > 0) {
                            rc.expand();
                        }
                        else {
                            const prod = Ext.getCmp('refProfile_productsCreatedByReferencesId');
                            if (prod && pageModel.ProductsCount > 0) {
                                prod.expand();
                            }
                            else {
                                const oProd = Ext.getCmp('refProfile_originatingProjectReferencesId');
                                if (oProd && pageModel.OriginatingPrpCount > 0) {
                                    oProd.expand();
                                }
                                else {
                                    const cr = Ext.getCmp('refProfile_crossReferencesId');
                                    if (cr && pageModel.CrossRefCount > 0) {
                                        cr.expand();
                                    }
                                    else {
                                        const v = Ext.getCmp('refProfile_versionedReferencesId');
                                        if (v && pageModel.RefVersionsCount > 0) {
                                            v.expand();
                                        }
                                    }
                                }
                            }
                        }
                    }
                    //show products if there are any even if the files and links grid is shown
                    else {
                        const prod1 = Ext.getCmp('refProfile_productsCreatedByReferencesId');
                        if (prod1 && pageModel.ProductsCount > 0) {
                            prod1.expand();
                        }
                        const rc = Ext.getCmp('refProfile_relatedReferencesId');
                        if (rc && pageModel.RelatedCount > 0) {
                            rc.expand();
                        }
                    }
                }
            }
        ]
    });

    Ext.define('NPS.DataStore.DataTable', {
        extend: 'Ext.data.Model',
        fields: [
            { name: 'Id', type: 'int' },
            { name: 'ReferenceId', type: 'int' },
            { name: 'ResourceId', type: 'int' },
            { name: 'ColumnName', type: 'string' },
            { name: 'Definition', type: 'string' },
            { name: 'Storage', type: 'string' },
            { name: 'Unit', type: 'string' }
        ]
    });

    Ext.create('Ext.window.Window', {
        title: 'Data Table Information',
        itemId: 'FileDataTableWindow',
        modal: true,
        constrain: true,
        resizable: false,
        closeAction: 'hide',
        y: 0,
        items: [
            {
                xtype: 'form',
                items: [
                    {
                        xtype: 'displayfield',
                        itemId: 'fileName',
                        fieldLabel: 'The following data table attributes were extracted from the EML file for',
                        labelSeparator: '',
                        labelAlign: 'top',
                        labelClsExtra: 'topDisplayLabel',
                        margin: '0 5 5 5',
                        fieldBodyCls: 'multilineDisplay',
                        width: 790
                    },
                    {
                        xtype: 'gridpanel',
                        itemId: 'FileDataTableGrid',
                        store: Ext.create('Ext.data.Store', {
                            storeId: 'FileDataTableGridStore',
                            model: 'NPS.DataStore.DataTable',
                            pageSize: 0,
                            proxy: {
                                type: 'ajax',
                                reader: 'json',
                                method: 'POST',
                                batchActions: false,
                                actionMethods: { read: 'POST' },
                                url: cachedData.getLoadMetaDataUrl(),
                                listeners: {
                                    exception: function (proxy, data, operation) {
                                        Ext.Msg.alert('Error', 'An error occurred retrieving attachment information. Please reload the page to try again.&nbsp;');
                                    }
                                }
                            }
                        }),
                        width: 800,
                        maxHeight: 500,
                        viewConfig: {
                            deferEmptyText: false,
                            emptyText: 'No columns found',
                            enableTextSelection: true
                        },
                        columns: [
                            {
                                xtype: 'actioncolumn',
                                width: 22,
                                align: 'center',
                                menuDisabled: true,
                                draggable: false,
                                hideable: false,
                                resizable: false,
                                items: [
                                    {
                                        icon: Nps.icons.add,
                                        tooltip: 'Show Categories',
                                        handler: function (grid, rowIndex, colIndex, item, e, record) {
                                            let scrollY = grid.getScrollY();
                                            record.set('Toggle', 1);
                                            grid.setScrollY(scrollY);
                                        },
                                        getClass: function (value, metadata, record, rowIndex, colIndex, store, view) {
                                            if (record.get('Toggle') == 2) {
                                                return 'x-hidden';
                                            }
                                            if (record.get('Toggle') == 1) {
                                                return 'x-hidden';
                                            }
                                        }
                                    },
                                    {
                                        icon: Nps.icons.minus,
                                        tooltip: 'Hide Categories',
                                        handler: function (grid, rowIndex, colIndex, item, e, record) {
                                            let scrollY = grid.getScrollY();
                                            record.set('Toggle', 0);
                                            grid.setScrollY(scrollY);
                                        },
                                        getClass: function (value, metadata, record, rowIndex, colIndex, store, view) {
                                            if (record.get('Toggle') == 2) {
                                                return 'x-hidden';
                                            }
                                            if (record.get('Toggle') == 0) {
                                                return 'x-hidden';
                                            }
                                        }
                                    }
                                ]
                            },
                            { text: 'Column Name', dataIndex: 'ColumnName', width: 200, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                            {
                                text: 'Definition', dataIndex: 'Definition', flex: 1, tdCls: 'multilineCell',
                                renderer: NPS.DataStore.util.Format.encodeRenderer()
                            },
                            { text: 'Data Type', dataIndex: 'Storage', width: 100, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                            { text: 'Unit', dataIndex: 'Unit' }
                        ],
                        features: [{
                            ftype: 'rowbody',
                            getAdditionalData: function (data, rowIndex, record, orig) {
                                let feature = this;
                                var headerCt = this.view.headerCt;
                                var colspan = headerCt.getColumnCount();
                                let toggle = record.get('Toggle');
                                if (toggle === 0 || toggle === 2) {
                                    return {
                                        rowBody: '',
                                        rowBodyCls: 'forceHidden',
                                        rowBodyColspan: 0
                                    };
                                }
                                let scales = record.get('DataTableScales');
                                let rowSet = feature.makeHeader();
                                Ext.each(scales, function (item) {
                                    let itemRow = feature.makeRow(item);
                                    rowSet += itemRow;
                                })

                                return {
                                    rowBody: rowSet,
                                    rowBodyCls: 'DataTableScaleBody',
                                    rowBodyColspan: colspan
                                };
                            },
                            makeHeader: function () {
                                let html =
                                    '<div class="scaleHeader">' +
                                    '<div class="Code">Category</div>' +
                                    '<div class="Definition">Definition</div>' +
                                    '</div>';
                                return html;
                            },
                            makeRow: function (data) {
                                let html =
                                    '<div class="scaleRow">' +
                                    '<div class="Code">' + Ext.String.htmlEncode(data.Code) + '</div>' +
                                    '<div class="Definition">' + Ext.String.htmlEncode(data.Definition) + '</div>' +
                                    '</div>';
                                return html;
                            }
                        }]
                    },
                    {
                        xtype: 'displayfield',
                        itemId: 'columnCount',
                        fieldLabel: '0',
                        labelSeparator: '',
                        labelWidth: 32,
                        labelAlign: 'right',
                        value: 'columns found',
                        margin: '0 0 0 5'
                    }
                ],
                buttons: [
                    '->',
                    {
                        xtype: "button",
                        overCls: '',
                        width: 70,
                        cls: 'mainActionButton',
                        text: 'Close',
                        handler: function () {
                            this.up('window').hide();
                        }
                    }
                ]
            }
        ],
        makeActive: function (record) {
            this.down('#fileName').setValue(record.get('FileDescription'));
            this.down('#columnCount').setFieldLabel(record.get('DataTableCount').toString());
            Ext.getStore('FileDataTableGridStore').load({ params: { referenceId: cachedData.getReferenceId(), resourceId: record.get('Id') } });
            this.center();
            this.show();
            this.setY(32);
        }
    });

    const tbar = Ext.create('Ext.toolbar.Toolbar', {
        dock: 'top',
        DownloadChecked: function () {
            var grid = panel.down('#digitalResourcesGrid');
            var store = grid.getStore();
            var fmids = store.getCheckedFileIds();
            if (fmids.length === 0) {
                return;
            }
            var form = document.createElement('FORM');
            form.method = 'POST';
            form.action = bundleURL;

            var input = document.createElement("INPUT");
            input.name = "referenceID";
            input.type = "hidden";
            input.value = inputs.ReferenceId;
            form.appendChild(input);

            for (var i = 0; i < fmids.length; i++) {
                var fid = document.createElement('INPUT');
                fid.type = "hidden";
                fid.name = "fileIDs";
                fid.value = fmids[i];
                form.appendChild(fid);
            }

            document.body.appendChild(form);
            form.submit(function (event) {
                event.preventDefault();
            });
            return true;
        },
        items: [
            '->',
            {
                xtype: 'button',
                style: 'border-color: #c3bbbb !important;',
                text: 'Show Data Table Info',
                hidden: !inputs.CanExtractMetadata,
                handler: function () {
                    let grid = panel.down('#digitalResourcesGrid');
                    let store = grid.getStore();
                    let selections = store.queryRecords('IsChecked', true);
                    if (selections.length === 0) {
                        return;
                    }

                    if (selections.length > 1) {
                        Ext.Msg.alert('Invalid Selection', 'Only a single file can be selected when viewing Data Table Info.');
                        return;
                    }
                    let record = selections[0];
                    if (record.get('DataTableCount') === 0) {
                        Ext.Msg.alert('Invalid Selection', 'This file does not have Data Table Info available.');
                        return;
                    }

                    let dataTableWindow = Ext.ComponentQuery.query('panel[itemId=FileDataTableWindow]')[0];
                    dataTableWindow.makeActive(record);
                }
            },
            {
                xtype: 'button',
                style: 'border-color: #c3bbbb !important;',                
                text: 'Download All',
                handler: function () {
                    var grid = panel.down('#digitalResourcesGrid');
                    var store = grid.getStore();
                    store.each(function (record) {
                        var setval = false;
                        if (record.data.HoldingType == 0) {
                            setval = true;
                        }
                        if (record.data.IsRetractedFile === true) {
                            setval = false;
                        }
                        if (record.data.Id <= 0) {
                            setval = false;
                        }

                        record.set('IsChecked', setval);
                    });
                    return this.up().DownloadChecked();
                }
            },
            {
                xtype: 'button',
                style: 'border-color: #c3bbbb !important;',                
                text: 'Download Selected',
                handler: function () {
                    return this.up().DownloadChecked();
                }
            }
        ]
    });
    panel.addDocked(tbar);

    return panel;
}
//========================================== define reference grid columns ============================================//
NPS.DataStore.ReferenceGridColumns = {
    refCodeColumn: {
        header: 'Code',
        dataIndex: 'Id',
        sortable: true,
        hideable: true,
        hidden: false,
        align: 'left',
        width: 60,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    },

    refTypeNameColumn: {
        header: 'Type',
        dataIndex: 'TypeName',
        sortable: true,
        hideable: true,
        hidden: false,
        align: 'left',
        width: 120,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refTypeNameNoSortColumn: {
        header: 'Type',
        dataIndex: 'TypeName',
        sortable: false,
        hideable: true,
        hidden: false,
        align: 'left',
        width: 100,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    },
    
    refYearOfIssueColumn: {
        header: 'Year',
        dataIndex: 'DateOfIssue',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 70,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.formatUTC(text, 'Y');
        }
    },

    referenceGroupTypeColumn: {
        header: 'Group Type',
        dataIndex: 'ReferenceGroupType',
        sortable: false,
        hideable: true,
        align: 'left',
        flex: 1,
        hidden: true
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refDateOfIssueNoSortColumn: {
        header: 'Date of Issue',
        dataIndex: 'DateOfIssue',
        sortable: false,
        hideable: true,
        align: 'left',
        width: 90,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.dateFormatter(text, 'm/d/Y');
        }
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refVersionNotesNoSortColumn: {
        header: 'Version Notes',
        dataIndex: 'Comment',
        sortable: false,
        hideable: true,
        align: 'left',
        width: 325,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.encodeToolTipSpan(text);
        }
    },

    refTitleColumn: {
        header: 'Title',
        dataIndex: 'Title',
        sortable: true,
        hideable: true,
        align: 'left',
        flex: 1,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.encodeToolTipSpan(text);
        }
    },

    refDisplayCitationColumn: {
        header: 'Display Citation',
        dataIndex: 'DisplayCitation',
        sortable: true,
        hideable: true,
        hidden: true,
        align: 'left',
        flex: 1,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    },

    refLifecycleColumnNotDefault: {
        header: 'Lifecycle',
        dataIndex: 'Lifecycle',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: true,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    refLifecycleColumn: {
        header: 'Lifecycle',
        dataIndex: 'Lifecycle',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refLifecycleNoSortColumn: {
        header: 'Lifecycle',
        dataIndex: 'Lifecycle',
        sortable: false,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    refVisibilityColumn: {
        header: 'Visibility',
        dataIndex: 'Visibility',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    refNewVersionColumn: {
        header: 'New Version',
        dataIndex: 'NewVersion',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refVisibilityNoSortColumn: {
        header: 'Visibility',
        dataIndex: 'Visibility',
        sortable: false,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: false,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    refDownloadabilityColumn: {
        header: 'Downloadability',
        dataIndex: 'Downloadability',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 85,
        hidden: true,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    // currently only used by the Version's grid. The user should not be allowed to sort.
    refDownloadabilityNoSortColumn: {
        header: 'Downloadability',
        dataIndex: 'Downloadability',
        sortable: false,
        hideable: true,
        align: 'left',
        width: 85,
        hidden: true,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    sortOrderColumn: {
        header: 'Display Order',
        //tooltip: ' a row to change the sort order value.',
        //tooltipType: 'title',
        dataIndex: 'SortOrder',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: true,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    //only used in quick and advanced search results grid
    rankingColumn: {
        header: 'Ranking',
        dataIndex: 'SortOrder',
        sortable: true,
        hideable: true,
        align: 'left',
        width: 80,
        hidden: true,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            //if user cannot access the information do not create a link just return the text
            if (record.data.Accessible === false) {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            return NPS.DataStore.util.ToolTip.singleHtmlTextFormat(text);
        }
    },

    //========================================== define reference grid columns ============================================//
    //special version of the ReferenceLinkProfileColumn only used in the search pages for the Reference Code value
    createReferenceLinkProfileColumnCode: function (header, dataIndex, sortable, hideable, hidden, width, profileUrl) {
        //defaults
        let uxSortable = true;
        let uxHideable = true;
        let uxHidden = false;
        let uxWidth = 50;

        if (width !== undefined && width !== null) {
            uxWidth = width;
        }
        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        const newColumn = {
            header: header,
            align: 'left',
            dataIndex: dataIndex,
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: uxWidth,
            renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
                //if user cannot access the information do not create a link just return the text
                if (record.data.Accessible === false) {
                    return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                }
                return NPS.DataStore.util.ToolTip.toolTipLink(profileUrl + '/' + record.data.Id, text, '_blank');
            }
        };
        return newColumn;
    },

    createReferenceContactsColumnSearch: function (header, dataIndex, sortable, hideable, hidden) {
        //defaults
        let uxHideable = true
        let uxHidden = false;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        const newColumn = {
            header: header,
            align: 'left',
            dataIndex: dataIndex,
            sortable: false, //uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: 300
        };
        return newColumn;
    },

    createReferenceLinkProfileColumnSearch: function (header, dataIndex, sortable, hideable, hidden, profileUrl, width, flex) {
        //defaults
        let uxSortable = true;
        let uxHideable = true;
        let uxHidden = false;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        const newColumn = {
            header: header,
            align: 'left',
            dataIndex: dataIndex,
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            //filter: {
            //    type: 'string',
            //    serializer: function (filter) {
            //        return filter.value;
            //    }
            //},
            renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
                //if user cannot access the information do not create a link just return the text
                if (record.data.Accessible === false) {
                    return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                }
                return NPS.DataStore.util.ToolTip.toolTipLink(profileUrl + '/' + record.data.Id, text, '_blank');
            }
        };
        if (width) {
            newColumn.width = width;
        }
        else if (flex) {
            newColumn.flex = flex;
        }
        else {
            newColumn.width = 300;
        }
        return newColumn;
    },

    createReferenceLinkProfileColumnDisplayCitation: function (header, dataIndex, sortable, hideable, hidden, width) {
        //defaults
        let uxSortable = true;
        let uxHideable = true;
        let uxHidden = false;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        const newColumn = {
            header: header,
            align: 'left',
            dataIndex: dataIndex,
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: width,
            renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
                //remove outline tags in title or display citation
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
        };
        return newColumn;
    },

    createReferenceLinkProfileColumn: function (header, dataIndex, sortable, hideable, hidden, width, profileUrl) {
        //defaults
        let uxSortable = true;
        let uxHideable = true;
        let uxHidden = false;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        const newColumn = {
            header: header,
            align: 'left',
            dataIndex: dataIndex,
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: width,
            renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
                //if user cannot access the information do not create a link just return the text
                if (record.data.Accessible === false) {
                    if (header !== 'Code') {
                        return NPS.DataStore.util.ToolTip.simpleToolTipSpan(NPS.DataStore.util.EncodeCheck.deOutline(text));
                    }
                    else { //remove outline tags in title or display citation
                        return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                    }
                }
                if (header !== 'Code') {
                    //remove outline tags in title or display citation
                    return NPS.DataStore.util.ToolTip.toolTipLink(profileUrl + '/' + record.data.Id, NPS.DataStore.util.EncodeCheck.deOutline(text), '_blank');
                }
                else {
                    return NPS.DataStore.util.ToolTip.toolTipLink(profileUrl + '/' + record.data.Id, text, '_blank');
                }
            }
        };
        return newColumn;
    },

    //Could either be the display citation or the title, both columns are created via this function.
    createReferenceProfileColumn: function (dataIndex, sortable, hideable, hidden, align, width, profileUrl) {
        //defaults
        let uxSortable = false;
        let uxHideable = false;
        let uxHidden = false;
        let uxAlign = 'center';
        let uxWidth = 22;

        if (width !== undefined && width !== null) {
            uxWidth = width;
        }
        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        if (align !== undefined && align !== null) {
            uxAlign = align;
        }

        const newColumn = {
            xtype: 'actioncolumn',
            dataIndex: dataIndex,
            menuDisabled: true,
            draggable: false,
            resizable: false,
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            align: uxAlign,
            width: uxWidth,
            items: [
                //profile icon
                {
                    tooltip: 'Click to open the Reference profile. ',
                    icon: Nps.icons.profile,
                    handler: function (vw, rowIndex, colIndex, item, e, record) {
                        const showProfile = function (id) {
                            if (id !== undefined && id !== null) {
                                var url = profileUrl + '/' + id;

                                //Always create a new tab when the user clicks the link.
                                $.post(url, null)
                                    .always(function (response) {
                                        window.open(url, '_blank');
                                    });
                            }
                        };
                        showProfile(record.data.Id);
                    },
                    isDisabled: function (view, rowIndex, colIndex, item, record) {
                        // Returns true if user cannot view restricted Profiles
                        if (record.data.Accessible === false) {
                            return true;
                        }
                        return false;
                    }
                }
            ]
        };
        return newColumn;
    },  

    createReferenceFileCountColumn: function (header, dataIndex, sortable, hideable, hidden, width, fileInfoUrl, downloadUrl, isUnauthenticated, isAdmin) {
        if (header === undefined || header === null || dataIndex === undefined || dataIndex === null) {
            throw error('A header and dataIndex must be provided.');
        }
        if (fileInfoUrl === undefined || fileInfoUrl === null) {
            throw error('A fileInfoUrl must be provided.');
        }

        //defaults
        let uxSortable = false;
        let uxHideable = true;
        let uxHidden = false;
        let uxWidth = 60;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        if (width !== undefined && width !== null) {
            uxWidth = width;
        }

        const newColumn = {
            header: header,
            dataIndex: dataIndex,
            align: 'center',
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: uxWidth,
            //filter: 'number', // May also be 'numeric'
            renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
                //if user cannot access the information do not create a link just return the text
                if (!isNaN(text) && text > 0 && record.data.Accessible === false) {
                    return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                }

                //if it is a number
                if (!isNaN(text) && text > 0) {
                    return ` <a href="" onmousedown='event.cancelBubble = true; showFileDialog( ${record.data.Id}, "${fileInfoUrl}", "${downloadUrl}", "${record.data.Downloadability}", "${isUnauthenticated}", "${isAdmin}"); return false'>${text}</a>`;
                }
                return '';
            }
        };
        return newColumn;
    },

    createProjectProductCountColumn: function (header, dataIndex, sortable, hideable, hidden, width) {
        if (header === undefined || header === null || dataIndex === undefined || dataIndex === null) {
            throw error('A header and dataIndex must be provided.');
        }        

        //defaults
        let uxSortable = false;
        let uxHideable = true;
        let uxHidden = false;
        let uxWidth = 60;

        if (sortable !== undefined && sortable !== null) {
            uxSortable = sortable;
        }
        if (hideable !== undefined && hideable !== null) {
            uxHideable = hideable;
        }
        if (hidden !== undefined && hidden !== null) {
            uxHidden = hidden;
        }
        if (width !== undefined && width !== null) {
            uxWidth = width;
        }

        const newColumn = {
            header: header,
            dataIndex: dataIndex,
            align: 'center',
            sortable: uxSortable,
            hideable: uxHideable,
            hidden: uxHidden,
            width: uxWidth
        };
        return newColumn;
    }
}
/*
/// BaseFilterPaginateGrid.js
//
//  This file contains an extension for an ExtJS grid, and an extension for the store used by the grid.
//  grid:   NPS.DataStore.Controls.BaseFilterPaginationGrid
//  store:  NPS.DataStore.Controls.BaseFilterPaginationGrid.Store
//
//  The grid extension defines the core functionality used by several pages in the DataStore app.
    Features:
        The onExpand listener will fire an ajax call to fetch data. This will enable a page to load with the grid panels collapsed with no data quickly and only if the user wants the data the they take the hit.
        The onActivate listener will fire an ajax call to fetch data. This will enable a page to load non-active tabs with no data quickly and only if the user wants the data the they take the hit.
        The pagination control can be hidden, see properties below.

//  CONFIG VALUES
//
//  hidePagination:         true to render the grid pagination control, default is true
//  searchUrl:              the url where to search (in the store control)
//  pageSize:               the store page size - number of rows returned to the grid (in the store control)
//  storeIsAutoLoad:        Used to flag the control that data is auto loaded and allows the flag, getFreshData, to be properly set so data is not fetched unless it is necessary.
                                NOTE: Most of the grids in DS only load data when a panel is expanded or a tab is activated,
                                and the data is only fetched once or when something like a filter, forces a fresh fetch.
                                The config variable, storeIsAutoLoad, is passed into the BaseFilterPaginationGrid and flags this fact.
                                storeIsAutoLoad defaults to false.
//
//  METHODS
//
//  processSearch( params): triggers the search to the url specified in config.searchUrl, passing params as an object with the parameters of the search
//

// PROPERTIES
//
//

NOTE: For constant results when calling the processSearch function the following seems to work the best:
            grid.processSearch(params);
            var gridStore = grid.getStore();
            gridStore.load({
                callback: function (records, operation, success) {
                }
            });
*/

//
//The basefilterpaginationgrid and its associated pagination control have a particular data load sequence to prevent multiple ajax calls.
//Thus the store is defined separately and passed into both controls. This allows both controls to use the same store and not make a copy of that store, and this permits only one ajax call.
//
Ext.define('NPS.DataStore.Controls.BaseFilterPaginationGrid.Store', {
    extend: 'Ext.data.Store',
    alias: 'basefilterpaginationgridstore',

    constructor: function (config) {
        const pageSize = config.pageSize || 50;
        Ext.apply(this, {
            itemId: 'baseFilterPaginationGridStoreItemId',            
            pageSize: pageSize,
            // sends single sort as multi parameter
            simpleSortMode: true
        });
        this.callParent(arguments);

        this.addListener('beforeload', function (store, operation, eOpts) {
            NPS.Common.Utils.showLoadMask();
            return true;
        });
    }
})

//define a general grid
Ext.define('NPS.DataStore.Controls.BaseFilterPaginationGrid', {
    extend: 'Ext.grid.Panel',
    xtype: 'basefilterpaginationgrid',
    resizable: {
        pinned: true,
        wrap: true,
        handles: 's',
        dynamic: true,
        heightIncrement: 20,
        minHeight: 100
    },

    //Process search is only used when firing a new search, not for pagination nor refreshing of the grid.
    processSearch: function (params) {
        //trying to get rid of the extra load mask doesn't work
        this.getView().setLoading(false);
        const gridStore = this.store;
        const proxy = gridStore.getProxy();
        proxy.extraParams = params;
    },

    /* Currently the download functionality is not going to be implemented. A report will be used instead. */
    //download the grid results to a file
    processDownload: function (format, url, params) {
        params.format = format;
        NPS.Common.Utils.submitForm(params, url, 'POST');
    },

    constructor: function (config) {
        //Used to flag the control that data is auto loaded and allows the flag,
        //getFreshData, to be properly set so data is not fetched unless it is necessary.
        var storeIsAutoLoad = config.storeIsAutoLoad || false;

        //only fetch the data once
        var getFreshData = true;

        //show or hide the pagination control default is to show it
        const hidePagination = config.hidePagination || false;

        Ext.apply(this,
            {
                viewConfig: {
                    emptyText: 'No results found',
                    deferEmptyText: false,
                    enableTextSelection: true,
                    loadMask: false,
                    preserveScrollOnRefresh: true
                },
                itemId: 'baseFilterPaginationGridItemId',
                collapsed: true,
                collapsible: true,
                titleCollapse: true,
                store: config.store,
                columns: {
                    defaults: {
                        sortable: true,
                        hidden: false,
                        hideable: true,
                        resizable: true
                    },
                    items: config.columns
                },
                bbar: new Ext.PagingToolbar({
                    itemId: 'resultsGridPagingToolbarItemId',
                    store: config.store,
                    displayInfo: true,
                    hidden: hidePagination,
                    displayMsg: 'Displaying {0} - {1} of {2}',
                    emptyMsg: "No Records Found",
                    plugins: [
                        {
                            ptype: 'pagingtoolbarresizer'
                        }
                    ]
                })
            });
        this.callParent(arguments);

        //Add all required listeners
        this.addListener('afterfirstlayout', function (grid, eOpts) { });

        this.addListener('expand', function (grid, eOpts) {
            //get all the data only once
            if (getFreshData === true && storeIsAutoLoad === false) {
                NPS.Common.Utils.showLoadMask();
                grid.store.load({
                    callback: function (records, operation, success) {
                        //do not go back for fresh data unless the user performs a filter operation
                        getFreshData = false;
                        NPS.Common.Utils.hideLoadMask();
                    }
                });
            }
        });

        this.addListener('activate', function (grid, eOpts) {
            //load the data only if the user activated the tab
            if (getFreshData) {
                //load the data only if the user activated the tab
                const gridStore = grid.getStore('baseFilterPaginationGridStoreItemId');
                gridStore.load();
                getFreshData = false;
            }
        });
    }
}) 

// Define grid that will automatically restore its selection after store reload
Ext.define('NPS.DataStore.Controls.PersistentSelectionGridPanel', {
    extend: 'NPS.DataStore.Controls.BaseFilterPaginationGrid',
    xtype: 'persistantselectiongrid',
    selectedRecords: [],

    initComponent: function () {
        this.callParent(arguments);
        this.getStore().on('beforeload', this.rememberSelection, this);
        this.getView().on('refresh', this.refreshSelection, this);
    },

    rememberSelection: function (selModel, selectedRecords) {
        this.selectedRecords = this.getSelectionModel().getSelection();
        this.getView().saveScrollState();
    },

    refreshSelection: function () {
        if (this.selectedRecords.length <= 0) {
            return;
        }
        const newRecordsToSelect = [];
        for (let i = 0; i < this.selectedRecords.length; i++) {
            const record = this.selectedRecords[i];
            if (!Ext.isEmpty(record)) {
                newRecordsToSelect.push(record);
            }
        }
        this.getSelectionModel().select(newRecordsToSelect);
    },

    clearSelection: function () {
        this.getSelectionModel().deselectAll();
        if (this.down('#action_ButtonItemId')) {
            this.down('#action_ButtonItemId').setDisabled(true);
        }
        if (this.down('#addReferencesToCollection_ButtonItemId')) {
            this.down('#addReferencesToCollection_ButtonItemId').setDisabled(true);
            this.down('#addReferencesToCollection_ButtonItemId').setVisible(false);
        }
        if (this.down('#deselectAll_ButtonItemId')) {
            this.down('#deselectAll_ButtonItemId').setDisabled(true);
        }
        if (this.down('#selectedLabelItemId')) {
            this.down('#selectedLabelItemId').setVisible(false);
            this.down('#selectedLabelItemId').setText('');
        }
    }
})
/*
    Configurations:

    enableActions:          Permits the selModel and action buttons to be turned on or off. The default value is true (show the actions)
    actionButtonTooltip:    The tool tip of the action button
    actionButtonText:       The text of the action button

 */

//define a grid derived from basefilterpaginationgrid and allows the user to select rows via a selModel checkbox and perform some action.
//
Ext.define('NPS.DataStore.Controls.ActionableGrid', {
    extend: 'NPS.DataStore.Controls.BaseFilterPaginationGrid',
    xtype: 'actionablegrid',    

    constructor: function (config) {
        config.id = config.id || Ext.id();

        var enableActions = true;
        if (config.enableActions !== undefined && config.enableActions !== null) {
            enableActions = config.enableActions;
        }

        var isSeriesSearch = false;
        if (config.isSeriesSearch !== undefined && config.isSeriesSearch !== null) {
            isSeriesSearch = config.isSeriesSearch;
        }

        //The object to hold the reference codes of selected items, regardless of pagination.
        var selectedRows = null;
        if (enableActions === true) {
            selectedRows = [];
        }

        //action button which will fire the above event
        const actionButton = {
            xtype: 'button',
            itemId: 'action_ButtonItemId',
            tooltip: config.actionButtonTooltip,
            icon: Nps.icons.arrowDown,
            text: config.actionButtonText,
            cls: 'plainLabel',
            disabled: true,
            hidden: (config.actionButtonHidden) ? config.actionButtonHidden : false,
            handler: function () {
                const g = this.up('grid');
                // ReSharper disable once Html.EventNotResolved
                g.fireEvent('executeActionOnSelected', selectedRows);
            }
        };

        //UN-checks all checkboxes
        const deselectAllButton = {
            //DE-select
            xtype: 'button',
            cls: 'plainLabel',
            itemId: 'deselectAll_ButtonItemId',
            tooltip: 'De-select all selected (checked) rows.',
            disabled: true,
            text: 'De-select All',
            icon: Nps.icons.cancel_red,
            hidden: (config.actionButtonHidden) ? config.actionButtonHidden : false,
            handler: function () {  

                const deselectAll = function (button) {
                    const g = button.up('grid');
                    let selected = g.getSelectionModel().multipageSelection;
                    if (selected) {
                        for (const [key, value] of Object.entries(selected)) {
                            if (value) {
                                selected[key] = null;
                            }
                        }
                        selected = {};
                        selectedRows = [];
                        g.getSelectionModel().multipageSelection = {};
                        g.getSelectionModel().deselectAll();
                        g.getStore().loadPage(1);
                    }
                };
                deselectAll(this);
            }
        };

        //add references to a collection
        const addReferencesToCollectionButton = {
            xtype: 'button',
            cls: 'plainLabel',
            itemId: 'addReferencesToCollection_ButtonItemId',
            tooltip: 'Add selected to a Collection',
            text: 'Add selected to a Collection',
            disabled: true,
            hidden: true,
            icon: Nps.icons.arrowDown,
            handler: function () {
                const addAll = function (button) {
                    const g = button.up('grid');
                    // ReSharper disable once Html.EventNotResolved
                    g.fireEvent('addToCollectionActionOnSelected', selectedRows);
                };
                addAll(this);
            }
        };

        //keeps a count of checked rows and displays them
        //all the buttons in the top toolbar
        var buttonArray = null;
        if (enableActions === true) {
            buttonArray = [];
            buttonArray.push(addReferencesToCollectionButton);
            buttonArray.push(actionButton);
            buttonArray.push(deselectAllButton);
            const rowCountLabel = {
                xtype: 'label',
                cls: 'plainLabelForSelectedReferenceCount',
                margin: '4 0 2 10',
                padding: '4 0 0 0',
                itemId: 'selectedLabelItemId',
                hidden: true
            };
            buttonArray.push(rowCountLabel);
        }

        //selModel object definition -- checkboxes
        var selectModel = null;
        if (enableActions === true || isSeriesSearch === true) {
            selectModel = new Ext.selection.CheckboxModel({
                checkOnly: true,
                selType: 'checkboxmodel',
                multipageSelection: {},
                listeners: {
                    select: function (th, records, index, opts) {
                        Ext.Array.include(selectedRows, records.data.Id);
                        // add select records to multi page object
                        this.multipageSelection[records.data.Id] = true;
                        const g = th.view.panel;
                        if (g.down('#selectedLabelItemId')) {
                            g.down('#selectedLabelItemId').setVisible(true);

                            let selected = 0;
                            for (const [key, value] of Object.entries(this.multipageSelection)) {
                                if (value) {
                                    selected++;
                                }
                            }
                            g.down('#selectedLabelItemId').setText('(' + selected + ') rows selected');
                            g.down('#deselectAll_ButtonItemId').setDisabled(false);
                            g.down('#action_ButtonItemId').setDisabled(false);
                            if (g.down('#addReferencesToCollection_ButtonItemId')) {
                                g.down('#addReferencesToCollection_ButtonItemId').setDisabled(false);
                            }
                        }
                    },
                    deselect: function (th, records, index, opts) {
                        Ext.Array.remove(selectedRows, records.data.Id);
                        // remove old selection from multi page object
                        this.multipageSelection[records.data.Id] = null;
                        const g = th.view.panel;
                        let selected = 0;
                        for (const [key, value] of Object.entries(this.multipageSelection)) {
                            if (value) {
                                selected++;
                            }
                        }
                        if (selected === 0) {                            
                            g.view.refresh();
                            if (g.down('#selectedLabelItemId')) {
                                g.down('#selectedLabelItemId').setVisible(false);
                                g.down('#selectedLabelItemId').setText('');
                                g.down('#deselectAll_ButtonItemId').setDisabled(true);
                                g.down('#action_ButtonItemId').setDisabled(true);
                                if (g.down('#addReferencesToCollection_ButtonItemId')) {
                                    g.down('#addReferencesToCollection_ButtonItemId').setDisabled(true);
                                }
                            }
                        }
                        else {
                            if (g.down('#selectedLabelItemId')) {
                                g.down('#selectedLabelItemId').setText('(' + selected + ') rows selected');
                            }
                            if (g.down('#deselectAll_ButtonItemId')) {
                                g.down('#deselectAll_ButtonItemId').setDisabled(false);
                            }
                            if (g.down('#action_ButtonItemId')) {
                                g.down('#action_ButtonItemId').setDisabled(false);
                            }
                            if (g.down('#addReferencesToCollection_ButtonItemId')) {
                                g.down('#addReferencesToCollection_ButtonItemId').setDisabled(false);
                            }
                        }
                    }
                },
                
                restoreSelection: function () {
                    this.store.data.each(function (i) {
                        if (this.multipageSelection[i.id] == true) {
                            this.select(i, true, true);
                        }
                    }, this);
                    this.page = this.store.currentPage;
                }
            });
        }

        //the top toolbar object
        const topBarObj = {
            xtype: 'toolbar',
            itemId: 'toolBarButtonArrayItemId',
            dock: 'top',
            height: 'auto',
            items: buttonArray
        };

        const dockedItemsObj = [{}];
        dockedItemsObj.push(topBarObj);

        Ext.apply(this, {
            dockedItems: dockedItemsObj,
            selModel: selectModel //only create the selModel checkboxes if the user can manage ownership
        });

        this.callParent(arguments);
    },

    clearSelection: function () {
               
        let selected = this.getSelectionModel().multipageSelection;
        if (selected) {
            for (const [key, value] of Object.entries(selected)) {
                if (value) {
                    selected[key] = null;
                }
            }
            selected = {};
            selectedRows = [];
            this.getSelectionModel().multipageSelection = {};
            this.getSelectionModel().deselectAll();
            this.getStore().loadPage(1);
        }

        
        this.getSelectionModel().deselectAll();
        if (this.down('#action_ButtonItemId')) {
            this.down('#action_ButtonItemId').setDisabled(true);
        }
        if (this.down('#addReferencesToCollection_ButtonItemId')) {
            this.down('#addReferencesToCollection_ButtonItemId').setDisabled(true);
            this.down('#addReferencesToCollection_ButtonItemId').setVisible(false);
        }
        if (this.down('#deselectAll_ButtonItemId')) {
            this.down('#deselectAll_ButtonItemId').setDisabled(true);
        }
        if (this.down('#selectedLabelItemId')) {
            this.down('#selectedLabelItemId').setVisible(false);
            this.down('#selectedLabelItemId').setText('');
        }
    }
})
/*
    Configurations:

    filterByUnit:       Boolean flag indicating whether the used can filter the grid by units.
    filterByType:       Boolean flag indicating whether the used can filter the grid by Reference types.
    filterByLifecycle:  Boolean flag indicating whether the used can filter the grid by lifecycle.
    pageSize:           The number of records per page, default is 25
    filterTitle:        The title of the filter popup default to blank.
 */

Ext.define('NPS.DataStore.Controls.BaseGridFilter', {
    extend: 'Ext.window.Window',
    xtype: 'basegridfilter',
    closable: true,
    modal: true,
    bodyPadding: 5,
    minHeight: 80,
    resizable: false,
    constrain: true,
    closeAction: 'hide',

    constructor: function (config) {
        /*
        Lifecycles will never change, so they are hard coded
        */
        const lcData = [
            [
                "Active", "Active"
            ],
            [
                "Draft", "Draft"
            ],
            [
                "Inactive", "Inactive"
            ]
        ];
        const lcStore = Ext.create('Ext.data.ArrayStore',
            {
                data: lcData,
                fields: [
                    'Value', 'Key'
                ]
            });
        config.id = config.id || Ext.id();
        config.filterTitle = config.filterTitle || '';
        var hasItemsCheckedrows;

        //container -- basegridfilter
        Ext.apply(this, {
            title: config.filterTitle,
            defaults: {
                labelSeparator: '',
                labelClsExtra: 'plainLabel',
                labelAlign: 'right',
                msgTarget: 'side',
                labelWidth: 110,
                margin: '0 5 5 0',
                width: 570
            },
            items: [
                //reference types
                {
                    xtype: 'combobox',
                    hidden: config.filterByType === undefined || config.filterByType === false,
                    fieldLabel: 'Reference Type',
                    itemId: 'typeComboItemId',
                    id: 'typeFilterComboId',
                    store: 'referenceTypeStore',
                    name: 'referenceType',
                    displayField: 'Name',
                    valueField: 'ReferenceType',
                    queryMode: 'local',
                    emptyText: "Select from list or start typing...",
                    forceSelection: true,
                    selectOnFocus: true,
                    anyMatch: true,
                    enableKeyEvents: true
                },
                //units
                {
                    xtype: 'combobox',
                    hidden: config.filterByUnit === undefined || config.filterByUnit === false,
                    fieldLabel: 'Unit',
                    itemId: 'npsUnitsId',
                    id: 'npsFilterUnitsId',
                    store: 'unitStore',
                    name: 'unitCode',
                    displayField: 'Name',
                    valueField: 'Code',
                    queryMode: 'local',
                    emptyText: "Select from list or start typing...",
                    blankText: 'You must select a Unit',
                    forceSelection: true,
                    selectOnFocus: true,
                    anyMatch: true,
                    editable: true
                },
                //life cycles
                {
                    xtype: 'combobox',
                    hidden: config.filterByLifecycle === undefined || config.filterByLifecycle === false,
                    fieldLabel: "Lifecycle",
                    itemId: 'npsLifecycleId',
                    id: 'npsFilterLifecycleId',
                    store: lcStore,
                    name: 'lifecycle',
                    displayField: 'Value',
                    valueField: 'Key',
                    queryMode: 'local',
                    emptyText: "Select from list or start typing...",
                    forceSelection: false,
                    selectOnFocus: true
                }
            ],
            buttons: [
                {
                    maxWidth: 60,
                    text: 'Cancel',
                    tooltip: 'Clears all filters inputs and closes the filter dialog.',
                    handler: function () {
                        //clear the visible filter params and controls
                        const win = this.up('window');
                        Ext.each(win.items.items, function (item) {
                            if (!item.hidden) {
                                item.reset();
                            }
                        });
                        win.close();
                    }
                },
                '->',
                {
                    maxWidth: 60,
                    text: 'Clear',
                    tooltip: 'Clears all filters inputs. Does not perform a search, or close the filter dialog.',
                    handler: function () {
                        //clear the visible filter params and controls
                        const win = this.up('window');
                        Ext.each(win.items.items, function (item) {
                            if (!item.hidden) {
                                item.reset();
                            }
                        });
                    }
                },
                {
                    text: "Apply",
                    maxWidth: 60,
                    cls: 'mainActionButton',
                    itemId: 'searchButtonId',
                    type: 'submit',
                    tooltip: 'Applies the filtered criteria and performs a new search.',
                    handler: function () {
                        let win = this.up('window');
                        //go get data
                        const executeQuery = function (theParams) {
                            //does the user want to run a new query and wipe out any checked rows?
                            if (hasItemsCheckedrows && theParams !== null) {
                                Ext.Msg.confirm('Confirm', 'Important! You are about to perform a filtered search any rows<br>that were previously checked will be cleared (un-checked).<br>Click yes to continue.', function (button) {
                                    if (button === 'yes') {
                                        //fire filter event and pass the theParams object out
                                        win.fireEvent('onFilteredQuery', theParams);
                                    }
                                });
                            }
                            else {
                                //fire filter event and pass the theParams object out
                                win.fireEvent('onFilteredQuery', theParams);
                            }
                        };

                        //Determine if there are additional filter options
                        let params = {};
                        let parameterData = false;
                        Ext.each(win.items.items, function (item) {
                            if (!item.hidden) {
                                const theValue = item.getValue() !== null && item.getValue() !== undefined
                                    ? item.multiSelect
                                    ? item.getValue().join(', ')
                                    : item.getValue()
                                    : null;
                                if (theValue !== null && theValue !== undefined && theValue !== '') {
                                    params[item.name] = theValue;
                                    parameterData = true;
                                }
                            }
                        });

                        //only search if there are filter params
                        if (parameterData) {
                            executeQuery(params);
                        }
                        else {
                            //return default (unfiltered) data
                            executeQuery(null);
                            //fire filter event and reset the filter buttons to the defaults
                            win.fireEvent('onClearFilter');
                        }
                        win.close();
                    }
                }
            ]
        });

        //add additional filter options
        var thePanel = this;
        if (config.filterOptions !== undefined) {
            Ext.each(config.filterOptions, function (item) {
                thePanel.items.push(item);
            });
        }

        this.callParent(arguments);

        //pass in a bool indicating whether or not there were any rows checked and warn the user as needed
        this.hasItemsChecked = function (hasCheckedRows) {
            hasItemsCheckedrows = hasCheckedRows;
        };
    } //end of basegridfilter constructor
})

//define a grid derived from actionablegrid that allows the user to select rows via a selModel checkbox and perform some action.
//It also allows filtering of the grid results
//
Ext.define('NPS.DataStore.Controls.FilterableActionableGrid', {
    extend: 'NPS.DataStore.Controls.ActionableGrid',
    xtype: 'filterableactionablegrid',

    constructor: function (config) {
        config.id = config.id || Ext.id();

        //ajax call to get the data actual call is in parent control, ActionableGrid via the processSearch function
        var getData = function (theConfig, grid, params) {
            //uncheck the checkboxes
            let selectedRecords;
            let selModelobj;

            if (grid.getSelectionModel()) {
                selectedRecords = grid.getSelectionModel().getSelection();
                if (grid.selModel) {
                    selModelobj = grid.selModel;
                    for (var i = 0, len = selectedRecords.length; i < len; i++) {
                        selModelobj.deselect(selectedRecords[i]);
                    }
                    //clears the selected items, but does not uncheck the checkboxes -- should be over kill
                    selModelobj.selected.items = [];
                }
            }

            //get filtered data
            grid.processSearch(params);

            let gridStore = grid.getStore();
            gridStore.load({
                callback: function (records, operation, success) {
                    //update the title with the correct number of rows.
                    if (theConfig.hideGridTitle === false) {
                        let theTitle = theConfig.title;
                        //make sure there are only one set of parentheses
                        const indexOf = theTitle.indexOf('(');
                        theTitle = theTitle.substring(0, indexOf - 1);
                        grid.setTitle(theTitle + ' (' + gridStore.getRange().length + ')');
                    }
                }
            });
        };

        // setting the popup variable to null will force the recreation of the filter
        // control the next time the filter button is clicked and thus clearing the control.
        var popup,

            //clear the filter control and get fresh unfiltered data
            clearFilter = function (theConfig, grid) {
                //reset the units combos otherwise the user only sees the units last filtered by
                Ext.getCmp('typeFilterComboId').setValue('');
                Ext.getCmp('npsFilterUnitsId').setValue('');
                Ext.getCmp('npsFilterLifecycleId').setValue('');

                //get unfiltered data
                getData(theConfig, grid, null);
            },

            //open the filter control and only get data if the user hits the search button, otherwise just close the filter.
            openFilter = function (theConfig, grid) {
                //Even though an event was fired, make sure the data is in the correct state before setting the buttons back to the defaults.
                var setFilterButtonsState = function (params, eventName) {
                    const filterBut = grid.down('#filterButtonItemId');
                    const clearButton = grid.down('#clearFilterButtonItemId');

                    if (eventName === 'onFilteredQuery' && params !== null) {
                        //set filter button back to the default
                        clearButton.setVisible(true).setDisabled(false);
                        filterBut.setText('Filter ON');
                        filterBut.removeCls('plainLabel');
                        filterBut.addCls('filterOnCls');
                    }
                    else {
                        //set clear button back to the default
                        filterBut.setText('Filter');
                        filterBut.addCls('plainLabel');
                        filterBut.removeCls('filterOnCls');
                        clearButton.setVisible(false).setDisabled(true);
                    }
                };

                if (!popup) {
                    popup = Ext.create('NPS.DataStore.Controls.BaseGridFilter', {
                        filterTitle: theConfig.filterTitle,
                        filterByUnit: theConfig.filterByUnit,
                        filterByType: theConfig.filterByType,
                        filterByLifecycle: theConfig.filterByLifecycle,
                        lifecycles: theConfig.lifecycles,
                        pageSize: theConfig.pageSize,
                        filterOptions: theConfig.filterOptions,
                        listeners: {
                            onFilteredQuery: function (params) {
                                //get filtered data
                                getData(theConfig, grid, params);
                                setFilterButtonsState(params, 'onFilteredQuery');
                            },
                            onClearFilter: function () {
                                setFilterButtonsState(null, 'onClearFilter');
                            }
                        }
                    });
                }

                //true if there are rows checked, filter needs this to know whether or not to show and alert box.
                const checkedItems = grid.getSelectionModel().getSelection()
                    ? grid.getSelectionModel().getSelection().length > 0
                        ? true
                        : false
                    : false;
                popup.hasItemsChecked(checkedItems),
                    popup.center(); //center within viewport...works
                popup.show();
            };

        this.callParent(arguments);

        const clearFilterButton = {
            xtype: 'button',
            itemId: 'clearFilterButtonItemId',
            cls: 'plainLabel',
            text: 'Clear Filter',
            tooltip: 'Clear all filter inputs and refresh the data to the default state.<br>This will also clear any checked rows.',
            hidden: true,
            disabled: true,
            handler: function (obj) {
                const grid = obj.up('gridpanel');
                clearFilter(config, grid);
                //set buttons back to the default state
                obj.setVisible(false).setDisabled(true);
                const filterBut = grid.down('#filterButtonItemId');
                filterBut.setText('Filter');
                filterBut.addCls('plainLabel');
                filterBut.removeCls('filterOnCls');
            }
        };

        const filterButton = {
            xtype: 'button',
            itemId: 'filterButtonItemId',
            cls: 'plainLabel',
            text: 'Filter',
            width: 80,
            icon: Nps.icons.manage,
            handler: function (obj) {
                const grid = obj.up('gridpanel');
                openFilter(config, grid);
            }
        };

        //first find the tbar and then add filter buttons to the right
        const toolbar = this.items.get(0).panel.getDockedItems('toolbar[dock="top"]')[0];
        toolbar.add(clearFilterButton);
        toolbar.add(filterButton);
    }
})
/* GMapPanel is an ExtJS wrapper to a  Google map.
Based on Ext.ux.GMap

This control should be able to:

For Profile page:
    Display zero to many bounding boxes(Geography), non editable, non drag-able, passed as an array of WKT strings as configuration.

For Edit/Create
    Display zero to one bounding box (rectangle), editable as rectangle, movable, passed as a WKT string parameter in config object
    Add a rectangle by dragging it( it will wipe out any existing features in the map), editable as rectangle, movable.
    Add a rectangle by entering lat/long( it will wipe out any existing features in the map), editable as rectangle, movable.
    Delete existing rectangle?
    Show coordinates of current rectangle.
    Return the drawn rectangle as a WKT string

For Advanced Search
    Draw a single rectangle
    Display one rectangle passed as a config parameter in WKT format.
    Delete any rectangle being displayed
    Return the WKT for that rectangle

If this is going to be a single control, the configs will have to determine of what type

mapMode: search, create, profile
boxes : Array of strings or single string. In Search or Create, will default to first element.

*/
Ext.define('NPS.DataStore.Controls.BBoxMap', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.bboxmap',

    initComponent: function () {
        Ext.applyIf(this, {
            plain: true,
            gmapType: 'map',
            border: false,
            mapMode: 'view',
            isNPS: true,
            defaultExtent: new google.maps.LatLngBounds(new google.maps.LatLng(30, -114), new google.maps.LatLng(45, -90))
        });
        this.callParent();
    },

    onBoxReady: function () {
        this.callParent(arguments);
        this.createMap();
    },

    //Creates the map button.
    createControlUI: function (title, text) {
        // Set CSS for the control border
        const controlUi = document.createElement('div');
        controlUi.style.backgroundColor = 'white';
        controlUi.style.height = '17px';
        controlUi.style.position = 'relative';
        controlUi.style.right = '-11px';
        controlUi.style.border = 'none';
        controlUi.style.cursor = 'pointer';
        controlUi.style.textAlign = 'center';
        controlUi.style.borderRadius = '1px';

        controlUi.title = title;

        // Set CSS for the control interior
        const controlText = document.createElement('div');
        controlText.style.fontFamily = 'Arial';
        controlText.style.fontSize = '11px';
        controlText.style.fontWeight = 'normal';
        controlText.style.paddingLeft = '4px';
        controlText.style.paddingTop = '2px';
        controlText.style.paddingRight = '4px';

        controlText.innerHTML = text;
        controlUi.appendChild(controlText);

        return controlUi;
    },

    //Creates the map button to allow enter coordinates manually.
    // Not to be used in 'view' or 'search' mode.
    typeCoordButton: function (controlDiv, map, scope) {
        controlDiv.style.padding = '6px 16px 6px 6px';

        const controlUi = scope.createControlUI('Manually enter the coordinates of your bounding box', 'Enter Coordinates');
        controlDiv.appendChild(controlUi);

        controlUi.addEventListener('click', function () {
            scope.manualCoordWindow.call(this, scope);
        });
    },

    clearCoordButton: function (controlDiv, map, scope) {
        controlDiv.style.padding = '6px';

        const controlUi = scope.createControlUI('Clear all coordinates for your bounding box', 'Clear Coordinates');
        controlDiv.appendChild(controlUi);

        controlUi.addEventListener('click', function () {
            scope.clearMapNow.call(this, scope);
        });
    },

    // Creates the map.
    // Applies defaults to mapOptions and featureOptions if not provided in the configuration.
    createMap: function () {
        var scope = this;

        this.features = [];

        var mapOptions = Ext.apply({}, this.mapOptions); //comment out to show map menu
        var featureOptions = Ext.apply({}, this.featureOptions);

        let isPoint = false;
        if (this.wktBoxes && this.wktBoxes.length == 1) {
            let theWkt = this.wktBoxes[0].wkt;
            isPoint = theWkt.startsWith('POINT');
        }

        //default options, in case there were not present from host configuration
        mapOptions = Ext.applyIf(mapOptions, {
            zoom: 8,
            disableDefaultUI: true,
            zoomControl: true,
            mapTypeControl: false, //set to true to show map menu
            streetViewControl: false,
            mapTypeId: "ParkTiles",
            mapTypeControlOptions: {
                style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
            },
            editable: true,
            draggable: true
        });

        featureOptions = Ext.applyIf(featureOptions, {
            icon: null,
            shadow: null,
            editable: this.mapMode === "view" ? false : true,
            draggable: this.mapMode === "view" ? false : true,
            strokeColor: '#990000',
            fillColor: '#EEFFCC',
            fillOpacity: 0.2
        });

        //create base layer
        var parktiler = new google.maps.ImageMapType({
            getTileUrl: function (coord, zoom) {
                return "https://atlas-stg.geoplatform.gov/styles/v1/atlas-user/ck58pyquo009v01p99xebegr9/tiles/256/" + zoom + "/" + coord.x + "/" + coord.y + "?access_token=pk.eyJ1IjoiYXRsYXMtdXNlciIsImEiOiJjazFmdGx2bjQwMDAwMG5wZmYwbmJwbmE2In0.lWXK2UexpXuyVitesLdwUg";
            },
            tileSize: new google.maps.Size(256, 256),
            name: "ParkTiles",
            maxZoom: 18,
            alt: "MapServer Layers",
            isPng: true,
            minZoom: 1
        });
        
        //Create map
        this.gmap = new google.maps.Map(this.body.dom, mapOptions);
        this.infoWindow = new google.maps.InfoWindow();
        //insert the layers
        //this.gmap.overlayMapTypes.insertAt(0, parktiler); //Not needed for nps unless the layer type changes
        //set the park layer
        this.gmap.mapTypes.set("ParkTiles", parktiler);
        
        if (this.mapMode !== 'view' && !isPoint) {
            //create drawing manager
            this.gmap.drawingManager = new google.maps.drawing.DrawingManager({
                drawingControlOptions: {
                    position: google.maps.ControlPosition.TOP_LEFT,
                    drawingModes: [
                        google.maps.drawing.OverlayType.RECTANGLE
                    ]
                },
                rectangleOptions: featureOptions
            });

            //Add drawing manager to map
            this.gmap.drawingManager.rectangleOptions.draggable = true;
            this.gmap.drawingManager.setMap(this.gmap);

            // Add custom control (button to open manual coordinates window)
            var typeCoordControlDiv = document.createElement('div');
            let typeCoordButtonLink = new this.typeCoordButton(typeCoordControlDiv, this.gmap, this);
            typeCoordControlDiv.index = 1;
            this.gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push(typeCoordControlDiv);

            // Add custom control (button to open manual coordinates window)
            var clearCoordControlDiv = document.createElement('div');
            let clearCoordButtonLink = new this.clearCoordButton(clearCoordControlDiv, this.gmap, this);
            clearCoordControlDiv.index = 1;
            this.gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push(clearCoordControlDiv);

            //Listener to handle the creation of a marker via drawing manager
            google.maps.event.addListener(this.gmap.drawingManager, 'rectanglecomplete', function (rectangle) {
                scope.clearMap();
                scope.features.push(rectangle);
                scope.writeCoordinates(rectangle);
                this.setDrawingMode(null);
            });
            google.maps.event.addListener(this.gmap.drawingManager, 'markercomplete', function (marker) {
                scope.clearMap();
                scope.features.push(marker);
                scope.writeMarkerCoordinates(marker);
                this.setDrawingMode(null);
            });
            google.maps.event.addListener(this.gmap.drawingManager, 'polygoncomplete', function (polygon) {
                scope.clearMap();
                scope.features.push(polygon);

                const theBounds = new google.maps.LatLngBounds();
                let paths = polygon.getPath().getArray();
                for (var i = 0; i < paths.length; i++) {
                    theBounds.extend(paths[i]);
                }

                scope.writePolygonCoordinates(polygon);
                this.setDrawingMode(null);
            });

            /*Uncomment if NPS and the map menu is required. Note: there is no way to switch back to the boundary layer after switch to another.*/
            //// check mapType when it is changing
            //google.maps.event.addListener(this.gmap, "maptypeid_changed", function (event) {
            //    if (this.mapTypeId === "ParkTiles") {
            //        // show my custom map layer - but only if its not already there
            //        // note: you could also check more specifiably your map type here
            //        if (this.overlayMapTypes.getLength() === 0) {
            //            this.overlayMapTypes.insertAt(0, new CoordMapType(new google.maps.Size(256, 256)));
            //        }
            //    }
            //    else {
            //        // if something else, remove it
            //        // note: you could also check more specifically your map type here, but for now
            //        // we just check if something is already there
            //        if (this.overlayMapTypes.getLength() > 0) {
            //            this.overlayMapTypes.removeAt(0);
            //        }
            //    }
            //});
        } //end mode != view

        //If present in configuration, add a marker via WKT
        if (this.wktBoxes) {
            this.addWktBoxes(this.wktBoxes);
        }

        this.setMapExtent();

        //Map is done. Fire event for whoever might consume it.
        this.fireEvent('mapready', this, this.gmap);
    },

    // Not used in search mode
    clearMap: function () {
        var i;
        for (i in this.features) {
            if (this.features.hasOwnProperty(i)) {
                this.features[i].setMap(null);
            }
        }
        this.features.length = 0;
    },

    // Not used in search mode
    addLatLongBox: function (bounds) {
        const newBox = new google.maps.Rectangle(this.featureOptions);
        newBox.setBounds(bounds);
        newBox.setMap(this.gmap);
        this.registerBox(newBox);
        this.setMapExtent();
    },

    // This function is used to add a feature in WKT format. Used when creating the control and the feature is passed as config.
    addWktBoxes: function (boxesArray) {
        const wkt = new Wkt.Wkt();
        for (let i = 0; i < boxesArray.length; i++) {
            const value = boxesArray[i];
            try { // Catch any malformed WKT strings
                wkt.read(value.wkt);
            }
            catch (e1) {
                try {
                    wkt.read(value.replace('\n', '').replace('\r', '').replace('\t', ''));
                }
                catch (e2) {
                    if (e2.name === 'WKTError') {
                        Ext.Msg.alert('Error', 'Wicket could not understand the WKT string you entered. Check that you have parentheses balanced, and try removing tabs and newline characters.&nbsp;');
                        return;
                    }
                }
            }

            const opts = this.featureOptions;
            const newBox = wkt.toObject(opts);

            if (wkt.type === 'polygon') {
                // Convert polygon to rectangle and register.
                this.registerBox(this.convertPolygonToRectangle(newBox, this.featureOptions), value.description);
                //this.addWktPolygon(wkt);
            }
            else if (wkt.type === 'point') {
                this.addWktMarker(value.description, wkt);
            }
        }
    },

    // This function is used to add a feature in WKT format.
    // Used from the Reference Profile page (Units and Geography) tab when the user selects one or more bounding box grid rows.
    addSelectedWktBox: function (boxesArray, featureOptions) {
        const wkt = new Wkt.Wkt();
        for (let i = 0; i < boxesArray.length; i++) {
            const value = boxesArray[i];
            try { // Catch any malformed WKT strings
                wkt.read(value.wkt);
            }
            catch (e1) {
                try {
                    wkt.read(value.replace('\n', '').replace('\r', '').replace('\t', ''));
                }
                catch (e2) {
                    if (e2.name === 'WKTError') {
                        Ext.Msg.alert('Error', 'Wicket could not understand the WKT string you entered. Check that you have parentheses balanced, and try removing tabs and newline characters.&nbsp;');
                        return;
                    }
                }
            }

            const newBox = wkt.toObject(featureOptions);

            if (wkt.type === 'polygon') {
                // Convert polygon to rectangle and register.
                this.registerBox(this.convertPolygonToRectangle(newBox, featureOptions), value.description);
            }
            else if (wkt.type === 'point') {
                this.addWktMarker(value.description, wkt);
            }
        }
    },

    //Converts a google.map.polygon that represents  a rectangle in to a  google.map.rectangle
    convertPolygonToRectangle: function (thePolygon, featureOptions) {
        if (featureOptions === undefined || featureOptions === null || featureOptions.length === 0) {
            featureOptions = this.featureOptions;
        }

        ////WARNING: This is not a good test to see if the original WKT format is correct.
        //// Wicket, the generator of thePolygon object, doesn't do any verification on the validity of the string passed to it.
        //// If the string says POLYGON but the points don't match or generate a close figure, wicket will take it, returning a 'weird' Google polygon.
        //if (!thePolygon instanceof google.maps.Polygon) {
        //    alert("The WKT string provided is not a polygon");
        //}

        var path = thePolygon.getPath().getArray(),
            // Verify that the polygon is a rectangle (very basic, needs more work)
            isRect = path.length === 4; // first test: contains exactly five segments

        if (isRect) { // try to pass second test
            for (let i = 0; i < 4; i++) {
                const next = i + 1 === 4 ? 0 : i + 1;
                if (path[i].lat() === path[next].lat() || path[i].lng() === path[next].lng()) {
                    continue;
                }
                else {
                    isRect = false;
                    break;
                }
            }
        }

        if (!isRect) {
            Ext.Msg.alert('WARNING', "Provided POLYGON is not a RECTANGLE.<br>The smallest rectangle that contains the polygon provided will be displayed instead&nbsp;");
        }
        const theBounds = new google.maps.LatLngBounds();
        for (let ii = 0; ii < path.length; ii++) {
            theBounds.extend(path[ii]);
        }

        const opt = featureOptions;
        opt.map = thePolygon.map;
        opt.bounds = theBounds;
        const rectangle = new google.maps.Rectangle(opt);

        return rectangle;
    },

    //Set the map extent to fit all rendered boxes
    setMapExtent: function () {
        const theFeatures = this.features;
        var theBounds;

        //Features present. Use to define extent
        if (theFeatures && theFeatures.length > 0) {
            theBounds = new google.maps.LatLngBounds();
            for (let i = 0; i < theFeatures.length; i++) {
                const f = theFeatures[i];
                if (f.bounds) {
                    let bounds = f.getBounds();
                    theBounds.union(bounds);
                }
                else if (f.position) {
                    let pos = f.position;
                    let bounds = new google.maps.LatLngBounds(new google.maps.LatLng(pos.lat(), pos.lng()), new google.maps.LatLng(pos.lat(), pos.lng()));
                    theBounds.union(bounds);
                }
                else if (f.getPath) {
                    let paths = f.getPath().getArray();
                    for (let i = 0; i < paths.length; i++) {
                        let pos = paths[i];
                        let bounds = new google.maps.LatLngBounds(new google.maps.LatLng(pos.lat(), pos.lng()), new google.maps.LatLng(pos.lat(), pos.lng()));
                        theBounds.union(bounds);
                    }
                }
            }
        }
        else { // No features. Use default extent.
            theBounds = this.defaultExtent;
        }
        this.gmap.fitBounds(theBounds);
    },

    addWktMarker: function (description, wkt) {
        if (wkt === null) {
            Ext.Msg.alert('Error', 'Wicket could not understand the WKT string you entered. Check that you have parentheses balanced, and try removing tabs and newline characters.');
            return;
        }

        let marker = new google.maps.Marker({
            position: new google.maps.LatLng(wkt.components[0].y, wkt.components[0].x),
            map: this.gmap,
            title: description
        });

        this.features.push(marker);

        let themap = this;
        marker.addListener("click", () => {
            themap.infoWindow.close();
            themap.infoWindow.setContent(marker.getTitle());
            themap.infoWindow.open(marker.getMap(), marker);
        });
        //marker.addListener("mouseover", () => {
        //    themap.infoWindow.close();
        //    themap.infoWindow.setContent(marker.getTitle());
        //    themap.infoWindow.open(marker.getMap(), marker);
        //});

        this.writeMarkerCoordinates(marker);
    },

    addWktPolygon: function (wkt) {
        if (wkt === null) {
            Ext.Msg.alert('Error', 'Wicket could not understand the WKT string you entered. Check that you have parentheses balanced, and try removing tabs and newline characters.');
            return;
        }

        var polygonCoords = new Array();
        const theBounds = new google.maps.LatLngBounds();
        for (var i = 0; i < wkt.components[0].length; i++) {
            polygonCoords[i] = new google.maps.LatLng(wkt.components[0][i].y, wkt.components[0][i].x);
            theBounds.extend(polygonCoords[i]);
        }

        let polygon = new google.maps.Polygon({
            path: polygonCoords
        });


        this.features.push(polygon);
        polygon.setMap(this.gmap);

        this.writePolygonCoordinates(polygon);
    },

    //This function takes as argument a Google RECTANGLE object and registers in the features list.
    registerBox: function (theRectangle, title) {
        //Clear the map from other markers and features
        // Only in view mode we don't clear, to show all the potential boxes that the control can render in this mode
        if (this.mapMode !== 'view') {
            this.clearMap();
        }

        let themap = this;
        if (title !== undefined) {
            google.maps.event.addListener(theRectangle, 'click', function (event) {
                themap.infoWindow.close();
                themap.infoWindow.setContent(title);
                themap.infoWindow.setPosition(event.latLng);
                themap.infoWindow.open(theRectangle.getMap());
            });
            google.maps.event.addListener(theRectangle, 'mouseover', function () {
                themap.gmap.getDiv().setAttribute('title', title);
            });
            google.maps.event.addListener(theRectangle, 'mouseout', function () {
                themap.gmap.getDiv().removeAttribute('title');
            });
        }

        // Add the new marker to the features collection
        this.features.push(theRectangle);

        //Add marker to map
        theRectangle.setMap(this.gmap);
        var tempScope = this;

        //Add listener for dragging
        if (this.mapMode !== 'view') { // Not used in  view mode
            google.maps.event.addListener(
                theRectangle,
                'bounds_changed',
                function (mouseEvent) {
                    tempScope.writeCoordinates(theRectangle);
                }
            );

            google.maps.event.addListener(
                theRectangle,
                'drag',
                function (mouseEvent) {
                    tempScope.writeCoordinates(theRectangle);
                }
            );

            google.maps.event.addListener(
                theRectangle,
                'rightclick',
                function (mouseEvent) {
                    //Create contextual menu for 'delete' link
                    //event.preventDefault();
                    $("<div id='deleteBoxMenuId'><a id='deleteLinkId'>Delete box</a></div>")
                        .appendTo("body")
                        .css({
                            top: event.pageY + "px",
                            left: event.pageX + "px",
                            zIndex: "100000",
                            position: "absolute",
                            backgroundColor: "#fff",
                            border: "1px solid black",
                            padding: "4px"
                        });

                    // Bind event to close contextual menu on any click
                    $(document).bind("click", function (event) {
                        $("div#deleteBoxMenuId").remove();
                    });

                    // Handler of the delete link click
                    // Be aware this will clean ALL boxes in map. But since the only case we have multiple boxes is in 'view' mode and this is
                    // not used in that mode, I guess we are OK for now
                    $('#deleteLinkId').bind("click", function (event) {
                        tempScope.clearMap();
                    });
                }
            );

            this.writeCoordinates(theRectangle);
        } // end if mapMode != 'view'
    },

    writeMarkerCoordinates: function (marker) {
        if (!this.coordElement) {
            this.coordElement = $("#" + this.coordLabel + "-inputEl");
        }
        var txt = "Lat: " + marker.position.lat() + ", Long: " + marker.position.lng();
        this.coordElement.html(txt);
    },

    writePolygonCoordinates: function (polygon) {
        //round the displayed coordinates to three decimal places
        const roundCoord = function (num) {
            return Math.round(num * 1000) / 1000;
        };

        const bounds = new google.maps.LatLngBounds();
        let paths = polygon.getPath().getArray();
        for (var i = 0; i < paths.length; i++) {
            bounds.extend(paths[i]);
        }

        if (!this.coordElement) {
            this.coordElement = $("#" + this.coordLabel + "-inputEl");
        }
        const txt = "N=" + roundCoord(bounds.getNorthEast().lat()) + "  &nbsp; S=" + roundCoord(bounds.getSouthWest().lat()) + "  &nbsp; E=" + roundCoord(bounds.getNorthEast().lng()) + " &nbsp; W=" + roundCoord(bounds.getSouthWest().lng());
        this.coordElement.html(txt);
    },

    //Writes the coordinates of the current box to an ExtJs control, whose id is passed as a config value. (Not used in 'view' mode)
    writeCoordinates: function (rectangle) {
        //round the displayed coordinates to three decimal places
        const roundCoord = function (num) {
            return Math.round(num * 1000) / 1000;
        };

        if (!this.coordLabel) {
            return;
        }

        if (!this.coordElement) {
            this.coordElement = $("#" + this.coordLabel + "-inputEl");
        }

        const llb = rectangle.getBounds();
        if (this.coordLabel) {
            const txt = "N=" + roundCoord(llb.getNorthEast().lat()) + "  &nbsp; S=" + roundCoord(llb.getSouthWest().lat()) + "  &nbsp; E=" + roundCoord(llb.getNorthEast().lng()) + " &nbsp; W=" + roundCoord(llb.getSouthWest().lng());
            this.coordElement.html(txt);
            Ext.getCmp(this.coordLabel).clearInvalid();
        }

        //show warning if box crosses the line
        const e = llb.getNorthEast().lng();
        const w = llb.getSouthWest().lng();
        const warn = w > e;
        if (warn) {
            Ext.Msg.show({
                title: 'Cross the International Date Line',
                msg: 'The box you defined crosses the International Date Line.',
                buttons: Ext.Msg.OK,
                icon: Ext.Msg.WARNING
            });
        }
    },

    afterComponentLayout: function (w, h) {
        this.callParent(arguments);
        this.redraw();
    },

    redraw: function () {
        if (this.gmap) {
            google.maps.event.trigger(this.gmap, 'resize');
        }
    },

    // Not used in 'view' mode. Returns an array of strings representing each of the drawn bboxes.
    getBoxes: function () {
        if (!this.features || this.features.length < 1) {
            return null;
        }
        const theWktBoxes = [];
        const theBoxes = this.features;
        const wkt = new Wkt.Wkt();
        for (let i = 0; i < theBoxes.length; i++) {
            wkt.fromObject(theBoxes[i]);
            theWktBoxes.push(wkt.write());
        }
        return theWktBoxes;
    },

    //Expects an array of strings, each containing a WKT box
    setBoxes: function (theWktBoxes) {
        if (!theWktBoxes) {
            return;
        }
        this.addWktBoxes(theWktBoxes);
        this.setMapExtent();
    },

    // Definition of elements and logic for the popup that allows user to enter coordinates manually.
    //not used in view mode
    manualCoordWindow: function (scope) {
        const googleLatLimit = 85.05115; //inherited limitations for Google maps http://stackoverflow.com/questions/11849636/maximum-lat-and-long-bounds-for-the-world-google-maps-api-latlngbounds

        const savedCoords = $("#" + scope.coordLabel + "-inputEl")[0].innerText;

        const popup = Ext.create('Ext.window.Window', {
            parentMapControl: this, // used to get hold of the map from inside the window.
            title: 'Enter Coordinates',
            id: 'coordWindowId',
            resizable: false,
            modal: true,
            constrain: true,
            closable: true,
            closeAction: 'destroy',
            layout: 'fit',
            defaults: {
                labelClsExtra: 'plainLabel',
                labelWidth: 60,
                labelSeparator: '',
                labelAlign: 'right',
                allowBlank: false,
                msgTarget: 'side',
                width: 375
            },
            items: [
                {
                    xtype: 'form',
                    itemId: 'coordinatesForm',
                    bodyPadding: 5,
                    defaults: {
                        bodyPadding: 10,
                        labelSeparator: '',
                        labelClsExtra: 'plainLabel',
                        labelAlign: 'right',
                        labelWidth: 60,
                        width: 200
                    },
                    items: [
                        {
                            xtype: 'displayfield',
                            width: 350,
                            value: 'Use Decimal Degree Notation <i>(e.g., 23.54, -100.2)</i>.<br> Positive values for the Northern Hemisphere(e.g., North = 40.26)<br>Negative values for the Western Hemisphere (e.g., West = -90.2)',
                            fieldStyle: "color: #444; font-size: .9em ",
                            margin: '0 0 10 20'
                        },
                        {
                            xtype: 'numberfield',
                            hideTrigger: true,
                            id: 'northId',
                            itemId: 'northItemId',
                            maxValue: googleLatLimit,
                            minValue: -googleLatLimit,
                            fieldLabel: '* North',
                            emptyText: '(e.g., 46.2)',
                            allowBlank: false,
                            listeners: {
                                afterrender: function (field) {
                                    field.focus(false, 1000);
                                }
                            }
                        },
                        {
                            xtype: 'numberfield',
                            hideTrigger: true,
                            id: 'southId',
                            fieldLabel: '* South',
                            maxValue: googleLatLimit,
                            minValue: -googleLatLimit,
                            allowBlank: false,
                            emptyText: '(e.g., 40.0)'
                        },
                        {
                            xtype: 'numberfield',
                            id: 'eastId',
                            hideTrigger: true,
                            fieldLabel: '* East',
                            maxValue: 180,
                            minValue: -180,
                            allowBlank: false,
                            emptyText: '(e.g., -90.0 )'
                        },
                        {
                            xtype: 'numberfield',
                            id: 'westId',
                            hideTrigger: true,
                            fieldLabel: '* West',
                            maxValue: 180,
                            minValue: -180,
                            allowBlank: false,
                            emptyText: '(e.g., -100.0 )'
                        }
                    ],
                    buttons:
                        [
                            {
                                text: 'Cancel',
                                handler: function (button, event) {
                                    button.up('window').close();
                                }
                            },
                            '->',
                            {
                                text: "Save",
                                cls: 'mainActionButton',
                                formBind: true,
                                disabled: true,
                                handler: function (button, event) {
                                    button.up('window').checkBoundaries();
                                }
                            }
                        ]
                }
            ],
            listeners: {
                afterrender: function (obj, eOpts) {
                    if (obj) {
                        //pre-fill the coordinate fields with the current bounding box points
                        const coordStr = obj.savedCoords.split(' ');
                        for (let i = 0; i < coordStr.length; i++) {
                            if (coordStr[i].trim().length > 0) {
                                if (coordStr[i].indexOf('N=') === 0) {
                                    Ext.getCmp('northId').setValue(coordStr[i].slice(2, coordStr[i].length));
                                }
                                if (coordStr[i].indexOf('S=') === 0) {
                                    Ext.getCmp('southId').setValue(coordStr[i].slice(2, coordStr[i].length));
                                }
                                if (coordStr[i].indexOf('E=') === 0) {
                                    Ext.getCmp('eastId').setValue(coordStr[i].slice(2, coordStr[i].length));
                                }
                                if (coordStr[i].indexOf('W=') === 0) {
                                    Ext.getCmp('westId').setValue(coordStr[i].slice(2, coordStr[i].length));
                                }
                            }
                        }
                    }
                }
            },
            checkBoundaries: function () {
                const nc = Ext.getCmp('northId');
                const sc = Ext.getCmp('southId');
                const ec = Ext.getCmp('eastId');
                const wc = Ext.getCmp('westId');
                const n = nc.getValue();
                const s = sc.getValue();
                const e = ec.getValue();
                const w = wc.getValue();
                const swCorner = new google.maps.LatLng(s, w);
                const neCorner = new google.maps.LatLng(n, e);
                const theBoxBounds = new google.maps.LatLngBounds(swCorner, neCorner);
                this.warnCrossDCL(theBoxBounds);
            },
            warnCrossDCL: function (theBounds) {
                const e = theBounds.getNorthEast().lng();
                const w = theBounds.getSouthWest().lng();
                var scope1 = this;
                const warn = w > e;

                if (warn) {
                    //First warn check
                    Ext.Msg.show({
                        title: 'Cross the International Date Line',
                        msg: 'The box you defined crosses the International Date Line.<br>Do you want to continue?',
                        buttons: Ext.Msg.YESNO,
                        icon: Ext.Msg.WARNING,
                        fn: function (buttonId, text, opt) {
                            if (buttonId === 'yes') {
                                scope1.saveRectangle(theBounds);
                                return true;
                            }
                            else {
                                return false;
                            }
                        }
                    });
                }
                else {
                    scope1.saveRectangle(theBounds);
                }
            },
            saveRectangle: function (theBounds) {
                const thePopup = Ext.getCmp("coordWindowId");
                scope.addLatLongBox(theBounds);
                thePopup.close();
            }
        });

        popup.center();
        popup.savedCoords = savedCoords;
        popup.show();
        //first st the focus to the window, then to the text control, it is the only way I have found to make it work.
        popup.focus();
        popup.down('#northItemId').focus();
    },

    clearMapNow: function (scope) {
        scope.clearMap();
        //clear label
        if (scope.coordLabel) {
            const txt = ' ';
            scope.coordElement.html(txt);
            Ext.getCmp(scope.coordLabel).clearInvalid();
        }
    }
});
function createHeader(params, profileURI) {    
    const allAuthors = params.allContactsDisplayCitation.replace(/\n/g, '  <br>');
    const safeString = NPS.DataStore.util.EncodeCheck.validate(params.displayCitation);
    const typeAndCode = params.typeAndCode || '';
    let allAuthorsCitation = '';
    let citation = '';
    let hasAllAuthorsORCid = false;   
    let hasOrcid = false;
    let programUrl = '';

    if (params.displayCitation.includes('<img')) {
        hasOrcid = true;
    }

    if (params.allContactsDisplayCitation.includes('<img')) {
        hasAllAuthorsORCid = true;
    }

    //this is a not DOI so show the Profile link
    if (!allAuthors.includes('https://doi.org')) {
        allAuthorsCitation = allAuthors;
        allAuthorsCitation += `<a id="profileLink" target="_blank" title="Click to go to Reference Profile page." href="${profileURI}${params.referenceCode}"> ${profileURI}${params.referenceCode} </a>`;
    }
    else {           
        allAuthorsCitation = allAuthors;
        allAuthorsCitation = allAuthorsCitation.replace('https://doi.org/' + params.digitalObjectIdentifier, '<a id="doiLink" class="orcidContact" target="_blank" title="Click to go to DOI." href="https://doi.org/' + params.digitalObjectIdentifier +'"');
        allAuthorsCitation += '> https://doi.org/' + params.digitalObjectIdentifier + '</a>';  
    }

    //this is a not DOI so show the Profile link
    if (!params.displayCitation.includes('https://doi.org')) {
        citation = params.displayCitation;
        citation += `<a id="profileLink" target="_blank" title="Click to go to Reference Profile page." href="${profileURI}${params.referenceCode}"> ${profileURI}${params.referenceCode} </a>`;
    }
    else {        
        citation = params.displayCitation;
        citation = citation.replace('https://doi.org/' + params.digitalObjectIdentifier, '<a id="doiLink1" target="_blank" class="orcidContact" title="Click to go to DOI." href="https://doi.org/' + params.digitalObjectIdentifier + '"');
        citation += '> https://doi.org/' + params.digitalObjectIdentifier + '</a>';
    }

    if (params.programTitles && params.programTitles !== '') {
        programUrl = params.programTitles;
    }

    Ext.create('Ext.container.Container', {
        renderTo: 'showHideButtonDiv',
        border: false,
        width: 1000,
        defaults: {
            labelSeparator: '',
            labelClsExtra: 'plainLabelHeader',
            labelAlign: 'right',
            labelWidth: 120
        },
        items: [            
            {
                xtype: 'displayfield',
                fieldLabel: 'Citation',
                hidden: hasOrcid,
                htmlEncode: false,                
                width: 980,
                value: citation
            },
            {
                xtype: 'displayfield',
                fieldLabel: 'Citation',
                hidden: !hasOrcid,
                htmlEncode: false,
                cls: 'orcidContact', 
                width: 980,
                value: citation
            },
            {
                xtype: 'container',
                margin: '3 0 0 0',
                defaults: {
                    labelSeparator: '',
                    labelClsExtra: 'plainLabelHeader',
                    labelAlign: 'right',
                    labelWidth: 120
                },
                layout: 'hbox',
                // ------------------------
                // The *calculated* Visibility for all records will be either Public, Internal, or Restricted.
                // If Public or Internal, then the record's Lifecycle is also Active
                // If Restricted, then record's Lifecycle is either Draft or Inactive
                // ------------------------
                items: [
                    {
                        xtype: 'displayfield',
                        fieldLabel: 'Visibility',
                        htmlEncode: !safeString,
                        value: params.visibility === 'Public' || params.visibility === 'Internal'
                            ? params.visibility
                            : ''
                    },
                    {
                        //show non-active lifecycle in red (style in profile page)
                        xtype: 'displayfield',
                        id: 'internalContentRed',
                        htmlEncode: !safeString,
                        value: 'Currently restricted to reference owner(s) until reference is set to active',
                        hidden: params.visibility === 'Restricted'
                            ? false
                            : true
                    }
                ]
            }, 
            {
                xtype: 'displayfield',                              
                margin: '5 15 0 0',
                fieldLabel: 'Citation with All Authors',
                hidden: !params.hasTwoOrMoreContacts || hasAllAuthorsORCid,                  
                width: 980,
                htmlEncode: false,
                value: allAuthorsCitation
            },               
            {
                xtype: 'displayfield',           
                margin: '5 15 0 0',
                fieldLabel: 'Citation with All Authors',
                hidden: !params.hasTwoOrMoreContacts || !hasAllAuthorsORCid, 
                width: 980,
                cls: 'orcidContact', 
                htmlEncode: false,                
                value: allAuthorsCitation
            },   
            {
                xtype: 'displayfield',
                fieldLabel: 'Reference Type',
                value: typeAndCode,
                htmlEncode: !safeString,
                margin: '2 0 0 0'
            },
            {
                xtype: 'displayfield',
                fieldLabel: 'Program Title',
                value: programUrl,
                hidden: !params.programTitles ? true : false,
                htmlEncode: false
            }
        ]
    });    
}

Ext.define('cachedFilter', {
    singleton: true,
    config: {
        FilterArray: new Array()
    },
    constructor: function (config) {
        this.initConfig(config);
        return this;
    }
});

//define a base grid for the collection's panels
Ext.define('NPS.DataStore.Controls.CollectionPaginationGrid', {
    extend: 'Ext.grid.Panel',
    xtype: 'collectionpaginationgrid',
    margin: '10 0 0 0',
    resizable: {
        pinned: true,
        wrap: true,
        handles: 's',
        dynamic: true,
        heightIncrement: 20
    },
    constructor: function (config) {
        //only fetch the data once
        var getFreshData = true;
        Ext.apply(this, {
            viewConfig: {
                emptyText: 'No results found',
                deferEmptyText: false,
                enableTextSelection: true,
                loadMask: false,
                preserveScrollOnRefresh: true
            },
            defaults: {
                overflowX: 'auto',
                overflowY: 'auto'
            },
            margin: '0 0 10 0',
            maxHeight: 800,
            minHeight: 100,
            collapsed: true,
            collapsible: true,
            titleCollapse: true,
            store: config.store,
            columns: {
                defaults: {
                    sortable: true,
                    hidden: false,
                    hideable: true,
                    resizable: true
                },
                items: config.columns
            },
            bbar: new Ext.PagingToolbar({
                store: config.store,
                displayInfo: true,
                displayMsg: 'Displaying {0} - {1} of {2}',
                emptyMsg: "No Records Found",
                plugins: [
                    {
                        ptype: 'pagingtoolbarresizer'
                    }
                ]
            }),
            listeners: {
                render: function (p) {
                    Ext.each(p.query('tool'), function (tool) {
                        if (tool.type === 'collapse-top') {
                            tool.tooltip = 'Open/Close Panel';
                            tool.isDefault = true;
                        }
                        if (tool.type === 'expand-bottom') {
                            tool.tooltip = 'Open/Close Panel';
                            tool.isDefault = true;
                        }
                        if (tool.isDefault) {
                            tool.ownerCt.on({
                                afterrender: function (owner) {
                                    owner.addTool(tool);
                                }
                            });
                        }
                    });
                }
            }
        });
        this.callParent(arguments);

        //Add all required listeners
        this.addListener('afterfirstlayout', function (grid, eOpts) { });

        this.addListener('expand', function (grid, eOpts) {
            //only get all the data once
            if (getFreshData === true) {
                NPS.Common.Utils.showLoadMask();
                grid.store.load({
                    callback: function (records, operation, success) {
                        //do not go back for fresh data unless the user performs a filter operation
                        getFreshData = false;
                        NPS.Common.Utils.hideLoadMask();
                    }
                });
            }
        });
    }
});

//define a base grid for the collection's panels
Ext.define('NPS.DataStore.Controls.ProjectCollectionPaginationGrid', {
    extend: 'NPS.DataStore.Controls.CollectionPaginationGrid',
    xtype: 'projectcollectionpaginationgrid',
    margin: '10 0 0 0',
    constructor: function (config) {
        //only fetch the data once
        let getFreshData = true;
        let hideTypeCombo = false;
        if (config.hideReferenceTypeCombos) {
            hideTypeCombo = config.hideReferenceTypeCombos;
        }

        Ext.apply(this, {
            store: config.store,
            dockedItems: [
                {
                    xtype: 'panel',
                    layout: 'hbox',
                    dock: 'top',
                    bodyPadding: 2,
                    bodyCls: 'x-panel-header-default',
                    defaults:
                    {
                        labelSeparator: '',
                        labelClsExtra: 'plainLabel',
                        labelAlign: 'right'
                    },
                    items: [
                        {
                            //set value in child controls that need a tip
                            xtype: 'displayfield',
                            value: 'Filter By: ',
                            id: 'filterby',
                            margin: '0 5 0 10'
                        },
                        {
                            xtype: 'textfield',
                            labelWidth: 25,
                            emptyText: ' ',
                            //make the title bigger if there are no Reference Type combos
                            width: (hideTypeCombo === false) ? 275 : 400,
                            submitEmptyText: false,
                            fieldLabel: 'Title',
                            allowBlank: true,
                            listeners: {
                                change: function (contr, newValue, oldValue, eOpts) {
                                    contr.up().up().applyFilters(null, 'title', contr.value);
                                }
                            }
                        },
                        {
                            xtype: 'combobox',
                            fieldLabel: 'Type',
                            labelWidth: 30,
                            width: 200,
                            hidden: hideTypeCombo,
                            store: Ext.create('Ext.data.Store', {
                                model: 'NPS.DataStore.Models.KeyValuePair',
                                storeId: 'referenceTypeStore',
                                proxy: {
                                    type: 'ajax',
                                    url: config.referenceTypeStoreUrl,
                                    extraParams: { id: config.referenceId }
                                },
                                loading: true,
                                autoLoad: true
                            }),
                            displayField: 'ReferenceTypeName',
                            valueField: 'ReferenceTypeId',
                            forceSelection: true,
                            selectOnFocus: true,
                            anyMatch: true,
                            enableKeyEvents: true,
                            queryMode: 'local',
                            minChars: 3,
                            queryDelay: 1000,
                            typeAhead: true,
                            autoSelect: false,
                            triggerAction: 'last',
                            lastQuery: '',
                            matchFieldWidth: false,
                            listConfig: { width: 300 },
                            listeners: {
                                change: function (contr, newValue, oldValue, eOpts) {
                                    contr.up().up().applyFilters(null, 'referencetype', newValue);
                                }
                            }
                        },
                        {
                            xtype: 'combobox',
                            fieldLabel: 'Content Unit',
                            labelWidth: 75,
                            //make the units combo bigger if there are no Reference Type combos
                            width: (hideTypeCombo === false) ? 250 : 330,
                            store: ('Ext.data.Store',
                            {
                                model: 'NPS.DataStore.Models.DataStoreUnit',
                                proxy: {
                                    type: 'ajax',
                                    url: config.linkedUnitStoreUrl,
                                    extraParams: { id: config.referenceId },
                                },
                                sorters: [
                                    { property: 'Name', direction: 'ASC' }
                                ],
                                loading: true,
                                autoLoad: true
                            }),
                            displayField: 'Name',
                            valueField: 'Code',
                            forceSelection: true,
                            selectOnFocus: true,
                            anyMatch: true,
                            enableKeyEvents: true,
                            queryMode: 'local',
                            minChars: 3,
                            queryDelay: 1000,
                            typeAhead: true,
                            autoSelect: false,
                            triggerAction: 'last',
                            lastQuery: '',
                            matchFieldWidth: false,
                            listConfig: { width: 400 },
                            listeners: {
                                change: function (contr, newValue, oldValue, eOpts) {
                                    contr.up().up().applyFilters(null, 'linkedunits', newValue);
                                }
                            }
                        },
                        {
                            xtype: 'numberfield',
                            hideTrigger: true,
                            fieldLabel: 'Year',
                            labelWidth: 30,
                            emptyText: 'YYYY',
                            maxValue: 9999,
                            minValue: 1,
                            width: 90,
                            allowBlank: true,
                            listeners: {
                                blur: function (contr, text, eOpts) {
                                    if (contr.value && contr.value.toString().length === 4) {
                                        contr.up().up().applyFilters(null, 'year', contr.value);
                                    }
                                    if (!contr.value || (contr.value && contr.value.toString().length === 0)) {
                                        contr.up().up().clearFilter();
                                    }
                                },
                                change: function (contr, newValue, oldValue, eOpts) {
                                    if (contr.value && contr.value.toString().length === 4) {
                                        contr.up().up().applyFilters(null, 'year', contr.value);
                                    }
                                    if (!contr.value || (contr.value && contr.value.toString().length === 0)) {
                                        contr.up().up().clearFilter();
                                    }
                                }
                            }
                        },
                        {
                            xtype: 'button',
                            text: 'Clear Filters',
                            margin: '0 0 0 10',
                            handler: function () {
                                this.up().up().clearFilter();
                            }
                        }
                    ]
                }
            ],
            applyFilters: function (op, prop, val) {
                let filters = cachedFilter.getFilterArray();
                //Clear empty title filter
                if (val === '' && prop === 'title') {
                    for (let i = filters.length - 1; i >= 0; i--) {
                        if (filters[i]) {
                            if (filters[i].property === 'title') {
                                delete filters[i];
                                filters = filters.filter(() => true);
                            }
                        }
                    }
                }

                //clear all other empty filters
                if (!val) {
                    for (let i = filters.length - 1; i >= 0; i--) {
                        if (filters[i]) {
                            delete filters[i];
                            filters = filters.filter(() => true);
                        }
                    }
                }

                //only add a filter if there is a valid filter to add
                if (val && val !== '') {
                    filters.push({
                        property: prop,
                        value: val,
                        operator: op
                    });
                }

                //add filters to the grid store
                filters = filters.filter(() => true);
                for (let i = 0; i < filters.length; i++) {
                    config.store.getFilters().add(filters);
                }

                //if there are no filters, reload default data
                if (filters.length === 0) {
                    this.clearFilter();
                }
            },
            clearFilter: function () {
                cachedFilter._FilterArray = new Array();

                //reset all controls
                const panels = this.query('panel');
                for (let p = 0; p < this.query('panel').length; p++) {
                    for (let c = 0; c < this.query('panel')[0].items.length; c++) {
                        let contr = this.query('panel')[p].items.items[c];
                        if (contr && contr.xtype !== 'button') {
                            try {
                                contr.reset();
                            }
                            catch { }
                        }
                    }
                }

                let targetStore = this.store;
                targetStore.filters.clear();
                targetStore.getSorters().add({ property: 'SortOrder', direction: 'ASC' });
            },
            columns: {
                defaults: {
                    sortable: true,
                    hidden: false,
                    hideable: true,
                    resizable: true
                },
                items: config.columns
            },
            bbar: new Ext.PagingToolbar({
                store: config.store,
                displayInfo: true,
                displayMsg: 'Displaying {0} - {1} of {2}',
                emptyMsg: "No Records Found",
                plugins: [
                    {
                        ptype: 'pagingtoolbarresizer'
                    }
                ]
            }),
            listeners: {
                render: function (p) {
                    Ext.each(p.query('tool'), function (tool) {
                        if (tool.type === 'collapse-top') {
                            tool.tooltip = 'Open/Close Panel';
                            tool.isDefault = true;
                        }
                        if (tool.type === 'expand-bottom') {
                            tool.tooltip = 'Open/Close Panel';
                            tool.isDefault = true;
                        }
                        if (tool.isDefault) {
                            tool.ownerCt.on({
                                afterrender: function (owner) {
                                    owner.addTool(tool);
                                }
                            });
                        }
                    });
                }
            }
        });
        this.callParent(arguments);

        //Add all required listeners        
        this.addListener('afterfirstlayout', function (grid, eOpts) { });
    }
});

/*Functions that create the store for all panels on the Reference profile page except the Files and Links panel.*/
function createCollectionStore(id, url, sorters) {
    return Ext.create('Ext.data.Store', {
        model: 'NPS.DataStore.Models.BaseReferenceModel',
        proxy: {
            type: 'ajax',
            writer: {
                type: 'json',
                partialDataOptions: {
                    changes: false,
                    critical: true
                }
            },
            method: 'POST',
            batchActions: false,
            url: url,
            extraParams: { Id: id },
            reader: { type: 'json', rootProperty: 'Results', totalProperty: 'TotalCount' },
            limitParam: 'limit',
            startParam: 'start',
            pageParam: 'page',
            sortParam: 'sort',
            listeners: {
                exception: function (request, data, operation) {
                    NPS.AjaxErrorHandler.forExtProxy("An error occurred retrieving information.", request, data, operation);
                }
            }
        },
        autoLoad: false,
        remoteSort: true,
        sortOnLoad: true,
        pageSize: 50,
        remoteFilter: false,
        sorters: sorters
    });
}

function createProjectProductCollectionStore(id, url, loadData, contrMethod, compId, titleStr, sorters, filters) {
    return Ext.create('Ext.data.Store', {
        model: 'NPS.DataStore.Models.BaseReferenceModel',
        proxy: {
            type: 'ajax',
            writer: {
                type: 'json',
                partialDataOptions: {
                    changes: false,
                    critical: true
                }
            },
            method: 'POST',
            batchActions: false,
            url: url,
            extraParams: { Id: id },
            reader: { type: 'json', rootProperty: 'Results', totalProperty: 'TotalCount' },
            limitParam: 'limit',
            startParam: 'start',
            pageParam: 'page',
            sortParam: 'sort',
            filterParam: 'filter',
            listeners: {
                exception: function (request, data, operation) {
                    NPS.AjaxErrorHandler.forExtProxy("An error occurred retrieving information.", request, data, operation);
                }
            }
        },
        listeners: {
            //remove from the grid any rows the user does not have permissions to view
            load: function (store, records, successful, operation, eOpts) {
                if (url.indexOf(contrMethod) > -1 && store.data.length === 0) {
                    Ext.getCmp(compId).setHidden(true);
                    return;
                }
                Ext.getCmp(compId).setTitle(`${titleStr} (${store.data.length})`)
            }
        },
        autoLoad: loadData,
        remoteSort: true,
        sortOnLoad: true,
        pageSize: 50,
        remoteFilter: true,
        sorters: sorters,
        filters: filters
    });
}

function createAssocProjectCollectionStore(id, projId, url) {
    return Ext.create('Ext.data.Store', {
        model: 'NPS.DataStore.Models.BaseReferenceModel',
        proxy: {
            type: 'ajax',
            writer: {
                type: 'json',
                partialDataOptions: {
                    changes: false,
                    critical: true
                }
            },
            method: 'POST',
            batchActions: false,
            url: url,
            extraParams: { Id: id, ProjId: projId },
            reader: { type: 'json', rootProperty: 'Results', totalProperty: 'TotalCount' },
            limitParam: 'limit',
            startParam: 'start',
            pageParam: 'page',
            sortParam: 'sort',
            listeners: {
                exception: function (request, data, operation) {
                    NPS.AjaxErrorHandler.forExtProxy("An error occurred retrieving information.", request, data, operation);
                }
            }
        },
        autoLoad: true,
        remoteSort: true,
        sortOnLoad: true,
        pageSize: 50
    });
}

//add the related children panel
function createRelatedChildrenGrid(inputs, childInputs, referenceProfileUrl, referenceFileUrl, documentDownloadUrl, isUnauthenticated, reportUrl, isAdmin) {
    if (inputs.TypeId === 'Program') {
        return;
    }

    //do not create the grid if there are no rows to display
    if (childInputs.relatedCount === 0 || childInputs.CanHaveChildren === false) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const relatedRefColumns = [];

    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceProfileColumn('Id', false, false, false, 'center', 22, referenceProfileUrl));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceFileCountColumn('File Count', 'FileCount', true, true, false, 70, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Code', 'Code', true, true, false, 80, referenceProfileUrl));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.refTypeNameColumn);
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', true, true, true, 450));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnSearch('Title', 'Title', true, true, false, referenceProfileUrl, null, 1));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.refYearOfIssueColumn);

    return Ext.create('NPS.DataStore.Controls.CollectionPaginationGrid', {
        title: childInputs.relatedCaption + ' (' + childInputs.relatedCount + ')',
        id: 'refProfile_relatedReferencesId',
        border: 1,
        width: 980,
        store: createCollectionStore(referenceId, childInputs.relatedChildrenUrl),
        columns: relatedRefColumns,
        tbar: [
            {
                xtype: 'label',
                margin: '0 10 0 830',
                html: `<a target="_blank" href="${reportUrl}">Download Reference List</a>`,
                hidden: childInputs.relatedCount === 0
            }
        ]
    });
}

function createProductsCreatedBy(inputs, productInputs, referenceProfileUrl, referenceFileUrl, documentDownloadUrl, refTypeStoreUrl, refGroupTypeUrl, linkedUnitStoreUrl, producingUnitStoreUrl, isUnauthenticated, isAdmin) {
    //do not create the grid if there are no rows to display
    if (productInputs.productsCount === 0 || productInputs.CanHaveProducts === false) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const refColumns = [];

    refColumns.push({
        xtype: 'actioncolumn',
        dataIndex: 'Id',
        menuDisabled: true,
        draggable: false,
        resizable: false,
        sortable: false,
        hideable: false,
        hidden: false,
        align: 'center',
        width: 22,
        items: [
            //profile icon
            {
                tooltip: 'Click to open the Reference profile. ',
                icon: Nps.icons.profile,
                handler: function (vw, rowIndex, colIndex, item, e, record) {
                    const showProfile = function (id) {
                        if (id !== undefined && id !== null) {
                            var url = referenceProfileUrl + '/' + id;

                            //Always create a new tab when the user clicks the link.
                            $.post(url, null)
                                .always(function (response) {
                                    window.open(url, '_blank');
                                });
                        }
                    };
                    showProfile(record.data.Id);
                },
                isDisabled: function (view, rowIndex, colIndex, item, record) {
                    //if user cannot access the information do not create a link just return the text
                    if (record.data.Accessible === true && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability === "Public") {
                        return false;
                    }
                    else if (isUnauthenticated === false && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability !== "Public") {
                        if (record.data.Downloadability === "Restricted") {
                            return false;
                        }
                        else {
                            return true;
                        }
                    }
                    else if (record.data.Lifecycle === 'Inactive') {
                        return true;
                    }
                    else if (isUnauthenticated === true && record.data.Accessible === true) {
                        return false;
                    }
                    return true;
                }
            }
        ]
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceFileCountColumn('File Count', 'FileCount', true, true, false, 70, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin));
    refColumns.push({
        header: 'Code',
        align: 'left',
        dataIndex: 'Code',
        sortable: true,
        hideable: true,
        hidden: true,
        width: 80,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            //if user cannot access the information do not create a link just return the text
            if (record.data.Accessible === true && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability === "Public") {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            else if (isUnauthenticated === false && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability !== "Public") {
                if (record.data.Downloadability === "Restricted") {
                    return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
                }
                else {
                    return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                }
            }
            else if (record.data.Lifecycle === 'Inactive') {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (isUnauthenticated === true && record.data.Accessible === true) {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refTypeNameColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', true, true, true, 450));
    refColumns.push({
        header: 'Title',
        align: 'left',
        dataIndex: 'Title',
        sortable: true,
        hideable: true,
        hidden: false,
        width: 450,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            //if user cannot access the information do not create a link just return the text
            if (record.data.Accessible === true && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability === "Public") {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            else if (isUnauthenticated === false && record.data.Lifecycle === "Active" && record.data.Visibility === "Public" && record.data.Downloadability !== "Public") {
                if (record.data.Downloadability === "Restricted") {
                    return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
                }
                else {
                    return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
                }
            }
            else if (record.data.Lifecycle === 'Inactive') {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (isUnauthenticated === true && record.data.Accessible === true) {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
        }
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refYearOfIssueColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refDownloadabilityColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.sortOrderColumn);

    const productName = inputs.TypeName.toLowerCase().indexOf('protocol') > -1
        ? 'Protocol '
        : inputs.TypeName;

    return Ext.create('NPS.DataStore.Controls.ProjectCollectionPaginationGrid', {
        id: 'refProfile_productsCreatedByReferencesId',
        border: 1,
        width: 980,
        height: 200,
        store: createProjectProductCollectionStore(referenceId, productInputs.productsCreatedByUrl, false, 'GetProductsCreatedByData', 'refProfile_productsCreatedByReferencesId', `Products Created By This ${productName}`),
        referenceTypeStoreUrl: refTypeStoreUrl,
        referenceGroupTypeUrl: refGroupTypeUrl,
        proUnitStoreUrl: producingUnitStoreUrl,
        linkedUnitStoreUrl: linkedUnitStoreUrl,
        referenceId: referenceId,
        columns: refColumns
    });
}

function createOriginatingProject(inputs, projectInputs, referenceProfileUrl, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin) {
    //do not create the grid if there are no rows to display
    if (projectInputs.AssociatedProjects === 0) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const refColumns = [];

    refColumns.push({
        xtype: 'actioncolumn',
        dataIndex: 'Id',
        menuDisabled: true,
        draggable: false,
        resizable: false,
        sortable: false,
        hideable: false,
        hidden: false,
        align: 'center',
        width: 22,
        items: [
            //profile icon
            {
                tooltip: 'Click to open the Reference profile. ',
                icon: Nps.icons.profile,
                handler: function (vw, rowIndex, colIndex, item, e, record) {
                    const showProfile = function (id) {
                        if (id !== undefined && id !== null) {
                            var url = referenceProfileUrl + '/' + id;

                            //Always create a new tab when the user clicks the link.
                            $.post(url, null)
                                .always(function (response) {
                                    window.open(url, '_blank');
                                });
                        }
                    };
                    showProfile(record.data.Id);
                },
                isDisabled: function (view, rowIndex, colIndex, item, record) {
                    // Returns true if user cannot view restricted Profiles

                    if (record.data.Accessible === true && record.data.Visibility === "Public" && record.data.Lifecycle === "Active") {
                        return false
                    }
                    //HACK!! to let user see some metadata on a Reference they cannot access
                    else if (record.data.Accessible === true && record.data.Downloadability === "Restricted" && record.data.Lifecycle === "Active" && isUnauthenticated === false) {
                        return true;
                    }
                    else if (record.data.Accessible === false && record.data.Downloadability !== "Public" && record.data.Lifecycle !== "Active") {
                        return true;
                    }
                    else if (record.data.Lifecycle === 'Inactive') {
                        return true;
                    }
                    return false;
                }
            }
        ]
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceFileCountColumn('File Count', 'FileCount', true, true, false, 70, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin));
    refColumns.push({
        header: 'Code',
        align: 'left',
        dataIndex: 'Code',
        sortable: true,
        hideable: true,
        hidden: true,
        width: 80,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            //if user cannot access the information do not create a link just return the text

            if (record.data.Accessible === true && record.data.Visibility === "Public" && record.data.Lifecycle === "Active") {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            //HACK!! to let user see some metadata on a Reference they cannot access
            else if (record.data.Accessible === true && record.data.Downloadability === "Restricted" && record.data.Lifecycle === "Active" && isUnauthenticated === false) {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (record.data.Accessible === false && record.data.Downloadability !== "Public" && record.data.Lifecycle !== "Active") {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (record.data.Lifecycle === 'Inactive') {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
        }
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refTypeNameColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', true, true, true, 450));
    refColumns.push({
        header: 'Title',
        align: 'left',
        dataIndex: 'Title',
        sortable: true,
        hideable: true,
        hidden: false,
        width: 450,
        renderer: function (text, metaData, record, rowIndex, colIndex, store, view) {
            //if user cannot access the information do not create a link just return the text

            if (record.data.Accessible === true && record.data.Visibility === "Public" && record.data.Lifecycle === "Active") {
                return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
            }
            //HACK!! to let user see some metadata on a Reference they cannot access
            else if (record.data.Accessible === true && record.data.Downloadability === "Restricted" && record.data.Lifecycle === "Active" && isUnauthenticated === false) {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (record.data.Accessible === false && record.data.Downloadability !== "Public" && record.data.Lifecycle !== "Active") {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            else if (record.data.Lifecycle === 'Inactive') {
                return NPS.DataStore.util.ToolTip.simpleToolTipSpan(text);
            }
            return NPS.DataStore.util.ToolTip.toolTipLink(referenceProfileUrl + '/' + record.data.Id, text, '_blank');
        }
    });
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refYearOfIssueColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.refDownloadabilityColumn);
    refColumns.push(NPS.DataStore.ReferenceGridColumns.sortOrderColumn);

    let projects = Ext.create('Ext.container.Container', {
        id: 'refProfile_originatingProjectReferencesId'
    });

function createProjectGrid(itemIdVal, projId, num) {
    return Ext.create('NPS.DataStore.Controls.CollectionPaginationGrid', {
        title: 'Originating Project and Related Products (' + num + ')',
        border: 1,
        width: 980,
        itemId: itemIdVal,
        store: createAssocProjectCollectionStore(referenceId, projId, projectInputs.originatingProjectUrl),
        columns: refColumns
    });
}

let counts = projectInputs.AssociatedProjectProductCount;
for (let i = 0; i < projectInputs.AssociatedProjects; i++) {
    let number = 0;
    for (let k = 0; k < counts.length; k++) {
        if (counts[k].Key === projectInputs.AssociatedProjectIds[i]) {
            //the one project and its associated products
            number = counts[k].Value + 1;
            break;
        }
    }

    let itemID = 'associatedProjGrid' + i;
    let proj = createProjectGrid(itemID, projectInputs.AssociatedProjectIds[i], number);
    projects.add(proj);
}

return projects;
}

function createCrossReferences(inputs, crossInputs, referenceProfileUrl, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin) {
    //do not create the grid if there are no rows to display
    if (crossInputs.crossRefCount === 0) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const crossRefColumns = [];

    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceProfileColumn('Id', false, false, false, 'center', 22, referenceProfileUrl));
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceFileCountColumn('File Count', 'FileCount', true, true, false, 70, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin));
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Code', 'Code', true, true, true, 80, referenceProfileUrl));
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.refTypeNameColumn);
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', true, true, true, 450));
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Title', 'Title', true, true, false, 450, referenceProfileUrl));
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.refYearOfIssueColumn);
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.refDownloadabilityColumn);
    crossRefColumns.push(NPS.DataStore.ReferenceGridColumns.sortOrderColumn);

    return Ext.create('NPS.DataStore.Controls.CollectionPaginationGrid', {
        title: `See Also (${crossInputs.crossRefCount})`,
        id: 'refProfile_crossReferencesId',
        border: 1,
        width: 980,
        height: 175,
        store: createCollectionStore(referenceId, crossInputs.crossReferenceUrl),
        columns: crossRefColumns
    });
}

function createVersionHistory(inputs, versionInputs, referenceProfileUrl, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin) {
    //do not create the grid if there are no rows to display
    if (versionInputs.versionsCount === 0 || versionInputs.CanBeVersioned === false) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const versionColumns = [];

    versionColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceProfileColumn('Id', false, false, false, 'center', 22, referenceProfileUrl));
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceFileCountColumn('File Count', 'FileCount', false, true, false, 70, referenceFileUrl, documentDownloadUrl, isUnauthenticated, isAdmin));
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Code', 'Code', false, true, false, 80, referenceProfileUrl));
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.refTypeNameNoSortColumn);
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', false, true, true, 450));
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Title', 'Title', false, true, false, 450, referenceProfileUrl));
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.refDateOfIssueNoSortColumn);
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.refDownloadabilityNoSortColumn);
    versionColumns.push(NPS.DataStore.ReferenceGridColumns.refVersionNotesNoSortColumn);

    return Ext.create('NPS.DataStore.Controls.CollectionPaginationGrid', {
        title: `Version History (${versionInputs.versionsCount})`,
        viewConfig: {
            getRowClass: function (record) {
                if (record && parseInt(record.get('Code')) === referenceId) {
                    return 'highlighted-row';
                }
                else {
                    return '';
                }
            }
        },
        id: 'refProfile_versionedReferencesId',
        border: 1,
        width: 980,
        height: 200,
        store: createCollectionStore(referenceId, versionInputs.versionReferenceUrl),
        columns: versionColumns
    });
}

//add the Related Projects panel
function createRelatedProjectsGrid(inputs, projectData, referenceProfileUrl, refTypeStoreUrl, programProjectLinkedUnits) {
    if (inputs.TypeId !== 'Program') {
        return;
    }
    //do not create the grid if there are no rows to display
    if (projectData.relatedCount === 0) {
        return null;
    }
    const referenceId = inputs.ReferenceId;
    const relatedRefColumns = [];

    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceProfileColumn('Id', false, false, false, 'center', 22, referenceProfileUrl));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createProjectProductCountColumn('Product Count', 'ProductCount', true, true, false, 100));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumn('Code', 'Code', true, true, false, 80, referenceProfileUrl));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnDisplayCitation('Display Citation', 'DisplayCitation', true, true, true, 450));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.createReferenceLinkProfileColumnSearch('Title', 'Title', true, true, false, referenceProfileUrl, null, 1));
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.refYearOfIssueColumn);
    relatedRefColumns.push(NPS.DataStore.ReferenceGridColumns.sortOrderColumn);

    return Ext.create('NPS.DataStore.Controls.ProjectCollectionPaginationGrid', {
        id: 'refProfile_relatedProjectsReferencesId',
        border: 1,
        width: 980,
        height: 200,
        store: createProjectProductCollectionStore(referenceId, projectData.relatedChildrenUrl, true, 'GetRelatedReferencesData', 'refProfile_relatedProjectsReferencesId', projectData.relatedCaption),
        hideReferenceTypeCombos: true,
        referenceTypeStoreUrl: refTypeStoreUrl,
        linkedUnitStoreUrl: programProjectLinkedUnits,
        referenceId: referenceId,
        columns: relatedRefColumns
    });
}
//create the core panel and all it's child display controls
function createCoreTab(model, fields, referenceProfileUrl) {
    var emptyValueMarker = ' - - ',
        //the trim version is needed because the textarea style is a bit different -- pain
        trimmedEmptyValueMarker = ' - - ',
        //helper functions that create the core display controls
        createOrcidDisplayField = function (panel, name, theFields, data) {
            if (theFields[name]) {
                panel.add({
                    xtype: 'displayfield',
                    hidden: (NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true)) === 'None Provided' ? true : false,
                    cls: 'orcidContact',
                    fieldLabel: NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true),
                    value: data.data[theFields[name].Name] !== null && data.data[theFields[name].Name].length > 0
                        ? data.data[theFields[name].Name].replace(/\n/g, '<br>')
                        : emptyValueMarker
                });
            }
        },
        createDisplayField = function (panel, name, theFields, data) {
            if (theFields[name]) {
                //is there a link to the orcid site and is that link in the first row
                if (data.data[theFields[name].Name] && data.data[theFields[name].Name].includes('https://orcid.org/') && data.data[theFields[name].Name].indexOf('https://orcid.org/') <= 145) {
                    createOrcidDisplayField(panel, name, theFields, data);
                }
                else {
                    panel.add({
                        xtype: 'displayfield',
                        hidden: (NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true)) === 'None Provided' ? true : false,
                        fieldLabel: NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true),
                        value: data.data[theFields[name].Name] !== null && data.data[theFields[name].Name].length > 0
                            ? data.data[theFields[name].Name].replace(/\n/g, '<br>')
                            : emptyValueMarker
                    });
                }
            }
        },

        createDisplayTextarea = function (panel, name, theFields, data) {
            if (theFields[name] && data.data[theFields[name].Name] !== null && data.data[theFields[name].Name].length > 0) {

                let info = data.data[theFields[name].Name] !== null && data.data[theFields[name].Name].length > 0
                    ? data.data[theFields[name].Name].replace(/\n/g, '<br>')
                    : trimmedEmptyValueMarker;
                panel.add({
                    xtype: 'fieldcontainer',
                    maxHeight: 175,
                    margin: '1 0 3 0',
                    border: false,
                    width: 955,
                    overflowY: 'auto',
                    fieldLabel: NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true),
                    hidden: (NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true)) === 'None Provided' ? true : false,
                    items: [
                        {
                            xtype: 'displayfield',
                            cls: 'force-display-contact-profile',
                            name: theFields[name].Name,
                            fieldLabel: name,
                            hideLabel: true,
                            value: NPS.DataStore.util.cleanUnicodeChars(info)
                        }
                    ]
                });
            }
            else {
                panel.add({
                    xtype: 'displayfield',
                    cls: 'force-display-contact-profile',
                    width: 300,
                    hidden: (NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true)) === 'None Provided' ? true : false,                    
                    fieldLabel: NPS.DataStore.util.ReferenceLabel.getFieldLabel(theFields[name], true),
                    value: trimmedEmptyValueMarker
                });
            }
        },

        //the panel (tab)
        coreTab = Ext.create('Ext.form.Panel', {
            xtype: 'panel',
            border: false,
            title: "Core Info",
            tooltip: 'Core bibliographic information, including title, description, authors, etc...',
            bodyPadding: 10,
            defaults: {
                labelSeparator: '',
                labelClsExtra: 'plainLabel',
                labelAlign: 'right',
                labelWidth: 120,
                msgTarget: 'side',
                border: false,
                width: 955
            },
            layout: 'vbox'
        });

    //Dynamically add all the display controls to the tab panel
    //
    //The order is important. Any Reference might display an element. It will key off of the value,
    //if the value is non-null it is displayed otherwise it is assumed by the GUI that the particular
    //element is not required by the given Reference. But the order is the same for all References, thus hard coded.

    //display fields
    createDisplayField(coreTab, 'Title', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'DateOfIssue', fields.SelfFieldInfo, model);

    //due to the need to display a link to the ORCiD site there can be no encoding of the contacts values. This will undoubtedly cause issues with special characters 
    createDisplayField(coreTab, 'Contacts1', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Contacts2', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Contacts3', fields.SelfFieldInfo, model);

    createDisplayField(coreTab, 'Publisher', fields.SelfFieldInfo, model);
    model.data.DigitalObjectIdentifier = model.data.DigitalObjectIdentifier ? `${model.data.DigitalObjectIdentifier}` : null;
    createDisplayField(coreTab, 'DigitalObjectIdentifier', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'MiscCode', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Location', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'ContentBeginDate', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'ContentEndDate', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Size1', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Size2', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Size3', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Description', fields.SelfFieldInfo, model);
    //text area
    createDisplayTextarea(coreTab, 'Purpose', fields.SelfFieldInfo, model);
    createDisplayTextarea(coreTab, 'TableOfContents', fields.SelfFieldInfo, model);
    //display fields
    createDisplayField(coreTab, 'Notes', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'DateRange', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'MeetingPlace', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Volume', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Issue', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'Edition', fields.SelfFieldInfo, model);
    createDisplayField(coreTab, 'PageRange', fields.SelfFieldInfo, model);

    /*
    * When value is Yes: The information resource described by this reference was created by or for the NPS
    * When value is No: The information resource described by this reference was not created by or for the NPS
    * When value is Unknown: The information resource described by this reference has not been evaluated to determine whether its origination is the NPS
    */

    //NOTE: ExtJS cannot handle tri-state boolean, so 0 = unknown, 1 = true, and 2 = false.
    //default to unknown i.e. zero
    let hasOriginationFlagMsg = 'The information resource described by this reference has not been evaluated to determine whether its origination is the NPS';
    //if true
    if (model.data.IsAgencyOriginated === 1) {
        hasOriginationFlagMsg = 'The information resource described by this reference was created by or for the NPS';
    }
    //if false
    if (model.data.IsAgencyOriginated === 2) {
        hasOriginationFlagMsg = 'The information resource described by this reference was not created by or for the NPS';
    }

    coreTab.add({
        xtype: 'displayfield',
        id: 'hasOrigination',
        fieldLabel: 'Origination',
        value: hasOriginationFlagMsg
    });

    coreTab.add({
        xtype: 'displayfield',
        fieldLabel: 'Metadata Standard',
        hidden: fields.SelfFieldInfo.MetadataStandard === undefined,
        encodeHtml: true,
        renderer: function (value, metaData, record) {
            if (fields.MetadataStandardID) {
                return '<a id="profileLink" title="Click to navigate to external Metadata Standard web site." href="' + fields.MetadataStandardURL + '">' + fields.MetadataStandardURL + '</a>';
            }
            return ' - - ';
        }
    });

    coreTab.add({
        xtype: 'displayfield',
        fieldLabel: 'License',
        hidden: fields.SelfFieldInfo.LicenseType == undefined,
        encodeHtml: true,
        renderer: function (value, metaData, record) {
            if (fields.LicenseTypeID) {
                return '<a id="profileLink" title="Click to navigate to external License web site." href="' + fields.LicenseTypeURL + '">' + fields.LicenseTypeName + '</a>';
            }
            return ' - - ';
        }
    });

    if (fields.ParentFieldInfo) {
        //container -- parent data
        //Parent Title -- linked to it's profile page
        if (fields.TypeId !== 'Project') {
            if (fields.ParentFieldInfo['Title']) {
                let pTitle = '';
                if (model.data['PpTypeName'].length > 0) {
                    pTitle = model.data['PpTypeName'] + ' Title';
                }
                else {
                    pTitle = 'Title';
                }

                let showParentAsLink = model.data['ParentId'] !== 0 && model.data.PpIsAccessible === true,
                    parentTitle = model.data['PpTitle'].length > 0
                        ? model.data['PpTitle']
                        : ` Code: ${model.data['ParentId']}`;

                coreTab.add({
                    xtype: 'container',
                    defaults: {
                        labelSeparator: '',
                        labelClsExtra: 'plainLabel',
                        labelAlign: 'right',
                        labelWidth: 120
                    },
                    layout: 'hbox',
                    items: [
                        {
                            xtype: 'displayfield',
                            fieldLabel: pTitle
                        },
                        {
                            xtype: 'component',
                            margin: model.data['PpTitle'] && model.data['PpTitle'].length > 100
                                ? '3 0 35 0'
                                : '3 0 10 0',
                            hidden: !showParentAsLink && fields.CanUserViewParent,
                            //create a link to the parent
                            autoEl: {
                                tag: 'a',
                                href: referenceProfileUrl + '/' + model.data['ParentId'],
                                html: parentTitle,
                                cls: 'ds-ancors'
                            }
                        },
                        {
                            xtype: 'component',
                            margin: model.data['PpTitle'] && model.data['PpTitle'].length > 100
                                ? '3 0 35 0'
                                : '3 0 10 0',
                            hidden: showParentAsLink,
                            html: model.data['PpTitle'].length > 0 ? parentTitle : emptyValueMarker
                        }
                    ]
                });
            }
        }
        createDisplayField(coreTab, 'DateRange', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'MeetingPlace', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'Volume', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'Issue', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'Edition', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'DateOfIssue', fields.ParentFieldInfo, model);

        //due to the need to display a link to the ORCiD site there can be no encoding of the contacts values. This will undoubtedly cause issues with special characters 
        createDisplayField(coreTab, 'Contacts1', fields.ParentFieldInfo, model, false);
        createDisplayField(coreTab, 'Contacts2', fields.ParentFieldInfo, model, false);
        createDisplayField(coreTab, 'Contacts3', fields.ParentFieldInfo, model, false);

        createDisplayField(coreTab, 'Publisher', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'MiscCode', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'Location', fields.ParentFieldInfo, model);
        createDisplayField(coreTab, 'Size1', fields.ParentFieldInfo, model);

        //serial -- grandparent data
        //Grandparent Title -- linked to it's profile page
        if (model.data['GpTitle'] !== undefined && model.data['GpTitle'] !== null) {
            let gPTitle = '';
            if (model.data['GpTypeName'].length > 0) {
                gPTitle = model.data['GpTypeName'] + ' Title';
            }
            else {
                gPTitle = 'Title';
            }

            //should the grandparent title be a link
            let showGrandparentAsLink = model.data['GpTitle'].length > 0 && model.data['GrandparentId'] !== 0 && model.data.GpIsAccessible === true;
            //should the grandparent title display at all
            let showGrandparentTitle = model.data['GpTypeName'].length > 0;

            if (showGrandparentTitle) {
                coreTab.add({
                    xtype: 'container',
                    defaults: {
                        labelSeparator: '',
                        labelClsExtra: 'plainLabel',
                        labelAlign: 'right',
                        labelWidth: 120
                    },
                    layout: 'hbox',
                    items: [
                        {
                            xtype: 'displayfield',
                            fieldLabel: gPTitle
                        },
                        {
                            xtype: 'component',
                            margin: model.data['GpTitle'] && model.data['GpTitle'].length > 100
                                ? '3 0 15 0'
                                : '3 0 5 0',
                            hidden: !showGrandparentAsLink && fields.CanUserViewGrandparent,
                            //create a link to the grandparent
                            autoEl: {
                                tag: 'a',
                                href: referenceProfileUrl + '/' + model.data['GrandparentId'],
                                html: model.data['GpTitle'],
                                cls: 'ds-ancors'
                            }
                        },
                        {
                            xtype: 'component',
                            margin: model.data['GpTitle'] && model.data['GpTitle'].length > 100
                                ? '3 0 15 0'
                                : '3 0 5 0',
                            hidden: showGrandparentAsLink,
                            html: model.data['GpTitle'].length > 0 ? model.data['GpTitle'] : emptyValueMarker
                        }
                    ]
                });

                if (model.data['GpPublisher'] !== undefined && model.data['GpPublisher'] !== null) {
                    coreTab.add({
                        xtype: 'displayfield',
                        name: 'GpPublisher',
                        fieldLabel: 'Publisher',
                        value: model.data.GpPublisher !== null && model.data.GpPublisher.length > 0
                            ? model.data.GpPublisher
                            : emptyValueMarker
                    });
                }
            }
        }
    }

    return coreTab;
}

//points to the tab control, needed so tabs can be dynamically show/hidden
var profileTabControl = null;

function createTabs(data, fields, profileUrl, isContributor) {
    profileTabControl = Ext.create("Ext.tab.Panel", {
        itemId: 'referenceProfileTabItemId',
        border: false,
        plain: true,
        renderTo: 'tabPanelsDiv',
        hidden: true,
        width: 980,
        items: [
            //create the bibliographic info
            createCoreTab(data, fields, profileUrl),

            //create permissions tab
            createPermissionsTab(fields.ReferenceId, fields.Lifecycle, fields.LinkedResourceCount, isContributor),

            //create units tab
            createProfileGeographyTab(fields.ReferenceId),

            //keywords
            createProfileSubjectKeywordTab(fields.ReferenceId),

            //taxonomy
            createProfileTaxonomyTab(fields.ReferenceId, fields.CanExtractMetadata),

            //method
            createProfileMethodTab(fields.ReferenceId, fields.CanExtractMetadata, fields.HasMethods),

            //history - non-Legacy record
            createHistoryTab(isContributor, fields.ReferenceId, data.data.IsLegacy, fields.CanExtractMetadata, pageModel)
        ]    
    });  

    if(data.data.IsLegacy === 'true') {
        //the last tab is the History tab
        profileTabControl.setActiveTab(profileTabControl.items.length - 1);
        profileTabControl.getTabBar().show();
        profileTabControl.setVisible(true);
    }
    return profileTabControl;
}

//get the core data and if successful, call createTabs
function createTabPanel(coreFields, referenceProfileUrl, isContributor) {
    NPS.Common.Utils.showLoadMask();
    //tell the model to go get the one we want
    NPS.DataStore.Models.Reference.Profile.Core.load(coreFields.ReferenceId, {
        success: function (data) {
            profileTabControl = createTabs(data, coreFields, referenceProfileUrl, isContributor);
            profileTabControl.getTabBar().show();
            profileTabControl.setVisible(true);
            NPS.Common.Utils.hideLoadMask();
        },
        failure: function () {
            NPS.Common.Utils.hideLoadMask();
            Ext.Msg.alert('Error', 'There was an error while retrieving profile data.<br>&nbsp;');
        }
    });
}

//creates all the controls (panels) on the Reference profile page
function createCorePanelControls(inputs, profileUrl, getHoldingsUrl, documentDownloadUrl, refPanelInputs, isContributor, isUnauthenticated,
    reportUrl, bundleURL, refTypeStoreUrl, refGroupTypeUrl, linkUnitStoreUrl, producingUnitStoreUrl, programProjectLinkedUnits, isAdmin)
{
    Ext.create('Ext.form.FieldContainer',
        {
            renderTo: 'coreInfoDiv',
            title: '',
            border: false,
            id: 'mainPanelId',
            itemId: 'mainPanelItemId',
            titleCollapse: true,
            colapseMode: 'header',
            width: 980,
            defaults: {
                labelSeparator: '',
                labelClsExtra: 'plainLabel',
                buttonAlign: 'right',
                labelWidth: 100
            },
            items: [                
                //Files and Links
                createFilesAndLinksGrid(inputs, getHoldingsUrl, documentDownloadUrl, isUnauthenticated, bundleURL),

                //Related references
                createRelatedChildrenGrid(inputs, refPanelInputs, profileUrl, getHoldingsUrl, documentDownloadUrl, isUnauthenticated, reportUrl),

                //Products Created by
                createProductsCreatedBy(inputs, refPanelInputs, profileUrl, getHoldingsUrl, documentDownloadUrl, refTypeStoreUrl, refGroupTypeUrl, linkUnitStoreUrl, producingUnitStoreUrl, isUnauthenticated, isAdmin),

                //Projects associated to a Program
                createRelatedProjectsGrid(inputs, refPanelInputs, profileUrl, refTypeStoreUrl, programProjectLinkedUnits),

                //Originating Project Related Products
                createOriginatingProject(inputs, refPanelInputs, profileUrl, getHoldingsUrl, documentDownloadUrl, isUnauthenticated),

                //Cross-References
                createCrossReferences(inputs, refPanelInputs, profileUrl, getHoldingsUrl, documentDownloadUrl, isUnauthenticated),

                //Version History
                createVersionHistory(inputs, refPanelInputs, profileUrl, getHoldingsUrl, documentDownloadUrl, isUnauthenticated),

                //More/Less
                {
                    xtype: 'container',
                    items: [
                        {
                            xtype: 'button',
                            id: 'moreInfoButtonId',
                            style: 'background-image: none !important;float:right', // remove the ugly default pink background when depressed
                            baseCls: '',
                            tooltip: 'View additional information about this reference<br><br>',
                            text: 'click here for less info',
                            handler: function () {
                                const toggleShowHideTabs = function (button) {
                                    if (button.text === 'click here for more info') {
                                        button.setText('click here for less info');
                                        if (profileTabControl === null) {
                                            //create the tab panel.
                                            profileTabControl = createTabPanel(inputs, profileUrl, isContributor);
                                        }
                                        else {
                                            profileTabControl.setActiveTab(0);
                                            profileTabControl.getTabBar().show();
                                            profileTabControl.setVisible(true);
                                        }
                                    }
                                    else {
                                        button.setText('click here for more info');
                                        if (profileTabControl !== null && profileTabControl !== undefined) {
                                            profileTabControl.getTabBar().hide();
                                            profileTabControl.setVisible(false);
                                        }
                                    }
                                };
                                toggleShowHideTabs(this);
                            }
                        }
                    ]
                }
            ]
        });

    //Expanding the Projects grid panel if it exists. The grid fetches data only when it is expanded.
    let projectPanel = Ext.ComponentQuery.query('panel[id=refProfile_relatedProjectsReferencesId]')[0];
    if (projectPanel) {
        projectPanel.expand();
    }    
}
function createActionButtonsSections(params) {
    //true if the user can edit the Reference
    var canEdit;
    // ask the backend if the user can edit
    (function () {
        Ext.Ajax.request({
            url: params.canEditRefUrl,
            timeout: _ajaxTimeout,
            async: false,
            method: 'POST',
            params: {
                id: params.modelId
            },
            success: function (response, opts) {
                canEdit = response.responseText === 'true'
                    ? true
                    : false;
            },
            failure: function (response, opts) {
            }
        });
    })();

    Ext.create('Ext.container.Container',
        {
            renderTo: 'actionButtonDiv',
            defaults: {
                collapsible: true,
                titleCollapse: true,
                maxHeight: 300,
                labelSeparator: '',
                labelWidth: 120,
                layout: 'vbox'
            },
            items: [
                {
                    xtype: 'button',
                    extraCls: 'plainLabel',
                    style: 'background-image: none !important;float:right', // remove the ugly default pink background when depressed                    
                    width: 130,
                    text: 'Actions',
                    menu: new Ext.menu.Menu({
                        items: [
                            {
                                text: 'Add to a Collection',
                                disabled: params.hideAddToCollections,
                                handler: function() {
                                    const addToCollectionsPopup = function() {
                                        const popup = new NPS.DataStore.Reference.Collection.Window({
                                            saveUpdateCollection: params.saveUpdateCollection,
                                            referenceCount: 1, //there is only one reference profile (reference) to be added
                                            recordData: params.modelId,
                                            isFromProfile: true,
                                            collectionProfileUrl: params.collectionProfileUrl
                                        });
                                        popup.center();
                                        popup.show();
                                    };
                                    addToCollectionsPopup();
                                }
                            },
                            {
                                text: 'Clone',
                                disabled: params.hideClone,
                                handler: function() {
                                    const createClone = function(code) {
                                        Ext.create('Ext.window.Window',
                                            {
                                                title: 'Clone the Reference',
                                                resizable: false,
                                                width: 650,
                                                modal: true,
                                                constrain: true,
                                                closeAction: 'destroy',
                                                layout: 'fit',
                                                hidden: true,
                                                items: [
                                                    {
                                                        xtype: 'form',
                                                        bodyPadding: 10,
                                                        layout: {
                                                            type: 'vbox',
                                                            align: 'stretch',
                                                            pack: 'start'
                                                        },
                                                        defaults: {
                                                            margin: '10 0 0 0',
                                                            buttonAlign: 'right',
                                                            labelSeparator: '',
                                                            labelClsExtra: 'plainLabel',
                                                            labelWidth: 100
                                                        },
                                                        items: [
                                                            {
                                                                xtype: 'label',
                                                                html:
                                                                    '<div>You are about to clone a reference. A cloned reference is identical to the original except for the following: <br>' +
                                                                        '<ul>' +
                                                                        '<li>The date of issue will be cleared</li>' +
                                                                        '<li>The Reference is set to "Draft"</li>' +
                                                                        '<li>Files and links are not copied</li>' +
                                                                        '<li>Links to projects and versions are not preserved</li>' +
                                                                        '<li>Existing ownership will be removed unless you select the following box</li>' +
                                                                        '<li>Existing producing unit links will be removed unless you select the following box</li>' +
                                                                        '<li>Existing content unit links will be removed unless you select the following box</li>' +
                                                                        '<li>Existing cross-reference links will be removed unless you select the following box</li>' +
                                                                        '</ul>'
                                                            },
                                                            {
                                                                xtype: 'checkbox',
                                                                boxLabel: 'Include existing owners',
                                                                name: 'includeOwners',
                                                                itemId: 'includeOwnersItemId',
                                                                margin: '10 0 0 10',
                                                                checked: false
                                                            },
                                                            {
                                                                //the so called producing units are stored in table ReferenceContentProducerUnits
                                                                xtype: 'checkbox',
                                                                boxLabel: 'Include all producing unit links',
                                                                name: 'includeContentUnits',
                                                                itemId: 'includeContentUnitsItemId',
                                                                margin: '10 0 0 10',
                                                                checked: false
                                                            },
                                                            {
                                                                //the so called content links are the original unit links stored in table ReferenceUnitLinks
                                                                xtype: 'checkbox',
                                                                boxLabel: 'Include all content unit links',
                                                                name: 'includeProducingUnits',
                                                                itemId: 'includeProducingUnitsItemId',
                                                                margin: '10 0 0 10',
                                                                checked: false
                                                            },
                                                            {
                                                                xtype: 'checkbox',
                                                                boxLabel: 'Include existing cross-reference links',
                                                                name: 'includeCrossReference',
                                                                itemId: 'includeCrossReferenceItemId',
                                                                margin: '10 0 0 10',
                                                                checked: false
                                                            },
                                                            {
                                                                xtype: 'displayfield',
                                                                id: 'cloneMessageId',
                                                                value:
                                                                    'Please be sure to make any necessary edits to the new Reference<br>to prevent creation of duplicate records.'
                                                            }
                                                        ],
                                                        buttons: [
                                                            {
                                                                width: 70,
                                                                text: 'Cancel',
                                                                handler: function() {
                                                                    this.up('window').destroy();
                                                                }
                                                            },
                                                            '->', //align to right
                                                            {
                                                                width: 70,
                                                                cls: 'mainActionButton',
                                                                text: 'Clone',
                                                                formBind: true,
                                                                listeners: {
                                                                    enable: function(btn) {
                                                                        btn.addCls('mainActionButton');
                                                                    },
                                                                    disable: function(btn) {
                                                                        btn.removeCls('mainActionButton');
                                                                    }
                                                                },
                                                                handler: function() {
                                                                    const cloneReference = function(button) {
                                                                        const form2 = button.up('window').down('form');
                                                                        if (!form2.isValid()) {
                                                                            Ext.Msg.alert('Invalid', 'The form is invalid. Errors are marked in red.&nbsp;');
                                                                            return;
                                                                        }
                                                                        const includeOwners = form2.down('#includeOwnersItemId').checked;
                                                                        const includeCrossReferences = form2.down('#includeCrossReferenceItemId').checked;
                                                                        const includeProducingUnits = form2.down('#includeProducingUnitsItemId').checked;
                                                                        const includeContentUnits = form2.down('#includeContentUnitsItemId').checked;

                                                                        NPS.Common.Utils.showLoadMask();
                                                                        $.ajax({
                                                                            url: params.cloneUrl,
                                                                            data: {
                                                                                id: code,
                                                                                includeOwners: includeOwners,
                                                                                includeCrossReferences: includeCrossReferences,
                                                                                includeProducingUnits: includeProducingUnits,
                                                                                includeContentUnits: includeContentUnits
                                                                            },
                                                                            type: 'post',
                                                                            timeout: _ajaxTimeout,
                                                                            success: function(data, textStatus, xhr) {
                                                                                NPS.Common.Utils.hideLoadMask();
                                                                                //force a reload of the edit page so the page state will be updated
                                                                                window.location = params.editRefUrl + '/' + data.CloneId;
                                                                            },
                                                                            error: function(xhr, status, error) {
                                                                                NPS.Common.Utils.hideLoadMask();
                                                                                NPS.AjaxErrorHandler.forJQuery('There was an error while attempting to Clone this Reference.',
                                                                                    xhr,
                                                                                    status,
                                                                                    error,
                                                                                    null);
                                                                            }
                                                                        });
                                                                        button.up('window').destroy();
                                                                    };
                                                                    cloneReference(this);
                                                                }
                                                            }
                                                        ]
                                                    }
                                                ]
                                            }).show();
                                    };
                                    createClone(params.modelId);
                                }
                            },
                            {
                                text: 'Edit',
                                disabled: !canEdit,
                                handler: function() {
                                    window.location = params.editRefUrl + '/' + params.modelId;
                                }
                            },
                            {
                                text: 'Export Reference Profile',
                                hidden: params.RefType !== 'Datapackage',
                                handler: function () {
                                    window.open(params.printToPdfReporAnonymousUrl, '_blank');
                                }
                            }
                        ]
                    })
                }
            ]
        });
} 

function createReportButtonsSections(params) {
    Ext.create('Ext.container.Container',
        {
            renderTo: 'actionButtonDiv',
            defaults: {
                collapsible: true,
                titleCollapse: true,
                maxHeight: 300,
                labelSeparator: '',
                labelWidth: 120,
                layout: 'vbox'
            },
            items: [
                {
                    xtype: 'button',
                    extraCls: 'plainLabel',
                    style: 'background-image: none !important;float:right', // remove the ugly default pink background when depressed
                    width: 150,
                    text: 'Export Reference Profile',
                    hidden: params.RefType !== 'Datapackage',
                    handler: function() {
                        window.open(params.printToPdfReporAnonymousUrl, '_blank');
                    }
                }
            ]
        });
}
/*
Create the Reference profile permissions tab.

Params
id: The Reference Id
lifecycle: The Reference lifecycle
linkedResourceCount: The total number of digital resources the Reference has.
*/

function createPermissionsTab(id, lifecycle, linkedResourceCount, isContributor) {
    var getFreshData = true;
    const permissionsTab = Ext.create('Ext.form.Panel', {
        border: false,
        title: 'Permissions',
        itemId: 'permissionsTabItemId',
        tooltip: 'Permission information includes who has access to both</ br>the reference and any associated digital files.',
        bodyPadding: 10,
        margin: '10 0 0 0',
        defaults: {
            labelSeparator: '',
            labelClsExtra: 'plainLabel',
            labelAlign: 'right',
            labelWidth: 120,
            msgTarget: 'side',
            border: false,
            width: 955,
            htmlEncode: true
        },
        layout: 'vbox',
        listeners: {
            activate: function (thisTab, eOpts) {
                if (getFreshData === true) {
                    //load tab data
                    //Get the application record, if available, and load into form
                    //load the data onto the Core tab nested form
                    NPS.Common.Utils.showLoadMask();
                    NPS.DataStore.Models.Reference.Profile.Permission.load(id, {
                        success: function (data) {
                            let emptyValueMarker = ' - - ';
                            let refAccessLevelLabel = 'Reference Access Level';
                            let dataStewardLabel = 'Data Stewards Permission to Edit';
                            //dynamically create the tab controls so the visibility can be controlled
                            const createDisplayfields = function (info) {
                                var refAccessLevel = Ext.create('Ext.form.FieldContainer', {
                                    bodyPadding: 10,
                                    margin: '10 0 0 0',
                                    defaults: {
                                        labelSeparator: '',
                                        labelClsExtra: 'plainLabel',
                                        labelAlign: 'right',
                                        labelWidth: 120,
                                        msgTarget: 'side',
                                        border: false,
                                        htmlEncode: true
                                    },
                                    layout: 'hbox'
                                });

                                thisTab.add(refAccessLevel);
                                //not public
                                if (info.IsInternal === 'true') {
                                    refAccessLevel.add({ xtype: 'displayfield', htmlEncode: true, name: 'IsInternal', fieldLabel: refAccessLevelLabel, value: "Internal" });
                                }
                                else {
                                    refAccessLevel.add({ xtype: 'displayfield', htmlEncode: true, name: 'IsInternal', fieldLabel: refAccessLevelLabel, value: 'Public' });
                                }
                                if (lifecycle === '(Draft)' || lifecycle === '(Inactive)') {
                                    refAccessLevel.add({
                                        xtype: 'displayfield',
                                        margin: '0 0 0 10',
                                        id: 'internalContentRedRefAccess',
                                        value: 'Currently restricted to reference owner(s) until reference is set to active'
                                    });
                                }

                                let fileAccessLevel = Ext.create('Ext.form.FieldContainer', {
                                    bodyPadding: 10,
                                    margin: '10 0 0 0',
                                    defaults: {
                                        labelSeparator: '',
                                        labelClsExtra: 'plainLabel',
                                        labelAlign: 'right',
                                        labelWidth: 120,
                                        msgTarget: 'side',
                                        border: false,
                                        htmlEncode: true
                                    },
                                    layout: 'hbox'
                                });

                                thisTab.add(fileAccessLevel);
                                if (linkedResourceCount && linkedResourceCount > 0) {
                                    //not public
                                    if (lifecycle === '(Draft)' || lifecycle === '(Inactive)') {
                                        fileAccessLevel.add({ xtype: 'displayfield', name: 'FileAccess', fieldLabel: 'File Access Level ', value: info.FileAccess });

                                        fileAccessLevel.add({
                                            xtype: 'displayfield',
                                            margin: '0 0 0 10',
                                            id: 'internalContentRedFileAccess',
                                            value: 'Currently restricted to reference owner(s) until reference is set to active'
                                        });
                                    }
                                    else {
                                        thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'FileAccess', fieldLabel: 'File Access Level ', value: info.FileAccess }); 
                                    }
                                }
                                else {
                                    thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'FileAccess', fieldLabel: 'File Access Level ', value: 'N/A' });
                                }

                                let isSensitivityOperations = info.Sensitivity.startsWith("Operations"); 
                                let isSensitivityUnknown = info.Sensitivity.startsWith("Unknown");
                                let isSensitivity = info.Sensitivity.startsWith("Sensitive");
                                let isNotSensitivityNot = info.SensitivityDetail === '' && info.Sensitivity === '';
                                let sensitivityDetails = '';

                                if (isSensitivityOperations === true) {
                                    sensitivityDetails = `Operations ${info.SensitivityDetails}`;
                                }
                                else if (isSensitivityUnknown === true) {
                                    sensitivityDetails = `Unknown ${info.SensitivityDetails}`;
                                }
                                else if (isSensitivity === true) {
                                    sensitivityDetails = `Sensitive - ${info.SensitivityDetails}`;
                                }
                                else if (isNotSensitivityNot === true) {
                                    sensitivityDetails = `Not Sensitive ${info.SensitivityDetails}`;
                                }
                                thisTab.add({ xtype: 'displayfield', htmlEncode: false, name: 'SensitivityDetails', fieldLabel: 'Sensitivity Designation(s)', value: sensitivityDetails });

                                let showCopyrights = info.Copyrights && info.Copyrights.length > 0;
                                let proprietaryValue = 'No Restriction';

                                if (showCopyrights === true) {
                                    proprietaryValue = info.Copyrights;
                                }
                                else if (info.Proprietary !== '') {
                                    proprietaryValue = info.Proprietary;
                                }
                                
                                thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'Copyrights', fieldLabel: 'Proprietary/Copyright Designation', value: proprietaryValue });
                                thisTab.add({
                                    xtype: 'displayfield',
                                    htmlEncode: true,
                                    name: 'Quality',
                                    fieldLabel: "Quality",
                                    value: info.Quality && info.Quality.length > 0
                                        ? info.Quality
                                        : 'Unknown'
                                });
                                thisTab.add({
                                    xtype: 'displayfield',
                                    hidden: (info.UseConstraints && info.UseConstraints.length > 0) ? false : true,
                                    htmlEncode: false,
                                    name: 'UseConstraints',
                                    fieldLabel: "Additional Use Constraints",
                                    value: info.UseConstraints && info.UseConstraints.length > 0
                                        ? info.UseConstraints.replace(/\n/g, '<br>')
                                        : emptyValueMarker
                                });
                                if (info.AllowDataStewardsEdit === 'true') {
                                    thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'AllowDataStewardsEdit', fieldLabel: dataStewardLabel, margin: '0 0 10 0', value: 'Yes' });
                                }
                                else {
                                    thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'AllowDataStewardsEdit', fieldLabel: dataStewardLabel, margin: '0 0 10 0', value: 'No' });
                                }

                                thisTab.add({
                                    xtype: 'displayfield',
                                    htmlEncode: true,
                                    name: 'Owners',
                                    fieldLabel: 'Reference Owners',
                                    value: info.Owners && info.Owners.length > 0 && isContributor === true
                                        ? info.Owners
                                        : 'NPS Staff'
                                });

                                /*file access*/
                                let fileAccessValue = 'N/A';
                                let fileAccess = info.FileAccess.toLowerCase();

                                if (fileAccess.toLowerCase() === 'public') {
                                    fileAccessValue = info.FileAccess;
                                }
                                else if (fileAccess.toLowerCase() === 'internal') {
                                    fileAccessValue = 'NPS Staff';
                                }
                                else if (fileAccess.toLowerCase() === 'specific individuals') {
                                    //internal or public
                                    if (info.UsersWithFileAccess.length === 0) {
                                        fileAccessValue = 'No Individuals Have Been Specified';
                                    }
                                    //internal
                                    else if (isContributor === true) {
                                        fileAccessValue = info.UsersWithFileAccess;
                                    }
                                    //public
                                    else {
                                        fileAccessValue = 'Only Specified Individuals';
                                    }
                                }
                                thisTab.add({ xtype: 'displayfield', htmlEncode: true, name: 'UsersWithFileAccess', fieldLabel: 'Permission to Download Files', value: fileAccessValue });
                            };
                            createDisplayfields(data.data);
                            NPS.Common.Utils.hideLoadMask();
                        },
                        //there is an error callback
                        error: function (xhr, status, error) {
                            NPS.Common.Utils.hideLoadMask();
                            NPS.AjaxErrorHandler.forJQuery('There was an error while retrieving permission data for the Reference.', xhr, status, error, null);
                        },
                        failure: function () {
                            NPS.Common.Utils.hideLoadMask();
                            Ext.Msg.alert('Error', 'There was an error while retrieving permission data for the Reference.');
                        }
                    });
                    getFreshData = false;
                }
            }
        }
    });
    return permissionsTab;
}
//Units and Geography
//param: id is the Reference Id

function createProfileGeographyTab(id) {
    var theTab = Ext.create('Ext.form.Panel', {
        border: false,
        title: 'Units and Geography',
        id: "geographyTabId",
        tooltip: 'The geographic footprint of the information resource and indicate whether it has park-specific information by linking it to one or more NPS units.&nbsp;',
        listeners: {
            afterrender: function (thisTab, eOpts) {
                //only need to load the lookup store once.
                Ext.getStore('profileReferenceContentProducerUnitStore').getProxy().extraParams.id = id;
                Ext.getStore('profileReferenceUnitStore').getProxy().extraParams.id = id;
                Ext.getStore('profileReferenceBoundingBoxStore').getProxy().extraParams.referenceId = id;

                //map panel
                UnitsGeographical.load(id,
                    {
                        success: function (data) {
                            const tab = Ext.getCmp('geographyTabId');
                            //add bounding boxes to map
                            const bboxStore = data.data.BBox;
                            var theBoxes = new Array();
                            bboxStore.forEach(function (rec) {
                                theBoxes.push({ description: rec.Description, wkt: rec.WKT });
                            });

                            const tmap = tab.down('bboxmap');
                            tmap.clearMap();
                            tmap.addWktBoxes(theBoxes);
                            tmap.setMapExtent();

                            NPS.Common.Utils.hideLoadMask();
                        },
                        failure: function () {
                            NPS.Common.Utils.hideLoadMask();
                            Ext.Msg.alert('Error', 'There was an error while fetching your data.&nbsp;');
                        }
                    });
            },
            activate: function (thisTab, eOpts) {
                //load the grid store every time the tab is activated
                Ext.getStore('profileReferenceContentProducerUnitStore').load();
                Ext.getStore('profileReferenceUnitStore').load();                
                Ext.getStore('profileReferenceBoundingBoxStore').on({                    
                    load: {
                        fn: function (store) {
                            if (store.data.length === 0) {
                                theTab.down('#gridHelpTextItemId').setVisible(false);
                            }
                        },
                        scope: this
                    }
                });
                Ext.getStore('profileReferenceBoundingBoxStore').load();
            }
        },
        layout: 'hbox',
        items: [
            {
                xtype: 'container',
                width: 400,
                margin: '0 10 0 0',
                items: [
                    {
                        xtype: 'grid',
                        title: 'Producing Unit Link(s)',
                        store: 'profileReferenceContentProducerUnitStore',
                        minHeight: 100,
                        maxHeight: 255,
                        margin: '10 0 0 0',
                        viewConfig: {
                            deferEmptyText: false,
                            emptyText: 'No results found',
                            enableTextSelection: true
                        },
                        columns: [
                            { text: 'Unit Name', dataIndex: 'FullName', flex: 1 },
                            { text: 'Unit Code', dataIndex: 'Code', align: 'center', width: 80 }
                        ]
                    },
                    {
                        xtype: 'grid',
                        title: 'Content Unit Link(s)',
                        store: 'profileReferenceUnitStore',
                        minHeight: 100,
                        maxHeight: 255,
                        margin: '10 0 0 0',
                        viewConfig: {
                            deferEmptyText: false,
                            emptyText: 'No results found',
                            enableTextSelection: true
                        },
                        columns: [
                            { text: 'Unit Name', dataIndex: 'FullName', flex: 1 },
                            { text: 'Unit Code', dataIndex: 'Code', align: 'center', width: 80 }
                        ]
                    },
                    {
                        xtype: 'grid',
                        title: 'Geography',
                        store: 'profileReferenceBoundingBoxStore',
                        minHeight: 100,
                        maxHeight: 255,
                        margin: '10 0 0 0',
                        viewConfig: {
                            deferEmptyText: false,
                            emptyText: 'No results found',
                            enableTextSelection: true
                        },
                        columns: [
                            { text: 'Id', dataIndex: 'Id', width: 60, hidden: true },
                            {
                                text: 'Description', dataIndex: 'Description', flex: 1,
                                renderer: function (value, metaData, record) {
                                    return NPS.DataStore.util.ToolTip.encodeToolTipSpan(value);
                                }
                            },
                            { text: 'Coordinates', dataIndex: 'WKT', flex: 2, hidden: true }
                        ],
                        tbar: [
                            '->',
                            {
                                xtype: 'displayfield',
                                itemId: 'gridHelpTextItemId',
                                cls: 'gridHeaderHelpText',
                                value: 'Hover over map markers for details.'
                            },
                            '->',
                            {
                                xtype: 'button',
                                text: 'Show All',
                                tooltip: 'Show all geography on the map',
                                handler: function (butt) {
                                    const map = theTab.down('bboxmap');
                                    var theBoxes = [],
                                        selModelobj = butt.up('grid').selModel;
                                    butt.up('grid').store.getRange().forEach(function (rec) {
                                        theBoxes.push({ description: rec.data.Description, wkt: rec.data.WKT });
                                        selModelobj.deselect(rec);
                                    });
                                    if (theBoxes.length > 0) {
                                        map.clearMap();
                                        map.addWktBoxes(theBoxes);
                                        map.setMapExtent();
                                    }                                   
                                }
                            }
                        ],
                        listeners: {
                            select: function (selModel, record, index, options) {
                                const map = theTab.down('bboxmap');
                                const theBoxes = [];
                                for (var i = 0, recs = selModel.selected.items, len = selModel.selected.items.length; i < len; i++) {
                                    theBoxes.push({ description: recs[i].data.Description, wkt: recs[i].data.WKT });
                                }
                                if (theBoxes.length > 0) {
                                    const featureOptions = {
                                        icon: null,
                                        shadow: null,
                                        editable: false,
                                        draggable: false,
                                        strokeColor: '#A06E0A',
                                        fillColor: '#D4D4CA',
                                        fillOpacity: 0.4
                                    };

                                    map.clearMap();
                                    map.addSelectedWktBox(theBoxes, featureOptions);
                                    map.setMapExtent();
                                }
                            }
                        }
                    }
                ]
            },
            {
                xtype: 'bboxmap',
                itemId: 'bboxMap',
                mapMode: 'view',
                isNPS: true,
                width: 570,
                height: 520,
                featureOptions: { strokeColor: '#456765' }
            }
        ]
    });

    return theTab;
}
function createProfileSubjectKeywordTab(id) {
    const theTab = Ext.create('Ext.form.Panel', {
        border: false,
        title: 'Keywords',
        id: "keywordsTabId",
        itemId: 'keywordsTabItemId',
        tooltip: 'A list of vocabularies and terms or free form keywords to facilitate discovery.<br><br>',
        layout: 'vbox',
        listeners: {
            afterrender: function (thisTab, eOpts) {
                //only need to load the lookup store once.
            },
            activate: function (thisTab, eOpts) {
                //load the grid store every time the tab is activated
                var tab = this;
                KeywordSubjectCategory.load(id, {
                    success: function (data) {
                        tab.down('#keywords').setValue(data.data.Keywords);

                        const subjectCategory = data.data.SubjectCategories;
                        var theCategories = new Array();
                        subjectCategory.forEach(function (rec) {
                            theCategories.push(rec.Tag);
                        });
                        tab.down('#subjectCategories').setValue(theCategories.join('<br>'));
                    },
                    failure: function () {
                        NPS.Common.Utils.hideLoadMask();
                        Ext.Msg.alert('Error', 'There was an error while fetching your data.&nbsp;');
                    }
                });
            }
        },
        defaults: {
            labelSeparator: '',
            labelClsExtra: 'plainLabel',
            labelAlign: 'right',
            labelWidth: 120,
            msgTarget: 'side',
            border: false,
            width: 955,
            htmlEncode: false
        },
        items: [
            {
                xtype: 'displayfield',
                htmlEncode: true,
                itemId: 'keywords',
                fieldLabel: 'Keywords',
                name: "Keywords",
                margin: '5 0 0 0'
            },
            {
                xtype: 'displayfield',
                itemId: 'subjectCategories',
                fieldLabel: 'Vocabularies and Terms',
                name: "SubjectCategories"
            }
        ]
    });

    return theTab;
}
//Taxonomy
function createProfileTaxonomyTab(id, canExtractMetadata) {
    const theTab = Ext.create('Ext.form.Panel', {
        border: false,
        title: 'Taxonomy',
        id: "taxonomyTabId",
        itemId: 'taxonomyTabItemId',
        tooltip: 'Specific biological taxa information to improve discoverability<br><br>',
        listeners: {
            afterrender: function (thisTab, eOpts) {
                //only need to load the lookup store once.
                Ext.getStore('taxonomyStore').getProxy().extraParams.referenceId = id;
                Ext.getStore('reportedTaxonomyStore').getProxy().extraParams.referenceId = id;
            },
            activate: function (thisTab, eOpts) {
                //load the grid store every time the tab is activated
                Ext.getStore('taxonomyStore').load();
                Ext.getStore('reportedTaxonomyStore').load();
            }
        },
        items: [
            {
                xtype: 'grid',
                id: 'taxonomyGrid',
                store: 'taxonomyStore',
                width: '100%',
                margin: '5 0 0 0',
                minHeight: 200,
                maxHeight: 400,
                htmlEncode: false,
                viewConfig: {
                    deferEmptyText: false,
                    emptyText: 'No results found',
                    enableTextSelection: true
                },
                tbar: [
                    {
                        xtype: 'label',
                        text: 'Taxonomic Records for this Reference',
                        style: 'font-weight: bold',
                        margin: '4 0 4 5'
                    }
                ],
                columns: [
                    { text: 'Code', dataIndex: 'TaxonCode', width: 80 },
                    { text: 'Category', dataIndex: 'CategoryName', width: 150 },
                    { text: 'Scientific Name', dataIndex: 'SciName', width: 200, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                    { text: 'Common Names', dataIndex: 'CommonNames', width: 200, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                    { text: 'Kingdom', dataIndex: 'Kingdom', width: 100, hidden: true },
                    { text: 'Order', dataIndex: 'Order', width: 100, hidden: true },
                    { text: 'Family', dataIndex: 'Family', width: 100, hidden: false },
                    { text: 'Source', dataIndex: 'SourceName', width: 150, hidden: true },
                    { text: 'Rank', dataIndex: 'Rank', width: 100, hidden: true },
                    { text: 'External Code', dataIndex: 'ExternalCode', width: 100, hidden: true }
                ],
                setGridTitle: function () {
                    const count = this.store.getCount();
                    this.down('label').setText(count !== 0 ? `Taxonomic Records for this Reference (${count})` : 'Taxonomic Records for this Reference');
                }
            },
            {
                xtype: 'grid',
                id: 'reportedtaxonomyGrid',
                store: 'reportedTaxonomyStore',
                width: '100%',
                margin: '5 0 0 0',
                minHeight: 200,
                maxHeight: 400,
                htmlEncode: false,
                hidden: !canExtractMetadata,
                viewConfig: {
                    deferEmptyText: false,
                    emptyText: 'No results found',
                    enableTextSelection: true
                },
                tbar: [
                    {
                        xtype: 'label',
                        text: 'Taxonomic Records Extracted from Metadata for this Reference',
                        style: 'font-weight: bold',
                        margin: '4 0 4 5'
                    }
                ],
                columns: [
                    { text: 'Source', dataIndex: 'SourceName', width: 80 },
                    { text: 'Reported Taxon ID', dataIndex: 'ReportedTaxonId', width: 150 },
                    { text: 'Taxon ID', dataIndex: 'TaxonId', width: 80 },
                    { text: 'Rank', dataIndex: 'Rank', width: 100 },
                    { text: 'Scientific Name', dataIndex: 'SciName', width: 200, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                    { text: 'Common Names', dataIndex: 'CommonNames', width: 200, renderer: NPS.DataStore.util.Format.qtipRenderer() },
                    { text: 'Kingdom', dataIndex: 'Kingdom', width: 100 },
                    { text: 'Phylum', dataIndex: 'Phylum', width: 100 },
                    { text: 'Class', dataIndex: 'Class', width: 100 },
                    { text: 'Order', dataIndex: 'Order', width: 100 },
                    { text: 'Family', dataIndex: 'Family', width: 100 },
                    { text: 'Genus', dataIndex: 'Genus', width: 100 },
                    { text: 'Subspecies', dataIndex: 'Subspecies', width: 100 }
                ],
                setGridTitle: function () {
                    const count = this.store.getCount();
                    this.down('label').setText(count !== 0 ? `Taxonomic Records Extracted from Metadata for this Reference (${count})` : 'Taxonomic Records Extracted from Metadata for this Reference');
                }
            }
        ]
    });

    return theTab;
}
function createProfileMethodTab(id, canExtractMetadata, hasMethods) {
    var showMethods = canExtractMetadata && hasMethods;
    var theTab = Ext.create('Ext.form.Panel', {
        title: 'Methods',
        tooltip: '',
        id: 'methodTab',
        hidden: !showMethods,
        listeners: {
            afterrender: function (thisTab, eOpts) {
                //only need to load the lookup store once.
                Ext.getStore('MethodGridStore').getProxy().extraParams.referenceId = id;
                Ext.getStore('MethodGridStore').load();
            },
            activate: function (thisTab, eOpts) {
                //load the grid store every time the tab is activated
            }
        },
        items: [
        ],
        setMethodView: function (records) {
            let selfPanel = this;
            let setCollapse = false;

            Ext.each(records, function (method) {
                selfPanel.add({
                    xtype: 'panel',
                    title: 'Data Package Method Description',
                    margin: '0 0 10 0',
                    border: false,
                    collapsible: true,
                    titleCollapse: true,
                    animCollapse: false,
                    collapsed: setCollapse,
                    cls: 'method-panel',
                    items: [
                        {
                            html: method.get('Paragraph')
                        }
                    ]
                });
                setCollapse = true;
            });
        }
    });
    return theTab;
}

function createHistoryTab(isContributor, id, isLegacyRec, canExtractMetadata, pageModel) {
    
    const theTab = Ext.create('Ext.form.Panel', {
        border: false,
        title: 'History',
        itemId: 'historyTabItemId',
        bodyPadding: 10,
        margin: '10 0 0 0',
        defaults: {
            labelSeparator: '',
            labelClsExtra: 'plainLabel',
            labelAlign: 'right',
            labelWidth: 120,
            msgTarget: 'side',
            border: false,
            width: 955,
            htmlEncode: true
        },
        layout: 'vbox',
        items: [
            {
                xtype: 'container',
                layout: 'hbox',
                items: [
                    {
                        xtype: 'container',
                        border: false,
                        width: '50%',
                        defaults: {
                            labelSeparator: '',
                            labelClsExtra: 'plainLabel',
                            labelAlign: 'right',
                            labelWidth: 120,
                            msgTarget: 'side',
                            border: false,
                            width: '50%',
                            htmlEncode: true
                        },
                        items: [
                            {
                                xtype: 'displayfield',
                                itemId: 'LastEditedItemId',
                                name: 'LastEdited',
                                fieldLabel: 'Reference - Date Last Edited'
                            },
                            {
                                xtype: 'displayfield',
                                itemId: 'LastEditedByItemId',
                                name: 'LastEditedBy',
                                fieldLabel: 'Last Edited By',
                                hidden: !isContributor
                            }
                        ]
                    },
                    {
                        xtype: 'container',
                        border: false,
                        defaults: {
                            labelSeparator: '',
                            labelClsExtra: 'plainLabel',
                            labelAlign: 'right',
                            labelWidth: 120,
                            msgTarget: 'side',
                            border: false,
                            width: '50%',
                            htmlEncode: true
                        },
                        items: [
                            {
                                xtype: 'displayfield',
                                itemId: 'FirstActivationDateItemId',
                                name: 'FirstActivationDate',
                                fieldLabel: 'Reference First Activated'
                            },
                            {
                                xtype: 'displayfield',
                                itemId: 'FirstActivatedByItemId',
                                name: 'FirstActivatedBy',
                                fieldLabel: 'Activated By',
                                hidden: !isContributor
                            }
                        ]
                    }
                ]
            }, 
            {
                xtype: 'displayfield',
                itemId: 'CreatedItemId',
                name: 'Created',
                fieldLabel: 'Reference - Date Created',
                margin: '30 0 5 0'
            },
            {
                xtype: 'displayfield',
                itemId: 'CreatedByItemId',
                name: 'CreatedBy',
                fieldLabel: 'Created By',
                hidden: !isContributor
            },
            {
                xtype: 'displayfield',
                itemId: 'ExtractedFileNameItemId',
                name: 'ExtractedFileName',
                fieldLabel: 'Metadata Extracted from',
                margin: '30 0 5 0',
                hidden: !canExtractMetadata
            },
            {
                xtype: 'displayfield',
                itemId: 'ExtractedItemId',
                name: 'Extracted',
                fieldLabel: 'Extracted Date',
                renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                hidden: !canExtractMetadata
            },
            {
                xtype: 'displayfield',
                itemId: 'ExtractedByItemId',
                name: 'ExtractedBy',
                fieldLabel: 'Extracted By',
                hidden: !canExtractMetadata
            },
            {
                xtype: 'displayfield',
                itemId: 'IsLegacyItemId',
                name: 'IsLegacy',
                labelWidth: 630,
                cls: 'legacyHistory',
                margin: '30 0 30 145',
                hidden: !isLegacyRec
            }
        ],
        listeners: {
            afterrender: function (thisTab, eOpts) {
                //only need to load the lookup store once.
            },
            activate: function (thisTab, eOpts) {
                //load tab data
                //Get the application record, if available, and load into form
                //load the data onto the Core tab nested form
                NPS.Common.Utils.showLoadMask();
                var tab = this;
                NPS.DataStore.Models.Reference.Profile.History.load(id, {
                    success: function (data) {
                        NPS.Common.Utils.hideLoadMask();
                        const isLegacy = data.data.IsLegacy;
                        tab.down('#LastEditedItemId').setValue(data.data.LastEdited);
                        tab.down('#LastEditedByItemId').setValue(data.data.LastEditedBy);
                        tab.down('#CreatedItemId').setValue(data.data.Created);
                        tab.down('#CreatedByItemId').setValue(data.data.CreatedBy);
                        tab.down('#ExtractedFileNameItemId').setValue(data.data.ExtractedFileName);
                        tab.down('#ExtractedItemId').setValue(data.data.Extracted);
                        tab.down('#ExtractedByItemId').setValue(data.data.ExtractedBy);
                        //data comes from EntityFramework not AJax call to ProfileHistoryModel
                        tab.down('#FirstActivatedByItemId').setValue(pageModel.FirstActivatedBy);
                        tab.down('#FirstActivationDateItemId').setValue(pageModel.FirstActivationDate); 

                        if (isLegacy === true) {
                            tab.down('#IsLegacyItemId').setValue('This is a legacy record and has not been reviewed for quality since the original 2010 data migration.');
                        }
                    },
                    error: function (xhr, status, error) {
                        NPS.Common.Utils.hideLoadMask();
                        NPS.AjaxErrorHandler.forJQuery('There was an error while retrieving history data for the Reference.', xhr, status, error, null);
                    },
                    failure: function () {
                        NPS.Common.Utils.hideLoadMask();
                        Ext.Msg.alert('Error', 'There was an error while retrieving history data for the Reference.&nbsp;');
                    }
                });
            }
        }
    });
    return theTab;
}
