sora 24.02.25 → 24.8.4

Sign up to get free protection for your applications and to get access to all the features.
data/www/index.html CHANGED
@@ -1,27 +1,47 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="ja">
3
3
 
4
4
  <head>
5
5
  <meta charset="utf-8">
6
6
  <title>sora</title>
7
- <link rel="stylesheet" href="/style">
8
- <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
8
  </head>
10
9
 
11
10
  <body>
11
+ <fluent-progress id="progress"></fluent-progress>
12
12
  <main id="main">
13
- <h1>sora</h1>
14
- <div class="Grid Text-SendButton">
15
- <input id="content" placeholder="Text..." class="TextBox" />
16
- <input type="button" id="send-button" value="Send" class="Accent Button SendButton" />
13
+ <div id="items" class="">
14
+ <fluent-listbox id="contents"></fluent-listbox>
15
+ <fluent-card id="details">
16
+ <div class="text-details">
17
+ <h2 id="text-title"></h2>
18
+ <p id="text-content"></p>
19
+ <fluent-button id="copy-text"></fluent-button>
20
+ <fluent-button id="remove-text" appearance="accent"></fluent-button>
21
+ </div>
22
+
23
+ <div class="file-details">
24
+ <h2 id="file-title"></h2>
25
+ <div id="operation-file">
26
+ <fluent-button id="open-file"></fluent-button>
27
+ <fluent-button id="download-file"></fluent-button>
28
+ </div>
29
+ <fluent-button id="remove-file" appearance="accent"></fluent-button>
30
+ </div>
31
+ </fluent-card>
32
+ </div>
33
+
34
+
35
+ <div id="input-content">
36
+ <fluent-text-field id="input-text"></fluent-text-field>
37
+ <fluent-button id="select-file"></fluent-button>
38
+ <fluent-button id="send-data" appearance="accent"></fluent-button>
17
39
  </div>
18
- <div id="data-list"></div>
40
+
41
+ <form id="upload-form" hidden>
42
+ <input placeholder="none" type="file" name="file" id="select-file-button-pre">
43
+ </form>
19
44
  </main>
20
- <div id="contextmenu">
21
- <ul>
22
- <li id="right-click-delete">Delete</li>
23
- </ul>
24
- </div>
25
45
  <script src="/js"></script>
26
46
  </body>
27
47
 
