diff --git a/js/.eslintrc.yml b/js/.eslintrc.yml
new file mode 100644
index 0000000..8362be8
--- /dev/null
+++ b/js/.eslintrc.yml
@@ -0,0 +1,36 @@
+env:
+  browser: true
+extends: 'eslint:all'
+globals:
+  Ext: true
+plugins:
+  - extjs
+rules:
+  indent: [error, 2, SwitchCase: 1]
+  semi: [warn, never]
+  max-len: [error, code: 120]
+  max-lines: [error, 600]
+  linebreak-style: [error, unix]
+  quotes: [error, single]
+  strict: [error, global]
+  quote-props: [error, as-needed]
+  max-statements: [warn, 20]
+  array-bracket-newline: [warn, consistent]
+  consistent-this: [error, me]
+  padded-blocks: off
+  no-tabs: off
+  no-warning-comments: off
+  func-names: off
+  function-paren-newline: off
+  init-declarations: off
+  sort-keys: off
+  id-length: off
+  no-magic-numbers: off
+  no-invalid-this: off
+
+# ES3/5 compatibility:
+  prefer-template: off
+  no-var: off
+  prefer-arrow-callback: off
+  object-shorthand: off
+  prefer-rest-params: off
diff --git a/js/app/controllers/ExplorerModule.js b/js/app/controllers/ExplorerModule.js
index ea60031..2a12460 100644
--- a/js/app/controllers/ExplorerModule.js
+++ b/js/app/controllers/ExplorerModule.js
@@ -1,343 +1,326 @@
-/** 
+/**
  * Project  : AMDA-NG
  * Name     : ExplorerModule.js
- * @class   amdaDesktop.ExplorerModule 
+ * @class   amdaDesktop.ExplorerModule
  * @extends amdaDesktop.AmdaModule
  * @brief   Explorer Module controller definition
  * @author  CDA
  */
 
