diff --git a/blame.py b/blame.py
index 53b8297..3e117ea 100644
--- a/blame.py
+++ b/blame.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 from changes import FileDiff
 import comment
 import filtering
@@ -146,72 +147,77 @@ def get(hard):
 __blame_info_text__ = ("Below are the number of rows from each author that have survived and are still "
                        "intact in the current revision")
 
-def output_html(hard):
-	get(hard)
+class BlameOutput(Outputable):
+	def __init__(self, hard):
+		self.hard = hard
+		Outputable.__init__(self)
 
-	blame_xml = "<div class=\"box statistics\">"
-	blame_xml += "<p>" + __blame_info_text__ + ".</p><div><table class=\"git\">"
-	blame_xml += "<thead><tr> <th>Author</th> <th>Rows</th> <th>% in comments</th> </tr></thead>"
-	blame_xml += "<tbody>"
-	chart_data = ""
-	blames = sorted(__blame__.get_summed_blames().items())
-	total_blames = 0
+	def output_html(self):
+		get(self.hard)
 
-	for i in blames:
-		total_blames += i[1].rows
+		blame_xml = "<div class=\"statistics right\"><div class=\"box\">"
+		blame_xml += "<p>" + __blame_info_text__ + ".</p><div><table class=\"git\">"
+		blame_xml += "<thead><tr> <th>Author</th> <th>Rows</th> <th>% in comments</th> </tr></thead>"
+		blame_xml += "<tbody>"
+		chart_data = ""
+		blames = sorted(__blame__.get_summed_blames().items())
+		total_blames = 0
 
-	for i, entry in enumerate(blames):
-		blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
-		blame_xml += "<td>" + entry[0] + "</td>"
-		blame_xml += "<td>" + str(entry[1].rows) + "</td>"
-		blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
-		blame_xml += "</tr>"
-		chart_data += "{{label: \"{0}\", data: {1}}}".format(entry[0], "{0:.2f}".format(100.0 * entry[1].rows / total_blames))
+		for i in blames:
+			total_blames += i[1].rows
 
-		if blames[-1] != entry:
-			chart_data += ", "
+		for i, entry in enumerate(blames):
+			blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
+			blame_xml += "<td>" + entry[0] + "</td>"
+			blame_xml += "<td>" + str(entry[1].rows) + "</td>"
+			blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
+			blame_xml += "</tr>"
+			chart_data += "{{label: \"{0}\", data: {1}}}".format(entry[0], "{0:.2f}".format(100.0 * entry[1].rows / total_blames))
 
-	blame_xml += "<tfoot><tr> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> </tr></tfoot></tbody></table>"
-	blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div></div>"
+			if blames[-1] != entry:
+				chart_data += ", "
 
-	blame_xml += "<script type=\"text/javascript\">"
-	blame_xml += "    $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
-	blame_xml += "        series: {"
-	blame_xml += "            pie: {"
-	blame_xml += "                innerRadius: 0.4,"
-	blame_xml += "                show: true"
-	blame_xml += "            }"
-	blame_xml += "        }"
-	blame_xml += "    });"
-	blame_xml += "</script>"
+		blame_xml += "<tfoot><tr> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> </tr></tfoot></tbody></table>"
+		blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div></div></div>"
 
-	print(blame_xml)
+		blame_xml += "<script type=\"text/javascript\">"
+		blame_xml += "    $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
+		blame_xml += "        series: {"
+		blame_xml += "            pie: {"
+		blame_xml += "                innerRadius: 0.4,"
+		blame_xml += "                show: true"
+		blame_xml += "            }"
+		blame_xml += "        }"
+		blame_xml += "    });"
+		blame_xml += "</script>"
 
-def output_text(hard):
-	print("")
-	get(hard)
+		print(blame_xml)
 
-	if hard and sys.stdout.isatty():
-		terminal.clear_row()
+	def output_text(self):
+		print("")
+		get(self.hard)
 
