Updated and refactored SVG visuaizations
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java
new file mode 100644
index 0000000..ed5d349
--- /dev/null
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java
@@ -0,0 +1,79 @@
+/**
+ ********************************************************************************
+ * Copyright (c) 2023 Robert Bosch GmbH.
+ * 
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Robert Bosch GmbH - initial API and implementation
+ ********************************************************************************
+ */
+
+package org.eclipse.app4mc.amalthea.visualizations.svg;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.app4mc.visualization.ui.VisualizationParameters;
+
+public abstract class AbstractConfig {
+
+	private static final String SCALE_KEY = "Scale";
+	private static final String SCALE_DEFAULT = "100";
+
+	private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+
+	protected final VisualizationParameters parameters;
+
+	protected AbstractConfig(VisualizationParameters viewParameters) {
+		parameters = viewParameters;
+	}
+
+	public int getScale() {
+		return Integer.parseInt(parameters.getOrDefault(SCALE_KEY, SCALE_DEFAULT));
+	}
+
+	/**
+	 * Sets a new scale value (if the new value is different and within the bounds [10, 200])
+	 * 
+	 * @param newScale
+	 * @return true if value was changed
+	 */
+	public boolean setScale(int newScale) {
+		int oldScale = getScale();
+		if (oldScale == newScale || newScale < 10 || newScale > 200) {
+			return false;	
+		}
+	
+		parameters.put(SCALE_KEY, Integer.toString(newScale));
+		firePropertyChange("scale", oldScale, newScale);
+		return true;
+	}
+
+	public boolean decrementScale() {
+		return setScale(Math.max(10, getScale() - 10)); // minimum 10 %
+	}
+
+	public boolean incrementScale() {
+		return setScale(Math.min(200, getScale() + 10)); // maximum 200 %
+	}
+
+	// property change handling
+
+	public void addChangeListener(PropertyChangeListener listener) {
+		changeSupport.addPropertyChangeListener(listener);
+	}
+
+	public void removeChangeListener(PropertyChangeListener listener) {
+		changeSupport.removePropertyChangeListener(listener);
+	}
+
+	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+		changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+	}
+
+}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java
new file mode 100644
index 0000000..b23df1b
--- /dev/null
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java
@@ -0,0 +1,157 @@
+/**
+ ********************************************************************************
+ * Copyright (c) 2023 Robert Bosch GmbH.
+ * 
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Robert Bosch GmbH - initial API and implementation
+ ********************************************************************************
+ */
+
+package org.eclipse.app4mc.amalthea.visualizations.svg;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.function.Consumer;
+
+import org.eclipse.app4mc.visualization.util.svg.SvgUtil;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.e4.core.services.events.IEventBroker;
+import org.eclipse.jface.layout.RowLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationListener;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class AbstractVisualization {
+
+	/**
+	 * Helper for adding a toggle button
+	 * 
+	 * @param parent          container element
+	 * @param text            label text
+	 * @param toolTip         label tool tip
+	 * @param f               button select action; takes the button's selection status as an argument
+	 * @param initialSelected initial selection state of the button
+	 */
+	protected void addToggleButton(Composite parent, String text, String toolTip, final Consumer<Boolean> f, boolean initialSelected) {
+		final Button btn = new Button(parent, SWT.TOGGLE | SWT.FLAT);
+		btn.setText(text);
+		btn.setToolTipText(toolTip);
+		btn.setSelection(initialSelected);
+		btn.addListener(SWT.Selection, e -> f.accept(btn.getSelection()));
+	}
+
+	protected void addZoomBox(Composite buttonArea, AbstractConfig config) {
+		// Align the box with the other buttons
+		final Composite zoomArea = new Composite(buttonArea, SWT.NONE);
+		RowLayoutFactory.fillDefaults().margins(1, 1).applyTo(zoomArea);
+	
+		final Composite box = new Composite(zoomArea, SWT.BORDER);
+	
+		final Button btnLeft = new Button(box, SWT.ARROW | SWT.LEFT | SWT.FLAT);
+		btnLeft.addListener(SWT.Selection, e -> config.decrementScale());
+	
+		final CLabel scaleLabel = new CLabel(box, SWT.FLAT | SWT.CENTER);
+		scaleLabel.setText(String.format("%d %%", config.getScale())); // set initial label text
+	
+		config.addChangeListener(e -> {
+			if (e.getPropertyName().equals("scale"))
+				scaleLabel.setText(String.format("%d %%", (int) e.getNewValue())); // update label text
+		});
+	
+		final Button btnRight = new Button(box, SWT.ARROW | SWT.RIGHT | SWT.FLAT);
+		btnRight.addListener(SWT.Selection, e -> config.incrementScale());
+	
+		RowLayoutFactory.fillDefaults().fill(true).applyTo(box);
+	}
+
+	protected Browser addBrowser(Composite pane, IEventBroker broker, final Context context) {
+		Browser browser = new Browser(pane, SWT.NONE);
+	
+		// Setup navigation to a selected element in the model viewer
+		if (broker != null) {
+			browser.addLocationListener(LocationListener.changingAdapter(c -> {
+				c.doit = true;
+			
+				Object target = null;
+				int idx = c.location.lastIndexOf('#');
+				if (idx >= 0) {
+					target = context.diagram.getObjectById(c.location.substring(idx + 1));
+				}
+				if (target != null) {
+					HashMap<String, Object> data = new HashMap<>();
+					data.put("modelElements", Collections.singletonList(target));
+					broker.send("org/eclipse/app4mc/amalthea/editor/SELECT", data);
+			
+					c.doit = false;
+				}
+			}));
+		}
+	
+		// React to configuration parameter changes
+		context.config.addChangeListener(e -> {
+			if (e.getPropertyName().equals("scale"))
+				updateSvgScale(browser, (int) e.getNewValue());
+			if (e.getPropertyName().startsWith("parameter"))
+				updateBrowserContent(browser, context);
+		});
+	
+		return browser;
+	}
+
+	protected void updateSvgScale(Browser browser, int newScale) {
+		// Update SVG size in browser via JavaScript/DOM
+		if (browser != null) {
+			browser.execute(SvgUtil.buildUpdateScaleCommand(newScale));
+		}
+	}
+
+	/**
+	 * Visualizes a given model element in a browser.
+	 * <p>
+	 * The plantUML graph is constructed and compiled in a separate thread.
+	 * 
+	 * @param browser 
+	 * @param context 
+	 */
+	protected void updateBrowserContent(Browser browser, Context context) {
+		new Thread(() -> {
+			// Build PlantUML diagram text
+			updateDiagram(context);
+	
+			// Render to SVG
+			String result;
+			try {
+				result = context.diagram.renderToSvg();
+			} catch (IOException e) {
+				result = "Error invoking PlantUML: \"" + e.getMessage()
+				+ "\". Make sure you have configured the path to the dot executable properly in the PlantUML preferences.";
+				Platform.getLog(EventChainMapVisualization.class).error(result, e);
+				return;
+			}
+	
+			// Apply initial scale and display
+			if (result != null && !browser.isDisposed()) {
+				final String browserContent = SvgUtil.initiallyApplyScale(result, context.config.getScale());
+				browser.getDisplay().asyncExec(() -> {
+					if (!browser.isDisposed()) {
+						browser.setText(browserContent);
+					}
+				});
+			}
+	
+		}).start();
+	}
+
+	protected abstract void updateDiagram(Context context);
+
+}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java
new file mode 100644
index 0000000..7cfb75f
--- /dev/null
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java
@@ -0,0 +1,33 @@
+/**
+ ********************************************************************************
+ * Copyright (c) 2023 Robert Bosch GmbH.
+ * 
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * 
+ * SPDX-License-Identifier: EPL-2.0
+ * 
+ * Contributors:
+ *     Robert Bosch GmbH - initial API and implementation
+ ********************************************************************************
+ */
+
+package org.eclipse.app4mc.amalthea.visualizations.svg;
+
+import org.eclipse.app4mc.visualization.util.svg.AbstractDiagram;
+import org.eclipse.emf.ecore.EObject;
+
+public final class Context {
+	public final EObject object;
+	public final AbstractConfig config;
+	public final AbstractDiagram diagram;
+
+	public Context(EObject object, AbstractConfig config, AbstractDiagram diagram) {
+		super();
+		this.object = object;
+		this.config = config;
+		this.diagram = diagram;
+	}
+
+}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java
index 80a83c6..42c0d5c 100644
--- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java
@@ -15,33 +15,27 @@
 
 package org.eclipse.app4mc.amalthea.visualizations.svg;
 
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-
 import org.eclipse.app4mc.visualization.ui.VisualizationParameters;
 
 /**
  * Configuration of the visualization
  *
  */
