From b66bac7851c331c8281418f9aa5a6068edc552d7 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 21 Feb 2017 09:49:35 +0900
Subject: [PATCH] wip

---
 src/web/app/boot.js                        |   5 +-
 src/web/app/desktop/tags/drive/browser.tag | 565 +++++++++++----------
 2 files changed, 298 insertions(+), 272 deletions(-)

diff --git a/src/web/app/boot.js b/src/web/app/boot.js
index ab147eb56..06e457e2b 100644
--- a/src/web/app/boot.js
+++ b/src/web/app/boot.js
@@ -27,13 +27,16 @@ riot.mixin({
 // ↓ iOS待ちPolyfill (SEE: http://caniuse.com/#feat=fetch)
 require('whatwg-fetch');
 
-// ↓ NodeList、HTMLCollectionで forEach を使えるようにする
+// ↓ NodeList、HTMLCollection、FileListで forEach を使えるようにする
 if (NodeList.prototype.forEach === undefined) {
 	NodeList.prototype.forEach = Array.prototype.forEach;
 }
 if (HTMLCollection.prototype.forEach === undefined) {
 	HTMLCollection.prototype.forEach = Array.prototype.forEach;
 }
+if (FileList.prototype.forEach === undefined) {
+	FileList.prototype.forEach = Array.prototype.forEach;
+}
 
 // ↓ iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
 try {
diff --git a/src/web/app/desktop/tags/drive/browser.tag b/src/web/app/desktop/tags/drive/browser.tag
index 1c6ed53ca..989ec48ae 100644
--- a/src/web/app/desktop/tags/drive/browser.tag
+++ b/src/web/app/desktop/tags/drive/browser.tag
@@ -8,7 +8,7 @@
 		</div>
 		<input class="search" type="search" placeholder="&#xf002; 検索"/>
 	</nav>
-	<div class="main { uploading: uploads.length > 0, loading: loading }" ref="main" onmousedown={ onmousedown } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu }>
+	<div class="main { uploading: uploads.length > 0, fetching: fetching }" ref="main" onmousedown={ onmousedown } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu }>
 		<div class="selection" ref="selection"></div>
 		<div class="contents" ref="contents">
 			<div class="folders" ref="foldersContainer" if={ folders.length > 0 }>
@@ -23,13 +23,13 @@
 				</virtual>
 				<button if={ moreFiles }>もっと読み込む</button>
 			</div>
-			<div class="empty" if={ files.length == 0 && folders.length == 0 && !loading }>
+			<div class="empty" if={ files.length == 0 && folders.length == 0 && !fetching }>
 				<p if={ draghover }>ドロップですか?いいですよ、ボクはカワイイですからね</p>
 				<p if={ !draghover && folder == null }><strong>ドライブには何もありません。</strong><br/>右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。</p>
 				<p if={ !draghover && folder != null }>このフォルダーは空です</p>
 			</div>
 		</div>
-		<div class="loading" if={ loading }>
+		<div class="loading" if={ fetching }>
 			<div class="spinner">
 				<div class="dot1"></div>
 				<div class="dot2"></div>
@@ -133,7 +133,7 @@
 				&, *
 					user-select none
 
-				&.loading
+				&.fetching
 					cursor wait !important
 
 					*
@@ -184,7 +184,7 @@
 						> p
 							margin 0
 
-				> .loading
+				> .fetching
 					.spinner
 						margin 100px auto
 						width 40px
@@ -275,379 +275,402 @@
 				});
 			});
 
-			this.stream.on 'drive_file_created' this.onStreamDriveFileCreated
-			this.stream.on 'drive_file_updated' this.onStreamDriveFileUpdated
-			this.stream.on 'drive_folder_created' this.onStreamDriveFolderCreated
-			this.stream.on 'drive_folder_updated' this.onStreamDriveFolderUpdated
+			this.stream.on('drive_file_created', this.onStreamDriveFileCreated);
+			this.stream.on('drive_file_updated', this.onStreamDriveFileUpdated);
+			this.stream.on('drive_folder_created', this.onStreamDriveFolderCreated);
+			this.stream.on('drive_folder_updated', this.onStreamDriveFolderUpdated);
 
 			// Riotのバグでnullを渡しても""になる
 			// https://github.com/riot/riot/issues/2080
