/** * Project: AMDA-NG * Name: EpnTapUI.js * @class amdaUI.EpnTapUI * @extends Ext.tab.Panel * @author Nathanael JOURDANE * 24/10/2016: file creation */ 'use strict' Ext.require(['Ext.grid.plugin.BufferedRenderer']) /** `productTypesStore`: An ExtJS Store containing the list of the different data product types defined on all granules, on all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron script). This list is used to fill the `productTypeCB` combo box, which is initilized in `EpnTapModule` at the panel creation. - `id`: the data product type IDs, according to the EPN-TAP specification (see https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=1148225); - `name`: the data product name, according to the EPN-TAP specification (ibid). These IDs and names are hard-defined in the JSon file `generic_data/EpnTapData/dataproduct_types.json`. Notes: - if a granule contains a data product type which is not conform to the EPN-TAP definition (ibid), it is not displayed in this store and an information message is displayed on the JavaScript console during the panel creation. - if a data product type is not present in any of the granules from the EPN-TAP services, it is not present in this store. */ Ext.create('Ext.data.Store', { storeId: 'productTypesStore', autoLoad: true, fields: ['id', 'name', 'desc'], data: [ {'id': 'all', 'name': '--All--', 'desc': 'Select all produt types.'}, {'id': 'clear', 'name': '--Clear--', 'desc': 'Clear the selection.'}, {'id': 'im', 'name': 'Image', 'desc': '2D series of values depending on 2 spatial axes, with measured parameters.'}, {'id': 'ma', 'name': 'Map', 'desc': '2D series of values depending on 2 spatial axes, with derived parameters.'}, {'id': 'sp', 'name': 'Spectrum', 'desc': '1D series of values depending on a spectral axis (or Frequency, Energy, Mass,...).'}, {'id': 'ds', 'name': 'Dynamic spectrum', 'desc': '2D series of values depending on time and on a spectral axis (Frequency, Energy, Mass,...), FoV is homogeneous.'}, {'id': 'sc', 'name': 'Spectral cube', 'desc': '3D series of values depending on 2 spatial axes and on a spectral axis (Frequency, Energy, Mass,..).'}, {'id': 'pr', 'name': 'Profile', 'desc': '1D series of values depending on a spatial axis.'}, {'id': 'vo', 'name': 'Volume', 'desc': '3D series of values depending on 3 spatial axes (spatial coordinates or tabulated values in a volumic grid).'}, {'id': 'mo', 'name': 'Movie', 'desc': '3D series of values depending on 2 spatial axes and on time.'}, // {'id': 'cu', 'name': 'Cube', 'desc': '.'}, {'id': 'ts', 'name': 'Time series', 'desc': '1D series of values depending on time.'}, {'id': 'ca', 'name': 'Catalogue', 'desc': '1D list of elements.'}, {'id': 'ci', 'name': 'Catalogue item', 'desc': '0D list of elements.'} ] }) /** `targetNamesStore`: An ExtJS Store containing the list of the different target names defined on all granules, on all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron script), which match with the selected data product and target class. This list is used to fill the `targetNameCB` combo box, which is updated by `EpnTapModule` each time a new target class (or, by transitivity, product type) is selected. - `id`: the target name in lowercase, with the underscore between each word; - `name`: the target name, capitalized with spaces between each word (done `EpnTapModule.prettify()`). */ Ext.create('Ext.data.Store', { storeId: 'targetNamesStore', fields: ['id', 'text', 'name', 'type', 'parent', 'aliases'], proxy: { type: 'ajax', url: 'php/epntap.php', extraParams: { action: 'resolver' } }, errorDisplayed: false, listeners: { load: function (store, records, successful) { if (!successful && !store.errorDisplayed) { Ext.Msg.alert('Error', 'Can not load results from the resolver. Please enter target names manually.') store.errorDisplayed = true } } } }) /** `servicesStore`: An ExtJS Store containing the list of the EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron script), which contains at least one granule matching with the granules filter (the selected data product type, target class and target name). This list is used to fill the `servicesGrid` table, which is updated by `EpnTapModule` each time a new target name (or, by transitivity, target class or product type) is selected. - `id`: the database name of the service, according to the `table_name` column from the `rr.res_table` in the registry database; - `nbResults`: the number of granules matching with the granules filter for this service; - `shortName`: the service short name, according to the `short_name` column from the `rr.resource` table in the registry database; - `title`: the service title, according to the `res_title` column from the `rr.resource` table in the registry database; - `accessURL`: the service access URL, according to the `access_url` column from the `rr.interface` table in the registry database. */ Ext.create('Ext.data.Store', { storeId: 'servicesStore', autoLoad: true, tMin: null, tMax: null, fields: [ {name: 'id', type: 'string'}, {name: 'short_name', type: 'string'}, {name: 'res_title', type: 'string'}, {name: 'ivoid', type: 'string'}, {name: 'access_url', type: 'string'}, {name: 'table_name', type: 'string'}, {name: 'content_type', type: 'string'}, {name: 'creator_seq', type: 'string'}, {name: 'content_level', type: 'string'}, {name: 'reference_url', type: 'string'}, {name: 'created', type: 'date', dateFormat: 'c'}, {name: 'updated', type: 'date', dateFormat: 'c'}, {name: 'nb_results', type: 'integer'}, {name: 'info', type: 'string'}, {name: 'time_min', type: 'string'}, {name: 'time_max', type: 'string'} ], proxy: { type: 'ajax', url: 'php/epntap.php', extraParams: {action: 'getServices'} }, sorters: [ {property: 'nb_results', direction: 'DESC'}, {property: 'short_name', direction: 'ASC'} ], listeners: { // beforeload: function(s, operation) { console.log(operation); }, load: function (store, records, successful) { if (!successful) { store.errorMessage = 'Can not get epntap services from registries.' } } } }) /** `granulesStore`: An ExtJS Store containing the list of granules of the selected service (on `servicesGrid`), which match with the granules filter (the selected data product type, target class and target name). This list is used to fill the `granulesGrid` table, which is updated by `EpnTapModule` each time a new service is selected. - `num`: the line number, according to the order of the query response and the current page (see `currentPageLb`); - `dataproduct_type`: the dataproduct_type EPN-TAP parameter, as defined in https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. - `target_name`: the target_name EPN-TAP parameter (ibid); - `time_min`: the time_min EPN-TAP parameter (ibid); - `time_max`: the time_max EPN-TAP parameter (ibid); - `access_format`: the access_format EPN-TAP parameter (ibid); - `granule_uid`: the granule_uid EPN-TAP parameter (ibid); - `access_estsize`: the access_estsize EPN-TAP parameter (ibid); - `access_url`: the access_url EPN-TAP parameter (ibid); - `thumbnail_url`: the thumbnail_url EPN-TAP parameter (ibid). */ // TODO: Add granules filter (see http://docs.sencha.com/extjs/4.0.7/#!/example/grid-filtering/grid-filter-local.html) Ext.define('GranulesModel', { extend: 'Ext.data.Model' // columns are created dynamically }) Ext.create('Ext.data.Store', { storeId: 'granulesStore', model: 'GranulesModel', buffered: true, autoload: false, pageSize: 500, leadingBufferZone: 0, proxy: { type: 'ajax', url: 'php/epntap.php', reader: {type: 'json', root: 'data'}, simpleSortMode: true }, listeners: { 'beforeprefetch': function (store) { const service = Ext.data.StoreManager.lookup('servicesStore').getById(store.selectedService).data store.getProxy().extraParams = { 'action': 'getGranules', 'url': service['access_url'], 'tableName': service['table_name'], 'targetNames': Ext.getCmp('epnTapTargetNameCB').rawValue, 'productTypes': Ext.getCmp('epnTapProductTypeCB').value.join(';'), 'timeMin': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStartTime(), 'd/m/Y H:i:s'), 'timeMax': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStopTime(), 'd/m/Y H:i:s'), 'nbRes': service['nb_results'] } }, // 'prefetch': function(store, records, successful, operation) { // console.log('(prefetch) operation ' + (successful ? 'success' : 'failed') + ': ', operation) // console.log(operation.params) // console.log(Ext.decode(operation.response.responseText)) // }, 'metachange': function (store, meta) { if (meta.metaHash !== store.metaHash) { Ext.getCmp('epnTapGranulesGrid').reconfigure(store, meta.columns) store.metaHash = meta.metaHash } } } }) /** Error are not displayed here, use try/catch each time it's necessary. */ Ext.define('App.util.Format', { override: 'Ext.util.Format', // Utils 'prettify': function (data) { return data.charAt(0).toUpperCase() + data.replace(/_/g, ' ').substr(1).toLowerCase() }, 'sanitizeData': function (data) { // noinspection ES6ConvertVarToLetConst for (var dKey in data) { if (data.hasOwnProperty(dKey) && typeof data[dKey] === 'string' && data[dKey] !== '') { data[dKey] = data[dKey].replace(/'/g, ''').replace(/"/g, '"') } } return data }, 'url': function (data) { const urlPattern = new RegExp('^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator return urlPattern.test(data) ? data : null }, 'cell': function (content, tooltip, tooltipTitle) { const ttTitle = tooltipTitle ? " data-qtitle='" + tooltipTitle + "'" : '' const ttAttr = tooltip === '' ? '' : "' data-qtip='" + (tooltip || content || 'No value.') + "'" return '
' + sData.info + '
' : '' const timeInfo = sData['time_min'] !== '-' || sData['time_min'] !== '-' ? 'Time period: from ' + sData['time_min'] + ' to ' + sData['time_min'] + '
' : '' const colums = ['short_name', 'res_title', 'ivoid', 'access_url', 'table_name', 'content_type', 'creator_seq', 'content_level', 'reference_url', 'created', 'updated'] // noinspection ES6ConvertVarToLetConst var details = '' // noinspection ES6ConvertVarToLetConst for (var cKey in colums) { if (colums.hasOwnProperty(cKey) && sData[colums[cKey]] !== '') { const val = colums[cKey] === 'content_level' ? sData[colums[cKey]].replace(/#/g, ', ') : sData[colums[cKey]] details += '' + mimetypeDict[data] + '
' : '' + data + '') } }) /** `EpnTapUI`: The view of the AMDA EPN-TAP module, allowing the user to query and display granules information from EPN-TAP services. Note: The controller part of this module is defined in `js/app/controller/EpnTapModule`. */ Ext.define('amdaUI.EpnTapUI', { extend: 'Ext.panel.Panel', alias: 'widget.panelEpnTap', requires: ['amdaUI.IntervalUI'], /** Method constructor, which basically call the `init()` method to create the EpnTap panel. */ constructor: function (config) { this.init(config) this.superclass.constructor.apply(this, arguments) }, /** Create all the EpnTapPanel UI elements, and apply the AMDA module `config` (which includes the created items). When the panel is correctly rendered, the panel triggers `EpnTapModule.onWindowLoaded()`. Note: All the UI elements creation are defined as functions in this init method and not as methods in order to make them private (ie. to avoid `EpnTapUI.createServicesGrid();`, which doesn't make sense). */ init: function (config) { const myConf = { id: 'epntapTab', tabConfig: { title: 'EPN-TAP {name}' } }, listeners: { render: function (cb) { Ext.ToolTip({target: cb.getEl(), html: '