From d9e90e97f811e48cefa81285911b26501f9aff87 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 08:37:30 +0900
Subject: [PATCH 01/32] :v:

---
 src/web/app/mobile/mixins.js | 23 +++++++++++++++++++++++
 src/web/app/mobile/mixins.ls | 19 -------------------
 src/web/app/mobile/script.js |  2 +-
 3 files changed, 24 insertions(+), 20 deletions(-)
 create mode 100644 src/web/app/mobile/mixins.js
 delete mode 100644 src/web/app/mobile/mixins.ls

diff --git a/src/web/app/mobile/mixins.js b/src/web/app/mobile/mixins.js
new file mode 100644
index 0000000000..6d16260070
--- /dev/null
+++ b/src/web/app/mobile/mixins.js
@@ -0,0 +1,23 @@
+const riot = require('riot');
+
+module.exports = me => {
+	if (me) {
+		require('./scripts/stream.ls')(me);
+	}
+
+	require('./scripts/ui.ls');
+
+	riot.mixin('open-post-form', {
+		openPostForm: opts => {
+			const app = document.getElementById('app');
+			app.style.display = 'none';
+			const form = riot.mount(document.body.appendChild(document.createElement('mk-post-form')), opts)[0];
+			function recover() {
+				app.style.display = 'block';
+			}
+			form
+				.on('cancel', recover)
+				.on('post', recover);
+		}
+	});
+};
diff --git a/src/web/app/mobile/mixins.ls b/src/web/app/mobile/mixins.ls
deleted file mode 100644
index 902774f91a..0000000000
--- a/src/web/app/mobile/mixins.ls
+++ /dev/null
@@ -1,19 +0,0 @@
-riot = require \riot
-
-module.exports = (me) ~>
-	if me?
-		(require './scripts/stream.ls') me
-
-	require './scripts/ui.ls'
-
-	riot.mixin \open-post-form do
-		open-post-form: (opts) ->
-			app = document.get-element-by-id \app
-			app.style.display = \none
-			form = document.body.append-child document.create-element \mk-post-form
-			form = riot.mount form, opts .0
-			form.on \cancel recover
-			form.on \post recover
-
-			function recover
-				app.style.display = \block
diff --git a/src/web/app/mobile/script.js b/src/web/app/mobile/script.js
index e22b4b6432..db4bff4501 100644
--- a/src/web/app/mobile/script.js
+++ b/src/web/app/mobile/script.js
@@ -4,7 +4,7 @@
 
 require('./tags');
 const boot = require('../boot.js');
-const mixins = require('./mixins.ls');
+const mixins = require('./mixins');
 const route = require('./router.ls');
 
 /**

From ba5896497105ffdac2c802da64d03ea0c09ec132 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 09:00:14 +0900
Subject: [PATCH 02/32] :v:

---
 src/web/app/mobile/router.js | 136 ++++++++++++++++++++++++++++++++++
 src/web/app/mobile/router.ls | 138 -----------------------------------
 src/web/app/mobile/script.js |   4 +-
 3 files changed, 138 insertions(+), 140 deletions(-)
 create mode 100644 src/web/app/mobile/router.js
 delete mode 100644 src/web/app/mobile/router.ls

diff --git a/src/web/app/mobile/router.js b/src/web/app/mobile/router.js
new file mode 100644
index 0000000000..df4871f292
--- /dev/null
+++ b/src/web/app/mobile/router.js
@@ -0,0 +1,136 @@
+/**
+ * Mobile App Router
+ */
+
+const riot = require('riot');
+const route = require('page');
+let page = null;
+
+module.exports = me => {
+	route('/',                           index);
+	route('/i/notifications',            notifications);
+	route('/i/messaging',                messaging);
+	route('/i/messaging/:username',      messaging);
+	route('/i/drive',                    drive);
+	route('/i/drive/folder/:folder',     drive);
+	route('/i/drive/file/:file',         drive);
+	route('/i/settings',                 settings);
+	route('/i/settings/signin-history',  settingsSignin);
+	route('/i/settings/api',             settingsApi);
+	route('/i/settings/twitter',         settingsTwitter);
+	route('/i/settings/authorized-apps', settingsAuthorizedApps);
+	route('/post/new',                   newPost);
+	route('/post::post',                 post);
+	route('/search::query',              search);
+	route('/:user',                      user.bind(null, 'posts'));
+	route('/:user/graphs',               user.bind(null, 'graphs'));
+	route('/:user/followers',            userFollowers);
+	route('/:user/following',            userFollowing);
+	route('/:user/:post',                post);
+	route('*',                           notFound);
+
+	function index() {
+		me ? home() : entrance();
+	}
+
+	function home() {
+		mount(document.createElement('mk-home-page'));
+	}
+
+	function entrance() {
+		mount(document.createElement('mk-entrance'));
+	}
+
+	function notifications() {
+		mount(document.createElement('mk-notifications-page'));
+	}
+
+	function messaging(ctx) {
+		if (ctx.params.username) {
+			const el = document.createElement('mk-messaging-room-page');
+			el.setAttribute('username', ctx.params.username);
+			mount(el);
+		} else {
+			mount(document.createElement('mk-messaging-page'));
+		}
+	}
+
+	function newPost() {
+		mount(document.createElement('mk-new-post-page'));
+	}
+
+	function settings() {
+		mount(document.createElement('mk-settings-page'));
+	}
+
+	function settingsSignin() {
+		mount(document.createElement('mk-signin-history-page'));
+	}
+
+	function settingsApi() {
+		mount(document.createElement('mk-api-info-page'));
+	}
+
+	function settingsTwitter() {
+		mount(document.createElement('mk-twitter-setting-page'));
+	}
+
+	function settingsAuthorizedApps() {
+		mount(document.createElement('mk-authorized-apps-page'));
+	}
+
+	function search(ctx) {
+		const el = document.createElement('mk-search-page');
+		el.setAttribute('query', ctx.params.query);
+		mount(el);
+	}
+
+	function user(page, ctx) {
+		const el = document.createElement('mk-user-page');
+		el.setAttribute('user', ctx.params.user);
+		el.setAttribute('page', page);
+		mount(el);
+	}
+
+	function userFollowing(ctx) {
+		const el = document.createElement('mk-user-following-page');
+		el.setAttribute('user', ctx.params.user);
+		mount(el);
+	}
+
+	function userFollowers(ctx) {
+		const el = document.createElement('mk-user-followers-page');
+		el.setAttribute('user', ctx.params.user);
+		mount(el);
+	}
+
+	function post(ctx) {
+		const el = document.createElement('mk-post-page');
+		el.setAttribute('post', ctx.params.post);
+		mount(el);
+	}
+
+	function drive(ctx) {
+		const el = document.createElement('mk-drive-page');
+		if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder);
+		if (ctx.params.file) el.setAttribute('file', ctx.params.file);
+		mount(el);
+	}
+
+	function notFound() {
+		mount(document.createElement('mk-not-found'));
+	}
+
+	riot.mixin('page', {
+		page: route
+	});
+
+	// EXEC
+	route();
+};
+
+function mount(content) {
+	if (page) page.unmount();
+	const body = document.getElementById('app');
+	page = riot.mount(body.appendChild(content))[0];
+}
diff --git a/src/web/app/mobile/router.ls b/src/web/app/mobile/router.ls
deleted file mode 100644
index 3c0cb42236..0000000000
--- a/src/web/app/mobile/router.ls
+++ /dev/null
@@ -1,138 +0,0 @@
-# Router
-#================================
-
-riot = require \riot
-route = require \page
-page = null
-
-module.exports = (me) ~>
-
-	# Routing
-	#--------------------------------
-
-	route \/ index
-	route \/i/notifications notifications
-	route \/i/messaging messaging
-	route \/i/messaging/:username messaging
-	route \/i/drive drive
-	route \/i/drive/folder/:folder drive
-	route \/i/drive/file/:file drive
-	route \/i/settings settings
-	route \/i/settings/signin-history settings-signin
-	route \/i/settings/api settings-api
-	route \/i/settings/twitter settings-twitter
-	route \/i/settings/authorized-apps settings-authorized-apps
-	route \/post/new new-post
-	route \/post::post post
-	route \/search::query search
-	route \/:user user.bind null \posts
-	route \/:user/graphs user.bind null \graphs
-	route \/:user/followers user-followers
-	route \/:user/following user-following
-	route \/:user/:post post
-	route \* not-found
-
-	# Handlers
-	#--------------------------------
-
-	# /
-	function index
-		if me? then home! else entrance!
-
-	# ホーム
-	function home
-		mount document.create-element \mk-home-page
-
-	# 玄関
-	function entrance
-		mount document.create-element \mk-entrance
-
-	# 通知
-	function notifications
-		mount document.create-element \mk-notifications-page
-
-	# メッセージ
-	function messaging ctx
-		if ctx.params.username
-			p = document.create-element \mk-messaging-room-page
-			p.set-attribute \username ctx.params.username
-			mount p
-		else
-			mount document.create-element \mk-messaging-page
-
-	# 新規投稿
-	function new-post
-		mount document.create-element \mk-new-post-page
-
-	# 設定
-	function settings
-		mount document.create-element \mk-settings-page
-	function settings-signin
-		mount document.create-element \mk-signin-history-page
-	function settings-api
-		mount document.create-element \mk-api-info-page
-	function settings-twitter
-		mount document.create-element \mk-twitter-setting-page
-	function settings-authorized-apps
-		mount document.create-element \mk-authorized-apps-page
-
-	# 検索
-	function search ctx
-		document.create-element \mk-search-page
-			..set-attribute \query ctx.params.query
-			.. |> mount
-
-	# ユーザー
-	function user page, ctx
-		document.create-element \mk-user-page
-			..set-attribute \user ctx.params.user
-			..set-attribute \page page
-			.. |> mount
-
-	# フォロー一覧
-	function user-following ctx
-		document.create-element \mk-user-following-page
-			..set-attribute \user ctx.params.user
-			.. |> mount
-
-	# フォロワー一覧
-	function user-followers ctx
-		document.create-element \mk-user-followers-page
-			..set-attribute \user ctx.params.user
-			.. |> mount
-
-	# 投稿詳細ページ
-	function post ctx
-		document.create-element \mk-post-page
-			..set-attribute \post ctx.params.post
-			.. |> mount
-
-	# ドライブ
-	function drive ctx
-		p = document.create-element \mk-drive-page
-		if ctx.params.folder then p.set-attribute \folder ctx.params.folder
-		if ctx.params.file then p.set-attribute \file ctx.params.file
-		mount p
-
-	# not found
-	function not-found
-		mount document.create-element \mk-not-found
-
-	# Register mixin
-	#--------------------------------
-
-	riot.mixin \page do
-		page: route
-
-	# Exec
-	#--------------------------------
-
-	route!
-
-# Mount
-#================================
-
-function mount content
-	if page? then page.unmount!
-	body = document.get-element-by-id \app
-	page := riot.mount body.append-child content .0
diff --git a/src/web/app/mobile/script.js b/src/web/app/mobile/script.js
index db4bff4501..6f732da9cc 100644
--- a/src/web/app/mobile/script.js
+++ b/src/web/app/mobile/script.js
@@ -3,9 +3,9 @@
  */
 
 require('./tags');
-const boot = require('../boot.js');
+const boot = require('../boot');
 const mixins = require('./mixins');
