Commit bd797eeabbed74f20402f24004d6d21898145764

Authored by Benjamin Renard
2 parents 5c744625 2deb6154

Merge branch 'master' into new-save-plot-request

generic_data/Functions/functions.xml
@@ -60,6 +60,7 @@ @@ -60,6 +60,7 @@
60 <info_brief>variance() function</info_brief> 60 <info_brief>variance() function</info_brief>
61 <new_kernel>#var_sm</new_kernel> 61 <new_kernel>#var_sm</new_kernel>
62 </function> 62 </function>
  63 +
63 <function name="skew_sm_(,)" args="1" kind="sliding"> 64 <function name="skew_sm_(,)" args="1" kind="sliding">
64 <prompt>input window time in secs</prompt> 65 <prompt>input window time in secs</prompt>
65 <info_brief>skewness() function</info_brief> 66 <info_brief>skewness() function</info_brief>
@@ -70,15 +71,49 @@ @@ -70,15 +71,49 @@
70 <info_brief>smooths with a boxcar average</info_brief> 71 <info_brief>smooths with a boxcar average</info_brief>
71 <new_kernel>#boxcar</new_kernel> 72 <new_kernel>#boxcar</new_kernel>
72 </function> 73 </function>
  74 +
73 <!-- <function name="shiftN_(,)" args="1" kind="amda"> 75 <!-- <function name="shiftN_(,)" args="1" kind="amda">
74 <prompt>input number of points N to delay by</prompt> 76 <prompt>input number of points N to delay by</prompt>
75 <info_brief>Delays array by N points back (N &lt; 0) and forth (N &gt; 0)</info_brief> 77 <info_brief>Delays array by N points back (N &lt; 0) and forth (N &gt; 0)</info_brief>
76 </function>--> 78 </function>-->
  79 +
77 <function name="shiftT_(,)" args="1" kind="amda"> 80 <function name="shiftT_(,)" args="1" kind="amda">
78 <prompt>input time interval T in secs to delay by</prompt> 81 <prompt>input time interval T in secs to delay by</prompt>
79 <info_brief>Delays array by T secs back (T &lt; 0) and forth (T &gt; 0))</info_brief> 82 <info_brief>Delays array by T secs back (T &lt; 0) and forth (T &gt; 0))</info_brief>
80 <new_kernel>#timeShift</new_kernel> 83 <new_kernel>#timeShift</new_kernel>
81 </function> 84 </function>
  85 +
  86 + <function name="Valfven(,)" params="2" kind="physics">
  87 + <prompt>Alfven velocity Valfven(density[cm^⁻3], mag[nT])</prompt>
  88 + <info_brief>Alfven velocity Valfven(density[cm^⁻3], mag[nT])</info_brief>
  89 + <new_kernel>alfvenVelocity</new_kernel>
  90 + </function>
  91 +
  92 + <function name="fairfield70(,)" params="3" kind="physics">
  93 + <prompt>FAIRFIELD 1970 model of neutral sheet position Z = fairfield70(y_sm, z_sm, x_ss)
  94 + where y_sm and z_sm are the solar magnetospheric coordinates of the spacecraft
  95 + and x_ss is geomagnetic latitude of the sun</prompt>
  96 +
  97 + <info_brief>FAIRFIELD 1970 model of neutral sheet position Z = fairfield70(y_sm, z_sm, x_ss)
  98 + where y_sm and z_sm are the solar magnetospheric coordinates of the spacecraft
  99 + and x_ss is geomagnetic latitude of the sun</info_brief>
  100 + <new_kernel>fairfield70</new_kernel>
  101 + </function>
  102 +
  103 + <function name="lopez90(,)" params="4" kind="physics">
  104 + <prompt>
  105 + Lopez 1990 model of magnetic latitude of the neutral sheet MLAT = lopez90(Kp, phi, R, DTA)
  106 + where Kp is the 3-hour magnetic index, phi the magnetic local time in degrees (phi = 0° at midnight)
  107 + R is the radial distance in RE and DTA is the dipole tilt angle
  108 + </prompt>
  109 + <info_brief>
  110 + Lopez 1990 model of magnetic latitude of the neutral sheet MLAT = lopez90(Kp, phi, R, DTA)
  111 + where Kp is the 3-hour magnetic index, phi the magnetic local time in degrees (phi = 0° at midnight)
  112 + R is the radial distance in RE and DTA is the dipole tilt angle
  113 + </info_brief>
  114 + <new_kernel>lopez90</new_kernel>
  115 + </function>
  116 +
82 <!-- <function name="gsegsm_()" argv="vector" kind="amda"> 117 <!-- <function name="gsegsm_()" argv="vector" kind="amda">
83 <prompt/> 118 <prompt/>
84 <info_brief>GSE to GSM transformation</info_brief> 119 <info_brief>GSE to GSM transformation</info_brief>
@@ -88,6 +123,7 @@ @@ -88,6 +123,7 @@
88 <prompt/> 123 <prompt/>
89 <info_brief>GSE to SM transformation</info_brief> 124 <info_brief>GSE to SM transformation</info_brief>
90 </function>--> 125 </function>-->
  126 +