-	print(textwrap.fill(__blame_info_text__ + ":", width=terminal.get_size()[0]) + "\n")
-	terminal.printb("Author".ljust(21) + "Rows".rjust(10) + "% in comments".rjust(16))
-	for i in sorted(__blame__.get_summed_blames().items()):
-		print(i[0].ljust(20)[0:20], end=" ")
-		print(str(i[1].rows).rjust(10), end=" ")
-		print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(15))
+		if self.hard and sys.stdout.isatty():
+			terminal.clear_row()
 
-def output_xml(hard):
-	get(hard)
+		print(textwrap.fill(__blame_info_text__ + ":", width=terminal.get_size()[0]) + "\n")
+		terminal.printb("Author".ljust(21) + "Rows".rjust(10) + "% in comments".rjust(16))
+		for i in sorted(__blame__.get_summed_blames().items()):
+			print(i[0].ljust(20)[0:20], end=" ")
+			print(str(i[1].rows).rjust(10), end=" ")
+			print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(15))
 
-	message_xml = "\t\t<message>" + __blame_info_text__ + "</message>\n"
-	blame_xml = ""
+	def output_xml(self):
+		get(self.hard)
 
-	for i in sorted(__blame__.get_summed_blames().items()):
-		name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
-		rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
-		percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
-		                              "</percentage-in-comments>\n")
-		blame_xml += "\t\t\t<author>\n" + name_xml + rows_xml + percentage_in_comments_xml + "\t\t\t</author>\n"
+		message_xml = "\t\t<message>" + __blame_info_text__ + "</message>\n"
+		blame_xml = ""
 
-	print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
+		for i in sorted(__blame__.get_summed_blames().items()):
+			name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
+			rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
+			percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
+			                              "</percentage-in-comments>\n")
+			blame_xml += "\t\t\t<author>\n" + name_xml + rows_xml + percentage_in_comments_xml + "\t\t\t</author>\n"
+
+		print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
diff --git a/changes.py b/changes.py
index 3873a1c..5c6fe20 100644
--- a/changes.py
+++ b/changes.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import extensions
 import filtering
 import re
@@ -151,60 +152,98 @@ def get(hard):
 	return __changes__
 
 __historical_info_text__ = "The following historical commit information, by author, was found in the repository"
+__no_commited_files__ = "No commited files with the specified extensions were found"
 
-def output_html(hard):
-	print("HTML output not yet supported.")
+class ChangesOutput(Outputable):
+	def __init__(self, hard):
+		self.hard = hard
+		Outputable.__init__(self)
 
-def output_text(hard):
-	authorinfo_list = get(hard).get_authorinfo_list()
-	total_changes = 0.0
+	def output_html(self):
+		authorinfo_list = get(self.hard).get_authorinfo_list()
+		total_changes = 0.0
+		changes_xml = "<div class=\"statistics right\"><div class=\"box\">"
 
-	for i in authorinfo_list:
-		total_changes += authorinfo_list.get(i).insertions
-		total_changes += authorinfo_list.get(i).deletions
+		for i in authorinfo_list:
+			total_changes += authorinfo_list.get(i).insertions
+			total_changes += authorinfo_list.get(i).deletions
 
-	if authorinfo_list:
-		print(textwrap.fill(__historical_info_text__ + ":", width=terminal.get_size()[0]) + "\n")
-		terminal.printb("Author".ljust(21) + "Commits   " + "Insertions   " + "Deletions   " + "% of changes")
+		if authorinfo_list:
+			changes_xml += "<p>" + __historical_info_text__ + ".</p><div><table class=\"git\">"
+			changes_xml += ("<thead><tr> <th>Author</th> <th>Commits</th> <th>Insertions</th> <th>Deletions</th>" +
+			               "<th>% of changes</th> </tr></thead>")
+			changes_xml += "<tbody>"
 
-		for i in sorted(authorinfo_list):
-			authorinfo = authorinfo_list.get(i)
-			percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
+			for i, entry in enumerate(sorted(authorinfo_list)):
+				authorinfo = authorinfo_list.get(entry)
+				percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
 