-const route = require('./router.ls');
+const route = require('./router');
 
 /**
  * Boot

From 7b9f11407f1a0f0267b6601b40fdf4dcef685a7c Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 09:07:24 +0900
Subject: [PATCH 03/32] :v:

---
 src/web/app/desktop/router.js | 74 +++++++++++++++++++++++++++++++++
 src/web/app/desktop/router.ls | 77 -----------------------------------
 src/web/app/desktop/script.js |  4 +-
 3 files changed, 76 insertions(+), 79 deletions(-)
 create mode 100644 src/web/app/desktop/router.js
 delete mode 100644 src/web/app/desktop/router.ls

diff --git a/src/web/app/desktop/router.js b/src/web/app/desktop/router.js
new file mode 100644
index 0000000000..f4d2ec347a
--- /dev/null
+++ b/src/web/app/desktop/router.js
@@ -0,0 +1,74 @@
+/**
+ * Desktop App Router
+ */
+
+const riot = require('riot');
+const route = require('page');
+let page = null;
+
+module.exports = me => {
+	route('/',              index);
+	route('/i>mentions',    mentions);
+	route('/post::post',    post);
+	route('/search::query', search);
+	route('/:user',         user.bind(null, 'home'));
+	route('/:user/graphs',  user.bind(null, 'graphs'));
+	route('/:user/:post',   post);
+	route('*',              notFound);
+
+	function index() {
+		me ? home() : entrance();
+	}
+
+	function home() {
+		mount(document.createElement('mk-home-page'));
+	}
+
+	function entrance() {
+		mount(document.createElement('mk-entrance'));
+		document.documentElement.setAttribute('data-page', 'entrance');
+	}
+
+	function mentions() {
+		const el = document.createElement('mk-home-page');
+		el.setAttribute('mode', 'mentions');
+		mount(el);
+	}
+
+	function search(ctx) {
+		const el = document.createElement('mk-search-page');
+		el.setAttribute('query', ctx.params.query);
+		mount(el);
+	}
+
+	function user(page, ctx) {
+		const el = document.createElement('mk-user-page');
+		el.setAttribute('user', ctx.params.user);
+		el.setAttribute('page', page);
+		mount(el);
+	}
+
+	function post(ctx) {
+		const el = document.createElement('mk-post-page');
+		el.setAttribute('post', ctx.params.post);
+		mount(el);
+	}
+
+	function notFound() {
+		mount(document.createElement('mk-not-found'));
+	}
+
+	riot.mixin('page', {
+		page: route
+	});
+
+	// EXEC
+	route();
+};
+
+function mount(content) {
+	document.documentElement.removeAttribute('data-page');
+	if (page) page.unmount();
+	const body = document.getElementById('app');
+	page = riot.mount(body.appendChild(content))[0];
+}
diff --git a/src/web/app/desktop/router.ls b/src/web/app/desktop/router.ls
deleted file mode 100644
index 02a7e11816..0000000000
--- a/src/web/app/desktop/router.ls
+++ /dev/null
@@ -1,77 +0,0 @@
-# Router
-#================================
-
-riot = require \riot
-route = require \page
-page = null
-
-module.exports = (me) ~>
-
-	# Routing
-	#--------------------------------
-
-	route \/ index
-	route \/i>mentions mentions
-	route \/post::post post
-	route \/search::query search
-	route \/:user user.bind null \home
-	route \/:user/graphs user.bind null \graphs
-	route \/:user/:post post
-	route \* not-found
-
-	# Handlers
-	#--------------------------------
-
-	function index
-		if me? then home! else entrance!
-
-	function home
-		mount document.create-element \mk-home-page
-
-	function entrance
-		mount document.create-element \mk-entrance
-		document.document-element.set-attribute \data-page \entrance
-
-	function mentions
-		document.create-element \mk-home-page
-			..set-attribute \mode \mentions
-			.. |> mount
-
-	function search ctx
-		document.create-element \mk-search-page
-			..set-attribute \query ctx.params.query
-			.. |> mount
-
-	function user page, ctx
-		document.create-element \mk-user-page
-			..set-attribute \user ctx.params.user
-			..set-attribute \page page
-			.. |> mount
-
-	function post ctx
-		document.create-element \mk-post-page
-			..set-attribute \post ctx.params.post
-			.. |> mount
-
-	function not-found
-		mount document.create-element \mk-not-found
-
-	# Register mixin
-	#--------------------------------
-
-	riot.mixin \page do
-		page: route
-
-	# Exec
-	#--------------------------------
-
-	route!
-
-# Mount
-#================================
-
-function mount content
-	document.document-element.remove-attribute \data-page
-	if page? then page.unmount!
-	body = document.get-element-by-id \app
-	page := riot.mount body.append-child content .0
diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js
index 845e6c4951..b63f712f62 100644
--- a/src/web/app/desktop/script.js
+++ b/src/web/app/desktop/script.js
@@ -5,9 +5,9 @@
 require('chart.js');
 require('./tags');
 const riot = require('riot');
-const boot = require('../boot.js');
+const boot = require('../boot');
 const mixins = require('./mixins.ls');
