- #!/usr/bin/ruby
- require 'cgi'
- class CGI
- def get_param(name)
- val = self.params[name]
- return nil unless val
- return nil if val.empty?
- val.to_s
- end
- end
- def main
- cgi = CGI.new
- res = respond(cgi)
- print "Content-Type: #{res.content_type}\r\n"
- print "Content-Length: #{res.content_length}\r\n"
- print "Cache-Control: no-cache\r\n"
- print "Pragma: no-cache\r\n"
- print "\r\n"
- print res.body
- end
- def respond(req)
- q = req.query_string.to_s.strip
- if /\A\d+,\d+\z/ =~ q
- x, y = *q.split(',', 2).map {|n| [[0, n.to_i].max, 640].min }
- return edit_response(x, y)
- else
- cmd = req.get_param('cmd').to_s.downcase
- case cmd
- when 'add'
- x = req.get_param('x').to_i
- y = req.get_param('y').to_i
- text = req.get_param('text').to_s
- add_node load_graph(), Node.new(x, y, text)
- return view_response()
- when 'img'
- return image_response()
- else
- return view_response()
- end
- end
- end
- CGI_URL = File.basename($0)
- def view_response
- Response.new('text/html', <<HTML)
- <html>
- <head><title>view</title></head>
- <p>
- <a href="#{CGI_URL}">
- <img ismap src="#{CGI_URL}?cmd=img" width="640" height="640">
- </a>
- </p>
- </html>
- HTML
- end
- def image_response
- Response.new('image/png', File.read(IMAGE_FILE))
- end
- def edit_response(x, y)
- Response.new('text/html; charset="euc-jp"', <<HTML)
- <html>
- <head><title>edit</title></head>
- <body>
- <p>日本語通りません</p>
- <form method#{CGI_URL}">
- n="#{CGI_URL}">
- <input type="hidden" name=">
- <input type="">
- <input type=" value="#{x}">
- " value="#{x}">
- <input type=" value="#{y}">
- " value="#{y}">
- <textarea name="text" cols="40" rows="3"></textarea>
- <input type="submit" id="write" value="Add">
- </form>
- </body>
- </html>
- HTML
- end
- class Response
- def initialize(type, body)
- @type = type
- @body = body
- end
- def content_type
- @type
- end
- def content_length
- @body.length
- end
- attr_reader :body
- end
- require 'log'
- LOGFILE = 'log'
- IMAGE_FILE = 'graph.png'
- def load_graph
- File.open(LOGFILE) {|f|
- return Marshal.load(f)
- }
- rescue
- return Graph.new
- end
- def add_node(graph, node)
- lock {
- graph.add node
- #{$$}", 'w') {|f|
- $$}", 'w') {|f|
- f.write generate_image(graph).png(f)
- }
- F#{$$}", IMAGE_FILE
- $}", IMAGE_FILE
- File.open(LOGFILE, 'w') {|f|
- Marshal.dump(graph, f)
- }
- }
- end
- INIT_W = 640
- INIT_H =#000000'
- LOR = '#000#ffffff'
- LOR = '#ffffff'
- def generate_image(graph)
- image = GD::Image.new(640, 640)
- image.transparent(image#fffffe'))
- ate('#fffffe'))
- image.interlace = true
- fgcolor = image.colorAllocate(FGCOLOR)
- bgcolor = image.colorAllocate(BGCOLOR)
- graph.each_node do |n|
- image.filledRectangle n.x, n.y, n.x2, n.y2, bgcolor
- image.rectangle n.x, n.y, n.x2, n.y2, fgcolor
- n.each_text_line do |line, x, y|
- image.string GD::Font::MediumFont, x, y, line, fgcolor
- end
- end
- image
- end
- def lock
- File.open('lockfile') {|f|
- begin
- f.flock(File::LOCK_EX)
- yield
- ensure
- f.flock(File::LOCK_UN)
- end
- }
- end
- class Graph
- def initialize
- @nodes = []
- end
- def add(node)
- @nodes.push node
- end
- def width
- @nodes.map {|n| n.x }.max
- end
- def height
- @nodes.map {|n| n.y }.max
- end
- def each_node(&block)
- @nodes.each(&block)
- end
- end
- class Node
- def initialize(x, y, text)
- @x = x
- @y = y
- @edges = []
- @lines = text.map {|line| line.rstrip }
- @created_at = Time.now
- end
- attr_reader :x
- attr_reader :y
- attr_reader :created_at
- def each_text_line(&block)
- y = @y + GAP_H
- @lines.each do |line|
- yield line, @x + GAP_W, y
- y += CHAR_HEIGHT + 1
- end
- end
- CHAR_WIDTH = 7
- CHAR_HEIGHT = 13
- GAP_W = 6
- GAP_H = 3
- def width
- @lines.map {|line| CHAR_WIDTH * line.size }.max + GAP_W * 2
- end
- def height
- CHAR_HEIGHT * @lines.size + GAP_H * 2
- end
- def x2
- @x + width()
- end
- def y2
- @y + height()
- end
- def each_edge(&block)
- @edges.each(&block)
Raw Paste