You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
10 KiB

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SeaweedFS Filer</title>
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <link rel="stylesheet" href="/seaweedfsstatic/bootstrap/3.3.1/css/bootstrap.min.css">
  7. <style>
  8. body {
  9. padding-bottom: 128px;
  10. }
  11. #drop-area {
  12. border: 1px transparent;
  13. margin-top: 5px;
  14. }
  15. #drop-area.highlight {
  16. border-color: purple;
  17. border: 2px dashed #ccc;
  18. }
  19. .button {
  20. display: inline-block;
  21. padding: 2px;
  22. background: #ccc;
  23. cursor: pointer;
  24. border-radius: 2px;
  25. border: 1px solid #ccc;
  26. float: right;
  27. margin-left: 2px;
  28. margin-bottom: 0;
  29. }
  30. label {
  31. font-weight: normal;
  32. }
  33. .button:hover {
  34. background: #ddd;
  35. }
  36. #fileElem {
  37. display: none;
  38. }
  39. td, th {
  40. vertical-align: bottom;
  41. }
  42. .footer {
  43. position: absolute;
  44. bottom: 0px;
  45. right: 5%;
  46. min-width: 25%;
  47. border-left: 1px solid #ccc;
  48. border-right: 1px solid #ccc;
  49. }
  50. .add-files {
  51. font-size: 46px;
  52. text-align: center;
  53. border: 1px dashed #999;
  54. padding-bottom: 9px;
  55. margin: 0 2px;
  56. }
  57. </style>
  58. </head>
  59. <body>
  60. <div class="container">
  61. <div class="page-header">
  62. <h1>
  63. <a href="https://github.com/chrislusf/seaweedfs"><img src="/seaweedfsstatic/seaweed50x50.png"></img></a>
  64. SeaweedFS Filer
  65. </h1>
  66. </div>
  67. <div class="row">
  68. <div>
  69. <div class="btn-group btn-group-sm pull-right" role="group" style="margin-top:3px;">
  70. <label class="btn btn-default" onclick="handleCreateDir()">New Folder</label>
  71. <label class="btn btn-default" for="fileElem">Upload</label>
  72. </div>
  73. <ol class="breadcrumb">
  74. {{ range $entry := .Breadcrumbs }}
  75. <li><a href="{{ printpath $entry.Link }}">
  76. {{ $entry.Name }}
  77. </li></a>
  78. {{ end }}
  79. </ol>
  80. </div>
  81. </div>
  82. <div class="row" id="drop-area">
  83. <form class="upload-form">
  84. <input type="file" id="fileElem" multiple onchange="handleFiles(this.files)">
  85. {{if .EmptyFolder}}
  86. <div class="row add-files">
  87. +
  88. </div>
  89. {{else}}
  90. <table width="100%" class="table table-hover">
  91. {{$path := .Path }}
  92. {{ range $entry_index, $entry := .Entries }}
  93. <tr>
  94. <td>
  95. {{if $entry.IsDirectory}}
  96. <img src="/seaweedfsstatic/images/folder.gif" width="20" height="16">
  97. <a href="{{ printpath $path "/" $entry.Name "/"}}" >
  98. {{ $entry.Name }}
  99. </a>
  100. {{else}}
  101. <a href="{{ printpath $path "/" $entry.Name }}" >
  102. {{ $entry.Name }}
  103. </a>
  104. {{end}}
  105. </td>
  106. <td align="right" nowrap>
  107. {{if $entry.IsDirectory}}
  108. {{else}}
  109. {{ $entry.Mime }}&nbsp;
  110. {{end}}
  111. </td>
  112. <td align="right" nowrap>
  113. {{if $entry.IsDirectory}}
  114. {{else}}
  115. {{ $entry.Size | humanizeBytes }}&nbsp;
  116. {{end}}
  117. </td>
  118. <td align="right" nowrap>
  119. {{ $entry.Timestamp.Format "2006-01-02 15:04" }}
  120. </td>
  121. <td>
  122. <div class="btn-group btn-group-xs pull-right" role="group">
  123. <label class="btn" onclick="handleRename('{{ $entry.Name }}', '{{ printpath $path "/" }}')"><img src="/seaweedfsstatic/images/rename-16.png" width="16" height="16"></label>
  124. {{if $entry.IsDirectory}}
  125. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name "/" }}')"><img src="/seaweedfsstatic/images/delete-16.png" width="16" height="16"></label>
  126. {{else}}
  127. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name }}')"><img src="/seaweedfsstatic/images/delete-16.png" width="16" height="16"></label>
  128. {{end}}
  129. </div>
  130. </td>
  131. </tr>
  132. {{ end }}
  133. </table>
  134. {{end}}
  135. </form>
  136. </div>
  137. {{if .ShouldDisplayLoadMore}}
  138. <div class="row">
  139. <a href={{ print .Path "?limit=" .Limit "&lastFileName=" .LastFileName}} >
  140. Load more
  141. </a>
  142. </div>
  143. {{end}}
  144. <br/>
  145. <br/>
  146. <div id="progress-area" class="footer" style="display: none;">
  147. </div>
  148. </div>
  149. </body>
  150. <script type="text/javascript">
  151. // ************************ Drag and drop ***************** //
  152. let dropArea = document.getElementById("drop-area");
  153. let progressArea = document.getElementById("progress-area");
  154. // Prevent default drag behaviors
  155. ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  156. dropArea.addEventListener(eventName, preventDefaults, false);
  157. document.body.addEventListener(eventName, preventDefaults, false);
  158. });
  159. // Highlight drop area when item is dragged over it
  160. ;['dragenter', 'dragover'].forEach(eventName => {
  161. dropArea.addEventListener(eventName, highlight, false);
  162. });
  163. ;['dragleave', 'drop'].forEach(eventName => {
  164. dropArea.addEventListener(eventName, unhighlight, false);
  165. });
  166. // Handle dropped files
  167. dropArea.addEventListener('drop', handleDrop, false);
  168. function preventDefaults(e) {
  169. e.preventDefault();
  170. e.stopPropagation();
  171. }
  172. function highlight(e) {
  173. dropArea.classList.add('highlight');
  174. }
  175. function unhighlight(e) {
  176. dropArea.classList.remove('highlight');
  177. }
  178. function handleDrop(e) {
  179. var dt = e.dataTransfer;
  180. var files = dt.files;
  181. handleFiles(files);
  182. }
  183. var uploadList = {};
  184. function handleFiles(files) {
  185. files = [...files];
  186. files.forEach(startUpload);
  187. renderProgress();
  188. files.forEach(uploadFile);
  189. }
  190. function startUpload(file, i) {
  191. uploadList[file.name] = {'name': file.name, 'percent': 0, 'finish': false};
  192. }
  193. function renderProgress() {
  194. var values = Object.values(uploadList);
  195. var html = '<table class="table">\n<tr><th>Uploading</th><\/tr>\n';
  196. for (let i of values) {
  197. var progressBarClass = 'progress-bar-striped active';
  198. if (i.percent >= 100) {
  199. progressBarClass = 'progress-bar-success';
  200. }
  201. html += '<tr>\n<td>\n';
  202. html += '<div class="progress" style="margin-bottom: 2px;">\n';
  203. html += '<div class="progress-bar ' + progressBarClass + '" role="progressbar" aria-valuenow="' + '100" aria-valuemin="0" aria-valuemax="100" style="width:' + i.percent + '%;">';
  204. html += '<span style="margin-right: 10px;">' + i.name + '</span>' + i.percent + '%<\/div>';
  205. html += '<\/div>\n<\/td>\n<\/tr>\n';
  206. }
  207. html += '<\/table>\n';
  208. progressArea.innerHTML = html;
  209. if (values.length > 0) {
  210. progressArea.attributes.style.value = '';
  211. }
  212. }
  213. function reportProgress(file, percent) {
  214. var item = uploadList[file]
  215. item.percent = percent;
  216. renderProgress();
  217. }
  218. function finishUpload(file) {
  219. uploadList[file]['finish'] = true;
  220. renderProgress();
  221. var allFinish = true;
  222. for (let i of Object.values(uploadList)) {
  223. if (!i.finish) {
  224. allFinish = false;
  225. break;
  226. }
  227. }
  228. if (allFinish) {
  229. console.log('All Finish');
  230. window.location.reload();
  231. }
  232. }
  233. function uploadFile(file, i) {
  234. var url = window.location.href;
  235. var xhr = new XMLHttpRequest();
  236. var fileName = file.name;
  237. xhr.upload.addEventListener('progress', function(e) {
  238. if (e.lengthComputable) {
  239. var percent = Math.ceil((e.loaded / e.total) * 100);
  240. reportProgress(fileName, percent)
  241. }
  242. });
  243. xhr.upload.addEventListener('loadend', function(e) {
  244. finishUpload(fileName);
  245. });
  246. var formData = new FormData();
  247. xhr.open('POST', url, true);
  248. formData.append('file', file);
  249. xhr.send(formData);
  250. }
  251. function handleCreateDir() {
  252. var dirName = prompt('Folder Name:', '');
  253. dirName = dirName.trim();
  254. if (dirName == null || dirName == '') {
  255. return;
  256. }
  257. var baseUrl = window.location.href;
  258. if (!baseUrl.endsWith('/')) {
  259. baseUrl += '/';
  260. }
  261. var url = baseUrl + dirName;
  262. if (!url.endsWith('/')) {
  263. url += '/';
  264. }
  265. var xhr = new XMLHttpRequest();
  266. xhr.open('POST', url, false);
  267. xhr.setRequestHeader('Content-Type', '');
  268. xhr.send();
  269. window.location.reload();
  270. }
  271. function handleRename(originName, basePath) {
  272. var newName = prompt('New Name:', originName);
  273. if (newName == null || newName == '') {
  274. return;
  275. }
  276. var url = basePath + newName;
  277. var originPath = basePath + originName;
  278. url += '?mv.from=' + originPath;
  279. var xhr = new XMLHttpRequest();
  280. xhr.open('POST', url, false);
  281. xhr.setRequestHeader('Content-Type', '');
  282. xhr.send();
  283. window.location.reload();
  284. }
  285. function handleDelete(path) {
  286. if (!confirm('Are you sure to delete ' + path + '?')) {
  287. return;
  288. }
  289. var url = path;
  290. if (url.endsWith('/')) {
  291. url += '?recursive=true';
  292. }
  293. var xhr = new XMLHttpRequest();
  294. xhr.open('DELETE', url, false);
  295. xhr.send();
  296. window.location.reload();
  297. }
  298. </script>
  299. </html>