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('Index of /'
 + path + '');
    106  
    107         // Start the table for the results.
    108         document.write('');
    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(' root + pathRoot +
    118                        'icons/blank.gif" alt="[ICO]"> | ');
    119         document.write('sortLink + '">Name | ');
    120         document.write('Last modified | ');
    121         document.write('Size | ');
    122         document.write('Storage Class | ');
    123         document.write('ETag | 
');
    124         document.write('
  | 
');
    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