ローカルHTMLで勤怠メールを作成する（完成？）

##はじめに

[前回](http://qiita.com/tnakagawa/items/b7df12d7ff35269c01fa)、[前々回](http://qiita.com/tnakagawa/items/69be0fe53c830782bb8d)、[前々々回](http://qiita.com/tnakagawa/items/b4a4160f13fcbcf66f83)を踏まえなんとか完成？まだまだ改良の余地はあるとは思いますが・・・

##機能

- 履歴は５件
- 履歴が保存される
- テンプレも変更可能
- インポート、エクスポート可能（localStorageが削除されても大丈夫なように）


##ソース

```html:CreateMail.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>メール作成</title>
	<style>
			* {
				font-family: MS Gothic;
				font-size: 10.5pt;
			}
			html {
				overflow-y: scroll;
			}
			button {
				padding: 0.5em 1em;
				border-radius: 5px;
			}
			input {
				width: 99%;
				height: 2em;
				border-radius: 3px;
				border: none;
			}
			select {
				width: 100%;
				height: 2em;
				border-radius: 3px;
			}
			textarea {
				border-radius: 3px;
				border: none;
			}
			td {
				border: 1px gray solid;
				text-align: center;
				border-radius: 3px;
			}
			#option {
				color: gray;
				margin: 1em 0em;
				cursor: pointer;
				-moz-user-select: none;
				-webkit-user-select: none;
				-ms-user-select: none;
				vertical-align: middle;
				display: table-cell;
				height: 1.5em;
			}
			#template {
				color: gray;
				margin: 1em 0em;
				cursor: pointer;
				-moz-user-select: none;
				-webkit-user-select: none;
				-ms-user-select: none;
			}
			#main {
				width: 800px;
				margin-right: auto;
				margin-left: auto;
			}
			#templateicon,#optionicon {
				border: solid gray 1px;
				border-radius: 3px;
			}
	</style>
	<script>
		var LOCAL_STORAGE_KEY = "createmailinfo";
		var MAX_HISTORY_LENGTH = 5;
		var info = {
			subject : "[__category__]__who__[__mmdd__]",
			body : "各位\n\nお疲れ様です。\n\n本日(__mmdd__)__hhmm__頃、__who__より電話連絡があり、\n"
				+ "__reason____category__とのことです。\n\n以上、よろしくお願いいたします。\n",
			hist : {
				toaddr : [],
				who : [],
				category : ["遅刻", "全休", "半休"],
				reason : [],
			}
		}
		var reader = null;
		function init() {
			document.getElementById("createbtn").addEventListener("click", createEmail, true);
			document.getElementById("resetbtn").addEventListener("click", reload, true);
			document.getElementById("registbtn").addEventListener("click", registTemplate, true);
			document.getElementById("option").addEventListener("click", toggleOption, true);
			document.getElementById("template").addEventListener("click", toggleOption, true);
			document.getElementById("importbtn").addEventListener("click", importHistory, true);
			document.getElementById("exportbtn").addEventListener("click", exportHistory, true);
			document.getElementById("removebtn").addEventListener("click", removeStorage, true);
			document.getElementById("importfile").addEventListener("change", importFile, true);
			document.getElementById("mmdd").value = getFormatDate("yyyy-mm-dd");
			document.getElementById("hhmm").value = getFormatDate("HH:MM");
			document.getElementById("mmdd").addEventListener("change", changeSubBody, true);
			document.getElementById("hhmm").addEventListener("change", changeSubBody, true);
			document.getElementById("who").addEventListener("input", changeSubBody, true);
			document.getElementById("category").addEventListener("input", changeSubBody, true);
			document.getElementById("reason").addEventListener("input", changeSubBody, true);
			getInfoData();
			document.getElementById("templatesubject").value = info.subject;
			document.getElementById("templatebody").value = info.body;
			setInfo();
			changeSubBody();
			reader = new FileReader();
			reader.onload = loadedFile;
		}
		function createEmail(event) {
			var toaddr = document.getElementById("toaddr").value;
			var subject = document.getElementById("subject").value;
			var body = document.getElementById("body").value;
			var message = "以下の内容で、" + toaddr + "宛のメールを作成しますがよろしいでしょうか？\n"
				+ "題名:" + subject + "\n=====本文　ここから=====\n" + body + "\n=====本文　ここまで=====";
			if (confirm(message)) {
				location.href = "mailto:" + encodeURIComponent(toaddr) + "?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
				var data = {
					toaddr : document.getElementById("toaddr").value,
					who : document.getElementById("who").value,
					category : document.getElementById("category").value,
					reason : document.getElementById("reason").value,
				};
				for (var key in data) {
					if (data[key]) {
						var array = [data[key]];
						for (var i = 0; i < info.hist[key].length; i++) {
							if (i > MAX_HISTORY_LENGTH) {
								break;
							}
							if (data[key] !== info.hist[key][i]) {
								array.push(info.hist[key][i]);
							}
						}
						info.hist[key] = array;
					}
				}
				setInfo();
				setInfoData();
			}
		}
		function registTemplate(event) {
			var subject = document.getElementById("templatesubject").value;
			var body = document.getElementById("templatebody").value;
			var message = "以下の内容で、テンプレートに登録しますがよろしいでしょうか？\n"
				+ "※※※　登録後、更新を行います。　※※※\n"
				+ "題名:" + subject
				+ "\n=====本文　ここから=====\n" + body + "\n=====本文　ここまで=====\n";
			if (confirm(message)) {
				info.body = body;
				info.subject = subject;
				setInfoData();
				reload();
			}
		}
		function changeSubBody(event) {
			var data = {
				mmdd : ("" + document.getElementById("mmdd").value).split("-").join("/").substring(5),
				hhmm : document.getElementById("hhmm").value,
				who : document.getElementById("who").value,
				category : document.getElementById("category").value,
				reason : document.getElementById("reason").value,
			};
			var subject = info.subject;
			var body = info.body;
			for (var key in data) {
				subject = subject.split("__" + key + "__").join(data[key]);
				body = body.split("__" + key + "__").join(data[key]);
			}
			document.getElementById("subject").value = subject;
			document.getElementById("body").value = body;
		}
		function setInfo() {
			for (var key in info.hist) {
				var element = document.getElementById(key + "list");
				while (element.firstChild) { element.removeChild(element.firstChild); }
				var val = info.hist[key];
				if (val.length > 0) {
					for (var i = 0; i < val.length; i++) {
						var option = document.createElement("option");
						option.setAttribute("value", val[i]);
						element.appendChild(option);
					}
				}
			}
		}
		function setInfoData() {
			localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(info));
		}
		function getInfoData(target) {
			var data =  target;
			if (!data) {
				data = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
			}
			if (data) {
				for (var key in info.hist) {
					try {
						var item = data.hist[key];
						if (item) {
							var array = [];
							info.hist[key] = array.concat(item);
						}
					} catch(e) {
						console.log(e.message);
					}
				}
				if (data.subject) {
					info.subject = "" + data.subject;
				}
				if (data.body) {
					info.body = "" + data.body;
				}
			}
		}
		function toggleOption(event) {
			try {
				var id = event.currentTarget.id;
				var disp = document.getElementById(id + "content").style.display;
				if (disp === "none") {
					document.getElementById(id + "icon").innerHTML = "－";
					document.getElementById(id + "content").style.display = "block";
				} else {
					document.getElementById(id + "icon").innerHTML = "＋";
					document.getElementById(id + "content").style.display = "none";
				}
				event.stopPropagation();
			} catch(e) {
				console.log(e.message);
			}
		}
		function removeStorage(event) {
			if (confirm("履歴を全て消しますがよろしいですか？")) {
				localStorage.removeItem(LOCAL_STORAGE_KEY);
				reload();
			}
		}
		function importHistory(event) {
			document.getElementById("importfile").click();
		}
		function importFile(event) {
			var file = event.target.files[0];
			reader.readAsText(file);
		}
		function loadedFile(event) {
			var data = JSON.parse(event.target.result);
			getInfoData(data);
			setInfoData();
			reload();
		}
		function exportHistory(event) {
			var blob = new Blob([ JSON.stringify(info, null, "\t") ], {"type": "application/json"});
			var url = URL.createObjectURL(blob);
			var a = document.getElementById("exportlink");
			a.setAttribute("download", LOCAL_STORAGE_KEY + "_" + getFormatDate("yyyymmddHHMMss") + ".json");
			a.href = url;
			a.click();
			URL.revokeObjectURL(url);
			a.href = null;
		}
		function getFormatDate(target) {
			var format = null;
			if (target) {
				var now = new Date();
				var yyyy = now.getFullYear();
				var mm = (now.getMonth() < 9) ? ("0" + (now.getMonth() + 1)) :  ("" + (now.getMonth() + 1));
				var dd = (now.getDate() < 10) ? ("0" + now.getDate()) :  ("" + now.getDate());
				var HH = (now.getHours() < 10) ? ("0" + now.getHours()) :  ("" + now.getHours());
				var MM = (now.getMinutes() < 10) ? ("0" + now.getMinutes()) :  ("" + now.getMinutes());
				var ss = (now.getSeconds() < 10) ? ("0" + now.getSeconds()) :  ("" + now.getSeconds());
				format = target;
				format = format.split("yyyy").join(yyyy);
				format = format.split("mm").join(mm);
				format = format.split("dd").join(dd);
				format = format.split("HH").join(HH);
				format = format.split("MM").join(MM);
				format = format.split("ss").join(ss);
			}
			return format;
		}
		function reload() {
			location.reload();
		}
		window.onload = init;
	</script>
</head>
<body>
	<div id="main">
		<table>
			<tr><td style="width: 80px">宛先</td><td style="width: 400px">
				<input id="toaddr" type="text" list="toaddrlist" value="" placeholder="----- 送信先メールアドレスを設定してください。例）test@sample.com -----" />
				<datalist id="toaddrlist"></datalist>
			</td></tr>
			<tr><td>月日</td><td><input id="mmdd" type="date" value="" /></td></tr>
			<tr><td>時分</td><td><input id="hhmm" type="time" value="" /></td></tr>
			<tr><td>誰？</td><td>
				<input id="who" type="text" list="wholist" placeholder="----- 対象者を設定してください。例）鈴木さん -----" />
				<datalist id="wholist"></datalist>
			</td></tr>
			<tr><td>区分</td><td>
				<input id="category" type="text" list="categorylist" placeholder="----- 休暇区分を設定してください。例）遅刻 -----" />
				<datalist id="categorylist"></datalist>
			</td></tr>
			<tr><td>理由</td><td>
				<input id="reason" type="text" placeholder="----- 理由を設定してください。例）電車遅延の為、30分ほど -----" list="reasonlist" />
				<datalist id="reasonlist"></datalist>
			</td></tr>
			<tr><td>題名</td><td><input id="subject" type="text" value="" placeholder="----- メールの題名を記入してください。 -----" /></td></tr>
			<tr><td>本文</td><td><textarea id="body" cols="80" rows="12"  placeholder="----- メールの本文を記入してください。 -----"></textarea></td></tr>
			<tr><td style="border: none; text-align: right;" colspan="2">
				<button id="resetbtn">リセット</button>
				<button id="createbtn">メール作成</button>
			</td></tr>
		</table>
		<div id="template">
			<span id="templateicon">＋</span>テンプレート
		</div>
		<div id="templatecontent" style="display: none;">
			<table>
				<tr><td style="width: 80px">宛先</td><td style="width: 400px">
					<input id="templatesubject" type="text" value="" placeholder="----- メールの題名（テンプレート）を記入してください。 -----" />
				</td></tr>
				<tr><td>本文</td><td><textarea id="templatebody" cols="80" rows="12"  placeholder="----- メールの本文（テンプレート）を記入してください。 -----"></textarea></td></tr>
				<tr><td style="border: none; text-align: right;" colspan="2">
					<button id="registbtn">登録</button>
				</td></tr>
			</table>
		</div>
		<div id="option">
			<span id="optionicon">＋</span>オプション
		</div>
		<div id="optioncontent" style="display: none;">
			<button id="importbtn">インポート</button><span id="importarea" style="display: none;"><input id="importfile" type="file"></span>
			<button id="exportbtn">エクスポート</button><a id="exportlink" download=""></a>
			<button id="removebtn">ストレージ削除</button>
		</div>
	</div>
</body>
</html>
```

##さいごに

ローカルHTMLでも案外アプリって作れるんですなぁ