-Ext.define('amdaDesktop.ExplorerModule', 
-{
-	extend: 'amdaDesktop.AmdaModule',
-    
-	requires : [
-		'amdaUI.ExplorerUI',
-		'amdaUI.AliasUI',
-		// Node Models
-		'amdaModel.BkgJobNode',
-		'amdaModel.DerivedParamNode',
-		'amdaModel.LocalParamNode',
-		'amdaModel.RemoteParamNode',
-		'amdaModel.RemoteSimuParamNode',
-		'amdaModel.AliasNode',
-		'amdaModel.TimeTableNode',
-		'amdaModel.CatalogNode',
-		'amdaModel.StatisticsNode',	// singleton; not shown in the tree
-		'amdaModel.sharedTimeTableNode',
-		'amdaModel.sharedCatalogNode',
-		'amdaModel.MyDataParamNode',
-		'amdaModel.MyDataNode',
-		'amdaModel.PlotNode',
-		'amdaModel.DownloadNode',  // singleton; not shown in the tree
-		'amdaModel.SearchNode',
-		// Object Models
-		'amdaModel.Parameter',
-		'amdaModel.Search',        
-		'amdaModel.Download',
-		'amdaModel.TimeTable',
-		'amdaModel.Catalog',	
-		'amdaModel.Stats',
-		'amdaModel.FileObject',
-		'amdaModel.FileParamObject',
-		'amdaModel.FilterInfo'
-	],
-    
-	contentId : 'explorerUI',
-   
-	filter : null,
-	filtersStore : null,
-    
-	paramInfoRegistry : {},
-    
-	constructor : function(config)
-	{
-		this.callParent(arguments);
-		if (!this.filtersStore) 
-		{
-			this.filtersStore = Ext.create('Ext.data.Store', {
-					model: 'amdaModel.FilterList' 
-			});                 
-			this.filtersStore.load(); 
-			//  amdaDesktop.ExplorerModule.filtersStore = this.filtersStore;
-		}                
-	},
-        
-	createWindow : function() 
-	{
-		var desktop = this.app.getDesktop();
-        
-		var win = desktop.getWindow(this.id);
-		if (!win) {            
-			win = desktop.createWindow( {
-				id : this.id,
-				title : this.title,
-				width : 340,
-				height : 500,
-				iconCls : this.icon,
-				animCollapse : false,
-				border : false,
-       //         constrainHeader : true,
-				closable: false,
-				stateful : true,
-				stateId  : this.id,
-				stateEvents: ['move','show','resize'],
-				constrain: true,
-				layout   :
-				{
-					type : 'border',
-					align : 'stretch'
-				},
-				x : 5,
-				y : 5,
-				items : [
-					{
-						xtype: 'panelExplorer',
-						id : this.contentId,
-						region: 'center'
-					},
-					{
-						xtype: 'panel', 
-						id : 'LogPanel',
-						region: 'south',
-						height : 150,
-						split : true,
-						layout: {
-							type: 'vbox',
-							align : 'stretch',
-							autoSize : true
-						}, 
-						autoScroll : true,
-						stateful : true,
-						stateId  : 'LogPanel',
-						buttons : [{
-							scope: this,
-							text : 'Clear',
-							handler: function(){	
-								win.items.get('LogPanel').removeAll();  
-								this.addLogMessage('<b>Log</b>');
-							}
-						}]
-					}
-				]
-			});
-		}
-        
-		win.on({
-			activate: {
-				fn : function() 
-				{
-					myDesktopApp.getLoadedModule(this.id, true, function (module) {
-					// on activation when there's a pinedModule 'null'
-						if (module.pinedModule===null) {
-							// get the ZIndex manager
-							var manager = myDesktopApp.desktop.getDesktopZIndexManager();
-							// ordering to send back this window
-							manager.sendToBack(win);
-						} 
-					});
-				},
-				scope: this
-			} 
-		});
-        
-		win.on({
-			show : 
-			{
-				fn : function(o)
-				{
-					//set current filter
-					this.setCrtFilter();    				
-					//resize the explorer in relation with the desktop size
-					if (win.y + win.height > myDesktopApp.desktop.el.getHeight())
-							win.setHeight(myDesktopApp.desktop.el.getHeight() - win.y);
-				},
-				scope : this
-			}
-		});
-        
-		win.addCls('window-active');
-		win.show();
-		this.addLogMessage('<b>Log</b>');
-		return win;        
-	},
-    
-	pinedModule : null,
-    
-	/**
-	* Getter of pinedModule
-	* @return {amdaDesktop.InteractiveModule} The pined module
-	*/
-	getPinedModule : function() 
-	{
-		// get the pined Module
-		return this.pinedModule;
-	},
+'use strict'
 
-	/**
-	* Setter of pinedModule
-	* @param {amdaDesktop.InteractiveModule} The pined module
-	*/
-	setPinedModule : function(amdaModule) 
-	{
-		// set the pined Module
-		this.pinedModule = amdaModule;
-	},
-    
-	/**
-	* Module Attachment Method
-	* @param {amdaDesktop.InteractiveModule} The module to attach
-	*/
-	pinMechanism : function(amdaModule) 
-	{
-		// if there is an old pined module
-		if (this.getPinedModule()!=null) {
-			// get old pined module :
-			var oldPinedModule = this.getPinedModule();
-			// call uncolor method on the old pined Module
-			myDesktopApp.getLoadedModule(oldPinedModule).uncolorHeaderModule();
-		}
-		// set pined module
-		this.setPinedModule(amdaModule);
-		// call color method on pined Module
-		myDesktopApp.getLoadedModule(amdaModule).colorHeaderModule();
-	},
-    
-    
-	addLogMessage : function(message)
-	{
-		var height = 40;
-		var msg = Ext.create('Ext.Component', {
-			height: height,
-			html: message,
-			style: { color : '#555555'}
-		});
-		var desktop = myDesktopApp.getDesktop();
-		var win = desktop.getWindow(this.id);
-		var panel = win.items.get('LogPanel');
-		panel.add(msg);
-	},
-     
-	//
-	setCrtFilter : function()
-	{
-		var me = this;
-		//get current filter to apply
-		AmdaAction.getCrtFilterResult(function (result, e)
-			{
-				var t = e.getTransaction();
-				if (e.status) 
-				{	
-					if (result)
-					{
-						//apply current filter
-						this.filter = result;
-						// add saved filter to Explorer List
-						if (!this.filtersStore.getById(this.filter.id)) 
-							this.filtersStore.add({id : this.filter.id, name : this.filter.name});
-						
-						//apply filter to the tree
-						var desktop = myDesktopApp.getDesktop();
-						var win = desktop.getWindow(this.id);
-						if (win)
-						{
-							var explorerUI = win.query('#'+this.contentId);
-							explorerUI[0].updateFilter();
-						}
-					}
-					else
-						Ext.Msg.show( {
-								title : 'Filter', 
-								msg : 'Cannot apply filter',
-								modal : true,
-								icon : Ext.Msg.ERROR,
-								buttons : Ext.Msg.OK
-						});
-				}
-				else
-				{
-					// FAILURE
-					Ext.Msg.show({title:'Error System', msg: e.message, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});
-				} 
-			},this);
-	},
-     
-	resetFilter : function()
-	{
-		AmdaAction.resetFilter( function (result, e)
-			{
-				var t = e.getTransaction();
-				if (e.status) 
-				{	
-					if (result)
-					{
-						this.setCrtFilter();						
-						//update filter win
-						var desktop = myDesktopApp.getDesktop();
-						var win = desktop.getWindow(myDesktopApp.dynamicModules.filters.id);
-						if (win)
-						{
-							var filterUI = win.query('panelFilters');
-							filterUI[0].reloadFilter();
-						}
-					}
-					else
-						Ext.Msg.show( {
-									title : 'Filter', 
-									msg : 'Cannot reset filter',
-									modal : true,
-									icon : Ext.Msg.ERROR,
-									buttons : Ext.Msg.OK
-						});
-				}
-				else
-				{
-					// FAILURE
-					Ext.Msg.show({title:'Error System', msg: e.message, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});
-				} 
-			},this);
-	},
-     
-	getParamInfo : function(paramId, onReady) 
-	{
-		if (this.paramInfoRegistry[paramId]) 
-		{
-			if (onReady)
-				onReady(this.paramInfoRegistry[paramId]);
-			return this.paramInfoRegistry[paramId];
-		}
-			
-		var me = this;
-		AmdaAction.getParamInfo({"paramId" : paramId}, function (result, e) 
-		{
-			var paramInfo = null;
-			if (e.status === true)
-			{
-				if (!result.success)
-				{
-				//No available info for this parameter. Do not display an error message
-					/*if (result.message && result.message != '')
-						Ext.Msg.show({title:'Get parameter arguments error ', msg: result.message, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});
-					else
-						Ext.Msg.show({title:'Get parameter arguments error', msg: 'Unknown error', icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});*/
-				}
-				else
-				{
-					//Store parameter info in registry
-					me.paramInfoRegistry[paramId] = result;
-					paramInfo = result;
-				}
-			}
-			else
-			{
-				Ext.Msg.show({title:'Error System', msg: e.message, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});
-			}
+/* global AmdaAction, myDesktopApp */
 
-			if (onReady)
-				onReady(paramInfo); 
-		});
-		
-		return null;
-	}
-	
-});
+/**
+ * @typedef {AmdaApp} myDesktopApp
+ * @typedef {Object} AmdaAction
+ * @typedef {Object} Ext
+ */
+
+Ext.define('amdaDesktop.ExplorerModule',
+  {
+    extend: 'amdaDesktop.AmdaModule',
+    requires: [
+      'amdaUI.ExplorerUI',
+      'amdaUI.AliasUI',
+      // Node Models
+      'amdaModel.BkgJobNode',
+      'amdaModel.DerivedParamNode',
+      'amdaModel.LocalParamNode',
+      'amdaModel.RemoteParamNode',
+      'amdaModel.RemoteSimuParamNode',
+      'amdaModel.AliasNode',
+      'amdaModel.TimeTableNode',
+      'amdaModel.CatalogNode',
+      // StatisticsNode: singleton, not shown in the tree
+      'amdaModel.StatisticsNode',
+      'amdaModel.sharedTimeTableNode',
+      'amdaModel.sharedCatalogNode',
+      'amdaModel.MyDataParamNode',
+      'amdaModel.MyDataNode',
+      'amdaModel.PlotNode',
+      // DownloadNode: singleton, not shown in the tree
+      'amdaModel.DownloadNode',
+      'amdaModel.SearchNode',
+      // Object Models
+      'amdaModel.Parameter',
+      'amdaModel.Search',
+      'amdaModel.Download',
+      'amdaModel.TimeTable',
+      'amdaModel.Catalog',
+      'amdaModel.Stats',
+      'amdaModel.FileObject',
+      'amdaModel.FileParamObject',
+      'amdaModel.FilterInfo'
+    ],
+
+    contentId: 'explorerUI',
+    filter: null,
+    filtersStore: null,
+    paramInfoRegistry: {},
+
+    constructor: function () {
+      this.superclass.constructor.apply(this, arguments)
+      if (!this.filtersStore) {
+        this.filtersStore = Ext.create('Ext.data.Store', {model: 'amdaModel.FilterList'})
+        this.filtersStore.load()
+        // Note: amdaDesktop.ExplorerModule.filtersStore = this.filtersStore;
+      }
+    },
+
+    createWindow: function () {
+      var desktop, win
+
+      desktop = this.app.getDesktop()
+      win = desktop.getWindow(this.id)
+      if (!win) {
+        win = desktop.createWindow({
+          id: this.id,
+          title: this.title,
+          width: 340,
+          height: 500,
+          iconCls: this.icon,
+          animCollapse: false,
+          border: false,
+          closable: false,
+          stateful: true,
+          stateId: this.id,
+          stateEvents: [
+            'move',
+            'show',
+            'resize'
+          ],
+          constrain: true,
+          layout:
+            {
+              type: 'border',
+              align: 'stretch'
+            },
+          x: 5,
+          y: 5,
+          items: [
+            {
+              xtype: 'panelExplorer',
+              id: this.contentId,
+              region: 'center'
+            },
+            {
+              xtype: 'panel',
+              id: 'LogPanel',
+              region: 'south',
+              height: 150,
+              split: true,
+              layout: {
+                type: 'vbox',
+                align: 'stretch',
+                autoSize: true
+              },
+              autoScroll: true,
+              stateful: true,
+              stateId: 'LogPanel',
+              buttons: [{
+                scope: this,
+                text: 'Clear',
+                handler: function () {
+                  var logPanel = win.items.get('LogPanel')
+                  logPanel.removeAll()
+                }
+              }]
+            }
+          ]
+        })
+      }
+
+      win.on('activate', function () {
+        var manager
+        myDesktopApp.getLoadedModule(this.id, true, function (module) {
+          // On activation when there's a pinedModule 'null'
+          if (module.pinedModule === null) {
+            // Get the ZIndex manager
+            manager = myDesktopApp.desktop.getDesktopZIndexManager()
+            // Ordering to send back this window
+            manager.sendToBack(win)
+          }
+        })
+      }, this)
+
+      win.on('show', function () {
+        var desktopHeight
+        // Set current filter
+        this.setCrtFilter()
+        // Resize the explorer in relation with the desktop size
+        if (win.y + win.height > myDesktopApp.desktop.el.getHeight()) {
+          desktopHeight = myDesktopApp.desktop.el.getHeight()
+          win.setHeight(desktopHeight - win.y)
+        }
+      }, this)
+
+      win.addCls('window-active')
+      win.show()
+      return win
+    },
+
+    pinedModule: null,
+
+    /**
+     * Getter of pinedModule
+     * @return {amdaDesktop.InteractiveModule} The pined module
+     */
+    getPinedModule: function () {
+      // Get the pined Module
+      return this.pinedModule
+    },
+
+    /**
+     * Setter of pinedModule
+     * @param {amdaDesktop.InteractiveModule} amdaModule The pined module
+     * @returns {void}
+     */
+    setPinedModule: function (amdaModule) {
+      // Set the pined Module
+      this.pinedModule = amdaModule
+    },
+
+    /**
+     * Module Attachment Method
+     * @param {amdaDesktop.InteractiveModule} amdaModule The module to attach
+     * @returns {void}
+     */
+    pinMechanism: function (amdaModule) {
+      var loadedAmdaModule, loadedPinedModule, oldPinedModule
+      // If there is an old pined module
+      if (this.getPinedModule() !== null) {
+        // Get old pined module :
+        oldPinedModule = this.getPinedModule()
+        loadedPinedModule = myDesktopApp.getLoadedModule(oldPinedModule)
+        // Call uncolor method on the old pined Module
+        loadedPinedModule.uncolorHeaderModule()
+      }
+      // Set pined module
+      this.setPinedModule(amdaModule)
+      // Call color method on pined Module
+      loadedAmdaModule = myDesktopApp.getLoadedModule(amdaModule)
+      loadedAmdaModule.colorHeaderModule()
+    },
+
+    addLogMessage: function (message) {
+      var height, msg, panel, win
+      height = 40
+      msg = Ext.create('Ext.Component', {
+        height: height,
+        html: message,
+        style: {color: '#555555'}
+      })
+      win = myDesktopApp.getDesktop().getWindow(this.id)
+      panel = win.items.get('LogPanel')
+      panel.add(msg)
+    },
+
+    //
+    setCrtFilter: function () {
+      // Get current filter to apply
+      AmdaAction.getCrtFilterResult(function (result, e) {
+        var explorerUI, win
+        if (!e.status) {
+          Ext.Msg.show({
+            title: 'Error System',
+            msg: e.message,
+            icon: Ext.MessageBox.ERROR,
+            buttons: Ext.Msg.OK
+          })
+        } else if (result) {
+          // Apply current filter
+          this.filter = result
+          // Add saved filter to Explorer List
+          if (!this.filtersStore.getById(this.filter.id)) {
+            this.filtersStore.add({
+              id: this.filter.id,
+              name: this.filter.name
+            })
+          }
+
+          // Apply filter to the tree
+          win = myDesktopApp.getDesktop().getWindow(this.id)
+          if (win) {
+            explorerUI = win.query('#' + this.contentId)
+            explorerUI[0].updateFilter()
+          }
+        } else {
+          Ext.Msg.show({
+            title: 'Filter',
+            msg: 'Cannot apply filter',
+            modal: true,
+            icon: Ext.Msg.ERROR,
+            buttons: Ext.Msg.OK
+          })
+        }
+      }, this)
+    },
+
+    resetFilter: function () {
+      AmdaAction.resetFilter(function (result, e) {
+        var filterUI, win
+        if (!e.status) {
+          Ext.Msg.show({
+            title: 'Error System',
+            msg: e.message,
+            icon: Ext.MessageBox.ERROR,
+            buttons: Ext.Msg.OK
+          })
+        } else if (result) {
+          this.setCrtFilter()
+          // Update filter win
+          win = myDesktopApp.getDesktop().getWindow(myDesktopApp.dynamicModules.filters.id)
+          if (win) {
+            filterUI = win.query('panelFilters')
+            filterUI[0].reloadFilter()
+          }
+        } else {
+          Ext.Msg.show({
+            title: 'Filter',
+            msg: 'Cannot reset filter',
+            modal: true,
+            icon: Ext.Msg.ERROR,
+            buttons: Ext.Msg.OK
+          })
+        }
+      }, this)
+    },
+
+    getParamInfo: function (paramId, onReady) {
+      var me = this
+
+      if (this.paramInfoRegistry[paramId]) {
+        if (onReady) {
+          onReady(this.paramInfoRegistry[paramId])
+        }
+        return this.paramInfoRegistry[paramId]
+      }
+
+      AmdaAction.getParamInfo({paramId: paramId}, function (result, e) {
+        var paramInfo = null
+        if (e.status === true) {
+          // No available info for this parameter, do not display an error message if no success
+          if (result.success) {
+            // Store parameter info in registry
+            me.paramInfoRegistry[paramId] = result
+            paramInfo = result
+          }
+        } else {
+          Ext.Msg.show({
+            title: 'Error System',
+            msg: e.message,
+            icon: Ext.MessageBox.ERROR,
+            buttons: Ext.Msg.OK
+          })
+        }
+
+        if (onReady) {
+          onReady(paramInfo)
+        }
+      })
+
+      return null
+    }
+
+  })
diff --git a/js/app/controllers/JobsMgr.js b/js/app/controllers/JobsMgr.js
index c4e4ca1..4091401 100644
--- a/js/app/controllers/JobsMgr.js
+++ b/js/app/controllers/JobsMgr.js
@@ -1,142 +1,212 @@
-/** 
+/**
  * Project  : AMDA-NG4
  * Name     : JobsMgr.js
- * @class   amdaDesktop.JobsMgr 
- * @extends Ext.AbstractManager 
+ * @class   amdaDesktop.JobsMgr
+ * @extends Ext.AbstractManager
  * @brief    Manages Jobs In Progress
  * @author  elena
  * @version $Id: JobsMgr.js 2759 2015-02-19 12:32:31Z elena $
- * @todo
  *******************************************************************************
  *    FT Id     :   Date   : Name - Description
  *******************************************************************************
- *  :           :28/01/2011: 
- 
+ *  :           :28/01/2011:
  */
