Commit b185823cc64cf068ca684591a59cee41f65bc534

Authored by Nathanael Jourdane
1 parent 9cd58692

Use IntervalUI module, code refactoring, add comments on .js files.

js/app/AmdaApp.js
1   -/**
  1 +/**
2 2 * Project : AMDA-NG4
3 3 * Name : AmdaApp.js
4   - * @class amdaApp.AmdaApp
  4 + * @class amdaApp.AmdaApp
5 5 * @extends Ext.ux.desktop.App
6 6 * @brief Main class defining Amda Desktop and its Modules
7 7 * @author Ext JS Library 4.0 Copyright(c) 2006-2011 Sencha Inc. licensing@sencha.com
8 8 */
9 9  
10   -
  10 +
11 11 Ext.define('amdaApp.AmdaApp', {
12 12 extend: 'Ext.ux.desktop.App',
13 13  
14   - requires: [
  14 + requires: [
15 15 'Ext.window.MessageBox',
16 16 'Ext.ux.desktop.ShortcutModel',
17 17 'amdaUI.SampToolBarUI',
18 18 'amdaDesktop.DynamicModule',
19 19 'MyDesktop.Settings'
20 20 ],
21   -
  21 +
22 22 dynamicModules: {
23 23 visu : {
24 24 id : 'visu-win',
... ... @@ -26,7 +26,7 @@ Ext.define('amdaApp.AmdaApp', {
26 26 title : 'Visualization',
27 27 source : 'amdaDesktop.VisuModule',
28 28 useLauncher : true
29   - },
  29 + },
30 30 statistics : {
31 31 id : 'statistics-win',
32 32 icon : 'icon-statistics',
... ... @@ -97,22 +97,22 @@ Ext.define('amdaApp.AmdaApp', {
97 97 source : 'amdaDesktop.InteropModule',
98 98 useLauncher : true
99 99 },
100   - epntap : {
101   - id : 'epntap-win',
102   - icon : 'icon-epntap',
103   - title : 'EPN-TAP data',
104   - source : 'amdaDesktop.EpnTapModule',
105   - useLauncher : true
106   - },
  100 + epntap : {
  101 + id : 'epntap-win',
  102 + icon : 'icon-epntap',
  103 + title : 'EPN-TAP data',
  104 + source : 'amdaDesktop.EpnTapModule',
  105 + useLauncher : true
  106 + },
107 107 info : {
108 108 id : 'info-win',
109 109 icon : 'icon-information',
110 110 title : 'About AMDA',
111 111 source : 'amdaDesktop.AboutModule',
112 112 useLauncher : false
113   - },
  113 + },
114 114 explorer : {
115   - id : 'explorer-win',
  115 + id : 'explorer-win',
116 116 icon : 'icon-elements',
117 117 title : 'Workspace Explorer',
118 118 source : 'amdaDesktop.ExplorerModule',
... ... @@ -162,13 +162,13 @@ Ext.define('amdaApp.AmdaApp', {
162 162 useLauncher : false
163 163 }
164 164 },
165   -
166   -// IDs of Modules working with parameters; used in Alias Node
  165 +
  166 +// IDs of Modules working with parameters; used in Alias Node
167 167 paramModulesID : ['plot-win', 'param-win', 'search-win', 'down-win'],
168   -
  168 +
169 169 // Important system constants
170 170 MAX_UPLOADED_FILE_SIZE : 30000000, // 30MB
171   -
  171 +
172 172 listeners : {
173 173 scope : this,
174 174 beforeunload : function ()
... ... @@ -179,19 +179,19 @@ Ext.define('amdaApp.AmdaApp', {
179 179 interopModule.forceSampDisconnect();
180 180 return true;
181 181 },
182   - ready : function ()
  182 + ready : function ()
183 183 {
184 184 //AKKA - Clean user WS
185 185 AmdaAction.cleanUserWS(function(res,e){},this);
186 186 }
187 187 },
188   -
  188 +
189 189 init: function() {
190 190 // custom logic before getXYZ methods get called...
191 191  
192 192 this.callParent();
193 193  
194   - // now ready...
  194 + // now ready...
195 195 //override createWindow method of desktop
196 196 Ext.override(Ext.ux.desktop.Desktop, {
197 197 createWindow: function (config, cls) {
... ... @@ -206,16 +206,16 @@ Ext.define('amdaApp.AmdaApp', {
206 206 w.resizer.widthIncrement = me.xTickSize;
207 207 w.resizer.heightIncrement = me.yTickSize;
208 208 }
209   -
  209 +
210 210 if (w.y < 0)
211 211 w.el.setY(0);
212   -
  212 +
213 213 if (w.x + w.width > me.el.getWidth())
214 214 w.el.setX(me.el.getWidth()-w.width);
215   -
  215 +
216 216 if (w.y + w.height > me.el.getHeight())
217 217 w.el.setY((me.el.getHeight()-w.height > 0) ? me.el.getHeight()-w.height : 0);
218   -
  218 +
219 219 },
220 220 single: true
221 221 });
... ... @@ -224,12 +224,12 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
224 224 });
225 225  
226 226 },
227   -//create InfoBox
  227 +//create InfoBox
228 228 infoMsg : function(msg) {
229 229 Ext.Msg.show({
230 230 title: 'AMDA Info',
231 231 cls: 'infoMsg',
232   - msg: msg,
  232 + msg: msg,
233 233 modal: false,
234 234 autoScroll: true,
235 235 resizable: true,
... ... @@ -237,35 +237,35 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
237 237 buttons: Ext.Msg.OK
238 238 });
239 239 },
240   -
241   -//create WarningBox
  240 +
  241 +//create WarningBox
242 242 warningMsg : function(msg) {
243 243 Ext.Msg.show({
244 244 title: 'Attention',
245   - msg: msg,
  245 + msg: msg,
246 246 icon: Ext.Msg.WARNING,
247 247 buttons: Ext.Msg.OK
248 248 });
249 249 },
250   -
251   -//create ErrorBox
  250 +
  251 +//create ErrorBox
252 252 errorMsg : function(msg) {
253 253 Ext.Msg.show({
254 254 title: 'Failure',
255   - msg: msg,
  255 + msg: msg,
256 256 icon: Ext.Msg.ERROR,
257 257 buttons: Ext.Msg.OK
258 258 });
259   - },
260   -
  259 + },
  260 +
261 261 getModules : function(){
262 262 var allModules = [];
263   -
  263 +
264 264 //Add dynamic modules
265 265 Ext.Object.each(this.dynamicModules, function(key, def) {
266 266 allModules.push(new amdaDesktop.DynamicModule(def.id, def.icon, def.title, def.source, def.useLauncher));
267 267 });
268   -
  268 +
269 269 return allModules;
270 270 },
271 271  
... ... @@ -284,9 +284,9 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
284 284  
285 285 shortcuts: Ext.create('Ext.data.Store', {
286 286 model: 'Ext.ux.desktop.ShortcutModel',
287   - data: [
288   - { name: 'Help', iconCls: 'help', module: 'help-win' },
289   - { name: 'Create/Modify parameter', iconCls: 'edit', module: 'param-win' },
  287 + data: [
  288 + { name: 'Help', iconCls: 'help', module: 'help-win' },
  289 + { name: 'Create/Modify parameter', iconCls: 'edit', module: 'param-win' },
290 290 { name: 'Plot data', iconCls: 'plot', module: 'plot-win'},
291 291 { name: 'Data mining', iconCls: 'search', module: 'search-win'},
292 292 { name: 'Statistics', iconCls: 'statistics', module: 'statistics-win'},
... ... @@ -296,8 +296,8 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
296 296 { name: 'TimeTables operations', iconCls: 'operations', module: 'ttsOpe-win' },
297 297 { name: 'Manage catalogs', iconCls: 'catalog', module: 'catalog-win'},
298 298 { name: 'Visualize catalogs', iconCls: 'visu_catalog', module: 'visu-win'},
299   - { name: 'EPN-TAP', iconCls: 'epntap', module: 'epntap-win' },
300   - { name: 'Interoperability', iconCls: 'interop', module: 'interop-win' }
  299 + { name: 'Interoperability', iconCls: 'interop', module: 'interop-win' },
  300 + { name: 'EPN-TAP', iconCls: 'epntap', module: 'epntap-win' }
301 301 ]
302 302 }),
303 303  
... ... @@ -310,7 +310,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
310 310 getStartConfig : function() {
311 311 var me = this, ret = me.callParent();
312 312 return Ext.apply(ret, {
313   - title: sessionID,
  313 + title: sessionID,
314 314 iconCls: 'icon-user',
315 315 height: 270,
316 316 toolConfig: {
... ... @@ -322,7 +322,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
322 322 handler: me.onSettings,
323 323 scope: me
324 324 },
325   - '-',
  325 + '-',
326 326 /* {
327 327 text : 'Manage Workspaces',
328 328 iconCls : 'icon-manage-ws',
... ... @@ -372,9 +372,9 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
372 372 this.getLoadedModule(moduleId.replace('-tool', ''), true, function(module) {
373 373 module.createWindow();
374 374 });
375   -
  375 +
376 376 },
377   - scope : this
  377 + scope : this
378 378 }, {
379 379 text : 'Help',
380 380 iconCls : 'icon-help',
... ... @@ -407,24 +407,24 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
407 407 }, '-', {
408 408 text : 'Logout',
409 409 iconCls : 'logout',
410   - scope : this,
  410 + scope : this,
411 411 handler : me.onLogout
412 412 }
413 413 ]
414 414 }
415 415 });
416 416 },
417   -
  417 +
418 418 getModuleDefinition: function(id) {
419 419 return this.getModule(id);
420 420 },
421   -
  421 +
422 422 getLoadedModule: function(id, forceLoad, onReady) {
423 423 var moduleDef = this.getModuleDefinition(id);
424 424 if (!moduleDef)
425 425 return null;
426 426 if (!moduleDef.isReady())
427   - {
  427 + {
428 428 if (forceLoad)
429 429 {
430 430 //loadMask.show();
... ... @@ -440,14 +440,14 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
440 440 onReady(moduleDef.get());
441 441 return moduleDef.get();
442 442 },
443   -
  443 +
444 444 getTaskbarConfig: function () {
445 445 var ret = this.callParent();
446 446 return Ext.apply(ret, {
447 447 quickStart: [],
448 448 trayItems: [
449 449 {
450   - name: this.dynamicModules.feedback.title, iconCls: 'icon-feedback',
  450 + name: this.dynamicModules.feedback.title, iconCls: 'icon-feedback',
451 451 tooltip: { text: this.dynamicModules.feedback.title, align: 'bl-tl' },
452 452 overflowText: this.dynamicModules.feedback.title,
453 453 iconCls: this.dynamicModules.feedback.icon,
... ... @@ -460,7 +460,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
460 460 }
461 461 },
462 462 {
463   - name: this.dynamicModules.info.title, iconCls: 'icon-information',
  463 + name: this.dynamicModules.info.title, iconCls: 'icon-information',
464 464 tooltip: { text: this.dynamicModules.info.title, align: 'bl-tl' },
465 465 overflowText: this.dynamicModules.info.title,
466 466 iconCls: this.dynamicModules.info.icon,
... ... @@ -476,7 +476,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
476 476 name: 'Logout', iconCls : 'logout',
477 477 tooltip: { text: 'Logout', align: 'bl-tl' },
478 478 overflowText: 'Logout',
479   - scope : this,
  479 + scope : this,
480 480 handler : this.onLogout
481 481 },
482 482 '-',
... ... @@ -486,23 +486,23 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
486 486 },
487 487  
488 488 onLogout: function (obj,e) {
489   - e.stopEvent();
490   - // var interopModule = this.getModule(amdaDesktop.InteropModule.id);
491   - if (isGuest) {
492   - this.guestLogout();
  489 + e.stopEvent();
  490 + // var interopModule = this.getModule(amdaDesktop.InteropModule.id);
  491 + if (isGuest) {
  492 + this.guestLogout();
493 493 }
494 494 else {
495 495 this.saveSessionState();
496   - }
  496 + }
497 497 },
498   -
  498 +
499 499 onGetUserInfo : function (result, e){
500 500 var t = e.getTransaction();
501   - if (e.status)
502   - {
  501 + if (e.status)
  502 + {
503 503 if (result && result.success)
504 504 {
505   - // SUCCESS
  505 + // SUCCESS
506 506 var msg = '<b>Login :</b> '+result['login']+'<br/>';
507 507 msg += ('<b>Last Name :</b> '+result['name']+'<br/>');
508 508 msg += ('<b>First Name :</b> '+result['first_name']+'<br/>');
... ... @@ -510,7 +510,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
510 510 msg += ('<b>Email :</b> '+result['email']+'<br/>');
511 511 msg += ('<b>Registration date :</b> '+result['date']+'<br/>');
512 512 msg += ('<b>Receive Newsletter :</b> '+(result['news'] == "1"?"true":"false")+'<br/>');
513   -
  513 +
514 514 this.infoMsg(msg);
515 515 }
516 516 else
... ... @@ -523,46 +523,46 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
523 523 {
524 524 // FAILURE
525 525 this.errorMsg('Cannot get user info : '+e.message);
526   - }
  526 + }
527 527 },
528   -
529   -
  528 +
  529 +
530 530 guestLogout: function()
531 531 {
532 532 Ext.Msg.show({
533   - title : 'Logout',
  533 + title : 'Logout',
534 534 msg :'Your guest workspace is to be deleted. Continue logout ?',
535 535 buttons : Ext.Msg.YESNO,
536   - iconCls : 'logout',
  536 + iconCls : 'logout',
537 537 fn : function(btn) {
538 538 if (btn == 'yes')
539 539 {
540 540 AmdaAction.logout(true, function(){
541 541 sessionID = '';
542   - window.location.href ='index.html';
543   - });
544   - }
  542 + window.location.href ='index.html';
  543 + });
  544 + }
545 545 }
546   - });
  546 + });
547 547 },
548   -
  548 +
549 549 forceLogout: function()
550 550 {
551 551 // myDesktopApp.warningMsg('Your guest session is finished');
552 552 AmdaAction.logout(true, function(){
553 553 sessionID = '';
554   - window.location.href ='index.html';
555   - });
  554 + window.location.href ='index.html';
  555 + });
556 556 },
557   -
  557 +
558 558 saveSessionState : function()
559 559 {
560 560 var me = this;
561 561 Ext.Msg.show({
562   - title : 'Logout',
  562 + title : 'Logout',
563 563 msg :'Do you want to keep current windows sizes and locations<br/> for the next sessions?',
564 564 buttons : Ext.Msg.YESNOCANCEL,
565   - iconCls : 'logout',
  565 + iconCls : 'logout',
566 566 fn : function(btn) {
567 567 if (btn == 'yes')
568 568 {
... ... @@ -577,8 +577,8 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
577 577 window.location.href ='index.html';
578 578 }
579 579 });
580   - Ext.state.Manager.getProvider().saveState();
581   - AmdaAction.logout();
  580 + Ext.state.Manager.getProvider().saveState();
  581 + AmdaAction.logout();
582 582 }
583 583 else if (btn == 'no')
584 584 {
... ... @@ -590,9 +590,9 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
590 590 window.location.href ='index.html';
591 591 }
592 592 });
593   - Ext.state.Manager.getProvider().set(me.desktop.id+'_wallpaper',me.desktop.getWallpaper());
594   - Ext.state.Manager.getProvider().saveLastTime();
595   - AmdaAction.logout();
  593 + Ext.state.Manager.getProvider().set(me.desktop.id+'_wallpaper',me.desktop.getWallpaper());
  594 + Ext.state.Manager.getProvider().saveLastTime();
  595 + AmdaAction.logout();
596 596 }
597 597 }
598 598 });
... ... @@ -604,7 +604,7 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
604 604 });
605 605 dlg.show();
606 606 },
607   -
  607 +
608 608 /**
609 609 * initialization at the start of AMDA-NG webApplication
610 610 */
... ... @@ -612,64 +612,63 @@ Ext.define(&#39;amdaApp.AmdaApp&#39;, {
612 612 moduleIds = new Ext.util.MixedCollection();
613 613 // Adding Workspace Explorer Id
614 614 moduleIds.add(this.dynamicModules.explorer.id);
615   -
  615 +
616 616 moduleIds.each(function(item) {
617 617 this.getLoadedModule(item, true, function (module) {
618 618 module.createWindow();
619 619 });
620 620 }, this);
621   -
  621 +
622 622 if (freeSpace < diskQuota / 20) {
623   - myDesktopApp.warningMsg('Think of cleaning up your work space.<br/>Only ' +
  623 + myDesktopApp.warningMsg('Think of cleaning up your work space.<br/>Only ' +
624 624 Math.round(freeSpace/1024/1024)+ 'MB of '+Math.round(diskQuota/1024/1024) +
625 625 'MB rests');
626 626 }
627   -
  627 +
628 628 if (isFirstVisit && !isGuest) {
629   - if (isSpecialInfo) {
  629 + if (isSpecialInfo) {
630 630 myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) {
631 631 module.createWindow(isSpecialInfo, 'Welcome to AMDA', true);
632 632 });
633 633 } else {
634   -
  634 +
635 635 myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.help.id, true, function(module) {
636 636 module.createWindow();
637 637 });
638 638 }
639   - }
  639 + }
640 640 else {
641 641 if (isSpecialInfo && !isNewInfo) {
642   - myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) {
  642 + myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) {
643 643 module.createWindow(isSpecialInfo, 'Welcome to AMDA', true);
644 644 });
645 645 }
646 646 }
647   -
  647 +
648 648 if (isNewInfo) {
649 649 myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function(module) {
650   - // module.createWindow('releaseNotes.' + AMDAVERSION, 'New Release V'+ AMDAVERSION);
  650 + // module.createWindow('releaseNotes.' + AMDAVERSION, 'New Release V'+ AMDAVERSION);
651 651 module.createWindow(news, 'Amda Latest News');
652 652 });
653 653 }
654   -
  654 +
655 655 if (isGuest) {
656 656 myDesktopApp.warningMsg("Welcome to Guest Session<br/>Guest session lasts for "+
657 657 guestSessionDuration/3600+" h maximum<br/>"+
658 658 "For extended use time and functionalities (saved sessions)<br/> please register at amda@irap.omp.eu");
659 659 Ext.Function.defer(myDesktopApp.warningMsg,(guestSessionDuration-300)*1000, this, ["Your session will be closed in 5 min!"]);
660   - Ext.Function.defer(myDesktopApp.forceLogout, guestSessionDuration*1000);
  660 + Ext.Function.defer(myDesktopApp.forceLogout, guestSessionDuration*1000);
661 661 }
662 662  
663   -
664   -
  663 +
  664 +
665 665 this.desktop.taskbar.tray.width = 130;
666 666 this.desktop.taskbar.insert(4,new amdaUI.SampToolBarUI({id : 'samptb', onSwitchConnect : function ()
667 667 {
668 668 myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.interop.id, true, function(module) {
669 669 module.switchSampConnect();
670 670 });
671   -
  671 +
672 672 }}));
673 673 }
674 674 });
675   -
... ...
js/app/controllers/EpnTapModule.js
... ... @@ -7,7 +7,7 @@
7 7 * @author Nathanael Jourdane
8 8 */
9 9  
10   -// Load text with Ajax synchronously: takes path to file and optional MIME type
  10 +// Load text with Ajax synchronously: takes path to file and optional MIME type.
11 11 function loadTextFileAjaxSync(filePath, mimeType) {
12 12 var xmlhttp=new XMLHttpRequest();
13 13 xmlhttp.open("GET", filePath, false);
... ... @@ -24,47 +24,9 @@ function loadTextFileAjaxSync(filePath, mimeType) {
24 24 }
25 25 }
26 26  
27   -function prettify(name) {
28   - return name.charAt(0).toUpperCase() + name.replace(/_/g, ' ').substr(1).toLowerCase();
29   -}
30   -
31   -function allPrettify(name) {
32   - return 'All ' + (name[name.length-1] == 's' ? name : name + 's').replace(/_/g, ' ').toLowerCase();
33   -}
34   -
35   -function isLatest(newStrDate, oldStrDate) {
36   - if (newStrDate === null) {
37   - return false;
38   - }
39   - if (oldStrDate === null) {
40   - return true;
41   - }
42   -
43   - var newDate = newStrDate.split('/');
44   - var oldDate = oldStrDate.split('/');
45   -
46   - if(newDate[2]>oldDate[2]) {
47   - return true;
48   - } else if(newDate[2]<oldDate[2]) {
49   - return false;
50   - }
51   - if(newDate[1]>oldDate[1]) {
52   - return true;
53   - } else if(newDate[1]<oldDate[1]) {
54   - return false;
55   - }
56   - if(newDate[0]>oldDate[0]) {
57   - return true;
58   - } else {
59   - return false;
60   - }
61   -}
62   -
63 27 Ext.define('amdaDesktop.EpnTapModule', {
64 28  
65 29 extend: 'amdaDesktop.AmdaModule',
66   -
67   - // requires: ['amdaUI.EpnTapUI', 'amdaReader.EpnTapReader'],
68 30 requires: ['amdaUI.EpnTapUI'],
69 31 contentId : 'EpnTapUI',
70 32  
... ... @@ -81,8 +43,13 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
81 43 width : 1000,
82 44 height: 550,
83 45  
84   - /** @class Module initialisation. */
  46 + /**
  47 + Module initialisation.
  48 + */
85 49 init: function() {
  50 +
  51 + // TODO: Utiliser des stores pour accรฉder aux fichiers JS !!
  52 +
86 53 this.metadata = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/metadata.json', 'application/json'));
87 54 this.services = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/services.json', 'application/json'));
88 55 this.productTypeDict = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/dataproduct_types.json', 'application/json'));
... ... @@ -101,36 +68,93 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
101 68 };
102 69 },
103 70  
  71 + /**
  72 + Capitalize a name and replace underscores with spaces.
  73 + - `name`: The string to make pretty.
  74 + */
  75 + prettify: function(name) {
  76 + return name.charAt(0).toUpperCase() + name.replace(/_/g, ' ').substr(1).toLowerCase();
  77 + },
  78 +
  79 + /**
  80 + Capitalize a name, replace underscores with spaces, and write it in a plurial form.
  81 + - `name`: The string to make pretty.
  82 + */
  83 + allPrettify: function(name) {
  84 + return 'All ' + (name[name.length-1] == 's' ? name : name + 's').replace(/_/g, ' ').toLowerCase();
  85 + },
  86 +
  87 + /**
  88 + Compare two dates formated as dd/mm/yyyy and return:
  89 + - `true` if `newStrDate` is more recent than `oldStrDate`, or if `oldStrDate` is null;
  90 + - `false` if `oldStrDate` is more recent than `newStrDate`, or if `newStrDate` is null;
  91 + - `null` if a date is not well formed.
  92 + */
  93 + isLatest: function(newStrDate, oldStrDate) {
  94 + if (newStrDate === null) {
  95 + return false;
  96 + }
  97 + if (oldStrDate === null) {
  98 + return true;
  99 + }
  100 +
  101 + var newDate = newStrDate.split('/');
  102 + var oldDate = oldStrDate.split('/');
  103 +
  104 + if(newDate[2]>oldDate[2]) {
  105 + return true;
  106 + } else if(newDate[2]<oldDate[2]) {
  107 + return false;
  108 + }
  109 + if(newDate[1]>oldDate[1]) {
  110 + return true;
  111 + } else if(newDate[1]<oldDate[1]) {
  112 + return false;
  113 + }
  114 + if(newDate[0]>oldDate[0]) {
  115 + return true;
  116 + } else {
  117 + return false;
  118 + }
  119 + return null;
  120 + },
  121 +
  122 + /****************************
  123 + *** Service filter events ***
  124 + ****************************/
  125 +
  126 + /**
  127 + Trigerred after the render of `gridsPanel` (containing `servicesGrid` and `granulesGrid`). Among other things,
  128 + initializes the `productType` combobox and the `servicesGrid` table.
  129 + */
104 130 onWindowLoaded: function() {
105   - // UI elements
106   - this.dataProdutTypeCB = Ext.getCmp('productTypeCB');
107   - this.targetClassCB = Ext.getCmp('targetClassCB');
108   - this.targetNameCB = Ext.getCmp('targetNameCB');
109   - this.startTimeDF = Ext.getCmp('startTimeDF');
110   - this.stopTimeDF = Ext.getCmp('stopTimeDF');
111   -
112   - this.servicesGrid = Ext.getCmp('servicesGrid');
113   - this.granulesGrid = Ext.getCmp('granulesGrid');
114   -
115   - this.rowsPerPageNf = Ext.getCmp('rowsPerPageNf');
116   - this.currentPageLb = Ext.getCmp('currentPageLb');
117   - this.totalPagesLb = Ext.getCmp('totalPagesLb');
118   -
119   - this.previousBtn = Ext.getCmp('previousPageBtn');
120   - this.nextBtn = Ext.getCmp('nextPageBtn');
121   - this.firstBtn = Ext.getCmp('firstPageBtn');
122   - this.lastBtn = Ext.getCmp('lastPageBtn');
123   -
124   - this.dataProdutTypeCB.getStore().removeAll();
125   - this.dataProdutTypeCB.getStore().add({'id': 'all', 'name': 'All data product types'});
  131 +
  132 + this.productTypeCB = Ext.getCmp('epnTapProductTypeCB');
  133 + this.targetClassCB = Ext.getCmp('epnTapTargetClassCB');
  134 + this.targetNameCB = Ext.getCmp('epnTapTargetNameCB');
  135 + this.timeSelector = Ext.getCmp('epnTapTimeSelector');
  136 + this.rowsPerPageNf = Ext.getCmp('epnTapRowsPerPageNf');
  137 + this.servicesGrid = Ext.getCmp('epnTapServicesGrid');
  138 + this.granulesGrid = Ext.getCmp('epnTapGranulesGrid');
  139 + this.currentPageLb = Ext.getCmp('epnTapCurrentPageLb');
  140 + this.totalPagesLb = Ext.getCmp('epnTapTotalPagesLb');
  141 + this.firstPageBtn = Ext.getCmp('epnTapFirstPageBtn');
  142 + this.previousPageBtn = Ext.getCmp('epnTapPreviousPageBtn');
  143 + this.nextPageBtn = Ext.getCmp('epnTapNextPageBtn');
  144 + this.lastPageBtn = Ext.getCmp('epnTapLastPageBtn');
  145 +
  146 + this.timeSelector.setInterval(new Date(), new Date()); // TODO: use min/max dates
  147 +
  148 + this.productTypeCB.getStore().removeAll();
  149 + this.productTypeCB.getStore().add({'id': 'all', 'name': 'All data product types'});
126 150 for (var productTypeId in this.metadata) {
127 151 if (productTypeId in this.productTypeDict) {
128   - this.dataProdutTypeCB.getStore().add({'id': productTypeId, 'name': prettify(this.productTypeDict[productTypeId])});
  152 + this.productTypeCB.getStore().add({'id': productTypeId, 'name': this.prettify(this.productTypeDict[productTypeId])});
129 153 } else {
130 154 console.log('Unknown data product type "' + productTypeId + '"');
131 155 }
132 156 }
133   - this.dataProdutTypeCB.select('all');
  157 + this.productTypeCB.select('all');
134 158  
135 159 this.targetClassCB.getStore().removeAll();
136 160 this.targetClassCB.getStore().add({'id': 'all', 'name': 'All target names'});
... ... @@ -145,26 +169,28 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
145 169 this.updateServices();
146 170 },
147 171  
148   - // *** form events ***
149   -
  172 + /**
  173 + Trigerred when a new item is selected in `productTypeCB` (see `EpnTapUI.createProductTypeCB()`). Among other things,
  174 + updates the `targetClassCB` combobox and the `servicesGrid` table.
  175 + */
150 176 onProductTypeCBChanged: function() {
151 177 this.targetClassCB.getStore().removeAll();
152 178 this.targetNameCB.getStore().removeAll();
153 179 this.targetNameCB.disable();
154 180  
155   - if (this.dataProdutTypeCB.value == 'all') {
  181 + if (this.productTypeCB.value == 'all') {
156 182 this.targetClassCB.disable();
157 183 } else {
158   - var targetClasses = this.metadata[this.dataProdutTypeCB.value];
  184 + var targetClasses = this.metadata[this.productTypeCB.value];
159 185  
160 186 if (Object.keys(targetClasses).length == 1) {
161   - this.targetClassCB.getStore().add({'id': Object.keys(targetClasses)[0], 'name': prettify(Object.keys(targetClasses)[0])});
  187 + this.targetClassCB.getStore().add({'id': Object.keys(targetClasses)[0], 'name': this.prettify(Object.keys(targetClasses)[0])});
162 188 this.targetClassCB.disable();
163 189 this.targetClassCB.select(this.targetClassCB.getStore().getAt(0)['internalId']);
164 190 } else {
165   - this.targetClassCB.getStore().add({'id': 'all', 'name': allPrettify(this.productTypeDict[this.dataProdutTypeCB.value])});
  191 + this.targetClassCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.productTypeDict[this.productTypeCB.value])});
166 192 for (var targetClassId in targetClasses) {
167   - this.targetClassCB.getStore().add({'id': targetClassId, 'name': prettify(targetClassId)});
  193 + this.targetClassCB.getStore().add({'id': targetClassId, 'name': this.prettify(targetClassId)});
168 194 }
169 195 this.targetClassCB.select('all');
170 196 this.targetClassCB.enable();
... ... @@ -175,6 +201,10 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
175 201 this.updateServices();
176 202 },
177 203  
  204 + /**
  205 + Trigerred when a new item is selected in `targetClassCB` (see `EpnTapUI.createTargetClassCB()`). Among other things,
  206 + updates the `targetNameCB` combobox and the `servicesGrid` table.
  207 + */
178 208 onTargetClassCBChanged: function() {
179 209 this.targetNameCB.getStore().removeAll();
180 210  
... ... @@ -183,16 +213,16 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
183 213 this.targetNameCB.select('all');
184 214 this.targetNameCB.disable();
185 215 } else {
186   - var targetNames = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value];
  216 + var targetNames = this.metadata[this.productTypeCB.value][this.targetClassCB.value];
187 217  
188 218 if (Object.keys(targetNames).length == 1) {
189   - this.targetNameCB.getStore().add({'id': Object.keys(targetNames)[0], 'name': prettify(Object.keys(targetNames)[0])});
  219 + this.targetNameCB.getStore().add({'id': Object.keys(targetNames)[0], 'name': this.prettify(Object.keys(targetNames)[0])});
190 220 this.targetNameCB.select(this.targetNameCB.getStore().getAt(0)['internalId']);
191 221 this.targetNameCB.disable();
192 222 } else {
193   - this.targetNameCB.getStore().add({'id': 'all', 'name': allPrettify(this.targetClassCB.value)});
  223 + this.targetNameCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.targetClassCB.value)});
194 224 for (var targetNameId in targetNames) {
195   - this.targetNameCB.getStore().add({'id': targetNameId, 'name': prettify(targetNameId)});
  225 + this.targetNameCB.getStore().add({'id': targetNameId, 'name': this.prettify(targetNameId)});
196 226 }
197 227 this.targetNameCB.select('all');
198 228 this.targetNameCB.enable();
... ... @@ -201,89 +231,112 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
201 231 this.updateServices();
202 232 },
203 233  
  234 + /**
  235 + Trigerred when a new item is selected in `targetNameCB` (see `EpnTapUI.createTargetNameCB()`). Updates the
  236 + `servicesGrid` table.
  237 + */
