From e7bda26659e56a8f23a517115d593e81a04f6f35 Mon Sep 17 00:00:00 2001
From: Oskar Marquardt <oskar.marquardt@student.uni-halle.de>
Date: Mon, 1 Jul 2024 12:27:44 +0200
Subject: [PATCH] Added character importing

---
 public/main.js            | 1175 ++++++++++++++++++++++++++++++++++++-
 public/template.json      |  377 ++++++++++++
 src/Character.elm         |    2 +-
 src/Main.elm              |   93 ++-
 src/OrderedCategories.elm |    1 +
 src/Stats.elm             |   13 +-
 6 files changed, 1649 insertions(+), 12 deletions(-)
 create mode 100644 public/template.json

diff --git a/public/main.js b/public/main.js
index 5bd4d75..76f0609 100644
--- a/public/main.js
+++ b/public/main.js
@@ -4370,7 +4370,182 @@ function _Browser_load(url)
 		}
 	}));
 }
-var $elm$core$Basics$EQ = {$: 'EQ'};
+
+
+
+// SEND REQUEST
+
+var _Http_toTask = F3(function(router, toTask, request)
+{
+	return _Scheduler_binding(function(callback)
+	{
+		function done(response) {
+			callback(toTask(request.expect.a(response)));
+		}
+
+		var xhr = new XMLHttpRequest();
+		xhr.addEventListener('error', function() { done($elm$http$Http$NetworkError_); });
+		xhr.addEventListener('timeout', function() { done($elm$http$Http$Timeout_); });
+		xhr.addEventListener('load', function() { done(_Http_toResponse(request.expect.b, xhr)); });
+		$elm$core$Maybe$isJust(request.tracker) && _Http_track(router, xhr, request.tracker.a);
+
+		try {
+			xhr.open(request.method, request.url, true);
+		} catch (e) {
+			return done($elm$http$Http$BadUrl_(request.url));
+		}
+
+		_Http_configureRequest(xhr, request);
+
+		request.body.a && xhr.setRequestHeader('Content-Type', request.body.a);
+		xhr.send(request.body.b);
+
+		return function() { xhr.c = true; xhr.abort(); };
+	});
+});
+
+
+// CONFIGURE
+
+function _Http_configureRequest(xhr, request)
+{
+	for (var headers = request.headers; headers.b; headers = headers.b) // WHILE_CONS
+	{
+		xhr.setRequestHeader(headers.a.a, headers.a.b);
+	}
+	xhr.timeout = request.timeout.a || 0;
+	xhr.responseType = request.expect.d;
+	xhr.withCredentials = request.allowCookiesFromOtherDomains;
+}
+
+
+// RESPONSES
+
+function _Http_toResponse(toBody, xhr)
+{
+	return A2(
+		200 <= xhr.status && xhr.status < 300 ? $elm$http$Http$GoodStatus_ : $elm$http$Http$BadStatus_,
+		_Http_toMetadata(xhr),
+		toBody(xhr.response)
+	);
+}
+
+
+// METADATA
+
+function _Http_toMetadata(xhr)
+{
+	return {
+		url: xhr.responseURL,
+		statusCode: xhr.status,
+		statusText: xhr.statusText,
+		headers: _Http_parseHeaders(xhr.getAllResponseHeaders())
+	};
+}
+
+
+// HEADERS
+
+function _Http_parseHeaders(rawHeaders)
+{
+	if (!rawHeaders)
+	{
+		return $elm$core$Dict$empty;
+	}
+
+	var headers = $elm$core$Dict$empty;
+	var headerPairs = rawHeaders.split('\r\n');
+	for (var i = headerPairs.length; i--; )
+	{
+		var headerPair = headerPairs[i];
+		var index = headerPair.indexOf(': ');
+		if (index > 0)
+		{
+			var key = headerPair.substring(0, index);
+			var value = headerPair.substring(index + 2);
+
+			headers = A3($elm$core$Dict$update, key, function(oldValue) {
+				return $elm$core$Maybe$Just($elm$core$Maybe$isJust(oldValue)
+					? value + ', ' + oldValue.a
+					: value
+				);
+			}, headers);
+		}
+	}
+	return headers;
+}
+
+
+// EXPECT
+
+var _Http_expect = F3(function(type, toBody, toValue)
+{
+	return {
+		$: 0,
+		d: type,
+		b: toBody,
+		a: toValue
+	};
+});
+
+var _Http_mapExpect = F2(function(func, expect)
+{
+	return {
+		$: 0,
+		d: expect.d,
+		b: expect.b,
+		a: function(x) { return func(expect.a(x)); }
+	};
+});
+
+function _Http_toDataView(arrayBuffer)
+{
+	return new DataView(arrayBuffer);
+}
+
+
+// BODY and PARTS
+
+var _Http_emptyBody = { $: 0 };
+var _Http_pair = F2(function(a, b) { return { $: 0, a: a, b: b }; });
+
+function _Http_toFormData(parts)
+{
+	for (var formData = new FormData(); parts.b; parts = parts.b) // WHILE_CONS
+	{
+		var part = parts.a;
+		formData.append(part.a, part.b);
+	}
+	return formData;
+}
+
+var _Http_bytesToBlob = F2(function(mime, bytes)
+{
+	return new Blob([bytes], { type: mime });
+});
+
+
+// PROGRESS
+
+function _Http_track(router, xhr, tracker)
+{
+	// TODO check out lengthComputable on loadstart event
+
+	xhr.upload.addEventListener('progress', function(event) {
+		if (xhr.c) { return; }
+		_Scheduler_rawSpawn(A2($elm$core$Platform$sendToSelf, router, _Utils_Tuple2(tracker, $elm$http$Http$Sending({
+			sent: event.loaded,
+			size: event.total
+		}))));
+	});
+	xhr.addEventListener('progress', function(event) {
+		if (xhr.c) { return; }
+		_Scheduler_rawSpawn(A2($elm$core$Platform$sendToSelf, router, _Utils_Tuple2(tracker, $elm$http$Http$Receiving({
+			received: event.loaded,
+			size: event.lengthComputable ? $elm$core$Maybe$Just(event.total) : $elm$core$Maybe$Nothing
+		}))));
+	});
+}var $elm$core$Basics$EQ = {$: 'EQ'};
 var $elm$core$Basics$GT = {$: 'GT'};
 var $elm$core$Basics$LT = {$: 'LT'};
 var $elm$core$List$cons = _List_cons;
@@ -5343,6 +5518,9 @@ var $elm$core$Platform$Sub$none = $elm$core$Platform$Sub$batch(_List_Nil);
 var $author$project$Main$subscriptions = function (_v0) {
 	return $elm$core$Platform$Sub$none;
 };
+var $author$project$Main$JsonContent = function (a) {
+	return {$: 'JsonContent', a: a};
+};
 var $author$project$Character$changeName = F2(
 	function (character, newName) {
 		return (newName === '') ? character : _Utils_update(
@@ -5515,7 +5693,7 @@ var $author$project$BoundedInt$changeTo = F2(
 			{value: value});
 	});
 var $author$project$Stats$changeDotsTo = F2(
-	function (stat, value) {
+	function (value, stat) {
 		return _Utils_update(
 			stat,
 			{
@@ -5624,11 +5802,960 @@ var $author$project$Character$changeStat = F2(
 						stats: A2(
 							$author$project$Stats$changeStatInList,
 							character.stats,
-							A2($author$project$Stats$changeDotsTo, stat, oldDots.value + difference))
+							A2($author$project$Stats$changeDotsTo, oldDots.value + difference, stat))
 					});
 			}
 		}
 	});