-const route = require('./router.ls');
+const route = require('./router');
 const fuckAdBlock = require('./scripts/fuck-ad-block.ls');
 
 /**

From 43f9fcb31bf930f7205bd713fe0bfc066832e934 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 09:17:10 +0900
Subject: [PATCH 04/32] Remove unused file

---
 src/web/app/common/scripts/log.ls | 18 ------------------
 1 file changed, 18 deletions(-)
 delete mode 100644 src/web/app/common/scripts/log.ls

diff --git a/src/web/app/common/scripts/log.ls b/src/web/app/common/scripts/log.ls
deleted file mode 100644
index 6e1e3735d8..0000000000
--- a/src/web/app/common/scripts/log.ls
+++ /dev/null
@@ -1,18 +0,0 @@
-riot = require \riot
-
-logs = []
-
-ev = riot.observable!
-
-function log(msg)
-	logs.push do
-		date: new Date!
-		message: msg
-	ev.trigger \log
-
-riot.mixin \log do
-	logs: logs
-	log: log
-	log-event: ev
-
-module.exports = log

From 6f6723e1c2acd902908a4e0fe22cc8915c21017b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 10:52:51 +0900
Subject: [PATCH 05/32] :v:

---
 src/web/app/common/scripts/date-stringify.js | 13 +++++++++++++
 src/web/app/common/scripts/date-stringify.ls | 14 --------------
 2 files changed, 13 insertions(+), 14 deletions(-)
 create mode 100644 src/web/app/common/scripts/date-stringify.js
 delete mode 100644 src/web/app/common/scripts/date-stringify.ls

diff --git a/src/web/app/common/scripts/date-stringify.js b/src/web/app/common/scripts/date-stringify.js
new file mode 100644
index 0000000000..48e19704d5
--- /dev/null
+++ b/src/web/app/common/scripts/date-stringify.js
@@ -0,0 +1,13 @@
+module.exports = date => {
+	if (typeof date == 'string') date = new Date(date);
+	return (
+		date.getFullYear()  + '年' + 
+		date.getMonth() + 1 + '月' + 
+		date.getDate()      + '日' +
+		' ' +
+		date.getHours()     + '時' + 
+		date.getMinutes()   + '分' + 
+		' ' +
+		`(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})`
+	);
+};
diff --git a/src/web/app/common/scripts/date-stringify.ls b/src/web/app/common/scripts/date-stringify.ls
deleted file mode 100644
index 7e85192ce7..0000000000
--- a/src/web/app/common/scripts/date-stringify.ls
+++ /dev/null
@@ -1,14 +0,0 @@
-module.exports = (date) ->
-	if typeof date == \string then date = new Date date
-
-	text =
-		date.get-full-year! + \年 +
-		date.get-month! + 1 + \月 +
-		date.get-date!      + \日 +
-		' ' +
-		date.get-hours!     + \時 +
-		date.get-minutes!   + \分 +
-		' ' +
-		"(#{[\日 \月 \火 \水 \木 \金 \土][date.get-day!]})"
-
-	return text

From 6836662bffc501911902b4168e08716a90595ae8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:07:34 +0900
Subject: [PATCH 06/32] :v:

---
 src/web/app/common/mixins.ls                  |  2 +-
 .../app/common/scripts/get-post-summary.js    | 37 +++++++++++++++++++
 .../app/common/scripts/get-post-summary.ls    | 30 ---------------
 3 files changed, 38 insertions(+), 31 deletions(-)
 create mode 100644 src/web/app/common/scripts/get-post-summary.js
 delete mode 100644 src/web/app/common/scripts/get-post-summary.ls

diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
index 812bcae102..ca27e03579 100644
--- a/src/web/app/common/mixins.ls
+++ b/src/web/app/common/mixins.ls
@@ -21,7 +21,7 @@ module.exports = (me) ~>
 		is-promise: require './scripts/is-promise.ls'
 
 	riot.mixin \get-post-summary do
-		get-post-summary: require './scripts/get-post-summary.ls'
+		get-post-summary: require './scripts/get-post-summary.js'
 
 	riot.mixin \date-stringify do
 		date-stringify: require './scripts/date-stringify.ls'
diff --git a/src/web/app/common/scripts/get-post-summary.js b/src/web/app/common/scripts/get-post-summary.js
new file mode 100644
index 0000000000..8e17d54e81
--- /dev/null
+++ b/src/web/app/common/scripts/get-post-summary.js
@@ -0,0 +1,37 @@
+const getPostSummary = post => {
+	let = post.text ? post.text : '';
+
+	// メディアが添付されているとき
+	if (post.media) {
+		summary += ` (${post.media.length}つのメディア)`;
+	}
+
+	// 投票が添付されているとき
+	if (post.poll) {
+		summary += ' (投票)';
+	}
+
+	// 返信のとき
+	if (post.reply_to_id) {
+		if (post.reply_to) {
+			replySummary = getPostSummary(post.reply_to);
+			summary += ` RE: ${replySummary}`;
+		} else {
+			summary += ' RE: ...';
+		}
+	}
+
+	// Repostのとき
+	if (post.repost_id) {
+		if (post.repost) {
+			repostSummary = getPostSummary(post.repost);
+			summary += ` RP: ${repostSummary}`;
+		} else {
+			summary += ' RP: ...';
+		}
+	}
+
+	return summary.trim();
+};
+
+module.exports = getPostSummary;
diff --git a/src/web/app/common/scripts/get-post-summary.ls b/src/web/app/common/scripts/get-post-summary.ls
deleted file mode 100644
index 67178bc324..0000000000
--- a/src/web/app/common/scripts/get-post-summary.ls
+++ /dev/null
@@ -1,30 +0,0 @@
-get-post-summary = (post) ~>
-	summary = if post.text? then post.text else ''
-
-	# メディアが添付されているとき
-	if post.media?
-		summary += " (#{post.media.length}つのメディア)"
-
-	# 投票が添付されているとき
-	if post.poll?
-		summary += " (投票)"
-
-	# 返信のとき
-	if post.reply_to_id?
-		if post.reply_to?
-			reply-summary = get-post-summary post.reply_to
-			summary += " RE: #{reply-summary}"
-		else
-			summary += " RE: ..."
-
-	# Repostのとき
-	if post.repost_id?
-		if post.repost?
-			repost-summary = get-post-summary post.repost
-			summary += " RP: #{repost-summary}"
-		else
-			summary += " RP: ..."
-
-	return summary.trim!
-
-module.exports = get-post-summary

From d168ac6106d282ad21e637ba0e5cf986ef109eb3 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:13:43 +0900
Subject: [PATCH 07/32] :v:

---
 src/web/app/common/mixins.ls          | 2 +-
 src/web/app/common/scripts/signout.js | 5 +++++
 src/web/app/common/scripts/signout.ls | 4 ----
 3 files changed, 6 insertions(+), 5 deletions(-)
 create mode 100644 src/web/app/common/scripts/signout.js
 delete mode 100644 src/web/app/common/scripts/signout.ls

diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
index ca27e03579..3999851a98 100644
--- a/src/web/app/common/mixins.ls
+++ b/src/web/app/common/mixins.ls
@@ -12,7 +12,7 @@ module.exports = (me) ~>
 		Cropper: require \cropperjs
 
 	riot.mixin \signout do
-		signout: require './scripts/signout.ls'
+		signout: require './scripts/signout.js'
 
 	riot.mixin \messaging-stream do
 		MessagingStreamConnection: require './scripts/messaging-stream.ls'
diff --git a/src/web/app/common/scripts/signout.js b/src/web/app/common/scripts/signout.js
new file mode 100644
index 0000000000..cd752423da
--- /dev/null
+++ b/src/web/app/common/scripts/signout.js
@@ -0,0 +1,5 @@
+module.exports = () => {
+	localStorage.removeItem('me');
+	document.cookie = `i=; domain=.${CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
+	location.href = '/';
+};
diff --git a/src/web/app/common/scripts/signout.ls b/src/web/app/common/scripts/signout.ls
deleted file mode 100644
index a647922678..0000000000
--- a/src/web/app/common/scripts/signout.ls
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = ->
-	local-storage.remove-item \me
-	document.cookie = "i=; domain=.#{CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
-	location.href = \/

From 4770e1fab89b35671f9fd1eb3080efca717e27ac Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:18:59 +0900
Subject: [PATCH 08/32] :v:

---
 src/web/app/common/scripts/stream.js  | 39 +++++++++++++++++++++++++++
 src/web/app/common/scripts/stream.ls  | 39 ---------------------------
 src/web/app/desktop/scripts/stream.ls |  4 +--
 src/web/app/mobile/scripts/stream.ls  |  2 +-
 4 files changed, 42 insertions(+), 42 deletions(-)
 create mode 100644 src/web/app/common/scripts/stream.js
 delete mode 100644 src/web/app/common/scripts/stream.ls

diff --git a/src/web/app/common/scripts/stream.js b/src/web/app/common/scripts/stream.js
new file mode 100644
index 0000000000..b31e570444
--- /dev/null
+++ b/src/web/app/common/scripts/stream.js
@@ -0,0 +1,39 @@
+const ReconnectingWebSocket = require('reconnecting-websocket');
+const riot = require('riot');
+
+module.exports = me => {
+	let state = 'initializing';
+	const stateEv = riot.observable();
+	const event = riot.observable();
+	const host = CONFIG.api.url.replace('http', 'ws');
+	const socket = new ReconnectingWebSocket(`${host}?i=${me.token}`);
+
+	socket.onopen = () => {
+		state = 'connected';
+		stateEv.trigger('connected');
+	};
+
+	socket.onclose = () => {
+		state = 'reconnecting';
+		stateEv.trigger('closed');
+	};
+
+	socket.onmessage = message => {
+		try {
+			const message = JSON.parse(message.data);
+			if (message.type) {
+				event.trigger(message.type, message.body);
+			}
+		} catch (e) {
+			// noop
+		}
+	};
+
+	event.on('i_updated', me.update);
+
+	return {
+		stateEv: stateEv,
+		getState: () => state,
+		event: event
+	};
+};
diff --git a/src/web/app/common/scripts/stream.ls b/src/web/app/common/scripts/stream.ls
deleted file mode 100644
index c2c061603e..0000000000
--- a/src/web/app/common/scripts/stream.ls
+++ /dev/null
@@ -1,39 +0,0 @@
-# Stream
-#================================
-
-ReconnectingWebSocket = require \reconnecting-websocket
-riot = require \riot
-
-module.exports = (me) ~>
-	state = \initializing
-	state-ev = riot.observable!
-	event = riot.observable!
-
-	host = CONFIG.api.url.replace \http \ws
-	socket = new ReconnectingWebSocket "#{host}?i=#{me.token}"
-
-	socket.onopen = ~>
-		state := \connected
-		state-ev.trigger \connected
-
-	socket.onclose = ~>
-		state := \reconnecting
-		state-ev.trigger \closed
-
-	socket.onmessage = (message) ~>
-		try
-			message = JSON.parse message.data
-			if message.type?
-				event.trigger message.type, message.body
-		catch
-			# ignore
-
-	get-state = ~> state
-
-	event.on \i_updated me.update
-
-	{
-		state-ev
-		get-state
-		event
-	}
diff --git a/src/web/app/desktop/scripts/stream.ls b/src/web/app/desktop/scripts/stream.ls
index f84d6097a7..88dac16cb6 100644
--- a/src/web/app/desktop/scripts/stream.ls
+++ b/src/web/app/desktop/scripts/stream.ls
@@ -1,8 +1,8 @@
 # Stream
 #================================
 
-stream = require '../../common/scripts/stream.ls'
-get-post-summary = require '../../common/scripts/get-post-summary.ls'
+stream = require '../../common/scripts/stream'
+get-post-summary = require '../../common/scripts/get-post-summary'
 riot = require \riot
 
 module.exports = (me) ~>
diff --git a/src/web/app/mobile/scripts/stream.ls b/src/web/app/mobile/scripts/stream.ls
index b7810b49ae..28418d7788 100644
--- a/src/web/app/mobile/scripts/stream.ls
+++ b/src/web/app/mobile/scripts/stream.ls
@@ -1,7 +1,7 @@
 # Stream
 #================================
 
-stream = require '../../common/scripts/stream.ls'
+stream = require '../../common/scripts/stream'
 riot = require \riot
 
 module.exports = (me) ~>

From 2db313986aa494da3d6ba3ac2773002ee1b2b8fe Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:23:23 +0900
Subject: [PATCH 09/32] :v:

---
 src/web/app/desktop/mixins.ls         |  2 +-
 src/web/app/desktop/scripts/stream.js | 45 +++++++++++++++++++++++++++
 src/web/app/desktop/scripts/stream.ls | 38 ----------------------
 src/web/app/mobile/mixins.js          |  2 +-
 src/web/app/mobile/scripts/stream.js  | 11 +++++++
 src/web/app/mobile/scripts/stream.ls  | 13 --------
 6 files changed, 58 insertions(+), 53 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/stream.js
 delete mode 100644 src/web/app/desktop/scripts/stream.ls
 create mode 100644 src/web/app/mobile/scripts/stream.js
 delete mode 100644 src/web/app/mobile/scripts/stream.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index b099b45165..054acbe745 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -2,7 +2,7 @@ riot = require \riot
 
 module.exports = (me) ~>
 	if me?
-		(require './scripts/stream.ls') me
+		(require './scripts/stream') me
 
 	require './scripts/user-preview.ls'
 
diff --git a/src/web/app/desktop/scripts/stream.js b/src/web/app/desktop/scripts/stream.js
new file mode 100644
index 0000000000..ea1548ecfd
--- /dev/null
+++ b/src/web/app/desktop/scripts/stream.js
@@ -0,0 +1,45 @@
+const stream = require('../../common/scripts/stream');
+const getPostSummary = require('../../common/scripts/get-post-summary');
+const riot = require('riot');
+
+module.exports = me => {
+	const s = stream(me);
+
+	s.event.on('drive_file_created', file => {
+		const n = new Notification('ファイルがアップロードされました', {
+			body: file.name,
+			icon: file.url + '?thumbnail&size=64'
+		});
+		setTimeout(n.close.bind(n), 5000);
+	});
+
+	s.event.on('mention', post => {
+		const n = new Notification(post.user.name + "さんから:", {
+			body: getPostSummary(post),
+			icon: post.user.avatar_url + '?thumbnail&size=64'
+		});
+		setTimeout(n.close.bind(n), 6000);
+	});
+
+	s.event.on('reply', post => {
+		const n = new Notification(post.user.name + "さんから返信:", {
+			body: getPostSummary(post),
+			icon: post.user.avatar_url + '?thumbnail&size=64'
+		});
+		setTimeout(n.close.bind(n), 6000);
+	});
+
+	s.event.on('quote', post => {
+		const n = new Notification(post.user.name + "さんが引用:", {
+			body: getPostSummary(post),
+			icon: post.user.avatar_url + '?thumbnail&size=64'
+		});
+		setTimeout(n.close.bind(n), 6000);
+	});
+
+	riot.mixin('stream', {
+		stream: s.event,
+		getStreamState: s.getState,
+		streamStateEv: s.stateEv
+	});
+};
diff --git a/src/web/app/desktop/scripts/stream.ls b/src/web/app/desktop/scripts/stream.ls
deleted file mode 100644
index 88dac16cb6..0000000000
--- a/src/web/app/desktop/scripts/stream.ls
+++ /dev/null
@@ -1,38 +0,0 @@
-# Stream
-#================================
-
-stream = require '../../common/scripts/stream'
-get-post-summary = require '../../common/scripts/get-post-summary'
-riot = require \riot
-
-module.exports = (me) ~>
-	s = stream me
-
-	s.event.on \drive_file_created (file) ~>
-		n = new Notification 'ファイルがアップロードされました' do
-			body: file.name
-			icon: file.url + '?thumbnail&size=64'
-		set-timeout (n.close.bind n), 5000ms
-
-	s.event.on \mention (post) ~>
-		n = new Notification "#{post.user.name}さんから:" do
-			body: get-post-summary post
-			icon: post.user.avatar_url + '?thumbnail&size=64'
-		set-timeout (n.close.bind n), 6000ms
-
-	s.event.on \reply (post) ~>
-		n = new Notification "#{post.user.name}さんから返信:" do
-			body: get-post-summary post
-			icon: post.user.avatar_url + '?thumbnail&size=64'
-		set-timeout (n.close.bind n), 6000ms
-
-	s.event.on \quote (post) ~>
-		n = new Notification "#{post.user.name}さんが引用:" do
-			body: get-post-summary post
-			icon: post.user.avatar_url + '?thumbnail&size=64'
-		set-timeout (n.close.bind n), 6000ms
-
-	riot.mixin \stream do
-		stream: s.event
-		get-stream-state: s.get-state
-		stream-state-ev: s.state-ev
diff --git a/src/web/app/mobile/mixins.js b/src/web/app/mobile/mixins.js
index 6d16260070..cd344d32e0 100644
--- a/src/web/app/mobile/mixins.js
+++ b/src/web/app/mobile/mixins.js
@@ -2,7 +2,7 @@ const riot = require('riot');
 
 module.exports = me => {
 	if (me) {
-		require('./scripts/stream.ls')(me);
+		require('./scripts/stream')(me);
 	}
 
 	require('./scripts/ui.ls');
diff --git a/src/web/app/mobile/scripts/stream.js b/src/web/app/mobile/scripts/stream.js
new file mode 100644
index 0000000000..e12788f60b
--- /dev/null
+++ b/src/web/app/mobile/scripts/stream.js
@@ -0,0 +1,11 @@
+const stream = require('../../common/scripts/stream');
+const riot = require('riot');
+
+module.exports = me => {
+	const s = stream(me);
+	riot.mixin('stream', {
+		stream: s.event,
+		getStreamState: s.getState,
+		streamStateEv: s.stateEv
+	});
+};
diff --git a/src/web/app/mobile/scripts/stream.ls b/src/web/app/mobile/scripts/stream.ls
deleted file mode 100644
index 28418d7788..0000000000
--- a/src/web/app/mobile/scripts/stream.ls
+++ /dev/null
@@ -1,13 +0,0 @@
-# Stream
-#================================
-
-stream = require '../../common/scripts/stream'
-riot = require \riot
-
-module.exports = (me) ~>
-	s = stream me
-
-	riot.mixin \stream do
-		stream: s.event
-		get-stream-state: s.get-state
-		stream-state-ev: s.state-ev

From fde6ed60736d688c63bdbc8de2875e2e687f9f16 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:24:39 +0900
Subject: [PATCH 10/32] :v:

---
 src/web/app/mobile/mixins.js     | 2 +-
 src/web/app/mobile/scripts/ui.js | 7 +++++++
 src/web/app/mobile/scripts/ui.ls | 6 ------
 3 files changed, 8 insertions(+), 7 deletions(-)
 create mode 100644 src/web/app/mobile/scripts/ui.js
 delete mode 100644 src/web/app/mobile/scripts/ui.ls

diff --git a/src/web/app/mobile/mixins.js b/src/web/app/mobile/mixins.js
index cd344d32e0..be0e0d155e 100644
--- a/src/web/app/mobile/mixins.js
+++ b/src/web/app/mobile/mixins.js
@@ -5,7 +5,7 @@ module.exports = me => {
 		require('./scripts/stream')(me);
 	}
 
-	require('./scripts/ui.ls');
+	require('./scripts/ui');
 
 	riot.mixin('open-post-form', {
 		openPostForm: opts => {
diff --git a/src/web/app/mobile/scripts/ui.js b/src/web/app/mobile/scripts/ui.js
new file mode 100644
index 0000000000..51ab6acd2d
--- /dev/null
+++ b/src/web/app/mobile/scripts/ui.js
@@ -0,0 +1,7 @@
+const riot = require('riot');
+
+const ui = riot.observable();
+
+riot.mixin('ui', {
+	ui: ui
+});
diff --git a/src/web/app/mobile/scripts/ui.ls b/src/web/app/mobile/scripts/ui.ls
deleted file mode 100644
index aa94a8b052..0000000000
--- a/src/web/app/mobile/scripts/ui.ls
+++ /dev/null
@@ -1,6 +0,0 @@
-riot = require \riot
-
-ui = riot.observable!
-
-riot.mixin \ui do
-	ui: ui

From cedeba580ee3846230161408f603f72fe169dadf Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 13:26:27 +0900
Subject: [PATCH 11/32] Improve readability

---
 src/web/app/mobile/mixins.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/web/app/mobile/mixins.js b/src/web/app/mobile/mixins.js
index be0e0d155e..98601a1072 100644
--- a/src/web/app/mobile/mixins.js
+++ b/src/web/app/mobile/mixins.js
@@ -11,10 +11,12 @@ module.exports = me => {
 		openPostForm: opts => {
 			const app = document.getElementById('app');
 			app.style.display = 'none';
-			const form = riot.mount(document.body.appendChild(document.createElement('mk-post-form')), opts)[0];
+
 			function recover() {
 				app.style.display = 'block';
 			}
+
+			const form = riot.mount(document.body.appendChild(document.createElement('mk-post-form')), opts)[0];
 			form
 				.on('cancel', recover)
 				.on('post', recover);

From 33cf76221157522ee62503e7cc7e70abf3d6500b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:37:22 +0900
Subject: [PATCH 12/32] :v:

---
 src/web/app/boot.js                           |  4 +-
 .../scripts/generate-default-userdata.js      | 47 +++++++++++++++++++
 .../scripts/generate-default-userdata.ls      | 28 -----------
 3 files changed, 49 insertions(+), 30 deletions(-)
 create mode 100644 src/web/app/common/scripts/generate-default-userdata.js
 delete mode 100644 src/web/app/common/scripts/generate-default-userdata.ls

diff --git a/src/web/app/boot.js b/src/web/app/boot.js
index 32950071a2..a77bdd6dae 100644
--- a/src/web/app/boot.js
+++ b/src/web/app/boot.js
@@ -5,8 +5,8 @@
 const riot = require('riot');
 require('velocity-animate');
 const api = require('./common/scripts/api');
-const signout = require('./common/scripts/signout.ls');
-const generateDefaultUserdata = require('./common/scripts/generate-default-userdata.ls');
+const signout = require('./common/scripts/signout');
+const generateDefaultUserdata = require('./common/scripts/generate-default-userdata');
 const mixins = require('./common/mixins.ls');
 const checkForUpdate = require('./common/scripts/check-for-update.ls');
 require('./common/tags');
diff --git a/src/web/app/common/scripts/generate-default-userdata.js b/src/web/app/common/scripts/generate-default-userdata.js
new file mode 100644
index 0000000000..f6c8c2fe58
--- /dev/null
+++ b/src/web/app/common/scripts/generate-default-userdata.js
@@ -0,0 +1,47 @@
+const uuid = require('./uuid.js');
+
+const home = {
+	left: [
+		'profile',
+		'calendar',
+		'rss-reader',
+		'photo-stream'
+	],
+	right: [
+		'broadcast',
+		'notifications',
+		'user-recommendation',
+		'donation',
+		'nav',
+		'tips'
+	]
+};
+
+module.exports = () => {
+	const homeData = [];
+
+	home.left.forEach(widget => {
+		homeData.push({
+			name: widget,
+			id: uuid(),
+			place: 'left'
+		});
+	});
+
+	home.right.forEach(widget => {
+		homeData.push({
+			name: widget,
+			id: uuid(),
+			place: 'right'
+		});
+	});
+
+	const data = {
+		cache: true,
+		debug: false,
+		nya: true,
+		home: homeData
+	};
+
+	return data;
+};
diff --git a/src/web/app/common/scripts/generate-default-userdata.ls b/src/web/app/common/scripts/generate-default-userdata.ls
deleted file mode 100644
index c13d221bb9..0000000000
--- a/src/web/app/common/scripts/generate-default-userdata.ls
+++ /dev/null
@@ -1,28 +0,0 @@
-uuid = require './uuid.js'
-
-home =
-	left: [ \profile \calendar \rss-reader \photo-stream ]
-	right: [ \broadcast \notifications \user-recommendation \donation \nav \tips ]
-
-module.exports = ~>
-	home-data = []
-
-	home.left.for-each (widget) ~>
-		home-data.push do
-			name: widget
-			id: uuid!
-			place: \left
-
-	home.right.for-each (widget) ~>
-		home-data.push do
-			name: widget
-			id: uuid!
-			place: \right
-
-	data =
-		cache: true
-		debug: false
-		nya: true
-		home: home-data
-
-	return data

From 168b5bb723e9dd136ef33e958640778720a7827d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:42:41 +0900
Subject: [PATCH 13/32] :v:

---
 src/web/app/common/mixins.ls    |  2 +-
 src/web/app/common/scripts/i.js | 20 ++++++++++++++++++++
 src/web/app/common/scripts/i.ls | 13 -------------
 3 files changed, 21 insertions(+), 14 deletions(-)
 create mode 100644 src/web/app/common/scripts/i.js
 delete mode 100644 src/web/app/common/scripts/i.ls

diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
index 3999851a98..947fad553d 100644
--- a/src/web/app/common/mixins.ls
+++ b/src/web/app/common/mixins.ls
@@ -3,7 +3,7 @@ riot = require \riot
 module.exports = (me) ~>
 	i = if me? then me.token else null
 
-	(require './scripts/i.ls') me
+	(require './scripts/i') me
 
 	riot.mixin \api do
 		api: (require './scripts/api').bind null i
diff --git a/src/web/app/common/scripts/i.js b/src/web/app/common/scripts/i.js
new file mode 100644
index 0000000000..66ce37d506
--- /dev/null
+++ b/src/web/app/common/scripts/i.js
@@ -0,0 +1,20 @@
+const riot = require('riot');
+
+module.exports = me => {
+	riot.mixin('i', {
+		init: () => {
+			this.I = me;
+			this.SIGNIN = me != null;
+
+			if (this.SIGNIN) {
+				this.on('mount', () => {
+					me.on('updated', this.update);
+				});
+				this.on('unmount', () => {
+					me.off('updated', this.update);
+				});
+			}
+		},
+		me: me
+	});
+};
diff --git a/src/web/app/common/scripts/i.ls b/src/web/app/common/scripts/i.ls
deleted file mode 100644
index 9b5fa87441..0000000000
--- a/src/web/app/common/scripts/i.ls
+++ /dev/null
@@ -1,13 +0,0 @@
-riot = require \riot
-
-module.exports = (me) ->
-	riot.mixin \i do
-		init: ->
-			@I = me
-			@SIGNIN = me?
-
-			if @SIGNIN
-				@on \mount   ~> me.on  \updated @update
-				@on \unmount ~> me.off \updated @update
-
-		me: me
\ No newline at end of file

From b4046da451ea0be70a5ded1a277ea3784c838050 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:51:11 +0900
Subject: [PATCH 14/32] :v:

---
 src/web/app/common/mixins.ls                  |  2 +-
 .../app/common/scripts/messaging-stream.js    | 36 +++++++++++++++++++
 .../app/common/scripts/messaging-stream.ls    | 34 ------------------
 3 files changed, 37 insertions(+), 35 deletions(-)
 create mode 100644 src/web/app/common/scripts/messaging-stream.js
 delete mode 100644 src/web/app/common/scripts/messaging-stream.ls

diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
index 947fad553d..1194793994 100644
--- a/src/web/app/common/mixins.ls
+++ b/src/web/app/common/mixins.ls
@@ -15,7 +15,7 @@ module.exports = (me) ~>
 		signout: require './scripts/signout.js'
 
 	riot.mixin \messaging-stream do
-		MessagingStreamConnection: require './scripts/messaging-stream.ls'
+		MessagingStreamConnection: require './scripts/messaging-stream'
 
 	riot.mixin \is-promise do
 		is-promise: require './scripts/is-promise.ls'
diff --git a/src/web/app/common/scripts/messaging-stream.js b/src/web/app/common/scripts/messaging-stream.js
new file mode 100644
index 0000000000..e6fc6f8bd0
--- /dev/null
+++ b/src/web/app/common/scripts/messaging-stream.js
@@ -0,0 +1,36 @@
+const ReconnectingWebSocket = require('reconnecting-websocket');
+const riot = require('riot');
+
+class Connection {
+	constructor(me, otherparty) {
+		this.event = riot.observable();
+		this.me = me;
+
+		const host = CONFIG.api.url.replace('http', 'ws');
+		this.socket = new ReconnectingWebSocket(`${host}/messaging?i=${me.token}&otherparty=${otherparty}`);
+		this.socket.addEventListener('open', this.onOpen);
+		this.socket.addEventListener('message', this.onMessage);
+	}
+
+	onOpen() {
+		this.socket.send(JSON.stringify({
+			i: this.me.token
+		}));
+	}
+
+	onMessage(message) {
+		try {
+			const message = JSON.parse(message.data);
+			if (message.type) this.event.trigger(message.type, message.body);
+		} catch(e) {
+			// noop
+		}
+	}
+
+	close() {
+		this.socket.removeEventListener('open', this.onOpen);
+		this.socket.removeEventListener('message', this.onMessage);
+	}
+}
+
+module.exports = Connection;
diff --git a/src/web/app/common/scripts/messaging-stream.ls b/src/web/app/common/scripts/messaging-stream.ls
deleted file mode 100644
index ac3e74f1f5..0000000000
--- a/src/web/app/common/scripts/messaging-stream.ls
+++ /dev/null
@@ -1,34 +0,0 @@
-# Stream
-#================================
-
-ReconnectingWebSocket = require 'reconnecting-websocket'
-riot = require 'riot'
-
-class Connection
-	(me, otherparty) ~>
-		@event = riot.observable!
-		@me = me
-		host = CONFIG.api.url.replace \http \ws
-		@socket = new ReconnectingWebSocket "#{host}/messaging?i=#{me.token}&otherparty=#{otherparty}"
-
-		@socket.add-event-listener \open @on-open
-		@socket.add-event-listener \message @on-message
-
-	on-open: ~>
-		@socket.send JSON.stringify do
-			i: @me.token
-
-	on-message: (message) ~>
-		try
-			message = JSON.parse message.data
-			if message.type?
-				@event.trigger message.type, message.body
-		catch
-			# ignore
-
-	close: ~>
-		@socket.remove-event-listener \open @on-open
-		@socket.remove-event-listener \message @on-message
-		@socket.close!
-
-module.exports = Connection

From f6f1eb1bca8209714a24b997c720fb27a9f15b86 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:52:14 +0900
Subject: [PATCH 15/32] :v:

---
 src/web/app/common/mixins.ls             | 12 ++++++------
 src/web/app/common/scripts/is-promise.js |  1 +
 src/web/app/common/scripts/is-promise.ls |  1 -
 3 files changed, 7 insertions(+), 7 deletions(-)
 create mode 100644 src/web/app/common/scripts/is-promise.js
 delete mode 100644 src/web/app/common/scripts/is-promise.ls

diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
index 1194793994..c412e3b322 100644
--- a/src/web/app/common/mixins.ls
+++ b/src/web/app/common/mixins.ls
@@ -12,23 +12,23 @@ module.exports = (me) ~>
 		Cropper: require \cropperjs
 
 	riot.mixin \signout do
-		signout: require './scripts/signout.js'
+		signout: require './scripts/signout'
 
 	riot.mixin \messaging-stream do
 		MessagingStreamConnection: require './scripts/messaging-stream'
 
 	riot.mixin \is-promise do
-		is-promise: require './scripts/is-promise.ls'
+		is-promise: require './scripts/is-promise'
 
 	riot.mixin \get-post-summary do
-		get-post-summary: require './scripts/get-post-summary.js'
+		get-post-summary: require './scripts/get-post-summary'
 
 	riot.mixin \date-stringify do
-		date-stringify: require './scripts/date-stringify.ls'
+		date-stringify: require './scripts/date-stringify'
 
 	riot.mixin \text do
-		analyze: require '../../../common/text/index.js'
-		compile: require './scripts/text-compiler.js'
+		analyze: require '../../../common/text/index'
+		compile: require './scripts/text-compiler'
 
 	riot.mixin \get-password-strength do
 		get-password-strength: require 'syuilo-password-strength'
diff --git a/src/web/app/common/scripts/is-promise.js b/src/web/app/common/scripts/is-promise.js
new file mode 100644
index 0000000000..fd3dc42da3
--- /dev/null
+++ b/src/web/app/common/scripts/is-promise.js
@@ -0,0 +1 @@
+module.exports = x => typeof x.then == 'function';
diff --git a/src/web/app/common/scripts/is-promise.ls b/src/web/app/common/scripts/is-promise.ls
deleted file mode 100644
index e3c7adff85..0000000000
--- a/src/web/app/common/scripts/is-promise.ls
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = (x) -> typeof x.then == \function

From 4dc96b87ddb9d2146116a54c7031d1682b236b18 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:53:46 +0900
Subject: [PATCH 16/32] :v:

---
 src/web/app/common/scripts/loading.js | 21 +++++++++++++++++++++
 src/web/app/common/scripts/loading.ls | 16 ----------------
 2 files changed, 21 insertions(+), 16 deletions(-)
 create mode 100644 src/web/app/common/scripts/loading.js
 delete mode 100644 src/web/app/common/scripts/loading.ls

diff --git a/src/web/app/common/scripts/loading.js b/src/web/app/common/scripts/loading.js
new file mode 100644
index 0000000000..fa7eafaf96
--- /dev/null
+++ b/src/web/app/common/scripts/loading.js
@@ -0,0 +1,21 @@
+const NProgress = require('nprogress');
+NProgress.configure({
+	trickleSpeed: 500,
+	showSpinner: false
+});
+
+const root = document.getElementsByTagName('html')[0];
+
+module.exports = {
+	start: () => {
+		root.classList.add('progress');
+		NProgress.start();
+	},
+	done: () => {
+		root.classList.remove('progress');
+		NProgress.done();
+	},
+	set: val => {
+		NProgress.set(val);
+	}
+};
diff --git a/src/web/app/common/scripts/loading.ls b/src/web/app/common/scripts/loading.ls
deleted file mode 100644
index 8ebede6db4..0000000000
--- a/src/web/app/common/scripts/loading.ls
+++ /dev/null
@@ -1,16 +0,0 @@
-NProgress = require \nprogress
-NProgress.configure do
-	trickle-speed: 500ms
-	show-spinner: false
-
-root = document.get-elements-by-tag-name \html .0
-
-module.exports =
-	start: ~>
-		root.class-list.add \progress
-		NProgress.start!
-	done: ~>
-		root.class-list.remove \progress
-		NProgress.done!
-	set: (val) ~>
-		NProgress.set val

From 90959a8347cfe07606dab5edaa474b0aabbb31f8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:55:18 +0900
Subject: [PATCH 17/32] :v:

---
 src/web/app/boot.js          |  2 +-
 src/web/app/common/mixins.js | 48 ++++++++++++++++++++++++++++++++++++
 src/web/app/common/mixins.ls | 37 ---------------------------
 3 files changed, 49 insertions(+), 38 deletions(-)
 create mode 100644 src/web/app/common/mixins.js
 delete mode 100644 src/web/app/common/mixins.ls

diff --git a/src/web/app/boot.js b/src/web/app/boot.js
index a77bdd6dae..75a7086d1c 100644
--- a/src/web/app/boot.js
+++ b/src/web/app/boot.js
@@ -7,7 +7,7 @@ require('velocity-animate');
 const api = require('./common/scripts/api');
 const signout = require('./common/scripts/signout');
 const generateDefaultUserdata = require('./common/scripts/generate-default-userdata');
-const mixins = require('./common/mixins.ls');
+const mixins = require('./common/mixins');
 const checkForUpdate = require('./common/scripts/check-for-update.ls');
 require('./common/tags');
 
diff --git a/src/web/app/common/mixins.js b/src/web/app/common/mixins.js
new file mode 100644
index 0000000000..208d0fdf54
--- /dev/null
+++ b/src/web/app/common/mixins.js
@@ -0,0 +1,48 @@
+const riot = require('riot');
+
+module.exports = me => {
+	const i = me ? me.token : null;
+
+	require('./scripts/i')(me);
+
+	riot.mixin('api', {
+		api: require('./scripts/api').bind(null, i)
+	});
+
+	riot.mixin('cropper', {
+		Cropper: require('cropperjs')
+	});
+
+	riot.mixin('signout', {
+		signout: require('./scripts/signout')
+	});
+
+	riot.mixin('messaging-stream', {
+		MessagingStreamConnection: require('./scripts/messaging-stream')
+	});
+
+	riot.mixin('is-promise', {
+		isPromise: require('./scripts/is-promise')
+	});
+
+	riot.mixin('get-post-summary', {
+		getPostSummary: require('./scripts/get-post-summary')
+	});
+
+	riot.mixin('date-stringify', {
+		dateStringify: require('./scripts/date-stringify')
+	});
+
+	riot.mixin('text', {
+		analyze: require('../../../common/text/index'),
+		compile: require('./scripts/text-compiler')
+	});
+
+	riot.mixin('get-password-strength', {
+		getPasswordStrength: require('syuilo-password-strength')
+	});
+
+	riot.mixin('ui-progress', {
+		Progress: require('./scripts/loading.ls')
+	});
+};
diff --git a/src/web/app/common/mixins.ls b/src/web/app/common/mixins.ls
deleted file mode 100644
index c412e3b322..0000000000
--- a/src/web/app/common/mixins.ls
+++ /dev/null
@@ -1,37 +0,0 @@
-riot = require \riot
-
-module.exports = (me) ~>
-	i = if me? then me.token else null
-
-	(require './scripts/i') me
-
-	riot.mixin \api do
-		api: (require './scripts/api').bind null i
-
-	riot.mixin \cropper do
-		Cropper: require \cropperjs
-
-	riot.mixin \signout do
-		signout: require './scripts/signout'
-
-	riot.mixin \messaging-stream do
-		MessagingStreamConnection: require './scripts/messaging-stream'
-
-	riot.mixin \is-promise do
-		is-promise: require './scripts/is-promise'
-
-	riot.mixin \get-post-summary do
-		get-post-summary: require './scripts/get-post-summary'
-
-	riot.mixin \date-stringify do
-		date-stringify: require './scripts/date-stringify'
-
-	riot.mixin \text do
-		analyze: require '../../../common/text/index'
-		compile: require './scripts/text-compiler'
-
-	riot.mixin \get-password-strength do
-		get-password-strength: require 'syuilo-password-strength'
-
-	riot.mixin \ui-progress do
-		Progress: require './scripts/loading.ls'

From f7444c6c0b01a70e3ed064df274de14c164e987a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 16:57:16 +0900
Subject: [PATCH 18/32] :v:

---
 src/web/app/boot.js                            |  2 +-
 src/web/app/common/mixins.js                   |  2 +-
 src/web/app/common/scripts/check-for-update.js | 11 +++++++++++
 src/web/app/common/scripts/check-for-update.ls |  9 ---------
 4 files changed, 13 insertions(+), 11 deletions(-)
 create mode 100644 src/web/app/common/scripts/check-for-update.js
 delete mode 100644 src/web/app/common/scripts/check-for-update.ls

diff --git a/src/web/app/boot.js b/src/web/app/boot.js
index 75a7086d1c..b98602d402 100644
--- a/src/web/app/boot.js
+++ b/src/web/app/boot.js
@@ -8,7 +8,7 @@ const api = require('./common/scripts/api');
 const signout = require('./common/scripts/signout');
 const generateDefaultUserdata = require('./common/scripts/generate-default-userdata');
 const mixins = require('./common/mixins');
-const checkForUpdate = require('./common/scripts/check-for-update.ls');
+const checkForUpdate = require('./common/scripts/check-for-update');
 require('./common/tags');
 
 /**
diff --git a/src/web/app/common/mixins.js b/src/web/app/common/mixins.js
index 208d0fdf54..220e033846 100644
--- a/src/web/app/common/mixins.js
+++ b/src/web/app/common/mixins.js
@@ -43,6 +43,6 @@ module.exports = me => {
 	});
 
 	riot.mixin('ui-progress', {
-		Progress: require('./scripts/loading.ls')
+		Progress: require('./scripts/loading')
 	});
 };
diff --git a/src/web/app/common/scripts/check-for-update.js b/src/web/app/common/scripts/check-for-update.js
new file mode 100644
index 0000000000..cd7279e3b8
--- /dev/null
+++ b/src/web/app/common/scripts/check-for-update.js
@@ -0,0 +1,11 @@
+module.exports = () => {
+	fetch('/api:meta').then(res => {
+		res.json().then(meta => {
+			if (meta.commit.hash !== VERSION) {
+				if (window.confirm('新しいMisskeyのバージョンがあります。更新しますか?\r\n(このメッセージが繰り返し表示される場合は、サーバーにデータがまだ届いていない可能性があるので、少し時間を置いてから再度お試しください)')) {
+					location.reload(true);
+				}
+			}
+		});
+	});
+};
diff --git a/src/web/app/common/scripts/check-for-update.ls b/src/web/app/common/scripts/check-for-update.ls
deleted file mode 100644
index 48e250a4c7..0000000000
--- a/src/web/app/common/scripts/check-for-update.ls
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = ->
-	fetch \/api:meta
-	.then (res) ~>
-		meta <~ res.json!.then
-		if meta.commit.hash != VERSION
-			if window.confirm '新しいMisskeyのバージョンがあります。更新しますか?\r\n(このメッセージが繰り返し表示される場合は、サーバーにデータがまだ届いていない可能性があるので、少し時間を置いてから再度お試しください)'
-				location.reload true
-	.catch ~>
-		# ignore

From 54eb188b0e6883239b0683dbcf276a778a00a3a8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:18:31 +0900
Subject: [PATCH 19/32] :v:

---
 src/web/app/desktop/mixins.ls               |   2 +-
 src/web/app/desktop/scripts/autocomplete.js | 124 ++++++++++++++++++++
 src/web/app/desktop/scripts/autocomplete.ls | 108 -----------------
 3 files changed, 125 insertions(+), 109 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/autocomplete.js
 delete mode 100644 src/web/app/desktop/scripts/autocomplete.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index 054acbe745..e32cb1ac4c 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -38,7 +38,7 @@ module.exports = (me) ~>
 		update-wallpaper: require './scripts/update-wallpaper.ls'
 
 	riot.mixin \autocomplete do
-		Autocomplete: require './scripts/autocomplete.ls'
+		Autocomplete: require './scripts/autocomplete'
 
 	riot.mixin \follow-scroll do
 		Follower: require './scripts/follow-scroll.ls'
diff --git a/src/web/app/desktop/scripts/autocomplete.js b/src/web/app/desktop/scripts/autocomplete.js
new file mode 100644
index 0000000000..54985874d6
--- /dev/null
+++ b/src/web/app/desktop/scripts/autocomplete.js
@@ -0,0 +1,124 @@
+const getCaretCoordinates = require('textarea-caret');
+const riot = require('riot');
+
+/**
+ * オートコンプリートを管理するクラス。
+ */
+class Autocomplete {
+
+	/**
+	 * 対象のテキストエリアを与えてインスタンスを初期化します。
+	 */
+	constructor(textarea) {
+		this.suggestion = null;
+		this.textarea = textarea;
+	}
+
+	/**
+	 * このインスタンスにあるテキストエリアの入力のキャプチャを開始します。
+	 */
+	attach() {
+		this.textarea.addEventListener('input', this.onInput);
+	}
+
+	/**
+	 * このインスタンスにあるテキストエリアの入力のキャプチャを解除します。
+	 */
+	detach() {
+		this.textarea.removeEventListener('input', this.onInput);
+		this.close();
+	}
+
+	/**
+	 * [Private] テキスト入力時
+	 */
+	onInput() {
+		this.close();
+
+		const caret = this.textarea.selectionStart;
+		const text = this.textarea.value.substr(0, caret);
+
+		const mentionIndex = text.lastIndexOf('@');
+
+		if (mentionIndex == -1) return;
+
+		const username = text.substr(mentionIndex + 1);
+
+		if (!username.match(/^[a-zA-Z0-9-]+$/)) return;
+
+		this.open('user', username);
+	}
+
+	/**
+	 * [Private] サジェストを提示します。
+	 */
+	open(type, q) {
+		// 既に開いているサジェストは閉じる
+		this.close();
+
+		// サジェスト要素作成
+		const suggestion = document.createElement('mk-autocomplete-suggestion');
+
+		// ~ サジェストを表示すべき位置を計算 ~
+
+		const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart);
+
+		const rect = this.textarea.getBoundingClientRect();
+
+		const x = rect.left + window.pageXOffset + caretPosition.left;
+		const y = rect.top + window.pageYOffset + caretPosition.top;
+
+		suggestion.style.left = x + 'px';
+		suggestion.style.top = y + 'px';
+
+		// 要素追加
+		const el = document.body.appendChild(suggestion);
+
+		// マウント
+		this.suggestion = riot.mount(el, {
+			textarea: this.textarea,
+			complete: this.complete,
+			close: this.close,
+			type: type,
+			q: q
+		})[0];
+	}
+
+	/**
+	 * [Private] サジェストを閉じます。
+	 */
+	close() {
+		if (this.suggestion == nul) return;
+
+		this.suggestion.unmount();
+		this.suggestion = null;
+
+		this.textarea.focus();
+	}
+
+	/**
+	 * [Private] オートコンプリートする
+	 */
+	complete(user) {
+		this.close();
+
+		const value = user.username;
+
+		const caret = this.textarea.selectionStart;
+		const source = this.textarea.value;
+
+		const before = source.substr(0, caret);
+		const trimedBefore = before.substring(0, before.lastIndexOf('@'));
+		const after = source.substr(caret);
+
+		// 結果を挿入する
+		this.textarea.value = trimedBefore + '@' + value + ' ' + after;
+
+		// キャレットを戻す
+		this.textarea.focus();
+		const pos = caret + value.length;
+		this.textarea.setSelectionRange(pos, pos);
+	}
+}
+
+module.exports = Autocomplete;
diff --git a/src/web/app/desktop/scripts/autocomplete.ls b/src/web/app/desktop/scripts/autocomplete.ls
deleted file mode 100644
index 391fb312e3..0000000000
--- a/src/web/app/desktop/scripts/autocomplete.ls
+++ /dev/null
@@ -1,108 +0,0 @@
-# Autocomplete
-#================================
-
-get-caret-coordinates = require 'textarea-caret'
-riot = require 'riot'
-
-# オートコンプリートを管理するクラスです。
-class Autocomplete
-
-	@textarea = null
-	@suggestion = null
-
-	# 対象のテキストエリアを与えてインスタンスを初期化します。
-	(textarea) ~>
-		@textarea = textarea
-
-	# このインスタンスにあるテキストエリアの入力のキャプチャを開始します。
-	attach: ~>
-		@textarea.add-event-listener \input @on-input
-
-	# このインスタンスにあるテキストエリアの入力のキャプチャを解除します。
-	detach: ~>
-		@textarea.remove-event-listener \input @on-input
-		@close!
-
-	# テキスト入力時
-	on-input: ~>
-		@close!
-
-		caret = @textarea.selection-start
-		text = @textarea.value.substr 0 caret
-
-		mention-index = text.last-index-of \@
-
-		if mention-index == -1
-			return
-
-		username = text.substr mention-index + 1
-
-		if not username.match /^[a-zA-Z0-9-]+$/
-			return
-
-		@open \user username
-
-	# サジェストを提示します。
-	open: (type, q) ~>
-		# 既に開いているサジェストは閉じる
-		@close!
-
-		# サジェスト要素作成
-		suggestion = document.create-element \mk-autocomplete-suggestion
-
-		# ~ サジェストを表示すべき位置を計算 ~
-
-		caret-position = get-caret-coordinates @textarea, @textarea.selection-start
-
-		rect = @textarea.get-bounding-client-rect!
-
-		x = rect.left + window.page-x-offset + caret-position.left
-		y = rect.top + window.page-y-offset + caret-position.top
-
-		suggestion.style.left = x + \px
-		suggestion.style.top = y + \px
-
-		# 要素追加
-		el = document.body.append-child suggestion
-
-		# マウント
-		mounted = riot.mount el, do
-			textarea: @textarea
-			complete: @complete
-			close: @close
-			type: type
-			q: q
-
-		@suggestion = mounted.0
-
-	# サジェストを閉じます。
-	close: ~>
-		if !@suggestion?
-			return
-
-		@suggestion.unmount!
-		@suggestion = null
-
-		@textarea.focus!
-
-	# オートコンプリートする
-	complete: (user) ~>
-		@close!
-		value = user.username
-
-		caret = @textarea.selection-start
-		source = @textarea.value
-
-		before = source.substr 0 caret
-		trimed-before = before.substring 0 before.last-index-of \@
-		after = source.substr caret
-
-		# 結果を挿入する
-		@textarea.value = trimed-before + \@ + value + ' ' + after
-
-		# キャレットを戻す
-		@textarea.focus!
-		pos = caret + value.length
-		@textarea.set-selection-range pos, pos
-
-module.exports = Autocomplete

