diff --git a/screening/source/core-plugins/microscopy/1/as/webapps/image-viewer/html/image-viewer.js b/screening/source/core-plugins/microscopy/1/as/webapps/image-viewer/html/image-viewer.js
index 70b9381f58d6b3bda23a162b9b452add52104f21..3f91894ec998f811ff15c365eb40aa98d78b2199 100644
--- a/screening/source/core-plugins/microscopy/1/as/webapps/image-viewer/html/image-viewer.js
+++ b/screening/source/core-plugins/microscopy/1/as/webapps/image-viewer/html/image-viewer.js
@@ -1,3 +1,91 @@
+//
+// IMAGE VIEWER VIEW
+//
+
+function ImageViewerView(controller) {
+    this.init(controller);
+}
+
+$.extend(ImageViewerView.prototype, {
+    init: function(controller){
+    	this.controller = controller;
+    	this.imageLoader = new ImageLoader();
+    	this.panel = $("<div>");
+    },
+    
+    render: function(){
+        this.panel.append(this.createChannelWidget());
+        this.panel.append(this.createResolutionWidget());
+        this.panel.append(this.createChannelStackWidget());
+        this.panel.append(this.createImageWidget());
+        
+        this.rendered = true;
+        this.refresh();
+        
+        return this.panel;
+    },
+    
+    refresh: function(){
+    	if(!this.rendered){
+    		return;
+    	}
+
+    	this.channelWidget.setSelectedChannel(this.controller.getSelectedChannel());
+    	this.channelWidget.setSelectedMergedChannels(this.controller.getSelectedMergedChannels());
+    	this.resolutionWidget.setSelectedResolution(this.controller.getSelectedResolution());
+    	this.channelStackWidget.setSelectedChannelStackId(this.controller.getSelectedChannelStackId());
+    	this.imageWidget.setImage(this.controller.getSelectedImageData());
+    },
+    
+    createChannelWidget: function(){
+    	var thisView = this;
+    	
+    	var widget = new ChannelChooserWidget(this.controller.getAllChannels());
+        widget.addChangeListener(function(){
+        	thisView.controller.setSelectedChannel(thisView.channelWidget.getSelectedChannel());
+        	thisView.controller.setSelectedMergedChannels(thisView.channelWidget.getSelectedMergedChannels());
+        });
+      
+        this.channelWidget = widget;
+        return widget.render();
+    },
+    
+    createResolutionWidget: function(){
+    	var thisView = this;
+
+    	var widget = new ResolutionChooserWidget(this.controller.getAllResolutions());
+        widget.addChangeListener(function(){
+        	thisView.controller.setSelectedResolution(thisView.resolutionWidget.getSelectedResolution());
+        });
+        
+        this.resolutionWidget = widget;
+        return widget.render();
+    },
+    
+    createChannelStackWidget: function(){
+    	var thisView = this;
+    	
+    	var widget = new ChannelStackChooserWidget(this.controller.getAllChannelStacks(), function(channelStack, callback){
+        	var imageData = thisView.controller.getSelectedImageData();
+        	imageData.setChannelStackId(channelStack.id);
+        	thisView.imageLoader.loadImage(imageData, callback);
+        });
+    	
+        widget.addChangeListener(function(){
+        	thisView.controller.setSelectedChannelStackId(thisView.channelStackWidget.getSelectedChannelStackId());
+        });
+        
+        this.channelStackWidget = widget;
+        return widget.render();
+    },
+    
+    createImageWidget: function(){
+        this.imageWidget = new ImageWidget(this.imageLoader);
+        return this.imageWidget.render();
+    }
+    
+});
+
 //
 // IMAGE VIEWER
 //