+var $elm$json$Json$Decode$decodeString = _Json_runOnString;
+var $elm$http$Http$BadStatus_ = F2(
+	function (a, b) {
+		return {$: 'BadStatus_', a: a, b: b};
+	});
+var $elm$http$Http$BadUrl_ = function (a) {
+	return {$: 'BadUrl_', a: a};
+};
+var $elm$http$Http$GoodStatus_ = F2(
+	function (a, b) {
+		return {$: 'GoodStatus_', a: a, b: b};
+	});
+var $elm$http$Http$NetworkError_ = {$: 'NetworkError_'};
+var $elm$http$Http$Receiving = function (a) {
+	return {$: 'Receiving', a: a};
+};
+var $elm$http$Http$Sending = function (a) {
+	return {$: 'Sending', a: a};
+};
+var $elm$http$Http$Timeout_ = {$: 'Timeout_'};
+var $elm$core$Dict$RBEmpty_elm_builtin = {$: 'RBEmpty_elm_builtin'};
+var $elm$core$Dict$empty = $elm$core$Dict$RBEmpty_elm_builtin;
+var $elm$core$Maybe$isJust = function (maybe) {
+	if (maybe.$ === 'Just') {
+		return true;
+	} else {
+		return false;
+	}
+};
+var $elm$core$Platform$sendToSelf = _Platform_sendToSelf;
+var $elm$core$Basics$compare = _Utils_compare;
+var $elm$core$Dict$get = F2(
+	function (targetKey, dict) {
+		get:
+		while (true) {
+			if (dict.$ === 'RBEmpty_elm_builtin') {
+				return $elm$core$Maybe$Nothing;
+			} else {
+				var key = dict.b;
+				var value = dict.c;
+				var left = dict.d;
+				var right = dict.e;
+				var _v1 = A2($elm$core$Basics$compare, targetKey, key);
+				switch (_v1.$) {
+					case 'LT':
+						var $temp$targetKey = targetKey,
+							$temp$dict = left;
+						targetKey = $temp$targetKey;
+						dict = $temp$dict;
+						continue get;
+					case 'EQ':
+						return $elm$core$Maybe$Just(value);
+					default:
+						var $temp$targetKey = targetKey,
+							$temp$dict = right;
+						targetKey = $temp$targetKey;
+						dict = $temp$dict;
+						continue get;
+				}
+			}
+		}
+	});
+var $elm$core$Dict$Black = {$: 'Black'};
+var $elm$core$Dict$RBNode_elm_builtin = F5(
+	function (a, b, c, d, e) {
+		return {$: 'RBNode_elm_builtin', a: a, b: b, c: c, d: d, e: e};
+	});
+var $elm$core$Dict$Red = {$: 'Red'};
+var $elm$core$Dict$balance = F5(
+	function (color, key, value, left, right) {
+		if ((right.$ === 'RBNode_elm_builtin') && (right.a.$ === 'Red')) {
+			var _v1 = right.a;
+			var rK = right.b;
+			var rV = right.c;
+			var rLeft = right.d;
+			var rRight = right.e;
+			if ((left.$ === 'RBNode_elm_builtin') && (left.a.$ === 'Red')) {
+				var _v3 = left.a;
+				var lK = left.b;
+				var lV = left.c;
+				var lLeft = left.d;
+				var lRight = left.e;
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Red,
+					key,
+					value,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, lK, lV, lLeft, lRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, rK, rV, rLeft, rRight));
+			} else {
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					color,
+					rK,
+					rV,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, key, value, left, rLeft),
+					rRight);
+			}
+		} else {
+			if ((((left.$ === 'RBNode_elm_builtin') && (left.a.$ === 'Red')) && (left.d.$ === 'RBNode_elm_builtin')) && (left.d.a.$ === 'Red')) {
+				var _v5 = left.a;
+				var lK = left.b;
+				var lV = left.c;
+				var _v6 = left.d;
+				var _v7 = _v6.a;
+				var llK = _v6.b;
+				var llV = _v6.c;
+				var llLeft = _v6.d;
+				var llRight = _v6.e;
+				var lRight = left.e;
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Red,
+					lK,
+					lV,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, llK, llV, llLeft, llRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, key, value, lRight, right));
+			} else {
+				return A5($elm$core$Dict$RBNode_elm_builtin, color, key, value, left, right);
+			}
+		}
+	});
+var $elm$core$Dict$insertHelp = F3(
+	function (key, value, dict) {
+		if (dict.$ === 'RBEmpty_elm_builtin') {
+			return A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, key, value, $elm$core$Dict$RBEmpty_elm_builtin, $elm$core$Dict$RBEmpty_elm_builtin);
+		} else {
+			var nColor = dict.a;
+			var nKey = dict.b;
+			var nValue = dict.c;
+			var nLeft = dict.d;
+			var nRight = dict.e;
+			var _v1 = A2($elm$core$Basics$compare, key, nKey);
+			switch (_v1.$) {
+				case 'LT':
+					return A5(
+						$elm$core$Dict$balance,
+						nColor,
+						nKey,
+						nValue,
+						A3($elm$core$Dict$insertHelp, key, value, nLeft),
+						nRight);
+				case 'EQ':
+					return A5($elm$core$Dict$RBNode_elm_builtin, nColor, nKey, value, nLeft, nRight);
+				default:
+					return A5(
+						$elm$core$Dict$balance,
+						nColor,
+						nKey,
+						nValue,
+						nLeft,
+						A3($elm$core$Dict$insertHelp, key, value, nRight));
+			}
+		}
+	});
+var $elm$core$Dict$insert = F3(
+	function (key, value, dict) {
+		var _v0 = A3($elm$core$Dict$insertHelp, key, value, dict);
+		if ((_v0.$ === 'RBNode_elm_builtin') && (_v0.a.$ === 'Red')) {
+			var _v1 = _v0.a;
+			var k = _v0.b;
+			var v = _v0.c;
+			var l = _v0.d;
+			var r = _v0.e;
+			return A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, k, v, l, r);
+		} else {
+			var x = _v0;
+			return x;
+		}
+	});
+var $elm$core$Dict$getMin = function (dict) {
+	getMin:
+	while (true) {
+		if ((dict.$ === 'RBNode_elm_builtin') && (dict.d.$ === 'RBNode_elm_builtin')) {
+			var left = dict.d;
+			var $temp$dict = left;
+			dict = $temp$dict;
+			continue getMin;
+		} else {
+			return dict;
+		}
+	}
+};
+var $elm$core$Dict$moveRedLeft = function (dict) {
+	if (((dict.$ === 'RBNode_elm_builtin') && (dict.d.$ === 'RBNode_elm_builtin')) && (dict.e.$ === 'RBNode_elm_builtin')) {
+		if ((dict.e.d.$ === 'RBNode_elm_builtin') && (dict.e.d.a.$ === 'Red')) {
+			var clr = dict.a;
+			var k = dict.b;
+			var v = dict.c;
+			var _v1 = dict.d;
+			var lClr = _v1.a;
+			var lK = _v1.b;
+			var lV = _v1.c;
+			var lLeft = _v1.d;
+			var lRight = _v1.e;
+			var _v2 = dict.e;
+			var rClr = _v2.a;
+			var rK = _v2.b;
+			var rV = _v2.c;
+			var rLeft = _v2.d;
+			var _v3 = rLeft.a;
+			var rlK = rLeft.b;
+			var rlV = rLeft.c;
+			var rlL = rLeft.d;
+			var rlR = rLeft.e;
+			var rRight = _v2.e;
+			return A5(
+				$elm$core$Dict$RBNode_elm_builtin,
+				$elm$core$Dict$Red,
+				rlK,
+				rlV,
+				A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, lK, lV, lLeft, lRight),
+					rlL),
+				A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, rK, rV, rlR, rRight));
+		} else {
+			var clr = dict.a;
+			var k = dict.b;
+			var v = dict.c;
+			var _v4 = dict.d;
+			var lClr = _v4.a;
+			var lK = _v4.b;
+			var lV = _v4.c;
+			var lLeft = _v4.d;
+			var lRight = _v4.e;
+			var _v5 = dict.e;
+			var rClr = _v5.a;
+			var rK = _v5.b;
+			var rV = _v5.c;
+			var rLeft = _v5.d;
+			var rRight = _v5.e;
+			if (clr.$ === 'Black') {
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, lK, lV, lLeft, lRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, rK, rV, rLeft, rRight));
+			} else {
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, lK, lV, lLeft, lRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, rK, rV, rLeft, rRight));
+			}
+		}
+	} else {
+		return dict;
+	}
+};
+var $elm$core$Dict$moveRedRight = function (dict) {
+	if (((dict.$ === 'RBNode_elm_builtin') && (dict.d.$ === 'RBNode_elm_builtin')) && (dict.e.$ === 'RBNode_elm_builtin')) {
+		if ((dict.d.d.$ === 'RBNode_elm_builtin') && (dict.d.d.a.$ === 'Red')) {
+			var clr = dict.a;
+			var k = dict.b;
+			var v = dict.c;
+			var _v1 = dict.d;
+			var lClr = _v1.a;
+			var lK = _v1.b;
+			var lV = _v1.c;
+			var _v2 = _v1.d;
+			var _v3 = _v2.a;
+			var llK = _v2.b;
+			var llV = _v2.c;
+			var llLeft = _v2.d;
+			var llRight = _v2.e;
+			var lRight = _v1.e;
+			var _v4 = dict.e;
+			var rClr = _v4.a;
+			var rK = _v4.b;
+			var rV = _v4.c;
+			var rLeft = _v4.d;
+			var rRight = _v4.e;
+			return A5(
+				$elm$core$Dict$RBNode_elm_builtin,
+				$elm$core$Dict$Red,
+				lK,
+				lV,
+				A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, llK, llV, llLeft, llRight),
+				A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					lRight,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, rK, rV, rLeft, rRight)));
+		} else {
+			var clr = dict.a;
+			var k = dict.b;
+			var v = dict.c;
+			var _v5 = dict.d;
+			var lClr = _v5.a;
+			var lK = _v5.b;
+			var lV = _v5.c;
+			var lLeft = _v5.d;
+			var lRight = _v5.e;
+			var _v6 = dict.e;
+			var rClr = _v6.a;
+			var rK = _v6.b;
+			var rV = _v6.c;
+			var rLeft = _v6.d;
+			var rRight = _v6.e;
+			if (clr.$ === 'Black') {
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, lK, lV, lLeft, lRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, rK, rV, rLeft, rRight));
+			} else {
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					$elm$core$Dict$Black,
+					k,
+					v,
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, lK, lV, lLeft, lRight),
+					A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, rK, rV, rLeft, rRight));
+			}
+		}
+	} else {
+		return dict;
+	}
+};
+var $elm$core$Dict$removeHelpPrepEQGT = F7(
+	function (targetKey, dict, color, key, value, left, right) {
+		if ((left.$ === 'RBNode_elm_builtin') && (left.a.$ === 'Red')) {
+			var _v1 = left.a;
+			var lK = left.b;
+			var lV = left.c;
+			var lLeft = left.d;
+			var lRight = left.e;
+			return A5(
+				$elm$core$Dict$RBNode_elm_builtin,
+				color,
+				lK,
+				lV,
+				lLeft,
+				A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Red, key, value, lRight, right));
+		} else {
+			_v2$2:
+			while (true) {
+				if ((right.$ === 'RBNode_elm_builtin') && (right.a.$ === 'Black')) {
+					if (right.d.$ === 'RBNode_elm_builtin') {
+						if (right.d.a.$ === 'Black') {
+							var _v3 = right.a;
+							var _v4 = right.d;
+							var _v5 = _v4.a;
+							return $elm$core$Dict$moveRedRight(dict);
+						} else {
+							break _v2$2;
+						}
+					} else {
+						var _v6 = right.a;
+						var _v7 = right.d;
+						return $elm$core$Dict$moveRedRight(dict);
+					}
+				} else {
+					break _v2$2;
+				}
+			}
+			return dict;
+		}
+	});
+var $elm$core$Dict$removeMin = function (dict) {
+	if ((dict.$ === 'RBNode_elm_builtin') && (dict.d.$ === 'RBNode_elm_builtin')) {
+		var color = dict.a;
+		var key = dict.b;
+		var value = dict.c;
+		var left = dict.d;
+		var lColor = left.a;
+		var lLeft = left.d;
+		var right = dict.e;
+		if (lColor.$ === 'Black') {
+			if ((lLeft.$ === 'RBNode_elm_builtin') && (lLeft.a.$ === 'Red')) {
+				var _v3 = lLeft.a;
+				return A5(
+					$elm$core$Dict$RBNode_elm_builtin,
+					color,
+					key,
+					value,
+					$elm$core$Dict$removeMin(left),
+					right);
+			} else {
+				var _v4 = $elm$core$Dict$moveRedLeft(dict);
+				if (_v4.$ === 'RBNode_elm_builtin') {
+					var nColor = _v4.a;
+					var nKey = _v4.b;
+					var nValue = _v4.c;
+					var nLeft = _v4.d;
+					var nRight = _v4.e;
+					return A5(
+						$elm$core$Dict$balance,
+						nColor,
+						nKey,
+						nValue,
+						$elm$core$Dict$removeMin(nLeft),
+						nRight);
+				} else {
+					return $elm$core$Dict$RBEmpty_elm_builtin;
+				}
+			}
+		} else {
+			return A5(
+				$elm$core$Dict$RBNode_elm_builtin,
+				color,
+				key,
+				value,
+				$elm$core$Dict$removeMin(left),
+				right);
+		}
+	} else {
+		return $elm$core$Dict$RBEmpty_elm_builtin;
+	}
+};
+var $elm$core$Dict$removeHelp = F2(
+	function (targetKey, dict) {
+		if (dict.$ === 'RBEmpty_elm_builtin') {
+			return $elm$core$Dict$RBEmpty_elm_builtin;
+		} else {
+			var color = dict.a;
+			var key = dict.b;
+			var value = dict.c;
+			var left = dict.d;
+			var right = dict.e;
+			if (_Utils_cmp(targetKey, key) < 0) {
+				if ((left.$ === 'RBNode_elm_builtin') && (left.a.$ === 'Black')) {
+					var _v4 = left.a;
+					var lLeft = left.d;
+					if ((lLeft.$ === 'RBNode_elm_builtin') && (lLeft.a.$ === 'Red')) {
+						var _v6 = lLeft.a;
+						return A5(
+							$elm$core$Dict$RBNode_elm_builtin,
+							color,
+							key,
+							value,
+							A2($elm$core$Dict$removeHelp, targetKey, left),
+							right);
+					} else {
+						var _v7 = $elm$core$Dict$moveRedLeft(dict);
+						if (_v7.$ === 'RBNode_elm_builtin') {
+							var nColor = _v7.a;
+							var nKey = _v7.b;
+							var nValue = _v7.c;
+							var nLeft = _v7.d;
+							var nRight = _v7.e;
+							return A5(
+								$elm$core$Dict$balance,
+								nColor,
+								nKey,
+								nValue,
+								A2($elm$core$Dict$removeHelp, targetKey, nLeft),
+								nRight);
+						} else {
+							return $elm$core$Dict$RBEmpty_elm_builtin;
+						}
+					}
+				} else {
+					return A5(
+						$elm$core$Dict$RBNode_elm_builtin,
+						color,
+						key,
+						value,
+						A2($elm$core$Dict$removeHelp, targetKey, left),
+						right);
+				}
+			} else {
+				return A2(
+					$elm$core$Dict$removeHelpEQGT,
+					targetKey,
+					A7($elm$core$Dict$removeHelpPrepEQGT, targetKey, dict, color, key, value, left, right));
+			}
+		}
+	});
+var $elm$core$Dict$removeHelpEQGT = F2(
+	function (targetKey, dict) {
+		if (dict.$ === 'RBNode_elm_builtin') {
+			var color = dict.a;
+			var key = dict.b;
+			var value = dict.c;
+			var left = dict.d;
+			var right = dict.e;
+			if (_Utils_eq(targetKey, key)) {
+				var _v1 = $elm$core$Dict$getMin(right);
+				if (_v1.$ === 'RBNode_elm_builtin') {
+					var minKey = _v1.b;
+					var minValue = _v1.c;
+					return A5(
+						$elm$core$Dict$balance,
+						color,
+						minKey,
+						minValue,
+						left,
+						$elm$core$Dict$removeMin(right));
+				} else {
+					return $elm$core$Dict$RBEmpty_elm_builtin;
+				}
+			} else {
+				return A5(
+					$elm$core$Dict$balance,
+					color,
+					key,
+					value,
+					left,
+					A2($elm$core$Dict$removeHelp, targetKey, right));
+			}
+		} else {
+			return $elm$core$Dict$RBEmpty_elm_builtin;
+		}
+	});
+var $elm$core$Dict$remove = F2(
+	function (key, dict) {
+		var _v0 = A2($elm$core$Dict$removeHelp, key, dict);
+		if ((_v0.$ === 'RBNode_elm_builtin') && (_v0.a.$ === 'Red')) {
+			var _v1 = _v0.a;
+			var k = _v0.b;
+			var v = _v0.c;
+			var l = _v0.d;
+			var r = _v0.e;
+			return A5($elm$core$Dict$RBNode_elm_builtin, $elm$core$Dict$Black, k, v, l, r);
+		} else {
+			var x = _v0;
+			return x;
+		}
+	});
+var $elm$core$Dict$update = F3(
+	function (targetKey, alter, dictionary) {
+		var _v0 = alter(
+			A2($elm$core$Dict$get, targetKey, dictionary));
+		if (_v0.$ === 'Just') {
+			var value = _v0.a;
+			return A3($elm$core$Dict$insert, targetKey, value, dictionary);
+		} else {
+			return A2($elm$core$Dict$remove, targetKey, dictionary);
+		}
+	});
+var $elm$core$Basics$composeR = F3(
+	function (f, g, x) {
+		return g(
+			f(x));
+	});
+var $elm$http$Http$expectStringResponse = F2(
+	function (toMsg, toResult) {
+		return A3(
+			_Http_expect,
+			'',
+			$elm$core$Basics$identity,
+			A2($elm$core$Basics$composeR, toResult, toMsg));
+	});
+var $elm$core$Result$mapError = F2(
+	function (f, result) {
+		if (result.$ === 'Ok') {
+			var v = result.a;
+			return $elm$core$Result$Ok(v);
+		} else {
+			var e = result.a;
+			return $elm$core$Result$Err(
+				f(e));
+		}
+	});
+var $elm$http$Http$BadBody = function (a) {
+	return {$: 'BadBody', a: a};
+};
+var $elm$http$Http$BadStatus = function (a) {
+	return {$: 'BadStatus', a: a};
+};
+var $elm$http$Http$BadUrl = function (a) {
+	return {$: 'BadUrl', a: a};
+};
+var $elm$http$Http$NetworkError = {$: 'NetworkError'};
+var $elm$http$Http$Timeout = {$: 'Timeout'};
+var $elm$http$Http$resolve = F2(
+	function (toResult, response) {
+		switch (response.$) {
+			case 'BadUrl_':
+				var url = response.a;
+				return $elm$core$Result$Err(
+					$elm$http$Http$BadUrl(url));
+			case 'Timeout_':
+				return $elm$core$Result$Err($elm$http$Http$Timeout);
+			case 'NetworkError_':
+				return $elm$core$Result$Err($elm$http$Http$NetworkError);
+			case 'BadStatus_':
+				var metadata = response.a;
+				return $elm$core$Result$Err(
+					$elm$http$Http$BadStatus(metadata.statusCode));
+			default:
+				var body = response.b;
+				return A2(
+					$elm$core$Result$mapError,
+					$elm$http$Http$BadBody,
+					toResult(body));
+		}
+	});
+var $elm$http$Http$expectJson = F2(
+	function (toMsg, decoder) {
+		return A2(
+			$elm$http$Http$expectStringResponse,
+			toMsg,
+			$elm$http$Http$resolve(
+				function (string) {
+					return A2(
+						$elm$core$Result$mapError,
+						$elm$json$Json$Decode$errorToString,
+						A2($elm$json$Json$Decode$decodeString, decoder, string));
+				}));
+	});
+var $elm$http$Http$emptyBody = _Http_emptyBody;
+var $elm$http$Http$Request = function (a) {
+	return {$: 'Request', a: a};
+};
+var $elm$http$Http$State = F2(
+	function (reqs, subs) {
+		return {reqs: reqs, subs: subs};
+	});
+var $elm$http$Http$init = $elm$core$Task$succeed(
+	A2($elm$http$Http$State, $elm$core$Dict$empty, _List_Nil));
+var $elm$core$Process$kill = _Scheduler_kill;
+var $elm$core$Process$spawn = _Scheduler_spawn;
+var $elm$http$Http$updateReqs = F3(
+	function (router, cmds, reqs) {
+		updateReqs:
+		while (true) {
+			if (!cmds.b) {
+				return $elm$core$Task$succeed(reqs);
+			} else {
+				var cmd = cmds.a;
+				var otherCmds = cmds.b;
+				if (cmd.$ === 'Cancel') {
+					var tracker = cmd.a;
+					var _v2 = A2($elm$core$Dict$get, tracker, reqs);
+					if (_v2.$ === 'Nothing') {
+						var $temp$router = router,
+							$temp$cmds = otherCmds,
+							$temp$reqs = reqs;
+						router = $temp$router;
+						cmds = $temp$cmds;
+						reqs = $temp$reqs;
+						continue updateReqs;
+					} else {
+						var pid = _v2.a;
+						return A2(
+							$elm$core$Task$andThen,
+							function (_v3) {
+								return A3(
+									$elm$http$Http$updateReqs,
+									router,
+									otherCmds,
+									A2($elm$core$Dict$remove, tracker, reqs));
+							},
+							$elm$core$Process$kill(pid));
+					}
+				} else {
+					var req = cmd.a;
+					return A2(
+						$elm$core$Task$andThen,
+						function (pid) {
+							var _v4 = req.tracker;
+							if (_v4.$ === 'Nothing') {
+								return A3($elm$http$Http$updateReqs, router, otherCmds, reqs);
+							} else {
+								var tracker = _v4.a;
+								return A3(
+									$elm$http$Http$updateReqs,
+									router,
+									otherCmds,
+									A3($elm$core$Dict$insert, tracker, pid, reqs));
+							}
+						},
+						$elm$core$Process$spawn(
+							A3(
+								_Http_toTask,
+								router,
+								$elm$core$Platform$sendToApp(router),
+								req)));
+				}
+			}
+		}
+	});
+var $elm$http$Http$onEffects = F4(
+	function (router, cmds, subs, state) {
+		return A2(
+			$elm$core$Task$andThen,
+			function (reqs) {
+				return $elm$core$Task$succeed(
+					A2($elm$http$Http$State, reqs, subs));
+			},
+			A3($elm$http$Http$updateReqs, router, cmds, state.reqs));
+	});
+var $elm$core$List$maybeCons = F3(
+	function (f, mx, xs) {
+		var _v0 = f(mx);
+		if (_v0.$ === 'Just') {
+			var x = _v0.a;
+			return A2($elm$core$List$cons, x, xs);
+		} else {
+			return xs;
+		}
+	});
+var $elm$core$List$filterMap = F2(
+	function (f, xs) {
+		return A3(
+			$elm$core$List$foldr,
+			$elm$core$List$maybeCons(f),
+			_List_Nil,
+			xs);
+	});
+var $elm$http$Http$maybeSend = F4(
+	function (router, desiredTracker, progress, _v0) {
+		var actualTracker = _v0.a;
+		var toMsg = _v0.b;
+		return _Utils_eq(desiredTracker, actualTracker) ? $elm$core$Maybe$Just(
+			A2(
+				$elm$core$Platform$sendToApp,
+				router,
+				toMsg(progress))) : $elm$core$Maybe$Nothing;
+	});
+var $elm$http$Http$onSelfMsg = F3(
+	function (router, _v0, state) {
+		var tracker = _v0.a;
+		var progress = _v0.b;
+		return A2(
+			$elm$core$Task$andThen,
+			function (_v1) {
+				return $elm$core$Task$succeed(state);
+			},
+			$elm$core$Task$sequence(
+				A2(
+					$elm$core$List$filterMap,
+					A3($elm$http$Http$maybeSend, router, tracker, progress),
+					state.subs)));
+	});
+var $elm$http$Http$Cancel = function (a) {
+	return {$: 'Cancel', a: a};
+};
+var $elm$http$Http$cmdMap = F2(
+	function (func, cmd) {
+		if (cmd.$ === 'Cancel') {
+			var tracker = cmd.a;
+			return $elm$http$Http$Cancel(tracker);
+		} else {
+			var r = cmd.a;
+			return $elm$http$Http$Request(
+				{
+					allowCookiesFromOtherDomains: r.allowCookiesFromOtherDomains,
+					body: r.body,
+					expect: A2(_Http_mapExpect, func, r.expect),
+					headers: r.headers,
+					method: r.method,
+					timeout: r.timeout,
+					tracker: r.tracker,
+					url: r.url
+				});
+		}
+	});
+var $elm$http$Http$MySub = F2(
+	function (a, b) {
+		return {$: 'MySub', a: a, b: b};
+	});
+var $elm$http$Http$subMap = F2(
+	function (func, _v0) {
+		var tracker = _v0.a;
+		var toMsg = _v0.b;
+		return A2(
+			$elm$http$Http$MySub,
+			tracker,
+			A2($elm$core$Basics$composeR, toMsg, func));
+	});
+_Platform_effectManagers['Http'] = _Platform_createManager($elm$http$Http$init, $elm$http$Http$onEffects, $elm$http$Http$onSelfMsg, $elm$http$Http$cmdMap, $elm$http$Http$subMap);
+var $elm$http$Http$command = _Platform_leaf('Http');
+var $elm$http$Http$subscription = _Platform_leaf('Http');
+var $elm$http$Http$request = function (r) {
+	return $elm$http$Http$command(
+		$elm$http$Http$Request(
+			{allowCookiesFromOtherDomains: false, body: r.body, expect: r.expect, headers: r.headers, method: r.method, timeout: r.timeout, tracker: r.tracker, url: r.url}));
+};
+var $elm$http$Http$get = function (r) {
+	return $elm$http$Http$request(
+		{body: $elm$http$Http$emptyBody, expect: r.expect, headers: _List_Nil, method: 'GET', timeout: $elm$core$Maybe$Nothing, tracker: $elm$core$Maybe$Nothing, url: r.url});
+};
+var $author$project$Character$Character = F3(
+	function (name, creationPoints, stats) {
+		return {creationPoints: creationPoints, name: name, stats: stats};
+	});
+var $elm$json$Json$Decode$field = _Json_decodeField;
+var $elm$json$Json$Decode$int = _Json_decodeInt;
+var $elm$json$Json$Decode$string = _Json_decodeString;
+var $author$project$Main$jsonAbilityDecoder = function (cat) {
+	return A3(
+		$elm$json$Json$Decode$map2,
+		F2(
+			function (name, dots) {
+				return A2(
+					$author$project$Stats$changeDotsTo,
+					dots,
+					A2($author$project$Stats$newAbility, cat, name));
+			}),
+		A2($elm$json$Json$Decode$field, 'name', $elm$json$Json$Decode$string),
+		A2($elm$json$Json$Decode$field, 'dots', $elm$json$Json$Decode$int));
+};
+var $elm$json$Json$Decode$list = _Json_decodeList;
+var $elm$json$Json$Decode$map3 = _Json_map3;
+var $author$project$Main$jsonAbilitiesDecoder = A4(
+	$elm$json$Json$Decode$map3,
+	F3(
+		function (talents, skills, knowledges) {
+			return _Utils_ap(
+				talents,
+				_Utils_ap(skills, knowledges));
+		}),
+	A2(
+		$elm$json$Json$Decode$field,
+		'talents',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAbilityDecoder($author$project$Stats$Skill))),
+	A2(
+		$elm$json$Json$Decode$field,
+		'skills',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAbilityDecoder($author$project$Stats$Talent))),
+	A2(
+		$elm$json$Json$Decode$field,
+		'knowledges',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAbilityDecoder($author$project$Stats$Knowledge))));
+var $author$project$Main$jsonAreteDecoder = A2(
+	$elm$json$Json$Decode$map,
+	function (dots) {
+		return A2($author$project$Stats$changeDotsTo, dots, $author$project$Stats$newArete);
+	},
+	A2($elm$json$Json$Decode$field, 'dots', $elm$json$Json$Decode$int));
+var $author$project$Main$jsonAttributeDecoder = function (cat) {
+	return A3(
+		$elm$json$Json$Decode$map2,
+		F2(
+			function (name, dots) {
+				return A2(
+					$author$project$Stats$changeDotsTo,
+					dots,
+					A2($author$project$Stats$newAttribute, cat, name));
+			}),
+		A2($elm$json$Json$Decode$field, 'name', $elm$json$Json$Decode$string),
+		A2($elm$json$Json$Decode$field, 'dots', $elm$json$Json$Decode$int));
+};
+var $author$project$Main$jsonAttributesDecoder = A4(
+	$elm$json$Json$Decode$map3,
+	F3(
+		function (physical, social, mental) {
+			return _Utils_ap(
+				physical,
+				_Utils_ap(social, mental));
+		}),
+	A2(
+		$elm$json$Json$Decode$field,
+		'physical',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAttributeDecoder($author$project$Stats$Physical))),
+	A2(
+		$elm$json$Json$Decode$field,
+		'social',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAttributeDecoder($author$project$Stats$Social))),
+	A2(
+		$elm$json$Json$Decode$field,
+		'mental',
+		$elm$json$Json$Decode$list(
+			$author$project$Main$jsonAttributeDecoder($author$project$Stats$Mental))));
+var $author$project$Main$jsonBackgroundDecoder = A4(
+	$elm$json$Json$Decode$map3,
+	F3(
+		function (name, dots, max) {
+			return A2(
+				$author$project$Stats$changeDotsTo,
+				dots,
+				A2($author$project$Stats$newBackground, name, max));
+		}),
+	A2($elm$json$Json$Decode$field, 'name', $elm$json$Json$Decode$string),
+	A2($elm$json$Json$Decode$field, 'dots', $elm$json$Json$Decode$int),
+	A2($elm$json$Json$Decode$field, 'max', $elm$json$Json$Decode$int));
+var $elm$json$Json$Decode$bool = _Json_decodeBool;
+var $author$project$Stats$setAffinityNoChecks = F2(
+	function (affinity, stat) {
+		var _v0 = stat.statType;
+		if (_v0.$ === 'Sphere') {
+			return _Utils_update(
+				stat,
+				{
+					statType: $author$project$Stats$Sphere(affinity)
+				});
+		} else {
+			return stat;
+		}
+	});
+var $author$project$Main$jsonSphereDecoder = A4(
+	$elm$json$Json$Decode$map3,
+	F3(
+		function (name, dots, affinity) {
+			return A2(
+				$author$project$Stats$setAffinityNoChecks,
+				affinity,
+				A2(
+					$author$project$Stats$changeDotsTo,
+					dots,
+					$author$project$Stats$newSphere(name)));
+		}),
+	A2($elm$json$Json$Decode$field, 'name', $elm$json$Json$Decode$string),
+	A2($elm$json$Json$Decode$field, 'dots', $elm$json$Json$Decode$int),
+	A2($elm$json$Json$Decode$field, 'affinity', $elm$json$Json$Decode$bool));
+var $elm$json$Json$Decode$map5 = _Json_map5;
+var $elm$core$List$singleton = function (value) {
+	return _List_fromArray(
+		[value]);
+};
+var $author$project$Main$jsonStatsDecoder = A6(
+	$elm$json$Json$Decode$map5,
+	F5(
+		function (spheres, arete, attributes, abilities, backgrounds) {
+			return _Utils_ap(
+				spheres,
+				_Utils_ap(
+					$elm$core$List$singleton(arete),
+					_Utils_ap(
+						attributes,
+						_Utils_ap(abilities, backgrounds))));
+		}),
+	A2(
+		$elm$json$Json$Decode$field,
+		'spheres',
+		$elm$json$Json$Decode$list($author$project$Main$jsonSphereDecoder)),
+	A2($elm$json$Json$Decode$field, 'arete', $author$project$Main$jsonAreteDecoder),
+	A2($elm$json$Json$Decode$field, 'attributes', $author$project$Main$jsonAttributesDecoder),
+	A2($elm$json$Json$Decode$field, 'abilities', $author$project$Main$jsonAbilitiesDecoder),
+	A2(
+		$elm$json$Json$Decode$field,
+		'backgrounds',
+		$elm$json$Json$Decode$list($author$project$Main$jsonBackgroundDecoder)));
+var $author$project$Main$jsonDecoder = A4(
+	$elm$json$Json$Decode$map3,
+	$author$project$Character$Character,
+	A2($elm$json$Json$Decode$field, 'name', $elm$json$Json$Decode$string),
+	$elm$json$Json$Decode$succeed($author$project$Creation$new),
+	A2($elm$json$Json$Decode$field, 'stats', $author$project$Main$jsonStatsDecoder));
 var $author$project$Main$modalValue = F2(
 	function (modalType, character) {
 		return character.name;
@@ -5746,7 +6873,7 @@ var $author$project$Main$update = F2(
 						model,
 						{dropdownActive: value}),
 					$elm$core$Platform$Cmd$none);
-			default:
+			case 'SetAffinity':
 				var stat = msg.a;
 				return _Utils_Tuple2(
 					_Utils_update(
@@ -5755,6 +6882,30 @@ var $author$project$Main$update = F2(
 							character: A2($author$project$Character$setAffinity, model.character, stat)
 						}),
 					$elm$core$Platform$Cmd$none);
+			case 'ImportCharacter':
+				return _Utils_Tuple2(
+					model,
+					$elm$http$Http$get(
+						{
+							expect: A2($elm$http$Http$expectJson, $author$project$Main$JsonContent, $author$project$Main$jsonDecoder),
+							url: './template.json'
+						}));
+			default:
+				var content = msg.a;
+				return _Utils_Tuple2(
+					_Utils_update(
+						model,
+						{
+							character: function () {
+								if (content.$ === 'Ok') {
+									var character = content.a;
+									return character;
+								} else {
+									return model.character;
+								}
+							}()
+						}),
+					$elm$core$Platform$Cmd$none);
 		}
 	});
 var $author$project$Main$Name = {$: 'Name'};
