]> git.draconx.ca Git - homepage.git/blobdiff - layouts/listing.xhtml
Implement sortable file listing tables.
[homepage.git] / layouts / listing.xhtml
index aad56081170f2d33949ca3db6614b4d6e1d6cfb7..406e42ba79ac2eaac0fba108141b4fa2ec6f0833 100644 (file)
@@ -30,50 +30,163 @@ files = {}
     next unless "#{d}/" == mydir
 
     if p =~ %r{/$}
-      sz = Dir.children(File.dirname(rep.raw_path)).length - 1
-      type = "DIR"
+      displaysize = Dir.children(File.dirname(rep.raw_path)).length - 1
+      size = displaysize - 1000000
+      type = :DIR
     else
-      sz = human_filesize(File.size(rep.raw_path))
+      size = File.size(rep.raw_path)
+      displaysize = human_filesize(size)
       type = nil
     end
 
     files[f] = {
-      mtime: if t then t.getutc.strftime "%Y-%m-%d %H:%M UTC" end,
-      size: sz,
+      sorttime: if t then t.to_f else 0.0 end,
+      displaytime: if t then t.getutc.strftime "%Y-%m-%d %H:%M UTC" end,
+      displaysize: displaysize,
+      size: size,
       type: type,
     }
   end
 end
 
 if @items["#{File.dirname(mydir)}/index.lst"]
-  files[".."] = { type: "UP" }
+  files[".."] = { type: :UP }
+end
+
+def render_entry(files, key)
+  f = files[key]
+  return <<~EOF
+    <td>#{if f[:type]
+      "<img alt='#{f[:type]}' width='16' height='16' src='#{case f[:type]
+            when :DIR; "/images/folder.svg"
+            when :UP;  "/images/return.svg"
+            else raise "no icon for filetype #{f[:type]}"
+            end}' />"
+    end}</td>
+    <td><a href='#{key}'>#{
+      if key == ".." then "[Parent Directory]" else key end
+    }</a></td>
+    <td>#{f[:displaytime]}</td>
+    <td>#{f[:displaysize]}</td>
+  EOF
 end
 %>
+<div>
+<script type='x'><![CDATA[]]x><!--]]></script>
+<input style='display: none' type='radio' name='filelist-sort' id='filelist-name-sort' checked='checked' />
+<input style='display: none' type='radio' name='filelist-sort' id='filelist-date-sort' />
+<input style='display: none' type='radio' name='filelist-sort' id='filelist-size-sort' />
+<input style='display: none' type='checkbox' id='filelist-name-rev' />
+<input style='display: none' type='checkbox' id='filelist-date-rev' />
+<input style='display: none' type='checkbox' id='filelist-size-rev' />
+<script type='x'>--></script>
 <table class='filelist'>
   <thead>
-    <tr><th /><th>Name</th><th>Last Modified</th><th>Size</th></tr>
+    <tr>
+      <th />
+      <th class='name'>
+        <label for='filelist-name-sort'><span>Name</span></label>
+        <script type='x'><![CDATA[]]x><!--]]></script>
+        <label for='filelist-name-rev' style='display: none'>
+          <span>Name</span>
+          <img alt='FWD' width='16' height='16' src='/images/down.svg' />
+          <img alt='REV' width='16' height='16' src='/images/up.svg' style='display: none' />
+        </label>
+        <script type='x'>--></script>
+      </th>
+      <th class='date'>
+        <label for='filelist-date-sort'><span>Last Modified</span></label>
+        <script type='x'><![CDATA[]]x><!--]]></script>
+        <label for='filelist-date-rev' style='display: none'>
+          <span>Last Modified</span>
+          <img alt='FWD' width='16' height='16' src='/images/down.svg' />
+          <img alt='REV' width='16' height='16' src='/images/up.svg' style='display: none' />
+        </label>
+        <script type='x'>--></script>
+      </th>
+      <th class='size'>
+        <label for='filelist-size-sort'><span>Size</span></label>
+        <script type='x'><![CDATA[]]x><!--]]></script>
+        <label for='filelist-size-rev' style='display: none'>
+          <span>Size</span>
+          <img alt='FWD' width='16' height='16' src='/images/down.svg' />
+          <img alt='REV' width='16' height='16' src='/images/up.svg' style='display: none' />
+        </label>
+        <script type='x'>--></script>
+      </th>
+    </tr>
   </thead>
   <tbody>
-<% files.keys.sort{ |a, b| strverscmp(a, b) }.each do |key| %>
-    <tr>
-      <td>
-<% if files[key][:type] %>
-        <div>
-          <img src='<%=
-            case files[key][:type]
-            when "DIR"; "/images/folder.svg"
-            when "UP";  "/images/return.svg"
-            else raise "no icon for filetype #{files[key][:type]}"
-            end %>' alt='<%= files[key][:type] %>' width='16' height='16' />
-        </div>
+<%=
+    parentrow = if files[".."] then "#{render_entry(files, "..")}" end
+    files.delete("..")
+    if parentrow then "<tr>#{parentrow}</tr>" end
+%>
+<%
+by_name = files.keys.sort{ |a, b| strverscmp(a, b) }
+by_name.each_index do |i|
+  entry = render_entry(files, by_name[i])
+  if i+1 == by_name.length
+    entry.sub!(%r{(.*)</td>}m,
+               "\\1<script type='x'><![CDATA[]]x><!--]]></script></td>")
+  end
+%>
+    <tr><%= entry %></tr>
 <% end %>
-      </td>
-      <td><a href='<%= key %>'><%=
-        if key == ".." then "[Parent Directory]" else key end
-      %></a></td>
-      <td><%= files[key][:mtime] %></td>
-      <td><%= files[key][:size] %></td>
+  </tbody>
+
+  <tbody style='display: none'>
+<%
+def meta_cmp(files, key, a, b)
+  av, bv = files[a][key], files[b][key]
+  return av <=> bv if av != bv
+  return strverscmp(a, b)
+end
+
+by_date = files.keys.sort { |a, b| meta_cmp(files, :sorttime, a, b) }
+by_size = files.keys.sort { |a, b| meta_cmp(files, :size, a, b) }
+
+listnames = [ "namerev", "date", "daterev", "size", "sizerev" ]
+lists = [ by_name.reverse, by_date, by_date.reverse, by_size, by_size.reverse ]
+if parentrow
+%>
+  <tr class='<%= listnames.join(" ") %>'><%= parentrow %></tr>
+<%
+end
+evenmap = (0..(lists.length-1)).map { false }
+even = false
+
+while not (elems = lists.map(&:first)).compact.empty?
+  matches = (0..(lists.length-1)).to_a.keep_if { |x| evenmap[x] == even }
+  if !matches.empty?
+    elems = elems.values_at(*matches).compact
+    mode = elems.group_by{|a| a}.max{|a, b| a[1].length <=> b[1].length}[0]
+    matches = []
+
+    lists.each_index do |i|
+      if evenmap[i] == even and lists[i].first.eql? mode
+        lists[i].shift
+        evenmap[i] ^= true
+        evenmap[i] = nil if lists[i].empty?
+
+        matches << i
+      end
+    end
+%>
+    <tr class='<%= listnames.values_at(*matches).join(" ") %>'>
+      <%= render_entry(files, mode) %>
     </tr>
-<% end %>
+<%
+  else
+%>
+    <tr><td /></tr>
+<%
+  end
+
+  even ^= true
+end
+%>
+    <tr><td><script type='x'>--></script></tr>
   </tbody>
 </table>
+</div>