-			print(i.ljust(20)[0:20], end=" ")
-			print(str(authorinfo.commits).rjust(7), end=" ")
-			print(str(authorinfo.insertions).rjust(12), end=" ")
-			print(str(authorinfo.deletions).rjust(11), end=" ")
-			print("{0:.2f}".format(percentage).rjust(14))
-	else:
-		print("No commited files with the specified extensions were found.")
+				changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
+				changes_xml += "<td>" + entry + "</td>"
+				changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
+				changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
+				changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
+				changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
+				changes_xml += "</tr>"
 
-def output_xml(hard):
-	authorinfo_list = get(hard).get_authorinfo_list()
-	total_changes = 0.0
+			changes_xml += ("<tfoot><tr> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td>" +
+			               "</tr></tfoot></tbody></table>")
+			changes_xml += "</div>"
+		else:
+			changes_xml += "<p>" + __no_commited_files__ + ".</p>"
 
-	for i in authorinfo_list:
-		total_changes += authorinfo_list.get(i).insertions
-		total_changes += authorinfo_list.get(i).deletions
+		changes_xml += "</div></div>"
+		print(changes_xml)
 
-	if authorinfo_list:
-		message_xml = "\t\t<message>" + __historical_info_text__ + "</message>\n"
-		changes_xml = ""
+	def output_text(self):
+		authorinfo_list = get(self.hard).get_authorinfo_list()
+		total_changes = 0.0
 
-		for i in sorted(authorinfo_list):
-			authorinfo = authorinfo_list.get(i)
-			percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
+		for i in authorinfo_list:
+			total_changes += authorinfo_list.get(i).insertions
+			total_changes += authorinfo_list.get(i).deletions
 
-			name_xml = "\t\t\t\t<name>" + i + "</name>\n"
-			commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
-			insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
-			deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
-			percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
+		if authorinfo_list:
+			print(textwrap.fill(__historical_info_text__ + ":", width=terminal.get_size()[0]) + "\n")
+			terminal.printb("Author".ljust(21) + "Commits   " + "Insertions   " + "Deletions   " + "% of changes")
 
-			changes_xml += ("\t\t\t<author>\n" + name_xml + commits_xml + insertions_xml +
-			                deletions_xml + percentage_xml + "\t\t\t</author>\n")
+			for i in sorted(authorinfo_list):
+				authorinfo = authorinfo_list.get(i)
+				percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
 
-		print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
-	else:
-		print("\t<changes>\n\t\t<exception>" + "No commited files with the specified extensions were found." +
-		      "</exception>\n\t</changes>")
+				print(i.ljust(20)[0:20], end=" ")
+				print(str(authorinfo.commits).rjust(7), end=" ")
+				print(str(authorinfo.insertions).rjust(12), end=" ")
+				print(str(authorinfo.deletions).rjust(11), end=" ")
+				print("{0:.2f}".format(percentage).rjust(14))
+		else:
+			print(__no_commited_files__ + ".")
+
+	def output_xml(self):
+		authorinfo_list = get(self.hard).get_authorinfo_list()
+		total_changes = 0.0
+
+		for i in authorinfo_list:
+			total_changes += authorinfo_list.get(i).insertions
+			total_changes += authorinfo_list.get(i).deletions
+
+		if authorinfo_list:
+			message_xml = "\t\t<message>" + __historical_info_text__ + "</message>\n"
+			changes_xml = ""
+
+			for i in sorted(authorinfo_list):
+				authorinfo = authorinfo_list.get(i)
+				percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
+
+				name_xml = "\t\t\t\t<name>" + i + "</name>\n"
+				commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
+				insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
+				deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
+				percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
+
+				changes_xml += ("\t\t\t<author>\n" + name_xml + commits_xml + insertions_xml +
+				                deletions_xml + percentage_xml + "\t\t\t</author>\n")
+
+			print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
+		else:
+			print("\t<changes>\n\t\t<exception>" + __no_commited_files__ + "</exception>\n\t</changes>")
diff --git a/extensions.py b/extensions.py
index 17fa510..3a7a3fc 100644
--- a/extensions.py
+++ b/extensions.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import terminal
 import textwrap
 
@@ -38,32 +39,30 @@ def add_located(string):
 
 __extensions_info_text__ = "The extensions below were found in the repository history"
 
