Index: app/views/wiki/map.rhtml
===================================================================
diff -u /dev/null app/views/wiki/map.rhtml
--- /dev/null	Mon Jun  7 12:11:00 2004
+++ app/views/wiki/map.rhtml	Fri Jun  4 02:29:06 2004
@@ -0,0 +1,7 @@
+<% @title = "Map" %><%= sub_template "top" %>
+
+<% create_map %> 
+<%= get_map %>
+<div align="center"><img src="../get_map_img" usemap="#map" border="0" /></div>
+
+<%= sub_template "bottom" %>
Index: instiki.rb
===================================================================
RCS file: /var/cvs/instiki/instiki/instiki.rb,v
retrieving revision 1.15
diff -u -r1.15 instiki.rb
--- instiki.rb	21 May 2004 17:30:15 -0000	1.15
+++ instiki.rb	7 Jun 2004 10:09:58 -0000
@@ -19,12 +19,15 @@
 end
 
 pdflatex_available = system "pdflatex --help"
+dot_available = system "dot -V"
+
 
 OPTIONS = {
   :server_type => fork_available ? Daemon : SimpleServer,
   :port        => 2500,
   :storage     => "#{cdir}/storage/",
-  :pdflatex    => pdflatex_available
+  :pdflatex    => pdflatex_available,
+  :dot         => dot_available
 }
 
 ARGV.options do |opts|
@@ -55,4 +58,4 @@
 WikiService.storage_path = OPTIONS[:storage] + OPTIONS[:port].to_s
 WikiController.template_root = "#{cdir}/app/views/"
 
-WebControllerServer.new(OPTIONS[:port], OPTIONS[:server_type], "#{cdir}/app/controllers/")
\ No newline at end of file
+WebControllerServer.new(OPTIONS[:port], OPTIONS[:server_type], "#{cdir}/app/controllers/")
Index: app/controllers/wiki.rb
===================================================================
RCS file: /var/cvs/instiki/instiki/app/controllers/wiki.rb,v
retrieving revision 1.30
diff -u -r1.30 wiki.rb
--- app/controllers/wiki.rb	3 Jun 2004 13:53:26 -0000	1.30
+++ app/controllers/wiki.rb	7 Jun 2004 10:09:59 -0000
@@ -128,6 +128,109 @@
     send_export(file_name + ".pdf", file_path + ".pdf")
   end
 
+  # ---------------------------------------------------------------------------
+
+  require 'rdoc/dot/dot'
+  REDRAW_PERIOD = 5 
+  FONT = 'Arial'
+  FONTPATH = "/usr/X11R6/lib/X11/fonts/webfonts" # TODO
+  DOT::GRAPH_OPTS << 'fontpath'
+
+  # TODO: prevent concurrent execution of create_map
+  def create_map
+    if @last_map_creation.nil? or (Time.now - @last_map_creation) > REDRAW_PERIOD
+      # create new map 
+      @last_map_creation = Time.now
+    else
+      # use old map
+      return
+    end
+
+    file_name = "#{web.address}-graph"
+    file_path = EXPORT_DIRECTORY + file_name
+
+    h = {
+      'name'     => 'map',
+      'label'    => '',
+      'fontname' => FONT,
+      'fontsize' => 8,
+      'compound' => 'true'
+    }
+    h['fontpath'] = %{"#{FONTPATH}"} if FONTPATH != nil
+
+    graph = DOT::DOTDigraph.new(h)
+
+    for name, page in @web.pages 
+      graph << DOT::DOTNode.new('name' => %{"#{name}"},
+      'label' => page.plain_name,
+      'shape' => 'box',
+      'fontname' => FONT,
+      'color' => 'black',
+      'style' => 'solid',
+      'fontsize' => 8,
+      'URL' => %{"../show/#{ name.gsub(/\s/, "+") }"} 
+      )
+
+      page.references.each do |ref|
+        graph << DOT::DOTEdge.new('from' => %{"#{ref.name}"}, 'to' => %{"#{name}"}, 'style' => 'solid')
+      end
+    end
+
+    @web.select.wanted_pages.each do |wanted|
+      graph << DOT::DOTNode.new('name' => %{"#{wanted}"},
+      'label' => WikiWords.separate(wanted, @web.brackets_only),
+      'shape' => 'box',
+      'style' => 'dashed',
+      'fontname' => FONT,
+      'color' => 'black',
+      'fontsize' => 8,
+      'URL' => %{"../show/#{ wanted.gsub(/\s/, "+") }"} 
+      )
+
+      @web.select.pages_that_reference(wanted).each do |page|
+        graph << DOT::DOTEdge.new('from' => %{"#{page.name}"}, 'to' => %{"#{wanted}"}, 'style' => 'dashed')
+      end
+    end
+      
+    File.open("#{ file_path }.dot", 'w+') {|f| f << graph.to_s}
+    `dot -Tpng -o #{ file_path }.png #{ file_path }.dot`
+
+    # ERROR: this does not work with konquerer, which expects x1 < x2, y1 < y2
+    # `dot -Tcmapx -o { file_path }.map #{ file_path }.dot`
+
+    # instead we parse a server side map generated by dot and create the client
+    # side map ourself
+    File.open("#{ file_path }.map", "w+") {|res| 
+      res << %{<map id="map" name="map">\n}
+      dot_map = `dot -Tismap #{file_path}.dot`
+      dot_map.each do |area|
+        unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([^\s]+)\s+(.*)/
+          STDERR.puts "Unexpected output from dot:\n#{area}"
+        end
+
+        xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i] 
+        url, area_name = $5, $6
+
+        res <<  %{  <area href="#{url}" alt="#{area_name}" shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" />\n}
+      end
+      res << "</map>\n"
+    }
+  end
+  
+  def get_map
+    file_name = "#{web.address}-graph"
+    file_path = EXPORT_DIRECTORY + file_name
+    File.read("#{ file_path }.map")
+  end
+
+  def get_map_img
+    file_name = "#{web.address}-graph"
+    file_path = EXPORT_DIRECTORY + file_name
+    send_export(file_name + ".png", file_path + ".png", "image/png")
+  end
+
+  # ---------------------------------------------------------------------------
+
   def export_tex
     file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.tex"
     file_path = EXPORT_DIRECTORY + file_name
