Commit 428eb66e9a862ce84908e6bc08a0d11403d813ae
1 parent
80d30e39
Exists in
master
and in
112 other branches
Use JS standard coding convention
Showing
2 changed files
with
758 additions
and
758 deletions
Show diff stats
js/app/controllers/EpnTapModule.js
1 | /** | 1 | /** |
2 | * Project : AMDA-NG | 2 | * Project : AMDA-NG |
3 | - * Name : EpnTapModule.js | 3 | + * Name : EpnTapModule.js |
4 | * @class amdaDesktop.EpnTapModule | 4 | * @class amdaDesktop.EpnTapModule |
5 | * @extends amdaDesktop.AmdaModule | 5 | * @extends amdaDesktop.AmdaModule |
6 | * @brief EpnTap Module controller definition | 6 | * @brief EpnTap Module controller definition |
@@ -9,175 +9,175 @@ | @@ -9,175 +9,175 @@ | ||
9 | 9 | ||
10 | Ext.define('amdaDesktop.EpnTapModule', { | 10 | Ext.define('amdaDesktop.EpnTapModule', { |
11 | 11 | ||
12 | - extend: 'amdaDesktop.AmdaModule', | ||
13 | - requires: ['amdaUI.EpnTapUI'], | ||
14 | - contentId : 'EpnTapUI', | ||
15 | - | ||
16 | - /** The alias name of the module view. */ | ||
17 | - // uiType: 'panelEpnTap', | ||
18 | - uiType : 'panelEpnTap', | ||
19 | - | ||
20 | - /** The text displayed on the *help button* tooltip. */ | ||
21 | - helpTitle: 'Help on EPN-TAP Module', | ||
22 | - | ||
23 | - /** The name of the documentation file related to the module. */ | ||
24 | - helpFile : 'epnTapHelp', | ||
25 | - | ||
26 | - /** | ||
27 | - Module initialisation. Called the first time that the user open the epnTap module. | ||
28 | - */ | ||
29 | - init: function() { | ||
30 | - this.launcher = { | ||
31 | - text: this.title, | ||
32 | - iconCls: this.icon, | ||
33 | - handler: this.createWindow, | ||
34 | - scope: this | ||
35 | - }; | ||
36 | - }, | ||
37 | - | ||
38 | - /** | ||
39 | - Called each time the epntap module is loaded. | ||
40 | - - `target`: an array of 3 values: [target_name, dataproduct_type]; or null. | ||
41 | - */ | ||
42 | - loadTarget: function(filter) { | ||
43 | - this.aquireElements(); | ||
44 | - this.addListeners(); | ||
45 | - | ||
46 | - this.servicesStore.each(function(record) { | ||
47 | - record.set('nb_results', -1); | ||
48 | - }, this); | ||
49 | - this.granulesStore.removeAll(); | ||
50 | - | ||
51 | - if(filter) { | ||
52 | - this.targetNameCB.setRawValue(filter['targetName']); | ||
53 | - this.productTypeCB.select(filter['productType']); | ||
54 | - this.getServices(); | ||
55 | - } | ||
56 | - }, | ||
57 | - | ||
58 | - aquireElements: function() { | ||
59 | - // UI elements | ||
60 | - this.servicesGrid = Ext.getCmp('epnTapServicesGrid'); | ||
61 | - this.granulesGrid = Ext.getCmp('epnTapGranulesGrid'); | ||
62 | - this.productTypeCB = Ext.getCmp('epnTapProductTypeCB'); | ||
63 | - this.targetNameCB = Ext.getCmp('epnTapTargetNameCB'); | ||
64 | - this.timeSelector = Ext.getCmp('epnTapTimeSelector'); | ||
65 | - this.getBtn = Ext.getCmp('epnTapGetBtn'); | ||
66 | - | ||
67 | - // stores elements | ||
68 | - this.servicesStore = Ext.data.StoreManager.lookup('servicesStore'); | ||
69 | - this.granulesStore = Ext.data.StoreManager.lookup('granulesStore'); | ||
70 | - this.productTypesStore = Ext.data.StoreManager.lookup('productTypesStore'); | ||
71 | - this.targetNamesStore = Ext.data.StoreManager.lookup('targetNamesStore'); | ||
72 | - }, | ||
73 | - | ||
74 | - addListeners: function() { | ||
75 | - this.targetNameCB.on('change', function() { | ||
76 | - this.updateGetBtnStatus(); | ||
77 | - }, this); | ||
78 | - | ||
79 | - this.productTypeCB.on('change', function() { | ||
80 | - this.updateGetBtnStatus(); | ||
81 | - }, this); | ||
82 | - | ||
83 | - this.servicesGrid.on('cellclick', function(grid, td, cellIndex, record) { | ||
84 | - this.onServiceSelected(record); | ||
85 | - }, this); | ||
86 | - | ||
87 | - this.getBtn.on('click', function() { | ||
88 | - this.getServices(); | ||
89 | - }, this); | ||
90 | - }, | ||
91 | - | ||
92 | - /********************** | ||
93 | - *** Utils functions *** | ||
94 | - **********************/ | ||
95 | - | ||
96 | - updateGetBtnStatus: function() { | ||
97 | - var shouldEnabled = this.targetNameCB.rawValue.length > 0 && this.productTypeCB.rawValue.length > 0; | ||
98 | - if(shouldEnabled) { | ||
99 | - this.getBtn.enable(); | ||
100 | - } else { | ||
101 | - this.getBtn.disable(); | ||
102 | - } | ||
103 | - }, | ||
104 | - | ||
105 | - /************* | ||
106 | - *** Events *** | ||
107 | - *************/ | ||
108 | - | ||
109 | - /** | ||
110 | - Trigerred when the 'Get results' button is clicked. | ||
111 | - */ | ||
112 | - getServices: function() { | ||
113 | - this.granulesStore.removeAll(); | ||
114 | - var targetName = this.targetNameCB.rawValue; | ||
115 | - var productTypes = this.productTypeCB.value.join(';'); | ||
116 | - var timeMin = Ext.Date.format(this.timeSelector.getStartTime(), 'd/m/Y H:i:s'); // start time | ||
117 | - var timeMax = Ext.Date.format(this.timeSelector.getStopTime(), 'd/m/Y H:i:s'); // stop time | ||
118 | - | ||
119 | - loadMask.show(); | ||
120 | - this.servicesStore.each(function(record) { | ||
121 | - // TODO: use store.load() method instead and add 'success' and 'enable' columns in the store | ||
122 | - Ext.Ajax.request({ | ||
123 | - url: 'php/epntap.php', | ||
124 | - method: 'GET', | ||
125 | - headers: {'Content-Type': 'application/json'}, | ||
126 | - params: { | ||
127 | - 'action': 'getNbResults', | ||
128 | - 'serviceId': record.data['id'], | ||
129 | - 'url': record.data['access_url'], | ||
130 | - 'tableName': record.data['table_name'], | ||
131 | - 'targetNames': targetName, | ||
132 | - 'productTypes': productTypes, | ||
133 | - 'timeMin': timeMin, | ||
134 | - 'timeMax': timeMax | ||
135 | - }, | ||
136 | - // timeout: 3000, | ||
137 | - success: function(response, options) { | ||
138 | - var record = this.servicesStore.getById(options.params['serviceId']); | ||
139 | - var responseObj = Ext.decode(response.responseText); | ||
140 | - this.updateService(record, responseObj['success'] ? responseObj['data'] : -2, responseObj['msg']); | ||
141 | - }, | ||
142 | - failure: function(response, options) { | ||
143 | - var record = this.servicesStore.getById(options.params['serviceId']); | ||
144 | - this.updateService(record, -1, response.statusText); | ||
145 | - }, | ||
146 | - scope: this | ||
147 | - }); | ||
148 | - }, this); | ||
149 | - }, | ||
150 | - | ||
151 | - /** | ||
152 | - Update the nb_result field of the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`. | ||
153 | - */ | ||
154 | - updateService: function(record, nbRes, info) { | ||
155 | - record.set('nb_results', nbRes); | ||
156 | - record.set('info', info); | ||
157 | - this.servicesStore.sort(); | ||
158 | - loadMask.hide(); | ||
159 | - }, | ||
160 | - | ||
161 | - /** | ||
162 | - Trigerred when a row is clicked in `servicesGrid` table (see `EpnTapUI.createServicesGrid()`). Among other things, | ||
163 | - send a new query and fill `granulesGrid`. | ||
164 | - */ | ||
165 | - onServiceSelected: function(record) { | ||
166 | - this.granulesStore.selectedService = record.data.id; | ||
167 | - Ext.Ajax.suspendEvent('requestexception'); | ||
168 | - Ext.Ajax.abortAll(); | ||
169 | - | ||
170 | - if(record.get('nb_results') > 0) { | ||
171 | - this.granulesStore.removeAll(); | ||
172 | - this.granulesStore.load({ | ||
173 | - callback: function (records, operation, success) { | ||
174 | - Ext.Ajax.resumeEvents('requestexception'); | ||
175 | - // console.log(Ext.decode(operation.response.responseText)); | ||
176 | - }, | ||
177 | - start: 0, | ||
178 | - limit: this.granulesStore.pageSize, | ||
179 | - scope: this | ||
180 | - }); | ||
181 | - } | ||
182 | - } | ||
183 | -}); | 12 | + extend: 'amdaDesktop.AmdaModule', |
13 | + requires: ['amdaUI.EpnTapUI'], | ||
14 | + contentId: 'EpnTapUI', | ||
15 | + | ||
16 | + /** The alias name of the module view. */ | ||
17 | + // uiType: 'panelEpnTap', | ||
18 | + uiType: 'panelEpnTap', | ||
19 | + | ||
20 | + /** The text displayed on the *help button* tooltip. */ | ||
21 | + helpTitle: 'Help on EPN-TAP Module', | ||
22 | + | ||
23 | + /** The name of the documentation file related to the module. */ | ||
24 | + helpFile: 'epnTapHelp', | ||
25 | + | ||
26 | + /** | ||
27 | + Module initialisation. Called the first time that the user open the epnTap module. | ||
28 | + */ | ||
29 | + init: function () { | ||
30 | + this.launcher = { | ||
31 | + text: this.title, | ||
32 | + iconCls: this.icon, | ||
33 | + handler: this.createWindow, | ||
34 | + scope: this | ||
35 | + } | ||
36 | + }, | ||
37 | + | ||
38 | + /** | ||
39 | + Called each time the epntap module is loaded. | ||
40 | + - `target`: an array of 3 values: [target_name, dataproduct_type]; or null. | ||
41 | + */ | ||
42 | + loadTarget: function (filter) { | ||
43 | + this.aquireElements() | ||
44 | + this.addListeners() | ||
45 | + | ||
46 | + this.servicesStore.each(function (record) { | ||
47 | + record.set('nb_results', -1) | ||
48 | + }, this) | ||
49 | + this.granulesStore.removeAll() | ||
50 | + | ||
51 | + if (filter) { | ||
52 | + this.targetNameCB.setRawValue(filter['targetName']) | ||
53 | + this.productTypeCB.select(filter['productType']) | ||
54 | + this.getServices() | ||
55 | + } | ||
56 | + }, | ||
57 | + | ||
58 | + aquireElements: function () { | ||
59 | + // UI elements | ||
60 | + this.servicesGrid = Ext.getCmp('epnTapServicesGrid') | ||
61 | + this.granulesGrid = Ext.getCmp('epnTapGranulesGrid') | ||
62 | + this.productTypeCB = Ext.getCmp('epnTapProductTypeCB') | ||
63 | + this.targetNameCB = Ext.getCmp('epnTapTargetNameCB') | ||
64 | + this.timeSelector = Ext.getCmp('epnTapTimeSelector') | ||
65 | + this.getBtn = Ext.getCmp('epnTapGetBtn') | ||
66 | + | ||
67 | + // stores elements | ||
68 | + this.servicesStore = Ext.data.StoreManager.lookup('servicesStore') | ||
69 | + this.granulesStore = Ext.data.StoreManager.lookup('granulesStore') | ||
70 | + this.productTypesStore = Ext.data.StoreManager.lookup('productTypesStore') | ||
71 | + this.targetNamesStore = Ext.data.StoreManager.lookup('targetNamesStore') | ||
72 | + }, | ||
73 | + | ||
74 | + addListeners: function () { | ||
75 | + this.targetNameCB.on('change', function () { | ||
76 | + this.updateGetBtnStatus() | ||
77 | + }, this) | ||
78 | + | ||
79 | + this.productTypeCB.on('change', function () { | ||
80 | + this.updateGetBtnStatus() | ||
81 | + }, this) | ||
82 | + | ||
83 | + this.servicesGrid.on('cellclick', function (grid, td, cellIndex, record) { | ||
84 | + this.onServiceSelected(record) | ||
85 | + }, this) | ||
86 | + | ||
87 | + this.getBtn.on('click', function () { | ||
88 | + this.getServices() | ||
89 | + }, this) | ||
90 | + }, | ||
91 | + | ||
92 | + /********************** | ||
93 | + *** Utils functions *** | ||
94 | + **********************/ | ||
95 | + | ||
96 | + updateGetBtnStatus: function () { | ||
97 | + var shouldEnabled = this.targetNameCB.rawValue.length > 0 && this.productTypeCB.rawValue.length > 0 | ||
98 | + if (shouldEnabled) { | ||
99 | + this.getBtn.enable() | ||
100 | + } else { | ||
101 | + this.getBtn.disable() | ||
102 | + } | ||
103 | + }, | ||
104 | + | ||
105 | + /************* | ||
106 | + *** Events *** | ||
107 | + *************/ | ||
108 | + | ||
109 | + /** | ||
110 | + Trigerred when the 'Get results' button is clicked. | ||
111 | + */ | ||
112 | + getServices: function () { | ||
113 | + this.granulesStore.removeAll() | ||
114 | + var targetName = this.targetNameCB.rawValue | ||
115 | + var productTypes = this.productTypeCB.value.join(';') | ||
116 | + var timeMin = Ext.Date.format(this.timeSelector.getStartTime(), 'd/m/Y H:i:s') // start time | ||
117 | + var timeMax = Ext.Date.format(this.timeSelector.getStopTime(), 'd/m/Y H:i:s') // stop time | ||
118 | + | ||
119 | + loadMask.show() | ||
120 | + this.servicesStore.each(function (record) { | ||
121 | + // TODO: use store.load() method instead and add 'success' and 'enable' columns in the store | ||
122 | + Ext.Ajax.request({ | ||
123 | + url: 'php/epntap.php', | ||
124 | + method: 'GET', | ||
125 | + headers: {'Content-Type': 'application/json'}, | ||
126 | + params: { | ||
127 | + 'action': 'getNbResults', | ||
128 | + 'serviceId': record.data['id'], | ||
129 | + 'url': record.data['access_url'], | ||
130 | + 'tableName': record.data['table_name'], | ||
131 | + 'targetNames': targetName, | ||
132 | + 'productTypes': productTypes, | ||
133 | + 'timeMin': timeMin, | ||
134 | + 'timeMax': timeMax | ||
135 | + }, | ||
136 | + // timeout: 3000, | ||
137 | + success: function (response, options) { | ||
138 | + var record = this.servicesStore.getById(options.params['serviceId']) | ||
139 | + var responseObj = Ext.decode(response.responseText) | ||
140 | + this.updateService(record, responseObj['success'] ? responseObj['data'] : -2, responseObj['msg']) | ||
141 | + }, | ||
142 | + failure: function (response, options) { | ||
143 | + var record = this.servicesStore.getById(options.params['serviceId']) | ||
144 | + this.updateService(record, -1, response.statusText) | ||
145 | + }, | ||
146 | + scope: this | ||
147 | + }) | ||
148 | + }, this) | ||
149 | + }, | ||
150 | + | ||
151 | + /** | ||
152 | + Update the nb_result field of the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`. | ||
153 | + */ | ||
154 | + updateService: function (record, nbRes, info) { | ||
155 | + record.set('nb_results', nbRes) | ||
156 | + record.set('info', info) | ||
157 | + this.servicesStore.sort() | ||
158 | + loadMask.hide() | ||
159 | + }, | ||
160 | + | ||
161 | + /** | ||
162 | + Trigerred when a row is clicked in `servicesGrid` table (see `EpnTapUI.createServicesGrid()`). Among other things, | ||
163 | + send a new query and fill `granulesGrid`. | ||
164 | + */ | ||
165 | + onServiceSelected: function (record) { | ||
166 | + this.granulesStore.selectedService = record.data.id | ||
167 | + Ext.Ajax.suspendEvent('requestexception') | ||
168 | + Ext.Ajax.abortAll() | ||
169 | + | ||
170 | + if (record.get('nb_results') > 0) { | ||
171 | + this.granulesStore.removeAll() | ||
172 | + this.granulesStore.load({ | ||
173 | + callback: function (records, operation, success) { | ||
174 | + Ext.Ajax.resumeEvents('requestexception') | ||
175 | + // console.log(Ext.decode(operation.response.responseText)); | ||
176 | + }, | ||
177 | + start: 0, | ||
178 | + limit: this.granulesStore.pageSize, | ||
179 | + scope: this | ||
180 | + }) | ||
181 | + } | ||
182 | + } | ||
183 | +}) |
js/app/views/EpnTapUI.js
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | * 24/10/2016: file creation | 7 | * 24/10/2016: file creation |
8 | */ | 8 | */ |
9 | 9 | ||
10 | -Ext.require(['Ext.grid.plugin.BufferedRenderer']); | 10 | +Ext.require(['Ext.grid.plugin.BufferedRenderer']) |
11 | /** | 11 | /** |
12 | `productTypesStore`: An ExtJS Store containing the list of the different data product types defined on all granules, on | 12 | `productTypesStore`: An ExtJS Store containing the list of the different data product types defined on all granules, on |
13 | all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron | 13 | all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron |
@@ -16,7 +16,7 @@ script). | @@ -16,7 +16,7 @@ script). | ||
16 | This list is used to fill the `productTypeCB` combo box, which is initilized in `EpnTapModule` at the panel creation. | 16 | This list is used to fill the `productTypeCB` combo box, which is initilized in `EpnTapModule` at the panel creation. |
17 | 17 | ||
18 | - `id`: the data product type IDs, according to the EPN-TAP specification (see | 18 | - `id`: the data product type IDs, according to the EPN-TAP specification (see |
19 | - https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=1148225); | 19 | + https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=1148225); |
20 | - `name`: the data product name, according to the EPN-TAP specification (ibid). | 20 | - `name`: the data product name, according to the EPN-TAP specification (ibid). |
21 | 21 | ||
22 | These IDs and names are hard-defined in the JSon file `generic_data/EpnTapData/dataproduct_types.json`. | 22 | These IDs and names are hard-defined in the JSon file `generic_data/EpnTapData/dataproduct_types.json`. |
@@ -28,26 +28,26 @@ in this store and an information message is displayed on the JavaScript console | @@ -28,26 +28,26 @@ in this store and an information message is displayed on the JavaScript console | ||
28 | store. | 28 | store. |
29 | */ | 29 | */ |
30 | Ext.create('Ext.data.Store', { | 30 | Ext.create('Ext.data.Store', { |
31 | - storeId: 'productTypesStore', | ||
32 | - autoLoad: true, | ||
33 | - fields: ['id', 'name', 'desc'], | ||
34 | - data: [ | ||
35 | - {'id': 'all', 'name': '--All--', 'desc': 'Select all produt types.'}, | ||
36 | - {'id': 'clear', 'name': '--Clear--', 'desc': 'Clear the selection.'}, | ||
37 | - {'id': 'im', 'name': 'Image', 'desc': '2D series of values depending on 2 spatial axes, with measured parameters.'}, | ||
38 | - {'id': 'ma', 'name': 'Map', 'desc': '2D series of values depending on 2 spatial axes, with derived parameters.'}, | ||
39 | - {'id': 'sp', 'name': 'Spectrum', 'desc': '1D series of values depending on a spectral axis (or Frequency, Energy, Mass,...).'}, | ||
40 | - {'id': 'ds', 'name': 'Dynamic spectrum', 'desc': '2D series of values depending on time and on a spectral axis (Frequency, Energy, Mass,...), FoV is homogeneous.'}, | ||
41 | - {'id': 'sc', 'name': 'Spectral cube', 'desc': '3D series of values depending on 2 spatial axes and on a spectral axis (Frequency, Energy, Mass,..).'}, | ||
42 | - {'id': 'pr', 'name': 'Profile', 'desc': '1D series of values depending on a spatial axis.'}, | ||
43 | - {'id': 'vo', 'name': 'Volume', 'desc': '3D series of values depending on 3 spatial axes (spatial coordinates or tabulated values in a volumic grid).'}, | ||
44 | - {'id': 'mo', 'name': 'Movie', 'desc': '3D series of values depending on 2 spatial axes and on time.'}, | ||
45 | - // {'id': 'cu', 'name': 'Cube', 'desc': '.'}, | ||
46 | - {'id': 'ts', 'name': 'Time series', 'desc': '1D series of values depending on time.'}, | ||
47 | - {'id': 'ca', 'name': 'Catalogue', 'desc': '1D list of elements.'}, | ||
48 | - {'id': 'ci', 'name': 'Catalogue item', 'desc': '0D list of elements.'} | ||
49 | - ] | ||
50 | -}); | 31 | + storeId: 'productTypesStore', |
32 | + autoLoad: true, | ||
33 | + fields: ['id', 'name', 'desc'], | ||
34 | + data: [ | ||
35 | + {'id': 'all', 'name': '--All--', 'desc': 'Select all produt types.'}, | ||
36 | + {'id': 'clear', 'name': '--Clear--', 'desc': 'Clear the selection.'}, | ||
37 | + {'id': 'im', 'name': 'Image', 'desc': '2D series of values depending on 2 spatial axes, with measured parameters.'}, | ||
38 | + {'id': 'ma', 'name': 'Map', 'desc': '2D series of values depending on 2 spatial axes, with derived parameters.'}, | ||
39 | + {'id': 'sp', 'name': 'Spectrum', 'desc': '1D series of values depending on a spectral axis (or Frequency, Energy, Mass,...).'}, | ||
40 | + {'id': 'ds', 'name': 'Dynamic spectrum', 'desc': '2D series of values depending on time and on a spectral axis (Frequency, Energy, Mass,...), FoV is homogeneous.'}, | ||
41 | + {'id': 'sc', 'name': 'Spectral cube', 'desc': '3D series of values depending on 2 spatial axes and on a spectral axis (Frequency, Energy, Mass,..).'}, | ||
42 | + {'id': 'pr', 'name': 'Profile', 'desc': '1D series of values depending on a spatial axis.'}, | ||
43 | + {'id': 'vo', 'name': 'Volume', 'desc': '3D series of values depending on 3 spatial axes (spatial coordinates or tabulated values in a volumic grid).'}, | ||
44 | + {'id': 'mo', 'name': 'Movie', 'desc': '3D series of values depending on 2 spatial axes and on time.'}, | ||
45 | + // {'id': 'cu', 'name': 'Cube', 'desc': '.'}, | ||
46 | + {'id': 'ts', 'name': 'Time series', 'desc': '1D series of values depending on time.'}, | ||
47 | + {'id': 'ca', 'name': 'Catalogue', 'desc': '1D list of elements.'}, | ||
48 | + {'id': 'ci', 'name': 'Catalogue item', 'desc': '0D list of elements.'} | ||
49 | + ] | ||
50 | +}) | ||
51 | 51 | ||
52 | /** | 52 | /** |
53 | `targetNamesStore`: An ExtJS Store containing the list of the different target names defined on all granules, on | 53 | `targetNamesStore`: An ExtJS Store containing the list of the different target names defined on all granules, on |
@@ -61,23 +61,23 @@ This list is used to fill the `targetNameCB` combo box, which is updated by `Epn | @@ -61,23 +61,23 @@ This list is used to fill the `targetNameCB` combo box, which is updated by `Epn | ||
61 | - `name`: the target name, capitalized with spaces between each word (done `EpnTapModule.prettify()`). | 61 | - `name`: the target name, capitalized with spaces between each word (done `EpnTapModule.prettify()`). |
62 | */ | 62 | */ |
63 | Ext.create('Ext.data.Store', { | 63 | Ext.create('Ext.data.Store', { |
64 | - storeId: 'targetNamesStore', | ||
65 | - fields: ['id', 'text', 'name', 'type', 'parent', 'aliases'], | ||
66 | - proxy: { | ||
67 | - type: 'ajax', | ||
68 | - url: 'php/epntap.php', | ||
69 | - extraParams: { action: 'resolver' } | ||
70 | - }, | ||
71 | - errorDisplayed: false, | ||
72 | - listeners: { | ||
73 | - load: function(store, records, successful) { | ||
74 | - if(!successful && !store.errorDisplayed) { | ||
75 | - Ext.Msg.alert('Error', 'Can not load results from the resolver. Please enter target names manually.'); | ||
76 | - store.errorDisplayed = true; | ||
77 | - } | ||
78 | - } | ||
79 | - } | ||
80 | -}); | 64 | + storeId: 'targetNamesStore', |
65 | + fields: ['id', 'text', 'name', 'type', 'parent', 'aliases'], | ||
66 | + proxy: { | ||
67 | + type: 'ajax', | ||
68 | + url: 'php/epntap.php', | ||
69 | + extraParams: { action: 'resolver' } | ||
70 | + }, | ||
71 | + errorDisplayed: false, | ||
72 | + listeners: { | ||
73 | + load: function (store, records, successful) { | ||
74 | + if (!successful && !store.errorDisplayed) { | ||
75 | + Ext.Msg.alert('Error', 'Can not load results from the resolver. Please enter target names manually.') | ||
76 | + store.errorDisplayed = true | ||
77 | + } | ||
78 | + } | ||
79 | + } | ||
80 | +}) | ||
81 | 81 | ||
82 | /** | 82 | /** |
83 | `servicesStore`: An ExtJS Store containing the list of the EPN-TAP services (defined in | 83 | `servicesStore`: An ExtJS Store containing the list of the EPN-TAP services (defined in |
@@ -88,51 +88,51 @@ This list is used to fill the `servicesGrid` table, which is updated by `EpnTapM | @@ -88,51 +88,51 @@ This list is used to fill the `servicesGrid` table, which is updated by `EpnTapM | ||
88 | (or, by transitivity, target class or product type) is selected. | 88 | (or, by transitivity, target class or product type) is selected. |
89 | 89 | ||
90 | - `id`: the database name of the service, according to the `table_name` column from the `rr.res_table` in the | 90 | - `id`: the database name of the service, according to the `table_name` column from the `rr.res_table` in the |
91 | - registry database; | 91 | + registry database; |
92 | - `nbResults`: the number of granules matching with the granules filter for this service; | 92 | - `nbResults`: the number of granules matching with the granules filter for this service; |
93 | - `shortName`: the service short name, according to the `short_name` column from the `rr.resource` table in the registry | 93 | - `shortName`: the service short name, according to the `short_name` column from the `rr.resource` table in the registry |
94 | - database; | 94 | + database; |
95 | - `title`: the service title, according to the `res_title` column from the `rr.resource` table in the registry database; | 95 | - `title`: the service title, according to the `res_title` column from the `rr.resource` table in the registry database; |
96 | - `accessURL`: the service access URL, according to the `access_url` column from the `rr.interface` table in the | 96 | - `accessURL`: the service access URL, according to the `access_url` column from the `rr.interface` table in the |
97 | - registry database. | 97 | + registry database. |
98 | */ | 98 | */ |
99 | Ext.create('Ext.data.Store', { | 99 | Ext.create('Ext.data.Store', { |
100 | - storeId: 'servicesStore', | ||
101 | - autoLoad: true, | ||
102 | - fields: [ | ||
103 | - {name: 'id', type: 'string'}, | ||
104 | - {name: 'short_name', type: 'string'}, | ||
105 | - {name: 'res_title', type: 'string'}, | ||
106 | - {name: 'ivoid', type: 'string'}, | ||
107 | - {name: 'access_url', type: 'string'}, | ||
108 | - {name: 'table_name', type: 'string'}, | ||
109 | - {name: 'content_type', type: 'string'}, | ||
110 | - {name: 'creator_seq', type: 'string'}, | ||
111 | - {name: 'content_level', type: 'string'}, | ||
112 | - {name: 'reference_url', type: 'string'}, | ||
113 | - {name: 'created', type: 'date', dateFormat: 'c'}, | ||
114 | - {name: 'updated', type: 'date', dateFormat: 'c'}, | ||
115 | - {name: 'nb_results', type: 'integer'}, | ||
116 | - {name: 'info', type: 'string'} | ||
117 | - ], | ||
118 | - proxy: { | ||
119 | - type: 'ajax', | ||
120 | - url: 'php/epntap.php', | ||
121 | - extraParams : {action: 'getServices'} | ||
122 | - }, | ||
123 | - sorters: [ | ||
124 | - {property: 'nb_results', direction: 'DESC'}, | ||
125 | - {property: 'short_name', direction: 'ASC'} | ||
126 | - ], | ||
127 | - listeners: { | ||
128 | - // beforeload: function(s, operation) { console.log(operation); }, | ||
129 | - load: function(store, records, successful) { | ||
130 | - if(!successful) { | ||
131 | - Ext.Msg.alert('Error', 'Can not get epntap services from registries.'); | ||
132 | - } | ||
133 | - } | ||
134 | - } | ||
135 | -}); | 100 | + storeId: 'servicesStore', |
101 | + autoLoad: true, | ||
102 | + fields: [ | ||
103 | + {name: 'id', type: 'string'}, | ||
104 | + {name: 'short_name', type: 'string'}, | ||
105 | + {name: 'res_title', type: 'string'}, | ||
106 | + {name: 'ivoid', type: 'string'}, | ||
107 | + {name: 'access_url', type: 'string'}, | ||
108 | + {name: 'table_name', type: 'string'}, | ||
109 | + {name: 'content_type', type: 'string'}, | ||
110 | + {name: 'creator_seq', type: 'string'}, | ||
111 | + {name: 'content_level', type: 'string'}, | ||
112 | + {name: 'reference_url', type: 'string'}, | ||
113 | + {name: 'created', type: 'date', dateFormat: 'c'}, | ||
114 | + {name: 'updated', type: 'date', dateFormat: 'c'}, | ||
115 | + {name: 'nb_results', type: 'integer'}, | ||
116 | + {name: 'info', type: 'string'} | ||
117 | + ], | ||
118 | + proxy: { | ||
119 | + type: 'ajax', | ||
120 | + url: 'php/epntap.php', | ||
121 | + extraParams: {action: 'getServices'} | ||
122 | + }, | ||
123 | + sorters: [ | ||
124 | + {property: 'nb_results', direction: 'DESC'}, | ||
125 | + {property: 'short_name', direction: 'ASC'} | ||
126 | + ], | ||
127 | + listeners: { | ||
128 | + // beforeload: function(s, operation) { console.log(operation); }, | ||
129 | + load: function (store, records, successful) { | ||
130 | + if (!successful) { | ||
131 | + Ext.Msg.alert('Error', 'Can not get epntap services from registries.') | ||
132 | + } | ||
133 | + } | ||
134 | + } | ||
135 | +}) | ||
136 | 136 | ||
137 | /** | 137 | /** |
138 | `granulesStore`: An ExtJS Store containing the list of granules of the selected service (on `servicesGrid`), which match | 138 | `granulesStore`: An ExtJS Store containing the list of granules of the selected service (on `servicesGrid`), which match |
@@ -143,7 +143,7 @@ selected. | @@ -143,7 +143,7 @@ selected. | ||
143 | 143 | ||
144 | - `num`: the line number, according to the order of the query response and the current page (see `currentPageLb`); | 144 | - `num`: the line number, according to the order of the query response and the current page (see `currentPageLb`); |
145 | - `dataproduct_type`: the dataproduct_type EPN-TAP parameter, as defined in | 145 | - `dataproduct_type`: the dataproduct_type EPN-TAP parameter, as defined in |
146 | - https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. | 146 | + https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. |
147 | - `target_name`: the target_name EPN-TAP parameter (ibid); | 147 | - `target_name`: the target_name EPN-TAP parameter (ibid); |
148 | - `time_min`: the time_min EPN-TAP parameter (ibid); | 148 | - `time_min`: the time_min EPN-TAP parameter (ibid); |
149 | - `time_max`: the time_max EPN-TAP parameter (ibid); | 149 | - `time_max`: the time_max EPN-TAP parameter (ibid); |
@@ -156,224 +156,224 @@ selected. | @@ -156,224 +156,224 @@ selected. | ||
156 | // TODO: Add granules filter (see http://docs.sencha.com/extjs/4.0.7/#!/example/grid-filtering/grid-filter-local.html) | 156 | // TODO: Add granules filter (see http://docs.sencha.com/extjs/4.0.7/#!/example/grid-filtering/grid-filter-local.html) |
157 | 157 | ||
158 | Ext.define('GranulesModel', { | 158 | Ext.define('GranulesModel', { |
159 | - extend: 'Ext.data.Model' | ||
160 | - // columns are created dynamically | ||
161 | -}); | 159 | + extend: 'Ext.data.Model' |
160 | + // columns are created dynamically | ||
161 | +}) | ||
162 | 162 | ||
163 | Ext.create('Ext.data.Store', { | 163 | Ext.create('Ext.data.Store', { |
164 | - storeId: 'granulesStore', | ||
165 | - model: 'GranulesModel', | ||
166 | - buffered: true, | ||
167 | - autoload: false, | ||
168 | - pageSize: 500, | ||
169 | - leadingBufferZone: 0, | ||
170 | - proxy: { | ||
171 | - type: 'ajax', | ||
172 | - url: 'php/epntap.php', | ||
173 | - reader: { type: 'json', root: 'data'}, | ||
174 | - simpleSortMode: true | ||
175 | - }, | ||
176 | - listeners: { | ||
177 | - 'beforeprefetch': function(store, operation) { | ||
178 | - var service = Ext.data.StoreManager.lookup('servicesStore').getById(store.selectedService).data; | ||
179 | - store.getProxy().extraParams = { | ||
180 | - 'action': 'getGranules', | ||
181 | - 'url': service['access_url'], | ||
182 | - 'tableName': service['table_name'], | ||
183 | - 'targetNames': Ext.getCmp('epnTapTargetNameCB').rawValue, | ||
184 | - 'productTypes': Ext.getCmp('epnTapProductTypeCB').value.join(';'), | ||
185 | - 'timeMin': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStartTime(), 'd/m/Y H:i:s'), | ||
186 | - 'timeMax': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStopTime(), 'd/m/Y H:i:s'), | ||
187 | - 'nbRes': service['nb_results'] | ||
188 | - }; | ||
189 | - }, | ||
190 | - 'prefetch': function(store, records, successful, operation) { | ||
191 | - // console.log('(prefetch) operation ' + (successful ? 'success' : 'failed') + ': ', operation); | ||
192 | - // console.log(operation.params); | ||
193 | - // console.log(Ext.decode(operation.response.responseText)); | ||
194 | - }, | ||
195 | - 'metachange': function(store, meta) { | ||
196 | - if(meta.metaHash != store.metaHash) { | ||
197 | - Ext.getCmp('epnTapGranulesGrid').reconfigure(store, meta.columns); | ||
198 | - store.metaHash = meta.metaHash; | ||
199 | - } | ||
200 | - } | ||
201 | - } | ||
202 | -}); | 164 | + storeId: 'granulesStore', |
165 | + model: 'GranulesModel', | ||
166 | + buffered: true, | ||
167 | + autoload: false, | ||
168 | + pageSize: 500, | ||
169 | + leadingBufferZone: 0, | ||
170 | + proxy: { | ||
171 | + type: 'ajax', | ||
172 | + url: 'php/epntap.php', | ||
173 | + reader: { type: 'json', root: 'data'}, | ||
174 | + simpleSortMode: true | ||
175 | + }, | ||
176 | + listeners: { | ||
177 | + 'beforeprefetch': function (store, operation) { | ||
178 | + var service = Ext.data.StoreManager.lookup('servicesStore').getById(store.selectedService).data | ||
179 | + store.getProxy().extraParams = { | ||
180 | + 'action': 'getGranules', | ||
181 | + 'url': service['access_url'], | ||
182 | + 'tableName': service['table_name'], | ||
183 | + 'targetNames': Ext.getCmp('epnTapTargetNameCB').rawValue, | ||
184 | + 'productTypes': Ext.getCmp('epnTapProductTypeCB').value.join(';'), | ||
185 | + 'timeMin': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStartTime(), 'd/m/Y H:i:s'), | ||
186 | + 'timeMax': Ext.Date.format(Ext.getCmp('epnTapTimeSelector').getStopTime(), 'd/m/Y H:i:s'), | ||
187 | + 'nbRes': service['nb_results'] | ||
188 | + } | ||
189 | + }, | ||
190 | + 'prefetch': function (store, records, successful, operation) { | ||
191 | + // console.log('(prefetch) operation ' + (successful ? 'success' : 'failed') + ': ', operation); | ||
192 | + // console.log(operation.params); | ||
193 | + // console.log(Ext.decode(operation.response.responseText)); | ||
194 | + }, | ||
195 | + 'metachange': function (store, meta) { | ||
196 | + if (meta.metaHash != store.metaHash) { | ||
197 | + Ext.getCmp('epnTapGranulesGrid').reconfigure(store, meta.columns) | ||
198 | + store.metaHash = meta.metaHash | ||
199 | + } | ||
200 | + } | ||
201 | + } | ||
202 | +}) | ||
203 | 203 | ||
204 | /** | 204 | /** |
205 | Error are not displayed here, use try/catch each time it's necessary. | 205 | Error are not displayed here, use try/catch each time it's necessary. |
206 | */ | 206 | */ |
207 | Ext.define('App.util.Format', { | 207 | Ext.define('App.util.Format', { |
208 | - override: 'Ext.util.Format', | ||
209 | - | ||
210 | - // Utils | ||
211 | - | ||
212 | - 'prettify': function(data) { | ||
213 | - return data.charAt(0).toUpperCase() + data.replace(/_/g, ' ').substr(1).toLowerCase(); | ||
214 | - }, | ||
215 | - 'url': function(data) { | ||
216 | - var url_pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol | ||
217 | - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|'+ // domain name | ||
218 | - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address | ||
219 | - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path | ||
220 | - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string | ||
221 | - '(\\#[-a-z\\d_]*)?$','i'); // fragment locator | ||
222 | - return url_pattern.test(data) ? data : null; | ||
223 | - }, | ||
224 | - 'cell': function(content, tooltip, tooltipTitle) { | ||
225 | - var ttAttr = ""; | ||
226 | - if(tooltip !== '') { | ||
227 | - var ttTitle = tooltipTitle ? " data-qtitle='" + tooltipTitle + "'" : ""; | ||
228 | - ttAttr = ttTitle + "' data-qtip='" + (tooltip ? tooltip : (content ? content : 'No value.')) + "'"; | ||
229 | - } | ||
230 | - return "<div class=epntap_cell " + ttAttr + ">" + (content ? content : '-') + "</div>"; | ||
231 | - }, | ||
232 | - | ||
233 | - // Services grid | ||
234 | - | ||
235 | - 'serviceTooltip': function(data) { | ||
236 | - for (var key in data) { | ||
237 | - if(typeof data[key] == 'string' && data[key] != '') { | ||
238 | - data[key] = data[key].replace(/'/g, ''').replace(/"/g, '"'); | ||
239 | - } | ||
240 | - } | ||
241 | - var infoColor = data.nb_results == -2 ? 'IndianRed' : 'green'; | ||
242 | - var info = data.info.length > 0 ? '<p style="color:' + infoColor + '">' + data.info + '</p>' : ''; | ||
243 | - | ||
244 | - var colums = ['res_title', 'ivoid', 'access_url', 'table_name', 'content_type', 'creator_seq', 'content_level', 'reference_url', 'created', 'updated']; | ||
245 | - var details = ''; | ||
246 | - for (var key in colums) { | ||
247 | - if(data[colums[key]] !== '') { | ||
248 | - var val = colums[key] === 'content_level' ? data[colums[key]].replace(/#/g, ', ') : data[colums[key]]; | ||
249 | - details += '<li><b>' + Ext.util.Format.prettify(colums[key]) + '</b>: ' + val + '</li>'; | ||
250 | - } | ||
251 | - } | ||
252 | - return info + '<ul>' + details + '</ul>'; | ||
253 | - }, | ||
254 | - 'service.text': function(data, metadata, record) { | ||
255 | - return Ext.util.Format.cell(data, Ext.util.Format.serviceTooltip(record.data), data); | ||
256 | - }, | ||
257 | - 'service.number': function(data, metadata, record) { | ||
258 | - value = '' + data; | ||
259 | - if(data < 0) { | ||
260 | - value = '-'; | ||
261 | - } else if(data >= 1000*1000) { | ||
262 | - value = (data/(1000*1000)).toPrecision(3) + 'm'; | ||
263 | - } else if(data >= 1000) { | ||
264 | - value = (data/1000).toPrecision(3) + 'k'; | ||
265 | - } | ||
266 | - return Ext.util.Format.cell(value, Ext.util.Format.serviceTooltip(record.data), record.data.short_name); | ||
267 | - }, | ||
268 | - | ||
269 | - // Granules grid | ||
270 | - | ||
271 | - 'granule.text': function(data, metadata, record) { | ||
272 | - return Ext.util.Format.cell(data); | ||
273 | - }, | ||
274 | - 'granule.link': function(data, metadata, record) { | ||
275 | - var icon_b64 = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxMDI0IiB3aWR0aD0iNzY4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik02NDAgNzY4SDEyOFYyNTcuOTA1OTk5OTk5OTk5OTVMMjU2IDI1NlYxMjhIMHY3NjhoNzY4VjU3Nkg2NDBWNzY4ek0zODQgMTI4bDEyOCAxMjhMMzIwIDQ0OGwxMjggMTI4IDE5Mi0xOTIgMTI4IDEyOFYxMjhIMzg0eiIvPjwvc3ZnPg=='; | ||
276 | - var icon = '<img style="width:15px;" alt="link" src="' + icon_b64 + '">'; | ||
277 | - url = Ext.util.Format.url(data); | ||
278 | - if(url) { | ||
279 | - txt = '<a style="font-size:150%" target="_blank" href="' + url + '">' + icon + '</a>'; | ||
280 | - } | ||
281 | - return Ext.util.Format.cell(txt, url); | ||
282 | - }, | ||
283 | - 'granule.img': function(data, metadata, record) { | ||
284 | - img_url = Ext.util.Format.url(data); | ||
285 | - if(img_url) { | ||
286 | - icon = '<img style="max-width:100%; max-height:100%" alt="-" src="' + img_url + '">'; | ||
287 | - img = '<img style="max-width:200px; max-height:200px" src="' + img_url + '">'; | ||
288 | - } | ||
289 | - return Ext.util.Format.cell(icon, img); | ||
290 | - }, | ||
291 | - 'granule.type': function(data, metadata, record) { | ||
292 | - var productTypeDict = Ext.data.StoreManager.lookup('productTypesStore').data.map; | ||
293 | - return Ext.util.Format.cell(productTypeDict[data].data.name); | ||
294 | - }, | ||
295 | - 'granule.size': function(data, metadata, record) { | ||
296 | - var size = parseInt(data); | ||
297 | - if (isNaN(size)) { | ||
298 | - } else if (size >= 1024*1024) { | ||
299 | - txt = (size/(1024*1024)).toPrecision(3) + 'Go'; | ||
300 | - } else if (size >= 1024) { | ||
301 | - txt = (size/1024).toPrecision(3) + 'Mo'; | ||
302 | - } else { | ||
303 | - txt = size + 'Ko'; | ||
304 | - } | ||
305 | - return Ext.util.Format.cell(txt); | ||
306 | - }, | ||
307 | - 'granule.proc_lvl': function(data, metadata, record) { | ||
308 | - var levels = {1: 'Raw', 2: 'Edited', 3: 'Calibrated', 4: 'Resampled', 5: 'Derived', 6: 'Ancillary'}; | ||
309 | - return Ext.util.Format.cell((data in levels) ? levels[data] : '<em>' + data + '</em>'); | ||
310 | - }, | ||
311 | - 'granule.date': function(data, metadata, record) { | ||
312 | - if(isNaN(data)) { | ||
313 | - return ''; | ||
314 | - } | ||
315 | - var f = Number(data) + 1401 + Math.floor((Math.floor((4 * Number(data) + 274277) / 146097) * 3) / 4) - 38; | ||
316 | - var e = 4 * f + 3; | ||
317 | - var g = Math.floor((e % 1461) / 4); | ||
318 | - var h = 5 * g + 2; | ||
319 | - var D = Math.floor((h % 153) / 5) + 1; | ||
320 | - var M = ((Math.floor(h / 153) + 2) % 12) + 1; | ||
321 | - var Y = Math.floor(e / 1461) - 4716 + Math.floor((12 + 2 - M) / 12); | ||
322 | - var date = new Date(Y, M-1, D); | ||
323 | - return Ext.util.Format.cell(Ext.Date.format(date, 'Y/m/d'), Ext.Date.format(date, 'F j, Y, g:i a')); | ||
324 | - }, | ||
325 | - 'granule.format': function(data, metadata, record) { | ||
326 | - var mimetypeDict = { | ||
327 | - 'application/fits': 'fits', | ||
328 | - 'application/x-pds': 'pds', | ||
329 | - 'image/x-pds': 'pds', | ||
330 | - 'application/gml+xml': 'gml', | ||
331 | - 'application/json': 'json', | ||
332 | - 'application/octet-stream': 'bin, idl, envi or matlab', | ||
333 | - 'application/pdf': 'pdf', | ||
334 | - 'application/postscript': 'ps', | ||
335 | - 'application/vnd.geo+json': 'geojson', | ||
336 | - 'application/vnd.google-earth.kml+xml': 'kml', | ||
337 | - 'application/vnd.google-earth.kmz': 'kmz', | ||
338 | - 'application/vnd.ms-excel': 'xls', | ||
339 | - 'application/x-asdm': 'asdm', | ||
340 | - 'application/x-cdf': 'cdf', | ||
341 | - 'application/x-cdf-istp': 'cdf', | ||
342 | - 'application/x-cdf-pds4': 'cdf', | ||
343 | - 'application/x-cef1': 'cef1', | ||
344 | - 'application/x-cef2': 'cef2', | ||
345 | - 'application/x-directory': 'dir', | ||
346 | - 'application/x-fits-bintable': 'bintable', | ||
347 | - 'application/x-fits-euro3d': 'euro3d', | ||
348 | - 'application/x-fits-mef': 'mef', | ||
349 | - 'application/x-geotiff': 'geotiff', | ||
350 | - 'application/x-hdf': 'hdf', | ||
351 | - 'application/x-netcdf': 'nc', | ||
352 | - 'application/x-netcdf4': 'nc', | ||
353 | - 'application/x-tar': 'tar', | ||
354 | - 'application/x-tar-gzip': 'gtar', | ||
355 | - 'application/x-votable+xml': 'votable', | ||
356 | - 'application/x-votable+xml;content=datalink': 'votable', | ||
357 | - 'application/zip': 'zip', | ||
358 | - 'image/fits': 'fits', | ||
359 | - 'image/gif': 'gif', | ||
360 | - 'image/jpeg': 'jpeg', | ||
361 | - 'image/png': 'png', | ||
362 | - 'image/tiff': 'tiff', | ||
363 | - 'image/x-fits-gzip': 'fits', | ||
364 | - 'image/x-fits-hcompress': 'fits', | ||
365 | - 'text/csv': 'csv', | ||
366 | - 'text/html': 'html', | ||
367 | - 'text/plain': 'txt', | ||
368 | - 'text/tab-separated-values': 'tsv', | ||
369 | - 'text/xml': 'xml', | ||
370 | - 'video/mpeg': 'mpeg', | ||
371 | - 'video/quicktime': 'mov', | ||
372 | - 'video/x-msvideo': 'avi' | ||
373 | - }; | ||
374 | - return Ext.util.Format.cell((data in mimetypeDict) ? '<p>' + mimetypeDict[data] + '</p>' : '<em>' + data + '</em>'); | ||
375 | - } | ||
376 | -}); | 208 | + override: 'Ext.util.Format', |
209 | + | ||
210 | + // Utils | ||
211 | + | ||
212 | + 'prettify': function (data) { | ||
213 | + return data.charAt(0).toUpperCase() + data.replace(/_/g, ' ').substr(1).toLowerCase() | ||
214 | + }, | ||
215 | + 'url': function (data) { | ||
216 | + var url_pattern = new RegExp('^(https?:\\/\\/)?' + // protocol | ||
217 | + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name | ||
218 | + '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address | ||
219 | + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path | ||
220 | + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string | ||
221 | + '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator | ||
222 | + return url_pattern.test(data) ? data : null | ||
223 | + }, | ||
224 | + 'cell': function (content, tooltip, tooltipTitle) { | ||
225 | + var ttAttr = '' | ||
226 | + if (tooltip !== '') { | ||
227 | + var ttTitle = tooltipTitle ? " data-qtitle='" + tooltipTitle + "'" : '' | ||
228 | + ttAttr = ttTitle + "' data-qtip='" + (tooltip || (content || 'No value.')) + "'" | ||
229 | + } | ||
230 | + return '<div class=epntap_cell ' + ttAttr + '>' + (content || '-') + '</div>' | ||
231 | + }, | ||
232 | + | ||
233 | + // Services grid | ||
234 | + | ||
235 | + 'serviceTooltip': function (data) { | ||
236 | + for (var key in data) { | ||
237 | + if (typeof data[key] === 'string' && data[key] != '') { | ||
238 | + data[key] = data[key].replace(/'/g, ''').replace(/"/g, '"') | ||
239 | + } | ||
240 | + } | ||
241 | + var infoColor = data.nb_results == -2 ? 'IndianRed' : 'green' | ||
242 | + var info = data.info.length > 0 ? '<p style="color:' + infoColor + '">' + data.info + '</p>' : '' | ||
243 | + | ||
244 | + var colums = ['res_title', 'ivoid', 'access_url', 'table_name', 'content_type', 'creator_seq', 'content_level', 'reference_url', 'created', 'updated'] | ||
245 | + var details = '' | ||
246 | + for (var key in colums) { | ||
247 | + if (data[colums[key]] !== '') { | ||
248 | + var val = colums[key] === 'content_level' ? data[colums[key]].replace(/#/g, ', ') : data[colums[key]] | ||
249 | + details += '<li><b>' + Ext.util.Format.prettify(colums[key]) + '</b>: ' + val + '</li>' | ||
250 | + } | ||
251 | + } | ||
252 | + return info + '<ul>' + details + '</ul>' | ||
253 | + }, | ||
254 | + 'service.text': function (data, metadata, record) { | ||
255 | + return Ext.util.Format.cell(data, Ext.util.Format.serviceTooltip(record.data), data) | ||
256 | + }, | ||
257 | + 'service.number': function (data, metadata, record) { | ||
258 | + value = '' + data | ||
259 | + if (data < 0) { | ||
260 | + value = '-' | ||
261 | + } else if (data >= 1000 * 1000) { | ||
262 | + value = (data / (1000 * 1000)).toPrecision(3) + 'm' | ||
263 | + } else if (data >= 1000) { | ||
264 | + value = (data / 1000).toPrecision(3) + 'k' | ||
265 | + } | ||
266 | + return Ext.util.Format.cell(value, Ext.util.Format.serviceTooltip(record.data), record.data.short_name) | ||
267 | + }, | ||
268 | + | ||
269 | + // Granules grid | ||
270 | + | ||
271 | + 'granule.text': function (data, metadata, record) { | ||
272 | + return Ext.util.Format.cell(data) | ||
273 | + }, | ||
274 | + 'granule.link': function (data, metadata, record) { | ||
275 | + var icon_b64 = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxMDI0IiB3aWR0aD0iNzY4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik02NDAgNzY4SDEyOFYyNTcuOTA1OTk5OTk5OTk5OTVMMjU2IDI1NlYxMjhIMHY3NjhoNzY4VjU3Nkg2NDBWNzY4ek0zODQgMTI4bDEyOCAxMjhMMzIwIDQ0OGwxMjggMTI4IDE5Mi0xOTIgMTI4IDEyOFYxMjhIMzg0eiIvPjwvc3ZnPg==' | ||
276 | + var icon = '<img style="width:15px;" alt="link" src="' + icon_b64 + '">' | ||
277 | + url = Ext.util.Format.url(data) | ||
278 | + if (url) { | ||
279 | + txt = '<a style="font-size:150%" target="_blank" href="' + url + '">' + icon + '</a>' | ||
280 | + } | ||
281 | + return Ext.util.Format.cell(txt, url) | ||
282 | + }, | ||
283 | + 'granule.img': function (data, metadata, record) { | ||
284 | + img_url = Ext.util.Format.url(data) | ||
285 | + if (img_url) { | ||
286 | + icon = '<img style="max-width:100%; max-height:100%" alt="-" src="' + img_url + '">' | ||
287 | + img = '<img style="max-width:200px; max-height:200px" src="' + img_url + '">' | ||
288 | + } | ||
289 | + return Ext.util.Format.cell(icon, img) | ||
290 | + }, | ||
291 | + 'granule.type': function (data, metadata, record) { | ||
292 | + var productTypeDict = Ext.data.StoreManager.lookup('productTypesStore').data.map | ||
293 | + return Ext.util.Format.cell(productTypeDict[data].data.name) | ||
294 | + }, | ||
295 | + 'granule.size': function (data, metadata, record) { | ||
296 | + var size = parseInt(data) | ||
297 | + if (isNaN(size)) { | ||
298 | + } else if (size >= 1024 * 1024) { | ||
299 | + txt = (size / (1024 * 1024)).toPrecision(3) + 'Go' | ||
300 | + } else if (size >= 1024) { | ||
301 | + txt = (size / 1024).toPrecision(3) + 'Mo' | ||
302 | + } else { | ||
303 | + txt = size + 'Ko' | ||
304 | + } | ||
305 | + return Ext.util.Format.cell(txt) | ||
306 | + }, | ||
307 | + 'granule.proc_lvl': function (data, metadata, record) { | ||
308 | + var levels = {1: 'Raw', 2: 'Edited', 3: 'Calibrated', 4: 'Resampled', 5: 'Derived', 6: 'Ancillary'} | ||
309 | + return Ext.util.Format.cell((data in levels) ? levels[data] : '<em>' + data + '</em>') | ||
310 | + }, | ||
311 | + 'granule.date': function (data, metadata, record) { | ||
312 | + if (isNaN(data)) { | ||
313 | + return '' | ||
314 | + } | ||
315 | + var f = Number(data) + 1401 + Math.floor((Math.floor((4 * Number(data) + 274277) / 146097) * 3) / 4) - 38 | ||
316 | + var e = 4 * f + 3 | ||
317 | + var g = Math.floor((e % 1461) / 4) | ||
318 | + var h = 5 * g + 2 | ||
319 | + var D = Math.floor((h % 153) / 5) + 1 | ||
320 | + var M = ((Math.floor(h / 153) + 2) % 12) + 1 | ||
321 | + var Y = Math.floor(e / 1461) - 4716 + Math.floor((12 + 2 - M) / 12) | ||
322 | + var date = new Date(Y, M - 1, D) | ||
323 | + return Ext.util.Format.cell(Ext.Date.format(date, 'Y/m/d'), Ext.Date.format(date, 'F j, Y, g:i a')) | ||
324 | + }, | ||
325 | + 'granule.format': function (data, metadata, record) { | ||
326 | + var mimetypeDict = { | ||
327 | + 'application/fits': 'fits', | ||
328 | + 'application/x-pds': 'pds', | ||
329 | + 'image/x-pds': 'pds', | ||
330 | + 'application/gml+xml': 'gml', | ||
331 | + 'application/json': 'json', | ||
332 | + 'application/octet-stream': 'bin, idl, envi or matlab', | ||
333 | + 'application/pdf': 'pdf', | ||
334 | + 'application/postscript': 'ps', | ||
335 | + 'application/vnd.geo+json': 'geojson', | ||
336 | + 'application/vnd.google-earth.kml+xml': 'kml', | ||
337 | + 'application/vnd.google-earth.kmz': 'kmz', | ||
338 | + 'application/vnd.ms-excel': 'xls', | ||
339 | + 'application/x-asdm': 'asdm', | ||
340 | + 'application/x-cdf': 'cdf', | ||
341 | + 'application/x-cdf-istp': 'cdf', | ||
342 | + 'application/x-cdf-pds4': 'cdf', | ||
343 | + 'application/x-cef1': 'cef1', | ||
344 | + 'application/x-cef2': 'cef2', | ||
345 | + 'application/x-directory': 'dir', | ||
346 | + 'application/x-fits-bintable': 'bintable', | ||
347 | + 'application/x-fits-euro3d': 'euro3d', | ||
348 | + 'application/x-fits-mef': 'mef', | ||
349 | + 'application/x-geotiff': 'geotiff', | ||
350 | + 'application/x-hdf': 'hdf', | ||
351 | + 'application/x-netcdf': 'nc', | ||
352 | + 'application/x-netcdf4': 'nc', | ||
353 | + 'application/x-tar': 'tar', | ||
354 | + 'application/x-tar-gzip': 'gtar', | ||
355 | + 'application/x-votable+xml': 'votable', | ||
356 | + 'application/x-votable+xml;content=datalink': 'votable', | ||
357 | + 'application/zip': 'zip', | ||
358 | + 'image/fits': 'fits', | ||
359 | + 'image/gif': 'gif', | ||
360 | + 'image/jpeg': 'jpeg', | ||
361 | + 'image/png': 'png', | ||
362 | + 'image/tiff': 'tiff', | ||
363 | + 'image/x-fits-gzip': 'fits', | ||
364 | + 'image/x-fits-hcompress': 'fits', | ||
365 | + 'text/csv': 'csv', | ||
366 | + 'text/html': 'html', | ||
367 | + 'text/plain': 'txt', | ||
368 | + 'text/tab-separated-values': 'tsv', | ||
369 | + 'text/xml': 'xml', | ||
370 | + 'video/mpeg': 'mpeg', | ||
371 | + 'video/quicktime': 'mov', | ||
372 | + 'video/x-msvideo': 'avi' | ||
373 | + } | ||
374 | + return Ext.util.Format.cell((data in mimetypeDict) ? '<p>' + mimetypeDict[data] + '</p>' : '<em>' + data + '</em>') | ||
375 | + } | ||
376 | +}) | ||
377 | 377 | ||
378 | /** | 378 | /** |
379 | `EpnTapUI`: The view of the AMDA EPN-TAP module, allowing the user to query and display granules information from | 379 | `EpnTapUI`: The view of the AMDA EPN-TAP module, allowing the user to query and display granules information from |
@@ -382,298 +382,298 @@ EPN-TAP services. | @@ -382,298 +382,298 @@ EPN-TAP services. | ||
382 | Note: The controller part of this module is defined in `js/app/controller/EpnTapModule`. | 382 | Note: The controller part of this module is defined in `js/app/controller/EpnTapModule`. |
383 | */ | 383 | */ |
384 | Ext.define('amdaUI.EpnTapUI', { | 384 | Ext.define('amdaUI.EpnTapUI', { |
385 | - extend: 'Ext.panel.Panel', | ||
386 | - alias: 'widget.panelEpnTap', | ||
387 | - requires: ['amdaUI.IntervalUI'], | ||
388 | - | ||
389 | - /** | ||
390 | - Method constructor, which basically call the `init()` method to create the EpnTap panel. | ||
391 | - */ | ||
392 | - constructor: function(config) { | ||
393 | - this.init(config); | ||
394 | - this.callParent(arguments); | ||
395 | - }, | ||
396 | - | ||
397 | - /** | ||
398 | - Create all the EpnTapPanel UI elements, and apply the AMDA module `config` (which includes the created items). | ||
399 | - | ||
400 | - When the panel is correctly rendered, the panel triggers `EpnTapModule.onWindowLoaded()`. | ||
401 | - | ||
402 | - Note: All the UI elements creation are defined as functions in this init method and not as methods in order to make | ||
403 | - them private (ie. to avoid `EpnTapUI.createServicesGrid();`, which doesn't make sense). | ||
404 | - */ | ||
405 | - init: function(config) { | ||
406 | - var myConf = { | ||
407 | - id: 'epntapTab', | ||
408 | - title: 'EPN-TAP', | ||
409 | - items: [{ | ||
410 | - xtype: 'container', | ||
411 | - layout: { type: 'vbox', pack: 'start', align: 'stretch'}, | ||
412 | - items: [ | ||
413 | - this.createServiceFilterPanel(), | ||
414 | - this.createGridsPanel() | ||
415 | - ] | ||
416 | - }] | ||
417 | - }; | ||
418 | - Ext.apply(this, Ext.apply(arguments, myConf)); | ||
419 | - }, | ||
420 | - | ||
421 | - /*************************** | ||
422 | - *** Service filter panel *** | ||
423 | - ***************************/ | ||
424 | - | ||
425 | - /** | ||
426 | - Create `epnTapServiceFilterPanel`, an ExtJS Panel containing two containers: | ||
427 | - - the left container, containing the combo boxes (for product type, target class and target name) | ||
428 | - and the navigation panel; | ||
429 | - - the right container, containing the time selector. | ||
430 | - */ | ||
431 | - createServiceFilterPanel: function() { | ||
432 | - return { | ||
433 | - xtype: 'form', | ||
434 | - id: 'epnTapServiceFilterPanel', | ||
435 | - layout: { type: 'hbox', pack: 'start', align: 'stretch' }, | ||
436 | - region: 'north', | ||
437 | - defaults: { margin: '5 0 5 5'}, | ||
438 | - items: [{ // Left part | ||
439 | - xtype : 'container', | ||
440 | - flex: 1, | ||
441 | - items: [ | ||
442 | - this.createTargetNameCB(), | ||
443 | - this.createProductTypeCB() | ||
444 | - ] | ||
445 | - }, { // Middle part | ||
446 | - xtype : 'container', | ||
447 | - flex: 1, | ||
448 | - items: [ | ||
449 | - this.createTimeSelector() | ||
450 | - ] | ||
451 | - }, { // Right part | ||
452 | - xtype : 'container', | ||
453 | - items: [ | ||
454 | - this.createSendButton() | ||
455 | - ] | ||
456 | - | ||
457 | - }] | ||
458 | - }; | ||
459 | - }, | ||
460 | - | ||
461 | - /** | ||
462 | - Create `epnTapTargetNameCB`, an ExtJS ComboBox, containing a list of target names corresponding to the selected | ||
463 | - target class, as defined in `targetNamesStore`, which is initilized by `EpnTapModule`. | ||
464 | - | ||
465 | - The selection of a target name triggers `EpnTapModule.onTargetNameCBChanged()`, which basically updates | ||
466 | - `granulesGrid`. | ||
467 | - */ | ||
468 | - createTargetNameCB: function() { | ||
469 | - return { | ||
470 | - xtype: 'combobox', | ||
471 | - id: 'epnTapTargetNameCB', | ||
472 | - fieldLabel: 'Target name', | ||
473 | - emptyText: 'Earth, Saturn, 67P, ...', | ||
474 | - tooltip: 'Start to type a text, then select a target name (required). Several values are allowed, separated by a semicolon.', | ||
475 | - store: Ext.data.StoreManager.lookup('targetNamesStore'), | ||
476 | - queryMode: 'remote', | ||
477 | - queryParam: 'input', | ||
478 | - displayField: 'text', | ||
479 | - valueField: 'id', | ||
480 | - margin: '15 0 5 0', | ||
481 | - labelWidth: 71, | ||
482 | - minWidth: 20, | ||
483 | - minChars: 2, | ||
484 | - hideTrigger: true, | ||
485 | - listConfig: { | ||
486 | - getInnerTpl: function() { | ||
487 | - return '<div data-qtitle="{name}" data-qtip="<p>type: {type}</p><p>parent: {parent}</p><p>aliases:</p><ul>{aliases}</ul>">{name}</div>'; | ||
488 | - } | ||
489 | - }, | ||
490 | - listeners: { | ||
491 | - render: function(cb) { | ||
492 | - new Ext.ToolTip({ target: cb.getEl(), html: '<div style="width:200px">' + cb.tooltip + '</div>'}); | ||
493 | - } | ||
494 | - } | ||
495 | - }; | ||
496 | - }, | ||
497 | - | ||
498 | - /** | ||
499 | - Create `epnTapProductTypeCB`, an ExtJS ComboBox, containing a list of product types as defined in | ||
500 | - `epnTapProductTypesStore`, which is initilized by `EpnTapModule`. | ||
501 | - | ||
502 | - The selection of a produt type triggers `EpnTapModule.onProductTypeCBChanged()`, which basically update | ||
503 | - `epnTapGranulesGrid`. | ||
504 | - */ | ||
505 | - createProductTypeCB: function() { | ||
506 | - return { | ||
507 | - xtype: 'combobox', | ||
508 | - id: 'epnTapProductTypeCB', | ||
509 | - fieldLabel: 'Product type', | ||
510 | - emptyText: 'Image, Time series, ...', | ||
511 | - tooltip: 'Select one or several data product types (required).', | ||
512 | - store: Ext.data.StoreManager.lookup('productTypesStore'), | ||
513 | - queryMode: 'local', | ||
514 | - valueField: 'id', | ||
515 | - multiSelect: true, | ||
516 | - displayField: 'name', | ||
517 | - labelWidth: 71, | ||
518 | - editable: false, | ||
519 | - listConfig: { | ||
520 | - getInnerTpl: function() { | ||
521 | - return '<div data-qtitle="{name}" data-qwidth=200 data-qtip="<p>{desc}</p>">{name}</div>'; | ||
522 | - } | ||
523 | - }, | ||
524 | - listeners: { | ||
525 | - change: function(cb, records) { | ||
526 | - var val = cb.value[cb.value.length - 1]; | ||
527 | - if(val === 'all') { | ||
528 | - cb.select(cb.store.getRange().slice(2)); | ||
529 | - } else if (val === 'clear') { | ||
530 | - cb.reset(); | ||
531 | - } | ||
532 | - }, | ||
533 | - render: function(cb) { | ||
534 | - new Ext.ToolTip({ target: cb.getEl(), html: '<div style="width:200px">' + cb.tooltip + '</div>'}); | ||
535 | - } | ||
536 | - } | ||
537 | - }; | ||
538 | - }, | ||
539 | - | ||
540 | - /** | ||
541 | - Create `epnTapTimeSelector`, an IntervalUI object, allowing the user to select a time interval (by filling two | ||
542 | - dates and/or a duration). | ||
543 | - | ||
544 | - See `js/app/views/IntervalUI.js` for more information about this component. | ||
545 | - */ | ||
546 | - createTimeSelector: function() { | ||
547 | - return { | ||
548 | - xtype: 'intervalSelector', | ||
549 | - id: 'epnTapTimeSelector' | ||
550 | - }; | ||
551 | - }, | ||
552 | - | ||
553 | - /*********************** | ||
554 | - *** Navigation panel *** | ||
555 | - ***********************/ | ||
556 | - | ||
557 | - /** | ||
558 | - The button used to send the query. | ||
559 | - */ | ||
560 | - createSendButton: function() { | ||
561 | - return { | ||
562 | - xtype: 'button', | ||
563 | - id: 'epnTapGetBtn', | ||
564 | - text: 'Get services', | ||
565 | - disabled: true, | ||
566 | - width: 140, | ||
567 | - height: 50, | ||
568 | - margin: 10 | ||
569 | - } | ||
570 | - }, | ||
571 | - | ||
572 | - /************ | ||
573 | - *** Grids *** | ||
574 | - ************/ | ||
575 | - | ||
576 | - /** | ||
577 | - Create `epnTapGridsPanel`, an ExtJS Panel, containing `epnTapServicesGrid` and `epnTapGranulesGrid`. | ||
578 | - | ||
579 | - After the rendering of the grids, it triggers `epnTapModule.onWindowLoaded()`, which basically fill | ||
580 | - `epnTapServicesGrid` for the first time. | ||
581 | - */ | ||
582 | - createGridsPanel: function() { | ||
583 | - return { | ||
584 | - xtype: 'panel', | ||
585 | - id: 'epnTapGridsPanel', | ||
586 | - layout: 'fit', | ||
587 | - flex: 1, | ||
588 | - items: [{ | ||
589 | - xtype: 'container', | ||
590 | - layout: { type: 'hbox', pack: 'start', align: 'stretch'}, | ||
591 | - items: [ | ||
592 | - this.createServicesGrid(), | ||
593 | - this.createGranulesGrid() | ||
594 | - ] | ||
595 | - }] | ||
596 | - }; | ||
597 | - }, | ||
598 | - | ||
599 | - /** | ||
600 | - Create `epnTapServicesGrid`, an ExtJS grid containing the EPN-TAP services matching with the filter form | ||
601 | - (`serviceFilterPanel`). | ||
602 | - | ||
603 | - For each service, this grid displays: | ||
604 | - - the service name; | ||
605 | - - the number of granules matching with the filter. | ||
606 | - | ||
607 | - Other informations are available through an ExtJS Tooltip, on each row: | ||
608 | - - short name; | ||
609 | - - title; | ||
610 | - - access URL. | ||
611 | - | ||
612 | - A click on a service triggers `EpnTapModule.onServiceSelected()`, which basically fills `GranulesGrid` by the | ||
613 | - service granules. | ||
614 | - */ | ||
615 | - createServicesGrid: function() { | ||
616 | - return { | ||
617 | - xtype: 'grid', | ||
618 | - cls: 'epntap_grid', | ||
619 | - id: 'epnTapServicesGrid', | ||
620 | - title: 'Services', | ||
621 | - store: Ext.data.StoreManager.lookup('servicesStore'), | ||
622 | - flex: 1, | ||
623 | - columns: [ | ||
624 | - {text: 'Name', dataIndex: 'short_name', flex: 1, renderer: 'service.text'}, | ||
625 | - {text: 'Nb res.', dataIndex: 'nb_results', width: 50, renderer: 'service.number'} | ||
626 | - ], | ||
627 | - viewConfig: { | ||
628 | - getRowClass: function(record, index) { | ||
629 | - var nb_res = record.get('nb_results'); | ||
630 | - if(nb_res == 0 || nb_res == -1) { | ||
631 | - return 'disabled_row'; | ||
632 | - } else if (nb_res == -2) { | ||
633 | - return 'error_row'; | ||
634 | - } | ||
635 | - } | ||
636 | - } | ||
637 | - }; | ||
638 | - }, | ||
639 | - | ||
640 | - /** | ||
641 | - Create `epnTapGranulesGrid`, an ExtJS grid containing the granules of the selected service in | ||
642 | - `epnTapServicesGrid`. | ||
643 | - | ||
644 | - For each granule, this grid displays: | ||
645 | - - the row number; | ||
646 | - - the dataproduct type; | ||
647 | - - the target name; | ||
648 | - - the min and max times; | ||
649 | - - the format; | ||
650 | - - the UID (granule identifier); | ||
651 | - - the estimated size; | ||
652 | - - the URL; | ||
653 | - - the thumbnail. | ||
654 | - | ||
655 | - Each of these information are displayed in a specific rendering to improve user experience. | ||
656 | - For more information about these parameters, see https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. | ||
657 | - | ||
658 | - Other informations are available through an ExtJS Tooltip on each row: | ||
659 | - - currently only the granule thumbnail, in full size. | ||
660 | - | ||
661 | - A click on a granule triggers `EpnTapModule.onGranuleSelected()`. | ||
662 | - */ | ||
663 | - createGranulesGrid: function() { | ||
664 | - return { | ||
665 | - xtype: 'grid', | ||
666 | - cls: 'epntap_grid', | ||
667 | - id: 'epnTapGranulesGrid', | ||
668 | - title: 'Granules', | ||
669 | - store: Ext.data.StoreManager.lookup('granulesStore'), | ||
670 | - flex: 4, | ||
671 | - plugins: { | ||
672 | - ptype: 'bufferedrenderer', | ||
673 | - trailingBufferZone: 20, | ||
674 | - leadingBufferZone: 50 | ||
675 | - }, | ||
676 | - columns: [] | ||
677 | - }; | ||
678 | - } | ||
679 | -}); | 385 | + extend: 'Ext.panel.Panel', |
386 | + alias: 'widget.panelEpnTap', | ||
387 | + requires: ['amdaUI.IntervalUI'], | ||
388 | + | ||
389 | + /** | ||
390 | + Method constructor, which basically call the `init()` method to create the EpnTap panel. | ||
391 | + */ | ||
392 | + constructor: function (config) { | ||
393 | + this.init(config) | ||
394 | + this.callParent(arguments) | ||
395 | + }, | ||
396 | + | ||
397 | + /** | ||
398 | + Create all the EpnTapPanel UI elements, and apply the AMDA module `config` (which includes the created items). | ||
399 | + | ||
400 | + When the panel is correctly rendered, the panel triggers `EpnTapModule.onWindowLoaded()`. | ||
401 | + | ||
402 | + Note: All the UI elements creation are defined as functions in this init method and not as methods in order to make | ||
403 | + them private (ie. to avoid `EpnTapUI.createServicesGrid();`, which doesn't make sense). | ||
404 | + */ | ||
405 | + init: function (config) { | ||
406 | + var myConf = { | ||
407 | + id: 'epntapTab', | ||
408 | + title: 'EPN-TAP', | ||
409 | + items: [{ | ||
410 | + xtype: 'container', | ||
411 | + layout: { type: 'vbox', pack: 'start', align: 'stretch'}, | ||
412 | + items: [ | ||
413 | + this.createServiceFilterPanel(), | ||
414 | + this.createGridsPanel() | ||
415 | + ] | ||
416 | + }] | ||
417 | + } | ||
418 | + Ext.apply(this, Ext.apply(arguments, myConf)) | ||
419 | + }, | ||
420 | + | ||
421 | + /*************************** | ||
422 | + *** Service filter panel *** | ||
423 | + ***************************/ | ||
424 | + | ||
425 | + /** | ||
426 | + Create `epnTapServiceFilterPanel`, an ExtJS Panel containing two containers: | ||
427 | + - the left container, containing the combo boxes (for product type, target class and target name) | ||
428 | + and the navigation panel; | ||
429 | + - the right container, containing the time selector. | ||
430 | + */ | ||
431 | + createServiceFilterPanel: function () { | ||
432 | + return { | ||
433 | + xtype: 'form', | ||
434 | + id: 'epnTapServiceFilterPanel', | ||
435 | + layout: { type: 'hbox', pack: 'start', align: 'stretch' }, | ||
436 | + region: 'north', | ||
437 | + defaults: { margin: '5 0 5 5'}, | ||
438 | + items: [{ // Left part | ||
439 | + xtype: 'container', | ||
440 | + flex: 1, | ||
441 | + items: [ | ||
442 | + this.createTargetNameCB(), | ||
443 | + this.createProductTypeCB() | ||
444 | + ] | ||
445 | + }, { // Middle part | ||
446 | + xtype: 'container', | ||
447 | + flex: 1, | ||
448 | + items: [ | ||
449 | + this.createTimeSelector() | ||
450 | + ] | ||
451 | + }, { // Right part | ||
452 | + xtype: 'container', | ||
453 | + items: [ | ||
454 | + this.createSendButton() | ||
455 | + ] | ||
456 | + | ||
457 | + }] | ||
458 | + } | ||
459 | + }, | ||
460 | + | ||
461 | + /** | ||
462 | + Create `epnTapTargetNameCB`, an ExtJS ComboBox, containing a list of target names corresponding to the selected | ||
463 | + target class, as defined in `targetNamesStore`, which is initilized by `EpnTapModule`. | ||
464 | + | ||
465 | + The selection of a target name triggers `EpnTapModule.onTargetNameCBChanged()`, which basically updates | ||
466 | + `granulesGrid`. | ||
467 | + */ | ||
468 | + createTargetNameCB: function () { | ||
469 | + return { | ||
470 | + xtype: 'combobox', | ||
471 | + id: 'epnTapTargetNameCB', | ||
472 | + fieldLabel: 'Target name', | ||
473 | + emptyText: 'Earth, Saturn, 67P, ...', | ||
474 | + tooltip: 'Start to type a text, then select a target name (required). Several values are allowed, separated by a semicolon.', | ||
475 | + store: Ext.data.StoreManager.lookup('targetNamesStore'), | ||
476 | + queryMode: 'remote', | ||
477 | + queryParam: 'input', | ||
478 | + displayField: 'text', | ||
479 | + valueField: 'id', | ||
480 | + margin: '15 0 5 0', | ||
481 | + labelWidth: 71, | ||
482 | + minWidth: 20, | ||
483 | + minChars: 2, | ||
484 | + hideTrigger: true, | ||
485 | + listConfig: { | ||
486 | + getInnerTpl: function () { | ||
487 | + return '<div data-qtitle="{name}" data-qtip="<p>type: {type}</p><p>parent: {parent}</p><p>aliases:</p><ul>{aliases}</ul>">{name}</div>' | ||
488 | + } | ||
489 | + }, | ||
490 | + listeners: { | ||
491 | + render: function (cb) { | ||
492 | + new Ext.ToolTip({ target: cb.getEl(), html: '<div style="width:200px">' + cb.tooltip + '</div>'}) | ||
493 | + } | ||
494 | + } | ||
495 | + } | ||
496 | + }, | ||
497 | + | ||
498 | + /** | ||
499 | + Create `epnTapProductTypeCB`, an ExtJS ComboBox, containing a list of product types as defined in | ||
500 | + `epnTapProductTypesStore`, which is initilized by `EpnTapModule`. | ||
501 | + | ||
502 | + The selection of a produt type triggers `EpnTapModule.onProductTypeCBChanged()`, which basically update | ||
503 | + `epnTapGranulesGrid`. | ||
504 | + */ | ||
505 | + createProductTypeCB: function () { | ||
506 | + return { | ||
507 | + xtype: 'combobox', | ||
508 | + id: 'epnTapProductTypeCB', | ||
509 | + fieldLabel: 'Product type', | ||
510 | + emptyText: 'Image, Time series, ...', | ||
511 | + tooltip: 'Select one or several data product types (required).', | ||
512 | + store: Ext.data.StoreManager.lookup('productTypesStore'), | ||
513 | + queryMode: 'local', | ||
514 | + valueField: 'id', | ||
515 | + multiSelect: true, | ||
516 | + displayField: 'name', | ||
517 | + labelWidth: 71, | ||
518 | + editable: false, | ||
519 | + listConfig: { | ||
520 | + getInnerTpl: function () { | ||
521 | + return '<div data-qtitle="{name}" data-qwidth=200 data-qtip="<p>{desc}</p>">{name}</div>' | ||
522 | + } | ||
523 | + }, | ||
524 | + listeners: { | ||
525 | + change: function (cb, records) { | ||
526 | + var val = cb.value[cb.value.length - 1] | ||
527 | + if (val === 'all') { | ||
528 | + cb.select(cb.store.getRange().slice(2)) | ||
529 | + } else if (val === 'clear') { | ||
530 | + cb.reset() | ||
531 | + } | ||
532 | + }, | ||
533 | + render: function (cb) { | ||
534 | + new Ext.ToolTip({ target: cb.getEl(), html: '<div style="width:200px">' + cb.tooltip + '</div>'}) | ||
535 | + } | ||
536 | + } | ||
537 | + } | ||
538 | + }, | ||
539 | + | ||
540 | + /** | ||
541 | + Create `epnTapTimeSelector`, an IntervalUI object, allowing the user to select a time interval (by filling two | ||
542 | + dates and/or a duration). | ||
543 | + | ||
544 | + See `js/app/views/IntervalUI.js` for more information about this component. | ||
545 | + */ | ||
546 | + createTimeSelector: function () { | ||
547 | + return { | ||
548 | + xtype: 'intervalSelector', | ||
549 | + id: 'epnTapTimeSelector' | ||
550 | + } | ||
551 | + }, | ||
552 | + | ||
553 | + /*********************** | ||
554 | + *** Navigation panel *** | ||
555 | + ***********************/ | ||
556 | + | ||
557 | + /** | ||
558 | + The button used to send the query. | ||
559 | + */ | ||
560 | + createSendButton: function () { | ||
561 | + return { | ||
562 | + xtype: 'button', | ||
563 | + id: 'epnTapGetBtn', | ||
564 | + text: 'Get services', | ||
565 | + disabled: true, | ||
566 | + width: 140, | ||
567 | + height: 50, | ||
568 | + margin: 10 | ||
569 | + } | ||
570 | + }, | ||
571 | + | ||
572 | + /************ | ||
573 | + *** Grids *** | ||
574 | + ************/ | ||
575 | + | ||
576 | + /** | ||
577 | + Create `epnTapGridsPanel`, an ExtJS Panel, containing `epnTapServicesGrid` and `epnTapGranulesGrid`. | ||
578 | + | ||
579 | + After the rendering of the grids, it triggers `epnTapModule.onWindowLoaded()`, which basically fill | ||
580 | + `epnTapServicesGrid` for the first time. | ||
581 | + */ | ||
582 | + createGridsPanel: function () { | ||
583 | + return { | ||
584 | + xtype: 'panel', | ||
585 | + id: 'epnTapGridsPanel', | ||
586 | + layout: 'fit', | ||
587 | + flex: 1, | ||
588 | + items: [{ | ||
589 | + xtype: 'container', | ||
590 | + layout: { type: 'hbox', pack: 'start', align: 'stretch'}, | ||
591 | + items: [ | ||
592 | + this.createServicesGrid(), | ||
593 | + this.createGranulesGrid() | ||
594 | + ] | ||
595 | + }] | ||
596 | + } | ||
597 | + }, | ||
598 | + | ||
599 | + /** | ||
600 | + Create `epnTapServicesGrid`, an ExtJS grid containing the EPN-TAP services matching with the filter form | ||
601 | + (`serviceFilterPanel`). | ||
602 | + | ||
603 | + For each service, this grid displays: | ||
604 | + - the service name; | ||
605 | + - the number of granules matching with the filter. | ||
606 | + | ||
607 | + Other informations are available through an ExtJS Tooltip, on each row: | ||
608 | + - short name; | ||
609 | + - title; | ||
610 | + - access URL. | ||
611 | + | ||
612 | + A click on a service triggers `EpnTapModule.onServiceSelected()`, which basically fills `GranulesGrid` by the | ||
613 | + service granules. | ||
614 | + */ | ||
615 | + createServicesGrid: function () { | ||
616 | + return { | ||
617 | + xtype: 'grid', | ||
618 | + cls: 'epntap_grid', | ||
619 | + id: 'epnTapServicesGrid', | ||
620 | + title: 'Services', | ||
621 | + store: Ext.data.StoreManager.lookup('servicesStore'), | ||
622 | + flex: 1, | ||
623 | + columns: [ | ||
624 | + {text: 'Name', dataIndex: 'short_name', flex: 1, renderer: 'service.text'}, | ||
625 | + {text: 'Nb res.', dataIndex: 'nb_results', width: 50, renderer: 'service.number'} | ||
626 | + ], | ||
627 | + viewConfig: { | ||
628 | + getRowClass: function (record, index) { | ||
629 | + var nb_res = record.get('nb_results') | ||
630 | + if (nb_res == 0 || nb_res == -1) { | ||
631 | + return 'disabled_row' | ||
632 | + } else if (nb_res == -2) { | ||
633 | + return 'error_row' | ||
634 | + } | ||
635 | + } | ||
636 | + } | ||
637 | + } | ||
638 | + }, | ||
639 | + | ||
640 | + /** | ||
641 | + Create `epnTapGranulesGrid`, an ExtJS grid containing the granules of the selected service in | ||
642 | + `epnTapServicesGrid`. | ||
643 | + | ||
644 | + For each granule, this grid displays: | ||
645 | + - the row number; | ||
646 | + - the dataproduct type; | ||
647 | + - the target name; | ||
648 | + - the min and max times; | ||
649 | + - the format; | ||
650 | + - the UID (granule identifier); | ||
651 | + - the estimated size; | ||
652 | + - the URL; | ||
653 | + - the thumbnail. | ||
654 | + | ||
655 | + Each of these information are displayed in a specific rendering to improve user experience. | ||
656 | + For more information about these parameters, see https://voparis-confluence.obspm.fr/display/VES/EPN-TAP+V2.0+parameters. | ||
657 | + | ||
658 | + Other informations are available through an ExtJS Tooltip on each row: | ||
659 | + - currently only the granule thumbnail, in full size. | ||
660 | + | ||
661 | + A click on a granule triggers `EpnTapModule.onGranuleSelected()`. | ||
662 | + */ | ||
663 | + createGranulesGrid: function () { | ||
664 | + return { | ||
665 | + xtype: 'grid', | ||
666 | + cls: 'epntap_grid', | ||
667 | + id: 'epnTapGranulesGrid', | ||
668 | + title: 'Granules', | ||
669 | + store: Ext.data.StoreManager.lookup('granulesStore'), | ||
670 | + flex: 4, | ||
671 | + plugins: { | ||
672 | + ptype: 'bufferedrenderer', | ||
673 | + trailingBufferZone: 20, | ||
674 | + leadingBufferZone: 50 | ||
675 | + }, | ||
676 | + columns: [] | ||
677 | + } | ||
678 | + } | ||
679 | +}) |