-def output_html():
-	print("HTML output not yet supported.")
+class Extensions(Outputable):
+	def output_text(self):
+		if __located_extensions__:
+			print("\n" + textwrap.fill(__extensions_info_text__ + "\n(extensions used during statistical analysis are marked):",
+			      width=terminal.get_size()[0]))
 
-def output_text():
-	if __located_extensions__:
-		print("\n" + textwrap.fill(__extensions_info_text__ + "\n(extensions used during statistical analysis are marked):",
-		      width=terminal.get_size()[0]))
+			for i in __located_extensions__:
+				if i in __extensions__:
+					print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
+				else:
+					print (i, end=" ")
+			print("")
 
-		for i in __located_extensions__:
-			if i in __extensions__:
-				print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
-			else:
-				print (i, end=" ")
-		print("")
+	def output_xml(self):
+		if __located_extensions__:
+			message_xml = "\t\t<message>" + __extensions_info_text__ + "</message>\n"
+			used_extensions_xml = ""
+			unused_extensions_xml = ""
 
-def output_xml():
-	if __located_extensions__:
-		message_xml = "\t\t<message>" + __extensions_info_text__ + "</message>\n"
-		used_extensions_xml = ""
-		unused_extensions_xml = ""
+			for i in __located_extensions__:
+				if i in __extensions__:
+					used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
+				else:
+					unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
 
