handle server which aren't cgi "Status" aware
[cwebfiles.git] / static / index.html
1 <!DOCTYPE html>
2 <html>
3   <head>
4     <script>
5 function hex(str) {
6   var res = "";
7   var achar = 'a'.charCodeAt(0)
8   for (var i=0; i<str.length; i++) {
9     var c = str.charCodeAt(i)
10     res += String.fromCharCode((c>>4)+achar) + String.fromCharCode((c&0xf)+achar)
11   }
12   return res
13 }
14
15 function dehex(str) {
16   var res = ""
17   var achar = 'a'.charCodeAt(0)
18   for (var i=0; i<str.length; i+=2) {
19     var c1 = str.charCodeAt(i)-achar, c2 = str.charCodeAt(i+1)-achar
20     res += String.fromCharCode((c1<<4)+c2)
21   }
22   return res
23 }
24
25 function cgireq(script, qs, body, cb) {
26   var req = new XMLHttpRequest()
27   req.open((body!=null)?'POST':'GET', '/cgi-bin/cwebfiles/'+script+(qs?'?'+qs:''), !!cb)
28   if (cb) {
29     req.onloadend = function() {
30       var status = req.getResponseHeader("Status") || req.status.toString(10)
31       status = status.split(' ')[0]
32       status = parseInt(status, 10)
33       cb(status, req.responseText)
34     }
35   }
36   req.send(body)
37   if (!cb) {
38     var status = req.getResponseHeader("Status") || req.status.toString(10)
39     status = status.split(' ')[0]
40     status = parseInt(status, 10)
41     return {status: status, data: req.responseText}
42   }
43 }
44
45 function login(user, pass, cb) {
46   return cgireq('login', null, user+':'+pass, cb)
47 }
48
49 function listdir(path, cb) {
50   cgireq('listdir', hex(path), null, function(status, data) {
51     if (status != 200) return cb(status, data, null)
52     var listing = data.split('\n').filter(function(line) {
53       return line.trim().length > 0
54     }).map(function(line) {
55       var parts = line.split(' ')
56       return {name: dehex(parts[0]), size: parseInt(parts[1]), mode: parseInt(parts[2])}
57     })
58     cb(status, data, listing)
59   })
60 }
61
62 var S_IFMT = parseInt("0170000",8) // bit mask for the file type bit fields
63 var S_IFSOCK = parseInt("0140000",8) // socket
64 var S_IFLNK = parseInt("0120000",8) // symbolic link
65 var S_IFREG = parseInt("0100000",8) // regular file
66 var S_IFBLK = parseInt("0060000",8) // block device
67 var S_IFDIR = parseInt("0040000",8) // directory
68 var S_IFCHR = parseInt("0020000",8) // character device
69 var S_IFIFO = parseInt("0010000",8) // FIFO
70
71 var S_ISUID = parseInt("0004000",8) // set UID bit
72 var S_ISGID = parseInt("0002000",8) // set-group-ID bit (see below)
73 var S_ISVTX = parseInt("0001000",8) // sticky bit (see below)
74
75 var S_IRWXU = parseInt("00700",8) // mask for file owner permissions
76 var S_IRUSR = parseInt("00400",8) // owner has read permission
77 var S_IWUSR = parseInt("00200",8) // owner has write permission
78 var S_IXUSR = parseInt("00100",8) // owner has execute permission
79 var S_IRWXG = parseInt("00070",8) // mask for group permissions
80 var S_IRGRP = parseInt("00040",8) // group has read permission
81 var S_IWGRP = parseInt("00020",8) // group has write permission
82 var S_IXGRP = parseInt("00010",8) // group has execute permission
83 var S_IRWXO = parseInt("00007",8) // mask for permissions for others (not in group)
84 var S_IROTH = parseInt("00004",8) // others have read permission
85 var S_IWOTH = parseInt("00002",8) // others have write permission
86 var S_IXOTH = parseInt("00001",8) // others have execute permission
87
88
89 var typechars = {}
90 typechars[S_IFSOCK] = 's'
91 typechars[S_IFLNK] = 'l'
92 typechars[S_IFREG] = '-'
93 typechars[S_IFBLK] = 'b'
94 typechars[S_IFDIR] = 'd'
95 typechars[S_IFCHR] = 'c'
96 typechars[S_IFIFO] = 'p'
97
98 function escapehtml(text) {
99   return text.replace(/&/g, '&amp;')
100               .replace(/</g, '&lt;')
101               .replace(/>/g, '&gt;')
102               .replace(/"/g, '&quot;')
103 }
104
105 var loc = '/'
106
107 function dive(hexedname) { __listdir(loc + dehex(hexedname) + '/'); }
108
109 function format_entry(entry) {
110   var modeline = ""
111   modeline += typechars[entry.mode&S_IFMT]
112   modeline += (entry.mode&S_IRUSR) ? 'r' : '-'
113   modeline += (entry.mode&S_IWUSR) ? 'w' : '-'
114   modeline += (entry.mode&S_ISUID) ? ((entry.mode&S_IXUSR) ? 's' : 'S') : ((entry.mode&S_IXUSR) ? 'x' : '-')
115   modeline += (entry.mode&S_IRGRP) ? 'r' : '-'
116   modeline += (entry.mode&S_IWGRP) ? 'w' : '-'
117   modeline += (entry.mode&S_ISGID) ? ((entry.mode&S_IXGRP) ? 's' : 'S') : ((entry.mode&S_IXGRP) ? 'x' : '-')
118   modeline += (entry.mode&S_IROTH) ? 'r' : '-'
119   modeline += (entry.mode&S_IWOTH) ? 'w' : '-'
120   modeline += (entry.mode&S_ISVTX) ? ((entry.mode&S_IXOTH) ? 's' : 'S') : ((entry.mode&S_IXOTH) ? 'x' : '-')
121   
122   var markred = (entry.mode&(S_ISUID|S_ISGID))
123   if (markred) {
124     modeline = '<span style="color: red">'+modeline+'</span>'
125   }
126   
127   var showsize = (entry.mode&S_IFMT) == S_IFREG
128   
129   var escaped_name = escapehtml(entry.name)
130   
131   var clickcode = ''
132   if ((entry.mode&S_IFMT) == S_IFDIR) clickcode = "dive('"+hex(entry.name)+"')"
133   
134   return '<tr><td>'+modeline+'</td><td '+(clickcode?'class="clickme" ':'')+'onclick="'+clickcode+'">'+escaped_name+'</td><td>'+(showsize?entry.size:'')+'</td></tr>'
135 }
136
137 function renderloc(path) {
138   var parts = path.split('/')
139   var curpath = '/';
140   var linkparts = parts.map(function(dir, i) {
141     var esc = (i==0) ? 'main directory' : escapehtml(dir)
142     if (i != 0) curpath += dir + '/'
143     var hexpath = hex(curpath)
144     var clickcode = "__listdir(dehex('"+hexpath+"'))"
145     return '<span class="clickme" onclick="'+clickcode+'">'+esc+'</span>'
146   })
147   return '<div id="loc">'+linkparts.join(' / ')+'</div>'
148 }
149
150 function __listdir(path) {
151   listdir(path, function(status, err, listing) {
152     if (status != 200) {
153       alert(err)
154       return
155     }
156     loc = path
157     var body = document.getElementsByTagName('body')[0]
158     body.innerHTML = renderloc(path)
159                    + '<table><thead><tr><th>mode</th><th>name</th><th>size</th></tr></thead><tbody>'+listing.filter(function(e) {
160       return e.name != '.' && e.name != '..'
161     }).map(function(e) {
162       return format_entry(e)
163     }).join('')+'</tbody></table>'
164     /*listing.forEach(function(e) {
165       //e.mode = e.mode.toString(8)
166       console.log(Object.keys(e).map(function(k) {
167         return k+':'+e[k]
168       }).join(', '))
169     })*/
170   })
171 }
172
173
174     </script>
175     <style>
176       .clickme {
177         text-decoration: underline;
178         color: blue;
179         cursor: pointer;
180       }
181     </style>
182   </head>
183   <body onload="__listdir('/')">
184   </body>
185 </html>