|
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