@@ -7,6 +95,7 @@ function ImageViewerWidget(openbis, dataSetCode) {
 
 $.extend(ImageViewerWidget.prototype, {
     init: function(openbis, dataSetCode){
+    	this.setView(new ImageViewerView(this));
     	this.facade = new OpenbisFacade(openbis);
     	this.dataSetCode = dataSetCode;
     	this.panel = $("<div>")
@@ -16,77 +105,180 @@ $.extend(ImageViewerWidget.prototype, {
     	if(this.loaded){
     		callback();
     	}else{
-            var thisImageViewer = this;
+            var thisViewer = this;
             
             var manager = new CallbackManager(function(){
-                thisImageViewer.loaded = true;  
+                thisViewer.loaded = true;  
+                
+                var channels = thisViewer.getAllChannels();
+                
+                if(channels && channels.length > 0){
+                    thisViewer.setSelectedMergedChannels(channels.map(function(channel){
+                    	return channel.code
+                    }));
+                }
+                
+                var channelStacks = thisViewer.getAllChannelStacks();
+                
+                if(channelStacks && channelStacks.length > 0){
+                    thisViewer.setSelectedChannelStackId(channelStacks[0].id);
+                }
+                
                 callback();
             });
             
-            this.facade.tryGetDataStoreBaseURL(thisImageViewer.dataSetCode, manager.registerCallback(function(response){
-                thisImageViewer.dataStoreUrl = response.result;
+            this.facade.tryGetDataStoreBaseURL(thisViewer.dataSetCode, manager.registerCallback(function(response){
+                thisViewer.dataStoreUrl = response.result;
             }));
             
-            this.facade.getImageInfo(thisImageViewer.dataSetCode, manager.registerCallback(function(response){
-                thisImageViewer.imageInfo = response.result;
+            this.facade.getImageInfo(thisViewer.dataSetCode, manager.registerCallback(function(response){
+                thisViewer.imageInfo = response.result;
             }));
             
-            this.facade.getImageResolutions(thisImageViewer.dataSetCode, manager.registerCallback(function(response){
-                thisImageViewer.imageResolutions = response.result;
+            this.facade.getImageResolutions(thisViewer.dataSetCode, manager.registerCallback(function(response){
+                thisViewer.imageResolutions = response.result;
             }));
     	}
     },
     
     render: function(){
-    	var thisImageViewer = this;
+    	var thisViewer = this;
         
         this.load(function(){
-        	
-            var channelWidget = new ChannelChooserWidget(thisImageViewer.getChannels());
-            channelWidget.addChangeListener(function(){
-            	imageWidget.setChannels(channelWidget.getSelectedOrMergedChannels());
-            });
-            thisImageViewer.panel.append(channelWidget.render());
-
-            var resolutionWidget = new ResolutionChooserWidget(thisImageViewer.getResolutions());
-            resolutionWidget.addChangeListener(function(){
-            	imageWidget.setResolution(resolutionWidget.getSelectedResolution());
-            });
-            thisImageViewer.panel.append(resolutionWidget.render());
-            
-            var channelStackWidget = new ChannelStackChooserWidget(thisImageViewer.getChannelStacks());
-            channelStackWidget.addChangeListener(function(){
-                imageWidget.setChannelStackId(channelStackWidget.getSelectedChannelStack().id);
-            });
-            thisImageViewer.panel.append(channelStackWidget.render());
-            
-            var imageWidget = new ImageWidget();
-            imageWidget.setSessionToken(thisImageViewer.facade.getSession());
-            imageWidget.setDataStoreUrl(thisImageViewer.dataStoreUrl);
-            imageWidget.setDataSetCode(thisImageViewer.dataSetCode);
-            imageWidget.setChannelStackId(channelStackWidget.getSelectedChannelStack().id);
-            imageWidget.setResolution(resolutionWidget.getSelectedResolution());
-            imageWidget.setChannels(channelWidget.getSelectedOrMergedChannels());
-            
-            thisImageViewer.panel.append(imageWidget.render());
-
+        	if(thisViewer.getView()){
+                thisViewer.panel.append(thisViewer.getView().render());
+        	}
         });
     	
     	return this.panel;
     },
     
-    getChannels: function(){
+    refresh: function(){
+    	if(this.getView()){
+    		this.view.refresh();
+    	}
+    },
+    
+    getView: function(){
+    	return this.view;
+    },
+    
+    setView: function(view){
+    	this.view = view;
+    	this.refresh();
+    },
+    
+    getSelectedChannel: function(){
+    	if(this.selectedChannel != null){
+        	return this.selectedChannel;
+    	}else{
+    		return null;
+    	}
+    },
+    
+    setSelectedChannel: function(channel){
+    	if(this.getSelectedChannel() != channel){
+	    	this.selectedChannel = channel;
+	    	this.refresh();
+    	}
+    },
+    
+    getSelectedMergedChannels: function(){
+    	if(this.selectedMergedChannels != null){
+    		return this.selectedMergedChannels;
+    	}else{
+    		return [];
+    	}
+    },
+    
+    setSelectedMergedChannels: function(channels){
+    	if(!channels){
+    		channels = [];
+    	}
+
+    	if(this.getSelectedMergedChannels().toString() != channels.toString()){
+	    	this.selectedMergedChannels = channels;
+	    	this.refresh();
+    	}
+    },
+    
+    getSelectedChannelStackId: function(){
+    	if(this.selectedChannelStackId != null){
+    		return this.selectedChannelStackId;	
+    	}else{
+    		return null;
+    	}
+    },
+    
+    setSelectedChannelStackId: function(channelStackId){
+    	if(this.getSelectedChannelStackId() != channelStackId){
+	    	this.selectedChannelStackId = channelStackId;
+	    	this.refresh();
+    	}
+    },
+    
+    getSelectedResolution: function(){
+    	if(this.selectedResolution != null){
+    		return this.selectedResolution;
+    	}else{
+    		return null;
+    	}
+    },
+    
+    setSelectedResolution: function(resolution){
+    	if(this.getSelectedResolution() != resolution){
+	    	this.selectedResolution = resolution;
+	    	this.refresh();
+    	}
+    },
+
+    getSelectedImageData: function(){
+    	var imageData = new ImageData();
+    	imageData.setDataStoreUrl(this.dataStoreUrl);
+        imageData.setSessionToken(this.facade.getSession());
+        imageData.setDataSetCode(this.dataSetCode);
+        imageData.setChannelStackId(this.getSelectedChannelStackId());
+        
+        if(this.getSelectedChannel()){
+        	imageData.setChannels([this.getSelectedChannel()]);
+        }else{
+        	imageData.setChannels(this.getSelectedMergedChannels());
+        }
+        imageData.setResolution(this.getSelectedResolution());
+        return imageData;
+    },
+
+    getAllChannels: function(){
     	return this.imageInfo.imageDataset.imageDataset.imageParameters.channels;
     },
     
-    getChannelStacks: function(){
-    	return this.imageInfo.channelStacks;
+    getAllChannelStacks: function(){
+    	return this.imageInfo.channelStacks.sort(function(o1, o2){
+    		var t1 = o1.timePointOrNull;
+    		var t2 = o2.timePointOrNull;
+    		var d1 = o1.depthOrNull;
+    		var d2 = o2.depthOrNull;
+    		
+    		var compare = function(v1, v2){
+        		if(v1 > v2){
+        			return 1;
+        		}else if(v1 < v2){
+        			return -1;
+        		}else{
+        			return 0;
+        		}
+    		}
+
+    		return compare(t1, t2) * 10 + compare(d1, d2);
+    	});
     },
     
-    getResolutions: function(){
+    getAllResolutions: function(){
         return this.imageResolutions;
     }
     
+    // TODO add listeners for channel, resoluton, channel stack
+    
 });
 
 //
@@ -119,122 +311,207 @@ $.extend(OpenbisFacade.prototype, {
 });
 
 //
-// CHANNEL CHOOSER
+// CHANNEL CHOOSER VIEW
 //
-function ChannelChooserWidget(channels) {
-    this.init(channels);
+function ChannelChooserView(controller) {
+	this.init(controller);
 }
 
-$.extend(ChannelChooserWidget.prototype, {
+$.extend(ChannelChooserView.prototype, {
 
-    init: function(channels){
-    	this.channels = channels;
-    	this.selectedChannel = null;
-    	this.mergedChannels = channels.map(function(channel){
-            return channel.code;
-        });
-    	
-    	this.listeners = new ListenerManager();
-    	this.panel = $("<div>").addClass("widget");
-    },
-    
-    render: function(){
+	init: function(controller){
+		this.controller = controller;
+		this.panel = $("<div>");
+	},
+	
+	render: function(){
     	var thisChooser = this; 
+
+    	this.panel.append(this.createChannelWidget());
+    	this.panel.append(this.createMergedChannelsWidget());
+
+        this.rendered = true;
+        this.refresh();
         
-    	$("<div>").text("Channel:").appendTo(this.panel);
+        return this.panel;
+	},
+	
+	refresh: function(){
+		if(!this.rendered){
+			return;
+		}
+		
+		var thisView = this;
     	
-        var select = $("<select>").appendTo(this.panel);
+    	var select = this.panel.find("select");
+    	var mergedChannels = this.panel.find(".mergedChannelsWidget");
+    	
+    	if(this.controller.getSelectedChannel() != null){
+            select.val(this.controller.getSelectedChannel());
+            mergedChannels.hide();
+    	}else{
+            select.val("");
+            mergedChannels.find("input").each(function(){
+                var checkbox = $(this);
+                checkbox.prop("checked", thisView.controller.isMergedChannelSelected(checkbox.val()));
+                checkbox.prop("disabled", !thisView.controller.isMergedChannelEnabled(checkbox.val()));
+            });
+            mergedChannels.show();
+    	}
+	},
+	
+	createChannelWidget: function(){
+		var thisView = this;
+		var widget = $("<div>").addClass("channelWidget");
+		
+		$("<div>").text("Channel:").appendTo(widget);
+    	
+        var select = $("<select>").appendTo(widget);
         
         $("<option>").attr("value", "").text("Merged Channels").appendTo(select);
         
-        this.channels.forEach(function(channel){
+        this.controller.getAllChannels().forEach(function(channel){
         	$("<option>").attr("value", channel.code).text(channel.label).appendTo(select);
         });
         
         select.change(function(){
             if(select.val() == ""){
-                thisChooser.setSelectedChannel(null);                    
+                thisView.controller.setSelectedChannel(null);                    
             }else{
-                thisChooser.setSelectedChannel(select.val());
+            	thisView.controller.setSelectedChannel(select.val());
             }
         });
-
-        var mergedChannels = $("<div>").addClass("mergedChannels").appendTo(this.panel);
         
-        this.channels.forEach(function(channel){
-            var mergedChannel = $("<span>").addClass("mergedChannel").appendTo(mergedChannels);
+        return widget;
+	},
+	
+	createMergedChannelsWidget: function(){
+		var thisView = this;
+        var widget = $("<div>").addClass("mergedChannelsWidget");
+        
+        this.controller.getAllChannels().forEach(function(channel){
+            var mergedChannel = $("<span>").addClass("mergedChannel").appendTo(widget);
             $("<input>").attr("type", "checkbox").attr("value", channel.code).appendTo(mergedChannel);
             mergedChannel.append(channel.label);
         });
 
-        mergedChannels.find("input").change(function(){
+        widget.find("input").change(function(){
         	var channels = []
-        	mergedChannels.find("input:checked").each(function(){
-        		var checkbox = $(this);
-        		channels.push(checkbox.val());
+        	widget.find("input:checked").each(function(){
+        		channels.push($(this).val());
         	});
-        	thisChooser.setMergedChannels(channels);
+        	thisView.controller.setSelectedMergedChannels(channels);
         });
 
-        this.rendered = true;
-        this.refresh();
-        
-        return this.panel;
+        return widget;
+	}
+	
+});
+
+
+//
+// CHANNEL CHOOSER
+//
+function ChannelChooserWidget(channels) {
+    this.init(channels);
+}
+
+$.extend(ChannelChooserWidget.prototype, {
+
+    init: function(channels){
+    	this.setView(new ChannelChooserView(this));
+    	this.listeners = new ListenerManager();
+    	this.setAllChannels(channels);
     },
     
-    refresh: function(){
-    	if(!this.rendered){
-    		return;
+    render: function(){
+    	if(this.getView()){
+    		return this.getView().render();
     	}
-    	
-    	var thisChooser = this;
-    	
-    	var select = this.panel.find("select");
-    	var mergedChannels = this.panel.find(".mergedChannels");
-    	
-    	if(this.getSelectedChannel()){
-            select.val(this.getSelectedChannel());
-            mergedChannels.hide();
-    	}else{
-            select.val("");
-            mergedChannels.find("input").each(function(){
-                var checkbox = $(this);
-                var checked = $.inArray(checkbox.val(), thisChooser.getMergedChannels()) != -1;
-                checkbox.prop("checked", checked);
-                if(checked){
-                    checkbox.prop("disabled", thisChooser.getMergedChannels().length == 1);
-                }
-            });
-            mergedChannels.show();
+    },
+    
+    refresh: function(){
+    	if(this.getView()){
+    		this.getView().refresh();
     	}
     },
+    
+    getView: function(){
+    	return this.view;
+    },
+    
+    setView: function(view){
+    	this.view = view;
+    	this.refresh();
+    },
 
     getSelectedChannel: function(){
-        return this.selectedChannel;
+    	if(this.selectedChannel){
+            return this.selectedChannel;
+    	}else{
+    		return null;
+    	}
     },
     
     setSelectedChannel: function(channel){
-        this.selectedChannel = channel;
-        this.refresh();
-        this.notifyChangeListeners();
+    	if(this.getSelectedChannel() != channel){
+	        this.selectedChannel = channel;
+	        this.refresh();
+	        this.notifyChangeListeners();
+    	}
     },
     
-    getMergedChannels: function(){
-    	return this.mergedChannels;
+    getSelectedMergedChannels: function(){
+    	if(this.selectedMergedChannels){
+    		return this.selectedMergedChannels;
+    	}else{
+    		return [];
+    	}
     },
     
-    setMergedChannels: function(channels){
-        this.mergedChannels = channels;
-        this.refresh();
-        this.notifyChangeListeners();
+    setSelectedMergedChannels: function(channels){
+    	if(!channels){
+    		channels = [];
+    	}
+
+    	if(this.getSelectedMergedChannels().toString() != channels.toString()){
+	        this.selectedMergedChannels = channels;
+	        this.refresh();
+	        this.notifyChangeListeners();
+    	}
     },
     
-    getSelectedOrMergedChannels: function(){
-        if(this.getSelectedChannel()){
-        	return [this.getSelectedChannel()];
-        }else{
-        	return this.getMergedChannels();
-        }
+    isMergedChannelSelected: function(channel){
+        return $.inArray(channel, this.getSelectedMergedChannels()) != -1;
+    },
+    
+    isMergedChannelEnabled: function(channel){
+    	if(this.getSelectedMergedChannels().length == 1){
+    		return !this.isMergedChannelSelected(channel);
+    	}    	
+    },
+    
+    getAllChannels: function(){
+    	if(this.channels){
+    		return this.channels;
+    	}else{
+    		return [];
+    	}
+    },
+    
+    setAllChannels: function(channels){
+    	if(!channels){
+    		channels = [];
+    	}
+    	
+    	if(this.getAllChannels().toString() != channels.toString()){
+	    	this.setSelectedChannel(null);
+	    	this.setSelectedMergedChannels(channels.map(function(channel){
+	    		return channel.code;
+	    	}));
+	    	this.channels = channels;
+	    	this.refresh();
+    	}
     },
     
     addChangeListener: function(listener){
@@ -247,6 +524,64 @@ $.extend(ChannelChooserWidget.prototype, {
     
 });
 
+//
+// RESOLUTION CHOOSER VIEW
+//
+function ResolutionChooserView(controller) {
+	this.init(controller);
+}
+
+$.extend(ResolutionChooserView.prototype, {
+
+	init: function(controller){
+		this.controller = controller;
+		this.panel = $("<div>").addClass("widget");
+	},
+	
+	render: function(){
+        var thisView = this; 
+        
+        $("<div>").text("Resolution:").appendTo(this.panel);
+        
+        var select = $("<select>").appendTo(this.panel);
+        
+        $("<option>").attr("value", "").text("Default").appendTo(select);
+        
+        this.controller.getAllResolutions().forEach(function(resolution){
+	       	 var value = resolution.width + "x" + resolution.height;
+	         $("<option>").attr("value", value).text(value).appendTo(select);
+        });
+
+        select.change(function(){
+	       	 if(select.val() == ""){
+	       		 thisView.controller.setSelectedResolution(null);
+	       	 }else{
+	       		 thisView.controller.setSelectedResolution(select.val());
+	       	 }
+        });
+        
+        this.rendered = true;
+        this.refresh();
+
+        return this.panel;
+	},
+	
+	refresh: function(){
+	   	 if(!this.rendered){
+			 return;
+		 }
+		 
+		 var select = this.panel.find("select");
+		 
+		 if(this.controller.getSelectedResolution() != null){
+			 select.val(this.controller.getSelectedResolution());
+		 }else{
+			 select.val("");
+		 }
+	}
+
+});
+
 //
 // RESOLUTION CHOOSER
 //
@@ -257,59 +592,62 @@ function ResolutionChooserWidget(resolutions) {
 $.extend(ResolutionChooserWidget.prototype, {
 
 	 init: function(resolutions){
-         this.resolutions = resolutions;
-         this.selectedResolution = null;
+		 this.view = new ResolutionChooserView(this);
          this.listeners = new ListenerManager();
-         this.panel = $("<div>").addClass("widget");
+         this.setAllResolutions(resolutions);
      },
      
      render: function(){
-         var thisChooser = this; 
-         
-         $("<div>").text("Resolution:").appendTo(this.panel);
-         
-         var select = $("<select>").appendTo(this.panel);
-         
-         $("<option>").attr("value", "").text("Default").appendTo(select);
-         
-         this.resolutions.forEach(function(resolution){
-        	 var value = resolution.width + "x" + resolution.height;
-             $("<option>").attr("value", value).text(value).appendTo(select);
-         });
-
-         select.change(function(){
-        	 if(select.val() == ""){
-        		 thisChooser.setSelectedResolution(null);
-        	 }else{
-        		 thisChooser.setSelectedResolution(select.val());
-        	 }
-         });
-         
-         this.rendered = true;
-         this.refresh();
-
-         return this.panel;
+    	 if(this.getView()){
+    		 return this.getView().render();
+    	 }
      },
 
      refresh: function(){
-    	 if(!this.rendered){
-    		 return;
-    	 }
-    	 
-    	 var select = this.panel.find("select");
-    	 if(this.selectedResolution){
-    		 select.val(this.selectedResolution);
+    	 if(this.getView()){
+    		 this.getView().refresh();
     	 }
      },
      
+     getView: function(){
+    	return this.view; 
+     },
+     
+     setView: function(view){
+    	 this.view = view;
+    	 this.refresh();
+     },
+     
      getSelectedResolution: function(){
          return this.selectedResolution;
      },
      
      setSelectedResolution: function(resolution){
-         this.selectedResolution = resolution;
-         this.refresh();
-         this.notifyChangeListeners();
+    	 if(this.selectedResolution != resolution){
+	         this.selectedResolution = resolution;
+	         this.refresh();
+	         this.notifyChangeListeners();
+    	 }
+     },
+     
+     getAllResolutions: function(){
+    	 if(this.resolutions){
+    		 return this.resolutions;
+    	 }else{
+    		 return [];
+    	 }
+     },
+     
+     setAllResolutions: function(resolutions){
+    	 if(!resolutions){
+    		 resolutions = [];
+    	 }
+    	 
+    	 if(this.getAllResolutions().toString() != resolutions.toString()){
+    		 this.resolutions = resolutions;
+    		 this.refresh();
+    		 this.notifyChangeListeners();
+    	 }
      },
      
      addChangeListener: function(listener){
@@ -325,19 +663,19 @@ $.extend(ResolutionChooserWidget.prototype, {
 //
 // CHANNEL STACK CHOOSER
 //
-function ChannelStackChooserWidget(channelStacks) {
-    this.init(channelStacks);
+function ChannelStackChooserWidget(channelStacks, channelStackContentLoader) {
+    this.init(channelStacks, channelStackContentLoader);
 }
 
 $.extend(ChannelStackChooserWidget.prototype, {
 
-     init: function(channelStacks){
+     init: function(channelStacks, channelStackContentLoader){
          var manager = new ChannelStackManager(channelStacks);
          
          if(manager.isMatrix()){
-        	 this.widget = new ChannelStackMatrixChooserWidget(channelStacks);
+        	 this.widget = new ChannelStackMatrixChooserWidget(channelStacks, channelStackContentLoader);
          }else{
-        	 this.widget = new ChannelStackDefaultChooserWidget(channelStacks);
+        	 this.widget = new ChannelStackDefaultChooserWidget(channelStacks, channelStackContentLoader);
          }
      },
      
@@ -345,8 +683,12 @@ $.extend(ChannelStackChooserWidget.prototype, {
     	 return this.widget.render();
      },
      
-     getSelectedChannelStack: function(){
-         return this.widget.getSelectedChannelStack();
+     getSelectedChannelStackId: function(){
+         return this.widget.getSelectedChannelStackId();
+     },
+     
+     setSelectedChannelStackId: function(channelStackId){
+    	this.widget.setSelectedChannelStackId(channelStackId); 
      },
      
      addChangeListener: function(listener){
@@ -360,102 +702,240 @@ $.extend(ChannelStackChooserWidget.prototype, {
 });
 
 //
-// CHANNEL STACK MATRIX CHOOSER
+// CHANNEL STACK MATRIX CHOOSER VIEW
 //
-function ChannelStackMatrixChooserWidget(channelStacks) {
-    this.init(channelStacks);
+
+function ChannelStackMatrixChooserView(controller) {
+    this.init(controller);
 }
 
-$.extend(ChannelStackMatrixChooserWidget.prototype, {
+$.extend(ChannelStackMatrixChooserView.prototype, {
 
-    init: function(channelStacks){
-        this.channelStackManager = new ChannelStackManager(channelStacks);
-        this.selectedTimePoint = this.channelStackManager.getTimePoints()[0]; 
-        this.selectedDepth = this.channelStackManager.getDepths()[0];
-        this.listeners = new ListenerManager();
+    init: function(controller){
+    	this.controller = controller;
         this.panel = $("<div>").addClass("widget");
     },
     
     render: function(){
-        var thisChooser = this; 
+        var thisView = this; 
         
         $("<div>").text("Channel Stack:").appendTo(this.panel);
+        this.panel.append(this.createTimePointWidget());
+        this.panel.append(this.createDepthWidget());
+        this.panel.append(this.createButtonsWidget());
         
-        $("<span>").text("T:").appendTo(this.panel);
+        this.rendered = true;
+        this.refresh();
 
-        var timeSelect = $("<select>").addClass("timeChooser").appendTo(this.panel);
+        return this.panel;
+    },
+    
+    refresh: function(){
+		 if(!this.rendered){
+		     return;
+		 }
+		 
+		 var timeSelect = this.panel.find("select.timeChooser");
+		 
+		 if(this.controller.getSelectedTimePoint() != null){
+		     timeSelect.val(this.controller.getSelectedTimePoint());
+		     // TODO get rid of empty string trick
+		     this.buttons.setSelectedFrame(this.controller.getTimePoints().indexOf("" + this.controller.getSelectedTimePoint()));
+		 }
+		 
+		 var depthSelect = this.panel.find("select.depthChooser");
+		 
+		 if(this.controller.getSelectedDepth() != null){
+		     depthSelect.val(this.controller.getSelectedDepth());
+		 }          
+    },
+    
+    createTimePointWidget: function(){
+    	var thisView = this;
+    	var widget = $("<span>");
+    	
+    	$("<span>").text("T:").appendTo(widget);
+
+        var timeSelect = $("<select>").addClass("timeChooser").appendTo(widget);
         
-        this.channelStackManager.getTimePoints().forEach(function(timePoint){
+        this.controller.getTimePoints().forEach(function(timePoint){
             $("<option>").attr("value", timePoint).text(timePoint).appendTo(timeSelect);
         });
 
         timeSelect.change(function(){
-            thisChooser.setSelectedTimePoint(timeSelect.val());
+            thisView.controller.setSelectedTimePoint(timeSelect.val());
         });
         
-        $("<span>").text("D:").appendTo(this.panel);
+        return widget;
+    },
+    
+    createDepthWidget: function(){
+    	var thisView = this;
+    	var widget = $("<span>");
+    	
+        $("<span>").text("D:").appendTo(widget);
         
-        var depthSelect = $("<select>").addClass("depthChooser").appendTo(this.panel);
+        var depthSelect = $("<select>").addClass("depthChooser").appendTo(widget);
         
-        this.channelStackManager.getDepths().forEach(function(depth){
+        this.controller.getDepths().forEach(function(depth){
             $("<option>").attr("value", depth).text(depth).appendTo(depthSelect);
         });
 
         depthSelect.change(function(){
-            thisChooser.setSelectedDepth(depthSelect.val());
+            thisView.controller.setSelectedDepth(depthSelect.val());
         });
-
-        this.buttons = new MovieButtonsWidget(this.channelStackManager.getTimePoints().length);
         
-        this.buttons.addChangeListener(function(){
-        	var frame = thisChooser.buttons.getSelectedFrame();
-        	thisChooser.setSelectedTimePoint(thisChooser.channelStackManager.getTimePoint(frame));
+        return widget;
+    },
+    
+    createButtonsWidget: function(){
+    	var thisView = this;
+
+        // TODO content loader should be specified via setter, if not specified an empty function should be used
+        buttons = new MovieButtonsWidget(this.controller.getTimePoints().length, function(frameIndex, callback){
+        	var timePoint = thisView.controller.getTimePoints()[frameIndex];
+        	var depth = thisView.controller.getSelectedDepth();
+        	var channelStack = thisView.controller.getChannelStackByTimePointAndDepth(timePoint, depth);
+        	thisView.controller.loadChannelStackContent(channelStack, callback);
         });
         
-        this.panel.append(this.buttons.render());
+        buttons.addChangeListener(function(){
+        	var timePoint = thisView.controller.getTimePoints()[buttons.getSelectedFrame()];
+        	thisView.controller.setSelectedTimePoint(timePoint);
+        });
         
-        this.rendered = true;
-        this.refresh();
+        this.buttons = buttons;
+        return buttons.render();
+    }
 
-        return this.panel;
+});
+
+//
+// CHANNEL STACK MATRIX CHOOSER
+//
+function ChannelStackMatrixChooserWidget(channelStacks, channelStackContentLoader) {
+    this.init(channelStacks, channelStackContentLoader);
+}
+
+$.extend(ChannelStackMatrixChooserWidget.prototype, {
+
+    init: function(channelStacks, channelStackContentLoader){
+    	this.setView(new ChannelStackMatrixChooserView(this));
+    	this.channelStackManager = new ChannelStackManager(channelStacks);
+    	this.channelStackContentLoader = channelStackContentLoader;
+        this.listeners = new ListenerManager();
+    },
+    
+    // TODO create abstract widget with render, refresh, getView, setView, isRendered, listeners
+    
+    render: function(){
+    	if(this.getView()){
+    		return this.getView().render();
+    	}
     },
     
     refresh: function(){
-    	 if(!this.rendered){
-             return;
-         }
-         
-         var timeSelect = this.panel.find("select.timeChooser");
-         
-         if(this.selectedTimePoint){
-             timeSelect.val(this.selectedTimePoint);
-         }
-         
-         var depthSelect = this.panel.find("select.depthChooser");
-         
-         if(this.selectedDepth){
-             depthSelect.val(this.selectedDepth);
-         }          
+    	if(this.getView()){
+    		this.getView().refresh();
+    	}
     },
     
-    getSelectedChannelStack: function(){
-    	return this.channelStackManager.getChannelStack(this.selectedTimePoint, this.selectedDepth);
+    getView: function(){
+    	return this.view;
     },
     
-    setSelectedTimePoint: function(timePoint){
-    	if(this.selectedTimePoint != timePoint){
-	    	this.selectedTimePoint = timePoint;
-	    	this.buttons.setSelectedFrame(this.channelStackManager.getTimePointIndex(timePoint));
+    setView: function(view){
+    	this.view = view;
+    	this.refresh();
+    },
+    
+    getTimePoints: function(){
+    	return this.channelStackManager.getTimePoints();
+    },
+    
+    getDepths: function(){
+		return this.channelStackManager.getDepths();
+	},
+	
+	getChannelStacks: function(){
+		return this.channelStackManager.getChannelStacks();
+	},
+	
+	getChannelStackById: function(channelStackId){
+		return this.channelStackManager.getChannelStackById(channelStackId);
+	},
+	
+	getChannelStackByTimePointAndDepth: function(timePoint, depth){
+		return this.channelStackManager.getChannelStackByTimePointAndDepth(timePoint, depth); 
+	},
+
+	loadChannelStackContent: function(channelStack, callback){
+		this.channelStackContentLoader(channelStack, callback);
+	},
+	
+    getSelectedChannelStackId: function(){
+    	return this.selectedChannelStackId;
+    },
+    
+    setSelectedChannelStackId: function(channelStackId){
+    	if(this.selectedChannelStackId != channelStackId){
+    		this.selectedChannelStackId = channelStackId;
 	    	this.refresh();
 	    	this.notifyChangeListeners();
     	}
     },
     
+    getSelectedChannelStack: function(){
+    	var channelStackId = this.getSelectedChannelStackId();
+    	
+    	if(channelStackId != null){
+        	return this.channelStackManager.getChannelStackById(channelStackId);
+    	}else{
+    		return null;
+    	}
+    },
+
+    setSelectedChannelStack: function(channelStack){
+    	if(channelStack != null){
+    		this.setSelectedChannelStackId(channelStack.id);
+    	}else{
+    		this.setSelectedChannelStackId(null);
+    	}
+    },
+
+    getSelectedTimePoint: function(){
+    	var channelStack = this.getSelectedChannelStack();
+    	if(channelStack != null){
+    		return channelStack.timePointOrNull;
+    	}else{
+    		return null;
+    	}
+    },
+    
+    setSelectedTimePoint: function(timePoint){
+    	if(timePoint != null && this.getSelectedDepth() != null){
+        	var channelStack = this.channelStackManager.getChannelStackByTimePointAndDepth(timePoint, this.getSelectedDepth());
+        	this.setSelectedChannelStack(channelStack);
+    	}else{
+    		this.setSelectedChannelStack(null);
+    	}
+    },
+    
+    getSelectedDepth: function(){
+    	var channelStack = this.getSelectedChannelStack();
+    	if(channelStack != null){
+    		return channelStack.depthOrNull;
+    	}else{
+    		return null;
+    	}
+    },
+    
     setSelectedDepth: function(depth){
-    	if(this.selectedDepth != depth){
-	        this.selectedDepth = depth;
-	        this.refresh();
-	        this.notifyChangeListeners();
+    	if(depth != null && this.getSelectedTimePoint() != null){
+    		var channelStack = this.channelStackManager.getChannelStackByTimePointAndDepth(this.getSelectedTimePoint(), depth);
+    		this.setSelectedChannelStack(channelStack);
+    	}else{
+    		this.setSelectedChannelStack(null);
     	}
     },
     
@@ -472,13 +952,13 @@ $.extend(ChannelStackMatrixChooserWidget.prototype, {
 //
 // CHANNEL STACK DEFAULT CHOOSER
 //
-function ChannelStackDefaultChooserWidget(channelStacks) {
-    this.init(channelStacks);
+function ChannelStackDefaultChooserWidget(channelStacks, channelStackContentLoader) {
+    this.init(channelStacks, channelStackContentLoader);
 }
 
 $.extend(ChannelStackDefaultChooserWidget.prototype, {
 
-    init: function(channelStacks){
+    init: function(channelStacks, channelStackContentLoader){
         this.channelStackManager = new ChannelStackManager(channelStacks);
         this.listeners = new ListenerManager();
         this.panel = $("<div>");
@@ -489,11 +969,11 @@ $.extend(ChannelStackDefaultChooserWidget.prototype, {
         return this.panel;
     },
     
-    getSelectedChannelStack: function(){
+    getSelectedChannelStackId: function(){
         return null;
     },
     
-    setSelectedChannelStack: function(channelStack){
+    setSelectedChannelStackId: function(channelStackId){
     },
     
     addChangeListener: function(listener){
@@ -543,7 +1023,7 @@ $.extend(ChannelStackManager.prototype, {
      },
      
      isDepthConsistent: function(){
-         var map = this.getChannelStackMap();
+         var map = this.getChannelStackByTimePointAndDepthMap();
          var depthCounts = {};
          
          for(timePoint in map){
@@ -621,22 +1101,33 @@ $.extend(ChannelStackManager.prototype, {
     	 return this.depthsMap[depth];
      },
      
-     getChannelStack: function(timePoint, depthLevel){
-    	 var map = this.getChannelStackMap();
+     getChannelStackByTimePointAndDepth: function(timePoint, depth){
+    	 var map = this.getChannelStackByTimePointAndDepthMap();
     	 var entry = map[timePoint];
     	 
     	 if(entry){
-    		 return entry[depthLevel];
+    		 return entry[depth];
     	 }else{
     		 return null;
     	 }
      },
      
+     getChannelStackById: function(channelStackId){
+         if(!this.channelStackByIdMap){
+             var map = {};
+             this.channelStacks.forEach(function(channelStack){
+            	 map[channelStack.id] = channelStack;
+             });
+             this.channelStackByIdMap = map;
+         }
+         return this.channelStackByIdMap[channelStackId];     	 
+     },
+     
      getChannelStacks: function(){
     	 return this.channelStacks;
      },
      
-     getChannelStackMap: function(){
+     getChannelStackByTimePointAndDepthMap: function(){
          if(!this.channelStackMap){
              var map = {};
              this.channelStacks.forEach(function(channelStack){
@@ -656,54 +1147,51 @@ $.extend(ChannelStackManager.prototype, {
 });
 
 //
-// MOVIE BUTTONS WIDGET
+// MOVIE BUTTONS VIEW
 //
-function MovieButtonsWidget(frameCount) {
-    this.init(frameCount);
+
+function MovieButtonsView(controller) {
+    this.init(controller);
 }
 
-$.extend(MovieButtonsWidget.prototype, {
+$.extend(MovieButtonsView.prototype, {
 
-     init: function(frameCount){
-    	 this.frameCount = frameCount;
-    	 this.frameAction = null;
-    	 this.selectedDelay = 500;
-    	 this.selectedFrame = 0;
-    	 this.listeners = new ListenerManager();
+     init: function(controller){
+    	 this.controller = controller;
          this.panel = $("<div>").addClass("widget");
      },
      
      render: function(){
-         var thisButtons = this; 
+         var thisView = this; 
          
          var play = $("<button>").addClass("play").text("Play").appendTo(this.panel);
          
          play.click(function(){
-        	 thisButtons.play();
+        	 thisView.controller.play();
          });
          
          var stop = $("<button>").addClass("stop").text("Stop").appendTo(this.panel);
          
          stop.click(function(){
-        	 thisButtons.stop();
+        	 thisView.controller.stop();
          });
 
          var prev = $("<button>").addClass("prev").text("<<").appendTo(this.panel);
          
          prev.click(function(){
-        	 thisButtons.setSelectedFrame(thisButtons.getSelectedFrame() - 1);
+        	 thisView.controller.prev();
          });
          
          var next = $("<button>").addClass("next").text(">>").appendTo(this.panel);
          
          next.click(function(){
-        	 thisButtons.setSelectedFrame(thisButtons.getSelectedFrame() + 1);
+        	 thisView.controller.next();
          });
          
          var delay = $("<input>").attr("type", "text").addClass("delay").appendTo(this.panel);
          
          delay.change(function(){
-        	 thisButtons.setSelectedDelay(delay.val());
+        	 thisView.controller.setSelectedDelay(parseInt(delay.val()));
          });
 
          this.rendered = true;
@@ -711,26 +1199,69 @@ $.extend(MovieButtonsWidget.prototype, {
 
          return this.panel;
      },
-
+     
      refresh: function(){
          if(!this.rendered){
              return;
          }
          
          var play = this.panel.find("button.play");
-         play.prop("disabled", this.frameAction != null);
+         play.prop("disabled", this.controller.isPlaying());
          
          var stop = this.panel.find("button.stop");
-         stop.prop("disabled", this.frameAction == null);
+         stop.prop("disabled", this.controller.isStopped());
          
          var prev = this.panel.find("button.prev");
-         prev.prop("disabled", this.getSelectedFrame() == 0);
+         prev.prop("disabled", this.controller.isFirstFrameSelected());
          
          var next = this.panel.find("button.next");
-         next.prop("disabled", this.getSelectedFrame() == (this.frameCount - 1));
+         next.prop("disabled", this.controller.isLastFrameSelected());
          
          var delay = this.panel.find("input.delay");
          delay.val(this.selectedDelay);
+     }
+
+});
+     
+//
+// MOVIE BUTTONS WIDGET
+//
+function MovieButtonsWidget(frameCount, frameLoader) {
+    this.init(frameCount, frameLoader);
+}
+
+$.extend(MovieButtonsWidget.prototype, {
+
+     init: function(frameCount, frameLoader){
+    	 this.setView(new MovieButtonsView(this))
+    	 this.frameCount = frameCount;
+    	 this.frameLoader = frameLoader;
+    	 this.frameAction = null;
+    	 this.selectedDelay = 500;
+    	 this.selectedFrame = 0;
+    	 this.listeners = new ListenerManager();
+         this.panel = $("<div>").addClass("widget");
+     },
+     
+     render: function(){
+    	 if(this.getView()){
+    		 return this.getView().render();
+    	 }
+     },
+
+     refresh: function(){
+    	 if(this.getView()){
+    		 this.getView().refresh();
+    	 }
+     },
+     
+     setView: function(view){
+    	 this.view = view;
+    	 this.refresh();
+     },
+     
+     getView: function(){
+    	 return this.view;
      },
      
      play: function(){
@@ -745,13 +1276,20 @@ $.extend(MovieButtonsWidget.prototype, {
     	 var thisButtons = this;
     	 
     	 this.frameAction = function(){
-    		 if(!thisButtons.frameAction){
-    			 return;
-    		 }
-    		 
     		 if(thisButtons.getSelectedFrame() < thisButtons.frameCount - 1){
-    			 thisButtons.setSelectedFrame(thisButtons.getSelectedFrame() + 1);
-      			 setTimeout(thisButtons.frameAction, thisButtons.selectedDelay);
+    			 var frame = thisButtons.getSelectedFrame() + 1;
+    			 var startTime = Date.now();
+    			 
+    			 thisButtons.setSelectedFrame(frame, function(){
+        			 var prefferedDelay = thisButtons.selectedDelay;
+        			 var actualDelay = Date.now() - startTime; 
+        			
+    				 setTimeout(function(){
+    					 if(thisButtons.frameAction){
+	            			 thisButtons.frameAction();
+    					 }
+    				 }, Math.max(1, prefferedDelay - actualDelay));
+        		 });
     		 }else{
     			 thisButtons.stop();
     			 thisButtons.setSelectedFrame(0);
@@ -759,13 +1297,40 @@ $.extend(MovieButtonsWidget.prototype, {
     	 };
     	 
     	 this.frameAction();
+    	 this.refresh();
      },
      
      stop: function(){
-    	 this.frameAction = null;
-         this.refresh();
+    	 if(this.frameAction){
+	    	 this.frameAction = null;
+	         this.refresh();
+    	 }
+     },
+     
+     prev: function(){
+    	 this.setSelectedFrame(this.getSelectedFrame() - 1);    	 
+     },
+
+     next: function(){
+    	 this.setSelectedFrame(this.getSelectedFrame() + 1);
      },
 
+     isPlaying: function(){
+      	return this.frameAction != null; 
+     },
+
+     isStopped: function(){
+       	return this.frameAction == null; 
+     },
+       
+     isFirstFrameSelected: function(){
+     	 return this.getSelectedFrame() == 0;
+     },
+      
+     isLastFrameSelected: function(){
+     	 return this.getSelectedFrame() == (this.frameCount - 1) 
+     },
+     
      getSelectedDelay: function(){
     	 return this.selectedDelay;
      },
@@ -781,11 +1346,13 @@ $.extend(MovieButtonsWidget.prototype, {
          return this.selectedFrame;
      },
      
-     setSelectedFrame: function(frame){
+     setSelectedFrame: function(frame, callback){
     	 frame = Math.min(Math.max(0, frame), this.frameCount - 1);
 
     	 if(this.selectedFrame != frame){
-	         this.selectedFrame = frame; 
+    		 log("Selected frame: " + frame);
+	         this.selectedFrame = frame;
+	         this.frameLoader(frame, callback);
 	         this.refresh();
 	         this.notifyChangeListeners();
     	 }
@@ -802,75 +1369,121 @@ $.extend(MovieButtonsWidget.prototype, {
 });
 
 //
-// IMAGE
+// IMAGE DATA
 //
-function ImageWidget() {
-    this.init();
+function ImageData() {
+	this.init();
 }
 
-$.extend(ImageWidget.prototype, {
-
-    init: function(){
-    	this.panel = $("<div>")
-    },
-    
-    render: function(){
-    	$("<img>").appendTo(this.panel);
-    	this.rendered = true;
-    	this.refresh();
-        return this.panel;
-    },
-    
-    refresh: function(){
-    	if(!this.rendered){
-    		return;
-    	}
-    	
-    	var url = this.dataStoreUrl + "/datastore_server_screening";
-    	url += "?sessionID=" + this.sessionToken;
-    	url += "&dataset=" + this.dataSetCode;
-    	url += "&channelStackId=" + this.channelStackId;
+$.extend(ImageData.prototype, {
 
-    	this.channels.forEach(function(channel){
-    		url += "&channel=" + channel;
-    	});
-    	
-    	if(this.resolution){
-    		url += "&mode=thumbnail" + this.resolution;	
-    	}else{
-    		url += "&mode=thumbnail480x480";
-    	}
-    	
-    	this.panel.find("img").attr("src", url);
-    },
-    
-    setDataStoreUrl: function(dataStoreUrl){
+	init: function(){
+	},
+	
+	setDataStoreUrl: function(dataStoreUrl){
     	this.dataStoreUrl = dataStoreUrl;
-    	this.refresh();
     },
     
     setSessionToken: function(sessionToken){
     	this.sessionToken = sessionToken;
-    	this.refresh();
     },
     
     setDataSetCode: function(dataSetCode){
     	this.dataSetCode = dataSetCode;
-    	this.refresh();
     },
     
     setChannelStackId: function(channelStackId){
     	this.channelStackId = channelStackId;
-    	this.refresh();
     },
     
     setChannels: function(channels){
     	this.channels = channels;
-    	this.refresh();
     },
     
     setResolution: function(resolution){
     	this.resolution = resolution;
+    }
+
+});
+
+//
+// IMAGE LOADER
+//
+function ImageLoader() {
+ this.init();
+}
+
+$.extend(ImageLoader.prototype, {
+
+	init: function(){
+	},
+
+	loadImage: function(imageData, callback){
+    	log("loadImage: " + imageData.channelStackId);
+    	
+    	var url = imageData.dataStoreUrl + "/datastore_server_screening";
+    	url += "?sessionID=" + imageData.sessionToken;
+    	url += "&dataset=" + imageData.dataSetCode;
+    	url += "&channelStackId=" + imageData.channelStackId;
+
+    	imageData.channels.forEach(function(channel){
+    		url += "&channel=" + channel;
+    	});
+
+    	if(imageData.resolution){
+    		url += "&mode=thumbnail" + imageData.resolution;	
+    	}else{
+    		url += "&mode=thumbnail480x480";
+    	}
+    	
+    	$("<img>").attr("src", url).load(function(){
+    		if(callback){
+    			callback(this);
+    		}
+    	});
+    }
+ 
+});
+ 
+//
+// IMAGE WIDGET
+//
+function ImageWidget(imageLoader) {
+    this.init(imageLoader);
+}
+
+$.extend(ImageWidget.prototype, {
+
+    init: function(imageLoader){
+    	this.imageLoader = imageLoader;
+    	this.panel = $("<div>")
+    },
+
+    render: function(){
+    	this.rendered = true;
+    	this.refresh();
+        return this.panel;
+    },
+    
+    refresh: function(){
+    	if(!this.rendered){
+    		return;
+    	}
+    	
+    	var thisWidget = this;
+    	
+    	if(this.imageData){
+	    	this.imageLoader.loadImage(this.imageData, function(image){
+	    		thisWidget.panel.empty();
+	    		thisWidget.panel.append(image);
+	    	});
+    	}else{
+    		this.panel.empty();
+    	}
+    },
+
+    setImage: function(imageData){
+    	this.imageData = imageData;
     	this.refresh();
     }
     
@@ -939,3 +1552,10 @@ $.extend(ListenerManager.prototype, {
         }
     }
 });
+
+function log(msg){
+	if(console){
+		var date = new Date();
+		console.log(date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "." + date.getMilliseconds() + " - " + msg);
+	}
+}