@@ -378,4 +481,4 @@
       @res["Content-Length"] = File.size(file_path)
       File.open(file_path, "rb") { |f| @res.body = f.read }
     end
-end
\ No newline at end of file
+end
Index: app/models/chunks/category.rb
===================================================================
RCS file: /var/cvs/instiki/instiki/app/models/chunks/category.rb,v
retrieving revision 1.1
diff -u -r1.1 category.rb
--- app/models/chunks/category.rb	29 May 2004 09:52:54 -0000	1.1
+++ app/models/chunks/category.rb	7 Jun 2004 10:09:59 -0000
@@ -21,8 +21,8 @@
   # This chunk is only kept if its mask could be replaced.
   def unmask(content)
     self if content.sub!( 
-      Regexp.new( pre_mask+'(.*)?'+post_mask ), 
-      '<div class="property">category:\1</div>'
+      Regexp.new( pre_mask+'(.*)?'+post_mask ),
+        '<div class="property">category:\1</div>'
     )
   end
 end
Index: app/views/navigation.rhtml
===================================================================
RCS file: /var/cvs/instiki/instiki/app/views/navigation.rhtml,v
retrieving revision 1.10
diff -u -r1.10 navigation.rhtml
--- app/views/navigation.rhtml	24 Apr 2004 23:53:45 -0000	1.10
+++ app/views/navigation.rhtml	7 Jun 2004 10:09:59 -0000
@@ -15,5 +15,8 @@
   <%= list_item "Authors", "../authors/", "Who wrote what" %> | 
   <%= list_item "Feeds", "../feeds/", "Subscribe to changes by RSS" %> | 
   <%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> | 
+<% if OPTIONS[:dot] %>
+  <%= list_item "Map", "../map/", "View a graphical map of the whole web", "M" %> | 
+<% end %>
   <input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true">
 </form>
Index: app/views/wiki/web_list.rhtml
===================================================================
RCS file: /var/cvs/instiki/instiki/app/views/wiki/web_list.rhtml,v
retrieving revision 1.6
diff -u -r1.6 web_list.rhtml
--- app/views/wiki/web_list.rhtml	29 May 2004 09:52:54 -0000	1.6
+++ app/views/wiki/web_list.rhtml	7 Jun 2004 10:09:59 -0000
@@ -3,10 +3,10 @@
 <ul>
 <% for web in @webs %>
   <li>
-    <a href="/<%= web.address %>/show/HomePage"><%= web.name %></a>
+    <a href="../<%= web.address %>/show/HomePage"><%= web.name %></a>
     (<%= web.pages.length %> pages by <%= web.authors.length %> authors)
   </li>
 <% end %>
 </ul>
 
-<%= sub_template "bottom" %>
\ No newline at end of file
+<%= sub_template "bottom" %>