-		for i in __located_extensions__:
-			if i in __extensions__:
-				used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
-			else:
-				unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
-
-		print("\t<extensions>\n" + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
-		      "\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
+			print("\t<extensions>\n" + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
+			      "\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
diff --git a/filtering.py b/filtering.py
index f0ee051..2ddd3f9 100644
--- a/filtering.py
+++ b/filtering.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import terminal
 import textwrap
 
@@ -46,23 +47,21 @@ def set_filtered(file_name):
 __filtering_info_text__ = ("The following files were excluded from the statistics due to the"
                            "specified exclusion patterns")
 
-def output_html():
-	print("HTML output not yet supported.")
+class Filtering(Outputable):
+	def output_text(self):
+		if __filtered_files__:
+			print("\n" + textwrap.fill(__filtering_info_text__ + ":", width=terminal.get_size()[0]))
 
-def output_text():
-	if __filtered_files__:
-		print("\n" + textwrap.fill(__filtering_info_text__ + ":", width=terminal.get_size()[0]))
+			for i in __filtered_files__:
+				(width, _) = terminal.get_size()
+				print("...%s" % i[-width+3:] if len(i) > width else i)
 
-		for i in __filtered_files__:
-			(width, _) = terminal.get_size()
-			print("...%s" % i[-width+3:] if len(i) > width else i)
+	def output_xml(self):
+		if __filtered_files__:
+			message_xml = "\t\t<message>" + __filtering_info_text__ + "</message>\n"
+			filtering_xml = ""
 
-def output_xml():
-	if __filtered_files__:
-		message_xml = "\t\t<message>" + __filtering_info_text__ + "</message>\n"
-		filtering_xml = ""
+			for i in __filtered_files__:
+				filtering_xml += "\t\t\t<file>" + i + "</file>\n"
 
-		for i in __filtered_files__:
-			filtering_xml += "\t\t\t<file>" + i + "</file>\n"
-
-		print("\t<filering>\n" + message_xml + "\t\t<files>\n" + filtering_xml + "\t\t</files>\n\t</filtering>")
+			print("\t<filering>\n" + message_xml + "\t\t<files>\n" + filtering_xml + "\t\t</files>\n\t</filtering>")
diff --git a/format.py b/format.py
index a64675a..6429eda 100644
--- a/format.py
+++ b/format.py
@@ -18,11 +18,10 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+import version
 import base64
 import basedir
 import os
-import terminal
-import version
 import zipfile
 
 __available_formats__ = ["html", "text", "xml"]
@@ -38,6 +37,9 @@ def select(format):
 
 	return format in __available_formats__
 
+def get_selected():
+	return __selected_format__
+
 def is_interactive_format():
 	return __selected_format__ == "text"
 
@@ -75,11 +77,3 @@ def output_footer():
 		print(html_footer)
 	elif __selected_format__ == "xml":
 		print("</gitinspector>")
- 
-def call_output_function(html_function, text_function, xml_function, *parameters):
-	if __selected_format__ == "html":
-		html_function(*parameters)
-	elif __selected_format__ == "text":
-		text_function(*parameters)
-	else:
-		xml_function(*parameters)
diff --git a/gitinspector.py b/gitinspector.py
index 59cc0c6..7adbe02 100755
--- a/gitinspector.py
+++ b/gitinspector.py
@@ -29,6 +29,7 @@ import help
 import metrics
 import missing
 import os
+import outputable
 import responsibilities
 import sys
 import terminal
@@ -51,27 +52,25 @@ class Runner:
 		previous_directory = os.getcwd()
 		os.chdir(self.repo)
 		format.output_header()
-		format.call_output_function(changes.output_html, changes.output_text, changes.output_xml, self.hard)
+		outputable.output(changes.ChangesOutput(self.hard))
 
 		if changes.get(self.hard).get_commits():
-			format.call_output_function(blame.output_html, blame.output_text, blame.output_xml, self.hard)
+			outputable.output(blame.BlameOutput(self.hard))
 
 			if self.timeline:
-				format.call_output_function(timeline.output_html, timeline.output_text, timeline.output_xml,
-				                            changes.get(self.hard), self.useweeks)
+				outputable.output(timeline.Timeline(changes.get(self.hard), self.useweeks))
 
 			if self.include_metrics:
-				format.call_output_function(metrics.output_html, metrics.output_text, metrics.output_xml)
+				outputable.output(metrics.Metrics())
 
 			if self.responsibilities:
-				format.call_output_function(responsibilities.output_html, responsibilities.output_text,
-				                            responsibilities.output_xml, self.hard)
+				outputable.output(responsibilities.ResponsibilitiesOutput(self.hard))
 
-			format.call_output_function(missing.output_html, missing.output_text, missing.output_xml)
-			format.call_output_function(filtering.output_html, filtering.output_text, filtering.output_xml)
+			outputable.output(missing.Missing())
+			outputable.output(filtering.Filtering())
 
 			if self.list_file_types:
-				format.call_output_function(extensions.output_html, extensions.output_text, extensions.output_xml)
+				outputable.output(extensions.Extensions())
 
 		format.output_footer()
 		os.chdir(previous_directory)
diff --git a/metrics.py b/metrics.py
index 442dd86..01e41c0 100644
--- a/metrics.py
+++ b/metrics.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 from changes import FileDiff
 import comment
 import filtering
@@ -27,7 +28,7 @@ import subprocess
 __metric_eloc__ = {"java": 500, "c": 500, "cpp": 500, "h": 300, "hpp": 300, "py": 500, "glsl": 1000,
                    "rb": 500, "js": 500, "sql": 1000, "xml": 1000}
 
-class Metrics:
+class MetricsLogic:
 	def __init__(self):
 		self.eloc = {}
 		ls_tree_r = subprocess.Popen("git ls-tree --name-only -r HEAD", shell=True, bufsize=1, stdout=subprocess.PIPE).stdout
@@ -38,7 +39,7 @@ class Metrics:
 				if not missing.add(i.strip()):
 					file_r = open(i.strip(), "rb")
 					extension = FileDiff.get_extension(i)
-					lines = Metrics.get_eloc(file_r, extension)
+					lines = MetricsLogic.get_eloc(file_r, extension)
 
 					if __metric_eloc__.get(extension, None) != None and __metric_eloc__[extension] < lines:
 						self.eloc[i.strip()] = lines
@@ -63,31 +64,29 @@ class Metrics:
 __eloc_info_text__ = "The following files are suspiciously big (in order of severity)"
 __metrics_missing_info_text__ = "No metrics violations were found in the repository"
 
-def output_html():
-	print("HTML output not yet supported.")
+class Metrics(Outputable):
+	def output_text(self):
+		metrics_logic = MetricsLogic()
 
-def output_text():
-	metrics = Metrics()
+		if not metrics_logic.eloc:
+			print("\n" + __metrics_missing_info_text__ + ".")
+		else:
+			print("\n" + __eloc_info_text__ + ":")
+			for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
+				print(i[1] + " (" + str(i[0]) + " eloc)")
 
-	if not metrics.eloc:
-		print("\n" + __metrics_missing_info_text__ + ".")
-	else:
-		print("\n" + __eloc_info_text__ + ":")
-		for i in sorted(set([(j, i) for (i, j) in metrics.eloc.items()]), reverse = True):
-			print(i[1] + " (" + str(i[0]) + " eloc)")
+	def output_xml(self):
+		metrics_logic = MetricsLogic()
 
-def output_xml():
-	metrics = Metrics()
+		if not metrics_logic.eloc:
+			print("\t<metrics>\n\t\t<message>" + __metrics_missing_info_text__ + "</message>\n\t</metrics>")
+		else:
+			eloc_xml = ""
+			for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
+				eloc_xml += "\t\t\t\t\t<violation>\n"
+				eloc_xml += "\t\t\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
+				eloc_xml += "\t\t\t\t\t\t<lines-of-code>" + str(i[0]) + "</lines-of-code>\n"
+				eloc_xml += "\t\t\t\t\t</violation>\n"
 
-	if not metrics.eloc:
-		print("\t<metrics>\n\t\t<message>" + __metrics_missing_info_text__ + "</message>\n\t</metrics>")
-	else:
-		eloc_xml = ""
-		for i in sorted(set([(j, i) for (i, j) in metrics.eloc.items()]), reverse = True):
-			eloc_xml += "\t\t\t\t\t<violation>\n"
-			eloc_xml += "\t\t\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
-			eloc_xml += "\t\t\t\t\t\t<lines-of-code>" + str(i[0]) + "</lines-of-code>\n"
-			eloc_xml += "\t\t\t\t\t</violation>\n"
-
-		print("\t\t<metrics>\n\t\t\t<eloc>\n\t\t\t\t<message>" + __eloc_info_text__ +
-		      "</message>\n\t\t\t\t<violations>\n" + eloc_xml + "\t\t\t\t</violations>\n\t\t\t</eloc>\n\t\t</metrics>")
+			print("\t\t<metrics>\n\t\t\t<eloc>\n\t\t\t\t<message>" + __eloc_info_text__ +
+			      "</message>\n\t\t\t\t<violations>\n" + eloc_xml + "\t\t\t\t</violations>\n\t\t\t</eloc>\n\t\t</metrics>")
diff --git a/missing.py b/missing.py
index 11e1649..c14b324 100644
--- a/missing.py
+++ b/missing.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import os
 import subprocess
 import terminal
@@ -43,23 +44,21 @@ __missing_info_text__ = ("The following files were missing in the repository and
                          "completely included in the statistical analysis. To include them, you can "
                          "either checkout manually using git or use the -c option in gitinspector")
 
-def output_html():
-	print("HTML output not yet supported.")
+class Missing(Outputable):
+	def output_text(self):
+		if __missing_files__:
+			print("\n" + textwrap.fill(__missing_info_text__ + ":", width=terminal.get_size()[0]))
 
-def output_text():
-	if __missing_files__:
-		print("\n" + textwrap.fill(__missing_info_text__ + ":", width=terminal.get_size()[0]))
+			for missing in __missing_files__:
+				(width, _) = terminal.get_size()
+				print("...%s" % missing[-width+3:] if len(missing) > width else missing)
 
-		for missing in __missing_files__:
-			(width, _) = terminal.get_size()
-			print("...%s" % missing[-width+3:] if len(missing) > width else missing)
+	def output_xml(self):
+		if __missing_files__:
+			message_xml = "\t\t<message>" + __missing_info_text__ + "</message>\n"
+			missing_xml = ""
 
-def output_xml():
-	if __missing_files__:
-		message_xml = "\t\t<message>" + __missing_info_text__ + "</message>\n"
-		missing_xml = ""
+			for missing in __missing_files__:
+				missing_xml += "\t\t\t<file>" + missing + "</file>\n"
 
-		for missing in __missing_files__:
-			missing_xml += "\t\t\t<file>" + missing + "</file>\n"
-
-		print("\t<missing>\n" + message_xml + "\t\t<files>\n" + missing_xml + "\t\t</files>\n\t</missing>")
+			print("\t<missing>\n" + message_xml + "\t\t<files>\n" + missing_xml + "\t\t</files>\n\t</missing>")
diff --git a/outputable.py b/outputable.py
new file mode 100644
index 0000000..648b26d
--- /dev/null
+++ b/outputable.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# Copyright © 2012 Ejwa Software. All rights reserved.
+#
+# This file is part of gitinspector.
+#
+# gitinspector is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# gitinspector is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+import format
+
+class Outputable(object):
+	def output_html(self):
+		print("HTML output not yet supported in " + self + ".")
+
+	def output_text(self):
+		print("Text output not yet supported in " + self + ".")
+
+	def output_xml(self):
+		print("XML output not yet supported in " + self + ".")
+
+def output(outputable):
+	if format.get_selected() == "html":
+		outputable.output_html()
+	elif format.get_selected() == "text":
+		outputable.output_text()
+	else:
+		outputable.output_xml()
diff --git a/responsibilities.py b/responsibilities.py
index bf7b05e..1ae9d82 100644
--- a/responsibilities.py
+++ b/responsibilities.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import blame
 import terminal
 import textwrap
@@ -41,46 +42,49 @@ class Responsibilities:
 __responsibilities_info_text__ = ("The following repsonsibilties, by author, were found in the current "
                                   "revision of the repository (comments are exluded from the line count, "
                                   "if possible)")
-def output_html(hard):
-	print("HTML output not yet supported.")
 
-def output_text(hard):
-	print("\n" + textwrap.fill(__responsibilities_info_text__ + ":", width=terminal.get_size()[0]))
+class ResponsibilitiesOutput(Outputable):
+	def __init__(self, hard):
+		self.hard = hard
+		Outputable.__init__(self)
 
-	for i in sorted(set(i[0] for i in blame.get(hard).blames)):
-		print("\n" + i, "is mostly responsible for:")
-		responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(hard, i)), reverse=True)
+	def output_text(self):
+		print("\n" + textwrap.fill(__responsibilities_info_text__ + ":", width=terminal.get_size()[0]))
 