+
+'use strict'
+/* global AmdaAction, myDesktopApp, amdaDesktop, amdaModel, amdaUI */
+
+/**
+ * @typedef {{
+ *   success:     boolean,
+ *   id:          string
+ *   name:        string
+ *   status:      string
+ *   jobType:     string
+ *   info:        string
+ *   start:       string
+ *   stop:        string
+ *   folder:      string
+ *   result:      string
+ *   format:      string
+ *   compression: string
+ *   sendToSamp:  boolean
+ * }} Job
+ */
+
+/**
+ * @typedef {{
+ *   success:        boolean,
+ *   nFinished:      number,
+ *   nError:         number,
+ *   nInProgress:    number,
+ *   jobsFinished:   Array.<Job>,
+ *   jobsInProgress: Array.<Job>
+ * }} Result
+ */
+
+/**
+ * @typedef {{
+ *   status: boolean
+ * }} Error
+ */
+
 Ext.define('amdaDesktop.JobsMgr', {
-     
-     extend: 'Ext.AbstractManager',
-     singleton: true,
-     first : true,
-     jobsInProgress : 0,
-     jobsFinished : 0, 
-     jobsError : 0,      
-     updateStatus : null,
-     interval : 60000,   //   //msec 60000
-    
-     jobTree : null,
-     
-     
-//TODO first => at login !!!
-     
-     getStatus :  function(){  
-             	   
-	     AmdaAction.getJobs(function(res,e){
-//TODO Errors processing	       
-             if (e.status) {        
-		 if (res.success) {
-// Update from Server Jobs Running and Finished	 				 
-		      if (res.nFinished > 0 || res.nError ) {
-			   Ext.each(res.jobsFinished, function(item, index, all){
-                             if (item.status != 'old') {
-                                switch (item.jobType) {
-                                case 'condition' : var type = 'Data Mining '; break;
-                                case 'request' : var type = 'Plot '; break;   
-                                case 'download' : var type = 'Download '; break;
-				    case 'statistics' : var type = 'Statistics '; break;
-                                default: var type = 'unknown';    
-                                }
-                                var message = Ext.Date.format(new Date(), 'd-m-Y H:i:s: ') + ': '+ type + ' '+item.name + ' completed';
-                                myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id, true, function (module) {
-                                	module.addLogMessage(message);
-                                });
-                             }
-			   }); 
-			   
-		      }
-			   
- 		      if (amdaDesktop.JobsMgr.first && (res.nInProgress > 0 || res.nFinished > 0 || res.nError > 0)) {			  
-			myDesktopApp.infoMsg('You have  '+  res.nFinished  + ' new finished jobs, ' + res.nInProgress + ' jobs in progress and ' + res.nError + 'jobs in error'); 		      
-		      } 
-	     		 
-		      amdaDesktop.JobsMgr.jobsInProgress = res.nInProgress;
-		      amdaDesktop.JobsMgr.jobsFinished = res.nFinished;
-		      amdaDesktop.JobsMgr.jobsError = res.nError;  
-	        
-		      if (res.nInProgress == 0 && amdaDesktop.JobsMgr.updateStatus)
-			 {				  			 				 
-			            Ext.TaskManager.stop(amdaDesktop.JobsMgr.updateStatus);
-				    amdaDesktop.JobsMgr.updateStatus = null;		  				  
-		       }
-		      if (res.nInProgress > 0 && !amdaDesktop.JobsMgr.updateStatus)
-		        {
-				   amdaDesktop.JobsMgr.updateStatus =
-				    Ext.TaskManager.start({
-				       run : amdaDesktop.JobsMgr.getStatus,
-				       interval : amdaDesktop.JobsMgr.interval 
-				    });			  	    				  
-			} 
-			
-		      if ((res.nFinished > 0 || res.nError) && !amdaDesktop.JobsMgr.first)  {
-			 if (!amdaDesktop.JobsMgr.jobTree) 
-			      amdaDesktop.JobsMgr.jobTree = Ext.getCmp(amdaUI.ExplorerUI.JOB_TAB.TREE_ID);
-			  
-			  Ext.each(res.jobsFinished, function(job) {
-                         
-			   switch (job.jobType) {
-			     case 'condition':	
-//TODO get root nodes depending on conditions			       
-				  var resRootNode = 
-				     amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.RES_ROOT_NODE.CONDITION, true);
-				  var jobRootNode = 
-				      amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.JOB_ROOT_NODE.CONDITION, true);
-			          break;
-			      case 'request' :
-				  var resRootNode = 
-				     amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.RES_ROOT_NODE.PLOT, true);
-				  var jobRootNode = 
-				      amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.JOB_ROOT_NODE.PLOT, true);
-			       break;
-			      case 'download' :
-				  var resRootNode = 
-				     amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.RES_ROOT_NODE.DOWNLOAD, true);
-				  var jobRootNode = 
-				      amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.JOB_ROOT_NODE.DOWNLOAD, true);
-			       break;
-			      case 'statistics' :
-				  var resRootNode = 
-				     amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.RES_ROOT_NODE.STATISTICS, true);
-				  var jobRootNode = 
-				      amdaDesktop.JobsMgr.jobTree.getRootNode().findChild('id',amdaModel.BkgJobNode.JOB_ROOT_NODE.STATISTICS, true);
-			       break; 
-			      default :
-			   }
-//TODO check: job sub tree is always expanded - after the first job is launched - there cannot be too many unfinished jobs on login
-//  so callback is not needed			   
-				  if (!resRootNode.parentNode.isExpanded()) resRootNode.parentNode.expand(false);				
-				  if (!jobRootNode.parentNode.isExpanded()) jobRootNode.parentNode.expand(false);
-				  if (!jobRootNode.isExpanded()) jobRootNode.expand(false);
-				  
-				  //AKKA - use processid to find job
-				  var nodeToMove = jobRootNode.findChild('id', job.id, false);
-				  nodeToMove.set('status',job.status);
-//TODO check if really job.status				  
-				  nodeToMove.set('iconCls','icon-'+job.status);
-
-				  jobRootNode.removeChild(nodeToMove); 
-			 	  
-				  if (!resRootNode.isExpanded()) {				      
-				     resRootNode.expand(false, function(){				   
-				       if (!resRootNode.findChild('id', nodeToMove.get('processId'))) 
-					    resRootNode.appendChild(nodeToMove);
-				    });				      
-				  }  
-				  else resRootNode.appendChild(nodeToMove); 				   				  			       
-			}); 
-		      } 
-		 }  
+  extend: 'Ext.AbstractManager',
+  first: true,
+  interval: 60000,
+  jobTree: null,
+  jobsError: 0,
+  jobsFinished: 0,
+  jobsInProgress: 0,
+  singleton: true,
+  updateStatus: null,
+
+  // TODO first => at login !!!
+  getStatus: function () {
+    AmdaAction.getJobs(
+
+      /**
+       * Callback function for AmdaAction.getJobs.
+       * @param {Result} res Server response of the PROCESSRUNNINGINFO request.
+       * @param {Error} error Error, if any.
+       * @returns {void}
+       */
+      function (res, error) {
+        // TODO Errors processing
+        var message, strDate, type
+        if (error.status && res.success && res.jobsFinished) {
+          // Update from Server Jobs Running and Finished
+          if (res.nFinished > 0 || res.nError) {
+            Ext.Array.each(res.jobsFinished, function (job) {
+
+              /**
+               * Called asynchronously on each finished job
+               * @param {Job} job The finished job.
+               */
+              if (job.status !== 'old') {
+                switch (job.jobType) {
+                  case 'condition':
+                    type = 'Data Mining '
+                    break
+                  case 'request':
+                    type = 'Plot '
+                    break
+                  case 'download':
+                    type = 'Download '
+                    break
+                  case 'statistics':
+                    type = 'Statistics '
+                    break
+                  default:
+                    type = 'unknown'
+                }
+                strDate = Ext.Date.format(new Date(), 'd-m-Y H:i:s: ')
+                message = strDate + type + ' ' + job.name + ' completed'
+                myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id, true,
+                  function (mod) {
+                    mod.addLogMessage(message)
+                  })
+              }
+            })
+          }
+
+          if (amdaDesktop.JobsMgr.first && (res.nInProgress > 0 || res.nFinished > 0 || res.nError > 0)) {
+            myDesktopApp.infoMsg('You have  ' + res.nFinished + ' new finished jobs, ' +
+              res.nInProgress + ' jobs in progress ' +
+              'and ' + res.nError + 'jobs in error')
+          }
+
+          amdaDesktop.JobsMgr.jobsInProgress = res.nInProgress
+          amdaDesktop.JobsMgr.jobsFinished = res.nFinished
+          amdaDesktop.JobsMgr.jobsError = res.nError
+
+          if (res.nInProgress === 0 && amdaDesktop.JobsMgr.updateStatus) {
+            Ext.TaskManager.stop(amdaDesktop.JobsMgr.updateStatus)
+            amdaDesktop.JobsMgr.updateStatus = null
+          }
+          if (res.nInProgress > 0 && !amdaDesktop.JobsMgr.updateStatus) {
+            amdaDesktop.JobsMgr.updateStatus =
+              Ext.TaskManager.start({
+                interval: amdaDesktop.JobsMgr.interval,
+                run: amdaDesktop.JobsMgr.getStatus
+              })
+          }
+
+          if ((res.nFinished > 0 || res.nError) && !amdaDesktop.JobsMgr.first && res.jobsFinished) {
+            if (!amdaDesktop.JobsMgr.jobTree) {
+              amdaDesktop.JobsMgr.jobTree = Ext.getCmp(amdaUI.ExplorerUI.JOB_TAB.TREE_ID)
             }
-            amdaDesktop.JobsMgr.first = false;
-        });        
-    }                    
+
+            Ext.Array.each(res.jobsFinished,
+
+              /**
+               * Called asynchronously on each finished job.
+               * @param {Job} job The finished job.
+               * @return {void}
+               */
+              function (job) {
+                var bkgJob, jobRootNode, nodeToMove, resRootNode, root
+                bkgJob = amdaModel.BkgJobNode
+                root = amdaDesktop.JobsMgr.jobTree.getRootNode()
+                switch (job.jobType) {
+                  case 'condition':
+                    resRootNode = root.findChild('id', bkgJob.RES_ROOT_NODE.CONDITION, true)
+                    jobRootNode = root.findChild('id', bkgJob.JOB_ROOT_NODE.CONDITION, true)
+                    break
+                  case 'request':
+                    resRootNode = root.findChild('id', bkgJob.RES_ROOT_NODE.PLOT, true)
+                    jobRootNode = root.findChild('id', bkgJob.JOB_ROOT_NODE.PLOT, true)
+                    break
+                  case 'download':
+                    resRootNode = root.findChild('id', bkgJob.RES_ROOT_NODE.DOWNLOAD, true)
+                    jobRootNode = root.findChild('id', bkgJob.JOB_ROOT_NODE.DOWNLOAD, true)
+                    break
+                  case 'statistics':
+                    resRootNode = root.findChild('id', bkgJob.RES_ROOT_NODE.STATISTICS, true)
+                    jobRootNode = root.findChild('id', bkgJob.JOB_ROOT_NODE.STATISTICS, true)
+                    break
+                  default:
+                    break
+                }
+
+                /*
+                 * TODO check job sub tree is always expanded - after the first job is launched - there
+                 * cannot be too many unfinished jobs on login so callback is not needed
+                 */
+
+                if (!resRootNode.parentNode.isExpanded()) {
+                  resRootNode.parentNode.expand(false)
+                }
+                if (!jobRootNode.parentNode.isExpanded()) {
+                  jobRootNode.parentNode.expand(false)
+                }
+                if (!jobRootNode.isExpanded()) {
+                  jobRootNode.expand(false)
+                }
+
+                // AKKA - use processid to find job
+                nodeToMove = jobRootNode.findChild('id', job.id, false)
+                nodeToMove.set('status', job.status)
+
+                // TODO check if really job.status
+
+                nodeToMove.set('iconCls', 'icon-' + job.status)
+                jobRootNode.removeChild(nodeToMove)
+
+                if (resRootNode.isExpanded()) {
+                  resRootNode.appendChild(nodeToMove)
+                } else {
+                  resRootNode.expand(false, function () {
+                    var wantedId = nodeToMove.get('processId')
+                    if (!resRootNode.findChild('id', wantedId)) {
+                      resRootNode.appendChild(nodeToMove)
+                    }
+                  })
+                }
+              })
+          }
+        }
+        amdaDesktop.JobsMgr.first = false
+      })
+  }
 })
--
libgit2 0.21.2