204 238 onTargetNameCBChanged: function() {
205 239 this.updateServices();
206 240 },
207 241  
  242 + /**
  243 + Trigerred when the value of `rowsPerPageNf` is updated (see `EpnTapUI.createRowsPerPageNf()`). Do nothing yet, used
  244 + for debug purposes only.
  245 + */
208 246 onRowsPerPageChanged: function() {
209   - console.log("rows per page: " + this.rowsPerPageNf.value);
  247 + console.log("rows per page: " + this.productTypeCB);
210 248 },
211 249  
212   - // *** Buttons events ***
  250 + /*********************
  251 + *** Buttons events ***
  252 + *********************/
  253 +
  254 + /**
  255 + Disable or enable the navigation buttons (see `EpnTapUI.createNavigationPanel()`).
  256 + */
  257 + disableNavBtns: function(firt, previous, next, last) {
  258 + Ext.getCmp('epnTapFirstPageBtn').setDisabled(firt);
  259 + Ext.getCmp('epnTapPreviousPageBtn').setDisabled(previous);
  260 + Ext.getCmp('epnTapNextPageBtn').setDisabled(next);
  261 + Ext.getCmp('epnTapLastPageBtn').setDisabled(last);
  262 + },
213 263  
  264 + /**
  265 + Trigerred when the `firstPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
  266 + send a new query and fill `granulesGrid`.
  267 + */