-		for j, entry in enumerate(responsibilities):
-			(width, _) = terminal.get_size()
-			width -= 7
+		for i in sorted(set(i[0] for i in blame.get(self.hard).blames)):
+			print("\n" + i, "is mostly responsible for:")
+			responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(self.hard, i)), reverse=True)
 
-			print(str(entry[0]).rjust(6), end=" ")
-			print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
+			for j, entry in enumerate(responsibilities):
+				(width, _) = terminal.get_size()
+				width -= 7
 
-			if j >= 9:
-				break
+				print(str(entry[0]).rjust(6), end=" ")
+				print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
 
-def output_xml(hard):
-	message_xml = "\t\t<message>" + __responsibilities_info_text__ + "</message>\n"
-	resp_xml = ""
+				if j >= 9:
+					break
 
-	for i in sorted(set(i[0] for i in blame.get(hard).blames)):
-		resp_xml += "\t\t\t<author>\n"
-		resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
-		resp_xml += "\t\t\t\t<files>\n"
-		responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(hard, i)), reverse=True)
+	def output_xml(self):
+		message_xml = "\t\t<message>" + __responsibilities_info_text__ + "</message>\n"
+		resp_xml = ""
 
-		for j, entry in enumerate(responsibilities):
-			resp_xml += "\t\t\t\t\t<file>\n"
-			resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
-			resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
-			resp_xml += "\t\t\t\t\t</file>\n"
+		for i in sorted(set(i[0] for i in blame.get(self.hard).blames)):
+			resp_xml += "\t\t\t<author>\n"
+			resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
+			resp_xml += "\t\t\t\t<files>\n"
+			responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(self.hard, i)), reverse=True)
 
