# # bitclust/screen.rb # # Copyright (C) 2006-2008 Minero Aoki # # This program is free software. # You can distribute/modify this program under the Ruby License. # require 'bitclust/rdcompiler' require 'bitclust/methodsignature' require 'bitclust/htmlutils' require 'bitclust/nameutils' require 'bitclust/messagecatalog' require 'erb' require 'json' require 'stringio' require 'uri' module BitClust class ScreenManager def initialize(h) h[:urlmapper] ||= URLMapper.new(h) tmpldir = h[:templatedir] || "#{h[:datadir]}/template" h[:template_repository] ||= TemplateRepository.new(tmpldir) h[:message_catalog] ||= default_message_catalog(h) @conf = h end def default_message_catalog(h) dir = h[:catalogdir] || "#{h[:datadir]}/catalog" loc = MessageCatalog.encoding2locale(h[:encoding] || 'utf-8') MessageCatalog.load_with_locales(dir, [loc]) end private :default_message_catalog def entry_screen(entry, opt) new_screen(Screen.for_entry(entry), entry, opt) end def library_index_screen(libs, opt) new_screen(LibraryIndexScreen, libs, opt) end def library_screen(lib, opt) new_screen(LibraryScreen, lib, opt) end def class_index_screen(cs, opt) new_screen(ClassIndexScreen, cs, opt) end def class_screen(c, opt) new_screen(ClassScreen, c, opt) end def method_screen(ms, opt) new_screen(MethodScreen, ms, opt) end def opensearchdescription_screen(request_full_uri, opt) new_screen(OpenSearchDescriptionScreen, request_full_uri, opt) end def search_screen(result, opt) new_screen(SearchScreen, result, opt) end def doc_screen(d, opt) new_screen(DocScreen, d, opt) end def function_screen(f, opt) new_screen(FunctionScreen, f, opt) end def function_index_screen(fs, opt) new_screen(FunctionIndexScreen, fs, opt) end private def new_screen(c, *args) c.new(@conf, *args) end end class URLMapper include NameUtils def initialize(h) @base_url = h[:base_url] @cgi_url = h[:cgi_url] @css_url = h[:css_url] @favicon_url = h[:favicon_url] @theme = h[:theme] || 'default' end attr_reader :base_url def cgi_url @cgi_url end def css_url return @css_url if @css_url "#{@base_url}/theme/#{@theme}/style.css" end def custom_css_url(css) "#{@base_url}/theme/#{@theme}/#{css}" end def js_url return @js_url if @js_url "#{@base_url}/theme/#{@theme}/t.js" end def custom_js_url(js) "#{@base_url}/theme/#{@theme}/#{js}" end def favicon_url return @favicon_url if @favicon_url "#{@base_url}/theme/#{@theme}/rurema.png" end def library_index_url "#{@cgi_url}/library/" end def library_url(name) "#{@cgi_url}/library/#{libname2id(name)}" end def class_url(name) "#{@cgi_url}/class/#{classname2id(name)}" end def method_url(spec) cname, tmark, mname = *split_method_spec(spec) "#{@cgi_url}/method/#{classname2id(cname)}/#{typemark2char(tmark)}/#{encodename_url(mname)}" end def function_index_url "#{@cgi_url}/function/" end def function_url(name) "#{@cgi_url}/function/#{name}" end def opensearchdescription_url "#{@cgi_url}/opensearchdescription" end def search_url "#{@cgi_url}/search" end def spec_url(name) "#{@cgi_url}/spec/#{name}" end def document_url(name) raise unless %r!\A[-\w/]+\z! =~ name "#{@cgi_url}/#{name}" end def canonical_url(current_url) current_url end end class TemplateRepository def initialize(prefix) @prefix = prefix end def load(id) preproc(File.read("#{@prefix}/#{id}")) end private def preproc(template) template.gsub(/^\.include ([\w\-]+)/) { load($1) } end end class Screen def Screen.for_entry(entry) ent = entry.kind_of?(Array) ? entry.first : entry ::BitClust.const_get("#{ent.type_id.to_s.capitalize}Screen") end def status nil end end class ErrorScreen < Screen include HTMLUtils def initialize(err) @error = err end def status 500 end def content_type 'text/html' end def body <<-EndHTML
#{escape_html(@error.message)} (#{escape_html(@error.class.name)}) #{@error.backtrace.map {|s| escape_html(s) }.join("\n")}EndHTML end end class NotFoundScreen < Screen include HTMLUtils def initialize(err) @error = err end def status 404 end def content_type 'text/html' end def body <<-EndHTML
#{escape_html(@error.message)} (#{escape_html(@error.class.name)})EndHTML end end class TemplateScreen < Screen include Translatable include HTMLUtils def initialize(h) @urlmapper = h[:urlmapper] @template_repository = h[:template_repository] @default_encoding = h[:default_encoding] || h[:database].propget('encoding') @target_version = h[:target_version] || h[:database].propget('version') init_message_catalog h[:message_catalog] @conf = h end def content_type "text/html; charset=#{encoding()}" end def encoding default_encoding() end def ruby_version @target_version || 'unknown' end def google_tag_manager tracking_id = @conf[:gtm_tracking_id] return "" unless tracking_id <<-HTML.chomp HTML end def meta_robots content = @conf[:meta_robots_content] return "" unless content return "" if content.empty? %Q() end def meta_description %Q() end private def default_encoding @default_encoding || 'us-ascii' end def run_template(id, layout = true) method_name = "#{id}_template".gsub('-', '_') unless respond_to? method_name erb = ERB.new(@template_repository.load(id)) erb.def_method(self.class, method_name, id + '.erb') end body = __send__(method_name) if layout unless respond_to? :layout erb = ERB.new(@template_repository.load('layout')) erb.def_method(self.class, 'layout', 'layout.erb') end layout{ body } else body end end def h(str) escape_html(str.to_s) end def css_url @urlmapper.css_url end def custom_css_url(css) @urlmapper.custom_css_url(css) end def js_url @urlmapper.js_url end def custom_js_url(js) @urlmapper.custom_js_url(js) end def favicon_url @urlmapper.favicon_url end def current_url raise NotImplementedError, "Must implement this method in subclass" end def canonical_url @urlmapper.canonical_url(current_url) end def opensearchdescription_url @urlmapper.opensearchdescription_url end def search_url @urlmapper.search_url end def library_index_url @urlmapper.library_index_url end def function_index_url @urlmapper.function_index_url end def headline_init @hlevel = 1 end def headline_push @hlevel += 1 end def headline_pop @hlevel -= 1 end def headline(str) "