From b55185f23623a03a8191eed11015fc4c422b85eb Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:27:57 +0900
Subject: [PATCH 20/32] :v:

---
 src/web/app/desktop/mixins.ls               |  2 +-
 src/web/app/desktop/scripts/user-preview.js | 70 +++++++++++++++++++
 src/web/app/desktop/scripts/user-preview.ls | 74 ---------------------
 3 files changed, 71 insertions(+), 75 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/user-preview.js
 delete mode 100644 src/web/app/desktop/scripts/user-preview.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index e32cb1ac4c..f8431ec482 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -4,7 +4,7 @@ module.exports = (me) ~>
 	if me?
 		(require './scripts/stream') me
 
-	require './scripts/user-preview.ls'
+	require './scripts/user-preview'
 
 	require './scripts/open-window.ls'
 
diff --git a/src/web/app/desktop/scripts/user-preview.js b/src/web/app/desktop/scripts/user-preview.js
new file mode 100644
index 0000000000..8816eb41b3
--- /dev/null
+++ b/src/web/app/desktop/scripts/user-preview.js
@@ -0,0 +1,70 @@
+const riot = require('riot');
+
+riot.mixin('user-preview', {
+	init: () => {
+		this.on('mount', () => {
+			scan.call(this);
+		});
+		this.on('updated', () => {
+			scan.call(this);
+		});
+		function scan(){
+			this.root.querySelectorAll('[data-user-preview]:not([data-user-preview-attached])')
+				.forEach(attach.bind(this));
+		}
+	}
+});
+
+function attach(el) {
+	el.setAttribute('data-user-preview-attached', true);
+
+	const user = el.getAttribute('data-user-preview');
+	let tag = null;
+	let showTimer = null;
+	let hideTimer = null;
+
+	el.addEventListener('mouseover', () => {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		showTimer = setTimeout(show, 500);
+	});
+
+	el.addEventListener('mouseleave', () => {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		hideTimer = setTimeout(close, 500);
+	});
+
+	this.on('unmount', () => {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		close();
+	});
+
+	const show = () => {
+		if (tag) return;
+		const preview = document.createElement('mk-user-preview');
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + el.offsetWidth + window.pageXOffset;
+		const y = rect.top + window.pageYOffset;
+		preview.style.top = y + 'px';
+		preview.style.left = x + 'px';
+		preview.addEventListener('mouseover', () => {
+			clearTimeout(hideTimer);
+		});
+		preview.addEventListener('mouseleave', () => {
+			clearTimeout(showTimer);
+			hideTimer = setTimeout(close, 500);
+		});
+		tag = riot.mount(document.body.appendChild(preview), {
+			user: user
+		})[0];
+	}
+
+	const close = () => {
+		if (tag) {
+			tag.close();
+			tag = null;
+		}
+	}
+}
diff --git a/src/web/app/desktop/scripts/user-preview.ls b/src/web/app/desktop/scripts/user-preview.ls
deleted file mode 100644
index 0c5a67aedb..0000000000
--- a/src/web/app/desktop/scripts/user-preview.ls
+++ /dev/null
@@ -1,74 +0,0 @@
-# User Preview
-#================================
-
-riot = require \riot
-
-riot.mixin \user-preview do
-	init: ->
-		@on \mount ~>
-			scan.call @
-		@on \updated ~>
-			scan.call @
-
-		function scan
-			elems = @root.query-selector-all '[data-user-preview]:not([data-user-preview-attached])'
-			elems.for-each attach.bind @
-
-function attach el
-	el.set-attribute \data-user-preview-attached true
-	user = el.get-attribute \data-user-preview
-
-	tag = null
-
-	show-timer = null
-	hide-timer = null
-
-	el.add-event-listener \mouseover ~>
-		clear-timeout show-timer
-		clear-timeout hide-timer
-		show-timer := set-timeout ~>
-			show!
-		, 500ms
-
-	el.add-event-listener \mouseleave ~>
-		clear-timeout show-timer
-		clear-timeout hide-timer
-		hide-timer := set-timeout ~>
-			close!
-		, 500ms
-
-	@on \unmount ~>
-		clear-timeout show-timer
-		clear-timeout hide-timer
-		close!
-
-	function show
-		if tag?
-			return
-
-		preview = document.create-element \mk-user-preview
-
-		rect = el.get-bounding-client-rect!
-		x = rect.left + el.offset-width + window.page-x-offset
-		y = rect.top + window.page-y-offset
-
-		preview.style.top = y + \px
-		preview.style.left = x + \px
-
-		preview.add-event-listener \mouseover ~>
-			clear-timeout hide-timer
-
-		preview.add-event-listener \mouseleave ~>
-			clear-timeout show-timer
-			hide-timer := set-timeout ~>
-				close!
-			, 500ms
-
-		tag := riot.mount (document.body.append-child preview), do
-			user: user
-		.0
-
-	function close
-		if tag?
-			tag.close!
-			tag := null

