init commit
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
// Platform-agnostic Gopher response handler and Gophermap parser
|
||||
// Depends on the platform-specific gopherRequest(resource, input, success, error) function
|
||||
// (defined in the transport.js file here)
|
||||
|
||||
Hi01379 = (function(psGopherRequest) {
|
||||
|
||||
var tagsToReplace = {'&': '&', '<': '<', '>': '>'}
|
||||
|
||||
function esc(str) { // escape HTML entities
|
||||
return str.replace(/[&<>]/g, function(tag) {return tagsToReplace[tag] || tag})
|
||||
}
|
||||
|
||||
function gophermapToHTML(gmap, chost, cport) {
|
||||
if(!cport) cport = 70
|
||||
if(!chost) chost = 'localhost'
|
||||
var lines = gmap.split(/\r?\n/), line, l = lines.length, i, lbr = '<br>',
|
||||
type, rest, desc, sel, host, port,
|
||||
output = '', pretag = 'pre', preflag = false
|
||||
for(i=0;i<l;i++) {
|
||||
line = lines[i]
|
||||
if(line === '.')
|
||||
break // immediately stop parsing on the . line
|
||||
if(line.indexOf('\t') == -1 && !line.startsWith('i')) // this also handles empty lines
|
||||
line = 'i' + line
|
||||
type = line[0] // type
|
||||
rest = line.substr(1).split('\t')
|
||||
desc = esc(rest[0]) // escaping description (obviously must not contain tabs)
|
||||
sel = rest.length > 1 ? rest[1] : '' // selector
|
||||
host = rest.length > 2 ? rest[2] : '' // hostname
|
||||
port = rest.length > 3 ? (0|rest[3]) : '' // port
|
||||
// check for the empty fields
|
||||
if(!sel) sel = desc // selector defaults to the description
|
||||
if(!host) host = chost // host defaults to the current host
|
||||
if(!port) port = cport // port defaults to the current port
|
||||
if(type == 'i') { // information message - wrap them in pretag
|
||||
if(!preflag) {
|
||||
preflag = true
|
||||
output += '<' + pretag + '>'
|
||||
}
|
||||
output += desc + '\n'
|
||||
}
|
||||
else { //information messages ended, stop preformatting
|
||||
if(preflag) {
|
||||
preflag = false
|
||||
output += '</' + pretag + '>'
|
||||
}
|
||||
if(desc = desc.trim()) { // ignore empty descriptions
|
||||
if(type == '3') // error message
|
||||
output += '<span class=error>' + desc + '</span>' + lbr
|
||||
else { // shape the link (internal hi:type|sel|host|port format unless it's an external URL)
|
||||
var deflink = 'hi:'+[type,sel,host,port].join('|'), link = deflink
|
||||
if(type == 'h' && sel.startsWith('URL:')) {
|
||||
link = sel.split(':').slice(1).join(':').trim()
|
||||
if(link.startsWith('javascript:')) // XSS protection
|
||||
link = deflink
|
||||
}
|
||||
output += '<a href="' + encodeURI(link) + '">' + desc + '</a>' + lbr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// resource format: [type, selector, host, port]
|
||||
function loadHole(resource, input, successCb, errorCb) {
|
||||
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)
|
||||
}
|
||||
successCb({
|
||||
content: datablob,
|
||||
contentType: ctype,
|
||||
contentName: fname,
|
||||
serviceMsg: type + ' ' + resource[1],
|
||||
updateAddr: false
|
||||
})
|
||||
}
|
||||
else { // assuming text content otherwise
|
||||
var output = decodeURIComponent(escape(rawdata)), ctype = 'text/plain' // defaulting to type 0
|
||||
if(type == '1' || type == '7') { // gophermap
|
||||
output = gophermapToHTML(output, resource[2], resource[3])
|
||||
ctype = 'text/html'
|
||||
}
|
||||
successCb({
|
||||
content: output,
|
||||
contentType: ctype,
|
||||
serviceMsg: type + ' ' + resource[1]
|
||||
})
|
||||
}
|
||||
}, errorCb)
|
||||
}
|
||||
|
||||
return {load: loadHole, render: gophermapToHTML}
|
||||
})(gopherRequest)
|
||||
Reference in New Issue
Block a user