@@ -5820,7 +6971,6 @@ var $author$project$Stats$count = function (stats) {
 				},
 				stats)));
 };
-var $elm$core$Basics$compare = _Utils_compare;
 var $author$project$Main$creationInfo = function (points) {
 	var notificationColor = function () {
 		var _v0 = A2($elm$core$Basics$compare, points, 0);
@@ -6680,6 +7830,18 @@ var $author$project$Main$viewEditableText = F2(
 						]))
 				]));
 	});
+var $author$project$Main$ImportCharacter = {$: 'ImportCharacter'};
+var $author$project$Main$viewImport = A2(
+	$elm$html$Html$button,
+	_List_fromArray(
+		[
+			$elm$html$Html$Attributes$class('button'),
+			$elm$html$Html$Events$onClick($author$project$Main$ImportCharacter)
+		]),
+	_List_fromArray(
+		[
+			$elm$html$Html$text('Import Character')
+		]));
 var $author$project$Main$ChangeModalValue = function (a) {
 	return {$: 'ChangeModalValue', a: a};
 };
@@ -6706,12 +7868,10 @@ var $elm$html$Html$Events$stopPropagationOn = F2(
 			event,
 			$elm$virtual_dom$VirtualDom$MayStopPropagation(decoder));
 	});
-var $elm$json$Json$Decode$field = _Json_decodeField;
 var $elm$json$Json$Decode$at = F2(
 	function (fields, decoder) {
 		return A3($elm$core$List$foldr, $elm$json$Json$Decode$field, decoder, fields);
 	});