From 1817b3e6c3b8b2a450cc8624b04cf545ab992fab Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:29:27 +0900
Subject: [PATCH 21/32] :v:

---
 src/web/app/desktop/scripts/user-preview.js | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/web/app/desktop/scripts/user-preview.js b/src/web/app/desktop/scripts/user-preview.js
index 8816eb41b3..8351f0e75a 100644
--- a/src/web/app/desktop/scripts/user-preview.js
+++ b/src/web/app/desktop/scripts/user-preview.js
@@ -2,16 +2,12 @@ const riot = require('riot');
 
 riot.mixin('user-preview', {
 	init: () => {
-		this.on('mount', () => {
-			scan.call(this);
-		});
-		this.on('updated', () => {
-			scan.call(this);
-		});
-		function scan(){
+		const scan = () => {
 			this.root.querySelectorAll('[data-user-preview]:not([data-user-preview-attached])')
 				.forEach(attach.bind(this));
-		}
+		};
+		this.on('mount', scan);
+		this.on('updated', scan);
 	}
 });
 
@@ -59,12 +55,12 @@ function attach(el) {
 		tag = riot.mount(document.body.appendChild(preview), {
 			user: user
 		})[0];
-	}
+	};
 
 	const close = () => {
 		if (tag) {
 			tag.close();
 			tag = null;
 		}
-	}
+	};
 }

