/** * Project : AMDA-NG * Name : EpnTapModule.js * @class amdaDesktop.EpnTapModule * @extends amdaDesktop.AmdaModule * @brief EpnTap Module controller definition * @author Nathanael Jourdane */ // Load text with Ajax synchronously: takes path to file and optional MIME type. function loadTextFileAjaxSync(filePath, mimeType) { var xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET", filePath, false); if (mimeType != null) { if (xmlhttp.overrideMimeType) { xmlhttp.overrideMimeType(mimeType); } } xmlhttp.send(); if (xmlhttp.status == 200) { return xmlhttp.responseText; } else { return null; } } Ext.define('amdaDesktop.EpnTapModule', { extend: 'amdaDesktop.AmdaModule', requires: ['amdaUI.EpnTapUI'], contentId : 'EpnTapUI', /** The alias name of the module view. */ uiType: 'panelEpnTap', /** The text displayed on the *help button* tooltip. */ helpTitle: 'Help on EPN-TAP Module', /** The name of the documentation file related to the module. */ helpFile : 'epnTapHelp', /** Window dimentions. */ width : 1000, height: 550, /** Module initialisation. */ init: function() { // TODO: Utiliser des stores pour accéder aux fichiers JS !! this.metadata = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/metadata.json', 'application/json')); this.services = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/services.json', 'application/json')); this.productTypeDict = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/dataproduct_types.json', 'application/json')); this.mimetypeDict = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/mimetypes.json', 'application/json')); this.select = Array(); this.filter = Array(); this.selectedServiceId = null; this.launcher = { text: this.title, iconCls: this.icon, handler: this.createWindow, scope: this }; }, /** Capitalize a name and replace underscores with spaces. - `name`: The string to make pretty. */ prettify: function(name) { return name.charAt(0).toUpperCase() + name.replace(/_/g, ' ').substr(1).toLowerCase(); }, /** Capitalize a name, replace underscores with spaces, and write it in a plurial form. - `name`: The string to make pretty. */ allPrettify: function(name) { return 'All ' + (name[name.length-1] == 's' ? name : name + 's').replace(/_/g, ' ').toLowerCase(); }, isDate: function(date) { if (date === null) { return false; } var dateArr = date.split('/'); if (dateArr.length != 3 || isNaN(parseInt(dateArr[0])) || isNaN(parseInt(dateArr[1])) || isNaN(parseInt(dateArr[2])) ) { return false; } return true; }, /**************************** *** Service filter events *** ****************************/ /** Trigerred after the render of `gridsPanel` (containing `servicesGrid` and `granulesGrid`). Among other things, initializes the `productType` combobox and the `servicesGrid` table. */ onWindowLoaded: function() { this.productTypeCB = Ext.getCmp('epnTapProductTypeCB'); this.targetClassCB = Ext.getCmp('epnTapTargetClassCB'); this.targetNameCB = Ext.getCmp('epnTapTargetNameCB'); this.timeSelector = Ext.getCmp('epnTapTimeSelector'); this.rowsPerPageNf = Ext.getCmp('epnTapRowsPerPageNf'); this.servicesGrid = Ext.getCmp('epnTapServicesGrid'); this.granulesGrid = Ext.getCmp('epnTapGranulesGrid'); this.currentPageLb = Ext.getCmp('epnTapCurrentPageLb'); this.totalPagesLb = Ext.getCmp('epnTapTotalPagesLb'); this.firstPageBtn = Ext.getCmp('epnTapFirstPageBtn'); this.previousPageBtn = Ext.getCmp('epnTapPreviousPageBtn'); this.nextPageBtn = Ext.getCmp('epnTapNextPageBtn'); this.lastPageBtn = Ext.getCmp('epnTapLastPageBtn'); this.productTypeCB.getStore().removeAll(); this.productTypeCB.getStore().add({'id': 'all', 'name': 'All data product types'}); for (var productTypeId in this.metadata) { if (productTypeId in this.productTypeDict) { this.productTypeCB.getStore().add({'id': productTypeId, 'name': this.prettify(this.productTypeDict[productTypeId])}); } else { console.log('Unknown data product type "' + productTypeId + '"'); } } this.productTypeCB.select('all'); this.targetClassCB.getStore().removeAll(); this.targetClassCB.getStore().add({'id': 'all', 'name': 'All target names'}); this.targetClassCB.select('all'); this.targetClassCB.disable(); this.targetNameCB.getStore().removeAll(); this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target classes'}); this.targetNameCB.select('all'); this.targetNameCB.disable(); this.updateServices(); }, /** Trigerred when a new item is selected in `productTypeCB` (see `EpnTapUI.createProductTypeCB()`). Among other things, updates the `targetClassCB` combobox and the `servicesGrid` table. */ onProductTypeCBChanged: function() { this.targetClassCB.getStore().removeAll(); this.targetNameCB.getStore().removeAll(); this.targetNameCB.disable(); if (this.productTypeCB.value == 'all') { this.targetClassCB.disable(); } else { var targetClasses = this.metadata[this.productTypeCB.value]; if (Object.keys(targetClasses).length == 1) { this.targetClassCB.getStore().add({'id': Object.keys(targetClasses)[0], 'name': this.prettify(Object.keys(targetClasses)[0])}); this.targetClassCB.disable(); this.targetClassCB.select(this.targetClassCB.getStore().getAt(0)['internalId']); } else { this.targetClassCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.productTypeDict[this.productTypeCB.value])}); for (var targetClassId in targetClasses) { this.targetClassCB.getStore().add({'id': targetClassId, 'name': this.prettify(targetClassId)}); } this.targetClassCB.select('all'); this.targetClassCB.enable(); } } this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target names'}); this.targetNameCB.select('all'); this.updateServices(); }, /** Trigerred when a new item is selected in `targetClassCB` (see `EpnTapUI.createTargetClassCB()`). Among other things, updates the `targetNameCB` combobox and the `servicesGrid` table. */ onTargetClassCBChanged: function() { this.targetNameCB.getStore().removeAll(); if (this.targetClassCB.value == 'all') { this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target names'}); this.targetNameCB.select('all'); this.targetNameCB.disable(); } else { var targetNames = this.metadata[this.productTypeCB.value][this.targetClassCB.value]; if (Object.keys(targetNames).length == 1) { this.targetNameCB.getStore().add({'id': Object.keys(targetNames)[0], 'name': this.prettify(Object.keys(targetNames)[0])}); this.targetNameCB.select(this.targetNameCB.getStore().getAt(0)['internalId']); this.targetNameCB.disable(); } else { this.targetNameCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.targetClassCB.value)}); for (var targetNameId in targetNames) { this.targetNameCB.getStore().add({'id': targetNameId, 'name': this.prettify(targetNameId)}); } this.targetNameCB.select('all'); this.targetNameCB.enable(); } } this.updateServices(); }, /** Trigerred when a new item is selected in `targetNameCB` (see `EpnTapUI.createTargetNameCB()`). Updates the `servicesGrid` table. */ onTargetNameCBChanged: function() { this.updateServices(); }, /** Trigerred when the value of `rowsPerPageNf` is updated (see `EpnTapUI.createRowsPerPageNf()`). Do nothing yet, used for debug purposes only. */ onRowsPerPageChanged: function() { console.log("rows per page: " + this.productTypeCB); }, /********************* *** Buttons events *** *********************/ /** Disable or enable the navigation buttons (see `EpnTapUI.createNavigationPanel()`). */ disableNavBtns: function(firt, previous, next, last) { Ext.getCmp('epnTapFirstPageBtn').setDisabled(firt); Ext.getCmp('epnTapPreviousPageBtn').setDisabled(previous); Ext.getCmp('epnTapNextPageBtn').setDisabled(next); Ext.getCmp('epnTapLastPageBtn').setDisabled(last); }, /** Trigerred when the `firstPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things, send a new query and fill `granulesGrid`. */ onFirstPageBtnClicked: function() { var newPageNumber = 1; var limit = Number(this.rowsPerPageNf.value); var offset = 0; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; this.wait(); this.disableNavBtns(true, true, false, false); this.currentPageLb.setText('' + newPageNumber); AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules); }, /** Trigerred when the `previousPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things, send a new query and fill `granulesGrid`. */ onPreviousPageBtnClicked: function() { var newPageNumber = Number(this.currentPageLb.text) - 1; var limit = Number(this.rowsPerPageNf.value); var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; this.wait(); this.currentPageLb.setText('' + newPageNumber); var isFirstPage = this.currentPageLb.text === '1'; this.disableNavBtns(isFirstPage, isFirstPage, false, false); AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules); }, /** Trigerred when the `nextPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things, send a new query and fill `granulesGrid`. */ onNextPageBtnClicked: function() { var newPageNumber = Number(this.currentPageLb.text) + 1; var limit = Number(this.rowsPerPageNf.value); var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; this.wait(); this.currentPageLb.setText('' + newPageNumber); var isLastPage = this.currentPageLb.text == this.totalPagesLb.text; this.disableNavBtns(false, false, isLastPage, isLastPage); AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules); }, /** Trigerred when the `lastPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things, send a new query and fill `granulesGrid`. */ onLastPageBtnClicked: function() { var newPageNumber = Number(this.totalPagesLb.text); var limit = Number(this.rowsPerPageNf.value); var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; console.log(newPageNumber, limit, offset, selectedServiceURL); this.wait(); this.currentPageLb.setText('' + newPageNumber); this.disableNavBtns(false, false, true, true); AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules); }, /******************* *** Grids events *** *******************/ /** Trigerred when a row is clicked in `servicesGrid` table (see `EpnTapUI.createServicesGrid()`). Among other things, send a new query and fill `granulesGrid`. */ onServiceSelected: function(selectedServiceId) { this.wait(); this.selectedServiceId = selectedServiceId; var selectedServiceURL = this.services[selectedServiceId]['accessurl']; this.select = Array(); if (! selectedServiceId in this.services) { throw this.selectedServiceId + ' not found in the list of services.'; return; } var columns = this.services[selectedServiceId]['columns'].split(','); for (var ic=0 ; ic<this.granulesGrid.columns.length ; ic++) { if (columns.indexOf(this.granulesGrid.columns[ic].dataIndex) != -1) { this.select.push(this.granulesGrid.columns[ic].dataIndex); } } this.filter = Array( this.productTypeCB.value !== 'all' ? this.productTypeCB.value : null, // product type this.targetNameCB.value !== 'all' ? this.targetNameCB.value : null, // target name this.timeSelector.getStartTime() !== '' ? this.timeSelector.getStartTime() : null, // start time this.timeSelector.getStopTime() !== '' ? this.timeSelector.getStopTime() : null // stop time ); AmdaAction.epnTapGetNbRows(selectedServiceId, selectedServiceURL, this.filter, this.updateNbRows); AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, this.rowsPerPageNf.value, 0, this.fillGranules); }, /** Trigerred when a row is clicked in `granulesGrid` table (see `EpnTapUI.createGranulesGrid()`). Do nothing yet, used for debug purposes only. */ onGranuleSelected: function(ui) { console.log('selected granule: ' + granule.targetName); }, /********************** *** Other functions *** **********************/ /** Update the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`. */ updateServices: function(ui) { this.servicesGrid.getStore().removeAll(); this.granulesGrid.getStore().removeAll(); var service = null; var newTimeMin = null; var newTimeMax = null; var timeMin = null; var timeMax = null; var productType = this.productTypeCB.value; var targetClass = this.targetClassCB.value; var targetName = this.targetNameCB.value; var filterDict = new Array(); if(productType === 'all') { for (var dpt in this.metadata) { for (var tc in this.metadata[dpt]) { for (tn in this.metadata[dpt][tc]) { for (serv in this.metadata[dpt][tc][tn]) { service = this.metadata[dpt][tc][tn][serv]; filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0); newTimeMin = Ext.Date.parse(service[1], 'd/m/Y'); newTimeMax = Ext.Date.parse(service[2], 'd/m/Y'); if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) { timeMin = newTimeMin; } if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) { timeMax = newTimeMax; } } } } } } else if (this.targetClassCB.value === 'all') { for (var tc in this.metadata[productType]) { for (tn in this.metadata[productType][tc]) { for (serv in this.metadata[productType][tc][tn]) { service = this.metadata[productType][tc][tn][serv]; filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0); newTimeMin = Ext.Date.parse(service[1], 'd/m/Y'); newTimeMax = Ext.Date.parse(service[2], 'd/m/Y'); if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) { timeMin = newTimeMin; } if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) { timeMax = newTimeMax; } } } } } else if (this.targetNameCB.value === 'all') { for (tn in this.metadata[productType][targetClass]) { for (serv in this.metadata[productType][targetClass][tn]) { service = this.metadata[productType][targetClass][tn][serv]; filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0); newTimeMin = Ext.Date.parse(service[1], 'd/m/Y'); newTimeMax = Ext.Date.parse(service[2], 'd/m/Y'); if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) { timeMin = newTimeMin; } if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) { timeMax = newTimeMax; } } } } else { for (serv in this.metadata[productType][targetClass][targetName]) { service = this.metadata[productType][targetClass][targetName][serv]; filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0); newTimeMin = Ext.Date.parse(service[1], 'd/m/Y'); newTimeMax = Ext.Date.parse(service[2], 'd/m/Y'); if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) { timeMin = newTimeMin; } if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) { timeMax = newTimeMax; } } } this.timeSelector.setLimits(timeMin, timeMax); this.timeSelector.setInterval(timeMin, timeMax); var filter = Object.keys(filterDict).map(function(key) { return [key, filterDict[key]]; }); filter.sort(function(first, second) { return second[1] - first[1]; }); for (var s = 0; s < filter.length; s++) { var service = this.services[filter[s][0]]; this.servicesGrid.getStore().add({'id': filter[s][0], 'nbResults': filter[s][1], 'shortName': service['shortname'], 'title': service['title'], 'accessURL': service['accessurl']}); } }, /** Callback function, called from the PHP script when the query result is received, when a service is selected. Among other things, update the `epnTapCurrentPageLb` label (see `EpnTapUI.createNavigationPanel()`). */ updateNbRows: function(nb_results) { var totalPages = Math.ceil(Number(nb_results) / Ext.getCmp('epnTapRowsPerPageNf').value); Ext.getCmp('epnTapCurrentPageLb').setText(totalPages == 0 ? '-' : '1'); Ext.getCmp('epnTapTotalPagesLb').setText(totalPages == 0 ? '-' : totalPages); Ext.getCmp('epnTapPreviousPageBtn').setDisabled(true); Ext.getCmp('epnTapFirstPageBtn').setDisabled(true); Ext.getCmp('epnTapNextPageBtn').setDisabled(totalPages <= 1); Ext.getCmp('epnTapLastPageBtn').setDisabled(totalPages <= 1); }, /** Callback function, called from the PHP script when the query result is received, when a service is selected or a navigation button is clicked. Among other things, fill the `epnTapGranulesGrid` table (see `EpnTapUI.granulesStore`). */ fillGranules: function(granules) { if (granules == null) { console.log("There is no granules to add."); } else { try { console.log('Added granules:', granules); Ext.getCmp('epnTapGranulesGrid').getStore().removeAll(); Ext.getCmp('epnTapGranulesGrid').getStore().add(granules); } catch( e ) { console.log('Can not add granules: ' + e); } } Ext.getCmp('epnTapServicesGrid').setDisabled(false); Ext.getCmp('epnTapServicesGrid').getEl().setStyle('cursor', 'default'); // CSS is correctly changed but without visible result. }, /** Called before to send a query. Set the EpnTap panel in "waiting mode", informing to the user that a request is processing. The altered elements are resetted in `fillGranules()`. */ wait: function() { this.servicesGrid.getEl().setStyle('cursor', 'wait'); this.servicesGrid.setDisabled(true); // CSS is correctly changed but without visible result. } });