|
2 | 2 | <html>
|
3 | 3 | <head>
|
4 | 4 | <title>MicroPython WebREPL</title>
|
5 |
| -<!-- |
6 |
| - term.js |
7 |
| - Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) |
8 |
| - Copyright (c) 2016, Paul Sokolovsky |
9 |
| ---> |
10 |
| -<style> |
11 |
| - html { |
12 |
| - background: #555; |
13 |
| - } |
14 |
| - |
15 |
| - h1 { |
16 |
| - margin-bottom: 20px; |
17 |
| - font: 20px/1.5 sans-serif; |
18 |
| - } |
19 |
| - |
20 |
| -/* |
21 |
| - .terminal { |
22 |
| - float: left; |
23 |
| - border: #000 solid 5px; |
24 |
| - font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; |
25 |
| - font-size: 11px; |
26 |
| - color: #f0f0f0; |
27 |
| - background: #000; |
28 |
| - } |
29 |
| -
|
30 |
| - .terminal-cursor { |
31 |
| - color: #000; |
32 |
| - background: #f0f0f0; |
33 |
| - } |
34 |
| -*/ |
35 |
| - |
36 |
| - .file-box { |
37 |
| - margin: 4px; |
38 |
| - padding: 4px; |
39 |
| - background: #888; |
40 |
| - } |
41 |
| -</style> |
| 5 | +<link rel="stylesheet" href="webrepl.css"> |
42 | 6 | <script src="term.js"></script>
|
43 | 7 | <script src="FileSaver.js"></script>
|
44 | 8 | </head>
|
|
77 | 41 | <i>To paste, press Ctrl+A, then Ctrl+V</i>
|
78 | 42 | </body>
|
79 | 43 |
|
80 |
| -<script> |
81 |
| -; |
82 |
| - |
83 |
| -var term; |
84 |
| -var ws; |
85 |
| -var connected = false; |
86 |
| -var binary_state = 0; |
87 |
| -var put_file_name = null; |
88 |
| -var put_file_data = null; |
89 |
| -var get_file_name = null; |
90 |
| -var get_file_data = null; |
91 |
| - |
92 |
| -function calculate_size(win) { |
93 |
| - var cols = Math.max(80, Math.min(150, (win.innerWidth - 280) / 7)) | 0; |
94 |
| - var rows = Math.max(24, Math.min(80, (win.innerHeight - 180) / 12)) | 0; |
95 |
| - return [cols, rows]; |
96 |
| -} |
97 |
| - |
98 |
| -(function() { |
99 |
| - window.onload = function() { |
100 |
| - var url = window.location.hash.substring(1); |
101 |
| - if (url) { |
102 |
| - document.getElementById('url').value = 'ws://' + url; |
103 |
| - } |
104 |
| - var size = calculate_size(self); |
105 |
| - term = new Terminal({ |
106 |
| - cols: size[0], |
107 |
| - rows: size[1], |
108 |
| - useStyle: true, |
109 |
| - screenKeys: true, |
110 |
| - cursorBlink: false |
111 |
| - }); |
112 |
| - term.open(document.getElementById("term")); |
113 |
| - show_https_warning(); |
114 |
| - }; |
115 |
| - window.addEventListener('resize', function() { |
116 |
| - var size = calculate_size(self); |
117 |
| - term.resize(size[0], size[1]); |
118 |
| - }); |
119 |
| -}).call(this); |
120 |
| - |
121 |
| -function show_https_warning() { |
122 |
| - if (window.location.protocol == 'https:') { |
123 |
| - var warningDiv = document.createElement('div'); |
124 |
| - warningDiv.style.cssText = 'background:#f99;padding:5px;margin-bottom:10px;line-height:1.5em;text-align:center'; |
125 |
| - warningDiv.innerHTML = [ |
126 |
| - 'At this time, the WebREPL client cannot be accessed over HTTPS connections.', |
127 |
| - 'Use a HTTP connection, eg. <a href="http://micropython.org/webrepl/">http://micropython.org/webrepl/</a>.', |
128 |
| - 'Alternatively, download the files from <a href="https://github.com/micropython/webrepl">GitHub</a> and run them locally.' |
129 |
| - ].join('<br>'); |
130 |
| - document.body.insertBefore(warningDiv, document.body.childNodes[0]); |
131 |
| - term.resize(term.cols, term.rows - 7); |
132 |
| - } |
133 |
| -} |
134 |
| - |
135 |
| -function button_click() { |
136 |
| - if (connected) { |
137 |
| - ws.close(); |
138 |
| - } else { |
139 |
| - document.getElementById('url').disabled = true; |
140 |
| - document.getElementById('button').value = "Disconnect"; |
141 |
| - connected = true; |
142 |
| - connect(document.getElementById('url').value); |
143 |
| - } |
144 |
| -} |
145 |
| - |
146 |
| -function prepare_for_connect() { |
147 |
| - document.getElementById('url').disabled = false; |
148 |
| - document.getElementById('button').value = "Connect"; |
149 |
| -} |
150 |
| - |
151 |
| -function update_file_status(s) { |
152 |
| - document.getElementById('file-status').innerHTML = s; |
153 |
| -} |
154 |
| - |
155 |
| -function connect(url) { |
156 |
| - window.location.hash = url.substring(5); |
157 |
| - ws = new WebSocket(url); |
158 |
| - ws.binaryType = 'arraybuffer'; |
159 |
| - ws.onopen = function() { |
160 |
| - term.removeAllListeners('data'); |
161 |
| - term.on('data', function(data) { |
162 |
| - // Pasted data from clipboard will likely contain |
163 |
| - // LF as EOL chars. |
164 |
| - data = data.replace(/\n/g, "\r"); |
165 |
| - ws.send(data); |
166 |
| - }); |
167 |
| - |
168 |
| - term.on('title', function(title) { |
169 |
| - document.title = title; |
170 |
| - }); |
171 |
| - |
172 |
| - term.focus(); |
173 |
| - term.element.focus(); |
174 |
| - term.write('\x1b[31mWelcome to MicroPython!\x1b[m\r\n'); |
175 |
| - |
176 |
| - ws.onmessage = function(event) { |
177 |
| - if (event.data instanceof ArrayBuffer) { |
178 |
| - var data = new Uint8Array(event.data); |
179 |
| - switch (binary_state) { |
180 |
| - case 11: |
181 |
| - // first response for put |
182 |
| - if (decode_resp(data) == 0) { |
183 |
| - // send file data in chunks |
184 |
| - for (var offset = 0; offset < put_file_data.length; offset += 1024) { |
185 |
| - ws.send(put_file_data.slice(offset, offset + 1024)); |
186 |
| - } |
187 |
| - binary_state = 12; |
188 |
| - } |
189 |
| - break; |
190 |
| - case 12: |
191 |
| - // final response for put |
192 |
| - if (decode_resp(data) == 0) { |
193 |
| - update_file_status('Sent ' + put_file_name + ', ' + put_file_data.length + ' bytes'); |
194 |
| - } else { |
195 |
| - update_file_status('Failed sending ' + put_file_name); |
196 |
| - } |
197 |
| - binary_state = 0; |
198 |
| - break; |
199 |
| - |
200 |
| - case 21: |
201 |
| - // first response for get |
202 |
| - if (decode_resp(data) == 0) { |
203 |
| - binary_state = 22; |
204 |
| - var rec = new Uint8Array(1); |
205 |
| - rec[0] = 0; |
206 |
| - ws.send(rec); |
207 |
| - } |
208 |
| - break; |
209 |
| - case 22: { |
210 |
| - // file data |
211 |
| - var sz = data[0] | (data[1] << 8); |
212 |
| - if (data.length == 2 + sz) { |
213 |
| - // we assume that the data comes in single chunks |
214 |
| - if (sz == 0) { |
215 |
| - // end of file |
216 |
| - binary_state = 23; |
217 |
| - } else { |
218 |
| - // accumulate incoming data to get_file_data |
219 |
| - var new_buf = new Uint8Array(get_file_data.length + sz); |
220 |
| - new_buf.set(get_file_data); |
221 |
| - new_buf.set(data.slice(2), get_file_data.length); |
222 |
| - get_file_data = new_buf; |
223 |
| - update_file_status('Getting ' + get_file_name + ', ' + get_file_data.length + ' bytes'); |
224 |
| - |
225 |
| - var rec = new Uint8Array(1); |
226 |
| - rec[0] = 0; |
227 |
| - ws.send(rec); |
228 |
| - } |
229 |
| - } else { |
230 |
| - binary_state = 0; |
231 |
| - } |
232 |
| - break; |
233 |
| - } |
234 |
| - case 23: |
235 |
| - // final response |
236 |
| - if (decode_resp(data) == 0) { |
237 |
| - update_file_status('Got ' + get_file_name + ', ' + get_file_data.length + ' bytes'); |
238 |
| - saveAs(new Blob([get_file_data], {type: "application/octet-stream"}), get_file_name); |
239 |
| - } else { |
240 |
| - update_file_status('Failed getting ' + get_file_name); |
241 |
| - } |
242 |
| - binary_state = 0; |
243 |
| - break; |
244 |
| - case 31: |
245 |
| - // first (and last) response for GET_VER |
246 |
| - console.log('GET_VER', data); |
247 |
| - binary_state = 0; |
248 |
| - break; |
249 |
| - } |
250 |
| - } |
251 |
| - term.write(event.data); |
252 |
| - }; |
253 |
| - }; |
254 |
| - |
255 |
| - ws.onclose = function() { |
256 |
| - connected = false; |
257 |
| - if (term) { |
258 |
| - term.write('\x1b[31mDisconnected\x1b[m\r\n'); |
259 |
| - } |
260 |
| - term.off('data'); |
261 |
| - prepare_for_connect(); |
262 |
| - } |
263 |
| -} |
264 |
| - |
265 |
| -function decode_resp(data) { |
266 |
| - if (data[0] == 'W'.charCodeAt(0) && data[1] == 'B'.charCodeAt(0)) { |
267 |
| - var code = data[2] | (data[3] << 8); |
268 |
| - return code; |
269 |
| - } else { |
270 |
| - return -1; |
271 |
| - } |
272 |
| -} |
273 |
| - |
274 |
| -function put_file() { |
275 |
| - var dest_fname = put_file_name; |
276 |
| - var dest_fsize = put_file_data.length; |
277 |
| - |
278 |
| - // WEBREPL_FILE = "<2sBBQLH64s" |
279 |
| - var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); |
280 |
| - rec[0] = 'W'.charCodeAt(0); |
281 |
| - rec[1] = 'A'.charCodeAt(0); |
282 |
| - rec[2] = 1; // put |
283 |
| - rec[3] = 0; |
284 |
| - rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0; |
285 |
| - rec[12] = dest_fsize & 0xff; rec[13] = (dest_fsize >> 8) & 0xff; rec[14] = (dest_fsize >> 16) & 0xff; rec[15] = (dest_fsize >> 24) & 0xff; |
286 |
| - rec[16] = dest_fname.length & 0xff; rec[17] = (dest_fname.length >> 8) & 0xff; |
287 |
| - for (var i = 0; i < 64; ++i) { |
288 |
| - if (i < dest_fname.length) { |
289 |
| - rec[18 + i] = dest_fname.charCodeAt(i); |
290 |
| - } else { |
291 |
| - rec[18 + i] = 0; |
292 |
| - } |
293 |
| - } |
294 |
| - |
295 |
| - // initiate put |
296 |
| - binary_state = 11; |
297 |
| - update_file_status('Sending ' + put_file_name + '...'); |
298 |
| - ws.send(rec); |
299 |
| -} |
300 |
| - |
301 |
| -function get_file() { |
302 |
| - var src_fname = document.getElementById('get_filename').value; |
303 |
| - |
304 |
| - // WEBREPL_FILE = "<2sBBQLH64s" |
305 |
| - var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); |
306 |
| - rec[0] = 'W'.charCodeAt(0); |
307 |
| - rec[1] = 'A'.charCodeAt(0); |
308 |
| - rec[2] = 2; // get |
309 |
| - rec[3] = 0; |
310 |
| - rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0; |
311 |
| - rec[12] = 0; rec[13] = 0; rec[14] = 0; rec[15] = 0; |
312 |
| - rec[16] = src_fname.length & 0xff; rec[17] = (src_fname.length >> 8) & 0xff; |
313 |
| - for (var i = 0; i < 64; ++i) { |
314 |
| - if (i < src_fname.length) { |
315 |
| - rec[18 + i] = src_fname.charCodeAt(i); |
316 |
| - } else { |
317 |
| - rec[18 + i] = 0; |
318 |
| - } |
319 |
| - } |
320 |
| - |
321 |
| - // initiate get |
322 |
| - binary_state = 21; |
323 |
| - get_file_name = src_fname; |
324 |
| - get_file_data = new Uint8Array(0); |
325 |
| - update_file_status('Getting ' + get_file_name + '...'); |
326 |
| - ws.send(rec); |
327 |
| -} |
328 |
| - |
329 |
| -function get_ver() { |
330 |
| - // WEBREPL_REQ_S = "<2sBBQLH64s" |
331 |
| - var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); |
332 |
| - rec[0] = 'W'.charCodeAt(0); |
333 |
| - rec[1] = 'A'.charCodeAt(0); |
334 |
| - rec[2] = 3; // GET_VER |
335 |
| - // rest of "rec" is zero |
336 |
| - |
337 |
| - // initiate GET_VER |
338 |
| - binary_state = 31; |
339 |
| - ws.send(rec); |
340 |
| -} |
341 |
| - |
342 |
| -function handle_put_file_select(evt) { |
343 |
| - // The event holds a FileList object which is a list of File objects, |
344 |
| - // but we only support single file selection at the moment. |
345 |
| - var files = evt.target.files; |
346 |
| - |
347 |
| - // Get the file info and load its data. |
348 |
| - var f = files[0]; |
349 |
| - put_file_name = f.name; |
350 |
| - var reader = new FileReader(); |
351 |
| - reader.onload = function(e) { |
352 |
| - put_file_data = new Uint8Array(e.target.result); |
353 |
| - document.getElementById('put-file-list').innerHTML = '' + escape(put_file_name) + ' - ' + put_file_data.length + ' bytes'; |
354 |
| - document.getElementById('put-file-button').disabled = false; |
355 |
| - }; |
356 |
| - reader.readAsArrayBuffer(f); |
357 |
| -} |
358 |
| - |
359 |
| -document.getElementById('put-file-select').addEventListener('click', function(){ |
360 |
| - this.value = null; |
361 |
| -}, false); |
362 |
| - |
363 |
| -document.getElementById('put-file-select').addEventListener('change', handle_put_file_select, false); |
364 |
| -document.getElementById('put-file-button').disabled = true; |
365 |
| - |
366 |
| -</script> |
| 44 | +<script src="webrepl.js"></script> |
367 | 45 |
|
368 | 46 | </html>
|
0 commit comments