From 8b89a9eb227f207a49ade4356800589b822f957e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:31:08 +0900
Subject: [PATCH 22/32] Remove unused mixin

---
 src/web/app/desktop/mixins.ls                |  3 --
 src/web/app/desktop/scripts/follow-scroll.ls | 56 --------------------
 2 files changed, 59 deletions(-)
 delete mode 100644 src/web/app/desktop/scripts/follow-scroll.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index f8431ec482..790b9339be 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -39,6 +39,3 @@ module.exports = (me) ~>
 
 	riot.mixin \autocomplete do
 		Autocomplete: require './scripts/autocomplete'
-
-	riot.mixin \follow-scroll do
-		Follower: require './scripts/follow-scroll.ls'
diff --git a/src/web/app/desktop/scripts/follow-scroll.ls b/src/web/app/desktop/scripts/follow-scroll.ls
deleted file mode 100644
index 5072e9c583..0000000000
--- a/src/web/app/desktop/scripts/follow-scroll.ls
+++ /dev/null
@@ -1,56 +0,0 @@
-class Follower
-	(el) ->
-		@follower = el
-		@last-scroll-top = window.scroll-y
-		@initial-follower-top = @follower.get-bounding-client-rect!.top
-		@page-top = 48
-
-	follow: ->
-		window-height = window.inner-height
-		follower-height = @follower.offset-height
-
-		scroll-top = window.scroll-y
-		scroll-bottom = scroll-top + window-height
-
-		follower-top = @follower.get-bounding-client-rect!.top + scroll-top
-		follower-bottom = follower-top + follower-height
-
-		height-delta = Math.abs window-height - follower-height
-		scroll-delta = @last-scroll-top - scroll-top
-
-		is-scrolling-down = (scroll-top > @last-scroll-top)
-		is-window-larger = (window-height > follower-height)
-
-		console.log @initial-follower-top
-
-		if (is-window-larger && scroll-top > @initial-follower-top) || (!is-window-larger && scroll-top > @initial-follower-top + height-delta)
-			@follower.class-list.add \fixed
-		else if !is-scrolling-down && scroll-top + @page-top <= @initial-follower-top
-			@follower.class-list.remove \fixed
-			@follower.style.top = 0
-			return
-
-		drag-bottom-down = (follower-bottom <= scroll-bottom && is-scrolling-down)
-		drag-top-up = (follower-top >= scroll-top + @page-top && !is-scrolling-down)
-
-		if drag-bottom-down
-			console.log \down
-			@follower.style.top = if is-window-larger then 0 else -height-delta + \px
-		else if drag-top-up
-			console.log \up
-			@follower.style.top = @page-top + \px
-		else if @follower.class-list.contains \fixed
-			console.log \-
-			current-top = parse-int @follower.style.top, 10
-
-			min-top = -height-delta
-			scrolled-top = current-top + scroll-delta
-
-			is-page-at-bottom = (scroll-top + window-height >= document.body.offset-height)
-			new-top = if is-page-at-bottom then min-top else scrolled-top
-
-			@follower.style.top = new-top + \px
-
-		@last-scroll-top = scroll-top
-
-module.exports = Follower

From c83302bb80d2b1cab884526b5296d8e10ae3c03f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:39:15 +0900
Subject: [PATCH 23/32] :v:

---
 src/web/app/desktop/mixins.ls              | 2 +-
 src/web/app/desktop/scripts/open-window.js | 8 ++++++++
 src/web/app/desktop/scripts/open-window.ls | 8 --------
 3 files changed, 9 insertions(+), 9 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/open-window.js
 delete mode 100644 src/web/app/desktop/scripts/open-window.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index 900d7ee193..cb3c93a3df 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -6,7 +6,7 @@ module.exports = (me) ~>
 
 	require './scripts/user-preview'
 
-	require './scripts/open-window.ls'
+	require './scripts/open-window'
 
 	riot.mixin \notify do
 		notify: require './scripts/notify.ls'
diff --git a/src/web/app/desktop/scripts/open-window.js b/src/web/app/desktop/scripts/open-window.js
new file mode 100644
index 0000000000..3f7cc424e0
--- /dev/null
+++ b/src/web/app/desktop/scripts/open-window.js
@@ -0,0 +1,8 @@
+const riot = require('riot');
+
+riot.mixin('open-window', {
+	openWindow: (name, opts) => {
+		const window = document.body.appendChild(document.createElement(name));
+		return riot.mount(window, opts)[0];
+	}
+});
diff --git a/src/web/app/desktop/scripts/open-window.ls b/src/web/app/desktop/scripts/open-window.ls
deleted file mode 100644
index 4388272ecf..0000000000
--- a/src/web/app/desktop/scripts/open-window.ls
+++ /dev/null
@@ -1,8 +0,0 @@
-riot = require \riot
-
-function open(name, opts)
-	window = document.body.append-child document.create-element name
-	riot.mount window, opts
-
-riot.mixin \open-window do
-	open-window: open

From 7602f1672713df88d292ba28093d4ce516d4aeee Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:40:07 +0900
Subject: [PATCH 24/32] :v:

---
 src/web/app/desktop/mixins.ls         | 2 +-
 src/web/app/desktop/scripts/notify.js | 8 ++++++++
 src/web/app/desktop/scripts/notify.ls | 6 ------
 3 files changed, 9 insertions(+), 7 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/notify.js
 delete mode 100644 src/web/app/desktop/scripts/notify.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index cb3c93a3df..1c80efa523 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -9,7 +9,7 @@ module.exports = (me) ~>
 	require './scripts/open-window'
 
 	riot.mixin \notify do
-		notify: require './scripts/notify.ls'
+		notify: require './scripts/notify'
 
 	dialog = require './scripts/dialog.ls'
 
diff --git a/src/web/app/desktop/scripts/notify.js b/src/web/app/desktop/scripts/notify.js
new file mode 100644
index 0000000000..048f22aea7
--- /dev/null
+++ b/src/web/app/desktop/scripts/notify.js
@@ -0,0 +1,8 @@
+const riot = require('riot');
+
+module.exports = message => {
+	const notification = document.body.appendChild(document.createElement('mk-ui-notification'));
+	riot.mount(notification, {
+		message: message
+	});
+};
diff --git a/src/web/app/desktop/scripts/notify.ls b/src/web/app/desktop/scripts/notify.ls
deleted file mode 100644
index 919bbc3dcf..0000000000
--- a/src/web/app/desktop/scripts/notify.ls
+++ /dev/null
@@ -1,6 +0,0 @@
-riot = require \riot
-
-module.exports = (message) ~>
-	notification = document.body.append-child document.create-element \mk-ui-notification
-	riot.mount notification, do
-		message: message

From a62fe67e25b012389afb6bd859f1808f7ef7db6f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:42:17 +0900
Subject: [PATCH 25/32] :v:

---
 src/web/app/desktop/mixins.ls         |  2 +-
 src/web/app/desktop/scripts/dialog.js | 16 ++++++++++++++++
 src/web/app/desktop/scripts/dialog.ls | 17 -----------------
 3 files changed, 17 insertions(+), 18 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/dialog.js
 delete mode 100644 src/web/app/desktop/scripts/dialog.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index 1c80efa523..d4c704b1c3 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -11,7 +11,7 @@ module.exports = (me) ~>
 	riot.mixin \notify do
 		notify: require './scripts/notify'
 
-	dialog = require './scripts/dialog.ls'
+	dialog = require './scripts/dialog'
 
 	riot.mixin \dialog do
 		dialog: dialog
diff --git a/src/web/app/desktop/scripts/dialog.js b/src/web/app/desktop/scripts/dialog.js
new file mode 100644
index 0000000000..6fe7b6e8d7
--- /dev/null
+++ b/src/web/app/desktop/scripts/dialog.js
@@ -0,0 +1,16 @@
+const riot = require('riot');
+
+module.exports = (title, text, buttons, canThrough, onThrough) => {
+	const dialog = document.body.appendChild(document.createElement('mk-dialog'));
+	const controller = riot.observable();
+	riot.mount(dialog, {
+		controller: controller,
+		title: title,
+		text: text,
+		buttons: buttons,
+		canThrough: canThrough,
+		onThrough: onThrough
+	});
+	controller.trigger('open');
+	return controller;
+};
diff --git a/src/web/app/desktop/scripts/dialog.ls b/src/web/app/desktop/scripts/dialog.ls
deleted file mode 100644
index f3dd6cea1b..0000000000
--- a/src/web/app/desktop/scripts/dialog.ls
+++ /dev/null
@@ -1,17 +0,0 @@
-# Dialog
-#================================
-
-riot = require 'riot'
-
-module.exports = (title, text, buttons, can-through, on-through) ~>
-	dialog = document.body.append-child document.create-element \mk-dialog
-	controller = riot.observable!
-	riot.mount dialog, do
-		controller: controller
-		title: title
-		text: text
-		buttons: buttons
-		can-through: can-through
-		on-through: on-through
-	controller.trigger \open
-	return controller