-			if j >= 9:
-				break
+			for j, entry in enumerate(responsibilities):
+				resp_xml += "\t\t\t\t\t<file>\n"
+				resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
+				resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
+				resp_xml += "\t\t\t\t\t</file>\n"
 
-		resp_xml += "\t\t\t\t</files>\n"
-		resp_xml += "\t\t\t</author>\n"
+				if j >= 9:
+					break
 
-	print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
+			resp_xml += "\t\t\t\t</files>\n"
+			resp_xml += "\t\t\t</author>\n"
+
+		print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
diff --git a/timeline.py b/timeline.py
index 40cc5ef..e5ee59f 100644
--- a/timeline.py
+++ b/timeline.py
@@ -18,6 +18,7 @@
 # along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import print_function
+from outputable import Outputable
 import datetime
 import terminal
 import textwrap
@@ -93,9 +94,6 @@ class TimelineData:
 
 __timeline_info_text__ = "The following history timeline has been gathered from the repository"
 
-def output_html(changes, useweeks):
-	print("HTML output not yet supported.")
-
 def __output_row__text__(timeline_data, periods, names):
 	print("\n" + terminal.__bold__ + "Author".ljust(20), end=" ")
 	
@@ -114,45 +112,51 @@ def __output_row__text__(timeline_data, periods, names):
 			               len(signs_str) == 0 else signs_str).rjust(10), end=" ")
 		print("")
 