data/www/main.js CHANGED
@@ -11,14 +11,27 @@ function EncodeHTMLForm(data) {
11
11
 
12
12
  const content = document.getElementById("content");
13
13
  const sendButton = document.getElementById("send-button");
14
+ const selectFileButton = document.getElementById("select-file-button");
15
+ const selectFileButtonPre = document.getElementById("select-file-button-pre");
14
16
  const rightClickDelete = document.getElementById("right-click-delete");
17
+ const rightClickDeleteFile = document.getElementById("right-click-delete-file");
18
+ const rightClickCopy = document.getElementById("right-click-copy");
19
+ const rightClickDownloadFile = document.getElementById("right-click-download-file");
20
+ const contextmenuFile = document.getElementById('contextmenu-file');
21
+ const fileListWrap = document.getElementById('file-list-wrap');
22
+ const dataListWrap = document.getElementById('data-list-wrap');
23
+ const fileListTitle = document.getElementById('file-list-title');
24
+ const dataListTitle = document.getElementById('data-list-title');
25
+
15
26
  let rightClickTargetId = "";
27
+ let fileTarget;
16
28
  let data = [];
17
29
 
18
30
  const refresh = () => {
19
31
  if(data == null) return;
20
32
  const dataList = document.getElementById("data-list");
21
33
  dataList.innerHTML = "";
34
+ dataListWrap.style.display = data.length == 0 ? "none" : "block";
22
35
  for(const text of data){
23
36
  const div = document.createElement("div");
24
37
  div.innerText = text['content'];
@@ -27,15 +40,14 @@ const refresh = () => {
27
40
  dataList.prepend(div);
28
41
 
29
42
  const contextmenu = document.getElementById('contextmenu');
30
- const main = div;
31
- main.addEventListener('contextmenu', (e) => {
43
+ div.addEventListener('contextmenu', (e) => {
32
44
  e.preventDefault();
33
45
  contextmenu.style.left = e.pageX + 'px';
34
46
  contextmenu.style.top = e.pageY + 'px';
35
47
  contextmenu.style.display = 'block';
36
48
  rightClickTargetId = e.target.id
37
49
  });
38
- main.addEventListener('click', () => {
50
+ div.addEventListener('click', () => {
39
51
  contextmenu.style.display = 'none';
40
52
  });
41
53
  }
@@ -45,23 +57,44 @@ const send = (write) => {
45
57
  const xhr = new XMLHttpRequest();
46
58
  xhr.open("POST", "/cgi-bin");
47
59
  write ? xhr.send(EncodeHTMLForm({ "data": content.value })) : xhr.send(EncodeHTMLForm({ "data": "" }));
60
+ if(write && content.value != ""){
61
+ const dataList = document.getElementById("data-list");
62
+ const div = document.createElement("div");
63
+ div.innerText = content.value;
64
+ div.className = "message";
65
+ dataList.prepend(div);
66
+ }
48
67
  xhr.onreadystatechange = () => {
49
68
  let texts = JSON.parse(xhr.responseText || "null");
50
69
  if(texts == null) return;
51
70
  data = texts;
52
71
  refresh();
72
+ sendButton.disabled = false;
73
+ sendButton.classList.remove("disabled")
53
74
  }
54
75
  }
55
76
 
56
77
  const i18n = () => {
57
78
  const lang = window.navigator.language;
58
- if(lang == 'ja'){
79
+ if(lang == 'ja' || lang == 'ja-JP'){
59
80
  content.placeholder = "テキストを入力...";
60
81
  sendButton.value = "送信";
61
- rightClickDelete.innerText = "削除"
82
+ rightClickDelete.innerText = "削除";
83
+ rightClickDeleteFile.innerText = "削除";
84
+ rightClickDownloadFile.innerText = "ダウンロード";
85
+ rightClickCopy.innerText = "コピー";
86
+ selectFileButton.value = "ファイルを選択";
87
+ fileListTitle.innerText = "ファイル一覧";
88
+ dataListTitle.innerText = "テキスト一覧";
62
89
  }
63
90
  }
64
91
 
92
+ rightClickCopy.addEventListener('click', () => {
93
+ const targetDOM = document.getElementById(rightClickTargetId);
94
+ navigator.clipboard.writeText(targetDOM.textContent);
95
+ contextmenu.style.display = 'none';
96
+ });
97
+
65
98
  rightClickDelete.addEventListener('click', () => {
66
99
  const targetDOM = document.getElementById(rightClickTargetId);
67
100
  targetDOM.remove();
@@ -79,6 +112,90 @@ rightClickDelete.addEventListener('click', () => {
79
112
  contextmenu.style.display = 'none';
80
113
  });
81
114
 
115
+ document.body.addEventListener('click', () => {
116
+ contextmenu.style.display = 'none';
117
+ contextmenuFile.style.display = 'none';
118
+ });
119
+
120
+ sendButton.addEventListener("click", () => {
121
+ if(content.value != ""){
122
+ sendButton.disabled = true;
123
+ sendButton.classList.add("disabled")
124
+ send(true)
125
+ }
126
+ });
127
+
128
+ selectFileButton.addEventListener('click', () => {
129
+ selectFileButtonPre.click();
130
+ });
131
+
132
+ selectFileButtonPre.addEventListener('change', (e) => {
133
+ const form = document.getElementById("upload-form");
134
+ const formData = new FormData(form);
135
+
136
+ const xhr = new XMLHttpRequest();
137
+ xhr.open("POST", "/cgi-bin");
138
+ xhr.upload.addEventListener('loadstart', () => {
139
+ console.log("Upload: start")
140
+ });
141
+
142
+ xhr.upload.addEventListener('load', () => {
143
+ // アップロード正常終了
144
+ console.log('Upload: done');
145
+ getFiles();
146
+ });
147
+
148
+ xhr.send(formData);
149
+ });
150
+
151
+ const getFiles = () => {
152
+ const xhr = new XMLHttpRequest();
153
+ xhr.open("POST", "/cgi-bin");
154
+ xhr.send(EncodeHTMLForm({ "files": "" }));
155
+ xhr.onreadystatechange = () => {
156
+ let texts = JSON.parse(xhr.responseText || "null");
157
+ if (texts == null)
158
+ return;
159
+
160
+ const fileListUl = document.getElementById('file-list-ul');
161
+ fileListUl.innerHTML = "";
162
+
163
+ const fileList = document.getElementById('file-list');
164
+ fileList.style.display = texts.length == 0 ? "none" : "block";
165
+ fileListWrap.style.display = texts.length == 0 ? "none" : "block";
166
+
167
+ for(const fileName of texts){
168
+ const div = document.createElement('li');
169
+ div.textContent = fileName;
170
+
171
+ div.addEventListener('contextmenu', (e) => {
172
+ e.preventDefault();
173
+ contextmenuFile.style.left = e.pageX + 'px';
174
+ contextmenuFile.style.top = e.pageY + 'px';
175
+ contextmenuFile.style.display = 'block';
176
+ fileTarget = e.target;
177
+ });
178
+ div.addEventListener('click', () => {
179
+ contextmenuFile.style.display = 'none';
180
+ location.href = `/get?filename=${fileName}`
181
+ });
182
+
183
+ fileListUl.append(div);
184
+ }
185
+ };
186
+ };
187
+
188
+ rightClickDeleteFile.addEventListener("click", () => {
189
+ const fileName = fileTarget.textContent;
190
+ fileTarget.remove();
191
+ const xhr = new XMLHttpRequest();
192
+ xhr.open("POST", "/cgi-bin");
193
+ xhr.send(EncodeHTMLForm({ "removefile": fileName }));
194
+ xhr.onreadystatechange = () => {
195
+ getFiles();
196
+ };
197
+ })
198
+
82
199
  i18n();
83
200
  send(false);
84
- sendButton.addEventListener("click", () => { send(true) });
201
+ getFiles();
data/www/style.css CHANGED
@@ -1,27 +1,42 @@
1
- body{
1
+ html {
2
+ height: 100%;
3
+ }
4
+
5
+ body {
2
6
  background-color: #F3F3F3;
3
7
  font-family: Segoe UI, Meiryo UI;
8
+ min-height: 100%;
9
+ margin: 0;
4
10
  }
5
11
 
6
- main{
12
+ main {
7
13
  max-width: 980px;
8
14
  margin: 0 auto;
9
15
  }
10
16
 
11
- .Grid{
17
+ #wrap {
18
+ margin: 0 16px;
19
+ padding-top: 8px;
20
+ }
21
+
22
+ .Grid {
12
23
  display: grid;
13
24
  }
14
25
 
15
- .Text-SendButton{
16
- grid-template-columns: 1fr auto;
26
+ .Text-SendButton {
27
+ grid-template-columns: 1fr auto auto;
17
28
  margin-bottom: 8px;
18
29
  }
19
30
 
20
- .Text-SendButton .TextBox{
31
+ .Text-SendButton .TextBox {
21
32
  margin-right: 8px;
22
33
  }
23
34
 
24
- .message{
35
+ #send-button{
36
+ margin-right: 8px;
37
+ }
38
+
39
+ .message {
25
40
  margin-bottom: 8px;
26
41
  padding: 16px;
27
42
  border: solid 1px #E5E5E5;
@@ -30,8 +45,49 @@ main{
30
45
  font-size: 14px;
31
46
  }
32
47
 
48
+ #contextmenu ul li {
49
+ margin-top: 4px;
50
+ }
51
+
52
+ #contextmenu ul li.menu-top {
53
+ margin-top: 0;
54
+ }
55
+
56
+ /* ファイルリスト */
57
+ #file-list{
58
+ background-color: #F9F9F9;
59
+ padding: 8px;
60
+ margin-bottom: 8px;
61
+ border: solid 1px #E3E3E3;
62
+ border-radius: 8px;
63
+ display: none;
64
+ }
65
+
66
+ #file-list-ul{
67
+ padding: 0;
68
+ margin: 0;
69
+ list-style-type: none;
70
+ user-select: none;
71
+ }
72
+
73
+ #file-list-ul li{
74
+ border-radius: 4px;
75
+ padding: 4.75px 12px 4.75px 12px;
76
+ cursor: pointer;
77
+ margin-top: 4px;
78
+ font-size: 14px;
79
+ }
80
+
81
+ #file-list-ul li:nth-child(1){
82
+ margin-top: 0;
83
+ }
84
+
85
+ #file-list-ul li:hover{
86
+ background-color: #F0F0F0;
87
+ }
88
+
33
89
  /* 右クリックメニュー */
34
- #contextmenu {
90
+ .contextmenu {
35
91
  display: none;
36
92
  position: fixed;
37
93
  left: 0;
@@ -43,12 +99,12 @@ main{
43
99
  width: 192px;
44
100
  }
45
101
 
46
- #contextmenu ul{
102
+ .contextmenu ul {
47
103
  padding-left: 0;
48
104
  margin: 0;
49
105
  }
50
106
 
51
- #contextmenu li {
107
+ .contextmenu li {
52
108
  cursor: pointer;
53
109
  margin: 0;
54
110
  padding: 4.75px 0 4.75px 16px;
@@ -58,12 +114,16 @@ main{
58
114
  user-select: none;
59
115
  }
60
116
 
61
- #contextmenu li:hover {
117
+ .contextmenu li:hover {
62
118
  background-color: #F0F0F0;
63
119
  }
64
120
 
65
121
  /* 共通コンポーネント */
66
- .TextBox{
122
+ .Hidden {
123
+ display: none;
124
+ }
125
+
126
+ .TextBox {
67
127
  height: 27px;
68
128
  border: solid 1px #E5E5E5;
69
129
  border-bottom-color: #868686;
@@ -73,11 +133,11 @@ main{
73
133
  background-color: #FBFBFB;
74
134
  }
75
135
 
76
- .TextBox:hover{
136
+ .TextBox:hover {
77
137
  background-color: #F6F6F6;
78
138
  }
79
139
 
80
- .TextBox:focus{
140
+ .TextBox:focus {
81
141
  background-color: white;
82
142
  height: 27px;
83
143
  border-bottom-color: #0067C0;
@@ -86,7 +146,8 @@ main{
86
146
  padding-bottom: 1px;
87
147
  }
88
148
 
89
- .Button{
149
+ .Button {
150
+ cursor: pointer;
90
151
  height: 32px;
91
152
  border: solid 1px #E5E5E5;
92
153
  border-bottom-color: #CCCCCC;
@@ -95,32 +156,40 @@ main{
95
156
  padding: 1px 12px;
96
157
  }
97
158
 
98
- .Button:hover{
159
+ .Button:hover {
99
160
  background-color: #F6F6F6;
100
161
  }
101
162
 
102
- .Button:active{
163
+ .Button:active {
103
164
  background-color: #F5F5F5;
104
165
  color: #5D5D5D;
105
166
  border-color: #E5E5E5;
106
167
  }
107
168
 
108
- .Button.Accent{
169
+ .Button.Accent {
109
170
  background-color: #0067C0;
110
171
  border: solid 1px #1473C5;
111
172
  border-bottom-color: #003E73;
112
173
  color: white;
113
174
  }
114
175
 
115
- .Button.Accent:hover{
176
+ .Button.Accent:hover {
116
177
  background-color: #1975C5;
117
178
  border: solid 1px #2B80CA;
118
179
  border-bottom-color: #0F4676;
119
180
  }
120
181
 
121
- .Button.Accent:active{
182
+ .Button.Accent:active {
122
183
  background-color: #3183CA;
123
184
  border: solid 1px #3183CA;
124
185
  border-bottom-color: #3183CA;
125
186
  color: #C2DAEF;
126
187
  }
188
+
189
+ .Button.disabled, .Button.disabled:hover {
190
+ cursor: not-allowed;
191
+ background-color: #F5F5F5 !important;
192
+ color: #9D9D9D;
193
+ border: solid 1px #E5E5E5 !important;
194
+ border-bottom-color: #E5E5E5 !important;
195
+ }