From a5ece57e3dcbc184ce8bafe88db52d4e6b845f5b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:43:31 +0900
Subject: [PATCH 26/32] :v:

---
 src/web/app/desktop/mixins.ls               |  2 +-
 src/web/app/desktop/scripts/input-dialog.js | 12 ++++++++++++
 src/web/app/desktop/scripts/input-dialog.ls | 13 -------------
 3 files changed, 13 insertions(+), 14 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/input-dialog.js
 delete mode 100644 src/web/app/desktop/scripts/input-dialog.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index d4c704b1c3..9613bcc77a 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -26,7 +26,7 @@ module.exports = (me) ~>
 				]
 
 	riot.mixin \input-dialog do
-		input-dialog: require './scripts/input-dialog.ls'
+		input-dialog: require './scripts/input-dialog'
 
 	riot.mixin \update-avatar do
 		update-avatar: require './scripts/update-avatar.ls'
diff --git a/src/web/app/desktop/scripts/input-dialog.js b/src/web/app/desktop/scripts/input-dialog.js
new file mode 100644
index 0000000000..ab9c57401f
--- /dev/null
+++ b/src/web/app/desktop/scripts/input-dialog.js
@@ -0,0 +1,12 @@
+const riot = require('riot');
+
+module.exports = (title, placeholder, defaultValue, onOk, onCancel) => {
+	const dialog = document.body.appendChild(document.createElement('mk-input-dialog'));
+	return riot.mount(dialog, {
+		title: title,
+		placeholder: placeholder,
+		'default': defaultValue,
+		onOk: onOk,
+		onCancel: onCancel
+	});
+};
diff --git a/src/web/app/desktop/scripts/input-dialog.ls b/src/web/app/desktop/scripts/input-dialog.ls
deleted file mode 100644
index f75b12dd01..0000000000
--- a/src/web/app/desktop/scripts/input-dialog.ls
+++ /dev/null
@@ -1,13 +0,0 @@
-# Input Dialog
-#================================
-
-riot = require 'riot'
-
-module.exports = (title, placeholder, default-value, on-ok, on-cancel) ~>
-	dialog = document.body.append-child document.create-element \mk-input-dialog
-	riot.mount dialog, do
-		title: title
-		placeholder: placeholder
-		default: default-value
-		on-ok: on-ok
-		on-cancel: on-cancel

From 4c955557bf4a2712653c1f2a7f02e9729eca64cf Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:45:32 +0900
Subject: [PATCH 27/32] :v:

---
 src/web/app/desktop/script.js                |  2 +-
 src/web/app/desktop/scripts/fuck-ad-block.js | 18 ++++++++++++++++++
 src/web/app/desktop/scripts/fuck-ad-block.ls | 19 -------------------
 3 files changed, 19 insertions(+), 20 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/fuck-ad-block.js
 delete mode 100644 src/web/app/desktop/scripts/fuck-ad-block.ls

diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js
index b63f712f62..92a4598305 100644
--- a/src/web/app/desktop/script.js
+++ b/src/web/app/desktop/script.js
@@ -8,7 +8,7 @@ const riot = require('riot');
 const boot = require('../boot');
 const mixins = require('./mixins.ls');
 const route = require('./router');
