sora 24.02.25 → 24.02.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +16 -0
- data/cgi-bin/get.rb +61 -0
- data/cgi-bin/main.rb +56 -0
- data/data/.gitignore +2 -1
- data/exe/sora +2 -2
- data/lib/sora/version.rb +1 -1
- data/lib/sora.rb +1 -0
- data/www/index.html +32 -7
- data/www/main.js +123 -6
- data/www/style.css +89 -20
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 488c9fe33a89000965087732638fafd292137467592fb99dc5341b45665a8b8a
|
4
|
+
data.tar.gz: 28c98019e9d9c51260264aec86c558fbc53538ae0aab46da688cd11a5623ee91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 022c2e1247b89595b5e0679739d4d99980d049a782b9ed1f4eaebedd89f92ea765cfc31afa5322dad4697ce45621502d376ed8d126d346f147257a935dd44b25
|
7
|
+
data.tar.gz: '0798d5c364a33d00dfd422badd7f544f1175ed09ce158294c11f5e881d1a3eae743e169301d2be4a7973da1920d8dab69a57018928f110184a80917d4e3edcfb'
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -2,3 +2,19 @@
|
|
2
2
|
sora is an http server in Ruby that can send and receive strings and files.
|
3
3
|
|
4
4
|
sora (Server Of Ruby for Access) can send and receive files, and transmit text strings to communicate information.
|
5
|
+
|
6
|
+
- https://rubygems.org/gems/sora
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
This gem is supported on Windows.
|
10
|
+
|
11
|
+
### How to Install
|
12
|
+
```ps1
|
13
|
+
gem install sora
|
14
|
+
```
|
15
|
+
|
16
|
+
or
|
17
|
+
|
18
|
+
```ps1
|
19
|
+
rake install # Install from repository
|
20
|
+
```
|
data/cgi-bin/get.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
require "json"
|
5
|
+
require "securerandom"
|
6
|
+
|
7
|
+
def get_content_type(filename)
|
8
|
+
content_types = {
|
9
|
+
".txt" => "text/plain",
|
10
|
+
".json" => "application/json",
|
11
|
+
".js" => "text/javascript",
|
12
|
+
".css" => "text/css",
|
13
|
+
".csv" => "text/csv",
|
14
|
+
".pdf" => "application/pdf",
|
15
|
+
".xls" => "application/vnd.ms-excel",
|
16
|
+
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
17
|
+
".doc" => "application/msword",
|
18
|
+
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
19
|
+
".ppt" => "application/vnd.ms-powerpoint",
|
20
|
+
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
21
|
+
".gif" => "image/gif",
|
22
|
+
".bmp" => "image/bmp",
|
23
|
+
".svg" => "image/svg+xml",
|
24
|
+
".zip" => "application/zip",
|
25
|
+
".lzh" => "application/x-lzh",
|
26
|
+
".tar" => "application/x-tar",
|
27
|
+
".mp3" => "audio/mpeg",
|
28
|
+
".mp4" => "video/mp4",
|
29
|
+
".mpeg" => "video/mpeg",
|
30
|
+
".jpg" => "image/jpeg",
|
31
|
+
".jpeg" => "image/jpeg",
|
32
|
+
".png" => "image/png",
|
33
|
+
".html" => "text/html"
|
34
|
+
}
|
35
|
+
|
36
|
+
ext = File.extname(filename)
|
37
|
+
content_types[ext] || "text/html"
|
38
|
+
end
|
39
|
+
|
40
|
+
def file
|
41
|
+
cgi = CGI.new
|
42
|
+
params = cgi.params
|
43
|
+
unless params.key?("filename")
|
44
|
+
print cgi.header("text/plain")
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
file_path = File.join(File.expand_path("../data"), params["filename"][0])
|
49
|
+
File.open(file_path, "rb") do |f|
|
50
|
+
cgi.out(
|
51
|
+
{
|
52
|
+
"type" => get_content_type(file_path),
|
53
|
+
"content-disposition" => "inline; filename=#{File.basename(file_path)}"
|
54
|
+
}
|
55
|
+
) do
|
56
|
+
f.read
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
file
|
data/cgi-bin/main.rb
CHANGED
@@ -7,8 +7,15 @@ require "securerandom"
|
|
7
7
|
cgi = CGI.new
|
8
8
|
print cgi.header("application/json")
|
9
9
|
params = cgi.params
|
10
|
+
|
10
11
|
@json_file_path = "../data/data.json"
|
11
12
|
|
13
|
+
def log(text)
|
14
|
+
open("tmp", "a") do |f|
|
15
|
+
f.puts(text)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
12
19
|
def read_json(filename)
|
13
20
|
unless File.exist? filename
|
14
21
|
File.open(filename, "w") do |f|
|
@@ -57,5 +64,54 @@ def delete(params)
|
|
57
64
|
print JSON.pretty_generate(response)
|
58
65
|
end
|
59
66
|
|
67
|
+
def save_file(params)
|
68
|
+
return unless params.key?("file")
|
69
|
+
|
70
|
+
file = params["file"][0]
|
71
|
+
response = {
|
72
|
+
filename: file.original_filename
|
73
|
+
}
|
74
|
+
|
75
|
+
return if [".gitignore", "data.json"].include?(response[:filename])
|
76
|
+
|
77
|
+
file_path = File.join(File.expand_path("../data"), response[:filename])
|
78
|
+
|
79
|
+
File.open(file_path, "wb") do |f|
|
80
|
+
f.write file.read
|
81
|
+
end
|
82
|
+
|
83
|
+
print JSON.pretty_generate(response)
|
84
|
+
end
|
85
|
+
|
86
|
+
def remove_file(params)
|
87
|
+
return unless params.key?("removefile")
|
88
|
+
|
89
|
+
removefile = params["removefile"][0]
|
90
|
+
response = {
|
91
|
+
filename: removefile,
|
92
|
+
status: "NG"
|
93
|
+
}
|
94
|
+
|
95
|
+
return if [".gitignore", "data.json"].include?(response[:filename])
|
96
|
+
|
97
|
+
file_path = File.join(File.expand_path("../data"), response[:filename])
|
98
|
+
|
99
|
+
response[:status] = "OK" if File.exist?(file_path) && File.delete(file_path)
|
100
|
+
|
101
|
+
print JSON.pretty_generate(response)
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_files(params)
|
105
|
+
return unless params.key?("files")
|
106
|
+
|
107
|
+
response = Dir.glob(File.join(File.expand_path("../data"), "*"))
|
108
|
+
response.map! { |e| File.basename(e) }
|
109
|
+
response.filter! { |e| !["data.json", ".gitignore"].include?(e) }
|
110
|
+
print JSON.pretty_generate(response)
|
111
|
+
end
|
112
|
+
|
60
113
|
get_json(params)
|
61
114
|
delete(params)
|
115
|
+
save_file(params)
|
116
|
+
get_files(params)
|
117
|
+
remove_file(params)
|
data/data/.gitignore
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
!.gitignore
|
2
|
+
*
|
data/exe/sora
CHANGED
@@ -6,9 +6,9 @@ require "optparse"
|
|
6
6
|
|
7
7
|
opt = OptionParser.new
|
8
8
|
|
9
|
-
params = { port: 8080, document_root: File.expand_path("
|
9
|
+
params = { port: 8080, document_root: File.expand_path("../www", __dir__) }
|
10
10
|
|
11
|
-
opt.on("-p [PORT]", "--port [PORT]") { |v| params[:port] = v.to_i }
|
11
|
+
opt.on("-p [PORT]", "--port [PORT]", "Port number, default: 8080") { |v| params[:port] = v.to_i }
|
12
12
|
|
13
13
|
opt.parse!(ARGV)
|
14
14
|
|
data/lib/sora/version.rb
CHANGED
data/lib/sora.rb
CHANGED
@@ -19,6 +19,7 @@ module Sora
|
|
19
19
|
}
|
20
20
|
)
|
21
21
|
srv.mount("/cgi-bin", WEBrick::HTTPServlet::CGIHandler, "#{document_root}/../cgi-bin/main.rb")
|
22
|
+
srv.mount("/get", WEBrick::HTTPServlet::CGIHandler, "#{document_root}/../cgi-bin/get.rb")
|
22
23
|
srv.mount("/", WEBrick::HTTPServlet::FileHandler, "#{document_root}/index.html")
|
23
24
|
srv.mount("/style", WEBrick::HTTPServlet::FileHandler, "#{document_root}/style.css")
|
24
25
|
srv.mount("/js", WEBrick::HTTPServlet::FileHandler, "#{document_root}/main.js")
|
data/www/index.html
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html>
|
2
|
+
<html lang="ja">
|
3
3
|
|
4
4
|
<head>
|
5
5
|
<meta charset="utf-8">
|
@@ -10,18 +10,43 @@
|
|
10
10
|
|
11
11
|
<body>
|
12
12
|
<main id="main">
|
13
|
-
<
|
14
|
-
|
15
|
-
<
|
16
|
-
|
13
|
+
<div id="wrap">
|
14
|
+
<h1 id="title-sora">sora</h1>
|
15
|
+
<div class="Grid Text-SendButton">
|
16
|
+
<input id="content" placeholder="Text..." class="TextBox">
|
17
|
+
<input type="button" id="send-button" value="Send" class="Accent Button SendButton">
|
18
|
+
<input value="Select a File" type="button" id="select-file-button" class="Button">
|
19
|
+
<form id="upload-form" class="Hidden">
|
20
|
+
<input placeholder="none" type="file" name="file" id="select-file-button-pre">
|
21
|
+
</form>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div id="file-list-wrap" class="Hidden">
|
25
|
+
<h2 id="file-list-title">Files</h2>
|
26
|
+
<div id="file-list">
|
27
|
+
<ul id="file-list-ul"></ul>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div id="data-list-wrap" class="Hidden">
|
32
|
+
<h2 id="data-list-title">Texts</h2>
|
33
|
+
<div id="data-list">
|
34
|
+
</div>
|
35
|
+
</div>
|
17
36
|
</div>
|
18
|
-
<div id="data-list"></div>
|
19
37
|
</main>
|
20
|
-
<div id="contextmenu">
|
38
|
+
<div id="contextmenu" class="contextmenu">
|
21
39
|
<ul>
|
40
|
+
<li id="right-click-copy" class="menu-top">Copy</li>
|
22
41
|
<li id="right-click-delete">Delete</li>
|
23
42
|
</ul>
|
24
43
|
</div>
|
44
|
+
<div id="contextmenu-file" class="contextmenu">
|
45
|
+
<ul>
|
46
|
+
<li id="right-click-download-file" class="menu-top Hidden">Download</li>
|
47
|
+
<li id="right-click-delete-file">Delete</li>
|
48
|
+
</ul>
|
49
|
+
</div>
|
25
50
|
<script src="/js"></script>
|
26
51
|
</body>
|
27
52
|
|
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
|
-
|
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
|
-
|
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
|
-
|
201
|
+
getFiles();
|
data/www/style.css
CHANGED
@@ -1,27 +1,42 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
102
|
+
.contextmenu ul {
|
47
103
|
padding-left: 0;
|
48
104
|
margin: 0;
|
49
105
|
}
|
50
106
|
|
51
|
-
|
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
|
-
|
117
|
+
.contextmenu li:hover {
|
62
118
|
background-color: #F0F0F0;
|
63
119
|
}
|
64
120
|
|
65
121
|
/* 共通コンポーネント */
|
66
|
-
.
|
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
|
+
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sora
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 24.02.
|
4
|
+
version: 24.02.26
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MURATA Mitsuharu
|
@@ -9,7 +9,21 @@ autorequire:
|
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-02-25 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webrick
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
13
27
|
description: sora (Server Of Ruby for Access) can send and receive files, and transmit
|
14
28
|
text strings to communicate information.
|
15
29
|
email:
|
@@ -25,6 +39,7 @@ files:
|
|
25
39
|
- LICENSE.txt
|
26
40
|
- README.md
|
27
41
|
- Rakefile
|
42
|
+
- cgi-bin/get.rb
|
28
43
|
- cgi-bin/main.rb
|
29
44
|
- data/.gitignore
|
30
45
|
- exe/sora
|