kopher

A simple Gopher client for KaiOS
git clone git://git.luxferre.top/kopher.git
Log | Files | Refs | README | LICENSE

commit b7e9b40e32e7eec1c6cabc33bdc403c1f5f66eba
parent 35bee36cb9f2e46d182189574c9afa87cad4ac3f
Author: Luxferre <lux@ferre>
Date:   Sat, 25 Mar 2023 18:52:38 +0200

fixed blob loading

Diffstat:
Mjs/app.js | 12+++---------
Mjs/hi01379.js | 38++++++++++++++++++--------------------
Mjs/transport.js | 17+++++++++++------
3 files changed, 32 insertions(+), 35 deletions(-)

diff --git a/js/app.js b/js/app.js @@ -27,21 +27,15 @@ function openURL(url, successCb, errorCb) { if(resource.length > 4) input = resource[4] Hi01379.load(resource, input, function(res) { if(res.content && res.content instanceof Blob) { // handle the download here - var opener = new MozActivity({name: 'open', data: {type: res.contentType, blob: res.content, filename: res.contentName}}) - opener.onsuccess = function() { - successCb({content: null, serviceMsg: 'Opening a downloaded file ' + contentName, updateAddr: false}) - } - opener.onerror = errorCb + successCb({content: null, serviceMsg: 'Opening a downloaded file ' + res.contentName, updateAddr: false}) + window.open(URL.createObjectURL(res.content)) } else successCb(res) // proceed to UI with non-downloads }, errorCb) } else { // handle non-Gopher URLs with the Web Activities var opener = new MozActivity({name: 'view', data: {type: 'url', url: url}}) - opener.onsuccess = function() { - successCb({content: null, serviceMsg: 'Opening an external resource ' + url, updateAddr: false}) - } - opener.onerror = errorCb + successCb({content: null, serviceMsg: 'Opening an external resource ' + url, updateAddr: false}) } } diff --git a/js/hi01379.js b/js/hi01379.js @@ -64,33 +64,31 @@ Hi01379 = (function(psGopherRequest) { // resource format: [type, selector, host, port] function loadHole(resource, input, successCb, errorCb) { + var fname = resource[1].split('/').pop(), // as the most intelligent guess + ext = fname.split('.').pop().toLowerCase(), // presumed extension + extMappings = { // not including AVIF here because they aren't supported by Gecko 48 anyway + 'gif': 'image/gif', // including GIF though because nothing prevents using I instead of g + 'png': 'image/png', + 'svg': 'image/svg+xml', + 'webp': 'image/webp', + 'apng': 'image/apng', + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'jfif': 'image/jpeg', + 'pjpeg': 'image/jpeg', + 'pjp': 'image/jpeg' + } psGopherRequest(resource, input, function(rawdata, type) { if(type == '5' || type == 's' || type == ';' || type == 'd') type = '9' // remap unknown binary types to 9 if(type == '9' || type == 'g' || type == 'I') { // binary file var ctype = 'application/octet-stream' // the default one if everything else fails - var datablob = new Blob([rawdata], {type: ctype}), - fname = resource[1].split('/').pop() // as the most intelligent guess // update content type if(type == 'g') // GIF-only resource type ctype = 'image/gif' - else if(type == 'I') { // attempt to guess the image MIME type by the extension - var ext = fname.split('.').pop().toLowerCase(), - extMappings = { // not including AVIF here because they aren't supported by Gecko 48 anyway - 'gif': 'image/gif', // including GIF though because nothing prevents using I instead of g - 'png': 'image/png', - 'svg': 'image/svg+xml', - 'webp': 'image/webp', - 'apng': 'image/apng', - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'jfif': 'image/jpeg', - 'pjpeg': 'image/jpeg', - 'pjp': 'image/jpeg' - } - if(ext in extMappings) - ctype = extMappings(ext) - } + else if(type == 'I') // attempt to guess the image MIME type by the extension + ctype = extMappings[ext] + var datablob = new Blob([rawdata], {type: ctype}) successCb({ content: datablob, contentType: ctype, @@ -100,7 +98,7 @@ Hi01379 = (function(psGopherRequest) { }) } else { // assuming text content otherwise - var output = decodeURIComponent(escape(rawdata)), ctype = 'text/plain' // defaulting to type 0 + var output = (new TextDecoder).decode(rawdata), ctype = 'text/plain' // defaulting to type 0 if(type == '1' || type == '7') { // gophermap output = gophermapToHTML(output, resource[2], resource[3]) ctype = 'text/html' diff --git a/js/transport.js b/js/transport.js @@ -6,19 +6,24 @@ // input is optional and only considered for resource type 7 function gopherRequest(resource, input, successCb, errorCb) { - var xsock = navigator.mozTCPSocket.open(resource[2], (resource[3]||70) | 0, {binaryType: 'string'}), - type = resource[0][0], databuf = '' - xsock.ondata = function(data) { - databuf += data.data + var xsock = navigator.mozTCPSocket.open(resource[2], (resource[3]||70) | 0, {binaryType: 'arraybuffer'}), + type = resource[0][0], databuf = [] + xsock.ondata = function(e) { + var i = 0, db = new Uint8Array(e.data), l = db.length + for(;i<l;i++) + databuf.push(db[i]) } xsock.onclose = function() { - successCb(databuf, type) + var res = new Uint8Array(databuf) + successCb(res, type) + databuf = [] } xsock.onerror = errorCb xsock.onopen = function() { var selpath = resource[1] if(type == '7' && input) selpath += '\t' + input - xsock.send(selpath + '\r\n') // send CRLF-terminated selector path and optionally the search string + var reqbuf = (new TextEncoder).encode(selpath + '\r\n').buffer + xsock.send(reqbuf) // send CRLF-terminated selector path and optionally the search string } }