-const fuckAdBlock = require('./scripts/fuck-ad-block.ls');
+const fuckAdBlock = require('./scripts/fuck-ad-block');
 
 /**
  * Boot
diff --git a/src/web/app/desktop/scripts/fuck-ad-block.js b/src/web/app/desktop/scripts/fuck-ad-block.js
new file mode 100644
index 0000000000..38208d34c0
--- /dev/null
+++ b/src/web/app/desktop/scripts/fuck-ad-block.js
@@ -0,0 +1,18 @@
+require('fuckadblock');
+const dialog = require('./dialog');
+
+module.exports = () => {
+	if (fuckAdBlock === undefined) {
+		adBlockDetected();
+	} else {
+		fuckAdBlock.onDetected(adBlockDetected);
+	}
+};
+
+function adBlockDetected() {
+	dialog('<i class="fa fa-exclamation-triangle"></i>広告ブロッカーを無効にしてください',
+		'<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。',
+	[{
+		text: 'OK'
+	}]);
+}
diff --git a/src/web/app/desktop/scripts/fuck-ad-block.ls b/src/web/app/desktop/scripts/fuck-ad-block.ls
deleted file mode 100644
index 7990f58f1f..0000000000
--- a/src/web/app/desktop/scripts/fuck-ad-block.ls
+++ /dev/null
@@ -1,19 +0,0 @@
-# FUCK AD BLOCK
-#================================
-
-require \fuckadblock
-dialog = require './dialog.ls'
-
-module.exports = ~>
-	if fuck-ad-block == undefined
-		ad-block-detected!
-	else
-		fuck-ad-block.on-detected ad-block-detected
-
-function ad-block-detected
-	dialog do
-		'<i class="fa fa-exclamation-triangle"></i>広告ブロッカーを無効にしてください'
-		'<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。'
-		[
-			text: \OK
-		]

From a83c19d1a59b19610de3b500fb1151628e46d472 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:56:27 +0900
Subject: [PATCH 28/32] :v:

---
 src/web/app/desktop/mixins.ls                |  4 +-
 src/web/app/desktop/scripts/update-avatar.js | 86 ++++++++++++++++++++
 src/web/app/desktop/scripts/update-avatar.ls | 81 ------------------
 src/web/app/desktop/scripts/update-banner.js | 86 ++++++++++++++++++++
 src/web/app/desktop/scripts/update-banner.ls | 81 ------------------
 5 files changed, 174 insertions(+), 164 deletions(-)
 create mode 100644 src/web/app/desktop/scripts/update-avatar.js
 delete mode 100644 src/web/app/desktop/scripts/update-avatar.ls
 create mode 100644 src/web/app/desktop/scripts/update-banner.js
 delete mode 100644 src/web/app/desktop/scripts/update-banner.ls

diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
index 9613bcc77a..cb5680ccec 100644
--- a/src/web/app/desktop/mixins.ls
+++ b/src/web/app/desktop/mixins.ls
@@ -29,10 +29,10 @@ module.exports = (me) ~>
 		input-dialog: require './scripts/input-dialog'
 
 	riot.mixin \update-avatar do
-		update-avatar: require './scripts/update-avatar.ls'
+		update-avatar: require './scripts/update-avatar'
 
 	riot.mixin \update-banner do
-		update-banner: require './scripts/update-banner.ls'
+		update-banner: require './scripts/update-banner'
 
 	riot.mixin \autocomplete do
 		Autocomplete: require './scripts/autocomplete'
diff --git a/src/web/app/desktop/scripts/update-avatar.js b/src/web/app/desktop/scripts/update-avatar.js
new file mode 100644
index 0000000000..ad971f1b11
--- /dev/null
+++ b/src/web/app/desktop/scripts/update-avatar.js
@@ -0,0 +1,86 @@
+const riot = require('riot');
+const dialog = require('./dialog');
+const api = require('../../common/scripts/api');
+
+module.exports = (I, cb, file = null) => {
+	const fileSelected = file => {
+		const cropper = riot.mount(document.body.appendChild(document.createElement('mk-crop-window')), {
+			file: file,
+			title: 'アバターとして表示する部分を選択',
+			aspectRatio: 1 / 1
+		})[0];
+	
+		cropper.on('cropped', blob => {
+			const data = new FormData();
+			data.append('i', I.token);
+			data.append('file', blob, file.name + '.cropped.png');
+
+			api(I, 'drive/folders/find', {
+				name: 'アイコン'
+			}).then(iconFolder => {
+				if (iconFolder.length === 0) {
+					 api(I, 'drive/folders/create', {
+						name: 'アイコン'
+					}).then(iconFolder => {
+						uplaod(data, iconFolder);
+					});
+				} else {
+					uplaod(data, iconFolder[0]);
+				}
+			});
+		});
+	
+		cropper.on('skiped', () => {
+			set(file);
+		});
+	};
+
+	const uplaod = (data, folder) => {
+		const progress = riot.mount(document.body.appendChild(document.createElement('mk-progress-dialog')), {
+			title: '新しいアバターをアップロードしています'
+		})[0];
+	
+		if (folder) data.append('folder_id', folder.id);
+	
+		const xhr = new XMLHttpRequest();
+		xhr.open('POST', CONFIG.api.url + '/drive/files/create', true);
+		xhr.onload = e => {
+			const file = JSON.parse(e.target.response);
+			progress.close();
+			set(file);
+		};
+
+		xhr.upload.onprogress = e => {
+			if (e.lengthComputable) progress.updateProgress(e.loaded, e.total);
+		};
+
+		xhr.send(data);
+	};
+
+	const set = file => {
+		api(I, 'i/update', {
+			avatar_id: file.id
+		}).then(i => {
+			dialog('<i class="fa fa-info-circle"></i>アバターを更新しました',
+				'新しいアバターが反映されるまで時間がかかる場合があります。',
+			[{
+				text: 'わかった'
+			}]);
+
+			if (cb) cb(i);
+		});
+	};
+
+	if (file) {
+		fileSelected(file);
+	} else {
+		const browser = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), {
+			multiple: false,
+			title: '<i class="fa fa-picture-o"></i>アバターにする画像を選択'
+		})[0];
+
+		browser.one('selected', file => {
+			fileSelected(file);
+		});
+	}
+};
diff --git a/src/web/app/desktop/scripts/update-avatar.ls b/src/web/app/desktop/scripts/update-avatar.ls
deleted file mode 100644
index 351e54fe51..0000000000
--- a/src/web/app/desktop/scripts/update-avatar.ls
+++ /dev/null
@@ -1,81 +0,0 @@
-# Update Avatar
-#================================
-
-riot = require 'riot'
-dialog = require './dialog.ls'
-api = require '../../common/scripts/api'
-
-module.exports = (I, cb, file = null) ~>
-
-	@file-selected = (file) ~>
-		cropper = document.body.append-child document.create-element \mk-crop-window
-		cropper = riot.mount cropper, do
-			file: file
-			title: 'アバターとして表示する部分を選択'
-			aspect-ratio: 1 / 1
-		.0
-		cropper.on \cropped (blob) ~>
-			data = new FormData!
-			data.append \i I.token
-			data.append \file blob, file.name + '.cropped.png'
-			api I, \drive/folders/find do
-				name: 'アイコン'
-			.then (icon-folder) ~>
-				if icon-folder.length == 0
-					api I, \drive/folders/create do
-						name: 'アイコン'
-					.then (icon-folder) ~>
-						@uplaod data, icon-folder
-				else
-					@uplaod data, icon-folder.0
-		cropper.on \skiped ~>
-			@set file
-
-	@uplaod = (data, folder) ~>
-
-		progress = document.body.append-child document.create-element \mk-progress-dialog
-		progress = riot.mount progress, do
-			title: '新しいアバターをアップロードしています'
-		.0
-
-		if folder?
-			data.append \folder_id folder.id
-
-		xhr = new XMLHttpRequest!
-		xhr.open \POST CONFIG.api.url + \/drive/files/create true
-		xhr.onload = (e) ~>
-			file = JSON.parse e.target.response
-			progress.close!
-			@set file
-
-		xhr.upload.onprogress = (e) ~>
-			if e.length-computable
-				progress.update-progress e.loaded, e.total
-
-		xhr.send data
-
-	@set = (file) ~>
-		api I, \i/update do
-			avatar_id: file.id
-		.then (i) ~>
-			dialog do
-				'<i class="fa fa-info-circle"></i>アバターを更新しました'
-				'新しいアバターが反映されるまで時間がかかる場合があります。'
-				[
-					text: \わかった
-				]
-			if cb? then cb i
-		.catch (err) ~>
-			console.error err
-			#@opts.ui.trigger \notification 'Error!'
-
-	if file?
-		@file-selected file
-	else
-		browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
-		browser = riot.mount browser, do
-			multiple: false
-			title: '<i class="fa fa-picture-o"></i>アバターにする画像を選択'
-		.0
-		browser.one \selected (file) ~>
-			@file-selected file
diff --git a/src/web/app/desktop/scripts/update-banner.js b/src/web/app/desktop/scripts/update-banner.js
new file mode 100644
index 0000000000..9242adcea5
--- /dev/null
+++ b/src/web/app/desktop/scripts/update-banner.js
@@ -0,0 +1,86 @@
+const riot = require('riot');
+const dialog = require('./dialog');
+const api = require('../../common/scripts/api');
+
+module.exports = (I, cb, file = null) => {
+	const fileSelected = file => {
+		const cropper = riot.mount(document.body.appendChild(document.createElement('mk-crop-window')), {
+			file: file,
+			title: 'バナーとして表示する部分を選択',
+			aspectRatio: 16 / 9
+		})[0];
+	
+		cropper.on('cropped', blob => {
+			const data = new FormData();
+			data.append('i', I.token);
+			data.append('file', blob, file.name + '.cropped.png');
+
+			api(I, 'drive/folders/find', {
+				name: 'バナー'
+			}).then(iconFolder => {
+				if (iconFolder.length === 0) {
+					 api(I, 'drive/folders/create', {
+						name: 'バナー'
+					}).then(iconFolder => {
+						uplaod(data, iconFolder);
+					});
+				} else {
+					uplaod(data, iconFolder[0]);
+				}
+			});
+		});
+	
+		cropper.on('skiped', () => {
+			set(file);
+		});
+	};
+
+	const uplaod = (data, folder) => {
+		const progress = riot.mount(document.body.appendChild(document.createElement('mk-progress-dialog')), {
+			title: '新しいバナーをアップロードしています'
+		})[0];
+	
+		if (folder) data.append('folder_id', folder.id);
+	
+		const xhr = new XMLHttpRequest();
+		xhr.open('POST', CONFIG.api.url + '/drive/files/create', true);
+		xhr.onload = e => {
+			const file = JSON.parse(e.target.response);
+			progress.close();
+			set(file);
+		};
+
+		xhr.upload.onprogress = e => {
+			if (e.lengthComputable) progress.updateProgress(e.loaded, e.total);
+		};
+
+		xhr.send(data);
+	};
+
+	const set = file => {
+		api(I, 'i/update', {
+			banner_id: file.id
+		}).then(i => {
+			dialog('<i class="fa fa-info-circle"></i>バナーを更新しました',
+				'新しいバナーが反映されるまで時間がかかる場合があります。',
+			[{
+				text: 'わかりました。'
+			}]);
+
+			if (cb) cb(i);
+		});
+	};
+
+	if (file) {
+		fileSelected(file);
+	} else {
+		const browser = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), {
+			multiple: false,
+			title: '<i class="fa fa-picture-o"></i>バナーにする画像を選択'
+		})[0];
+
+		browser.one('selected', file => {
+			fileSelected(file);
+		});
+	}
+};
diff --git a/src/web/app/desktop/scripts/update-banner.ls b/src/web/app/desktop/scripts/update-banner.ls
deleted file mode 100644
index 2417b8ab2a..0000000000
--- a/src/web/app/desktop/scripts/update-banner.ls
+++ /dev/null
@@ -1,81 +0,0 @@
-# Update Banner
-#================================
-
-riot = require 'riot'
-dialog = require './dialog.ls'
-api = require '../../common/scripts/api'
-
-module.exports = (I, cb, file = null) ~>
-
-	@file-selected = (file) ~>
-		cropper = document.body.append-child document.create-element \mk-crop-window
-		cropper = riot.mount cropper, do
-			file: file
-			title: 'バナーとして表示する部分を選択'
-			aspect-ratio: 16 / 9
-		.0
-		cropper.on \cropped (blob) ~>
-			data = new FormData!
-			data.append \i I.token
-			data.append \file blob, file.name + '.cropped.png'
-			api I, \drive/folders/find do
-				name: 'バナー'
-			.then (banner-folder) ~>
-				if banner-folder.length == 0
-					api I, \drive/folders/create do
-						name: 'バナー'
-					.then (banner-folder) ~>
-						@uplaod data, banner-folder
-				else
-					@uplaod data, banner-folder.0
-		cropper.on \skiped ~>
-			@set file
-
-	@uplaod = (data, folder) ~>
-
-		progress = document.body.append-child document.create-element \mk-progress-dialog
-		progress = riot.mount progress, do
-			title: '新しいバナーをアップロードしています'
-		.0
-
-		if folder?
-			data.append \folder_id folder.id
-
-		xhr = new XMLHttpRequest!
-		xhr.open \POST CONFIG.api.url + \/drive/files/create true
-		xhr.onload = (e) ~>
-			file = JSON.parse e.target.response
-			progress.close!
-			@set file
-
-		xhr.upload.onprogress = (e) ~>
-			if e.length-computable
-				progress.update-progress e.loaded, e.total
-
-		xhr.send data
-
-	@set = (file) ~>
-		api I, \i/update do
-			banner_id: file.id
-		.then (i) ~>
-			dialog do
-				'<i class="fa fa-info-circle"></i>バナーを更新しました'
-				'新しいバナーが反映されるまで時間がかかる場合があります。'
-				[
-					text: \わかりました。
-				]
-			if cb? then cb i
-		.catch (err) ~>
-			console.error err
-			#@opts.ui.trigger \notification 'Error!'
-
-	if file?
-		@file-selected file
-	else
-		browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
-		browser = riot.mount browser, do
-			multiple: false
-			title: '<i class="fa fa-picture-o"></i>バナーにする画像を選択'
-		.0
-		browser.one \selected (file) ~>
-			@file-selected file

From 201f48d7e4e3df27ab4adde7b6b11c0f72d3e21a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 17:58:37 +0900
Subject: [PATCH 29/32] :v:

---
 src/web/app/desktop/mixins.js | 42 +++++++++++++++++++++++++++++++++++
 src/web/app/desktop/mixins.ls | 38 -------------------------------
 src/web/app/desktop/script.js |  2 +-
 3 files changed, 43 insertions(+), 39 deletions(-)
 create mode 100644 src/web/app/desktop/mixins.js
 delete mode 100644 src/web/app/desktop/mixins.ls

diff --git a/src/web/app/desktop/mixins.js b/src/web/app/desktop/mixins.js
new file mode 100644
index 0000000000..4a14b81763
--- /dev/null
+++ b/src/web/app/desktop/mixins.js
@@ -0,0 +1,42 @@
+const riot = require('riot');
+
+module.exports = me => {
+	if (me) require('./scripts/stream')(me);
+
+	require('./scripts/user-preview');
+	require('./scripts/open-window');
+
+	riot.mixin('notify', {
+		notify: require('./scripts/notify')
+	});
+
+	const dialog = require('./scripts/dialog');
+
+	riot.mixin('dialog', {
+		dialog: dialog
+	});
+
+	riot.mixin('NotImplementedException', {
+		NotImplementedException: () => {
+			return dialog('<i class="fa fa-exclamation-triangle"></i>Not implemented yet', '要求された操作は実装されていません。<br>→<a href="https://github.com/syuilo/misskey" target="_blank">Misskeyの開発に参加する</a>', [{
+				text: 'OK'
+			}]);
+		}
+	});
+
+	riot.mixin('input-dialog', {
+		inputDialog: require('./scripts/input-dialog')
+	});
+
+	riot.mixin('update-avatar', {
+		updateAvatar: require('./scripts/update-avatar')
+	});
+
+	riot.mixin('update-banner', {
+		updateBanner: require('./scripts/update-banner')
+	});
+
+	riot.mixin('autocomplete', {
+		Autocomplete: require('./scripts/autocomplete')
+	});
+};
diff --git a/src/web/app/desktop/mixins.ls b/src/web/app/desktop/mixins.ls
deleted file mode 100644
index cb5680ccec..0000000000
--- a/src/web/app/desktop/mixins.ls
+++ /dev/null
@@ -1,38 +0,0 @@
-riot = require \riot
-
-module.exports = (me) ~>
-	if me?
-		(require './scripts/stream') me
-
-	require './scripts/user-preview'
-
-	require './scripts/open-window'
-
-	riot.mixin \notify do
-		notify: require './scripts/notify'
-
-	dialog = require './scripts/dialog'
-
-	riot.mixin \dialog do
-		dialog: dialog
-
-	riot.mixin \NotImplementedException do
-		NotImplementedException: ~>
-			dialog do
-				'<i class="fa fa-exclamation-triangle"></i>Not implemented yet'
-				'要求された操作は実装されていません。<br>→<a href="https://github.com/syuilo/misskey" target="_blank">Misskeyの開発に参加する</a>'
-				[
-					text: \OK
-				]
-
-	riot.mixin \input-dialog do
-		input-dialog: require './scripts/input-dialog'
-
-	riot.mixin \update-avatar do
-		update-avatar: require './scripts/update-avatar'
-
-	riot.mixin \update-banner do
-		update-banner: require './scripts/update-banner'
-
-	riot.mixin \autocomplete do
-		Autocomplete: require './scripts/autocomplete'
diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js
index 92a4598305..b240e9ab98 100644
--- a/src/web/app/desktop/script.js
+++ b/src/web/app/desktop/script.js
@@ -6,7 +6,7 @@ require('chart.js');
 require('./tags');
 const riot = require('riot');
 const boot = require('../boot');
-const mixins = require('./mixins.ls');
+const mixins = require('./mixins');
 const route = require('./router');
 const fuckAdBlock = require('./scripts/fuck-ad-block');
 

From 6cd3c3e9ec8320af3220816f6cf4a896b0ff849b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 18:03:52 +0900
Subject: [PATCH 30/32] :v:

---
 src/web/app/dev/router.js | 42 ++++++++++++++++++++++++++++++++
 src/web/app/dev/router.ls | 51 ---------------------------------------
 src/web/app/dev/script.js |  4 +--
 3 files changed, 44 insertions(+), 53 deletions(-)
 create mode 100644 src/web/app/dev/router.js
 delete mode 100644 src/web/app/dev/router.ls

diff --git a/src/web/app/dev/router.js b/src/web/app/dev/router.js
new file mode 100644
index 0000000000..71c098463b
--- /dev/null
+++ b/src/web/app/dev/router.js
@@ -0,0 +1,42 @@
+const route = require('page');
+let page = null;
+
+module.exports = me => {
+	route('/',         index);
+	route('/apps',     apps);
+	route('/app/new',  newApp);
+	route('/app/:app', app);
+	route('*',         notFound);
+
+	function index() {
+		mount(document.createElement('mk-index'));
+	}
+
+	function apps() {
+		mount(document.createElement('mk-apps-page'));
+	}
+
+	function newApp() {
+		mount(document.createElement('mk-new-app-page'));
+	}
+
+	function app(ctx) {
+		const el = document.createElement('mk-app-page');
+		el.setAttribute('app', ctx.params.app);
+		mount(el);
+	}
+
+	function notFound() {
+		mount(document.createElement('mk-not-found'));
+	}
+
+	// EXEC
+	route();
+};
+
+const riot = require('riot');
+function mount(content) {
+	if (page) page.unmount();
+	const body = document.getElementById('app');
+	page = riot.mount(body.appendChild(content))[0];
+}
diff --git a/src/web/app/dev/router.ls b/src/web/app/dev/router.ls
deleted file mode 100644
index ac408b36ed..0000000000
--- a/src/web/app/dev/router.ls
+++ /dev/null
@@ -1,51 +0,0 @@
-# Router
-#================================
-
-route = require \page
-page = null
-
-module.exports = (me) ~>
-
-	# Routing
-	#--------------------------------
-
-	route \/ index
-	route \/apps apps
-	route \/app/new new-app
-	route \/app/:app app
-	route \* not-found
-
-	# Handlers
-	#--------------------------------
-
-	function index
-		mount document.create-element \mk-index
-
-	function apps
-		mount document.create-element \mk-apps-page
-
-	function new-app
-		mount document.create-element \mk-new-app-page
-
-	function app ctx
-		document.create-element \mk-app-page
-			..set-attribute \app ctx.params.app
-			.. |> mount
-
-	function not-found
-		mount document.create-element \mk-not-found
-
-	# Exec
-	#--------------------------------
-
-	route!
-
-# Mount
-#================================
-
-riot = require \riot
-
-function mount content
-	if page? then page.unmount!
-	body = document.get-element-by-id \app
-	page := riot.mount body.append-child content .0
diff --git a/src/web/app/dev/script.js b/src/web/app/dev/script.js
index c9c5ee51e5..e0644c263b 100644
--- a/src/web/app/dev/script.js
+++ b/src/web/app/dev/script.js
@@ -3,8 +3,8 @@
  */
 
 require('./tags');
-const boot = require('../boot.js');
-const route = require('./router.ls');
+const boot = require('../boot');
+const route = require('./router');
 
 /**
  * Boot

From 064dc501c0b3cd2be96a40804254f07a3e81b2b3 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 18:04:12 +0900
Subject: [PATCH 31/32] :v: :v: :v:

---
 gulpfile.ts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/gulpfile.ts b/gulpfile.ts
index a88c57e70d..85e5942401 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -17,7 +17,6 @@ import * as es from 'event-stream';
 import stylus = require('gulp-stylus');
 import cssnano = require('gulp-cssnano');
 import * as uglify from 'gulp-uglify';
-import ls = require('browserify-livescript');
 import riotify = require('riotify');
 import transformify = require('syuilo-transformify');
 import pug = require('gulp-pug');
@@ -169,7 +168,6 @@ gulp.task('build:client:scripts', () => new Promise(async (ok) => {
 			browserify({
 				entries: [entry]
 			})
-			.transform(ls)
 			.transform(transformify((source, file) => {
 				return source
 					.replace(/VERSION/g, `'${commit ? commit.hash : 'null'}'`)

From 6ef224cf49e8f11e03c90bae4866642e79acb07b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 18 Feb 2017 18:08:26 +0900
Subject: [PATCH 32/32] Remove unused dependency

---
 package.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/package.json b/package.json
index 3c7cb95b66..de8aa794a0 100644
--- a/package.json
+++ b/package.json
@@ -73,7 +73,6 @@
     "bcryptjs": "2.4.3",
     "body-parser": "1.16.1",
     "browserify": "14.1.0",
-    "browserify-livescript": "0.2.3",
     "chai": "3.5.0",
     "chai-http": "3.0.0",
     "chalk": "1.1.3",