1<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 2<html> 3 <head> 4 <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type"> 5 <script language="javascript"> 6 // Split a string in 2 parts. The first is the leading number, if any, 7 // the second is the string following the numbers. 8 function splitNum(s) { 9 var results = new Array(); 10 results[0] = 'None'; 11 for (var i = 0; i < s.length; i++) { 12 var substr = s.substr(0, i+1) 13 if (isNaN(substr)) { 14 // Not a number anymore. 15 results[1] = s.substr(i) 16 break; 17 } else { 18 // This is a number. update the results. 19 results[0] = parseFloat(substr); 20 } 21 } 22 return results; 23 } 24 25 // Compare 2 strings using a custom alphanumerical algorithm. 26 // This is similar to a normal string sort, except that we sort 27 // first by leading digits, if any. 28 // For example: 29 // 100hello > 2goodbye 30 // Numbers anywhere else in the string are compared using the normal 31 // sort algorithm. 32 function alphanumCompare(a, b) { 33 var parsedA = splitNum(a); 34 var parsedB = splitNum(b); 35 var numA = parsedA[0]; 36 var numB = parsedB[0]; 37 var strA = parsedA[1]; 38 var strB = parsedB[1]; 39 40 if (isNaN(numA) == false && isNaN(numB) == false) { 41 // They both start with numbers. 42 if (numA < numB) return -1; 43 if (numA > numB) return 1; 44 // Identical. Fallback to string. 45 return (strA < strB) ? -1 : (strA > strB ? 1 : 0) 46 } 47 48 // If only one starts with a number, we start with that one as 49 // the lowest. 50 if (isNaN(numA) == false) return -1 51 if (isNaN(numB) == false) return 1 52 53 // They are both strings. 54 return (a < b) ? -1 : (a > b ? 1 : 0) 55 } 56 </script> 57 </head> 58 <body> 59 <script type="application/javascript"> 60 String.prototype.startsWith = function(str) { 61 return (this.match('^' + str) == str) 62 } 63 64 // Helper function to retrieve the value of a GET query parameter. 65 // Greatly inspired from http://alturl.com/8rj7a 66 function getParameter(parameterName) { 67 // Add '=' to the parameter name (i.e. parameterName=value) 68 var parameterName = parameterName + '='; 69 var queryString = window.location.search.substring(1); 70 if (queryString.length <= 0) { 71 return ''; 72 } 73 74 // Find the beginning of the string 75 begin = queryString.indexOf(parameterName); 76 77 // If the parameter name is not found, skip it, otherwise return the 78 // value. 79 if (begin == -1) { 80 return ''; 81 } 82 83 // Add the length (integer) to the beginning. 84 begin += parameterName.length; 85 86 // Multiple parameters are separated by the '&' sign. 87 end = queryString.indexOf ('&', begin); 88 89 if (end == -1) { 90 end = queryString.length; 91 } 92 93 // Return the string. 94 return unescape(queryString.substring(begin, end)); 95 } 96 97 // Given a tag and a node, returns the value for this tag on this node. 98 function getNodeValue(node, tag) { 99 return node.getElementsByTagName(tag)[0].firstChild.nodeValue; 100 } 101 102 // Displays the directory listing given the XML and path. 103 function displayList(xmlstring, root, path, pathRoot) { 104 // Display the header 105 document.write('<h1>Index of /' + path + '</h1>'); 106 107 // Start the table for the results. 108 document.write('<table style="border-spacing:15px 0px;">'); 109 110 var sortOrder = getParameter('sort'); 111 var sortLink = location.pathname + '?path=' + path; 112 if (sortOrder != 'desc') { 113 sortLink += '&sort=desc'; 114 } 115 116 // Display the table header. 117 document.write('<tr><th><img src="' + root + pathRoot + 118 'icons/blank.gif" alt="[ICO]"></th>'); 119 document.write('<th><a href="' + sortLink + '">Name</a></th>'); 120 document.write('<th>Last modified</th>'); 121 document.write('<th>Size</th>'); 122 document.write('<th>Storage Class</th>'); 123 document.write('<th>ETag</th></tr>'); 124 document.write('<tr><th colspan="6"><hr></th></tr>'); 125 126 // Display the 'go back' button. 127 if (path != '') { 128 var backpath = location.pathname; 129 130 // If there is more than one section delimited by '/' in the current 131 // path we truncate the last section and append the rest to backpath. 132 var delimiter = path.lastIndexOf('/'); 133 if (delimiter >= 0) { 134 delimiter = path.substr(0, delimiter).lastIndexOf('/'); 135 if (delimiter >= 0) { 136 backpath += '?path='; 137 backpath += path.substr(0, delimiter+1); 138 } 139 } 140 141 document.write('<tr><td valign="top"><img src="' + root + pathRoot + 142 'icons/back.gif" alt="[DIR]"></td>'); 143 document.write('<td><a href="'); 144 document.write(backpath); 145 document.write('">Parent Directory</a></td>'); 146 document.write('<td> </td>'); 147 document.write('<td align="right"> - </td></tr>'); 148 } 149 150 // Set up the variables. 151 var directories = new Array(); 152 var files = new Array(); 153 154 for (var iter = 0; iter < xmlstrings.length; iter++) { 155 var xmlstring = xmlstrings[iter]; 156 // Parse the XML output. 157 var parser = new DOMParser(); 158 var xmlDoc = parser.parseFromString(xmlstring, 'text/xml'); 159 160 // Get the main element. 161 var results = xmlDoc.getElementsByTagName('ListBucketResult'); 162 163 // Get all the directories. 164 var prefixes = results[0].getElementsByTagName('CommonPrefixes'); 165 for (var i = 0; i < prefixes.length; i++) { 166 var prefix = getNodeValue(prefixes[i], 'Prefix'); 167 directories.push(prefix.substr(path.length)); 168 } 169 170 // Get all the files. 171 var contents = results[0].getElementsByTagName('Contents'); 172 for (var i = 0; i < contents.length; i++) { 173 var obj = new Object(); 174 obj.keyName = getNodeValue(contents[i], 'Key'); 175 obj.lastModified = getNodeValue(contents[i], 'LastModified'); 176 obj.eTag = getNodeValue(contents[i], 'ETag'); 177 obj.size = getNodeValue(contents[i], 'Size'); 178 files.push(obj); 179 } 180 } 181 182 files.sort(alphanumCompare); 183 directories.sort(alphanumCompare); 184 185 // Reverse the list for a descending sort. 186 if (sortOrder == 'desc') { 187 files.reverse(); 188 directories.reverse(); 189 } 190 191 // Display the directories. 192 for (var i = 0; i < directories.length; i++) { 193 var lnk = location.pathname.substr(0, location.pathname.indexOf('?')); 194 lnk += '?path=' + path + directories[i]; 195 196 document.write('<tr>'); 197 document.write('<td valign="top"><img src="' + root + pathRoot + 198 'icons/folder.gif" alt="[DIR]"></td>'); 199 document.write('<td><a href="' + lnk + '">' + 200 directories[i].split('/')[0] + '</a></td>'); 201 document.write('<td align="right">-</td>'); 202 document.write('<td align="right">-</td>'); 203 document.write('<td align="right">-</td>'); 204 document.write('<td align="right">-</td>'); 205 document.write('</tr>'); 206 } 207 208 // Display the files. 209 for (var i = 0; i < files.length; i++) { 210 var link = root + files[i].keyName; 211 var filename = files[i].keyName.substr(path.length); 212 var size = files[i].size / 1024 / 1024; 213 var lastModified = files[i].lastModified.replace('T', ' '); 214 lastModified = lastModified.substr(0, lastModified.indexOf('.')); 215 216 // Remove the entries we don't want to show. 217 if (filename == '') { 218 continue; 219 } 220 221 if (filename.indexOf('$folder$') >= 0) { 222 continue; 223 } 224 225 // Display the row. 226 document.write('<tr>'); 227 document.write('<td valign="top"><img src="' + root + pathRoot + 228 'icons/binary.gif" alt="[DIR]"></td>'); 229 document.write('<td><a href="' + link + '">' + filename + 230 '</a></td>'); 231 document.write('<td align="right">' + lastModified + '</td>'); 232 document.write('<td align="right">' + size.toFixed(2) + 'MB</td>'); 233 document.write('<td align="right"><pre>' + 234 files[i].eTag.split('"')[1] + '</pre></td>'); 235 document.write('</tr>'); 236 } 237 238 // Close the table. 239 document.write('<tr><th colspan="6"><hr></th></tr>'); 240 document.write('</table>'); 241 } 242 243 var xmlstrings = new Array(); 244 245 function fetchAndDisplay(marker) { 246 var path = getParameter('path'); 247 var lastSlash = location.pathname.lastIndexOf("/"); 248 var filename = location.pathname.substring(lastSlash + 1); 249 var firstSlash = location.pathname.indexOf("/", 1); 250 var root = location.pathname.substring(0, firstSlash + 1); 251 var pathRoot = location.pathname.substring(firstSlash + 1, 252 lastSlash + 1); 253 if (!path) { 254 path = location.pathname.substring(firstSlash + 1, lastSlash + 1); 255 } 256 257 var markerParam = ''; 258 if (marker != '') { 259 markerParam = '&marker=' + marker; 260 } 261 262 var http = new XMLHttpRequest(); 263 http.open('GET', 264 root + '?delimiter=/&prefix=' + path + markerParam, 265 true); 266 http.onreadystatechange = useHttpResponse; 267 http.send(null); 268 function useHttpResponse() { 269 if (http.readyState == 4) { 270 var xmlstring = http.responseText; 271 xmlstrings.push(xmlstring); 272 273 // Check if the data is truncated. if so, we need to request the 274 // rest. 275 var parser = new DOMParser(); 276 var xmlDoc = parser.parseFromString(xmlstring, 'text/xml'); 277 278 // Get the main element. 279 var results = xmlDoc.getElementsByTagName('ListBucketResult'); 280 281 // Get IsTruncated. 282 var truncated = getNodeValue(results[0], 'IsTruncated'); 283 var nextMarker = ''; 284 if (truncated == 'true') { 285 nextMarker = getNodeValue(results[0], 'NextMarker'); 286 fetchAndDisplay(nextMarker); 287 } else { 288 displayList(xmlstrings, root, path, pathRoot); 289 } 290 } 291 } 292 } 293 294 fetchAndDisplay(''); 295 </script> 296 </body> 297</html> 298