-def output_text(changes, useweeks):
-	if changes.get_commits():
-		print("\n" + textwrap.fill(__timeline_info_text__ + ":", width=terminal.get_size()[0]))
+class Timeline(Outputable):
+	def __init__(self, changes, useweeks):
+		self.changes = changes
+		self.useweeks = useweeks
+		Outputable.__init__(self)
 
-		timeline_data = TimelineData(changes, useweeks)
-		periods = timeline_data.get_periods()
-		names = timeline_data.get_authors()
-		(width, _) = terminal.get_size()
-		max_periods_per_row = int((width - 21) / 11)
+	def output_text(self):
+		if self.changes.get_commits():
+			print("\n" + textwrap.fill(__timeline_info_text__ + ":", width=terminal.get_size()[0]))
 
-		for i in range(0, len(periods), max_periods_per_row):
-			__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
+			timeline_data = TimelineData(self.changes, self.useweeks)
+			periods = timeline_data.get_periods()
+			names = timeline_data.get_authors()
+			(width, _) = terminal.get_size()
+			max_periods_per_row = int((width - 21) / 11)
 
-def output_xml(changes, useweeks):
-	if changes.get_commits():
-		message_xml = "\t\t<message>" + __timeline_info_text__ + "</message>\n"
-		timeline_xml = ""
-		periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if useweeks else "month")
+			for i in range(0, len(periods), max_periods_per_row):
+				__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
 
-		timeline_data = TimelineData(changes, useweeks)
-		periods = timeline_data.get_periods()
-		names = timeline_data.get_authors()
+	def output_xml(self):
+		if self.changes.get_commits():
+			message_xml = "\t\t<message>" + __timeline_info_text__ + "</message>\n"
+			timeline_xml = ""
+			periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
 
-		for period in periods:
-			name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
-			authors_xml = ""
+			timeline_data = TimelineData(self.changes, self.useweeks)
+			periods = timeline_data.get_periods()
+			names = timeline_data.get_authors()
 
-			for name in names:
-				authors_xml += "\t\t\t\t<authors>\n"
-				multiplier = timeline_data.get_multiplier(period, 24)
-				signs = timeline_data.get_author_signs_in_period(name, period, multiplier)
-				signs_str = (signs[1] * "-" + signs[0] * "+")
+			for period in periods:
+				name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
+				authors_xml = ""
 
-				if not len(signs_str) == 0:
-					authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name + "</name>\n"
-					authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
+				for name in names:
+					authors_xml += "\t\t\t\t<authors>\n"
+					multiplier = timeline_data.get_multiplier(period, 24)
+					signs = timeline_data.get_author_signs_in_period(name, period, multiplier)
+					signs_str = (signs[1] * "-" + signs[0] * "+")
 
-				authors_xml += "\t\t\t\t</authors>\n"
+					if not len(signs_str) == 0:
+						authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name + "</name>\n"
+						authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
 
-			timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + "\t\t\t</period>\n"
+					authors_xml += "\t\t\t\t</authors>\n"
 
-		print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
+				timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + "\t\t\t</period>\n"
+
+			print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")