forked from SheepTester/words-go-here
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
267 lines (257 loc) · 11.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<!DOCTYPE html>
<html lang="en">
<head>
<title>SB3转HTML</title>
<meta charset="UTF-8">
<meta name="description" content="将一个Scratch项目转换为HTML文件"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="./hacky-file-getter.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="toggle.css">
<link rel="stylesheet" type="text/css" href="radio.css">
<style media="screen">
@font-face {
font-family: '幼圆';
src: url('assets/幼圆.ttf') format('truetype');
}
body {
margin:16px;
font-family: 幼圆;
color: white;
background-image: linear-gradient(to bottom right, #6D15A1, #C763E8);
background-size: cover;
background-repeat: no-repeat;
}
/* ========== Tooltips ========== */
u {
border-bottom: 2px dotted white;
text-decoration: none;
}
u:hover {
cursor: help;
}
#load-no-minify {
color: white;
padding: 4px 26px;
font: 1.2em 幼圆;
background-color: transparent;
border: 2px solid white;
border-radius: 100px;
}
#load-no-minify:hover, #load-no-minify:disabled {
border: 2px solid transparent;
background-color: rgba(255, 255, 255, 0.2);
}
/* ========== Some A U T I S T I C Tweaks ========== */
input[type="text"], input[type="number"], textarea {
padding: 6px 10px;
border-radius: 6px;
background-color: rgba(255, 255, 255, 0.2);
border: 0px;
resize: none;
}
.seperate {
padding: 0px 8px;
border-left: 1px solid white;
}
</style>
</head>
<body>
<!-- Everything, probably -->
<script src="scratch-vm/vm.min.js"></script>
<script src="./hacky-file-getter.js" charset="utf-8"></script>
<!-- This HTML file is truly ugly. -->
<h1>Scratch 3 项目转换器 <img style="float: right;" src="assets/Scratchcat2.svg" alt="the scartch mascot cat"></img></h1>
<h3><em>将一个Scratch项目转换为HTML文件</em></h3>
<p>本程序可将您的Scratch项目打包成一个能在浏览器里独立运行的HTML文件。</p>
<p>生成的HTML文件会比较大,因为这个文件包含了整个Scratch引擎和项目里的造型和声音素材。</p>
<p>转换完的项目会自动运行,所以没有绿旗和停止按钮。</p>
<p>云变量会保存在<u title="您在Scratch以外使用cookie还是得遵守隐私法律!">电脑本地</u>且允许存储字符串值,方便用来保存游戏数据。</p>
<p><label class="container"><input type="radio" name="upload-mode" value="id" checked><span class="checkmark"></span> 项目ID: <input type="number" placeholder="项目ID" value="284516654" id="id"> (Scratch官网上项目网址最后面的一串数字)</label></p>
<p><label class="container"><input type="radio" name="upload-mode" value="file"><span class="checkmark"></span> 上传项目(sb3)文件: <input type="file" id="file" accept=".sb,.sb2,.sb3"></label></p>
<fieldset style="border: 1px solid white; border-radius: 6px">
<legend> 选项 </legend>
<p><label for="title">项目名称: </label><input type="text" placeholder="项目名称" value="Zombie Cube Escape!" id="title"> (浏览器窗口标题)</p>
<p><label for="username">用户名的取值: </label><input type="text" placeholder="用户名" value="griffpatch" id="username"> (“用户名”积木块的返回值)</p>
<table style="border:none">
<tr>
<td style="padding-right: 8px;"><label class="switch"><input type="checkbox" id="compatibility" checked><span class="slider"></span></label> 启用<u title="像Scratch 2.0一样强制将项目限速在30FPS以下">兼容模式</u></td>
<td class="seperate"><label class="switch"><input type="checkbox" id="turbo"><span class="slider"></span></label> 启用<a href="https://en.scratch-wiki.info/wiki/Turbo_Mode" target="_blank">加速模式</a></td>
<td class="seperate"><label class="switch"><input type="checkbox" id="progress" checked><span class="slider"></span></label> 显示加载进度条</td>
<td class="seperate"><label class="switch"><input type="checkbox" id="fullscreen" checked><span class="slider"></span></label> 显示全屏按钮</td>
</tr>
</table>
<p><label class="switch"><input type="checkbox" id="use-colour"><span class="slider"></span></label> 自定义变量、链表监视器颜色: <input type="color" id="monitor-colour" value="#ff8c1a"> (如果不选,将默认使用半透明黑色。)</p>
<p><label class="switch"><input type="checkbox" id="wider"><span class="slider"></span></label> 本项目是用<a href="https://sheeptester.github.io/scratch-gui/?width=640&height=360" target="_blank">E羊icques</a>的16比9宽屏模式开发的</p>
</fieldset>
<p>
<button id="load-no-minify">转换▶</button>
</p>
<textarea id="error" rows="8" cols="80" placeholder="状态输出" readonly></textarea>
<script type="text/javascript">
/* fetch API replaced by NodeJS 'fs' module's readFile method
In order to patch the file loading bug. -David 2020 */
const fs = require("fs");
/* Importing some electron functionalities */
const { remote } = require("electron");
// Promise-fy readFile method
function readFileAsync(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, "utf-8", (err, buffer) => {
if (err) reject(err); else resolve(buffer);
});
});
};
// NO FILE DRAG'N'DROPPING ALLOWED - YET!
window.addEventListener("dragover", function (e) {
e.preventDefault();
}, false);
window.addEventListener("drop", function (e) {
e.preventDefault();
}, false);
function removePercentSection(str, key) {
/*
performs the following on str:
% key %
this part (and other parts surrounded in a similar fashion) will be removed
% /key %
returns str with the parts removed
*/
const startKey = `% ${key} %`;
const endKey = `% /${key} %`;
while (str.includes(startKey) && str.includes(endKey)) {
str = str.slice(0, str.indexOf(startKey))
+ str.slice(str.indexOf(endKey) + endKey.length);
}
return str;
}
function getDataURL(url) {
return fetch(url).then(r => r.blob()).then(blob => new Promise(res => {
const reader = new FileReader();
reader.onload = e => res(e.target.result);
reader.readAsDataURL(blob);
}));
}
function downloadAsHTML(projectSrc, {
title = 'Project',
username = 'griffpatch',
ratio16to9 = false,
progressBar = true,
fullscreen = true,
log = console.log,
monitorColour = null
} = {}) {
log('正在拉取资源...');
return Promise.all([
// make preface variables
projectSrc.id
? runBenchmark(projectSrc.id, ({complete, total}) => log(complete + '/' + total))
.then(({assets, projectJSON}) => {
log('正在组装资源...');
return Promise.all([
getDataURL(projectJSON).then(data => projectJSON = data),
...Object.keys(assets).map(assetId => getDataURL(assets[assetId]).then(data => assets[assetId] = data))
]).then(() => {
return `var SRC = "id", PROJECT_JSON = "${projectJSON}",`
+ `ASSETS = ${JSON.stringify(assets)},`;
});
})
: Promise.resolve(`var SRC = "file", FILE = "${projectSrc.data}",`),
// fetch scripts
readFileAsync(ratio16to9 ? 'resources/app.asar/scratch-vm/16-9/vm.min.js' : 'resources/app.asar/scratch-vm/vm.min.js')
.then(vmCode => {
log('成功获得Scratch引擎...');
// console.log(vmCode);
// remove dumb script end tags in comments
return vmCode.replace('</scr' + 'ipt>', '');
}),
// fetch template
readFileAsync('resources/app.asar/template.html')
]).then(([preface, scripts, template]) => {
scripts = preface
+ `DESIRED_USERNAME = ${JSON.stringify(username)},`
+ `COMPAT = ${compatibility.checked}, TURBO = ${turbo.checked};`
+ scripts;
log('完成!');
template = removePercentSection(template, 'no-vm');
if (ratio16to9) template = removePercentSection(template, '4-3');
else template = removePercentSection(template, '16-9');
if (!progressBar) template = removePercentSection(template, 'loading-progress');
if (!fullscreen) template = removePercentSection(template, 'fullscreen');
if (monitorColour) template = template.replace('{COLOUR}', () => monitorColour);
else template = removePercentSection(template, 'monitor-colour');
return template
.replace(/% \/?[a-z0-9-]+ %/g, '')
// .replace(/\s*\r?\n\s*/g, '')
.replace('{TITLE}', () => title)
.replace('{SCRIPTS}', () => scripts);
});
}
const loadNoMinifyBtn = document.getElementById('load-no-minify');
const title = document.getElementById('title');
const id = document.getElementById('id');
const username = document.getElementById('username');
const file = document.getElementById('file');
const error = document.getElementById('error');
const compatibility = document.getElementById('compatibility');
const turbo = document.getElementById('turbo');
const wider = document.getElementById('wider');
const progressBar = document.getElementById('progress');
const fullscreen = document.getElementById('fullscreen');
const useColour = document.getElementById('use-colour');
const monitorColour = document.getElementById('monitor-colour');
function getProject() {
if (document.querySelector('input[name="upload-mode"]:checked').value === 'file') {
if (!file.files || !file.files[0]) return Promise.reject(new Error('没有选择任何文件'));
return new Promise(res => {
const reader = new FileReader();
reader.onload = () => res({data: reader.result});
reader.readAsDataURL(file.files[0]);
});
} else {
return Promise.resolve({id: id.value});
}
}
function load() {
loadNoMinifyBtn.disabled = true;
error.value = '';
getProject()
.then(src => downloadAsHTML(src, {
title: title.value,
username: username.value,
log: output => {
error.value += output + '\n';
error.scrollTop = error.scrollHeight;
},
ratio16to9: wider.checked,
progressBar: progressBar.checked,
fullscreen: fullscreen.checked,
monitorColour: useColour.checked ? monitorColour.value : null
}))
.then(html => {
remote.dialog.showSaveDialog(remote.getCurrentWindow(), {
title: "将HTML文件保存至...",
defaultPath: "project.html"
}).then(res => {
var fPath = res.filePath;
if (fPath === undefined) { fPath = "./project.html" } // default save in same dir, in case user chose nothing
fs.writeFileSync(fPath, html, "utf-8");
loadNoMinifyBtn.disabled = false;
});
}).catch(err => {
console.log(err);
error.value = err.message;
loadNoMinifyBtn.disabled = false;
});
}
loadNoMinifyBtn.addEventListener('click', load);
file.addEventListener('change', e => {
if (file.files[0]) {
document.querySelector('input[name="upload-mode"][value="file"]').checked = true;
}
});
monitorColour.addEventListener('change', e => {
useColour.checked = true;
});
</script>
</body>
</html>