214 268 onFirstPageBtnClicked: function() {
215   - this.wait();
216   - this.currentPageLb.setText('1');
217   -
218   - this.nextBtn.setDisabled(false);
219   - this.lastBtn.setDisabled(false);
220   - this.firstBtn.setDisabled(true);
221   - this.previousBtn.setDisabled(true);
222   -
  269 + var newPageNumber = 1;
  270 + var limit = Number(this.rowsPerPageNf.value);
  271 + var offset = 0;
223 272 var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];
224   - var limit = this.rowsPerPageNf.value;
225   - var offset = '0';
226 273  
  274 + this.wait();
  275 + this.disableNavBtns(true, true, false, false);
  276 + this.currentPageLb.setText('' + newPageNumber);
227 277 AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
228 278 },
229 279  
  280 + /**
  281 + Trigerred when the `previousPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
  282 + send a new query and fill `granulesGrid`.
  283 + */
230 284 onPreviousPageBtnClicked: function() {
231   - this.wait();
232 285 var newPageNumber = Number(this.currentPageLb.text) - 1;
233   - this.currentPageLb.setText('' + newPageNumber);
234   -
235   - this.nextBtn.setDisabled(false);
236   - this.lastBtn.setDisabled(false);
237   - if (this.currentPageLb.text === '1') {
238   - this.previousBtn.setDisabled(true);
239   - this.firstBtn.setDisabled(true);
240   - }
241   -
  286 + var limit = Number(this.rowsPerPageNf.value);
  287 + var offset = (newPageNumber-1) * limit;
242 288 var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];
243   - var limit = this.rowsPerPageNf.value;
244   - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value);
245 289  
  290 + this.wait();
  291 + this.currentPageLb.setText('' + newPageNumber);
  292 + var isFirstPage = this.currentPageLb.text === '1';
  293 + this.disableNavBtns(isFirstPage, isFirstPage, false, false);
246 294 AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
247 295 },
248 296  
  297 + /**
  298 + Trigerred when the `nextPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
  299 + send a new query and fill `granulesGrid`.
  300 + */
249 301 onNextPageBtnClicked: function() {
250   - this.wait();
251 302 var newPageNumber = Number(this.currentPageLb.text) + 1;
252   - this.currentPageLb.setText('' + newPageNumber);
253   -
254   - this.previousBtn.setDisabled(false);
255   - this.firstBtn.setDisabled(false);
256   - if (this.currentPageLb.text === this.totalPagesLb.text) {
257   - this.nextBtn.setDisabled(true);
258   - this.lastBtn.setDisabled(true);
259   - }
260   -
  303 + var limit = Number(this.rowsPerPageNf.value);
  304 + var offset = (newPageNumber-1) * limit;
261 305 var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];
262   - var limit = this.rowsPerPageNf.value;
263   - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value);
264 306  
  307 + this.wait();
  308 + this.currentPageLb.setText('' + newPageNumber);
  309 + var isLastPage = this.currentPageLb.text == this.totalPagesLb.text;
  310 + this.disableNavBtns(false, false, isLastPage, isLastPage);
265 311 AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
266 312 },
267 313  
  314 + /**
  315 + Trigerred when the `lastPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
  316 + send a new query and fill `granulesGrid`.
  317 + */
268 318 onLastPageBtnClicked: function() {
269   - this.wait();
270   - var newPageNumber = this.totalPagesLb.text;
271   - this.currentPageLb.setText('' + newPageNumber);
272   -
273   - this.previousBtn.setDisabled(false);
274   - this.firstBtn.setDisabled(false);
275   - this.nextBtn.setDisabled(true);
276   - this.lastBtn.setDisabled(true);
277   -
  319 + var newPageNumber = Number(this.totalPagesLb.text);
  320 + var limit = Number(this.rowsPerPageNf.value);
  321 + var offset = (newPageNumber-1) * limit;
278 322 var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];
279   - var limit = this.rowsPerPageNf.value;
280   - var offset = '' + (newPageNumber-1) * Number(this.rowsPerPageNf.value);
281 323  
  324 + console.log(newPageNumber, limit, offset, selectedServiceURL);
  325 +
  326 + this.wait();
  327 + this.currentPageLb.setText('' + newPageNumber);
  328 + this.disableNavBtns(false, false, true, true);
