From adc02f89b4a48409652db72b8c5cb63553b97670 Mon Sep 17 00:00:00 2001 From: pkupczyk <pkupczyk> Date: Mon, 28 Apr 2014 13:47:46 +0000 Subject: [PATCH] SSDM-75 / Javascript-based Microscopy Data Viewer: - introduced abstract view and widget - all widgets now have a corresponding view class - timepoints and depths are returned as integers - content loaders getters and setters SVN: 31424 --- .../webapps/image-viewer/html/image-viewer.js | 2818 ++++++++--------- 1 file changed, 1399 insertions(+), 1419 deletions(-) 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 3f91894ec99..d42c90f8522 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,988 +1,1227 @@ +// +// ABSTRACT VIEW +// + +function AbstractView(controller) { + this.init(controller); +} + +$.extend(AbstractView.prototype, { + + init : function(controller) { + this.controller = controller; + }, + + render : function() { + return null; + }, + + refresh : function() { + + } + +}); + +// +// ABSTRACT WIDGET +// + +function AbstractWidget(view) { + this.init(view); +} + +$.extend(AbstractWidget.prototype, { + init : function(view) { + this.setView(view); + this.listeners = new ListenerManager(); + this.panel = $("<div>").addClass("widget"); + }, + + load : function(callback) { + callback(); + }, + + render : function() { + var thisWidget = this; + + this.load(function() { + if (thisWidget.getView()) { + thisWidget.panel.append(thisWidget.getView().render()); + thisWidget.rendered = true; + } + }); + + return this.panel; + }, + + refresh : function() { + if (!this.rendered) { + return; + } + + if (this.getView()) { + this.getView().refresh(); + } + }, + + getView : function() { + return this.view; + }, + + setView : function(view) { + this.view = view; + this.refresh(); + }, + + addChangeListener : function(listener) { + this.listeners.addListener('change', listener); + }, + + notifyChangeListeners : function() { + this.listeners.notifyListeners('change'); + } +}); + +// TODO do not pollute the global namespace and expose only ImageViewer + // // IMAGE VIEWER VIEW // -function ImageViewerView(controller) { - this.init(controller); -} +function ImageViewerView(controller) { + this.init(controller); +} + +$.extend(ImageViewerView.prototype, AbstractView.prototype, { + init : function(controller) { + AbstractView.prototype.init.call(this, 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.refresh(); + + return this.panel; + }, + + refresh : function() { + 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.setImageData(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()); + + widget.setChannelStackContentLoader(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 +// + +function ImageViewerWidget(openbis, dataSetCode) { + this.init(openbis, dataSetCode); +} + +$.extend(ImageViewerWidget.prototype, AbstractWidget.prototype, { + init : function(openbis, dataSetCode) { + AbstractWidget.prototype.init.call(this, new ImageViewerView(this)); + this.facade = new OpenbisFacade(openbis); + this.dataSetCode = dataSetCode; + }, + + load : function(callback) { + if (this.loaded) { + callback(); + } else { + var thisViewer = this; + + var manager = new CallbackManager(function() { + 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(thisViewer.dataSetCode, manager.registerCallback(function(response) { + thisViewer.dataStoreUrl = response.result; + })); + + this.facade.getImageInfo(thisViewer.dataSetCode, manager.registerCallback(function(response) { + thisViewer.imageInfo = response.result; + })); + + this.facade.getImageResolutions(thisViewer.dataSetCode, manager.registerCallback(function(response) { + thisViewer.imageResolutions = response.result; + })); + } + }, + + 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; + }, + + 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); + }); + }, + + getAllResolutions : function() { + return this.imageResolutions; + } + +// TODO add listeners for channel, resoluton, channel stack + +}); + +// +// FACADE +// + +function OpenbisFacade(openbis) { + this.init(openbis); +} + +$.extend(OpenbisFacade.prototype, { + init : function(openbis) { + this.openbis = openbis; + }, + + getSession : function() { + return this.openbis.getSession(); + }, + + tryGetDataStoreBaseURL : function(dataSetCode, action) { + this.openbis.tryGetDataStoreBaseURL(dataSetCode, action); + }, + + getImageInfo : function(dataSetCode, callback) { + this.openbis.getImageInfo(dataSetCode, null, callback); + }, + + getImageResolutions : function(dataSetCode, callback) { + this.openbis.getImageResolutions(dataSetCode, callback); + } +}); + +// +// CHANNEL CHOOSER VIEW +// + +function ChannelChooserView(controller) { + this.init(controller); +} + +$.extend(ChannelChooserView.prototype, AbstractView.prototype, { + + init : function(controller) { + AbstractView.prototype.init.call(this, controller); + this.panel = $("<div>"); + }, + + render : function() { + this.panel.append(this.createChannelWidget()); + this.panel.append(this.createMergedChannelsWidget()); + + this.refresh(); + + return this.panel; + }, + + refresh : function() { + var thisView = this; + + 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.controller.getAllChannels().forEach(function(channel) { + $("<option>").attr("value", channel.code).text(channel.label).appendTo(select); + }); + + select.change(function() { + if (select.val() == "") { + thisView.controller.setSelectedChannel(null); + } else { + thisView.controller.setSelectedChannel(select.val()); + } + }); + + 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); + }); + + widget.find("input").change(function() { + var channels = [] + widget.find("input:checked").each(function() { + channels.push($(this).val()); + }); + thisView.controller.setSelectedMergedChannels(channels); + }); + + return widget; + } + +}); + +// +// CHANNEL CHOOSER +// + +function ChannelChooserWidget(channels) { + this.init(channels); +} + +$.extend(ChannelChooserWidget.prototype, AbstractWidget.prototype, { + + init : function(channels) { + AbstractWidget.prototype.init.call(this, new ChannelChooserView(this)); + this.setAllChannels(channels); + }, + + getSelectedChannel : function() { + if (this.selectedChannel) { + return this.selectedChannel; + } else { + return null; + } + }, + + setSelectedChannel : function(channel) { + if (this.getSelectedChannel() != channel) { + this.selectedChannel = channel; + this.refresh(); + this.notifyChangeListeners(); + } + }, + + getSelectedMergedChannels : function() { + if (this.selectedMergedChannels) { + return this.selectedMergedChannels; + } else { + return []; + } + }, + + setSelectedMergedChannels : function(channels) { + if (!channels) { + channels = []; + } + + if (this.getSelectedMergedChannels().toString() != channels.toString()) { + this.selectedMergedChannels = channels; + this.refresh(); + this.notifyChangeListeners(); + } + }, + + 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(); + } + } + +}); + +// +// RESOLUTION CHOOSER VIEW +// + +function ResolutionChooserView(controller) { + this.init(controller); +} + +$.extend(ResolutionChooserView.prototype, AbstractView.prototype, { + + init : function(controller) { + AbstractView.prototype.init.call(this, controller); + this.panel = $("<div>"); + }, + + 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.refresh(); + + return this.panel; + }, + + refresh : function() { + var select = this.panel.find("select"); + + if (this.controller.getSelectedResolution() != null) { + select.val(this.controller.getSelectedResolution()); + } else { + select.val(""); + } + } + +}); + +// +// RESOLUTION CHOOSER +// + +function ResolutionChooserWidget(resolutions) { + this.init(resolutions); +} + +$.extend(ResolutionChooserWidget.prototype, AbstractWidget.prototype, { + + init : function(resolutions) { + AbstractWidget.prototype.init.call(this, new ResolutionChooserView(this)); + this.setAllResolutions(resolutions); + }, + + getSelectedResolution : function() { + return this.selectedResolution; + }, + + setSelectedResolution : function(resolution) { + 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(); + } + } + +}); + +// +// CHANNEL STACK CHOOSER +// + +function ChannelStackChooserWidget(channelStacks) { + this.init(channelStacks); +} + +$.extend(ChannelStackChooserWidget.prototype, { + + init : function(channelStacks) { + var manager = new ChannelStackManager(channelStacks); + + if (manager.isMatrix()) { + this.widget = new ChannelStackMatrixChooserWidget(channelStacks); + } else { + this.widget = new ChannelStackDefaultChooserWidget(channelStacks); + } + }, + + render : function() { + return this.widget.render(); + }, + + getSelectedChannelStackId : function() { + return this.widget.getSelectedChannelStackId(); + }, + + setSelectedChannelStackId : function(channelStackId) { + this.widget.setSelectedChannelStackId(channelStackId); + }, + + getChannelStackContentLoader : function() { + return this.widget.getChannelStackContentLoader(); + }, + + setChannelStackContentLoader : function(channelStackContentLoader) { + return this.widget.setChannelStackContentLoader(channelStackContentLoader); + }, + + addChangeListener : function(listener) { + this.widget.addChangeListener(listener); + }, + + notifyChangeListeners : function() { + this.widget.notifyChangeListeners(); + } + +}); + +// +// CHANNEL STACK MATRIX CHOOSER VIEW +// + +function ChannelStackMatrixChooserView(controller) { + this.init(controller); +} + +$.extend(ChannelStackMatrixChooserView.prototype, AbstractView.prototype, { + + init : function(controller) { + AbstractView.prototype.init.call(this, controller); + this.panel = $("<div>"); + }, + + render : function() { + 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()); + + this.refresh(); + + return this.panel; + }, + + refresh : function() { + var timeSelect = this.panel.find("select.timeChooser"); + + if (this.controller.getSelectedTimePoint() != null) { + timeSelect.val(this.controller.getSelectedTimePoint()); + 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.controller.getTimePoints().forEach(function(timePoint) { + $("<option>").attr("value", timePoint).text(timePoint).appendTo(timeSelect); + }); + + timeSelect.change(function() { + thisView.controller.setSelectedTimePoint(timeSelect.val()); + }); + + return widget; + }, + + createDepthWidget : function() { + var thisView = this; + var widget = $("<span>"); + + $("<span>").text("D:").appendTo(widget); + + var depthSelect = $("<select>").addClass("depthChooser").appendTo(widget); + + this.controller.getDepths().forEach(function(depth) { + $("<option>").attr("value", depth).text(depth).appendTo(depthSelect); + }); + + depthSelect.change(function() { + thisView.controller.setSelectedDepth(depthSelect.val()); + }); + + return widget; + }, + + createButtonsWidget : function() { + var thisView = this; + + buttons = new MovieButtonsWidget(this.controller.getTimePoints().length); + + buttons.setFrameContentLoader(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); + }); + + buttons.addChangeListener(function() { + var timePoint = thisView.controller.getTimePoints()[buttons.getSelectedFrame()]; + thisView.controller.setSelectedTimePoint(timePoint); + }); + + this.buttons = buttons; + return buttons.render(); + } + +}); + +// +// CHANNEL STACK MATRIX CHOOSER +// + +function ChannelStackMatrixChooserWidget(channelStacks) { + this.init(channelStacks); +} + +$.extend(ChannelStackMatrixChooserWidget.prototype, AbstractWidget.prototype, { + + init : function(channelStacks) { + AbstractWidget.prototype.init.call(this, new ChannelStackMatrixChooserView(this)); + this.channelStackManager = new ChannelStackManager(channelStacks); + }, + + 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.getChannelStackContentLoader()(channelStack, callback); + }, + + getChannelStackContentLoader : function() { + if (this.channelStackContentLoader) { + return this.channelStackContentLoader; + } else { + return function(channelStack, callback) { + callback(); + } + } + }, + + setChannelStackContentLoader : function(channelStackContentLoader) { + this.channelStackContentLoader = channelStackContentLoader; + }, + + 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 (depth != null && this.getSelectedTimePoint() != null) { + var channelStack = this.channelStackManager.getChannelStackByTimePointAndDepth(this.getSelectedTimePoint(), depth); + this.setSelectedChannelStack(channelStack); + } else { + this.setSelectedChannelStack(null); + } + } -$.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 +// CHANNEL STACK DEFAULT CHOOSER VIEW // -function ImageViewerWidget(openbis, dataSetCode) { - this.init(openbis, dataSetCode); + +function ChannelStackDefaultChooserView(controller) { + this.init(controller); } -$.extend(ImageViewerWidget.prototype, { - init: function(openbis, dataSetCode){ - this.setView(new ImageViewerView(this)); - this.facade = new OpenbisFacade(openbis); - this.dataSetCode = dataSetCode; - this.panel = $("<div>") - }, - - load: function(callback){ - if(this.loaded){ - callback(); - }else{ - var thisViewer = this; - - var manager = new CallbackManager(function(){ - 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(thisViewer.dataSetCode, manager.registerCallback(function(response){ - thisViewer.dataStoreUrl = response.result; - })); - - this.facade.getImageInfo(thisViewer.dataSetCode, manager.registerCallback(function(response){ - thisViewer.imageInfo = response.result; - })); - - this.facade.getImageResolutions(thisViewer.dataSetCode, manager.registerCallback(function(response){ - thisViewer.imageResolutions = response.result; - })); - } - }, - - render: function(){ - var thisViewer = this; - - this.load(function(){ - if(thisViewer.getView()){ - thisViewer.panel.append(thisViewer.getView().render()); - } - }); - - return this.panel; - }, - - 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; - }, - - 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); - }); - }, - - getAllResolutions: function(){ - return this.imageResolutions; - } - - // TODO add listeners for channel, resoluton, channel stack - +$.extend(ChannelStackDefaultChooserView.prototype, AbstractView.prototype, { + + init : function(controller) { + AbstractView.prototype.init.call(this, controller); + } + }); // -// FACADE +// CHANNEL STACK DEFAULT CHOOSER // -function OpenbisFacade(openbis){ - this.init(openbis); + +function ChannelStackDefaultChooserWidget(channelStacks) { + this.init(channelStacks); } -$.extend(OpenbisFacade.prototype, { - init: function(openbis){ - this.openbis = openbis; - }, - - getSession: function(){ - return this.openbis.getSession(); - }, - - tryGetDataStoreBaseURL: function(dataSetCode, action){ - this.openbis.tryGetDataStoreBaseURL(dataSetCode, action); - }, - - getImageInfo: function(dataSetCode, callback){ - this.openbis.getImageInfo(dataSetCode, null, callback); - }, - - getImageResolutions: function(dataSetCode, callback){ - this.openbis.getImageResolutions(dataSetCode, callback); - } +$.extend(ChannelStackDefaultChooserWidget.prototype, AbstractWidget.prototype, { + + init : function(channelStacks) { + AbstractWidget.prototype.init.call(this, new ChannelStackDefaultChooserView(this)); + this.channelStackManager = new ChannelStackManager(channelStacks); + }, + + getSelectedChannelStackId : function() { + return null; + }, + + setSelectedChannelStackId : function(channelStackId) { + } + }); // -// CHANNEL CHOOSER VIEW +// MOVIE BUTTONS VIEW // -function ChannelChooserView(controller) { + +function MovieButtonsView(controller) { this.init(controller); } -$.extend(ChannelChooserView.prototype, { +$.extend(MovieButtonsView.prototype, AbstractView.prototype, { - init: function(controller){ - this.controller = controller; + init : function(controller) { + AbstractView.prototype.init.call(this, controller); this.panel = $("<div>"); }, - - render: function(){ - var thisChooser = this; - - this.panel.append(this.createChannelWidget()); - this.panel.append(this.createMergedChannelsWidget()); - this.rendered = true; - this.refresh(); - - return this.panel; - }, - - refresh: function(){ - if(!this.rendered){ - return; - } - - var thisView = this; - - 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.controller.getAllChannels().forEach(function(channel){ - $("<option>").attr("value", channel.code).text(channel.label).appendTo(select); - }); - - select.change(function(){ - if(select.val() == ""){ - thisView.controller.setSelectedChannel(null); - }else{ - thisView.controller.setSelectedChannel(select.val()); - } - }); - - return widget; - }, - - createMergedChannelsWidget: function(){ + render : 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); - }); - - widget.find("input").change(function(){ - var channels = [] - widget.find("input:checked").each(function(){ - channels.push($(this).val()); - }); - thisView.controller.setSelectedMergedChannels(channels); - }); - - return widget; - } - -}); + var play = $("<button>").addClass("play").text("Play").appendTo(this.panel); -// -// CHANNEL CHOOSER -// -function ChannelChooserWidget(channels) { - this.init(channels); -} + play.click(function() { + thisView.controller.play(); + }); -$.extend(ChannelChooserWidget.prototype, { - - init: function(channels){ - this.setView(new ChannelChooserView(this)); - this.listeners = new ListenerManager(); - this.setAllChannels(channels); - }, - - render: function(){ - if(this.getView()){ - return this.getView().render(); - } - }, - - refresh: function(){ - if(this.getView()){ - this.getView().refresh(); - } - }, - - getView: function(){ - return this.view; - }, - - setView: function(view){ - this.view = view; - this.refresh(); - }, - - getSelectedChannel: function(){ - if(this.selectedChannel){ - return this.selectedChannel; - }else{ - return null; - } - }, - - setSelectedChannel: function(channel){ - if(this.getSelectedChannel() != channel){ - this.selectedChannel = channel; - this.refresh(); - this.notifyChangeListeners(); - } - }, - - getSelectedMergedChannels: function(){ - if(this.selectedMergedChannels){ - return this.selectedMergedChannels; - }else{ - return []; - } - }, - - setSelectedMergedChannels: function(channels){ - if(!channels){ - channels = []; - } - - if(this.getSelectedMergedChannels().toString() != channels.toString()){ - this.selectedMergedChannels = channels; - this.refresh(); - this.notifyChangeListeners(); - } - }, - - 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){ - this.listeners.addListener('change', listener); - }, - - notifyChangeListeners: function(){ - this.listeners.notifyListeners('change'); - } - -}); + var stop = $("<button>").addClass("stop").text("Stop").appendTo(this.panel); -// -// RESOLUTION CHOOSER VIEW -// -function ResolutionChooserView(controller) { - this.init(controller); -} + stop.click(function() { + thisView.controller.stop(); + }); -$.extend(ResolutionChooserView.prototype, { + var prev = $("<button>").addClass("prev").text("<<").appendTo(this.panel); - init: function(controller){ - this.controller = controller; - this.panel = $("<div>").addClass("widget"); + prev.click(function() { + thisView.controller.prev(); + }); + + var next = $("<button>").addClass("next").text(">>").appendTo(this.panel); + + next.click(function() { + thisView.controller.next(); + }); + + var delay = $("<input>").attr("type", "text").addClass("delay").appendTo(this.panel); + + delay.change(function() { + thisView.controller.setSelectedDelay(parseInt(delay.val())); + }); + + this.refresh(); + + return this.panel; }, - - 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(""); - } + + refresh : function() { + var play = this.panel.find("button.play"); + play.prop("disabled", this.controller.isPlaying()); + + var stop = this.panel.find("button.stop"); + stop.prop("disabled", this.controller.isStopped()); + + var prev = this.panel.find("button.prev"); + prev.prop("disabled", this.controller.isFirstFrameSelected()); + + var next = this.panel.find("button.next"); + next.prop("disabled", this.controller.isLastFrameSelected()); + + var delay = this.panel.find("input.delay"); + delay.val(this.controller.getSelectedDelay()); } }); // -// RESOLUTION CHOOSER +// MOVIE BUTTONS WIDGET // -function ResolutionChooserWidget(resolutions) { - this.init(resolutions); + +function MovieButtonsWidget(frameCount) { + this.init(frameCount); } -$.extend(ResolutionChooserWidget.prototype, { - - init: function(resolutions){ - this.view = new ResolutionChooserView(this); - this.listeners = new ListenerManager(); - this.setAllResolutions(resolutions); - }, - - render: function(){ - if(this.getView()){ - return this.getView().render(); - } - }, - - refresh: function(){ - 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){ - 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){ - this.listeners.addListener('change', listener); - }, - - notifyChangeListeners: function(){ - this.listeners.notifyListeners('change'); - } +$.extend(MovieButtonsWidget.prototype, AbstractWidget.prototype, { + + init : function(frameCount) { + AbstractWidget.prototype.init.call(this, new MovieButtonsView(this)); + this.frameCount = frameCount; + this.frameContentLoader = function(frameIndex, callback) { + callback(); + }; + this.frameAction = null; + this.selectedDelay = 500; + this.selectedFrame = 0; + }, -}); + play : function() { + if (this.frameAction) { + return; + } -// -// CHANNEL STACK CHOOSER -// -function ChannelStackChooserWidget(channelStacks, channelStackContentLoader) { - this.init(channelStacks, channelStackContentLoader); -} + if (this.getSelectedFrame() == this.frameCount - 1) { + this.setSelectedFrame(0); + } -$.extend(ChannelStackChooserWidget.prototype, { + var thisButtons = this; + + this.frameAction = function() { + if (thisButtons.getSelectedFrame() < thisButtons.frameCount - 1) { + 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); + } + }; + + this.frameAction(); + this.refresh(); + }, + + stop : function() { + if (this.frameAction) { + this.frameAction = null; + this.refresh(); + } + }, - init: function(channelStacks, channelStackContentLoader){ - var manager = new ChannelStackManager(channelStacks); - - if(manager.isMatrix()){ - this.widget = new ChannelStackMatrixChooserWidget(channelStacks, channelStackContentLoader); - }else{ - this.widget = new ChannelStackDefaultChooserWidget(channelStacks, channelStackContentLoader); - } - }, - - render: function(){ - return this.widget.render(); - }, - - getSelectedChannelStackId: function(){ - return this.widget.getSelectedChannelStackId(); - }, - - setSelectedChannelStackId: function(channelStackId){ - this.widget.setSelectedChannelStackId(channelStackId); - }, - - addChangeListener: function(listener){ - this.widget.addChangeListener(listener); - }, - - notifyChangeListeners: function(){ - this.widget.notifyChangeListeners(); - } + prev : function() { + this.setSelectedFrame(this.getSelectedFrame() - 1); + }, -}); + next : function() { + this.setSelectedFrame(this.getSelectedFrame() + 1); + }, -// -// CHANNEL STACK MATRIX CHOOSER VIEW -// + isPlaying : function() { + return this.frameAction != null; + }, -function ChannelStackMatrixChooserView(controller) { - this.init(controller); -} + isStopped : function() { + return this.frameAction == null; + }, + + isFirstFrameSelected : function() { + return this.getSelectedFrame() == 0; + }, + + isLastFrameSelected : function() { + return this.getSelectedFrame() == (this.frameCount - 1) + }, -$.extend(ChannelStackMatrixChooserView.prototype, { - - init: function(controller){ - this.controller = controller; - this.panel = $("<div>").addClass("widget"); - }, - - render: function(){ - 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()); - - this.rendered = true; - this.refresh(); - - 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.controller.getTimePoints().forEach(function(timePoint){ - $("<option>").attr("value", timePoint).text(timePoint).appendTo(timeSelect); - }); - - timeSelect.change(function(){ - thisView.controller.setSelectedTimePoint(timeSelect.val()); - }); - - return widget; - }, - - createDepthWidget: function(){ - var thisView = this; - var widget = $("<span>"); - - $("<span>").text("D:").appendTo(widget); - - var depthSelect = $("<select>").addClass("depthChooser").appendTo(widget); - - this.controller.getDepths().forEach(function(depth){ - $("<option>").attr("value", depth).text(depth).appendTo(depthSelect); - }); - - depthSelect.change(function(){ - thisView.controller.setSelectedDepth(depthSelect.val()); - }); - - 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); - }); - - buttons.addChangeListener(function(){ - var timePoint = thisView.controller.getTimePoints()[buttons.getSelectedFrame()]; - thisView.controller.setSelectedTimePoint(timePoint); - }); - - this.buttons = buttons; - return buttons.render(); - } + getSelectedDelay : function() { + return this.selectedDelay; + }, + + setSelectedDelay : function(delay) { + if (this.selectedDelay != delay) { + this.selectedDelay = delay; + this.refresh(); + } + }, + + getSelectedFrame : function() { + return this.selectedFrame; + }, + + setSelectedFrame : function(frame, callback) { + frame = Math.min(Math.max(0, frame), this.frameCount - 1); + + if (this.selectedFrame != frame) { + log("Selected frame: " + frame); + this.selectedFrame = frame; + this.frameContentLoader(frame, callback); + this.refresh(); + this.notifyChangeListeners(); + } + }, + + setFrameContentLoader : function(frameContentLoader) { + this.frameContentLoader = frameContentLoader; + }, + + getFrameContentLoader : function(frameContentLoader) { + return this.frameContentLoader; + } }); // -// CHANNEL STACK MATRIX CHOOSER +// IMAGE VIEW // -function ChannelStackMatrixChooserWidget(channelStacks, channelStackContentLoader) { - this.init(channelStacks, channelStackContentLoader); + +function ImageView(controller) { + this.init(controller); } -$.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.getView()){ - this.getView().refresh(); - } - }, - - getView: function(){ - return this.view; - }, - - 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(); +$.extend(ImageView.prototype, AbstractView.prototype, { + + init : function(controller) { + AbstractView.prototype.init.call(this, controller); + this.panel = $("<div>") }, - - getChannelStackById: function(channelStackId){ - return this.channelStackManager.getChannelStackById(channelStackId); + + render : function() { + this.refresh(); + return this.panel; }, - - 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(depth != null && this.getSelectedTimePoint() != null){ - var channelStack = this.channelStackManager.getChannelStackByTimePointAndDepth(this.getSelectedTimePoint(), depth); - this.setSelectedChannelStack(channelStack); - }else{ - this.setSelectedChannelStack(null); - } - }, - - addChangeListener: function(listener){ - this.listeners.addListener('change', listener); - }, - - notifyChangeListeners: function(){ - this.listeners.notifyListeners('change'); - } + + refresh : function() { + var thisView = this; + + if (this.controller.getImageData()) { + this.controller.loadImage(this.controller.getImageData(), function(image) { + thisView.panel.empty(); + thisView.panel.append(image); + }); + } else { + this.panel.empty(); + } + } }); // -// CHANNEL STACK DEFAULT CHOOSER +// IMAGE WIDGET // -function ChannelStackDefaultChooserWidget(channelStacks, channelStackContentLoader) { - this.init(channelStacks, channelStackContentLoader); + +function ImageWidget(imageLoader) { + this.init(imageLoader); } -$.extend(ChannelStackDefaultChooserWidget.prototype, { - - init: function(channelStacks, channelStackContentLoader){ - this.channelStackManager = new ChannelStackManager(channelStacks); - this.listeners = new ListenerManager(); - this.panel = $("<div>"); - }, - - render: function(){ - this.panel.append("Default channel stack chooser"); - return this.panel; - }, - - getSelectedChannelStackId: function(){ - return null; - }, - - setSelectedChannelStackId: function(channelStackId){ - }, - - addChangeListener: function(listener){ - this.listeners.addListener('change', listener); - }, - - notifyChangeListeners: function(){ - this.listeners.notifyListeners('change'); - } +$.extend(ImageWidget.prototype, AbstractWidget.prototype, { + + init : function(imageLoader) { + AbstractWidget.prototype.init.call(this, new ImageView(this)); + this.imageLoader = imageLoader; + }, + + loadImage : function(imageData, callback) { + this.imageLoader.loadImage(imageData, callback); + }, + + getImageData : function(imageData) { + return this.imageData; + }, + + setImageData : function(imageData) { + this.imageData = imageData; + this.refresh(); + } }); @@ -991,570 +1230,311 @@ $.extend(ChannelStackDefaultChooserWidget.prototype, { // function ChannelStackManager(channelStacks) { - this.init(channelStacks); + this.init(channelStacks); } $.extend(ChannelStackManager.prototype, { - init: function(channelStacks){ - this.channelStacks = channelStacks; - }, - - isMatrix: function(){ - return !this.isSeriesNumberPresent() && !this.isTimePointMissing() && !this.isDepthMissing() && this.isDepthConsistent(); - }, - - isSeriesNumberPresent: function(){ - return this.channelStacks.some(function(channelStack){ - return channelStack.seriesNumberOrNull; - }); - }, - - isTimePointMissing: function(){ - return this.channelStacks.some(function(channelStack){ - return channelStack.timePointOrNull == null; - }); - }, - - isDepthMissing: function(){ - return this.channelStacks.some(function(channelStack){ - return channelStack.depthOrNull == null; - }); - }, - - isDepthConsistent: function(){ - var map = this.getChannelStackByTimePointAndDepthMap(); - var depthCounts = {}; - - for(timePoint in map){ - var entry = map[timePoint]; - var depthCount = Object.keys(entry).length; - depthCounts[depthCount] = true; - } - - return Object.keys(depthCounts).length == 1; - }, - - getTimePoints: function(){ - if(!this.timePoints){ - var timePoints = {}; - - this.channelStacks.forEach(function(channelStack){ - if(channelStack.timePointOrNull != null){ - timePoints[channelStack.timePointOrNull] = true; - } - }); - - this.timePoints = Object.keys(timePoints); - } - return this.timePoints; - }, - - getTimePoint: function(index){ - return this.getTimePoints()[index]; - }, - - getTimePointIndex: function(timePoint){ - if(!this.timePointsMap){ - var map = {}; - - this.getTimePoints().forEach(function(timePoint, index){ - map[timePoint] = index; - }); - - this.timePointsMap = map; - } - - return this.timePointsMap[timePoint]; - }, - - getDepths: function(){ - if(!this.depths){ - var depths = {}; - - this.channelStacks.forEach(function(channelStack){ - if(channelStack.depthOrNull != null){ - depths[channelStack.depthOrNull] = true; - } - }); - - this.depths = Object.keys(depths); - } - return this.depths; - }, - - getDepth: function(index){ - return this.getDepths()[index]; - }, - - getDepthIndex: function(depth){ - if(!this.depthsMap){ - var map = {}; - - this.getDepths().forEach(function(depth, index){ - map[depth] = index; - }); - - this.depthsMap = map; - } - - return this.depthsMap[depth]; - }, - - getChannelStackByTimePointAndDepth: function(timePoint, depth){ - var map = this.getChannelStackByTimePointAndDepthMap(); - var entry = map[timePoint]; - - if(entry){ - 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; - }, - - getChannelStackByTimePointAndDepthMap: function(){ - if(!this.channelStackMap){ - var map = {}; - this.channelStacks.forEach(function(channelStack){ - if(channelStack.timePointOrNull != null && channelStack.depthOrNull != null){ - var entry = map[channelStack.timePointOrNull]; - if(!entry){ - entry = {}; - map[channelStack.timePointOrNull] = entry; - } - entry[channelStack.depthOrNull] = channelStack; - } - }); - this.channelStackMap = map; - } - return this.channelStackMap; - } -}); + init : function(channelStacks) { + this.channelStacks = channelStacks; + }, -// -// MOVIE BUTTONS VIEW -// + isMatrix : function() { + return !this.isSeriesNumberPresent() && !this.isTimePointMissing() && !this.isDepthMissing() && this.isDepthConsistent(); + }, -function MovieButtonsView(controller) { - this.init(controller); -} + isSeriesNumberPresent : function() { + return this.channelStacks.some(function(channelStack) { + return channelStack.seriesNumberOrNull; + }); + }, -$.extend(MovieButtonsView.prototype, { - - init: function(controller){ - this.controller = controller; - this.panel = $("<div>").addClass("widget"); - }, - - render: function(){ - var thisView = this; - - var play = $("<button>").addClass("play").text("Play").appendTo(this.panel); - - play.click(function(){ - thisView.controller.play(); - }); - - var stop = $("<button>").addClass("stop").text("Stop").appendTo(this.panel); - - stop.click(function(){ - thisView.controller.stop(); - }); - - var prev = $("<button>").addClass("prev").text("<<").appendTo(this.panel); - - prev.click(function(){ - thisView.controller.prev(); - }); - - var next = $("<button>").addClass("next").text(">>").appendTo(this.panel); - - next.click(function(){ - thisView.controller.next(); - }); - - var delay = $("<input>").attr("type", "text").addClass("delay").appendTo(this.panel); - - delay.change(function(){ - thisView.controller.setSelectedDelay(parseInt(delay.val())); - }); - - this.rendered = true; - this.refresh(); - - return this.panel; - }, - - refresh: function(){ - if(!this.rendered){ - return; - } - - var play = this.panel.find("button.play"); - play.prop("disabled", this.controller.isPlaying()); - - var stop = this.panel.find("button.stop"); - stop.prop("disabled", this.controller.isStopped()); - - var prev = this.panel.find("button.prev"); - prev.prop("disabled", this.controller.isFirstFrameSelected()); - - var next = this.panel.find("button.next"); - next.prop("disabled", this.controller.isLastFrameSelected()); - - var delay = this.panel.find("input.delay"); - delay.val(this.selectedDelay); - } + isTimePointMissing : function() { + return this.channelStacks.some(function(channelStack) { + return channelStack.timePointOrNull == null; + }); + }, -}); - -// -// MOVIE BUTTONS WIDGET -// -function MovieButtonsWidget(frameCount, frameLoader) { - this.init(frameCount, frameLoader); -} + isDepthMissing : function() { + return this.channelStacks.some(function(channelStack) { + return channelStack.depthOrNull == null; + }); + }, + + isDepthConsistent : function() { + var map = this.getChannelStackByTimePointAndDepthMap(); + var depthCounts = {}; + + for (timePoint in map) { + var entry = map[timePoint]; + var depthCount = Object.keys(entry).length; + depthCounts[depthCount] = true; + } + + return Object.keys(depthCounts).length == 1; + }, + + getTimePoints : function() { + if (!this.timePoints) { + var timePoints = {}; + + this.channelStacks.forEach(function(channelStack) { + if (channelStack.timePointOrNull != null) { + timePoints[channelStack.timePointOrNull] = true; + } + }); + + this.timePoints = Object.keys(timePoints).map(function(timePoint) { + return parseInt(timePoint); + }); + } + return this.timePoints; + }, + + getTimePoint : function(index) { + return this.getTimePoints()[index]; + }, + + getTimePointIndex : function(timePoint) { + if (!this.timePointsMap) { + var map = {}; + + this.getTimePoints().forEach(function(timePoint, index) { + map[timePoint] = index; + }); + + this.timePointsMap = map; + } + + return this.timePointsMap[timePoint]; + }, + + getDepths : function() { + if (!this.depths) { + var depths = {}; + + this.channelStacks.forEach(function(channelStack) { + if (channelStack.depthOrNull != null) { + depths[channelStack.depthOrNull] = true; + } + }); + + this.depths = Object.keys(depths).map(function(depth) { + return parseInt(depth); + }); + } + return this.depths; + }, + + getDepth : function(index) { + return this.getDepths()[index]; + }, + + getDepthIndex : function(depth) { + if (!this.depthsMap) { + var map = {}; + + this.getDepths().forEach(function(depth, index) { + map[depth] = index; + }); + + this.depthsMap = map; + } + + return this.depthsMap[depth]; + }, + + getChannelStackByTimePointAndDepth : function(timePoint, depth) { + var map = this.getChannelStackByTimePointAndDepthMap(); + var entry = map[timePoint]; -$.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(){ - if(this.frameAction){ - return; - } - - if(this.getSelectedFrame() == this.frameCount - 1){ - this.setSelectedFrame(0); - } - - var thisButtons = this; - - this.frameAction = function(){ - if(thisButtons.getSelectedFrame() < thisButtons.frameCount - 1){ - 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); - } - }; - - this.frameAction(); - this.refresh(); - }, - - stop: function(){ - 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; - }, - - setSelectedDelay: function(delay){ - if(this.selectedDelay != delay){ - this.selectedDelay = delay; - this.refresh(); - } - }, - - getSelectedFrame: function(){ - return this.selectedFrame; - }, - - setSelectedFrame: function(frame, callback){ - frame = Math.min(Math.max(0, frame), this.frameCount - 1); - - if(this.selectedFrame != frame){ - log("Selected frame: " + frame); - this.selectedFrame = frame; - this.frameLoader(frame, callback); - this.refresh(); - this.notifyChangeListeners(); - } - }, - - addChangeListener: function(listener){ - this.listeners.addListener('change', listener); - }, - - notifyChangeListeners: function(){ - this.listeners.notifyListeners('change'); - } + if (entry) { + 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; + }, + getChannelStackByTimePointAndDepthMap : function() { + if (!this.channelStackMap) { + var map = {}; + this.channelStacks.forEach(function(channelStack) { + if (channelStack.timePointOrNull != null && channelStack.depthOrNull != null) { + var entry = map[channelStack.timePointOrNull]; + if (!entry) { + entry = {}; + map[channelStack.timePointOrNull] = entry; + } + entry[channelStack.depthOrNull] = channelStack; + } + }); + this.channelStackMap = map; + } + return this.channelStackMap; + } }); // // IMAGE DATA // + function ImageData() { this.init(); } $.extend(ImageData.prototype, { - init: function(){ - }, - - setDataStoreUrl: function(dataStoreUrl){ - this.dataStoreUrl = dataStoreUrl; - }, - - setSessionToken: function(sessionToken){ - this.sessionToken = sessionToken; - }, - - setDataSetCode: function(dataSetCode){ - this.dataSetCode = dataSetCode; - }, - - setChannelStackId: function(channelStackId){ - this.channelStackId = channelStackId; - }, - - setChannels: function(channels){ - this.channels = channels; - }, - - setResolution: function(resolution){ - this.resolution = resolution; - } + init : function() { + }, + + setDataStoreUrl : function(dataStoreUrl) { + this.dataStoreUrl = dataStoreUrl; + }, + + setSessionToken : function(sessionToken) { + this.sessionToken = sessionToken; + }, + + setDataSetCode : function(dataSetCode) { + this.dataSetCode = dataSetCode; + }, + + setChannelStackId : function(channelStackId) { + this.channelStackId = channelStackId; + }, + + setChannels : function(channels) { + this.channels = channels; + }, + + setResolution : function(resolution) { + this.resolution = resolution; + } }); // // IMAGE LOADER // + function ImageLoader() { - this.init(); + 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); -} + 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); + } + }); + } -$.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(); - } - }); // // CALLBACK MANAGER // + function CallbackManager(callback) { this.init(callback); } $.extend(CallbackManager.prototype, { - init: function(callback){ + init : function(callback) { this.callback = callback; this.callbacks = {}; }, - - registerCallback: function(callback){ - var manager = this; - - var wrapper = function(){ - callback.apply(this, arguments); - - delete manager.callbacks[callback] - - for(c in manager.callbacks){ - return; - } - - manager.callback(); - } - - this.callbacks[callback] = callback; - return wrapper; - } -}); + registerCallback : function(callback) { + var manager = this; + + var wrapper = function() { + callback.apply(this, arguments); + + delete manager.callbacks[callback] + + for (c in manager.callbacks) { + return; + } + + manager.callback(); + } + + this.callbacks[callback] = callback; + return wrapper; + } +}); // // LISTENER MANAGER // + function ListenerManager() { this.init(); } $.extend(ListenerManager.prototype, { - init: function(){ + init : function() { this.listeners = {}; }, - - addListener: function(eventType, listener){ - if(!this.listeners[eventType]){ - this.listeners[eventType] = [] - } - this.listeners[eventType].push(listener); - }, - - notifyListeners: function(eventType){ - if(this.listeners[eventType]){ - this.listeners[eventType].forEach(function(listener){ - listener(); - }); - } - } + + addListener : function(eventType, listener) { + if (!this.listeners[eventType]) { + this.listeners[eventType] = [] + } + this.listeners[eventType].push(listener); + }, + + notifyListeners : function(eventType) { + if (this.listeners[eventType]) { + this.listeners[eventType].forEach(function(listener) { + listener(); + }); + } + } }); -function log(msg){ - if(console){ +function log(msg) { + if (console) { var date = new Date(); console.log(date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "." + date.getMilliseconds() + " - " + msg); } -- GitLab