-public class EventChainMapConfig {
+public class EventChainMapConfig extends AbstractConfig {
 	private static final String SHOW_ALL_EVENTS_KEY = "ShowAllEvents";
 	private static final String SHOW_ALL_EVENTS_DEFAULT = "true";
 
+	private static final String SHOW_LINKS_KEY = "ShowLinks";
+	private static final String SHOW_LINKS_DEFAULT = "true";
+
 	private static final String EXPAND_SUBCHAIN_REFERENCES_KEY = "ExpandSubchainReferences";
 	private static final String EXPAND_SUBCHAIN_REFERENCES_DEFAULT = "false";
-	
+
 	private static final String SHOW_REPEATING_EVENTS_GRAYED_KEY = "ShowRepeatingEventsGrayed";
 	private static final String SHOW_REPEATING_EVENTS_GRAYED_DEFAULT = "false";
-
-	private static final String SCALE_KEY = "Scale";
-	private static final String SCALE_DEFAULT = "100";
-
-	private final VisualizationParameters parameters;
-	private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
-
+	
 	public EventChainMapConfig(VisualizationParameters viewParameters) {
-		parameters = viewParameters;
+		super(viewParameters);
 	}
 
 	public boolean isShowAllEvents() {
@@ -53,13 +47,13 @@
 		firePropertyChange("parameter1", null, showAllEvents);
 	}
 
-	public boolean isShowRepeatingEventsGrayed() {
-		return Boolean.parseBoolean(parameters.getOrDefault(SHOW_REPEATING_EVENTS_GRAYED_KEY, SHOW_REPEATING_EVENTS_GRAYED_DEFAULT));
+	public boolean isShowLinks() {
+		return Boolean.parseBoolean(parameters.getOrDefault(SHOW_LINKS_KEY, SHOW_LINKS_DEFAULT));
 	}
 
-	public void setShowRepeatingEventsGrayed(boolean showEventsGrayed) {
-		parameters.put(SHOW_REPEATING_EVENTS_GRAYED_KEY, Boolean.toString(showEventsGrayed));
-		firePropertyChange("parameter2", null, showEventsGrayed);
+	public void setShowLinks(boolean showLinks) {
+		parameters.put(SHOW_LINKS_KEY, Boolean.toString(showLinks));
+		firePropertyChange("parameter2", null, showLinks);
 	}
 
 	public boolean isExpandSubchainReferences() {
@@ -71,47 +65,13 @@
 		firePropertyChange("parameter3", null, expandReferences);
 	}
 
-	public int getScale() {
-		return Integer.parseInt(parameters.getOrDefault(SCALE_KEY, SCALE_DEFAULT));
+	public boolean isShowRepeatingEventsGrayed() {
+		return Boolean.parseBoolean(parameters.getOrDefault(SHOW_REPEATING_EVENTS_GRAYED_KEY, SHOW_REPEATING_EVENTS_GRAYED_DEFAULT));
 	}
 
-	/**
-	 * Sets a new scale value (if the new value is different and within the bounds [10, 200])
-	 * 
-	 * @param newScale
-	 * @return true if value was changed
-	 */
-	public boolean setScale(int newScale) {
-		int oldScale = getScale();
-		if (oldScale == newScale || newScale < 10 || newScale > 200) {
-			return false;	
-		}
-
-		parameters.put(SCALE_KEY, Integer.toString(newScale));
-		firePropertyChange("scale", oldScale, newScale);
-		return true;
-	}
-
-	public boolean decrementScale() {
-		return setScale(Math.max(10, getScale() - 10)); // minimum 10 %
-	}
-
-	public boolean incrementScale() {
-		return setScale(Math.min(200, getScale() + 10)); // maximum 200 %
-	}
-
-	// property change handling
-
-	public void addChangeListener(PropertyChangeListener listener) {
-		changeSupport.addPropertyChangeListener(listener);
-	}
-
-	public void removeChangeListener(PropertyChangeListener listener) {
-		changeSupport.removePropertyChangeListener(listener);
-	}
-
-	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
-		changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+	public void setShowRepeatingEventsGrayed(boolean showEventsGrayed) {
+		parameters.put(SHOW_REPEATING_EVENTS_GRAYED_KEY, Boolean.toString(showEventsGrayed));
+		firePropertyChange("parameter4", null, showEventsGrayed);
 	}
 
 }
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java
index 20e0a2f..c65f7aa 100644
--- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java
@@ -41,28 +41,28 @@
 		throw new IllegalStateException("Utility class");
 	}
 
-	public static void updateDiagram(PlantUmlDiagram diagram, AbstractEventChain eventChain, EventChainMapConfig config) {
+	public static void updateDiagram(PlantUmlDiagram diagram, AbstractEventChain chain, EventChainMapConfig config) {
+
 		// reset old diagram data
 		diagram.resetDiagramData();
 
 		// generate new diagram
-		
-		diagram.append("@startmindmap\n\n");
-
 		diagram.append("' Created by EventChainMapGenerator (" + timestamp() + ")\n\n");
-
-		buildContent(diagram, eventChain, config);
-
-		diagram.append("\n@endmindmap");
+		buildContent(diagram, chain, config);
 	}
 
 	private static void buildContent(PlantUmlDiagram diagram, AbstractEventChain eventChain, EventChainMapConfig config) {
 
-		diagram.append("\n' ===== Event chain (as PlantUML mind map) =====\n\n");
+		diagram.append("' ===== Event chain (as PlantUML mind map) =====\n\n");
+
+		diagram.append("@startmindmap\n\n");
 
 		List<AbstractEventChain> predecessors = new ArrayList<>();
 		List<Event> previousEvents = new ArrayList<>();
+
 		createChain(predecessors, previousEvents, null, eventChain, "+", config, diagram);
+
+		diagram.append("\n@endmindmap");
 	}
 
 	private static void createChain(List<AbstractEventChain> predecessors, List<Event> previousEvents, EventChainItem chainItem, AbstractEventChain chain, String prefix, EventChainMapConfig config, PlantUmlDiagram diagram) {
@@ -75,7 +75,7 @@
 		// chain
 		final String chainName = getChainName(chain);
 		final String chainType = getChainType(chain);
-		final String chainLink = getLinkToObject(chain, diagram);
+		final String chainLink = getLinkToObject(chain, config, diagram);
 		final String chainIcon = (chainItem instanceof EventChainReference) ? REF_ICON : "";
 
 		diagram.append(prefix + " " + chainIcon + chainName + chainType + chainLink + "\n");
@@ -89,7 +89,7 @@
 		if (showEvents) {
 			Event stimulus = chain.getStimulus();
 			String stimulusName = getEventName(stimulus, parent, chainItem, EventType.STIMULUS);
-			String stimulusLink = getLinkToObject(stimulus, diagram);
+			String stimulusLink = getLinkToObject(stimulus, config, diagram);
 
 			if (config.isShowRepeatingEventsGrayed()) {
 				stimulusName = updateEventName(stimulusName, stimulus, previousEvents);				
@@ -114,7 +114,7 @@
 		if (showEvents) {
 			Event response = chain.getResponse();
 			String responseName = getEventName(response, parent, chainItem, EventType.RESPONSE);
-			String responseLink = getLinkToObject(response, diagram);
+			String responseLink = getLinkToObject(response, config, diagram);
 
 			if (config.isShowRepeatingEventsGrayed()) {
 				responseName = updateEventName(responseName, response, previousEvents);				
@@ -124,8 +124,8 @@
 		}
 	}
 
-	private static String getLinkToObject(EObject eObj, PlantUmlDiagram diagram) {
-		if (eObj == null || diagram == null)
+	private static String getLinkToObject(EObject eObj, EventChainMapConfig config, PlantUmlDiagram diagram) {
+		if (eObj == null || diagram == null || !config.isShowLinks())
 			return "";
 
 		final String id = diagram.getOrCreateId(eObj);
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java
index e2d41f6..12cff9c 100644
--- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java
+++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java
@@ -15,28 +15,18 @@
 
 package org.eclipse.app4mc.amalthea.visualizations.svg;
 
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.function.Consumer;
-
 import javax.annotation.PostConstruct;
 
 import org.eclipse.app4mc.amalthea.model.AbstractEventChain;
 import org.eclipse.app4mc.visualization.ui.VisualizationParameters;
 import org.eclipse.app4mc.visualization.ui.registry.Visualization;
 import org.eclipse.app4mc.visualization.util.svg.PlantUmlDiagram;
-import org.eclipse.app4mc.visualization.util.svg.SvgUtil;
-import org.eclipse.core.runtime.Platform;
 import org.eclipse.e4.core.services.events.IEventBroker;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.jface.layout.RowLayoutFactory;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.browser.LocationListener;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.osgi.service.component.annotations.Component;
 
@@ -44,7 +34,7 @@
 		"name=Eventchain Map",
 		"description=Map Visualization for Eventchains"
 })
-public class EventChainMapVisualization implements Visualization {
+public class EventChainMapVisualization extends AbstractVisualization implements Visualization {
 
 	/**
 	 * Entry point for the visualization framework
@@ -62,20 +52,23 @@
 			IEventBroker broker) {
 
 		// Create central context object with all relevant inputs
-		final Context context = new Context(eventChain, parameters);
+		final EventChainMapConfig config = new EventChainMapConfig(parameters);
+		final Context context = createContext(eventChain, config);
 
 		Composite pane = new Composite(parent, SWT.NONE);
 		GridLayoutFactory.fillDefaults().applyTo(pane);
 		Composite buttonArea = new Composite(pane, SWT.NONE);
 
 		addToggleButton(buttonArea, "Show All Event", "Also show events (stimulus and response) of parent chains",
-				context.config::setShowAllEvents, context.config.isShowAllEvents());
+				config::setShowAllEvents, config.isShowAllEvents());
+		addToggleButton(buttonArea, "Show Links", "Show links to model elements",
+				config::setShowLinks, config.isShowLinks());
 		addToggleButton(buttonArea, "Expand Subchain References", null,
-				context.config::setExpandSubchainReferences, context.config.isExpandSubchainReferences());
+				config::setExpandSubchainReferences, config.isExpandSubchainReferences());
 		addToggleButton(buttonArea, "Gray out repeating events", null,
-				context.config::setShowRepeatingEventsGrayed, context.config.isShowRepeatingEventsGrayed());
+				config::setShowRepeatingEventsGrayed, config.isShowRepeatingEventsGrayed());
 
-		addZoomBox(buttonArea, context);
+		addZoomBox(buttonArea, context.config);
 
 		RowLayoutFactory.swtDefaults().fill(true).applyTo(buttonArea);
 
@@ -84,136 +77,27 @@
 		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(browser);
 
 		// Create and display content
-		updateContent(browser, context);
+		updateBrowserContent(browser, context);
 	}
 
-	private Browser addBrowser(Composite pane, IEventBroker broker, final Context context) {
-		Browser browser = new Browser(pane, SWT.NONE);
+	// handling of generic context (and type casts)
 
-		// Setup navigation to a selected element in the model viewer
-		if (broker != null) {
-			browser.addLocationListener(LocationListener.changingAdapter(c -> {
-				c.doit = true;
-			
-				Object target = null;
-				int idx = c.location.lastIndexOf('#');
-				if (idx >= 0) {
-					target = context.diagram.getObjectById(c.location.substring(idx + 1));
-				}
-				if (target != null) {
-					HashMap<String, Object> data = new HashMap<>();
-					data.put("modelElements", Collections.singletonList(target));
-					broker.send("org/eclipse/app4mc/amalthea/editor/SELECT", data);
-			
-					c.doit = false;
-				}
-			}));
-		}
-
-		// React to configuration parameter changes
-		context.config.addChangeListener(e -> {
-			if (e.getPropertyName().equals("scale"))
-				updateSvgScale(browser, (int) e.getNewValue());
-			if (e.getPropertyName().startsWith("parameter"))
-				updateContent(browser, context);
-		});
-
-		return browser;
+	private Context createContext(AbstractEventChain eventChain, EventChainMapConfig config) {
+		return new Context(
+				eventChain,
+				config,
+				new PlantUmlDiagram());
 	}
 
-	/**
-	 * Helper for adding a toggle button
-	 * 
-	 * @param parent          container element
-	 * @param text            label text
-	 * @param toolTip         label tool tip
-	 * @param f               button select action; takes the button's selection status as an argument
-	 * @param initialSelected initial selection state of the button
-	 */
-	private void addToggleButton(Composite parent, String text, String toolTip, final Consumer<Boolean> f, boolean initialSelected) {
-		final Button btn = new Button(parent, SWT.TOGGLE | SWT.FLAT);
-		btn.setText(text);
-		btn.setToolTipText(toolTip);
-		btn.setSelection(initialSelected);
-		btn.addListener(SWT.Selection, e -> f.accept(btn.getSelection()));
-	}
+	protected void updateDiagram(Context context) {
+		if (context.diagram instanceof PlantUmlDiagram
+				&& context.object instanceof AbstractEventChain
+				&& context.config instanceof EventChainMapConfig) {			
 
-	private void addZoomBox(Composite buttonArea, Context context) {
-		// Align the box with the other buttons
-		final Composite zoomArea = new Composite(buttonArea, SWT.NONE);
-		RowLayoutFactory.fillDefaults().margins(1, 1).applyTo(zoomArea);
-
-		final Composite box = new Composite(zoomArea, SWT.BORDER);
-
-		final Button btnLeft = new Button(box, SWT.ARROW | SWT.LEFT | SWT.FLAT);
-		btnLeft.addListener(SWT.Selection, e -> context.config.decrementScale());
-
-		final CLabel scaleLabel = new CLabel(box, SWT.FLAT | SWT.CENTER);
-		scaleLabel.setText(String.format("%d %%", context.config.getScale())); // set initial label text
-
-		context.config.addChangeListener(e -> {
-			if (e.getPropertyName().equals("scale"))
-				scaleLabel.setText(String.format("%d %%", (int) e.getNewValue())); // update label text
-		});
-
-		final Button btnRight = new Button(box, SWT.ARROW | SWT.RIGHT | SWT.FLAT);
-		btnRight.addListener(SWT.Selection, e -> context.config.incrementScale());
-
-		RowLayoutFactory.fillDefaults().fill(true).applyTo(box);
-	}
-
-	private void updateSvgScale(Browser browser, int newScale) {
-		// Update SVG size in browser via JavaScript/DOM
-		if (browser != null) {
-			browser.execute(SvgUtil.buildUpdateScaleCommand(newScale));
-		}
-	}
-
-	/**
-	 * Visualizes a given model element in a browser.
-	 * <p>
-	 * The plantUML graph is constructed and compiled in a separate thread.
-	 * 
-	 * @param browser 
-	 * @param context 
-	 */
-	private void updateContent(Browser browser, Context context) {
-		new Thread(() -> {
-			// Build PlantUML diagram text
-			EventChainMapGenerator.updateDiagram(context.diagram, context.eventChain, context.config);
-
-			// Render to SVG
-			String result;
-			try {
-				result = context.diagram.renderToSvg();
-			} catch (IOException e) {
-				result = "Error invoking PlantUML: \"" + e.getMessage()
-				+ "\". Make sure you have configured the path to the dot executable properly in the PlantUML preferences.";
-				Platform.getLog(EventChainMapVisualization.class).error(result, e);
-				return;
-			}
-
-			// Apply initial scale and display
-			if (result != null && !browser.isDisposed()) {
-				final String browserContent = SvgUtil.initiallyApplyScale(result, context.config.getScale());
-				browser.getDisplay().asyncExec(() -> {
-					if (!browser.isDisposed()) {
-						browser.setText(browserContent);
-					}
-				});
-			}
-
-		}).start();
-	}
-
-	static class Context {
-		public final AbstractEventChain eventChain;
-		public final EventChainMapConfig config;
-		public final PlantUmlDiagram diagram = new PlantUmlDiagram();
-
-		public Context(AbstractEventChain eventChain, VisualizationParameters viewParameters) {
-			this.eventChain = eventChain;
-			this.config =  new EventChainMapConfig(viewParameters);
+			EventChainMapGenerator.updateDiagram(
+					(PlantUmlDiagram) context.diagram,
+					(AbstractEventChain) context.object,
+					(EventChainMapConfig) context.config);
 		}
 	}
 
diff --git a/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java b/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java
index e52172e..ca0f77d 100644
--- a/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java
+++ b/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java
@@ -15,6 +15,8 @@
 
 package org.eclipse.app4mc.visualization.util.svg;
 
+import java.io.IOException;
+
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
 import org.eclipse.core.runtime.preferences.InstanceScope;
@@ -24,7 +26,7 @@
 
 import net.sourceforge.plantuml.eclipse.utils.PlantumlConstants;
 
-abstract class AbstractDiagram {
+public abstract class AbstractDiagram {
 
 	private StringBuilder diagramBuilder = new StringBuilder();
 	private BiMap<Object, String> idMap = HashBiMap.create();
@@ -97,4 +99,6 @@
 		}
 	}
 
+	public abstract String renderToSvg() throws IOException;
+
 }