282 329 AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
283 330 },
284 331  
285   - // *** Grid click events ***
  332 + /*******************
  333 + *** Grids events ***
  334 + *******************/
286 335  
  336 + /**
  337 + Trigerred when a row is clicked in `servicesGrid` table (see `EpnTapUI.createServicesGrid()`). Among other things,
  338 + send a new query and fill `granulesGrid`.
  339 + */
287 340 onServiceSelected: function(selectedServiceId) {
288 341 this.wait();
289 342 this.selectedServiceId = selectedServiceId;
... ... @@ -302,64 +355,32 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
302 355 }
303 356 }
304 357 this.filter = Array(
305   - this.dataProdutTypeCB.value !== 'all' ? this.dataProdutTypeCB.value : null, // product type
  358 + this.productTypeCB.value !== 'all' ? this.productTypeCB.value : null, // product type
306 359 this.targetNameCB.value !== 'all' ? this.targetNameCB.value : null, // target name
307   - Ext.getCmp('startTimeDF').getRawValue() !== '' ? Ext.getCmp('startTimeDF').getRawValue() : null, // start time
308   - Ext.getCmp('stopTimeDF').getRawValue() !== '' ? Ext.getCmp('stopTimeDF').getRawValue() : null // stop time
  360 + this.timeSelector.getStartTime() !== '' ? this.timeSelector.getStartTime() : null, // start time
  361 + this.timeSelector.getStopTime() !== '' ? this.timeSelector.getStopTime() : null // stop time
309 362 );
310 363  
311   - var limit = this.rowsPerPageNf.value;
312 364 AmdaAction.epnTapGetNbRows(selectedServiceId, selectedServiceURL, this.filter, this.updateNbRows);
313   - AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, limit, 0, this.fillGranules);
  365 + AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, this.rowsPerPageNf.value, 0, this.fillGranules);