-var $elm$json$Json$Decode$string = _Json_decodeString;
 var $elm$html$Html$Events$targetValue = A2(
 	$elm$json$Json$Decode$at,
 	_List_fromArray(
@@ -7074,6 +8234,7 @@ var $author$project$Main$view = function (model) {
 						$author$project$Main$viewSpheres,
 						model.character.creationPoints,
 						$author$project$Stats$spheresInList(model.character.stats)),
+						$author$project$Main$viewImport,
 						$author$project$Main$viewModal(model)
 					]))
 			]),
diff --git a/public/template.json b/public/template.json
new file mode 100644
index 0000000..2d11a85
--- /dev/null
+++ b/public/template.json
@@ -0,0 +1,377 @@
+{
+    "name": "John Doe",
+    "creation": {
+        "freebie": 15,
+        "spheres": 6,
+        "attributes": {
+            "points": [ 7, 5, 3 ],
+            "order": [
+                "physical",
+                "social",
+                "mental"
+            ]
+        },
+        "abilities": {
+            "points": [ 13, 9, 5 ],
+            "order": [
+                "skills",
+                "talents",
+                "knowledges"
+            ]
+        },
+        "abilityDotLimit": 3,
+        "backgrounds": 7
+    },
+    "stats": {
+        "spheres": [ {
+                "name": "Correspondence",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Entropy",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Forces",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Life",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Matter",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Mind",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Prime",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Spirit",
+                "dots": 0,
+                "affinity": false
+            }, {
+                "name": "Time",
+                "dots": 0,
+                "affinity": false
+            }
+        ],
+        "arete": {
+            "dots": 1
+        },
+        "attributes": {
+            "physical": [
+                {
+                    "name": "Strength",
+                    "dots": 1
+                }, {
+                    "name": "Dexterity",
+                    "dots": 1
+                }, {
+                    "name": "Stamina",
+                    "dots": 1
+                }
+            ],
+            "social" : [
+                {
+                    "name": "Charisma",
+                    "dots": 1
+                }, {
+                    "name": "Manipulation",
+                    "dots": 1
+                }, {
+                    "name": "Appearance",
+                    "dots": 1
+                }
+            ],
+            "mental" : [
+                {
+                    "name": "Perception",
+                    "dots": 1
+                }, {
+                    "name": "Intelligence",
+                    "dots": 1
+                }, {
+                    "name": "Wits",
+                    "dots": 1
+                }
+            ]
+        },
+        "abilities" : {
+            "talents": [
+                {
+                    "name": "Alertness",
+                    "dots": 0
+                }, {
+                    "name": "Art",
+                    "dots": 0
+                }, {
+                    "name": "Athletics",
+                    "dots": 0
+                }, {
+                    "name": "Awareness",
+                    "dots": 0
+                }, {
+                    "name": "Brawl",
+                    "dots": 0
+                }, {
+                    "name": "Empathy",
+                    "dots": 0
+                }, {
+                    "name": "Expression",
+                    "dots": 0
+                }, {
+                    "name": "Intimidation",
+                    "dots": 0
+                }, {
+                    "name": "Leadership",
+                    "dots": 0
+                }, {
+                    "name": "Streetwise",
+                    "dots": 0
+                }, {
+                    "name": "Subterfuge",
+                    "dots": 0
+                }
+            ],
+            "skills": [
+                {
+                    "name": "Crafts",
+                    "dots": 0
+                }, {
+                    "name": "Drive",
+                    "dots": 0
+                }, {
+                    "name": "Etiquette",
+                    "dots": 0
+                }, {
+                    "name": "Firearms",
+                    "dots": 0
+                }, {
+                    "name": "Martial Arts",
+                    "dots": 0
+                }, {
+                    "name": "Meditation",
+                    "dots": 0
+                }, {
+                    "name": "Melee",
+                    "dots": 0
+                }, {
+                    "name": "Research",
+                    "dots": 0
+                }, {
+                    "name": "Stealth",
+                    "dots": 0
+                }, {
+                    "name": "Survival",
+                    "dots": 0
+                }, {
+                    "name": "Technology",
+                    "dots": 0
+                }
+            ],
+            "knowledges": [
+                {
+                    "name": "Academics",
+                    "dots": 0
+                }, {
+                    "name": "Computer",
+                    "dots": 0
+                }, {
+                    "name": "Cosmology",
+                    "dots": 0
+                }, {
+                    "name": "Enigmas",
+                    "dots": 0
+                }, {
+                    "name": "Esoterica",
+                    "dots": 0
+                }, {
+                    "name": "Investigation",
+                    "dots": 0
+                }, {
+                    "name": "Law",
+                    "dots": 0
+                }, {
+                    "name": "Medicine",
+                    "dots": 0
+                }, {
+                    "name": "Occult",
+                    "dots": 0
+                }, {
+                    "name": "Politics",
+                    "dots": 0
+                }, {
+                    "name": "Science",
+                    "dots": 0
+                }
+            ]
+        },
+        "backgrounds": [
+            {
+                "name": "Allies",
+                "dots": 0,
+                "max": 10
+            }, {
+                "name": "Alternate Identity",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Arcane",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Avatar",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Backup",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Certification",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Chantry",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Contacts",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Cult",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Demesne",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Destiny",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Dream",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Enhancements",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Fame",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Familiar",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Influence",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Legend",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Library",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Mentor",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Node",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Past Lives",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Patron",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Rank",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Requisitions",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Resources",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Retainers",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Sanctum",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Secret Weapons",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Spies",
+                "dots": 0,
+                "max": 10
+            }
+            , {
+                "name": "Status",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Totem",
+                "dots": 0,
+                "max": 5
+            }
+            , {
+                "name": "Wonder",
+                "dots": 0,
+                "max": 5
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/src/Character.elm b/src/Character.elm
index d9805fb..5289e58 100644
--- a/src/Character.elm
+++ b/src/Character.elm
@@ -63,7 +63,7 @@ changeStat character stat =
                     Nothing -> character
                     Just oldDots ->
                         { character
-                        | stats = Stats.changeStatInList character.stats ( Stats.changeDotsTo stat ( oldDots.value + difference ) )
+                        | stats = Stats.changeStatInList character.stats ( Stats.changeDotsTo ( oldDots.value + difference ) stat )
                         }
 
 changeOrder : Character -> CategoriesType -> Category -> Int -> Character
diff --git a/src/Main.elm b/src/Main.elm
index fc42b44..d4c6f90 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -12,6 +12,8 @@ import BoundedInt exposing (BoundedInt)
 import Stats exposing (Category)
 import Creation exposing (CreationPoints)
 import OrderedCategories as OCat exposing (OCat, CategoriesType)
+import Http
+import Json.Decode
 
 main : Program () Model Msg
 main = 
@@ -48,6 +50,8 @@ type Msg
     | ChangePriority CategoriesType Category Int
     | SetDropDownActive Bool
     | SetAffinity Stat
+    | ImportCharacter
+    | JsonContent (Result Http.Error Character )
 
 type ModalType
     = Name
@@ -106,6 +110,21 @@ update msg model =
                 }
             , Cmd.none
             )
+        ImportCharacter ->
+            ( model
+            , Http.get
+                { url = "./template.json"
+                , expect = Http.expectJson JsonContent jsonDecoder
+                }
+            )
+        JsonContent content ->
+            (   { model
+                | character = case content of
+                    Ok character -> character
+                    Err _ -> model.character
+                }
+            , Cmd.none
+            )
 
 view : Model -> Browser.Document Msg
 view model =
@@ -118,6 +137,7 @@ view model =
             , viewAbilities model.character.creationPoints.abilities model.character.stats
             , viewBackgrounds model.dropdownActive model.character.creationPoints ( Stats.backgroundsInList model.character.stats )
             , viewSpheres model.character.creationPoints ( Stats.spheresInList model.character.stats )
+            , viewImport
             , viewModal model
             ]
         ]
