From b185823cc64cf068ca684591a59cee41f65bc534 Mon Sep 17 00:00:00 2001 From: Nathanael Jourdane <nathanael.jourdane@irap.omp.eu> Date: Wed, 1 Feb 2017 17:48:01 +0100 Subject: [PATCH] Use IntervalUI module, code refactoring, add comments on .js files. --- js/app/AmdaApp.js | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------ js/app/controllers/EpnTapModule.js | 450 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- js/app/views/EpnTapUI.js | 857 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- js/app/views/IntervalUI.js | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------- php/classes/EpnTapMgr.php | 4 ++-- 5 files changed, 986 insertions(+), 691 deletions(-) diff --git a/js/app/AmdaApp.js b/js/app/AmdaApp.js index 02b5543..baa841c 100755 --- a/js/app/AmdaApp.js +++ b/js/app/AmdaApp.js @@ -1,24 +1,24 @@ -/** +/** * Project : AMDA-NG4 * Name : AmdaApp.js - * @class amdaApp.AmdaApp + * @class amdaApp.AmdaApp * @extends Ext.ux.desktop.App * @brief Main class defining Amda Desktop and its Modules * @author Ext JS Library 4.0 Copyright(c) 2006-2011 Sencha Inc. licensing@sencha.com */ - + Ext.define('amdaApp.AmdaApp', { extend: 'Ext.ux.desktop.App', - requires: [ + requires: [ 'Ext.window.MessageBox', 'Ext.ux.desktop.ShortcutModel', 'amdaUI.SampToolBarUI', 'amdaDesktop.DynamicModule', 'MyDesktop.Settings' ], - + dynamicModules: { visu : { id : 'visu-win', @@ -26,7 +26,7 @@ Ext.define('amdaApp.AmdaApp', { title : 'Visualization', source : 'amdaDesktop.VisuModule', useLauncher : true - }, + }, statistics : { id : 'statistics-win', icon : 'icon-statistics', @@ -97,22 +97,22 @@ Ext.define('amdaApp.AmdaApp', { source : 'amdaDesktop.InteropModule', useLauncher : true }, - epntap : { - id : 'epntap-win', - icon : 'icon-epntap', - title : 'EPN-TAP data', - source : 'amdaDesktop.EpnTapModule', - useLauncher : true - }, + epntap : { + id : 'epntap-win', + icon : 'icon-epntap', + title : 'EPN-TAP data', + source : 'amdaDesktop.EpnTapModule', + useLauncher : true + }, info : { id : 'info-win', icon : 'icon-information', title : 'About AMDA', source : 'amdaDesktop.AboutModule', useLauncher : false - }, + }, explorer : { - id : 'explorer-win', + id : 'explorer-win', icon : 'icon-elements', title : 'Workspace Explorer', source : 'amdaDesktop.ExplorerModule', @@ -162,13 +162,13 @@ Ext.define('amdaApp.AmdaApp', { useLauncher : false } }, - -// IDs of Modules working with parameters; used in Alias Node + +// IDs of Modules working with parameters; used in Alias Node paramModulesID : ['plot-win', 'param-win', 'search-win', 'down-win'], - + // Important system constants MAX_UPLOADED_FILE_SIZE : 30000000, // 30MB - + listeners : { scope : this, beforeunload : function () @@ -179,19 +179,19 @@ Ext.define('amdaApp.AmdaApp', { interopModule.forceSampDisconnect(); return true; }, - ready : function () + ready : function () { //AKKA - Clean user WS AmdaAction.cleanUserWS(function(res,e){},this); } }, - + init: function() { // custom logic before getXYZ methods get called... this.callParent(); - // now ready... + // now ready... //override createWindow method of desktop Ext.override(Ext.ux.desktop.Desktop, { createWindow: function (config, cls) { @@ -206,16 +206,16 @@ Ext.define('amdaApp.AmdaApp', { w.resizer.widthIncrement = me.xTickSize; w.resizer.heightIncrement = me.yTickSize; } - + if (w.y < 0) w.el.setY(0); - + if (w.x + w.width > me.el.getWidth()) w.el.setX(me.el.getWidth()-w.width); - + if (w.y + w.height > me.el.getHeight()) w.el.setY((me.el.getHeight()-w.height > 0) ? me.el.getHeight()-w.height : 0); - + }, single: true }); @@ -224,12 +224,12 @@ Ext.define('amdaApp.AmdaApp', { }); }, -//create InfoBox +//create InfoBox infoMsg : function(msg) { Ext.Msg.show({ title: 'AMDA Info', cls: 'infoMsg', - msg: msg, + msg: msg, modal: false, autoScroll: true, resizable: true, @@ -237,35 +237,35 @@ Ext.define('amdaApp.AmdaApp', { buttons: Ext.Msg.OK }); }, - -//create WarningBox + +//create WarningBox warningMsg : function(msg) { Ext.Msg.show({ title: 'Attention', - msg: msg, + msg: msg, icon: Ext.Msg.WARNING, buttons: Ext.Msg.OK }); }, - -//create ErrorBox + +//create ErrorBox errorMsg : function(msg) { Ext.Msg.show({ title: 'Failure', - msg: msg, + msg: msg, icon: Ext.Msg.ERROR, buttons: Ext.Msg.OK }); - }, - + }, + getModules : function(){ var allModules = []; - + //Add dynamic modules Ext.Object.each(this.dynamicModules, function(key, def) { allModules.push(new amdaDesktop.DynamicModule(def.id, def.icon, def.title, def.source, def.useLauncher)); }); - + return allModules; }, @@ -284,9 +284,9 @@ Ext.define('amdaApp.AmdaApp', { shortcuts: Ext.create('Ext.data.Store', { model: 'Ext.ux.desktop.ShortcutModel', - data: [ - { name: 'Help', iconCls: 'help', module: 'help-win' }, - { name: 'Create/Modify parameter', iconCls: 'edit', module: 'param-win' }, + data: [ + { name: 'Help', iconCls: 'help', module: 'help-win' }, + { name: 'Create/Modify parameter', iconCls: 'edit', module: 'param-win' }, { name: 'Plot data', iconCls: 'plot', module: 'plot-win'}, { name: 'Data mining', iconCls: 'search', module: 'search-win'}, { name: 'Statistics', iconCls: 'statistics', module: 'statistics-win'}, @@ -296,8 +296,8 @@ Ext.define('amdaApp.AmdaApp', { { name: 'TimeTables operations', iconCls: 'operations', module: 'ttsOpe-win' }, { name: 'Manage catalogs', iconCls: 'catalog', module: 'catalog-win'}, { name: 'Visualize catalogs', iconCls: 'visu_catalog', module: 'visu-win'}, - { name: 'EPN-TAP', iconCls: 'epntap', module: 'epntap-win' }, - { name: 'Interoperability', iconCls: 'interop', module: 'interop-win' } + { name: 'Interoperability', iconCls: 'interop', module: 'interop-win' }, + { name: 'EPN-TAP', iconCls: 'epntap', module: 'epntap-win' } ] }), @@ -310,7 +310,7 @@ Ext.define('amdaApp.AmdaApp', { getStartConfig : function() { var me = this, ret = me.callParent(); return Ext.apply(ret, { - title: sessionID, + title: sessionID, iconCls: 'icon-user', height: 270, toolConfig: { @@ -322,7 +322,7 @@ Ext.define('amdaApp.AmdaApp', { handler: me.onSettings, scope: me }, - '-', + '-', /* { text : 'Manage Workspaces', iconCls : 'icon-manage-ws', @@ -372,9 +372,9 @@ Ext.define('amdaApp.AmdaApp', { this.getLoadedModule(moduleId.replace('-tool', ''), true, function(module) { module.createWindow(); }); - + }, - scope : this + scope : this }, { text : 'Help', iconCls : 'icon-help', @@ -407,24 +407,24 @@ Ext.define('amdaApp.AmdaApp', { }, '-', { text : 'Logout', iconCls : 'logout', - scope : this, + scope : this, handler : me.onLogout } ] } }); }, - + getModuleDefinition: function(id) { return this.getModule(id); }, - + getLoadedModule: function(id, forceLoad, onReady) { var moduleDef = this.getModuleDefinition(id); if (!moduleDef) return null; if (!moduleDef.isReady()) - { + { if (forceLoad) { //loadMask.show(); @@ -440,14 +440,14 @@ Ext.define('amdaApp.AmdaApp', { onReady(moduleDef.get()); return moduleDef.get(); }, - + getTaskbarConfig: function () { var ret = this.callParent(); return Ext.apply(ret, { quickStart: [], trayItems: [ { - name: this.dynamicModules.feedback.title, iconCls: 'icon-feedback', + name: this.dynamicModules.feedback.title, iconCls: 'icon-feedback', tooltip: { text: this.dynamicModules.feedback.title, align: 'bl-tl' }, overflowText: this.dynamicModules.feedback.title, iconCls: this.dynamicModules.feedback.icon, @@ -460,7 +460,7 @@ Ext.define('amdaApp.AmdaApp', { } }, { - name: this.dynamicModules.info.title, iconCls: 'icon-information', + name: this.dynamicModules.info.title, iconCls: 'icon-information', tooltip: { text: this.dynamicModules.info.title, align: 'bl-tl' }, overflowText: this.dynamicModules.info.title, iconCls: this.dynamicModules.info.icon, @@ -476,7 +476,7 @@ Ext.define('amdaApp.AmdaApp', { name: 'Logout', iconCls : 'logout', tooltip: { text: 'Logout', align: 'bl-tl' }, overflowText: 'Logout', - scope : this, + scope : this, handler : this.onLogout }, '-', @@ -486,23 +486,23 @@ Ext.define('amdaApp.AmdaApp', { }, onLogout: function (obj,e) { - e.stopEvent(); - // var interopModule = this.getModule(amdaDesktop.InteropModule.id); - if (isGuest) { - this.guestLogout(); + e.stopEvent(); + // var interopModule = this.getModule(amdaDesktop.InteropModule.id); + if (isGuest) { + this.guestLogout(); } else { this.saveSessionState(); - } + } }, - + onGetUserInfo : function (result, e){ var t = e.getTransaction(); - if (e.status) - { + if (e.status) + { if (result && result.success) { - // SUCCESS + // SUCCESS var msg = '<b>Login :</b> '+result['login']+'<br/>'; msg += ('<b>Last Name :</b> '+result['name']+'<br/>'); msg += ('<b>First Name :</b> '+result['first_name']+'<br/>'); @@ -510,7 +510,7 @@ Ext.define('amdaApp.AmdaApp', { msg += ('<b>Email :</b> '+result['email']+'<br/>'); msg += ('<b>Registration date :</b> '+result['date']+'<br/>'); msg += ('<b>Receive Newsletter :</b> '+(result['news'] == "1"?"true":"false")+'<br/>'); - + this.infoMsg(msg); } else @@ -523,46 +523,46 @@ Ext.define('amdaApp.AmdaApp', { { // FAILURE this.errorMsg('Cannot get user info : '+e.message); - } + } }, - - + + guestLogout: function() { Ext.Msg.show({ - title : 'Logout', + title : 'Logout', msg :'Your guest workspace is to be deleted. Continue logout ?', buttons : Ext.Msg.YESNO, - iconCls : 'logout', + iconCls : 'logout', fn : function(btn) { if (btn == 'yes') { AmdaAction.logout(true, function(){ sessionID = ''; - window.location.href ='index.html'; - }); - } + window.location.href ='index.html'; + }); + } } - }); + }); }, - + forceLogout: function() { // myDesktopApp.warningMsg('Your guest session is finished'); AmdaAction.logout(true, function(){ sessionID = ''; - window.location.href ='index.html'; - }); + window.location.href ='index.html'; + }); }, - + saveSessionState : function() { var me = this; Ext.Msg.show({ - title : 'Logout', + title : 'Logout', msg :'Do you want to keep current windows sizes and locations<br/> for the next sessions?', buttons : Ext.Msg.YESNOCANCEL, - iconCls : 'logout', + iconCls : 'logout', fn : function(btn) { if (btn == 'yes') { @@ -577,8 +577,8 @@ Ext.define('amdaApp.AmdaApp', { window.location.href ='index.html'; } }); - Ext.state.Manager.getProvider().saveState(); - AmdaAction.logout(); + Ext.state.Manager.getProvider().saveState(); + AmdaAction.logout(); } else if (btn == 'no') { @@ -590,9 +590,9 @@ Ext.define('amdaApp.AmdaApp', { window.location.href ='index.html'; } }); - Ext.state.Manager.getProvider().set(me.desktop.id+'_wallpaper',me.desktop.getWallpaper()); - Ext.state.Manager.getProvider().saveLastTime(); - AmdaAction.logout(); + Ext.state.Manager.getProvider().set(me.desktop.id+'_wallpaper',me.desktop.getWallpaper()); + Ext.state.Manager.getProvider().saveLastTime(); + AmdaAction.logout(); } } }); @@ -604,7 +604,7 @@ Ext.define('amdaApp.AmdaApp', { }); dlg.show(); }, - + /** * initialization at the start of AMDA-NG webApplication */ @@ -612,64 +612,63 @@ Ext.define('amdaApp.AmdaApp', { moduleIds = new Ext.util.MixedCollection(); // Adding Workspace Explorer Id moduleIds.add(this.dynamicModules.explorer.id); - + moduleIds.each(function(item) { this.getLoadedModule(item, true, function (module) { module.createWindow(); }); }, this); - + if (freeSpace < diskQuota / 20) { - myDesktopApp.warningMsg('Think of cleaning up your work space.<br/>Only ' + + myDesktopApp.warningMsg('Think of cleaning up your work space.<br/>Only ' + Math.round(freeSpace/1024/1024)+ 'MB of '+Math.round(diskQuota/1024/1024) + 'MB rests'); } - + if (isFirstVisit && !isGuest) { - if (isSpecialInfo) { + if (isSpecialInfo) { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) { module.createWindow(isSpecialInfo, 'Welcome to AMDA', true); }); } else { - + myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.help.id, true, function(module) { module.createWindow(); }); } - } + } else { if (isSpecialInfo && !isNewInfo) { - myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) { + myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) { module.createWindow(isSpecialInfo, 'Welcome to AMDA', true); }); } } - + if (isNewInfo) { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) { - // module.createWindow('releaseNotes.' + AMDAVERSION, 'New Release V'+ AMDAVERSION); + // module.createWindow('releaseNotes.' + AMDAVERSION, 'New Release V'+ AMDAVERSION); module.createWindow(news, 'Amda Latest News'); }); } - + if (isGuest) { myDesktopApp.warningMsg("Welcome to Guest Session<br/>Guest session lasts for "+ guestSessionDuration/3600+" h maximum<br/>"+ "For extended use time and functionalities (saved sessions)<br/> please register at amda@irap.omp.eu"); Ext.Function.defer(myDesktopApp.warningMsg,(guestSessionDuration-300)*1000, this, ["Your session will be closed in 5 min!"]); - Ext.Function.defer(myDesktopApp.forceLogout, guestSessionDuration*1000); + Ext.Function.defer(myDesktopApp.forceLogout, guestSessionDuration*1000); } - - + + this.desktop.taskbar.tray.width = 130; this.desktop.taskbar.insert(4,new amdaUI.SampToolBarUI({id : 'samptb', onSwitchConnect : function () { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.interop.id, true, function(module) { module.switchSampConnect(); }); - + }})); } }); - diff --git a/js/app/controllers/EpnTapModule.js b/js/app/controllers/EpnTapModule.js index 04f61d4..e8ea920 100644 --- a/js/app/controllers/EpnTapModule.js +++ b/js/app/controllers/EpnTapModule.js @@ -7,7 +7,7 @@ * @author Nathanael Jourdane */ -// Load text with Ajax synchronously: takes path to file and optional MIME type +// 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); @@ -24,47 +24,9 @@ function loadTextFileAjaxSync(filePath, mimeType) { } } -function prettify(name) { - return name.charAt(0).toUpperCase() + name.replace(/_/g, ' ').substr(1).toLowerCase(); -} - -function allPrettify(name) { - return 'All ' + (name[name.length-1] == 's' ? name : name + 's').replace(/_/g, ' ').toLowerCase(); -} - -function isLatest(newStrDate, oldStrDate) { - if (newStrDate === null) { - return false; - } - if (oldStrDate === null) { - return true; - } - - var newDate = newStrDate.split('/'); - var oldDate = oldStrDate.split('/'); - - if(newDate[2]>oldDate[2]) { - return true; - } else if(newDate[2]<oldDate[2]) { - return false; - } - if(newDate[1]>oldDate[1]) { - return true; - } else if(newDate[1]<oldDate[1]) { - return false; - } - if(newDate[0]>oldDate[0]) { - return true; - } else { - return false; - } -} - Ext.define('amdaDesktop.EpnTapModule', { extend: 'amdaDesktop.AmdaModule', - - // requires: ['amdaUI.EpnTapUI', 'amdaReader.EpnTapReader'], requires: ['amdaUI.EpnTapUI'], contentId : 'EpnTapUI', @@ -81,8 +43,13 @@ Ext.define('amdaDesktop.EpnTapModule', { width : 1000, height: 550, - /** @class Module initialisation. */ + /** + 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')); @@ -101,36 +68,93 @@ Ext.define('amdaDesktop.EpnTapModule', { }; }, + /** + 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(); + }, + + /** + Compare two dates formated as dd/mm/yyyy and return: + - `true` if `newStrDate` is more recent than `oldStrDate`, or if `oldStrDate` is null; + - `false` if `oldStrDate` is more recent than `newStrDate`, or if `newStrDate` is null; + - `null` if a date is not well formed. + */ + isLatest: function(newStrDate, oldStrDate) { + if (newStrDate === null) { + return false; + } + if (oldStrDate === null) { + return true; + } + + var newDate = newStrDate.split('/'); + var oldDate = oldStrDate.split('/'); + + if(newDate[2]>oldDate[2]) { + return true; + } else if(newDate[2]<oldDate[2]) { + return false; + } + if(newDate[1]>oldDate[1]) { + return true; + } else if(newDate[1]<oldDate[1]) { + return false; + } + if(newDate[0]>oldDate[0]) { + return true; + } else { + return false; + } + return null; + }, + + /**************************** + *** 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() { - // UI elements - this.dataProdutTypeCB = Ext.getCmp('productTypeCB'); - this.targetClassCB = Ext.getCmp('targetClassCB'); - this.targetNameCB = Ext.getCmp('targetNameCB'); - this.startTimeDF = Ext.getCmp('startTimeDF'); - this.stopTimeDF = Ext.getCmp('stopTimeDF'); - - this.servicesGrid = Ext.getCmp('servicesGrid'); - this.granulesGrid = Ext.getCmp('granulesGrid'); - - this.rowsPerPageNf = Ext.getCmp('rowsPerPageNf'); - this.currentPageLb = Ext.getCmp('currentPageLb'); - this.totalPagesLb = Ext.getCmp('totalPagesLb'); - - this.previousBtn = Ext.getCmp('previousPageBtn'); - this.nextBtn = Ext.getCmp('nextPageBtn'); - this.firstBtn = Ext.getCmp('firstPageBtn'); - this.lastBtn = Ext.getCmp('lastPageBtn'); - - this.dataProdutTypeCB.getStore().removeAll(); - this.dataProdutTypeCB.getStore().add({'id': 'all', 'name': 'All data product types'}); + + 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.timeSelector.setInterval(new Date(), new Date()); // TODO: use min/max dates + + 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.dataProdutTypeCB.getStore().add({'id': productTypeId, 'name': prettify(this.productTypeDict[productTypeId])}); + this.productTypeCB.getStore().add({'id': productTypeId, 'name': this.prettify(this.productTypeDict[productTypeId])}); } else { console.log('Unknown data product type "' + productTypeId + '"'); } } - this.dataProdutTypeCB.select('all'); + this.productTypeCB.select('all'); this.targetClassCB.getStore().removeAll(); this.targetClassCB.getStore().add({'id': 'all', 'name': 'All target names'}); @@ -145,26 +169,28 @@ Ext.define('amdaDesktop.EpnTapModule', { this.updateServices(); }, - // *** form events *** - + /** + 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.dataProdutTypeCB.value == 'all') { + if (this.productTypeCB.value == 'all') { this.targetClassCB.disable(); } else { - var targetClasses = this.metadata[this.dataProdutTypeCB.value]; + var targetClasses = this.metadata[this.productTypeCB.value]; if (Object.keys(targetClasses).length == 1) { - this.targetClassCB.getStore().add({'id': Object.keys(targetClasses)[0], 'name': prettify(Object.keys(targetClasses)[0])}); + 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': allPrettify(this.productTypeDict[this.dataProdutTypeCB.value])}); + 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': prettify(targetClassId)}); + this.targetClassCB.getStore().add({'id': targetClassId, 'name': this.prettify(targetClassId)}); } this.targetClassCB.select('all'); this.targetClassCB.enable(); @@ -175,6 +201,10 @@ Ext.define('amdaDesktop.EpnTapModule', { 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(); @@ -183,16 +213,16 @@ Ext.define('amdaDesktop.EpnTapModule', { this.targetNameCB.select('all'); this.targetNameCB.disable(); } else { - var targetNames = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value]; + 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': prettify(Object.keys(targetNames)[0])}); + 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': allPrettify(this.targetClassCB.value)}); + this.targetNameCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.targetClassCB.value)}); for (var targetNameId in targetNames) { - this.targetNameCB.getStore().add({'id': targetNameId, 'name': prettify(targetNameId)}); + this.targetNameCB.getStore().add({'id': targetNameId, 'name': this.prettify(targetNameId)}); } this.targetNameCB.select('all'); this.targetNameCB.enable(); @@ -201,89 +231,112 @@ Ext.define('amdaDesktop.EpnTapModule', { 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.rowsPerPageNf.value); + console.log("rows per page: " + this.productTypeCB); }, - // *** Buttons events *** + /********************* + *** 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() { - this.wait(); - this.currentPageLb.setText('1'); - - this.nextBtn.setDisabled(false); - this.lastBtn.setDisabled(false); - this.firstBtn.setDisabled(true); - this.previousBtn.setDisabled(true); - + var newPageNumber = 1; + var limit = Number(this.rowsPerPageNf.value); + var offset = 0; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; - var limit = this.rowsPerPageNf.value; - var offset = '0'; + 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() { - this.wait(); var newPageNumber = Number(this.currentPageLb.text) - 1; - this.currentPageLb.setText('' + newPageNumber); - - this.nextBtn.setDisabled(false); - this.lastBtn.setDisabled(false); - if (this.currentPageLb.text === '1') { - this.previousBtn.setDisabled(true); - this.firstBtn.setDisabled(true); - } - + var limit = Number(this.rowsPerPageNf.value); + var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; - var limit = this.rowsPerPageNf.value; - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value); + 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() { - this.wait(); var newPageNumber = Number(this.currentPageLb.text) + 1; - this.currentPageLb.setText('' + newPageNumber); - - this.previousBtn.setDisabled(false); - this.firstBtn.setDisabled(false); - if (this.currentPageLb.text === this.totalPagesLb.text) { - this.nextBtn.setDisabled(true); - this.lastBtn.setDisabled(true); - } - + var limit = Number(this.rowsPerPageNf.value); + var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; - var limit = this.rowsPerPageNf.value; - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value); + 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() { - this.wait(); - var newPageNumber = this.totalPagesLb.text; - this.currentPageLb.setText('' + newPageNumber); - - this.previousBtn.setDisabled(false); - this.firstBtn.setDisabled(false); - this.nextBtn.setDisabled(true); - this.lastBtn.setDisabled(true); - + var newPageNumber = Number(this.totalPagesLb.text); + var limit = Number(this.rowsPerPageNf.value); + var offset = (newPageNumber-1) * limit; var selectedServiceURL = this.services[this.selectedServiceId]['accessurl']; - var limit = this.rowsPerPageNf.value; - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value); + 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); }, - // *** Grid click events *** + /******************* + *** 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; @@ -302,64 +355,32 @@ Ext.define('amdaDesktop.EpnTapModule', { } } this.filter = Array( - this.dataProdutTypeCB.value !== 'all' ? this.dataProdutTypeCB.value : null, // product type + this.productTypeCB.value !== 'all' ? this.productTypeCB.value : null, // product type this.targetNameCB.value !== 'all' ? this.targetNameCB.value : null, // target name - Ext.getCmp('startTimeDF').getRawValue() !== '' ? Ext.getCmp('startTimeDF').getRawValue() : null, // start time - Ext.getCmp('stopTimeDF').getRawValue() !== '' ? Ext.getCmp('stopTimeDF').getRawValue() : null // stop time + this.timeSelector.getStartTime() !== '' ? this.timeSelector.getStartTime() : null, // start time + this.timeSelector.getStopTime() !== '' ? this.timeSelector.getStopTime() : null // stop time ); - var limit = this.rowsPerPageNf.value; AmdaAction.epnTapGetNbRows(selectedServiceId, selectedServiceURL, this.filter, this.updateNbRows); - AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, limit, 0, this.fillGranules); + AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, this.rowsPerPageNf.value, 0, this.fillGranules); }, - onGranuleSelected: function() { - // console.log('selected granule: ' + granule.targetName); + /** + 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 *** - - updateNbRows: function(nb_results) { - /* /!\ Can not get `this`. */ - var totalPages = '' + Math.ceil(Number(nb_results) / Ext.getCmp('rowsPerPageNf').value); - - Ext.getCmp('currentPageLb').setText('1'); - Ext.getCmp('totalPagesLb').setText(totalPages); - Ext.getCmp('previousPageBtn').setDisabled(true); - Ext.getCmp('firstPageBtn').setDisabled(true); - if (totalPages === '1') { - Ext.getCmp('nextPageBtn').setDisabled(true); - Ext.getCmp('lastPageBtn').setDisabled(true); - } else { - Ext.getCmp('nextPageBtn').setDisabled(false); - Ext.getCmp('lastPageBtn').setDisabled(false); - } - }, - - fillGranules: function(granules) { - /* /!\ Can not get `this`. */ - - if (granules == null) { - console.log("There is no granules to add."); - } else { - try { - console.log('Added granules:', granules); - Ext.getCmp('granulesGrid').getStore().removeAll(); - Ext.getCmp('granulesGrid').getStore().add(granules); - } catch( e ) { - console.log('Can not add granules: ' + e); - } - } - Ext.getCmp('servicesGrid').setDisabled(false); - Ext.getCmp('servicesGrid').getEl().setStyle('cursor', 'default'); // CSS is correctly changed but without visible result. - }, - - wait: function() { - this.servicesGrid.getEl().setStyle('cursor', 'wait'); - this.servicesGrid.setDisabled(true); // CSS is correctly changed but without visible result. - }, + /********************** + *** Other functions *** + **********************/ - updateServices: function() { + /** + 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(); @@ -368,9 +389,12 @@ Ext.define('amdaDesktop.EpnTapModule', { var timeMaxArr = 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(this.dataProdutTypeCB.value === 'all') { + if(productType === 'all') { for (var dpt in this.metadata) { for (var tc in this.metadata[dpt]) { for (tn in this.metadata[dpt][tc]) { @@ -378,10 +402,10 @@ Ext.define('amdaDesktop.EpnTapModule', { service = this.metadata[dpt][tc][tn][serv]; timeMinArr = service[1].split('/'); filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0); - if (isLatest(service[1], timeMin)) { + if (this.isLatest(service[1], timeMin)) { timeMin = service[1]; } - if (isLatest(service[2], timeMax)) { + if (this.isLatest(service[2], timeMax)) { timeMax = service[2]; } } @@ -389,41 +413,41 @@ Ext.define('amdaDesktop.EpnTapModule', { } } } else if (this.targetClassCB.value === 'all') { - for (var tc in this.metadata[this.dataProdutTypeCB.value]) { - for (tn in this.metadata[this.dataProdutTypeCB.value][tc]) { - for (serv in this.metadata[this.dataProdutTypeCB.value][tc][tn]) { - service = this.metadata[this.dataProdutTypeCB.value][tc][tn][serv]; + 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); - if (isLatest(service[1], timeMin)) { + if (this.isLatest(service[1], timeMin)) { timeMin = service[1]; } - if (isLatest(service[2], timeMax)) { + if (this.isLatest(service[2], timeMax)) { timeMax = service[2]; } } } } } else if (this.targetNameCB.value === 'all') { - for (tn in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value]) { - for (serv in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][tn]) { - service = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][tn][serv]; + 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); - if (isLatest(service[1], timeMin)) { + if (this.isLatest(service[1], timeMin)) { timeMin = service[1]; } - if (isLatest(service[2], timeMax)) { + if (this.isLatest(service[2], timeMax)) { timeMax = service[2]; } } } } else { - for (serv in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][this.targetNameCB.value]) { - service = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][this.targetNameCB.value][serv]; + 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); - if (isLatest(service[1], timeMin)) { + if (this.isLatest(service[1], timeMin)) { timeMin = service[1]; } - if (isLatest(service[2], timeMax)) { + if (this.isLatest(service[2], timeMax)) { timeMax = service[2]; } } @@ -443,6 +467,54 @@ Ext.define('amdaDesktop.EpnTapModule', { 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('1'); + Ext.getCmp('epnTapTotalPagesLb').setText(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. } }); diff --git a/js/app/views/EpnTapUI.js b/js/app/views/EpnTapUI.js index 1f47ae8..b6cf074 100644 --- a/js/app/views/EpnTapUI.js +++ b/js/app/views/EpnTapUI.js @@ -3,379 +3,590 @@ * Name: EpnTapUI.js * @class amdaUI.EpnTapUI * @extends Ext.tab.Panel - * @brief client for EPN-TAP services (View) * @author Nathanael JOURDANE * 24/10/2016: file creation */ - Ext.create('Ext.data.Store', { +// TODO: Déplacer les stores dans un fichier séparé dans js.stores ! + +/** +`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', fields: ['id', 'name'] }); +/** +`targetClassesStore`: An ExtJS Store containing the list of the different target classes 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 type. + +This list is used to fill the `targetClassCB` combo box, which is updated by `EpnTapModule` each time a new product type +is selected. + +- `id`: the target class in lowercase, with the underscore between each word; +- `name`: the target class, capitalized with spaces between each word (done `EpnTapModule.prettify()`). +*/ Ext.create('Ext.data.Store', { storeId:'targetClassesStore', fields: ['id', 'name'] }); +/** +`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', 'name'] }); +/** +`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', fields: ['id', 'nbResults', 'shortName', 'title', 'accessURL'] }); +/** +`granulesStore`: An ExtJS Store containing the list of granules of the selected service (on `servicesGrid`), which match +with tge 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). +*/ Ext.create('Ext.data.Store', { storeId:'granulesStore', fields:['num', 'dataproduct_type', 'target_name', 'time_min', 'time_max', 'access_format', 'granule_uid', 'access_estsize', 'access_url', 'thumbnail_url'] }); +/** +`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.container.Container', + extend: 'Ext.panel.Panel', alias: 'widget.panelEpnTap', + requires: ['amdaUI.IntervalUI'], - txtRender: function(val) { - return '<p style="white-space: normal;">' + val + '</p>'; - }, - linkRender: function(val) { - return '<a href="' + val + '">data</a>'; - }, - imgRender: function(val) { - return '<img width="40px height="40px" src="' + val + '">'; - }, - dptRender: function(val) { - var productTypeDict = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id).productTypeDict; - return (val in productTypeDict) ? '<p style="white-space: normal;">' + productTypeDict[val] + '</p>' : '<em>' + val + '</em>'; - }, - formatRender: function(val) { - var mimetypeDict = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id).mimetypeDict; - return (val in mimetypeDict) ? mimetypeDict[val] : '<em style="white-space: normal;">' + val + '</em>'; - }, - sizeRender: function(val) { - var size = parseInt(val); - if (isNaN(size)) { - return ''; - } else if (size >= 1024*1024) { - return (size/(1024*1024)).toPrecision(3) + 'Go'; - } else if (size >= 1024) { - return (size/1024).toPrecision(3) + 'Mo'; - } else { - return size + 'Ko'; - } - }, - + /** + Method constructor, which basically call the `init()` method to create the EpnTap panel. + */ constructor: function(config) { this.init(config); this.callParent(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) { + var mod = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id); + + /************ + *** Grids *** + ************/ + + /** + Create `epnTapServicesGrid`, an ExtJS grid containing the EPN-TAP services matching with the filter form + (`serviceFilterPanel`). + + For each service, this grid displays: + - the service name; + - the number of granules matching with the filter. + + Other informations are available through the tooltip `serviceTooltip`. + + A click on a service triggers `EpnTapModule.onServiceSelected()`, which basically fills `GranulesGrid` by the + service granules. + */ + var createServicesGrid = function() { + return new Ext.grid.Panel({ + id: 'epnTapServicesGrid', + title: 'Services', + store: Ext.data.StoreManager.lookup('servicesStore'), + flex: 1, + columns: [ + {text: 'Name', dataIndex: 'id', flex: 3}, + {text: 'Results', dataIndex: 'nbResults', flex: 2} + ], + renderer: function(value, metadata,record) { + return getExpandableImage(value, metadata,record); + }, + listeners: { + 'cellclick': function(grid, td, cellIndex, record) { mod.onServiceSelected(record.data['id']); } + }, + renderTo: Ext.getBody() + }); + }; - var epnTapModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id); - - // *** Grids *** - - this.servicesGrid = new Ext.grid.Panel({ - id: 'servicesGrid', - title: 'Services', - store: Ext.data.StoreManager.lookup('servicesStore'), - flex: 1, - columns: [ - {text: 'Name', dataIndex: 'id', flex: 3}, - {text: 'Results', dataIndex: 'nbResults', flex: 2} - ], - renderer: function(value, metadata,record) { - return getExpandableImage(value, metadata,record); - }, - listeners: { - 'cellclick': function(grid, td, cellIndex, record) { - epnTapModule.onServiceSelected(record.data['id']); - } - }, - renderTo: Ext.getBody() - }); - - this.serviceTooltip = new Ext.tip.ToolTip({ - id: 'serviceTooltip', - target: Ext.getCmp('servicesGrid').getView().el, - delegate: Ext.getCmp('servicesGrid').getView().itemSelector, - trackMouse: true, - listeners: { - beforeshow: function updateTipBody(tooltip) { - var service = Ext.getCmp('servicesGrid').getView().getRecord(tooltip.triggerElement); - var ttContent = '<h3>' + service.get('shortName') + '</h3>'; - ttContent += '<p>' + service.get('title') + '</p>'; - ttContent += '<p>' + service.get('accessURL') + '</p>'; - tooltip.update(ttContent); - } - }, - renderTo: Ext.getBody() - }); - - this.granulesGrid = new Ext.grid.Panel({ - id: 'granulesGrid', - title: 'Granules', - store: Ext.data.StoreManager.lookup('granulesStore'), - flex: 5, - columns: [ - { text: 'Num', dataIndex: 'num', flex: 1, renderer: this.txtRender }, - { text: 'Type', dataIndex: 'dataproduct_type', flex: 2, renderer: this.dptRender }, - { text: 'Target', dataIndex: 'target_name', flex: 2, renderer: this.txtRender }, - { text: 'Time min', dataIndex: 'time_min', flex: 2, renderer: this.txtRender }, - { text: 'Time max', dataIndex: 'time_max', flex: 2, renderer: this.txtRender }, - { text: 'Format', dataIndex: 'access_format', flex: 2, renderer: this.formatRender }, - { text: 'uid', dataIndex: 'granule_uid', flex: 2, renderer: this.txtRender }, - { text: 'Size', dataIndex: 'access_estsize', flex: 1, renderer: this.sizeRender }, - { text: 'URL', dataIndex: 'access_url', flex: 1, renderer: this.linkRender }, - { text: 'Thumb.', dataIndex: 'thumbnail_url', flex: 1, renderer: this.imgRender} - ], - listeners: { - 'cellclick': function(grid, td, cellIndex, record) { - epnTapModule.onGranuleSelected(record.data['id']); - } - }, - renderTo: Ext.getBody() - }); - - this.granuleTooltip = new Ext.tip.ToolTip({ - id: 'granuleTooltip', - target: Ext.getCmp('granulesGrid').getView().el, - delegate: Ext.getCmp('granulesGrid').getView().itemSelector, - trackMouse: true, - listeners: { - beforeshow: function updateTipBody(tooltip) { - var thumb = Ext.getCmp('granulesGrid').getView().getRecord(tooltip.triggerElement).get('thumbnail_url'); - tooltip.update('<img src="' + thumb + '">'); - } - }, - renderTo: Ext.getBody() - }); - - // *** Service filter elements, left part *** - - this.productTypeCB = new Ext.form.field.ComboBox({ - id: 'productTypeCB', - fieldLabel: 'Product type', - store: Ext.data.StoreManager.lookup('productTypesStore'), - queryMode: 'local', - displayField: 'name', - valueField: 'id', - name: 'productType', - editable: false, - listeners: { - 'select': function(combo) { - epnTapModule.onProductTypeCBChanged(combo.value); + /** + Create `epnTapServiceTooltip`, an ExtJS tooltip for the `servicesGrid` rows, in order to display additional + information for each service, such as: + - short name; + - title; + - access URL. + */ + var createServiceTooltip = function() { + return new Ext.tip.ToolTip({ + id: 'epnTapServiceTooltip', + target: Ext.getCmp('epnTapServicesGrid').getView().el, + delegate: Ext.getCmp('epnTapServicesGrid').getView().itemSelector, + trackMouse: true, + listeners: { + beforeshow: function updateTipBody(tooltip) { + var service = Ext.getCmp('servicesGrid').getView().getRecord(tooltip.triggerElement); + var ttContent = '<h3>' + service.get('shortName') + '</h3>'; + ttContent += '<p>' + service.get('title') + '</p>'; + ttContent += '<p>' + service.get('accessURL') + '</p>'; + tooltip.update(ttContent); + } + }, + renderTo: Ext.getBody() + }); + }; + + /** + Create `epnTapGranulesGrid`, an ExtJS grid containing the granules of the selected service in + `epnTapServiceGrid`. + + For each granule, this grid displays: + - the row number; + - the dataproduct type; + - the target name; + - the min and max times; + - the format; + - the UID (granule identifier); + - the estimated size; + - the URL; + - the thumbnail. + + For more information about these parameters, see https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. + Each of these information are displayed in a specific rendering to improve user experience. + Other informations are available through the tooltip `granuleTooltip`. + + A click on a granule triggers `EpnTapModule.onGranuleSelected()`. + */ + var createGranulesGrid = function() { + var txtRender = function(val) { + return '<p style="white-space: normal;">' + val + '</p>'; + }; + var linkRender = function(val) { + return '<a href="' + val + '">data</a>'; + }; + var imgRender = function(val) { + return '<img width="40px height="40px" src="' + val + '">'; + }; + var dptRender = function(val) { + return (val in mod.productTypeDict) ? '<p style="white-space: normal;">' + mod.productTypeDict[val] + '</p>' : '<em>' + val + '</em>'; + }; + var formatRender = function(val) { + return (val in mod.mimetypeDict) ? mod.mimetypeDict[val] : '<em style="white-space: normal;">' + val + '</em>'; + }; + var sizeRender = function(val) { + var size = parseInt(val); + if (isNaN(size)) { + return ''; + } else if (size >= 1024*1024) { + return (size/(1024*1024)).toPrecision(3) + 'Go'; + } else if (size >= 1024) { + return (size/1024).toPrecision(3) + 'Mo'; + } else { + return size + 'Ko'; } - } - }); - - this.targetClassCB = new Ext.form.field.ComboBox({ - id: 'targetClassCB', - fieldLabel: 'Target class', - store: Ext.data.StoreManager.lookup('targetClassesStore'), - queryMode: 'local', - displayField: 'name', - valueField: 'id', - name: 'targetClass', - editable: false, - listeners: { - 'select': function(combo) { - epnTapModule.onTargetClassCBChanged(combo.value); + }; + + return new Ext.grid.Panel({ + id: 'epnTapGranulesGrid', + title: 'Granules', + store: Ext.data.StoreManager.lookup('granulesStore'), + flex: 5, + columns: [ + { text: 'Num', dataIndex: 'num', flex: 1, renderer: txtRender }, + { text: 'Type', dataIndex: 'dataproduct_type', flex: 2, renderer: dptRender }, + { text: 'Target', dataIndex: 'target_name', flex: 2, renderer: txtRender }, + { text: 'Time min', dataIndex: 'time_min', flex: 2, renderer: txtRender }, + { text: 'Time max', dataIndex: 'time_max', flex: 2, renderer: txtRender }, + { text: 'Format', dataIndex: 'access_format', flex: 2, renderer: formatRender }, + { text: 'uid', dataIndex: 'granule_uid', flex: 2, renderer: txtRender }, + { text: 'Size', dataIndex: 'access_estsize', flex: 1, renderer: sizeRender }, + { text: 'URL', dataIndex: 'access_url', flex: 1, renderer: linkRender }, + { text: 'Thumb.', dataIndex: 'thumbnail_url', flex: 1, renderer: imgRender} + ], + listeners: { + 'cellclick': function(grid, td, cellIndex, record) { mod.onGranuleSelected(record.data['id']); } + }, + renderTo: Ext.getBody() + }); + }; + + /** + Create `epnTapGranuleTooltip`, an ExtJS tooltip for the `granulesGrid` rows, in order to display additional + information for each granule which is currently only the granule thumbnail, in full size. + */ + var createGranuleTooltip = function() { + return new Ext.tip.ToolTip({ + id: 'epnTapGranuleTooltip', + target: Ext.getCmp('granulesGrid').getView().el, + delegate: Ext.getCmp('granulesGrid').getView().itemSelector, + trackMouse: true, + listeners: { + beforeshow: function updateTipBody(tooltip) { + var thumb = Ext.getCmp('granulesGrid').getView().getRecord(tooltip.triggerElement).get('thumbnail_url'); + tooltip.update('<img src="' + thumb + '">'); + } + }, + renderTo: Ext.getBody() + }); + }; + + /** + Create `epnTapGridsPanel`, an ExtJS Panel, containing `epnTapServicesGrid` and `epnTapGranulesGrid`. + + After the rendering of the grids, it triggers `epnTapModule.onWindowLoaded()`, which basically fill + `epnTapServicesGrid` for the first time. + */ + var createGridsPanel = function() { + var self = this; + return new Ext.panel.Panel({ + id: 'epnTapGridsPanel', + region: 'center', + height: 350, + layout: { type: 'hbox', pack: 'start', align: 'stretch' }, + items: [ + createServicesGrid(), + createGranulesGrid() + ], + listeners: { + afterrender: function() { mod.onWindowLoaded(self); } } - } - }); - - this.targetNameCB = new Ext.form.field.ComboBox({ - id: 'targetNameCB', - fieldLabel: 'Target name', - store: Ext.data.StoreManager.lookup('targetNamesStore'), - queryMode: 'local', - displayField: 'name', - valueField: 'id', - name: 'targetName', - triggerAction: 'all', - typeAhead: true, - mode: 'remote', - minChars: 2, - forceSelection: true, - listeners: { - 'select': function(combo) { - epnTapModule.onTargetNameCBChanged(combo.value); + }); + }; + + /*************************** + *** Service filter panel *** + ***************************/ + + /** + Create `epnTapProductTypeCB`, an ExtJS ComboBox, containing a list of product types as defined in + `epnTapProductTypesStore`, which is initilized by `EpnTapModule`. + + The selection of a produt type triggers `EpnTapModule.onProductTypeCBChanged()`, which basically update + `epnTapTargetClassCB` and `epnTapGranulesGrid`. + */ + var createProductTypeCB = function() { + return new Ext.form.field.ComboBox({ + id: 'epnTapProductTypeCB', + fieldLabel: 'Product type', + store: Ext.data.StoreManager.lookup('productTypesStore'), + queryMode: 'local', + displayField: 'name', + valueField: 'id', + name: 'productType', + editable: false, + listeners: { + 'select': function(combo) { mod.onProductTypeCBChanged(combo.value); } } - } - }); - - // *** Service filter elements, right part *** - - this.startTimeDF = new Ext.form.field.Date({ - id: 'startTimeDF', - fieldLabel: 'Start time', - format: 'Y/m/d H:i:s', - width: 100, - listeners: { - 'select': function(dateField, value) { - epnTapModule.onTargetNameCBChanged(value); + }); + }; + + /** + Create `epnTapTargetClassCB`, an ExtJS ComboBox, containing a list of target classes corresponding to the + selected product type, as defined in `targetClassesStore`, which is initilized by `EpnTapModule`. + + The selection of a target class triggers the `EpnTapModule.onTargetClassCBChanged()`, which basically updates + `targetNameCB` and `granulesGrid`. + */ + var createTargetClassCB = function() { + return new Ext.form.field.ComboBox({ + id: 'epnTapTargetClassCB', + fieldLabel: 'Target class', + store: Ext.data.StoreManager.lookup('targetClassesStore'), + queryMode: 'local', + displayField: 'name', + valueField: 'id', + name: 'targetClass', + editable: false, + listeners: { + 'select': function(combo) { mod.onTargetClassCBChanged(combo.value); } } - } - }); - - this.stopTimeDF = new Ext.form.field.Date({ - id: 'stopTimeDF', - fieldLabel: 'Stop time', - format: 'Y/m/d H:i:s', - width: 100, - listeners: { - 'select': function(dateField, value) { - epnTapModule.onTargetNameCBChanged(value); + }); + }; + + /** + Create `epnTapTargetNameCB`, an ExtJS ComboBox, containing a list of target names corresponding to the selected + target class, as defined in `targetNamesStore`, which is initilized by `EpnTapModule`. + + The selection of a target name triggers `EpnTapModule.onTargetNameCBChanged()`, which basically updates + `granulesGrid`. + */ + var createTargetNameCB = function() { + return new Ext.form.field.ComboBox({ + id: 'epnTapTargetNameCB', + fieldLabel: 'Target name', + store: Ext.data.StoreManager.lookup('targetNamesStore'), + queryMode: 'local', + displayField: 'name', + valueField: 'id', + name: 'targetName', + triggerAction: 'all', + typeAhead: true, + mode: 'remote', + minChars: 2, + forceSelection: true, + listeners: { + 'select': function(combo) { mod.onTargetNameCBChanged(combo.value); } } - } - }); - - this.durationPanel = new Ext.panel.Panel({ - id: 'duration', - layout: { type: 'hbox', pack: 'start', align: 'stretch' }, - border: false, - defaults: { width: 60, margin: '0, 5, 0, 5', xtype: 'numberfield', listeners: { 'select': function(elmt) { epnTapModule.onDurationChanged(elmt); } } }, - items: [{ - id: 'days', - margin: '0, 5, 0, 0', - fieldLabel: 'Duration', - emptyText: 'Days', - width: 170 - }, { - id: 'hours', - emptyText: 'Hours' - }, { - id: 'minutes', - emptyText: 'Min.' - }, { - id: 'seconds', - emptyText: 'Sec.' - }] - }); - - this.rowPerPageNf = new Ext.form.field.Number({ - id: 'rowsPerPageNf', - fieldLabel: 'Rows per page', - margin: '4 0 4 0', - width: 160, - height: 20, - value: 20, - minValue: 1, - maxValue: 2000, - listeners: { - 'change': function(rowPerPageNf, newValue) { - epnTapModule.onRowsPerPageChanged(newValue); + }); + }; + + /** + Create `epnTapServiceFilterPanel`, an ExtJS Panel containing two containers: + - the left container, containing the combo boxes (for product type, target class and target name) + and the navigation panel; + - the right container, containing the time selector. + */ + var createServiceFilterPanel = function() { + return new Ext.panel.Panel({ + id: 'epnTapServiceFilterPanel', + region : 'north', + layout: { type: 'hbox', pack: 'start', align: 'stretch' }, + defaults: { margin: 5 }, + items: [{ // Left part + xtype : 'container', + layout: 'form', + flex: 2, + items: [ + createProductTypeCB(), + createTargetClassCB(), + createTargetNameCB(), + { + xtype: 'panel', + layout: { type: 'hbox', pack: 'start', align: 'stretch' }, + border: false, + items: [ + createRowPerPageNf(), + createNavigationPanel() + ] + } + ] + }, { // Right part + xtype : 'form', + id: 'epnTapIntervalSelector', + layout: 'form', + border: 'false', + flex: 2, + items: [ + createTimeSelector() + ] + }] + }); + }; + + /** + Create `epnTapTimeSelector`, an IntervalUI object, allowing the user to select a time interval (by filling two + dates and/or a duration). + + See `js/app/views/IntervalUI.js` for more information about this component. + */ + var createTimeSelector = function() { + return Ext.create('amdaUI.IntervalUI', { + id: 'epnTapTimeSelector' + }); + }; + + /*********************** + *** Navigation panel *** + ***********************/ + + /** + Create `epnTapRowsPerPageNf`, a ExtJS Number field, allowing the user to select the number of rows to display in + `epnTapGranulesGrid`. + + When a new number is entered, it triggers `EpnTapModule.onRowsPerPageChanged()`. + */ + var createRowPerPageNf = function() { + return new Ext.form.field.Number({ + id: 'epnTapRowsPerPageNf', + fieldLabel: 'Rows per page', + margin: '4 0 4 0', + width: 160, + height: 20, + value: 20, + minValue: 1, + maxValue: 2000, + listeners: { + 'change': function(rowPerPageNf, newValue) { mod.onRowsPerPageChanged(newValue); } } - } - }); - - // *** Panels *** - - this.pageSelectPanel = new Ext.panel.Panel({ - id: 'pageSelect', - border: false, - margin: '2 0 2 50', - defaults: { margin: '0 5 0 5', width: 20, xtype: 'button', disabled: true}, - items: [{ - xtype: 'label', - text: 'Page:' - }, { - id: 'firstPageBtn', - text: '|<', - tooltip: 'First page', - handler: function() { epnTapModule.onFirstPageBtnClicked(); } - }, { - id: 'previousPageBtn', - text: '<', - tooltip: 'Previous page', - handler: function() { epnTapModule.onPreviousPageBtnClicked(); } - }, { - xtype: 'label', - id: 'currentPageLb', - tooltip: 'Current page', - text: '-' - }, { - xtype: 'label', - text: '/' - }, { - xtype: 'label', - id: 'totalPagesLb', - tooltip: 'Total pages', - text: '-' - }, { - id: 'nextPageBtn', - text: '>', - tooltip: 'Next page', - handler: function() { epnTapModule.onNextPageBtnClicked(); } - }, { - id: 'lastPageBtn', - text: '>|', - tooltip: 'Last page', - handler: function() { epnTapModule.onLastPageBtnClicked(); } - }] - }); - - this.granulePagePanel = new Ext.panel.Panel({ - id: 'granulePagePanel', - layout: { type: 'hbox', pack: 'start', align: 'stretch' }, - border: false, - items: [this.rowPerPageNf, this.pageSelectPanel] - }); - - this.serviceFilterPanel = new Ext.panel.Panel({ - id: 'serviceFilterPanel', - region : 'north', - layout: { type: 'hbox', pack: 'start', align: 'stretch' }, - defaults: { margin: 5 }, - items: [{ // Left part - xtype : 'container', - layout: 'form', - flex: 2, - items: [ this.productTypeCB, this.targetClassCB, this.targetNameCB ] - }, { // Right part - xtype : 'container', - layout: 'form', - flex: 2, - items: [ this.startTimeDF, this.stopTimeDF, this.durationPanel, this.granulePagePanel ] - }] - }); - - this.gridsPanel = new Ext.panel.Panel({ - id: 'gridsPanel', - region: 'center', - height: 350, - layout: { type: 'hbox', pack: 'start', align: 'stretch' }, - items: [ this.servicesGrid, this.granulesGrid ], - listeners: { - afterrender: function() { epnTapModule.onWindowLoaded(); } - } - }); - - this.infoPanel = new Ext.panel.Panel({ - id: 'infoPanel', - region: 'south', - title: 'Information', - collapsible: true, - flex: 0, - height: 100, - autoHide: false, - bodyStyle: 'padding: 5px', - iconCls: 'icon-information', - loader: { autoLoad: true, url: helpDir + 'epnTapHOWTO' } - }); + }); + }; + + /** + Create `epnTapNavigationPanel`, an ExtJSPanel containing several elements in order to navigate through the + different pages of the query result. If the number of results is highter than the `epnTapRowsPerPageNf` field + value, the result appears to be displayed in different pages. This panel is used to select and display the page + number, mainly with these following elements: + - `epnTapFirstPageBtn`: an ExtJS Button, used to come back to the first page of result, + handling `EpnTapModule.onFirstPageBtnClicked()`; + - `epnTapPreviousPageBtn`: an ExtJS Button, used to come back to the previous page of result, + handling `EpnTapModule.onPreviousPageBtnClicked()`; + - `epnTapCurrentPageLb`: an ExtJS Label, displaying the actual current page; TODO: use a Number field instead! + - `epnTapTotalPagesLb`: an ExtJS Label, displaying the total page number of results (according to the + `epnTapRowsPerPageNf` field value); + - `epnTapNextPageBtn`: an ExtJS Button, used to go to the next page of result, + handling `EpnTapModule.onNextPageBtnClicked()`; + - `epnTapLastPageBtn`: an ExtJS Button, used to come back to the last page of result, + handling `EpnTapModule.onLastPageBtnClicked()`. + + Note: Pages are not actually a "graphical filter": when the user navigate through the pages, a new query is send + to the server with the corresponding range, which improves the response time on large requests. + */ + var createNavigationPanel = function() { + return new Ext.panel.Panel({ + name: 'epnTapNavigationPanel', + border: false, + margin: '2 0 2 50', + defaults: { margin: '0 5 0 5', width: 20, xtype: 'button', disabled: true}, + items: [{ + xtype: 'label', + text: 'Page:' + }, { + id: 'epnTapFirstPageBtn', + text: '|<', + tooltip: 'First page', + handler: function() { mod.onFirstPageBtnClicked(); } + }, { + id: 'epnTapPreviousPageBtn', + text: '<', + tooltip: 'Previous page', + handler: function() { mod.onPreviousPageBtnClicked(); } + }, { + xtype: 'label', + id: 'epnTapCurrentPageLb', + tooltip: 'Current page', + text: '-' + }, { + xtype: 'label', + text: '/' + }, { + xtype: 'label', + id: 'epnTapTotalPagesLb', + tooltip: 'Total pages', + text: '-' + }, { + id: 'epnTapNextPageBtn', + text: '>', + tooltip: 'Next page', + handler: function() { mod.onNextPageBtnClicked(); } + }, { + id: 'epnTapLastPageBtn', + text: '>|', + tooltip: 'Last page', + handler: function() { mod.onLastPageBtnClicked(); } + }] + }); + }; + + /******************* + *** Other panels *** + *******************/ + + /** + Create `epnTapInfoPanel`, an ExtJS Panel used to display a brief user guide about how to use this module. + */ + var createInfoPanel = function() { + return new Ext.panel.Panel({ + id: 'epnTapInfoPanel', + region: 'south', + title: 'Information', + collapsible: true, + flex: 0, + height: 100, + autoHide: false, + bodyStyle: 'padding: 5px', + iconCls: 'icon-information', + loader: { autoLoad: true, url: helpDir + 'epnTapHOWTO' } + }); + }; + + // TODO tester ceci: + // config.title = 'EPN-TAP'; + // config.layout = 'border'; + // config.items = [ + // createServiceFilterPanel(config.targetName), + // createGridsPanel(), + // createInfoPanel() + // ]; + // Ext.apply(this, Ext.apply(arguments, config)); var myConf = { width: 1000, height: 550, layout: 'border', - items: [ this.serviceFilterPanel, this.gridsPanel, this.infoPanel ] + items: [ + createServiceFilterPanel(), + createGridsPanel(), + createInfoPanel() + ] }; - Ext.apply(this, Ext.apply(arguments, myConf)); - } }); diff --git a/js/app/views/IntervalUI.js b/js/app/views/IntervalUI.js index c716491..67163b6 100644 --- a/js/app/views/IntervalUI.js +++ b/js/app/views/IntervalUI.js @@ -6,7 +6,7 @@ * @brief common component to select interval * @author Benjamin * @version $Id: IntervalUI.js 2077 2014-02-11 11:33:36Z elena $ - * @todo Validations + * @todo Validations ***************************************************************************** * FT Id : Date : Name - Description ****************************************************************************** @@ -15,17 +15,21 @@ Ext.define('amdaUI.IntervalUI', { extend: 'Ext.container.Container', - alias: 'widget.intervalSelector', - activeField : null, - + constructor: function(config) { this.init(config); this.callParent(arguments); }, - - setInterval : function(startDate,stopDate) + + /** + Set the start and stop date, and update the duration field. + - startDate: A Extjs Date object representing the new start time. + - stopDate: A Extjs Date object representing the new stop time. + - return: None. + */ + setInterval : function(startDate, stopDate) { // get the search form var form = this.findParentByType('form').getForm(); @@ -33,44 +37,55 @@ Ext.define('amdaUI.IntervalUI', { var startField = form.findField('startDate'); // get stop field var stopField = form.findField('stopDate'); - + if (startField != null) startField.setValue(startDate); - + if (stopField != null) stopField.setValue(stopDate); - + this.updateDuration(); }, - + + /** + Get the start time field value. + - return: A Extjs Date object representing the start time. + */ getStartTime : function() { // get the search form var form = this.findParentByType('form').getForm(); // get start field var startField = form.findField('startDate'); - + return startField.getValue(); }, - + + /** + Get the stop time field value. + - return: A Extjs Date object representing the stop time. + */ getStopTime : function() { // get the search form var form = this.findParentByType('form').getForm(); // get stop field var stopField = form.findField('stopDate'); - - return stopField.getValue(); + + return stopField.getValue(); }, - - updateDuration: function() { + /* + #### Private methods from here #### + */ + + updateDuration: function() { // get the search form var form = this.findParentByType('form').getForm(); // get start value - var start = form.findField('startDate').getValue(); + var start = this.getStartTime(); // get stop value - var stop = form.findField('stopDate').getValue(); + var stop = this.getStopTime(); // if duration computable if (stop != null && start != null) { @@ -78,18 +93,17 @@ Ext.define('amdaUI.IntervalUI', { var zoneOffset = stop.getTimezoneOffset() - start.getTimezoneOffset(); // compute duration var diff = stop - start - zoneOffset*60000; - + var durationDays = Math.floor(diff/86400000); // set all duration values - form.findField('durationDay').setValue(Ext.String.leftPad(durationDays,4,'0')); + form.findField('durationDay').setValue(Ext.String.leftPad(durationDays,4,'0')); form.findField('durationHour').setValue(Ext.String.leftPad(Math.floor(diff/3600000 % 24),2,'0')); form.findField('durationMin').setValue(Ext.String.leftPad(Math.floor(diff/60000 % 60),2,'0')); form.findField('durationSec').setValue(Ext.String.leftPad(Math.floor(diff/1000 % 60),2,'0')); - - if (durationDays > 9999) { + + if (durationDays > 9999) { form.findField('durationDay').markInvalid('Maximum interval is 9999 days!'); } - } }, @@ -99,13 +113,12 @@ Ext.define('amdaUI.IntervalUI', { var form = this.findParentByType('form').getForm(); // get global validation status for duration fields return ( - form.findField('durationDay').isValid() && form.findField('durationHour').isValid() + form.findField('durationDay').isValid() && form.findField('durationHour').isValid() && form.findField('durationMin').isValid() && form.findField('durationSec').isValid() );// return true if all duration fields are Valid false otherwise }, updateStop: function() { - // get the time form var form = this.findParentByType('form').getForm(); // get duration value @@ -121,68 +134,72 @@ Ext.define('amdaUI.IntervalUI', { form.findField('stopDate').setValue(stop); }, - - onChangeStartField : function(field, newValue, oldValue) - { - if (field.isValid()) { - // get the search form - var form = this.findParentByType('form').getForm(); - // set to the stop datefield the newValue as minValue - form.findField('stopDate').setMinValue(newValue); - // if it's a user modification - if (oldValue != null && this.activeField == 'start') { - // launch the update of duration fields - this.updateDuration(); - } - } + + onChangeStartField : function(field, newValue, oldValue) + { + if (field.isValid()) { + // get the search form + var form = this.findParentByType('form').getForm(); + // set to the stop datefield the newValue as minValue + form.findField('stopDate').setMinValue(newValue); + // if it's a user modification + if (oldValue != null && this.activeField == 'start') { + // launch the update of duration fields + this.updateDuration(); + } + } }, - - onChangeStopField: function(field, newValue, oldValue){ - if (field.isValid() && oldValue != null && this.activeField == 'stop') { - // launch the update of duration fields - this.updateDuration(); - } + + onChangeStopField: function(field, newValue, oldValue) { + if (field.isValid() && oldValue != null && this.activeField == 'stop') { + // launch the update of duration fields + this.updateDuration(); + } }, - + getDateField : function(fieldName,fieldText,fieldId,onChangeField) { return { layout: {type: 'hbox', align: 'middle'}, items: [ - { - xtype: 'datefield', name: fieldName, format: 'Y/m/d H:i:s', - enforceMaxLength : true, - maxLength: 19, - fieldLabel: fieldText, labelAlign: 'right', labelWidth: 60, + { + xtype: 'datefield', + name: fieldName, + format: 'Y/m/d H:i:s', + enforceMaxLength: true, + maxLength: 19, + fieldLabel: fieldText, + labelAlign: 'right', + labelWidth: 60, listeners: { change: onChangeField, focus: function(field) { this.activeField = fieldId; }, scope : this - } - } - ] + } + } + ] }; }, - + getStartField : function() { - return this.getDateField('startDate','Start Time','start',this.onChangeStartField); + return this.getDateField('startDate','Start Time','start', this.onChangeStartField); }, - + getStopField : function() { return this.getDateField('stopDate','Stop Time','stop',this.onChangeStopField); }, - + getDurationField : function() { return { layout: {type: 'hbox', align: 'middle'}, height: 45, - defaults: { - xtype: 'textfield', labelAlign: 'top', width: 30, + defaults: { + xtype: 'textfield', labelAlign: 'top', width: 30, allowBlank: false, maxLength:2, enforceMaxLength : true, hideTrigger: true, regex: /^[0-9]([0-9])*$/i, @@ -190,15 +207,15 @@ Ext.define('amdaUI.IntervalUI', { change: function(field, newValue, oldValue){ if (this.isValidDuration() && oldValue != null && this.activeField == 'duration') { // launch the update of stop datefield - this.updateStop(); - } + this.updateStop(); + } }, focus: function(field) { - this.activeField = 'duration'; - }, + this.activeField = 'duration'; + }, scope : this - } - }, + } + }, items:[ { xtype: 'displayfield', labelWidth: 60, labelAlign: 'right', width: 60, fieldLabel: '<br>Duration'}, { xtype: 'component', width: 5}, @@ -206,20 +223,19 @@ Ext.define('amdaUI.IntervalUI', { { xtype: 'component', width: 5}, { name: 'durationHour', fieldLabel: 'Hrs'}, { xtype: 'component', width: 5}, - { name: 'durationMin', fieldLabel: 'Mins'}, + { name: 'durationMin', fieldLabel: 'Mins'}, { xtype: 'component', width: 5}, { name: 'durationSec', fieldLabel: 'Secs'} ] }; }, - + init : function(config) { - var me = this; - + var myConf = { border: false, - plain: true, + plain: true, flex: 1, layout: 'anchor', defaults: { height : 30, xtype : 'container'}, @@ -230,9 +246,6 @@ Ext.define('amdaUI.IntervalUI', { me.getDurationField() ] }; - - Ext.apply (this , Ext.apply (arguments, myConf)); + Ext.apply (this , Ext.apply (arguments, myConf)); } }); - - \ No newline at end of file diff --git a/php/classes/EpnTapMgr.php b/php/classes/EpnTapMgr.php index 2dc60b0..0a41b1c 100644 --- a/php/classes/EpnTapMgr.php +++ b/php/classes/EpnTapMgr.php @@ -54,7 +54,7 @@ class EpnTapMgr { /* filter order: product type, target name, time min, time max */ public function getGranules($table_name, $access_url, $filter, $select, $limit, $offset) { - $query = "SELECT TOP $limit " . join(', ', $select) . " FROM $table_name.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]) . " OFFSET $offset"; + $query = "SELECT TOP {$limit} " . join(', ', $select) . " FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]) . " OFFSET {$offset}"; // return $query; $result = $this->request($access_url, $query); for ($i = 0 ; $i < sizeof($result) ; $i++) { @@ -67,7 +67,7 @@ class EpnTapMgr { /* filter order: product type, target name, time min, time max */ public function getNbRows($table_name, $access_url, $filter) { - $query = "SELECT COUNT(*) AS nb_rows FROM $table_name.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]); + $query = "SELECT COUNT(*) AS nb_rows FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]); // return $query; return $this->request($access_url, $query)[0]['nb_rows']; } -- libgit2 0.21.2