91 <function name="angle(,)" params="2" kind="vectors"> 127 <function name="angle(,)" params="2" kind="vectors">
92 <info_brief>Angle between two vectors</info_brief> 128 <info_brief>Angle between two vectors</info_brief>
93 <new_kernel>angle</new_kernel> 129 <new_kernel>angle</new_kernel>
js/app/views/CalculatorUI.js
@@ -14,500 +14,502 @@ @@ -14,500 +14,502 @@
14 * @ptype calculator 14 * @ptype calculator
15 */ 15 */
16 16
17 -var CalculatorData = ['1','2','3','4','5','6','7','8','9','0','(',')','[',']','+','-','*','/','^', '.','>','<', '&', '|']; 17 +var CalculatorData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '(', ')', '[', ']', '+', '-', '*', '/', '^', '.', '>', '<', '&', '|'];
18 // var CalculatorData = ['1','2','3','4','5','6','7','8','9','0','(',')','[',']','+','-','*','/','^', '.','>','>=', '=', '<=', '<', '&', '|']; 18 // var CalculatorData = ['1','2','3','4','5','6','7','8','9','0','(',')','[',']','+','-','*','/','^', '.','>','>=', '=', '<=', '<', '&', '|'];
19 19
20 Ext.define('amdaUI.CalculatorUI', { 20 Ext.define('amdaUI.CalculatorUI', {
21 - extend: 'Ext.util.Observable',  
22 -  
23 - requires : [  
24 - 'amdaModel.Constant',  
25 - 'amdaModel.Function'  
26 - ],  
27 -  
28 - alias: 'plugin.calculator',  
29 -  
30 - statics : {  
31 - constantStore : null,  
32 - functionStore : null  
33 - },  
34 -  
35 - win: null,  
36 -  
37 - constructor: function(config) {  
38 - Ext.apply(this, config);  
39 - this.callParent(arguments);  
40 - },  
41 -  
42 - init: function(cmp)  
43 - {  
44 - this.hostCmp = cmp;  
45 - this.hostCmp.on({  
46 - scope: this,  
47 - added: function(){  
48 - this.hostCmp.ownerCt.on({  
49 - render: this.onRender,  
50 - show: this.onShow,  
51 - hide : this.onHide,  
52 - scope: this });  
53 - }  
54 - });  
55 - },  
56 -  
57 - onRender: function()  
58 - {  
59 - this.win = new Ext.Window({  
60 - width: 350,  
61 - height: 170,  
62 - x: 380, y: 0,  
63 - baseCls:'x-panel',  
64 - title: 'Tools For ' + this.context + ' Construction',  
65 - layout: 'fit',  
66 - closable: false,  
67 - collapsible: true,  
68 - constrain: true,  
69 - floating: true,  
70 - ghost: false,  
71 - renderTo: this.hostCmp.id,  
72 - items: this.getFormConfig(),  
73 - listeners : {  
74 - boxready: function (w)  
75 - {  
76 - if (w.y + w.height > myDesktopApp.desktop.el.getHeight())  
77 - w.el.setY((myDesktopApp.desktop.el.getHeight()-w.height > 0) ? myDesktopApp.desktop.el.getHeight()-w.height : 0);  
78 - }  
79 - },  
80 - getConstrainVector: function(constrainTo){  
81 - var me = this;  
82 - if (me.constrain || me.constrainHeader) {  
83 - constrainTo = constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container || me.el.getScopeParent();  
84 - return (me.constrainHeader ? me.header.el : me.el).getConstrainVector(constrainTo);  
85 - }  
86 - }  
87 - });  
88 -  
89 - //load constants store  
90 - if (!amdaUI.CalculatorUI.constantStore)  
91 - {  
92 - amdaUI.CalculatorUI.constantStore = Ext.create('Ext.data.Store',{model: 'amdaModel.Constant'});  
93 - amdaUI.CalculatorUI.constantStore.load({  
94 - scope : this,  
95 - callback: function(records, operation, success)  
96 - {  
97 - this.createAllConstantBtns();  
98 - }  
99 - });  
100 - }  
101 - else  
102 - this.createAllConstantBtns();  
103 -  
104 - //load functions store  
105 - if (!amdaUI.CalculatorUI.functionStore)  
106 - {  
107 - amdaUI.CalculatorUI.functionStore = Ext.create('Ext.data.Store',{model: 'amdaModel.Function'});  
108 - amdaUI.CalculatorUI.functionStore.load({  
109 - scope : this,  
110 - callback: function(records, operation, success)  
111 - {  
112 - this.createAllFunctionBtns();  
113 - }  
114 - });  
115 - }  
116 - else  
117 - this.createAllFunctionBtns();  
118 - },  
119 -  
120 - onShow: function() {  
121 - this.win.show();  
122 - var parentWin = this.hostCmp.findParentByType('window');  
123 -  
124 - if (parentWin.getId() === myDesktopApp.dynamicModules.param.id) {  
125 - this.win.setPosition(335,10);  
126 - }  
127 - else {  
128 -  
129 - var posX = parentWin.getWidth() - this.win.getWidth() - 30;  
130 - var posY = parentWin.getHeight() - this.win.getHeight() - 110 - 30/*20*/;  
131 - this.win.setPosition(posX,posY);//(420,290);  
132 - }  
133 - },  
134 -  
135 - onHide : function()  
136 - {  
137 - this.win.hide();  
138 - },  
139 -  
140 - getFormConfig: function(){  
141 - return {  
142 - xtype: 'tabpanel',  
143 - border: false, frame: true, plain: true,  
144 - enableTabScroll: true,  
145 - defaults: { frame: true, border: false, plain: true, autoScroll:true},  
146 - activeTab: 0,  
147 - items: [ {  
148 - title: 'Calculator',layout: 'column',  
149 - defaults: { xtype: 'button', columnWidth: .11},  
150 - items: this.getItems('Calculator')  
151 - } , {  
152 - title: 'Constants', xtype:'tabpanel', //iconCls: 'tabs',  
153 - enableTabScroll: true, tabPosition: 'bottom',  
154 - defaults: { frame: true, border: false, plain: true, layout: 'column', autoScroll:true},  
155 - activeTab: 0,  
156 - id : 'calc_tab_const_id'  
157 - }, {  
158 - title: 'Functions', xtype:'tabpanel', //iconCls: 'tabs',  
159 - enableTabScroll: true, tabPosition: 'bottom',  
160 - defaults: { frame: true, border: false, plain: true, layout: 'column', autoScroll:true},  
161 - activeTab: 0,  
162 - id : 'calc_tab_func_id'  
163 - }]  
164 - };  
165 - },  
166 -  
167 - /**  
168 - * Prompt any argument to user before processing Formula  
169 - * @param sel selected text  
170 - * @param currentBtn calculator button pressed  
171 - * @param params array of parameters  
172 - */  
173 - preProcessFormula: function(sel,currentBtn, params) {  
174 -  
175 - if (currentBtn.initialConfig.args!=0 && currentBtn.initialConfig.prompt!=""){  
176 - // Prompt for user precision and process the result using a callback  
177 - Ext.Msg.prompt('Argument',currentBtn.initialConfig.prompt, function(bt2, text){  
178 - if (bt2 === 'ok'){  
179 - var afterParamsText = "," + text + ")";  
180 - //TODO: more than one args and prompt  
181 - this.processFormula(sel,currentBtn, params, afterParamsText);  
182 -  
183 - }  
184 - },this);  
185 - } else {  
186 - this.processFormula(sel,currentBtn, params, ")");  
187 - }// endif prompt  
188 - },  
189 -  
190 - processFormula : function(sel,currentBtn, params, afterParamsText){  
191 - // calculation of the required number of parameters  
192 - var nbParams = currentBtn.initialConfig.params;  
193 - var fnText = currentBtn.text.split('(');  
194 - var newConstruction = sel.beforeText + fnText[0]+"(";  
195 - // if there at least one parameter selected  
196 - if (params.length){  
197 - for (var i=0;i<nbParams;i++) {  
198 - if(i>0) {  
199 - newConstruction += ",";  
200 - }  
201 - newConstruction += params[i];  
202 - }  
203 - }  
204 - // we keep position  
205 - var afterParameterPos = newConstruction.length;  
206 - newConstruction += afterParamsText;  
207 - var caretPos = newConstruction.length;  
208 - newConstruction += sel.afterText;  
209 - this.hostCmp.constructionField.setValue(newConstruction);  
210 -  
211 - // If we haven't the right number of selected parameters  
212 - if (params.length < nbParams){  
213 - var stringParamRequired = currentBtn.initialConfig.params+" parameter(s)";  
214 - Ext.Msg.alert('Caution', 'you\'ll have to add '+ stringParamRequired +' to apply this function',  
215 - function(){  
216 - // set Caret Position at placement of required parameter in function  
217 - this.hostCmp.constructionField.setCaretPosition(afterParameterPos);  
218 - },this  
219 - );  
220 - } else {  
221 - // set Caret Position after inserted Text  
222 - this.hostCmp.constructionField.setCaretPosition(caretPos);  
223 - }  
224 - },  
225 -  
226 - /**  
227 - * This method construct an array of arguments into selected text  
228 - * @param selectedText the selection to parse  
229 - * @param parseIndex the index to start parsing  
230 - * @return the arguments array  
231 - */  
232 - parseArgsInFormula : function (selectedText, parseIndex) {  
233 -  
234 - if (!selectedText || selectedText==""){  
235 - return [];  
236 - } else {  
237 - var params = [];  
238 - var startIndex = parseIndex;  
239 - var curIndex = parseIndex;  
240 - var openBrace = 0;  
241 - var sep = 0;  
242 - var closeBrace = 0;  
243 -  
244 - // while there is a separator  
245 - while(sep!=-1){  
246 - openBrace = selectedText.indexOf("(",curIndex);  
247 - sep = selectedText.indexOf(",",curIndex);  
248 - closeBrace = selectedText.indexOf(")",curIndex);  
249 -  
250 - // if there's an open bracket and no close bracket or inversely  
251 - if (openBrace!=-1 && closeBrace==-1 || openBrace==-1 && closeBrace!=-1) {  
252 - // invalid selection  
253 - return -1;  
254 - }  
255 -  
256 - // if there's a separator and opening brackets into selection  
257 - if (sep!=-1 && openBrace!=-1){  
258 - // if brace is before separator  
259 - if (openBrace<sep) {  
260 - curIndex = this.getEndBracket(selectedText,openBrace+1);  
261 - if (curIndex===-1){  
262 - return -1;  
263 - }  
264 - } else {// else separator is before brace  
265 - params.push(selectedText.substring(startIndex,sep));  
266 - startIndex = curIndex = sep+1;  
267 - }  
268 - // if there's only separators into selection  
269 - } else if (sep!=-1) {  
270 - params.push(selectedText.substring(startIndex,sep));  
271 - startIndex = curIndex = sep+1;  
272 - }  
273 - }  
274 - params.push(selectedText.substring(startIndex,selectedText.length));  
275 - return params;  
276 - }  
277 - },  
278 -  
279 - getEndBracket : function(string,indOpenBrace){  
280 - // we search for the corresponding end brace (after open bracket)  
281 - var currentIndex = indOpenBrace;  
282 - var nextCloseBrace = 0;  
283 - var nextOpenBrace = 0;  
284 - var braceLevel = 1;  
285 - while (nextCloseBrace!==-1 && braceLevel!==0){  
286 - // get index of next opening bracket  
287 - nextOpenBrace = string.indexOf("(",currentIndex);  
288 - // get index of next closing bracket  
289 - nextCloseBrace = string.indexOf(")",currentIndex);  
290 - // if both exist  
291 - if (nextOpenBrace!=-1 && nextCloseBrace!=-1) {  
292 - // if opening bracket is before closing one  
293 - if (nextOpenBrace<nextCloseBrace) {  
294 - currentIndex = nextOpenBrace+1;  
295 - braceLevel++;  
296 - } else { // if closing bracket is before opening one  
297 - currentIndex = nextCloseBrace+1;  
298 - braceLevel--;  
299 - }  
300 - // if there's only a next opening bracket  
301 - } else if (nextOpenBrace!=-1 && nextCloseBrace==-1) {  
302 - currentIndex = nextOpenBrace+1;  
303 - braceLevel++;  
304 - // if there's only a next closing bracket  
305 - } else if (nextOpenBrace==-1 && nextCloseBrace!=-1) {  
306 - currentIndex = nextCloseBrace+1;  
307 - braceLevel--;  
308 - }  
309 - }  
310 - // if no level imbrication left return index after closing bracket of block else -1  
311 - return braceLevel==0 ? currentIndex : -1;  
312 - },  
313 -  
314 - getCalculatorBtn : function()  
315 - {  
316 - var btns = [];  
317 -  
318 - Ext.each(CalculatorData, function (c){  
319 - btns.push({  
320 - text: c,  
321 - scope: this,  
322 - handler: function(b,e){  
323 - // keep selection into construction field  
324 - var selection = this.hostCmp.constructionField.getSelection();  
325 - // the new value of construction field  
326 - var newConstruction = "";  
327 - // replacement of selection into construction field by text of clicked button  
328 - newConstruction = selection.beforeText + b.text;  
329 - var caretPos = newConstruction.length;  
330 - newConstruction += selection.afterText;  
331 - this.hostCmp.constructionField.setValue(newConstruction);  
332 - // set Caret Position after inserted Text  
333 - this.hostCmp.constructionField.setCaretPosition(caretPos);  
334 - }  
335 - })  
336 - },  
337 - this  
338 - );  
339 -  
340 - return btns;  
341 - },  
342 -  
343 - createAllFunctionBtns : function()  
344 - {  
345 - this.createFunctionBtns('MathFunctions','Simple Maths');  
346 - this.createFunctionBtns('VectorFunctions','Vector Functions');  
347 - this.createFunctionBtns('TimeFunctions','Statistics');  
348 - this.createFunctionBtns('FunctionsSliding','Statistics/Sliding');  
349 - this.createFunctionBtns('AmdaFunctions','Special');  
350 - },  
351 -  
352 - createAllConstantBtns : function()  
353 - {  
354 - this.createConstantBtns('Space','Planets Constants');  
355 - this.createConstantBtns('Physics','Physics Constants');  
356 - this.createConstantBtns('Units','Units Conversion');  
357 - },  
358 -  
359 - createConstantBtns : function(item, tabTitle)  
360 - {  
361 - var constTab = this.win.query('#calc_tab_const_id');  
362 -  
363 - if (constTab.length < 1)  
364 - return;  
365 -  
366 - switch (item)  
367 - {  
368 - case 'Space' :  
369 - amdaUI.CalculatorUI.constantStore.filter('kind','space');  
370 - break;  
371 - case 'Physics' :  
372 - amdaUI.CalculatorUI.constantStore.filter('kind','physics');  
373 - break;  
374 - case 'Units' :  
375 - amdaUI.CalculatorUI.constantStore.filter('kind','units');  
376 - break;  
377 - }  
378 -  
379 - var crtTab = constTab[0].add(  
380 - {  
381 - title : tabTitle,  
382 - defaults: { xtype: 'button', columnWidth: .20}  
383 - });  
384 -  
385 - amdaUI.CalculatorUI.constantStore.each( function(c){  
386 - crtTab.add(  
387 - {  
388 - text: c.get('name'),  
389 - tooltip: c.get('units') == '' ? c.get('info')+'<br/>'+c.get('value') :  
390 - c.get('info')+'<br/>'+c.get('value')+' '+c.get('units'),  
391 - scope: this,  
392 - handler: function(b,e){  
393 - // keep selection into construction field  
394 - var selection = this.hostCmp.constructionField.getSelection();  
395 - // the new value of construction field  
396 - var newConstruction = "";  
397 - // replacement of selection into construction field by text of clicked button  
398 - newConstruction = selection.beforeText + '@'+b.text;  
399 - var caretPos = newConstruction.length;  
400 - newConstruction += selection.afterText;  
401 - this.hostCmp.constructionField.setValue(newConstruction);  
402 - // set Caret Position after inserted Text  
403 - this.hostCmp.constructionField.setCaretPosition(caretPos);  
404 - }  
405 - });  
406 - },this);  
407 -  
408 - //clear filter  
409 - amdaUI.CalculatorUI.constantStore.clearFilter();  
410 - },  
411 -  
412 - createFunctionBtns : function(item, tabTitle)  
413 - {  
414 - var funcTab = this.win.query('#calc_tab_func_id');  
415 -  
416 - if (funcTab.length < 1)  
417 - return;  
418 -  
419 - switch (item)  
420 - {  
421 - case 'MathFunctions' :  
422 - amdaUI.CalculatorUI.functionStore.filter('kind','math');  
423 - break;  
424 - case 'AmdaFunctions' :  
425 - amdaUI.CalculatorUI.functionStore.filter('kind','amda');  
426 - break;  
427 - case 'TimeFunctions' :  
428 - amdaUI.CalculatorUI.functionStore.filter('kind','time');  
429 - break;  
430 - case 'FunctionsSliding' :  
431 - amdaUI.CalculatorUI.functionStore.filter('kind','sliding');  
432 - break;  
433 - case 'VectorFunctions' :  
434 - amdaUI.CalculatorUI.functionStore.filter('kind','vector');  
435 - break;  
436 - }  
437 -  
438 - var crtTab = funcTab[0].add(  
439 - {  
440 - title : tabTitle,  
441 - defaults: { xtype: 'button', columnWidth: .20}  
442 - });  
443 -  
444 - amdaUI.CalculatorUI.functionStore.each( function(f){  
445 - crtTab.add(  
446 - {  
447 - text: f.get('name'),  
448 - args: f.get('args'),  
449 - params: f.get('params'),  
450 - prompt: f.get('prompt'),  
451 - tooltip: f.get('info_brief'),  
452 - scope: this,  
453 - handler: function(b,e){  
454 - var selection = this.hostCmp.constructionField.getSelection();  
455 - var selectedText = selection&&selection.text!="" ? Ext.util.Format.trim(selection.text) : null;  
456 - if (selectedText && selectedText!==""){  
457 - selectedText.replace("[","(");  
458 - selectedText.replace("{","(");  
459 - selectedText.replace("}",")");  
460 - selectedText.replace("]",")");  
461 - }  
462 - // Formula Parsing for arguments  
463 - var params = this.parseArgsInFormula(selectedText,0); 21 + extend: 'Ext.util.Observable',
  22 +
  23 + requires: [
  24 + 'amdaModel.Constant',
  25 + 'amdaModel.Function'
  26 + ],
  27 +
  28 + alias: 'plugin.calculator',
  29 +
  30 + statics: {
  31 + constantStore: null,
  32 + functionStore: null
  33 + },
  34 +
  35 + win: null,
  36 +
  37 + constructor: function (config) {
  38 + Ext.apply(this, config);
  39 + this.callParent(arguments);
  40 + },
  41 +
  42 + init: function (cmp)
  43 + {
  44 + this.hostCmp = cmp;
  45 + this.hostCmp.on({
  46 + scope: this,
  47 + added: function () {
  48 + this.hostCmp.ownerCt.on({
  49 + render: this.onRender,
  50 + show: this.onShow,
  51 + hide: this.onHide,
  52 + scope: this});
  53 + }
  54 + });
  55 + },
  56 +
  57 + onRender: function ()
  58 + {
  59 + this.win = new Ext.Window({
  60 + width: 350,
  61 + height: 170,
  62 + x: 380, y: 0,
  63 + baseCls: 'x-panel',
  64 + title: 'Tools For ' + this.context + ' Construction',
  65 + layout: 'fit',
  66 + closable: false,
  67 + collapsible: true,
  68 + constrain: true,
  69 + floating: true,
  70 + ghost: false,
  71 + renderTo: this.hostCmp.id,
  72 + items: this.getFormConfig(),
  73 + listeners: {
  74 + boxready: function (w)
  75 + {
  76 + if (w.y + w.height > myDesktopApp.desktop.el.getHeight())
  77 + w.el.setY((myDesktopApp.desktop.el.getHeight() - w.height > 0) ? myDesktopApp.desktop.el.getHeight() - w.height : 0);
  78 + }
  79 + },
  80 + getConstrainVector: function (constrainTo) {
  81 + var me = this;
  82 + if (me.constrain || me.constrainHeader) {
  83 + constrainTo = constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container || me.el.getScopeParent();
  84 + return (me.constrainHeader ? me.header.el : me.el).getConstrainVector(constrainTo);
  85 + }
  86 + }
  87 + });
  88 +
  89 + //load constants store
  90 + if (!amdaUI.CalculatorUI.constantStore)
  91 + {
  92 + amdaUI.CalculatorUI.constantStore = Ext.create('Ext.data.Store', {model: 'amdaModel.Constant'});
  93 + amdaUI.CalculatorUI.constantStore.load({
  94 + scope: this,
  95 + callback: function (records, operation, success)
  96 + {
  97 + this.createAllConstantBtns();
  98 + }
  99 + });
  100 + } else
  101 + this.createAllConstantBtns();
  102 +
  103 + //load functions store
  104 + if (!amdaUI.CalculatorUI.functionStore)
  105 + {
  106 + amdaUI.CalculatorUI.functionStore = Ext.create('Ext.data.Store', {model: 'amdaModel.Function'});
  107 + amdaUI.CalculatorUI.functionStore.load({
  108 + scope: this,
  109 + callback: function (records, operation, success)
  110 + {
  111 + this.createAllFunctionBtns();
  112 + }
  113 + });
  114 + } else
  115 + this.createAllFunctionBtns();
  116 + },
  117 +
  118 + onShow: function () {
  119 + this.win.show();
  120 + var parentWin = this.hostCmp.findParentByType('window');
  121 +
  122 + if (parentWin.getId() === myDesktopApp.dynamicModules.param.id) {
  123 + this.win.setPosition(335, 10);
  124 + } else {
  125 +
  126 + var posX = parentWin.getWidth() - this.win.getWidth() - 30;
  127 + var posY = parentWin.getHeight() - this.win.getHeight() - 110 - 30/*20*/;
  128 + this.win.setPosition(posX, posY);//(420,290);
  129 + }
  130 + },
  131 +
  132 + onHide: function ()
  133 + {
  134 + this.win.hide();
  135 + },
  136 +
  137 + getFormConfig: function () {
  138 + return {
  139 + xtype: 'tabpanel',
  140 + border: false, frame: true, plain: true,
  141 + enableTabScroll: true,
  142 + defaults: {frame: true, border: false, plain: true, autoScroll: true},
  143 + activeTab: 0,
  144 + items: [{
  145 + title: 'Calculator', layout: 'column',
  146 + defaults: {xtype: 'button', columnWidth: .11},
  147 + items: this.getItems('Calculator')
  148 + }, {
  149 + title: 'Constants', xtype: 'tabpanel', //iconCls: 'tabs',
  150 + enableTabScroll: true, tabPosition: 'bottom',
  151 + defaults: {frame: true, border: false, plain: true, layout: 'column', autoScroll: true},
  152 + activeTab: 0,
  153 + id: 'calc_tab_const_id'
  154 + }, {
  155 + title: 'Functions', xtype: 'tabpanel', //iconCls: 'tabs',
  156 + enableTabScroll: true, tabPosition: 'bottom',
  157 + defaults: {frame: true, border: false, plain: true, layout: 'column', autoScroll: true},
  158 + activeTab: 0,
  159 + id: 'calc_tab_func_id'
  160 + }]
  161 + };
  162 + },
  163 +
  164 + /**
  165 + * Prompt any argument to user before processing Formula
  166 + * @param sel selected text
  167 + * @param currentBtn calculator button pressed
  168 + * @param params array of parameters
  169 + */
  170 + preProcessFormula: function (sel, currentBtn, params) {
  171 +
  172 + if (currentBtn.initialConfig.args != 0 && currentBtn.initialConfig.prompt != "") {
  173 + // Prompt for user precision and process the result using a callback
  174 + Ext.Msg.prompt('Argument', currentBtn.initialConfig.prompt, function (bt2, text) {
  175 + if (bt2 === 'ok') {
  176 + var afterParamsText = "," + text + ")";
  177 + //TODO: more than one args and prompt
  178 + this.processFormula(sel, currentBtn, params, afterParamsText);
  179 +
  180 + }
  181 + }, this);
  182 + } else {
  183 + this.processFormula(sel, currentBtn, params, ")");
  184 + }// endif prompt
  185 + },
  186 +
  187 + processFormula: function (sel, currentBtn, params, afterParamsText) {
  188 + // calculation of the required number of parameters
  189 + var nbParams = currentBtn.initialConfig.params;
  190 + var fnText = currentBtn.text.split('(');
  191 + var newConstruction = sel.beforeText + fnText[0] + "(";
  192 + // if there at least one parameter selected
  193 + if (params.length) {
  194 + for (var i = 0; i < nbParams; i++) {
  195 + if (i > 0) {
  196 + newConstruction += ",";
  197 + }
  198 + newConstruction += params[i];
  199 + }
  200 + }
  201 + // we keep position
  202 + var afterParameterPos = newConstruction.length;
  203 + newConstruction += afterParamsText;
  204 + var caretPos = newConstruction.length;
  205 + newConstruction += sel.afterText;
  206 + this.hostCmp.constructionField.setValue(newConstruction);
  207 +
  208 + // If we haven't the right number of selected parameters
  209 + if (params.length < nbParams) {
  210 + var stringParamRequired = currentBtn.initialConfig.params + " parameter(s)";
  211 + Ext.Msg.alert('Caution', 'you\'ll have to add ' + stringParamRequired + ' to apply this function',
  212 + function () {
  213 + // set Caret Position at placement of required parameter in function
  214 + this.hostCmp.constructionField.setCaretPosition(afterParameterPos);
  215 + }, this
  216 + );
  217 + } else {
  218 + // set Caret Position after inserted Text
  219 + this.hostCmp.constructionField.setCaretPosition(caretPos);
  220 + }
  221 + },
  222 +
  223 + /**
  224 + * This method construct an array of arguments into selected text
  225 + * @param selectedText the selection to parse
  226 + * @param parseIndex the index to start parsing
  227 + * @return the arguments array
  228 + */
  229 + parseArgsInFormula: function (selectedText, parseIndex) {
  230 +
  231 + if (!selectedText || selectedText == "") {
  232 + return [];
  233 + } else {
  234 + var params = [];
  235 + var startIndex = parseIndex;
  236 + var curIndex = parseIndex;
  237 + var openBrace = 0;
  238 + var sep = 0;
  239 + var closeBrace = 0;
  240 +
  241 + // while there is a separator
  242 + while (sep != -1) {
  243 + openBrace = selectedText.indexOf("(", curIndex);
  244 + sep = selectedText.indexOf(",", curIndex);
  245 + closeBrace = selectedText.indexOf(")", curIndex);
  246 +
  247 + // if there's an open bracket and no close bracket or inversely
  248 + if (openBrace != -1 && closeBrace == -1 || openBrace == -1 && closeBrace != -1) {
  249 + // invalid selection
  250 + return -1;
  251 + }
  252 +
  253 + // if there's a separator and opening brackets into selection
  254 + if (sep != -1 && openBrace != -1) {
  255 + // if brace is before separator
  256 + if (openBrace < sep) {
  257 + curIndex = this.getEndBracket(selectedText, openBrace + 1);
  258 + if (curIndex === -1) {
  259 + return -1;
  260 + }
  261 + } else {// else separator is before brace
  262 + params.push(selectedText.substring(startIndex, sep));
  263 + startIndex = curIndex = sep + 1;
  264 + }
  265 + // if there's only separators into selection
  266 + } else if (sep != -1) {
  267 + params.push(selectedText.substring(startIndex, sep));
  268 + startIndex = curIndex = sep + 1;
  269 + }
  270 + }
  271 + params.push(selectedText.substring(startIndex, selectedText.length));
  272 + return params;
  273 + }
  274 + },
  275 +
  276 + getEndBracket: function (string, indOpenBrace) {
  277 + // we search for the corresponding end brace (after open bracket)
  278 + var currentIndex = indOpenBrace;
  279 + var nextCloseBrace = 0;
  280 + var nextOpenBrace = 0;
  281 + var braceLevel = 1;
  282 + while (nextCloseBrace !== -1 && braceLevel !== 0) {
  283 + // get index of next opening bracket
  284 + nextOpenBrace = string.indexOf("(", currentIndex);
  285 + // get index of next closing bracket
  286 + nextCloseBrace = string.indexOf(")", currentIndex);
  287 + // if both exist
  288 + if (nextOpenBrace != -1 && nextCloseBrace != -1) {
  289 + // if opening bracket is before closing one
  290 + if (nextOpenBrace < nextCloseBrace) {
  291 + currentIndex = nextOpenBrace + 1;
  292 + braceLevel++;
  293 + } else { // if closing bracket is before opening one
  294 + currentIndex = nextCloseBrace + 1;
  295 + braceLevel--;
  296 + }
  297 + // if there's only a next opening bracket
  298 + } else if (nextOpenBrace != -1 && nextCloseBrace == -1) {
  299 + currentIndex = nextOpenBrace + 1;
  300 + braceLevel++;
  301 + // if there's only a next closing bracket
  302 + } else if (nextOpenBrace == -1 && nextCloseBrace != -1) {
  303 + currentIndex = nextCloseBrace + 1;
  304 + braceLevel--;
  305 + }
  306 + }
  307 + // if no level imbrication left return index after closing bracket of block else -1
  308 + return braceLevel == 0 ? currentIndex : -1;
  309 + },
  310 +
  311 + getCalculatorBtn: function ()
  312 + {
  313 + var btns = [];
  314 +
  315 + Ext.each(CalculatorData, function (c) {
  316 + btns.push({
  317 + text: c,
  318 + scope: this,
  319 + handler: function (b, e) {
  320 + // keep selection into construction field
  321 + var selection = this.hostCmp.constructionField.getSelection();
  322 + // the new value of construction field
  323 + var newConstruction = "";
  324 + // replacement of selection into construction field by text of clicked button
  325 + newConstruction = selection.beforeText + b.text;
  326 + var caretPos = newConstruction.length;
  327 + newConstruction += selection.afterText;
  328 + this.hostCmp.constructionField.setValue(newConstruction);
  329 + // set Caret Position after inserted Text
  330 + this.hostCmp.constructionField.setCaretPosition(caretPos);
  331 + }
  332 + })
  333 + },
  334 + this
  335 + );
  336 +
  337 + return btns;
  338 + },
  339 +
  340 + createAllFunctionBtns: function ()
  341 + {
  342 + this.createFunctionBtns('MathFunctions', 'Simple Maths');
  343 + this.createFunctionBtns('VectorFunctions', 'Vector Functions');
  344 + this.createFunctionBtns('TimeFunctions', 'Statistics');
  345 + this.createFunctionBtns('FunctionsSliding', 'Statistics/Sliding');
  346 + this.createFunctionBtns('PhysicsFunctions', 'Physics');
  347 + this.createFunctionBtns('AmdaFunctions', 'Special');
  348 + },
  349 +
  350 + createAllConstantBtns: function ()
  351 + {
  352 + this.createConstantBtns('Space', 'Planets Constants');
  353 + this.createConstantBtns('Physics', 'Physics Constants');
  354 + this.createConstantBtns('Units', 'Units Conversion');
  355 + },
  356 +
  357 + createConstantBtns: function (item, tabTitle)
  358 + {
  359 + var constTab = this.win.query('#calc_tab_const_id');
  360 +
  361 + if (constTab.length < 1)
  362 + return;
  363 +
  364 + switch (item)
  365 + {
  366 + case 'Space' :
  367 + amdaUI.CalculatorUI.constantStore.filter('kind', 'space');
  368 + break;
  369 + case 'Physics' :
  370 + amdaUI.CalculatorUI.constantStore.filter('kind', 'physics');
  371 + break;
  372 + case 'Units' :
  373 + amdaUI.CalculatorUI.constantStore.filter('kind', 'units');
  374 + break;
  375 + }
  376 +
  377 + var crtTab = constTab[0].add(
  378 + {
  379 + title: tabTitle,
  380 + defaults: {xtype: 'button', columnWidth: .20}
  381 + });
  382 +
  383 + amdaUI.CalculatorUI.constantStore.each(function (c) {
  384 + crtTab.add(
  385 + {
  386 + text: c.get('name'),
  387 + tooltip: c.get('units') == '' ? c.get('info') + '<br/>' + c.get('value') :
  388 + c.get('info') + '<br/>' + c.get('value') + ' ' + c.get('units'),
  389 + scope: this,
  390 + handler: function (b, e) {
  391 + // keep selection into construction field
  392 + var selection = this.hostCmp.constructionField.getSelection();
  393 + // the new value of construction field
  394 + var newConstruction = "";
  395 + // replacement of selection into construction field by text of clicked button
  396 + newConstruction = selection.beforeText + '@' + b.text;
  397 + var caretPos = newConstruction.length;
  398 + newConstruction += selection.afterText;
  399 + this.hostCmp.constructionField.setValue(newConstruction);
  400 + // set Caret Position after inserted Text
  401 + this.hostCmp.constructionField.setCaretPosition(caretPos);
  402 + }
  403 + });
  404 + }, this);
  405 +
  406 + //clear filter
  407 + amdaUI.CalculatorUI.constantStore.clearFilter();
  408 + },
  409 +
  410 + createFunctionBtns: function (item, tabTitle)
  411 + {
  412 + var funcTab = this.win.query('#calc_tab_func_id');
  413 +
  414 + if (funcTab.length < 1)
  415 + return;
  416 + var width = .20;
  417 +
  418 + switch (item)
  419 + {
  420 + case 'MathFunctions' :
  421 + amdaUI.CalculatorUI.functionStore.filter('kind', 'math');
  422 + break;
  423 + case 'AmdaFunctions' :
  424 + amdaUI.CalculatorUI.functionStore.filter('kind', 'amda');
  425 + break;
  426 + case 'TimeFunctions' :
  427 + amdaUI.CalculatorUI.functionStore.filter('kind', 'time');
  428 + break;
  429 + case 'FunctionsSliding' :
  430 + amdaUI.CalculatorUI.functionStore.filter('kind', 'sliding');
  431 + break;
  432 + case 'VectorFunctions' :
  433 + amdaUI.CalculatorUI.functionStore.filter('kind', 'vector');
  434 + break;
  435 + case 'PhysicsFunctions' :
  436 + amdaUI.CalculatorUI.functionStore.filter('kind', 'physics');
  437 + width = .25;
  438 + break;
  439 + }
  440 +
  441 + var crtTab = funcTab[0].add(
  442 + {
  443 + title: tabTitle,
  444 + defaults: {xtype: 'button', columnWidth: width}
  445 + });
  446 +
  447 + amdaUI.CalculatorUI.functionStore.each(function (f) {
  448 + crtTab.add(
  449 + {
  450 + text: f.get('name'),
  451 + args: f.get('args'),
  452 + params: f.get('params'),
  453 + prompt: f.get('prompt'),
  454 + tooltip: f.get('info_brief'),
  455 + scope: this,
  456 + handler: function (b, e) {
  457 + var selection = this.hostCmp.constructionField.getSelection();
  458 + var selectedText = selection && selection.text != "" ? Ext.util.Format.trim(selection.text) : null;
  459 + if (selectedText && selectedText !== "") {
  460 + selectedText.replace("[", "(");
  461 + selectedText.replace("{", "(");
  462 + selectedText.replace("}", ")");
  463 + selectedText.replace("]", ")");
  464 + }
  465 + // Formula Parsing for arguments
  466 + var params = this.parseArgsInFormula(selectedText, 0);
464 // var params = selectedText ? selectedText.split(',') : []; 467 // var params = selectedText ? selectedText.split(',') : [];
465 - if (params === -1) {  
466 - Ext.Msg.alert("Invalid Selection", "Action aborted");  
467 - }  
468 - else {  
469 - // calculation of the required number of parameters  
470 - var nbParams = b.initialConfig.params;  
471 -  
472 - if (params.length>nbParams)  
473 - {  
474 - // Show a dialog using config options:  
475 - Ext.Msg.show({  
476 - title:'Caution',  
477 - msg: 'you have selected more than '+nbParams+' parameter(s) to apply this function<br>Only the first will be kept, others will be deleted',  
478 - buttons: Ext.Msg.OKCANCEL, 468 + if (params === -1) {
  469 + Ext.Msg.alert("Invalid Selection", "Action aborted");
  470 + } else {
  471 + // calculation of the required number of parameters
  472 + var nbParams = b.initialConfig.params;
  473 +
  474 + if (params.length > nbParams)
  475 + {
  476 + // Show a dialog using config options:
  477 + Ext.Msg.show({
  478 + title: 'Caution',
  479 + msg: 'you have selected more than ' + nbParams + ' parameter(s) to apply this function<br>Only the first will be kept, others will be deleted',
  480 + buttons: Ext.Msg.OKCANCEL,
479 // animEl: 'elId', 481 // animEl: 'elId',
480 - icon: Ext.MessageBox.WARNING,  
481 - fn: function(bt1){  
482 - if (bt1 === 'ok'){  
483 - this.preProcessFormula(selection,b, params);  
484 - }  
485 - },  
486 - scope:this  
487 - });  
488 - }  
489 - else  
490 - {  
491 - this.preProcessFormula(selection,b, params);  
492 - }  
493 - }  
494 - }  
495 - });  
496 - },  
497 - this  
498 - );  
499 - //clear filter  
500 - amdaUI.CalculatorUI.functionStore.clearFilter();  
501 - },  
502 -  
503 - getItems: function(item)  
504 - {  
505 - switch (item)  
506 - {  
507 - case 'Calculator':  
508 - return this.getCalculatorBtn();  
509 - default: break;  
510 - }  
511 - return [];  
512 - } 482 + icon: Ext.MessageBox.WARNING,
  483 + fn: function (bt1) {
  484 + if (bt1 === 'ok') {
  485 + this.preProcessFormula(selection, b, params);
  486 + }
  487 + },
  488 + scope: this
  489 + });
  490 + } else
  491 + {
  492 + this.preProcessFormula(selection, b, params);
  493 + }
  494 + }
  495 + }
  496 + });
  497 + },
  498 + this
  499 + );
  500 + //clear filter
  501 + amdaUI.CalculatorUI.functionStore.clearFilter();
  502 + },
  503 +
  504 + getItems: function (item)
  505 + {
  506 + switch (item)
  507 + {
  508 + case 'Calculator':
  509 + return this.getCalculatorBtn();
  510 + default:
  511 + break;
  512 + }
  513 + return [];
  514 + }
513 }); 515 });