@@ -496,6 +516,13 @@ valueOrReset dots value =
     then dots.min
     else value
 
+viewImport : Html Msg
+viewImport =
+    button 
+        [ class "button"
+        , onClick ImportCharacter
+        ] [ text "Import Character" ]
+
 viewModal : Model -> Html Msg
 viewModal model =
     case model.modal of
@@ -570,4 +597,68 @@ ariaHidden value =
 
 ariaRole : String -> Attribute msg
 ariaRole value =
-    Html.Attributes.attribute "role" value
\ No newline at end of file
+    Html.Attributes.attribute "role" value
+
+-- json decoder
+
+jsonDecoder : Json.Decode.Decoder Character
+jsonDecoder =
+    Json.Decode.map3 Character
+        ( Json.Decode.field   "name"  Json.Decode.string )
+        ( Json.Decode.succeed         Creation.new       )
+        ( Json.Decode.field   "stats" jsonStatsDecoder   )
+
+
+jsonStatsDecoder : Json.Decode.Decoder ( List Stat )
+jsonStatsDecoder =
+    Json.Decode.map5 ( \spheres arete attributes abilities backgrounds -> spheres ++ ( List.singleton arete ) ++ attributes ++ abilities ++ backgrounds )
+        ( Json.Decode.field "spheres"     <| Json.Decode.list jsonSphereDecoder     )
+        ( Json.Decode.field "arete"          jsonAreteDecoder                       )
+        ( Json.Decode.field "attributes"  <| jsonAttributesDecoder                  )
+        ( Json.Decode.field "abilities"   <| jsonAbilitiesDecoder                   )
+        ( Json.Decode.field "backgrounds" <| Json.Decode.list jsonBackgroundDecoder )
+
+jsonSphereDecoder : Json.Decode.Decoder Stat
+jsonSphereDecoder =
+    Json.Decode.map3 ( \name dots affinity -> Stats.newSphere name |> Stats.changeDotsTo dots |> Stats.setAffinityNoChecks affinity )
+        ( Json.Decode.field "name"     Json.Decode.string )
+        ( Json.Decode.field "dots"     Json.Decode.int    )
+        ( Json.Decode.field "affinity" Json.Decode.bool   )
+
+jsonAreteDecoder : Json.Decode.Decoder Stat
+jsonAreteDecoder =
+    Json.Decode.map ( \dots -> Stats.newArete |> Stats.changeDotsTo dots )
+        ( Json.Decode.field "dots" Json.Decode.int )
+
+jsonAttributesDecoder : Json.Decode.Decoder ( List Stat )
+jsonAttributesDecoder =
+    Json.Decode.map3 ( \physical social mental -> physical ++ social ++ mental )
+        ( Json.Decode.field "physical" <| Json.Decode.list ( jsonAttributeDecoder Stats.Physical ) )
+        ( Json.Decode.field "social"   <| Json.Decode.list ( jsonAttributeDecoder Stats.Social   ) )
+        ( Json.Decode.field "mental"   <| Json.Decode.list ( jsonAttributeDecoder Stats.Mental   ) )
+
+jsonAttributeDecoder : Stats.Category -> Json.Decode.Decoder Stat
+jsonAttributeDecoder cat =
+    Json.Decode.map2 ( \name dots -> Stats.newAttribute cat name |> Stats.changeDotsTo dots )
+        ( Json.Decode.field "name" Json.Decode.string )
+        ( Json.Decode.field "dots" Json.Decode.int    )
+
+jsonAbilitiesDecoder : Json.Decode.Decoder ( List Stat )
+jsonAbilitiesDecoder =
+    Json.Decode.map3 ( \talents skills knowledges -> talents ++ skills ++ knowledges )
+        ( Json.Decode.field "talents"    <| Json.Decode.list ( jsonAbilityDecoder Stats.Skill     ) )
+        ( Json.Decode.field "skills"     <| Json.Decode.list ( jsonAbilityDecoder Stats.Talent    ) )
+        ( Json.Decode.field "knowledges" <| Json.Decode.list ( jsonAbilityDecoder Stats.Knowledge ) )
+
+jsonAbilityDecoder : Stats.Category -> Json.Decode.Decoder Stat
+jsonAbilityDecoder cat =
+    Json.Decode.map2 ( \name dots -> Stats.newAbility cat name |> Stats.changeDotsTo dots )
+        ( Json.Decode.field "name" Json.Decode.string )
+        ( Json.Decode.field "dots" Json.Decode.int    )
+
+jsonBackgroundDecoder : Json.Decode.Decoder Stat
+jsonBackgroundDecoder =
+    Json.Decode.map3 ( \name dots max -> Stats.newBackground name max |> Stats.changeDotsTo dots )
+        ( Json.Decode.field "name" Json.Decode.string )
+        ( Json.Decode.field "dots" Json.Decode.int    )
+        ( Json.Decode.field "max"  Json.Decode.int    )
\ No newline at end of file
diff --git a/src/OrderedCategories.elm b/src/OrderedCategories.elm
index 7d5cb99..17afc0d 100644
--- a/src/OrderedCategories.elm
+++ b/src/OrderedCategories.elm
@@ -10,6 +10,7 @@ type alias OrderedCategories =
     { points : List Int 
     , order : List Category
     }
+
 type alias OCat = OrderedCategories
 
 new : CategoriesType -> OCat
diff --git a/src/Stats.elm b/src/Stats.elm
index 250397a..28f4318 100644
--- a/src/Stats.elm
+++ b/src/Stats.elm
@@ -249,8 +249,8 @@ changeStatIfSame newStat oldStat =
     else
         oldStat
 
-changeDotsTo : Stat -> Int -> Stat
-changeDotsTo stat value =
+changeDotsTo : Int -> Stat -> Stat
+changeDotsTo value stat =
     { stat 
     | dots = BoundedInt.changeTo stat.dots value
     , statType = case stat.statType of
@@ -319,4 +319,11 @@ hasSetAffinity spheres =
             |> List.filter ( \x -> x.statType == Sphere True )
             |> List.length
     in 
-        affinityCount /= 0
\ No newline at end of file
+        affinityCount /= 0
+
+setAffinityNoChecks : Bool -> Stat -> Stat
+setAffinityNoChecks affinity stat =
+    case stat.statType of
+        Sphere _ ->
+            { stat | statType = Sphere affinity }
+        _ -> stat
\ No newline at end of file
-- 
GitLab