-			#if this.opts.folder?
-			if this.opts.folder? and this.opts.folder != ''
-				this.move this.opts.folder
-			else
+			//if (this.opts.folder)
+			if (this.opts.folder && this.opts.folder != '') {
+				this.move(this.opts.folder);
+			} else {
 				this.load();
+			}
+		});
 
 		this.on('unmount', () => {
-			this.stream.off 'drive_file_created' this.onStreamDriveFileCreated
-			this.stream.off 'drive_file_updated' this.onStreamDriveFileUpdated
-			this.stream.off 'drive_folder_created' this.onStreamDriveFolderCreated
-			this.stream.off 'drive_folder_updated' this.onStreamDriveFolderUpdated
+			this.stream.off('drive_file_created', this.onStreamDriveFileCreated);
+			this.stream.off('drive_file_updated', this.onStreamDriveFileUpdated);
+			this.stream.off('drive_folder_created', this.onStreamDriveFolderCreated);
+			this.stream.off('drive_folder_updated', this.onStreamDriveFolderUpdated);
+		});
 
-		this.onStreamDriveFileCreated = (file) => {
-			this.addFile file, true
+		this.onStreamDriveFileCreated = file => {
+			this.addFile(file, true);
+		};
 
-		this.onStreamDriveFileUpdated = (file) => {
-			current = if this.folder? then this.folder.id else null
-			if current != file.folder_id
-				@remove-file file
-			else
-				this.addFile file, true
+		this.onStreamDriveFileUpdated = file => {
+			const current = this.folder ? this.folder.id : null;
+			if (current != file.folder_id) {
+				this.removeFile(file);
+			} else {
+				this.addFile(file, true);
+			}
+		};
 
-		this.onStreamDriveFolderCreated = (folder) => {
-			this.addFolder folder, true
+		this.onStreamDriveFolderCreated = folder => {
+			this.addFolder(folder, true);
+		};
 
-		this.onStreamDriveFolderUpdated = (folder) => {
-			current = if this.folder? then this.folder.id else null
-			if current != folder.parent_id
-				this.removeFolder folder
-			else
-				this.addFolder folder, true
+		this.onStreamDriveFolderUpdated = folder => {
+			const current = this.folder ? this.folder.id : null;
+			if (current != folder.parent_id) {
+				this.removeFolder(folder);
+			} else {
+				this.addFolder(folder, true);
+			}
+		};
 
-		this.onmousedown = (e) => {
-			if (contains this.refs.folders-container, e.target) or (contains this.refs.files-container, e.target)
-				return true
+		this.onmousedown = e => {
+			if (contains(this.refs.foldersContainer, e.target) || contains(this.refs.filesContainer, e.target)) return true;
 
-			rect = this.refs.main.getBoundingClientRect();
+			const rect = this.refs.main.getBoundingClientRect();
 
-			left = e.pageX + this.refs.main.scroll-left - rect.left - window.pageXOffset
-			top = e.pageY + this.refs.main.scroll-top - rect.top - window.pageYOffset
+			const left = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset
+			const top = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset
 
-			move = (e) =>
-				this.refs.selection.style.display = 'block' 
+			const move = e => {
+				this.refs.selection.style.display = 'block';
 
-				cursorX = e.pageX + this.refs.main.scroll-left - rect.left - window.pageXOffset
-				cursorY = e.pageY + this.refs.main.scroll-top - rect.top - window.pageYOffset
-				w = cursorX - left
-				h = cursorY - top
+				const cursorX = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset;
+				const cursorY = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset;
+				const w = cursorX - left;
+				const h = cursorY - top;
 
-				if w > 0
-					this.refs.selection.style.width = w + 'px' 
-					this.refs.selection.style.left = left + 'px' 
-				else
-					this.refs.selection.style.width = -w + 'px' 
-					this.refs.selection.style.left = cursorX + 'px' 
+				if (w > 0) {
+					this.refs.selection.style.width = w + 'px';
+					this.refs.selection.style.left = left + 'px';
+				} else {
+					this.refs.selection.style.width = -w + 'px';
+					this.refs.selection.style.left = cursorX + 'px';
+				}
 
-				if h > 0
-					this.refs.selection.style.height = h + 'px' 
-					this.refs.selection.style.top = top + 'px' 
-				else
-					this.refs.selection.style.height = -h + 'px' 
-					this.refs.selection.style.top = cursorY + 'px' 
+				if (h > 0) {
+					this.refs.selection.style.height = h + 'px';
+					this.refs.selection.style.top = top + 'px';
+				} else {
+					this.refs.selection.style.height = -h + 'px';
+					this.refs.selection.style.top = cursorY + 'px';
+				}
+			};
 
-			up = (e) =>
-				document.documentElement.removeEventListener 'mousemove' move
-				document.documentElement.removeEventListener 'mouseup' up
+			up = e => {
+				document.documentElement.removeEventListener('mousemove', move);
+				document.documentElement.removeEventListener('mouseup', up);
 
-				this.refs.selection.style.display = 'none' 
+				this.refs.selection.style.display = 'none';
+			};
 
-			document.documentElement.addEventListener 'mousemove' move
-			document.documentElement.addEventListener 'mouseup' up
+			document.documentElement.addEventListener('mousemove', move);
+			document.documentElement.addEventListener('mouseup', up);
+		};
 
-		this.path-oncontextmenu = (e) => {
+		this.pathOncontextmenu = e => {
 			e.preventDefault();
 			e.stopImmediatePropagation();
-			return false
+			return false;
+		};
 
-		this.ondragover = (e) => {
+		this.ondragover = e => {
 			e.preventDefault();
 			e.stopPropagation();
 
 			// ドラッグ元が自分自身の所有するアイテムかどうか
-			if !@isDragSource
+			if (!this.isDragSource) {
 				// ドラッグされてきたものがファイルだったら
-				if e.dataTransfer.effectAllowed == 'all' 
-					e.dataTransfer.dropEffect = 'copy' 
-				else
-					e.dataTransfer.dropEffect = 'move' 
-				this.draghover = true
-			else
+				e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+				this.draghover = true;
+			} else {
 				// 自分自身にはドロップさせない
-				e.dataTransfer.dropEffect = 'none' 
-			return false
+				e.dataTransfer.dropEffect = 'none';
+				return false;
+			}
+		};
 
-		this.ondragenter = (e) => {
+		this.ondragenter = e => {
 			e.preventDefault();
-			if !@isDragSource
-				this.draghover = true
+			if (!this.isDragSource) this.draghover = true;
+		};
 
-		this.ondragleave = (e) => {
-			this.draghover = false
+		this.ondragleave = e => {
+			this.draghover = false;
+		};
 
-		this.ondrop = (e) => {
+		this.ondrop = e => {
 			e.preventDefault();
 			e.stopPropagation();
 
-			this.draghover = false
+			this.draghover = false;
 
 			// ドロップされてきたものがファイルだったら
-			if e.dataTransfer.files.length > 0
-				Array.prototype.forEach.call e.dataTransfer.files, (file) =>
-					@upload file, this.folder
-				return false
+			if (e.dataTransfer.files.length > 0) {
+				e.dataTransfer.files.forEach(file => {
+					this.upload(file, this.folder);
+				});
+				return false;
+			}
 
 			// データ取得
-			data = e.dataTransfer.get-data 'text'
-			if !data?
-				return false
+			const data = e.dataTransfer.getData('text');
+			if (data == null) return false;
 
 			// パース
-			obj = JSON.parse data
+			// TODO: JSONじゃなかったら中断
+			const obj = JSON.parse(data);
 
 			// (ドライブの)ファイルだったら
-			if obj.type == 'file' 
-				file = obj.id
-				if (this.files.some (f) => f.id == file)
-					return false
-				@remove-file file
+			if (obj.type == 'file') {
+				const file = obj.id;
+				if (this.files.some(f => f.id == file)) return false;
+				this.removeFile(file);
 				this.api('drive/files/update', {
-					file_id: file
-					folder_id: if this.folder? then this.folder.id else null
-				}).then(() => {
-					// something
-				.catch (err, text-status) =>
-					console.error err
-
+					file_id: file,
+					folder_id: this.folder ? this.folder.id : null
+				});
 			// (ドライブの)フォルダーだったら
-			else if obj.type == 'folder' 
-				folder = obj.id
+			} else if (obj.type == 'folder') {
+				const folder = obj.id;
 				// 移動先が自分自身ならreject
-				if this.folder? and folder == this.folder.id
-					return false
-				if (this.folders.some (f) => f.id == folder)
-					return false
-				this.removeFolder folder
+				if (this.folder && folder == this.folder.id) return false;
+				if (this.folders.some(f => f.id == folder)) return false;
+				this.removeFolder(folder);
 				this.api('drive/folders/update', {
-					folder_id: folder
-					parent_id: if this.folder? then this.folder.id else null
+					folder_id: folder,
+					parent_id: this.folder ? this.folder.id : null
 				}).then(() => {
 					// something
-				.catch (err) =>
-					if err == 'detected-circular-definition'
-						@dialog do
-							'<i class="fa fa-exclamation-triangle"></i>操作を完了できません'
-							'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。'
-							[
-								text: 'OK' 
-							]
+				}).catch(err => {
+					switch (err) {
+						case 'detected-circular-definition':
+							this.dialog('<i class="fa fa-exclamation-triangle"></i>操作を完了できません',
+								'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。', [{
+								text: 'OK'
+							}]);
+							break;
+						default:
+							alert('不明なエラー' + err);
+					}
+				});
+			}
 
-			return false
+			return false;
+		};
 
-		this.oncontextmenu = (e) => {
+		this.oncontextmenu = e => {
 			e.preventDefault();
 			e.stopImmediatePropagation();
 
-			ctx = document.body.appendChild(document.createElement('mk-drive-browser-base-contextmenu'));
- 			ctx = riot.mount ctx, do
+			const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-base-contextmenu')), {
 				browser: this
-			ctx = ctx.0
-			ctx.open do
-				x: e.pageX - window.pageXOffset
+			})[0];
+			ctx.open({
+				x: e.pageX - window.pageXOffset,
 				y: e.pageY - window.pageYOffset
+			});
 
-			return false
+			return false;
+		};
 
-		this.select-local-file = () => {
-			this.refs.file-input.click();
+		this.selectLocalFile = () => {
+			this.refs.fileInput.click();
+		};
 
-		this.url-upload = () => {
-			url <~ @input-dialog do
-				'URLアップロード'
-				'アップロードしたいファイルのURL'
-				null
-
-			if url? and url != ''
+		this.urlUpload = () => {
+			this.inputDialog('URLアップロード', 'アップロードしたいファイルのURL', null, url => {
 				this.api('drive/files/upload_from_url', {
-					url: url
-					folder_id: if this.folder? then this.folder.id else undefined
+					url: url,
+					folder_id: this.folder ? this.folder.id : undefined
+				});
 
-				@dialog do
-					'<i class="fa fa-check"></i>アップロードをリクエストしました'
-					'アップロードが完了するまで時間がかかる場合があります。'
-					[
-						text: 'OK' 
-					]
+				this.dialog('<i class="fa fa-check"></i>アップロードをリクエストしました',
+					'アップロードが完了するまで時間がかかる場合があります。', [{
+					text: 'OK'
+				}]);
+			});
+		};
 
 		this.createFolder = () => {
-			name <~ @input-dialog do
-				'フォルダー作成'
-				'フォルダー名'
-				null
+			this.inputDialog('フォルダー作成', 'フォルダー名', null, name => {
+				this.api('drive/folders/create', {
+					name: name,
+					folder_id: this.folder ? this.folder.id : undefined
+				}).then(folder => {
+					this.addFolder(folder, true);
+					this.update();
+				});
+			});
+		};
 
-			this.api('drive/folders/create', {
-				name: name
-				folder_id: if this.folder? then this.folder.id else undefined
-			}).then((folder) => {
-				this.addFolder folder, true
-				this.update();
-			.catch (err) =>
-				console.error err
-
-		this.change-file-input = () => {
-			files = this.refs.file-input.files
-			for i from 0 to files.length - 1
-				file = files.item i
-				@upload file, this.folder
+		this.changeFileInput = () => {
+			this.refs.fileInput.files.forEach(file => {
+				this.upload(file, this.folder);
+			});
+		};
 
 		this.upload = (file, folder) => {
-			if folder? and typeof folder == 'object' 
-				folder = folder.id
-			this.refs.uploader.upload file, folder
+			if (folder && typeof folder == 'object') folder = folder.id;
+			this.refs.uploader.upload(file, folder);
+		};
 
-		this.get-selection = () => {
-			this.files.filter (file) -> file._selected
+		this.getSelection = () => {
+			this.files.filter(file => file._selected);
+		};
 
-		this.newWindow = (folder-id) => {
-			browser = document.body.appendChild(document.createElement('mk-drive-browser-window'));
- 			riot.mount browser, do
-				folder: folder-id
+		this.newWindow = folderId => {
+			riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-window')), {
+				folder: folderId
+			});
+		};
 
-		this.move = (target-folder) => {
-			if target-folder? and typeof target-folder == 'object' 
-				target-folder = target-folder.id
+		this.move = target => {
+			if (target == null) {
+				this.goRoot();
+				return;
+			} else if (typeof target == 'object') {
+				target = target.id;
+			}
 
-			if target-folder == null
-				@go-root!
-				return
-
-			this.loading = true
-			this.update();
+			this.update({
+				fetching: true
+			});
 
 			this.api('drive/folders/show', {
-				folder_id: target-folder
-			}).then((folder) => {
-				this.folder = folder
-				this.hierarchyFolders = []
+				folder_id: target
+			}).then(folder => {
+				this.folder = folder;
+				this.hierarchyFolders = [];
 
-				x = (f) =>
-					@hierarchyFolders.unshift f
-					if f.parent?
-						x f.parent
+				const dive = folder => {
+					this.hierarchyFolders.unshift(folder);
+					if (folder.parent) dive(folder.parent);
+				};
 
-				if folder.parent?
-					x folder.parent
+				if (folder.parent) dive(folder.parent);
 
 				this.update();
 				this.load();
-			.catch (err, text-status) ->
-				console.error err
+			});
+		};
 
-		this.add-folder = (folder, unshift = false) => {
-			current = if this.folder? then this.folder.id else null
-			if current != folder.parent_id
-				return
+		this.addFolder = (folder, unshift = false) => {
+			const current = this.folder ? this.folder.id : null;
+			if (current != folder.parent_id) return;
 
-			if (this.folders.some (f) => f.id == folder.id)
-				exist = (this.folders.map (f) -> f.id).index-of folder.id
-				this.folders[exist] = folder
+			if (this.folders.some(f => f.id == folder.id)) {
+				const exist = this.folders.map(f => f.id).indexOf(folder.id);
+				this.folders[exist] = folder;
 				this.update();
-				return
+				return;
+			}
 
-			if unshift
-				this.folders.unshift folder
-			else
-				this.folders.push folder
+			if (unshift) {
+				this.folders.unshift(folder);
+			} else {
+				this.folders.push(folder);
+			}
 
 			this.update();
+		};
 
-		this.add-file = (file, unshift = false) => {
-			current = if this.folder? then this.folder.id else null
-			if current != file.folder_id
-				return
+		this.addFile = (file, unshift = false) => {
+			const current = this.folder ? this.folder.id : null;
+			if (current != file.folder_id) return;
 
-			if (this.files.some (f) => f.id == file.id)
-				exist = (this.files.map (f) -> f.id).index-of file.id
-				this.files[exist] = file
+			if (this.files.some(f => f.id == file.id)) {
+				const exist = this.files.map(f => f.id).indexOf(file.id);
+				this.files[exist] = file;
 				this.update();
-				return
+				return;
+			}
 
-			if unshift
-				this.files.unshift file
-			else
-				this.files.push file
+			if (unshift) {
+				this.files.unshift(file);
+			} else {
+				this.files.push(file);
+			}
 
 			this.update();
+		};
 
-		this.remove-folder = (folder) => {
-			if typeof folder == 'object' 
-				folder = folder.id
-			this.folders = this.folders.filter (f) -> f.id != folder
+		this.removeFolder = folder => {
+			if (typeof folder == 'object') folder = folder.id;
+			this.folders = this.folders.filter(f => f.id != folder);
 			this.update();
+		};
 
-		this.remove-file = (file) => {
-			if typeof file == 'object' 
-				file = file.id
-			this.files = this.files.filter (f) -> f.id != file
+		this.removeFile = file => {
+			if (typeof file == 'object') file = file.id;
+			this.files = this.files.filter(f => f.id != file);
 			this.update();
+		};
 
-		this.go-root = () => {
-			if this.folder != null
-				this.folder = null
-				this.hierarchyFolders = []
-				this.update();
-				this.load();
+		this.goRoot = () => {
+			// 既にrootにいるなら何もしない
+			if (this.folder == null) return;
+
+			this.update({
+				folder: null,
+				hierarchyFolders: []
+			});
+			this.load();
+		};
 
 		this.load = () => {
-			this.folders = []
-			this.files = []
-			this.more-folders = false
-			this.more-files = false
-			this.loading = true
-			this.update();
+			this.update({
+				folders: [],
+				files: [],
+				moreFolders: false,
+				moreFiles: false,
+				fetching: true
+			});
 
-			load-folders = null
-			load-files = null
+			let fetchedFolders = null;
+			let fetchedFiles = null;
 
-			folders-max = 30
-			files-max = 30
+			const foldersMax = 30;
+			const filesMax = 30;
 
 			// フォルダ一覧取得
 			this.api('drive/folders', {
-				folder_id: if this.folder? then this.folder.id else null
-				limit: folders-max + 1
-			}).then((folders) => {
-				if folders.length == folders-max + 1
-					this.more-folders = true
-					folders.pop!
-				load-folders := folders
-				complete!
-			.catch (err, text-status) =>
-				console.error err
+				folder_id: this.folder ? this.folder.id : null,
+				limit: foldersMax + 1
+			}).then(folders => {
+				if (folders.length == foldersMax + 1) {
+					this.moreFolders = true;
+					folders.pop();
+				}
+				fetchedFolders = folders;
+				complete();
+			});
 
 			// ファイル一覧取得
 			this.api('drive/files', {
-				folder_id: if this.folder? then this.folder.id else null
-				limit: files-max + 1
-			}).then((files) => {
-				if files.length == files-max + 1
-					this.more-files = true
-					files.pop!
-				load-files := files
-				complete!
-			.catch (err, text-status) =>
-				console.error err
+				folder_id: this.folder ? this.folder.id : null,
+				limit: filesMax + 1
+			}).then(files => {
+				if (files.length == filesMax + 1) {
+					this.moreFiles = true;
+					files.pop();
+				}
+				fetchedFiles = files;
+				complete();
+			});
 
-			flag = false
-			complete = =>
-				if flag
-					load-folders.forEach (folder) =>
-						this.addFolder folder
-					load-files.forEach (file) =>
-						this.addFile file
-					this.loading = false
-					this.update();
-				else
-					flag := true
+			let flag = false;
+			complete = () => {
+				if (flag) {
+					fetchedFolders.forEach(folder => this.addFolder);
+					fetchedFiles.forEach(file => this.addFile);
+					this.update({
+						fetching: false
+					});
+				} else {
+					flag = true;
+				}
+			};
+		};
 
 	</script>
 </mk-drive-browser>