314 366 },
315 367  
316   - onGranuleSelected: function() {
317   - // console.log('selected granule: ' + granule.targetName);
  368 + /**
  369 + Trigerred when a row is clicked in `granulesGrid` table (see `EpnTapUI.createGranulesGrid()`). Do nothing yet, used
  370 + for debug purposes only.
  371 + */
  372 + onGranuleSelected: function(ui) {
  373 + console.log('selected granule: ' + granule.targetName);
318 374 },
319 375  
320   - // *** Other functions ***
321   -
322   - updateNbRows: function(nb_results) {
323   - /* /!\ Can not get `this`. */
324   - var totalPages = '' + Math.ceil(Number(nb_results) / Ext.getCmp('rowsPerPageNf').value);
325   -
326   - Ext.getCmp('currentPageLb').setText('1');
327   - Ext.getCmp('totalPagesLb').setText(totalPages);
328   - Ext.getCmp('previousPageBtn').setDisabled(true);
329   - Ext.getCmp('firstPageBtn').setDisabled(true);
330   - if (totalPages === '1') {
331   - Ext.getCmp('nextPageBtn').setDisabled(true);
332   - Ext.getCmp('lastPageBtn').setDisabled(true);
333   - } else {
334   - Ext.getCmp('nextPageBtn').setDisabled(false);
335   - Ext.getCmp('lastPageBtn').setDisabled(false);
336   - }
337   - },
338   -
339   - fillGranules: function(granules) {
340   - /* /!\ Can not get `this`. */
341   -
342   - if (granules == null) {
343   - console.log("There is no granules to add.");
344   - } else {
345   - try {
346   - console.log('Added granules:', granules);
347   - Ext.getCmp('granulesGrid').getStore().removeAll();
348   - Ext.getCmp('granulesGrid').getStore().add(granules);
349   - } catch( e ) {
350   - console.log('Can not add granules: ' + e);
351   - }
352   - }
353   - Ext.getCmp('servicesGrid').setDisabled(false);
354   - Ext.getCmp('servicesGrid').getEl().setStyle('cursor', 'default'); // CSS is correctly changed but without visible result.
355   - },
356   -
357   - wait: function() {
358   - this.servicesGrid.getEl().setStyle('cursor', 'wait');
359   - this.servicesGrid.setDisabled(true); // CSS is correctly changed but without visible result.
360   - },
  376 + /**********************
  377 + *** Other functions ***
  378 + **********************/
361 379  
362   - updateServices: function() {
  380 + /**
  381 + Update the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`.
  382 + */
  383 + updateServices: function(ui) {
363 384 this.servicesGrid.getStore().removeAll();
364 385 this.granulesGrid.getStore().removeAll();
365 386  
... ... @@ -368,9 +389,12 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
368 389 var timeMaxArr = null;
369 390 var timeMin = null;
370 391 var timeMax = null;
  392 + var productType = this.productTypeCB.value;
  393 + var targetClass = this.targetClassCB.value;
  394 + var targetName = this.targetNameCB.value;
371 395  
372 396 var filterDict = new Array();
373   - if(this.dataProdutTypeCB.value === 'all') {
  397 + if(productType === 'all') {
374 398 for (var dpt in this.metadata) {
375 399 for (var tc in this.metadata[dpt]) {
376 400 for (tn in this.metadata[dpt][tc]) {
... ... @@ -378,10 +402,10 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
378 402 service = this.metadata[dpt][tc][tn][serv];
379 403 timeMinArr = service[1].split('/');
380 404 filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);
381   - if (isLatest(service[1], timeMin)) {
  405 + if (this.isLatest(service[1], timeMin)) {
382 406 timeMin = service[1];
383 407 }
384   - if (isLatest(service[2], timeMax)) {
  408 + if (this.isLatest(service[2], timeMax)) {
385 409 timeMax = service[2];
386 410 }
387 411 }
... ... @@ -389,41 +413,41 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
389 413 }
390 414 }
391 415 } else if (this.targetClassCB.value === 'all') {
392   - for (var tc in this.metadata[this.dataProdutTypeCB.value]) {
393   - for (tn in this.metadata[this.dataProdutTypeCB.value][tc]) {
394   - for (serv in this.metadata[this.dataProdutTypeCB.value][tc][tn]) {
395   - service = this.metadata[this.dataProdutTypeCB.value][tc][tn][serv];
  416 + for (var tc in this.metadata[productType]) {
  417 + for (tn in this.metadata[productType][tc]) {
  418 + for (serv in this.metadata[productType][tc][tn]) {
  419 + service = this.metadata[productType][tc][tn][serv];
396 420 filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);
397   - if (isLatest(service[1], timeMin)) {
  421 + if (this.isLatest(service[1], timeMin)) {
398 422 timeMin = service[1];
399 423 }
400   - if (isLatest(service[2], timeMax)) {
  424 + if (this.isLatest(service[2], timeMax)) {
401 425 timeMax = service[2];
402 426 }
403 427 }
404 428 }
405 429 }
406 430 } else if (this.targetNameCB.value === 'all') {
407   - for (tn in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value]) {
408   - for (serv in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][tn]) {
409   - service = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][tn][serv];
  431 + for (tn in this.metadata[productType][targetClass]) {
  432 + for (serv in this.metadata[productType][targetClass][tn]) {
  433 + service = this.metadata[productType][targetClass][tn][serv];
410 434 filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);
411   - if (isLatest(service[1], timeMin)) {
  435 + if (this.isLatest(service[1], timeMin)) {
412 436 timeMin = service[1];
413 437 }
414   - if (isLatest(service[2], timeMax)) {
  438 + if (this.isLatest(service[2], timeMax)) {
415 439 timeMax = service[2];
416 440 }
417 441 }
418 442 }
419 443 } else {
420   - for (serv in this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][this.targetNameCB.value]) {
421   - service = this.metadata[this.dataProdutTypeCB.value][this.targetClassCB.value][this.targetNameCB.value][serv];
  444 + for (serv in this.metadata[productType][targetClass][targetName]) {
  445 + service = this.metadata[productType][targetClass][targetName][serv];
422 446 filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);
423   - if (isLatest(service[1], timeMin)) {
  447 + if (this.isLatest(service[1], timeMin)) {
424 448 timeMin = service[1];
425 449 }
426   - if (isLatest(service[2], timeMax)) {
  450 + if (this.isLatest(service[2], timeMax)) {
427 451 timeMax = service[2];
428 452 }
429 453 }
... ... @@ -443,6 +467,54 @@ Ext.define(&#39;amdaDesktop.EpnTapModule&#39;, {
443 467 var service = this.services[filter[s][0]];
444 468 this.servicesGrid.getStore().add({'id': filter[s][0], 'nbResults': filter[s][1], 'shortName': service['shortname'], 'title': service['title'], 'accessURL': service['accessurl']});
445 469 }
  470 + },
  471 +
  472 + /**
  473 + Callback function, called from the PHP script when the query result is received, when a service is selected.
  474 +
  475 + Among other things, update the `epnTapCurrentPageLb` label (see `EpnTapUI.createNavigationPanel()`).
  476 + */
  477 + updateNbRows: function(nb_results) {
  478 + var totalPages = '' + Math.ceil(Number(nb_results) / Ext.getCmp('epnTapRowsPerPageNf').value);
  479 +
  480 + Ext.getCmp('epnTapCurrentPageLb').setText('1');
  481 + Ext.getCmp('epnTapTotalPagesLb').setText(totalPages);
  482 +
  483 + Ext.getCmp('epnTapPreviousPageBtn').setDisabled(true);
  484 + Ext.getCmp('epnTapFirstPageBtn').setDisabled(true);
  485 + Ext.getCmp('epnTapNextPageBtn').setDisabled(totalPages === '1');
  486 + Ext.getCmp('epnTapLastPageBtn').setDisabled(totalPages === '1');
  487 + },
  488 +
  489 + /**
  490 + Callback function, called from the PHP script when the query result is received, when a service is selected or a
  491 + navigation button is clicked.
  492 +
  493 + Among other things, fill the `epnTapGranulesGrid` table (see `EpnTapUI.granulesStore`).
  494 + */
  495 + fillGranules: function(granules) {
  496 + if (granules == null) {
  497 + console.log("There is no granules to add.");
  498 + } else {
  499 + try {
  500 + console.log('Added granules:', granules);
  501 + Ext.getCmp('epnTapGranulesGrid').getStore().removeAll();
  502 + Ext.getCmp('epnTapGranulesGrid').getStore().add(granules);
  503 + } catch( e ) {
  504 + console.log('Can not add granules: ' + e);
  505 + }
  506 + }
  507 + Ext.getCmp('epnTapServicesGrid').setDisabled(false);
  508 + Ext.getCmp('epnTapServicesGrid').getEl().setStyle('cursor', 'default'); // CSS is correctly changed but without visible result.
  509 + },
  510 +
  511 + /**
  512 + Called before to send a query. Set the EpnTap panel in "waiting mode", informing to the user that a request is
  513 + processing. The altered elements are resetted in `fillGranules()`.
  514 + */
  515 + wait: function() {
  516 + this.servicesGrid.getEl().setStyle('cursor', 'wait');
  517 + this.servicesGrid.setDisabled(true); // CSS is correctly changed but without visible result.
446 518 }
447 519  
448 520 });
... ...
js/app/views/EpnTapUI.js
... ... @@ -3,379 +3,590 @@
3 3 * Name: EpnTapUI.js
4 4 * @class amdaUI.EpnTapUI
5 5 * @extends Ext.tab.Panel
6   - * @brief client for EPN-TAP services (View)
7 6 * @author Nathanael JOURDANE
8 7 * 24/10/2016: file creation
9 8 */
10 9  
11   - Ext.create('Ext.data.Store', {
  10 +// TODO: Dรฉplacer les stores dans un fichier sรฉparรฉ dans js.stores !
  11 +
  12 +/**
  13 +`productTypesStore`: An ExtJS Store containing the list of the different data product types defined on all granules, on
  14 +all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron
  15 +script).
  16 +
  17 +This list is used to fill the `productTypeCB` combo box, which is initilized in `EpnTapModule` at the panel creation.
  18 +
  19 +- `id`: the data product type IDs, according to the EPN-TAP specification (see
  20 + https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=1148225);
  21 +- `name`: the data product name, according to the EPN-TAP specification (ibid).
  22 +
  23 +These IDs and names are hard-defined in the JSon file `generic_data/EpnTapData/dataproduct_types.json`.
  24 +
  25 +Notes:
  26 +- if a granule contains a data product type which is not conform to the EPN-TAP definition (ibid), it is not displayed
  27 +in this store and an information message is displayed on the JavaScript console during the panel creation.
  28 +- if a data product type is not present in any of the granules from the EPN-TAP services, it is not present in this
  29 +store.
  30 +*/
  31 +Ext.create('Ext.data.Store', {
12 32 storeId:'productTypesStore',
13 33 fields: ['id', 'name']
14 34 });
15 35  
  36 +/**
  37 +`targetClassesStore`: An ExtJS Store containing the list of the different target classes defined on all granules, on
  38 +all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron
  39 +script), which match with the selected data product type.
  40 +
  41 +This list is used to fill the `targetClassCB` combo box, which is updated by `EpnTapModule` each time a new product type
  42 +is selected.
  43 +
  44 +- `id`: the target class in lowercase, with the underscore between each word;
  45 +- `name`: the target class, capitalized with spaces between each word (done `EpnTapModule.prettify()`).
  46 +*/
16 47 Ext.create('Ext.data.Store', {
17 48 storeId:'targetClassesStore',
18 49 fields: ['id', 'name']
19 50 });
20 51  
  52 +/**
  53 +`targetNamesStore`: An ExtJS Store containing the list of the different target names defined on all granules, on
  54 +all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron
  55 +script), which match with the selected data product and target class.
  56 +
  57 +This list is used to fill the `targetNameCB` combo box, which is updated by `EpnTapModule` each time a new target class
  58 +(or, by transitivity, product type) is selected.
  59 +
  60 +- `id`: the target name in lowercase, with the underscore between each word;
  61 +- `name`: the target name, capitalized with spaces between each word (done `EpnTapModule.prettify()`).
  62 +*/
21 63 Ext.create('Ext.data.Store', {
22 64 storeId: 'targetNamesStore',
23 65 fields: ['id', 'name']
24 66 });
25 67  
  68 +/**
  69 +`servicesStore`: An ExtJS Store containing the list of the EPN-TAP services (defined in
  70 +`generic_data/EpnTapData/metadata.json`, updated periodically with a cron script), which contains at least one granule
  71 +matching with the granules filter (the selected data product type, target class and target name).
  72 +
  73 +This list is used to fill the `servicesGrid` table, which is updated by `EpnTapModule` each time a new target name
  74 +(or, by transitivity, target class or product type) is selected.
  75 +
  76 +- `id`: the database name of the service, according to the `table_name` column from the `rr.res_table` in the
  77 + registry database;
  78 +- `nbResults`: the number of granules matching with the granules filter for this service;
  79 +- `shortName`: the service short name, according to the `short_name` column from the `rr.resource` table in the registry
  80 + database;
  81 +- `title`: the service title, according to the `res_title` column from the `rr.resource` table in the registry database;
  82 +- `accessURL`: the service access URL, according to the `access_url` column from the `rr.interface` table in the
  83 + registry database.
  84 +*/
26 85 Ext.create('Ext.data.Store', {
27 86 storeId: 'servicesStore',
28 87 fields: ['id', 'nbResults', 'shortName', 'title', 'accessURL']
29 88 });
30 89  
  90 +/**
  91 +`granulesStore`: An ExtJS Store containing the list of granules of the selected service (on `servicesGrid`), which match
  92 +with tge granules filter (the selected data product type, target class and target name).
  93 +
  94 +This list is used to fill the `granulesGrid` table, which is updated by `EpnTapModule` each time a new service is
  95 +selected.
  96 +
  97 +- `num`: the line number, according to the order of the query response and the current page (see `currentPageLb`);
  98 +- `dataproduct_type`: the dataproduct_type EPN-TAP parameter, as defined in
  99 + https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters.
  100 +- `target_name`: the target_name EPN-TAP parameter (ibid);
  101 +- `time_min`: the time_min EPN-TAP parameter (ibid);
  102 +- `time_max`: the time_max EPN-TAP parameter (ibid);
  103 +- `access_format`: the access_format EPN-TAP parameter (ibid);
  104 +- `granule_uid`: the granule_uid EPN-TAP parameter (ibid);
  105 +- `access_estsize`: the access_estsize EPN-TAP parameter (ibid);
  106 +- `access_url`: the access_url EPN-TAP parameter (ibid);
  107 +- `thumbnail_url`: the thumbnail_url EPN-TAP parameter (ibid).
  108 +*/
31 109 Ext.create('Ext.data.Store', {
32 110 storeId:'granulesStore',
33 111 fields:['num', 'dataproduct_type', 'target_name', 'time_min', 'time_max', 'access_format', 'granule_uid', 'access_estsize', 'access_url', 'thumbnail_url']
34 112 });
35 113  
  114 +/**
  115 +`EpnTapUI`: The view of the AMDA EPN-TAP module, allowing the user to query and display granules information from
  116 +EPN-TAP services.
  117 +
  118 +Note: The controller part of this module is defined in `js/app/controller/EpnTapModule`.
  119 +*/
36 120 Ext.define('amdaUI.EpnTapUI', {
37   - extend: 'Ext.container.Container',
  121 + extend: 'Ext.panel.Panel',
38 122 alias: 'widget.panelEpnTap',
  123 + requires: ['amdaUI.IntervalUI'],
39 124  
40   - txtRender: function(val) {
41   - return '<p style="white-space: normal;">' + val + '</p>';
42   - },
43   - linkRender: function(val) {
44   - return '<a href="' + val + '">data</a>';
45   - },
46   - imgRender: function(val) {
47   - return '<img width="40px height="40px" src="' + val + '">';
48   - },
49   - dptRender: function(val) {
50   - var productTypeDict = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id).productTypeDict;
51   - return (val in productTypeDict) ? '<p style="white-space: normal;">' + productTypeDict[val] + '</p>' : '<em>' + val + '</em>';
52   - },
53   - formatRender: function(val) {
54   - var mimetypeDict = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id).mimetypeDict;
55   - return (val in mimetypeDict) ? mimetypeDict[val] : '<em style="white-space: normal;">' + val + '</em>';
56   - },
57   - sizeRender: function(val) {
58   - var size = parseInt(val);
59   - if (isNaN(size)) {
60   - return '';
61   - } else if (size >= 1024*1024) {
62   - return (size/(1024*1024)).toPrecision(3) + 'Go';
63   - } else if (size >= 1024) {
64   - return (size/1024).toPrecision(3) + 'Mo';
65   - } else {
66   - return size + 'Ko';
67   - }
68   - },
69   -
  125 + /**
  126 + Method constructor, which basically call the `init()` method to create the EpnTap panel.
  127 + */
70 128 constructor: function(config) {
71 129 this.init(config);
72 130 this.callParent(arguments);
73 131 },
74 132  
  133 + /**
  134 + Create all the EpnTapPanel UI elements, and apply the AMDA module `config` (which includes the created items).
  135 +
  136 + When the panel is correctly rendered, the panel triggers `EpnTapModule.onWindowLoaded()`.
  137 +
  138 + Note: All the UI elements creation are defined as functions in this init method and not as methods in order to make
  139 + them private (ie. to avoid `EpnTapUI.createServicesGrid();`, which doesn't make sense).
  140 + */
75 141 init: function(config) {
  142 + var mod = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id);
  143 +
  144 + /************
  145 + *** Grids ***
  146 + ************/
  147 +
  148 + /**
  149 + Create `epnTapServicesGrid`, an ExtJS grid containing the EPN-TAP services matching with the filter form
  150 + (`serviceFilterPanel`).
  151 +
  152 + For each service, this grid displays:
  153 + - the service name;
  154 + - the number of granules matching with the filter.
  155 +
  156 + Other informations are available through the tooltip `serviceTooltip`.
  157 +
  158 + A click on a service triggers `EpnTapModule.onServiceSelected()`, which basically fills `GranulesGrid` by the
  159 + service granules.
  160 + */
  161 + var createServicesGrid = function() {
  162 + return new Ext.grid.Panel({
  163 + id: 'epnTapServicesGrid',
  164 + title: 'Services',
  165 + store: Ext.data.StoreManager.lookup('servicesStore'),
  166 + flex: 1,
  167 + columns: [
  168 + {text: 'Name', dataIndex: 'id', flex: 3},
  169 + {text: 'Results', dataIndex: 'nbResults', flex: 2}
  170 + ],
  171 + renderer: function(value, metadata,record) {
  172 + return getExpandableImage(value, metadata,record);
  173 + },
  174 + listeners: {
  175 + 'cellclick': function(grid, td, cellIndex, record) { mod.onServiceSelected(record.data['id']); }
  176 + },
  177 + renderTo: Ext.getBody()
  178 + });
  179 + };
76 180  
77   - var epnTapModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.epntap.id);
78   -
79   - // *** Grids ***
80   -
81   - this.servicesGrid = new Ext.grid.Panel({
82   - id: 'servicesGrid',
83   - title: 'Services',
84   - store: Ext.data.StoreManager.lookup('servicesStore'),
85   - flex: 1,
86   - columns: [
87   - {text: 'Name', dataIndex: 'id', flex: 3},
88   - {text: 'Results', dataIndex: 'nbResults', flex: 2}
89   - ],
90   - renderer: function(value, metadata,record) {
91   - return getExpandableImage(value, metadata,record);
92   - },
93   - listeners: {
94   - 'cellclick': function(grid, td, cellIndex, record) {
95   - epnTapModule.onServiceSelected(record.data['id']);
96   - }
97   - },
98   - renderTo: Ext.getBody()
99   - });
100   -
101   - this.serviceTooltip = new Ext.tip.ToolTip({
102   - id: 'serviceTooltip',
103   - target: Ext.getCmp('servicesGrid').getView().el,
104   - delegate: Ext.getCmp('servicesGrid').getView().itemSelector,
105   - trackMouse: true,
106   - listeners: {
107   - beforeshow: function updateTipBody(tooltip) {
108   - var service = Ext.getCmp('servicesGrid').getView().getRecord(tooltip.triggerElement);
109   - var ttContent = '<h3>' + service.get('shortName') + '</h3>';
110   - ttContent += '<p>' + service.get('title') + '</p>';
111   - ttContent += '<p>' + service.get('accessURL') + '</p>';
112   - tooltip.update(ttContent);
113   - }
114   - },
115   - renderTo: Ext.getBody()
116   - });
117   -
118   - this.granulesGrid = new Ext.grid.Panel({
119   - id: 'granulesGrid',
120   - title: 'Granules',
121   - store: Ext.data.StoreManager.lookup('granulesStore'),
122   - flex: 5,
123   - columns: [
124   - { text: 'Num', dataIndex: 'num', flex: 1, renderer: this.txtRender },
125   - { text: 'Type', dataIndex: 'dataproduct_type', flex: 2, renderer: this.dptRender },
126   - { text: 'Target', dataIndex: 'target_name', flex: 2, renderer: this.txtRender },
127   - { text: 'Time min', dataIndex: 'time_min', flex: 2, renderer: this.txtRender },
128   - { text: 'Time max', dataIndex: 'time_max', flex: 2, renderer: this.txtRender },
129   - { text: 'Format', dataIndex: 'access_format', flex: 2, renderer: this.formatRender },
130   - { text: 'uid', dataIndex: 'granule_uid', flex: 2, renderer: this.txtRender },
131   - { text: 'Size', dataIndex: 'access_estsize', flex: 1, renderer: this.sizeRender },
132   - { text: 'URL', dataIndex: 'access_url', flex: 1, renderer: this.linkRender },
133   - { text: 'Thumb.', dataIndex: 'thumbnail_url', flex: 1, renderer: this.imgRender}
134   - ],
135   - listeners: {
136   - 'cellclick': function(grid, td, cellIndex, record) {
137   - epnTapModule.onGranuleSelected(record.data['id']);
138   - }
139   - },
140   - renderTo: Ext.getBody()
141   - });
142   -
143   - this.granuleTooltip = new Ext.tip.ToolTip({
144   - id: 'granuleTooltip',
145   - target: Ext.getCmp('granulesGrid').getView().el,
146   - delegate: Ext.getCmp('granulesGrid').getView().itemSelector,
147   - trackMouse: true,
148   - listeners: {
149   - beforeshow: function updateTipBody(tooltip) {
150   - var thumb = Ext.getCmp('granulesGrid').getView().getRecord(tooltip.triggerElement).get('thumbnail_url');
151   - tooltip.update('<img src="' + thumb + '">');
152   - }
153   - },
154   - renderTo: Ext.getBody()
155   - });
156   -
157   - // *** Service filter elements, left part ***
158   -
159   - this.productTypeCB = new Ext.form.field.ComboBox({
160   - id: 'productTypeCB',
161   - fieldLabel: 'Product type',
162   - store: Ext.data.StoreManager.lookup('productTypesStore'),
163   - queryMode: 'local',
164   - displayField: 'name',
165   - valueField: 'id',
166   - name: 'productType',
167   - editable: false,
168   - listeners: {
169   - 'select': function(combo) {
170   - epnTapModule.onProductTypeCBChanged(combo.value);
  181 + /**
  182 + Create `epnTapServiceTooltip`, an ExtJS tooltip for the `servicesGrid` rows, in order to display additional
  183 + information for each service, such as:
  184 + - short name;
  185 + - title;
  186 + - access URL.
  187 + */
  188 + var createServiceTooltip = function() {
  189 + return new Ext.tip.ToolTip({
  190 + id: 'epnTapServiceTooltip',
  191 + target: Ext.getCmp('epnTapServicesGrid').getView().el,
  192 + delegate: Ext.getCmp('epnTapServicesGrid').getView().itemSelector,
  193 + trackMouse: true,
  194 + listeners: {
  195 + beforeshow: function updateTipBody(tooltip) {
  196 + var service = Ext.getCmp('servicesGrid').getView().getRecord(tooltip.triggerElement);
  197 + var ttContent = '<h3>' + service.get('shortName') + '</h3>';
  198 + ttContent += '<p>' + service.get('title') + '</p>';
  199 + ttContent += '<p>' + service.get('accessURL') + '</p>';
  200 + tooltip.update(ttContent);
  201 + }
  202 + },
  203 + renderTo: Ext.getBody()
  204 + });
  205 + };
  206 +
  207 + /**
  208 + Create `epnTapGranulesGrid`, an ExtJS grid containing the granules of the selected service in
  209 + `epnTapServiceGrid`.
  210 +
  211 + For each granule, this grid displays:
  212 + - the row number;
  213 + - the dataproduct type;
  214 + - the target name;
  215 + - the min and max times;
  216 + - the format;
  217 + - the UID (granule identifier);
  218 + - the estimated size;
  219 + - the URL;
  220 + - the thumbnail.
  221 +
  222 + For more information about these parameters, see https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters.
  223 + Each of these information are displayed in a specific rendering to improve user experience.
  224 + Other informations are available through the tooltip `granuleTooltip`.
  225 +
  226 + A click on a granule triggers `EpnTapModule.onGranuleSelected()`.
  227 + */
  228 + var createGranulesGrid = function() {
  229 + var txtRender = function(val) {
  230 + return '<p style="white-space: normal;">' + val + '</p>';
  231 + };
  232 + var linkRender = function(val) {
  233 + return '<a href="' + val + '">data</a>';
  234 + };
  235 + var imgRender = function(val) {
  236 + return '<img width="40px height="40px" src="' + val + '">';
  237 + };
  238 + var dptRender = function(val) {
  239 + return (val in mod.productTypeDict) ? '<p style="white-space: normal;">' + mod.productTypeDict[val] + '</p>' : '<em>' + val + '</em>';
  240 + };
  241 + var formatRender = function(val) {
  242 + return (val in mod.mimetypeDict) ? mod.mimetypeDict[val] : '<em style="white-space: normal;">' + val + '</em>';
  243 + };
  244 + var sizeRender = function(val) {
  245 + var size = parseInt(val);
  246 + if (isNaN(size)) {
  247 + return '';
  248 + } else if (size >= 1024*1024) {
  249 + return (size/(1024*1024)).toPrecision(3) + 'Go';
  250 + } else if (size >= 1024) {
  251 + return (size/1024).toPrecision(3) + 'Mo';
  252 + } else {
  253 + return size + 'Ko';
171 254 }
172   - }
173   - });
174   -
175   - this.targetClassCB = new Ext.form.field.ComboBox({
176   - id: 'targetClassCB',
177   - fieldLabel: 'Target class',
178   - store: Ext.data.StoreManager.lookup('targetClassesStore'),
179   - queryMode: 'local',
180   - displayField: 'name',
181   - valueField: 'id',
182   - name: 'targetClass',
183   - editable: false,
184   - listeners: {
185   - 'select': function(combo) {
186   - epnTapModule.onTargetClassCBChanged(combo.value);
  255 + };
  256 +
  257 + return new Ext.grid.Panel({
  258 + id: 'epnTapGranulesGrid',
  259 + title: 'Granules',
  260 + store: Ext.data.StoreManager.lookup('granulesStore'),
  261 + flex: 5,
  262 + columns: [
  263 + { text: 'Num', dataIndex: 'num', flex: 1, renderer: txtRender },
  264 + { text: 'Type', dataIndex: 'dataproduct_type', flex: 2, renderer: dptRender },
  265 + { text: 'Target', dataIndex: 'target_name', flex: 2, renderer: txtRender },
  266 + { text: 'Time min', dataIndex: 'time_min', flex: 2, renderer: txtRender },
  267 + { text: 'Time max', dataIndex: 'time_max', flex: 2, renderer: txtRender },
  268 + { text: 'Format', dataIndex: 'access_format', flex: 2, renderer: formatRender },
  269 + { text: 'uid', dataIndex: 'granule_uid', flex: 2, renderer: txtRender },
  270 + { text: 'Size', dataIndex: 'access_estsize', flex: 1, renderer: sizeRender },
  271 + { text: 'URL', dataIndex: 'access_url', flex: 1, renderer: linkRender },
  272 + { text: 'Thumb.', dataIndex: 'thumbnail_url', flex: 1, renderer: imgRender}
  273 + ],
  274 + listeners: {
  275 + 'cellclick': function(grid, td, cellIndex, record) { mod.onGranuleSelected(record.data['id']); }
  276 + },
  277 + renderTo: Ext.getBody()
  278 + });
  279 + };
  280 +
  281 + /**
  282 + Create `epnTapGranuleTooltip`, an ExtJS tooltip for the `granulesGrid` rows, in order to display additional
  283 + information for each granule which is currently only the granule thumbnail, in full size.
  284 + */
  285 + var createGranuleTooltip = function() {
  286 + return new Ext.tip.ToolTip({
  287 + id: 'epnTapGranuleTooltip',
  288 + target: Ext.getCmp('granulesGrid').getView().el,
  289 + delegate: Ext.getCmp('granulesGrid').getView().itemSelector,
  290 + trackMouse: true,
  291 + listeners: {
  292 + beforeshow: function updateTipBody(tooltip) {
  293 + var thumb = Ext.getCmp('granulesGrid').getView().getRecord(tooltip.triggerElement).get('thumbnail_url');
  294 + tooltip.update('<img src="' + thumb + '">');
  295 + }
  296 + },
  297 + renderTo: Ext.getBody()
  298 + });
  299 + };
  300 +
  301 + /**
  302 + Create `epnTapGridsPanel`, an ExtJS Panel, containing `epnTapServicesGrid` and `epnTapGranulesGrid`.
  303 +
  304 + After the rendering of the grids, it triggers `epnTapModule.onWindowLoaded()`, which basically fill
  305 + `epnTapServicesGrid` for the first time.
  306 + */
  307 + var createGridsPanel = function() {
  308 + var self = this;
  309 + return new Ext.panel.Panel({
  310 + id: 'epnTapGridsPanel',
  311 + region: 'center',
  312 + height: 350,
  313 + layout: { type: 'hbox', pack: 'start', align: 'stretch' },
  314 + items: [
  315 + createServicesGrid(),
  316 + createGranulesGrid()
  317 + ],
  318 + listeners: {
  319 + afterrender: function() { mod.onWindowLoaded(self); }
187 320 }
188   - }
189   - });
190   -
191   - this.targetNameCB = new Ext.form.field.ComboBox({
192   - id: 'targetNameCB',
193   - fieldLabel: 'Target name',
194   - store: Ext.data.StoreManager.lookup('targetNamesStore'),
195   - queryMode: 'local',
196   - displayField: 'name',
197   - valueField: 'id',
198   - name: 'targetName',
199   - triggerAction: 'all',
200   - typeAhead: true,
201   - mode: 'remote',
202   - minChars: 2,
203   - forceSelection: true,
204   - listeners: {
205   - 'select': function(combo) {
206   - epnTapModule.onTargetNameCBChanged(combo.value);
  321 + });
  322 + };
  323 +
  324 + /***************************
  325 + *** Service filter panel ***
  326 + ***************************/
  327 +
  328 + /**
  329 + Create `epnTapProductTypeCB`, an ExtJS ComboBox, containing a list of product types as defined in
  330 + `epnTapProductTypesStore`, which is initilized by `EpnTapModule`.
  331 +
  332 + The selection of a produt type triggers `EpnTapModule.onProductTypeCBChanged()`, which basically update
  333 + `epnTapTargetClassCB` and `epnTapGranulesGrid`.
  334 + */
  335 + var createProductTypeCB = function() {
  336 + return new Ext.form.field.ComboBox({
  337 + id: 'epnTapProductTypeCB',
  338 + fieldLabel: 'Product type',
  339 + store: Ext.data.StoreManager.lookup('productTypesStore'),
  340 + queryMode: 'local',
  341 + displayField: 'name',
  342 + valueField: 'id',
  343 + name: 'productType',
  344 + editable: false,
  345 + listeners: {
  346 + 'select': function(combo) { mod.onProductTypeCBChanged(combo.value); }
207 347 }
208   - }
209   - });
210   -
211   - // *** Service filter elements, right part ***
212   -
213   - this.startTimeDF = new Ext.form.field.Date({
214   - id: 'startTimeDF',
215   - fieldLabel: 'Start time',
216   - format: 'Y/m/d H:i:s',
217   - width: 100,
218   - listeners: {
219   - 'select': function(dateField, value) {
220   - epnTapModule.onTargetNameCBChanged(value);
  348 + });
  349 + };
  350 +
  351 + /**
  352 + Create `epnTapTargetClassCB`, an ExtJS ComboBox, containing a list of target classes corresponding to the
  353 + selected product type, as defined in `targetClassesStore`, which is initilized by `EpnTapModule`.
  354 +
  355 + The selection of a target class triggers the `EpnTapModule.onTargetClassCBChanged()`, which basically updates
  356 + `targetNameCB` and `granulesGrid`.
  357 + */
  358 + var createTargetClassCB = function() {
  359 + return new Ext.form.field.ComboBox({
  360 + id: 'epnTapTargetClassCB',
  361 + fieldLabel: 'Target class',
  362 + store: Ext.data.StoreManager.lookup('targetClassesStore'),
  363 + queryMode: 'local',
  364 + displayField: 'name',
  365 + valueField: 'id',
  366 + name: 'targetClass',
  367 + editable: false,
  368 + listeners: {
  369 + 'select': function(combo) { mod.onTargetClassCBChanged(combo.value); }
221 370 }
222   - }
223   - });
224   -
225   - this.stopTimeDF = new Ext.form.field.Date({
226   - id: 'stopTimeDF',
227   - fieldLabel: 'Stop time',
228   - format: 'Y/m/d H:i:s',
229   - width: 100,
230   - listeners: {
231   - 'select': function(dateField, value) {
232   - epnTapModule.onTargetNameCBChanged(value);
  371 + });
  372 + };
  373 +
  374 + /**
  375 + Create `epnTapTargetNameCB`, an ExtJS ComboBox, containing a list of target names corresponding to the selected
  376 + target class, as defined in `targetNamesStore`, which is initilized by `EpnTapModule`.
  377 +
  378 + The selection of a target name triggers `EpnTapModule.onTargetNameCBChanged()`, which basically updates
  379 + `granulesGrid`.
  380 + */
  381 + var createTargetNameCB = function() {
  382 + return new Ext.form.field.ComboBox({
  383 + id: 'epnTapTargetNameCB',
  384 + fieldLabel: 'Target name',
  385 + store: Ext.data.StoreManager.lookup('targetNamesStore'),
  386 + queryMode: 'local',
  387 + displayField: 'name',
  388 + valueField: 'id',
  389 + name: 'targetName',
  390 + triggerAction: 'all',
  391 + typeAhead: true,
  392 + mode: 'remote',
  393 + minChars: 2,
  394 + forceSelection: true,
  395 + listeners: {
  396 + 'select': function(combo) { mod.onTargetNameCBChanged(combo.value); }
233 397 }
234   - }
235   - });
236   -
237   - this.durationPanel = new Ext.panel.Panel({
238   - id: 'duration',
239   - layout: { type: 'hbox', pack: 'start', align: 'stretch' },
240   - border: false,
241   - defaults: { width: 60, margin: '0, 5, 0, 5', xtype: 'numberfield', listeners: { 'select': function(elmt) { epnTapModule.onDurationChanged(elmt); } } },
242   - items: [{
243   - id: 'days',
244   - margin: '0, 5, 0, 0',
245   - fieldLabel: 'Duration',
246   - emptyText: 'Days',
247   - width: 170
248   - }, {
249   - id: 'hours',
250   - emptyText: 'Hours'
251   - }, {
252   - id: 'minutes',
253   - emptyText: 'Min.'
254   - }, {
255   - id: 'seconds',
256   - emptyText: 'Sec.'
257   - }]
258   - });
259   -
260   - this.rowPerPageNf = new Ext.form.field.Number({
261   - id: 'rowsPerPageNf',
262   - fieldLabel: 'Rows per page',
263   - margin: '4 0 4 0',
264   - width: 160,
265   - height: 20,
266   - value: 20,
267   - minValue: 1,
268   - maxValue: 2000,
269   - listeners: {
270   - 'change': function(rowPerPageNf, newValue) {
271   - epnTapModule.onRowsPerPageChanged(newValue);
  398 + });
  399 + };
  400 +
  401 + /**
  402 + Create `epnTapServiceFilterPanel`, an ExtJS Panel containing two containers:
  403 + - the left container, containing the combo boxes (for product type, target class and target name)
  404 + and the navigation panel;
  405 + - the right container, containing the time selector.
  406 + */
  407 + var createServiceFilterPanel = function() {
  408 + return new Ext.panel.Panel({
  409 + id: 'epnTapServiceFilterPanel',
  410 + region : 'north',
  411 + layout: { type: 'hbox', pack: 'start', align: 'stretch' },
  412 + defaults: { margin: 5 },
  413 + items: [{ // Left part
  414 + xtype : 'container',
  415 + layout: 'form',
  416 + flex: 2,
  417 + items: [
  418 + createProductTypeCB(),
  419 + createTargetClassCB(),
  420 + createTargetNameCB(),
  421 + {
  422 + xtype: 'panel',
  423 + layout: { type: 'hbox', pack: 'start', align: 'stretch' },
  424 + border: false,
  425 + items: [
  426 + createRowPerPageNf(),
  427 + createNavigationPanel()
  428 + ]
  429 + }
  430 + ]
  431 + }, { // Right part
  432 + xtype : 'form',
  433 + id: 'epnTapIntervalSelector',
  434 + layout: 'form',
  435 + border: 'false',
  436 + flex: 2,
  437 + items: [
  438 + createTimeSelector()
  439 + ]
  440 + }]
  441 + });
  442 + };
  443 +
  444 + /**
  445 + Create `epnTapTimeSelector`, an IntervalUI object, allowing the user to select a time interval (by filling two
  446 + dates and/or a duration).
  447 +
  448 + See `js/app/views/IntervalUI.js` for more information about this component.
  449 + */
  450 + var createTimeSelector = function() {
  451 + return Ext.create('amdaUI.IntervalUI', {
  452 + id: 'epnTapTimeSelector'
  453 + });
  454 + };
  455 +
  456 + /***********************
  457 + *** Navigation panel ***
  458 + ***********************/
  459 +
  460 + /**
  461 + Create `epnTapRowsPerPageNf`, a ExtJS Number field, allowing the user to select the number of rows to display in
  462 + `epnTapGranulesGrid`.
  463 +
  464 + When a new number is entered, it triggers `EpnTapModule.onRowsPerPageChanged()`.
  465 + */
  466 + var createRowPerPageNf = function() {
  467 + return new Ext.form.field.Number({
  468 + id: 'epnTapRowsPerPageNf',
  469 + fieldLabel: 'Rows per page',
  470 + margin: '4 0 4 0',
  471 + width: 160,
  472 + height: 20,
  473 + value: 20,
  474 + minValue: 1,
  475 + maxValue: 2000,
  476 + listeners: {
  477 + 'change': function(rowPerPageNf, newValue) { mod.onRowsPerPageChanged(newValue); }
272 478 }
273   - }
274   - });
275   -
276   - // *** Panels ***
277   -
278   - this.pageSelectPanel = new Ext.panel.Panel({
279   - id: 'pageSelect',
280   - border: false,
281   - margin: '2 0 2 50',
282   - defaults: { margin: '0 5 0 5', width: 20, xtype: 'button', disabled: true},
283   - items: [{
284   - xtype: 'label',
285   - text: 'Page:'
286   - }, {
287   - id: 'firstPageBtn',
288   - text: '|<',
289   - tooltip: 'First page',
290   - handler: function() { epnTapModule.onFirstPageBtnClicked(); }
291   - }, {
292   - id: 'previousPageBtn',
293   - text: '<',
294   - tooltip: 'Previous page',
295   - handler: function() { epnTapModule.onPreviousPageBtnClicked(); }
296   - }, {
297   - xtype: 'label',
298   - id: 'currentPageLb',
299   - tooltip: 'Current page',
300   - text: '-'
301   - }, {
302   - xtype: 'label',
303   - text: '/'
304   - }, {
305   - xtype: 'label',
306   - id: 'totalPagesLb',
307   - tooltip: 'Total pages',
308   - text: '-'
309   - }, {
310   - id: 'nextPageBtn',
311   - text: '>',
312   - tooltip: 'Next page',
313   - handler: function() { epnTapModule.onNextPageBtnClicked(); }
314   - }, {
315   - id: 'lastPageBtn',
316   - text: '>|',
317   - tooltip: 'Last page',
318   - handler: function() { epnTapModule.onLastPageBtnClicked(); }
319   - }]
320   - });
321   -
322   - this.granulePagePanel = new Ext.panel.Panel({
323   - id: 'granulePagePanel',
324   - layout: { type: 'hbox', pack: 'start', align: 'stretch' },
325   - border: false,
326   - items: [this.rowPerPageNf, this.pageSelectPanel]
327   - });
328   -
329   - this.serviceFilterPanel = new Ext.panel.Panel({
330   - id: 'serviceFilterPanel',
331   - region : 'north',
332   - layout: { type: 'hbox', pack: 'start', align: 'stretch' },
333   - defaults: { margin: 5 },
334   - items: [{ // Left part
335   - xtype : 'container',
336   - layout: 'form',
337   - flex: 2,
338   - items: [ this.productTypeCB, this.targetClassCB, this.targetNameCB ]
339   - }, { // Right part
340   - xtype : 'container',
341   - layout: 'form',
342   - flex: 2,
343   - items: [ this.startTimeDF, this.stopTimeDF, this.durationPanel, this.granulePagePanel ]
344   - }]
345   - });
346   -
347   - this.gridsPanel = new Ext.panel.Panel({
348   - id: 'gridsPanel',
349   - region: 'center',
350   - height: 350,
351   - layout: { type: 'hbox', pack: 'start', align: 'stretch' },
352   - items: [ this.servicesGrid, this.granulesGrid ],
353   - listeners: {
354   - afterrender: function() { epnTapModule.onWindowLoaded(); }
355   - }
356   - });
357   -
358   - this.infoPanel = new Ext.panel.Panel({
359   - id: 'infoPanel',
360   - region: 'south',
361   - title: 'Information',
362   - collapsible: true,
363   - flex: 0,
364   - height: 100,
365   - autoHide: false,
366   - bodyStyle: 'padding: 5px',
367   - iconCls: 'icon-information',
368   - loader: { autoLoad: true, url: helpDir + 'epnTapHOWTO' }
369   - });
  479 + });
  480 + };
  481 +
  482 + /**
  483 + Create `epnTapNavigationPanel`, an ExtJSPanel containing several elements in order to navigate through the
  484 + different pages of the query result. If the number of results is highter than the `epnTapRowsPerPageNf` field
  485 + value, the result appears to be displayed in different pages. This panel is used to select and display the page
  486 + number, mainly with these following elements:
  487 + - `epnTapFirstPageBtn`: an ExtJS Button, used to come back to the first page of result,
  488 + handling `EpnTapModule.onFirstPageBtnClicked()`;
  489 + - `epnTapPreviousPageBtn`: an ExtJS Button, used to come back to the previous page of result,
  490 + handling `EpnTapModule.onPreviousPageBtnClicked()`;
  491 + - `epnTapCurrentPageLb`: an ExtJS Label, displaying the actual current page; TODO: use a Number field instead!
  492 + - `epnTapTotalPagesLb`: an ExtJS Label, displaying the total page number of results (according to the
  493 + `epnTapRowsPerPageNf` field value);
  494 + - `epnTapNextPageBtn`: an ExtJS Button, used to go to the next page of result,
  495 + handling `EpnTapModule.onNextPageBtnClicked()`;
  496 + - `epnTapLastPageBtn`: an ExtJS Button, used to come back to the last page of result,
  497 + handling `EpnTapModule.onLastPageBtnClicked()`.
  498 +
  499 + Note: Pages are not actually a "graphical filter": when the user navigate through the pages, a new query is send
  500 + to the server with the corresponding range, which improves the response time on large requests.
  501 + */
  502 + var createNavigationPanel = function() {
  503 + return new Ext.panel.Panel({
  504 + name: 'epnTapNavigationPanel',
  505 + border: false,
  506 + margin: '2 0 2 50',
  507 + defaults: { margin: '0 5 0 5', width: 20, xtype: 'button', disabled: true},
  508 + items: [{
  509 + xtype: 'label',
  510 + text: 'Page:'
  511 + }, {
  512 + id: 'epnTapFirstPageBtn',
  513 + text: '|<',
  514 + tooltip: 'First page',
  515 + handler: function() { mod.onFirstPageBtnClicked(); }
  516 + }, {
  517 + id: 'epnTapPreviousPageBtn',
  518 + text: '<',
  519 + tooltip: 'Previous page',
  520 + handler: function() { mod.onPreviousPageBtnClicked(); }
  521 + }, {
  522 + xtype: 'label',
  523 + id: 'epnTapCurrentPageLb',
  524 + tooltip: 'Current page',
  525 + text: '-'
  526 + }, {
  527 + xtype: 'label',
  528 + text: '/'
  529 + }, {
  530 + xtype: 'label',
  531 + id: 'epnTapTotalPagesLb',
  532 + tooltip: 'Total pages',
  533 + text: '-'
  534 + }, {
  535 + id: 'epnTapNextPageBtn',
  536 + text: '>',
  537 + tooltip: 'Next page',
  538 + handler: function() { mod.onNextPageBtnClicked(); }
  539 + }, {
  540 + id: 'epnTapLastPageBtn',
  541 + text: '>|',
  542 + tooltip: 'Last page',
  543 + handler: function() { mod.onLastPageBtnClicked(); }
  544 + }]
  545 + });
  546 + };
  547 +
  548 + /*******************
  549 + *** Other panels ***
  550 + *******************/
  551 +
  552 + /**
  553 + Create `epnTapInfoPanel`, an ExtJS Panel used to display a brief user guide about how to use this module.
  554 + */
  555 + var createInfoPanel = function() {
  556 + return new Ext.panel.Panel({
  557 + id: 'epnTapInfoPanel',
  558 + region: 'south',
  559 + title: 'Information',
  560 + collapsible: true,
  561 + flex: 0,
  562 + height: 100,
  563 + autoHide: false,
  564 + bodyStyle: 'padding: 5px',
  565 + iconCls: 'icon-information',
  566 + loader: { autoLoad: true, url: helpDir + 'epnTapHOWTO' }
  567 + });
  568 + };
  569 +
  570 + // TODO tester ceci:
  571 + // config.title = 'EPN-TAP';
  572 + // config.layout = 'border';
  573 + // config.items = [
  574 + // createServiceFilterPanel(config.targetName),
  575 + // createGridsPanel(),
  576 + // createInfoPanel()
  577 + // ];
  578 + // Ext.apply(this, Ext.apply(arguments, config));
370 579  
371 580 var myConf = {
372 581 width: 1000,
373 582 height: 550,
374 583 layout: 'border',
375   - items: [ this.serviceFilterPanel, this.gridsPanel, this.infoPanel ]
  584 + items: [
  585 + createServiceFilterPanel(),
  586 + createGridsPanel(),
  587 + createInfoPanel()
  588 + ]
376 589 };
377   -
378 590 Ext.apply(this, Ext.apply(arguments, myConf));
379   -
380 591 }
381 592 });
... ...
js/app/views/IntervalUI.js
... ... @@ -6,7 +6,7 @@
6 6 * @brief common component to select interval
7 7 * @author Benjamin
8 8 * @version $Id: IntervalUI.js 2077 2014-02-11 11:33:36Z elena $
9   - * @todo Validations
  9 + * @todo Validations
10 10 *****************************************************************************
11 11 * FT Id : Date : Name - Description
12 12 ******************************************************************************
... ... @@ -15,17 +15,21 @@
15 15  
16 16 Ext.define('amdaUI.IntervalUI', {
17 17 extend: 'Ext.container.Container',
18   -
19 18 alias: 'widget.intervalSelector',
20   -
21 19 activeField : null,
22   -
  20 +
23 21 constructor: function(config) {
24 22 this.init(config);
25 23 this.callParent(arguments);
26 24 },
27   -
28   - setInterval : function(startDate,stopDate)
  25 +
  26 + /**
  27 + Set the start and stop date, and update the duration field.
  28 + - startDate: A Extjs Date object representing the new start time.
  29 + - stopDate: A Extjs Date object representing the new stop time.
  30 + - return: None.
  31 + */
  32 + setInterval : function(startDate, stopDate)
29 33 {
30 34 // get the search form
31 35 var form = this.findParentByType('form').getForm();
... ... @@ -33,44 +37,55 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
33 37 var startField = form.findField('startDate');
34 38 // get stop field
35 39 var stopField = form.findField('stopDate');
36   -
  40 +
37 41 if (startField != null)
38 42 startField.setValue(startDate);
39   -
  43 +
40 44 if (stopField != null)
41 45 stopField.setValue(stopDate);
42   -
  46 +
43 47 this.updateDuration();
44 48 },
45   -
  49 +
  50 + /**
  51 + Get the start time field value.
  52 + - return: A Extjs Date object representing the start time.
  53 + */
46 54 getStartTime : function()
47 55 {
48 56 // get the search form
49 57 var form = this.findParentByType('form').getForm();
50 58 // get start field
51 59 var startField = form.findField('startDate');
52   -
  60 +
53 61 return startField.getValue();
54 62 },
55   -
  63 +
  64 + /**
  65 + Get the stop time field value.
  66 + - return: A Extjs Date object representing the stop time.
  67 + */
56 68 getStopTime : function()
57 69 {
58 70 // get the search form
59 71 var form = this.findParentByType('form').getForm();
60 72 // get stop field
61 73 var stopField = form.findField('stopDate');
62   -
63   - return stopField.getValue();
  74 +
  75 + return stopField.getValue();
64 76 },
65   -
66   - updateDuration: function() {
67 77  
  78 + /*
  79 + #### Private methods from here ####
  80 + */
  81 +
  82 + updateDuration: function() {
68 83 // get the search form
69 84 var form = this.findParentByType('form').getForm();
70 85 // get start value
71   - var start = form.findField('startDate').getValue();
  86 + var start = this.getStartTime();
72 87 // get stop value
73   - var stop = form.findField('stopDate').getValue();
  88 + var stop = this.getStopTime();
74 89 // if duration computable
75 90 if (stop != null && start != null) {
76 91  
... ... @@ -78,18 +93,17 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
78 93 var zoneOffset = stop.getTimezoneOffset() - start.getTimezoneOffset();
79 94 // compute duration
80 95 var diff = stop - start - zoneOffset*60000;
81   -
  96 +
82 97 var durationDays = Math.floor(diff/86400000);
83 98 // set all duration values
84   - form.findField('durationDay').setValue(Ext.String.leftPad(durationDays,4,'0'));
  99 + form.findField('durationDay').setValue(Ext.String.leftPad(durationDays,4,'0'));
85 100 form.findField('durationHour').setValue(Ext.String.leftPad(Math.floor(diff/3600000 % 24),2,'0'));
86 101 form.findField('durationMin').setValue(Ext.String.leftPad(Math.floor(diff/60000 % 60),2,'0'));
87 102 form.findField('durationSec').setValue(Ext.String.leftPad(Math.floor(diff/1000 % 60),2,'0'));
88   -
89   - if (durationDays > 9999) {
  103 +
  104 + if (durationDays > 9999) {
90 105 form.findField('durationDay').markInvalid('Maximum interval is 9999 days!');
91 106 }
92   -
93 107 }
94 108  
95 109 },
... ... @@ -99,13 +113,12 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
99 113 var form = this.findParentByType('form').getForm();
100 114 // get global validation status for duration fields
101 115 return (
102   - form.findField('durationDay').isValid() && form.findField('durationHour').isValid()
  116 + form.findField('durationDay').isValid() && form.findField('durationHour').isValid()
103 117 && form.findField('durationMin').isValid() && form.findField('durationSec').isValid()
104 118 );// return true if all duration fields are Valid false otherwise
105 119 },
106 120  
107 121 updateStop: function() {
108   -
109 122 // get the time form
110 123 var form = this.findParentByType('form').getForm();
111 124 // get duration value
... ... @@ -121,68 +134,72 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
121 134 form.findField('stopDate').setValue(stop);
122 135  
123 136 },
124   -
125   - onChangeStartField : function(field, newValue, oldValue)
126   - {
127   - if (field.isValid()) {
128   - // get the search form
129   - var form = this.findParentByType('form').getForm();
130   - // set to the stop datefield the newValue as minValue
131   - form.findField('stopDate').setMinValue(newValue);
132   - // if it's a user modification
133   - if (oldValue != null && this.activeField == 'start') {
134   - // launch the update of duration fields
135   - this.updateDuration();
136   - }
137   - }
  137 +
  138 + onChangeStartField : function(field, newValue, oldValue)
  139 + {
  140 + if (field.isValid()) {
  141 + // get the search form
  142 + var form = this.findParentByType('form').getForm();
  143 + // set to the stop datefield the newValue as minValue
  144 + form.findField('stopDate').setMinValue(newValue);
  145 + // if it's a user modification
  146 + if (oldValue != null && this.activeField == 'start') {
  147 + // launch the update of duration fields
  148 + this.updateDuration();
  149 + }
  150 + }
138 151 },
139   -
140   - onChangeStopField: function(field, newValue, oldValue){
141   - if (field.isValid() && oldValue != null && this.activeField == 'stop') {
142   - // launch the update of duration fields
143   - this.updateDuration();
144   - }
  152 +
  153 + onChangeStopField: function(field, newValue, oldValue) {
  154 + if (field.isValid() && oldValue != null && this.activeField == 'stop') {
  155 + // launch the update of duration fields
  156 + this.updateDuration();
  157 + }
145 158 },
146   -
  159 +
147 160 getDateField : function(fieldName,fieldText,fieldId,onChangeField)
148 161 {
149 162 return {
150 163 layout: {type: 'hbox', align: 'middle'},
151 164 items: [
152   - {
153   - xtype: 'datefield', name: fieldName, format: 'Y/m/d H:i:s',
154   - enforceMaxLength : true,
155   - maxLength: 19,
156   - fieldLabel: fieldText, labelAlign: 'right', labelWidth: 60,
  165 + {
  166 + xtype: 'datefield',
  167 + name: fieldName,
  168 + format: 'Y/m/d H:i:s',
  169 + enforceMaxLength: true,
  170 + maxLength: 19,
  171 + fieldLabel: fieldText,
  172 + labelAlign: 'right',
  173 + labelWidth: 60,
157 174 listeners: {
158 175 change: onChangeField,
159 176 focus: function(field) {
160 177 this.activeField = fieldId;
161 178 },
162 179 scope : this
163   - }
164   - }
165   - ]
  180 + }
  181 + }
  182 + ]
166 183 };
167 184 },
168   -
  185 +
169 186 getStartField : function()
170 187 {
171   - return this.getDateField('startDate','Start Time','start',this.onChangeStartField);
  188 + return this.getDateField('startDate','Start Time','start', this.onChangeStartField);
172 189 },
173   -
  190 +
174 191 getStopField : function()
175 192 {
176 193 return this.getDateField('stopDate','Stop Time','stop',this.onChangeStopField);
177 194 },
178   -
  195 +
179 196 getDurationField : function()
180 197 {
181 198 return {
182 199 layout: {type: 'hbox', align: 'middle'},
183 200 height: 45,
184   - defaults: {
185   - xtype: 'textfield', labelAlign: 'top', width: 30,
  201 + defaults: {
  202 + xtype: 'textfield', labelAlign: 'top', width: 30,
186 203 allowBlank: false, maxLength:2, enforceMaxLength : true,
187 204 hideTrigger: true,
188 205 regex: /^[0-9]([0-9])*$/i,
... ... @@ -190,15 +207,15 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
190 207 change: function(field, newValue, oldValue){
191 208 if (this.isValidDuration() && oldValue != null && this.activeField == 'duration') {
192 209 // launch the update of stop datefield
193   - this.updateStop();
194   - }
  210 + this.updateStop();
  211 + }
195 212 },
196 213 focus: function(field) {
197   - this.activeField = 'duration';
198   - },
  214 + this.activeField = 'duration';
  215 + },
199 216 scope : this
200   - }
201   - },
  217 + }
  218 + },
202 219 items:[
203 220 { xtype: 'displayfield', labelWidth: 60, labelAlign: 'right', width: 60, fieldLabel: '<br>Duration'},
204 221 { xtype: 'component', width: 5},
... ... @@ -206,20 +223,19 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
206 223 { xtype: 'component', width: 5},
207 224 { name: 'durationHour', fieldLabel: 'Hrs'},
208 225 { xtype: 'component', width: 5},
209   - { name: 'durationMin', fieldLabel: 'Mins'},
  226 + { name: 'durationMin', fieldLabel: 'Mins'},
210 227 { xtype: 'component', width: 5},
211 228 { name: 'durationSec', fieldLabel: 'Secs'}
212 229 ]
213 230 };
214 231 },
215   -
  232 +
216 233 init : function(config) {
217   -
218 234 var me = this;
219   -
  235 +
220 236 var myConf = {
221 237 border: false,
222   - plain: true,
  238 + plain: true,
223 239 flex: 1,
224 240 layout: 'anchor',
225 241 defaults: { height : 30, xtype : 'container'},
... ... @@ -230,9 +246,6 @@ Ext.define(&#39;amdaUI.IntervalUI&#39;, {
230 246 me.getDurationField()
231 247 ]
232 248 };
233   -
234   - Ext.apply (this , Ext.apply (arguments, myConf));
  249 + Ext.apply (this , Ext.apply (arguments, myConf));
235 250 }
236 251 });
237   -
238   -
239 252 \ No newline at end of file
... ...
php/classes/EpnTapMgr.php
... ... @@ -54,7 +54,7 @@ class EpnTapMgr {
54 54  
55 55 /* filter order: product type, target name, time min, time max */
56 56 public function getGranules($table_name, $access_url, $filter, $select, $limit, $offset) {
57   - $query = "SELECT TOP $limit " . join(', ', $select) . " FROM $table_name.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]) . " OFFSET $offset";
  57 + $query = "SELECT TOP {$limit} " . join(', ', $select) . " FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]) . " OFFSET {$offset}";
58 58 // return $query;
59 59 $result = $this->request($access_url, $query);
60 60 for ($i = 0 ; $i < sizeof($result) ; $i++) {
... ... @@ -67,7 +67,7 @@ class EpnTapMgr {
67 67  
68 68 /* filter order: product type, target name, time min, time max */
69 69 public function getNbRows($table_name, $access_url, $filter) {
70   - $query = "SELECT COUNT(*) AS nb_rows FROM $table_name.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]);
  70 + $query = "SELECT COUNT(*) AS nb_rows FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]);
71 71 // return $query;
72 72 return $this->request($access_url, $query)[0]['nb_rows'];
73 73 }
... ...