ヘッダーサンプル

ho.rb ソース

ho.rb

#! ruby -Ks
#ho 文書をhtmlに書き出す
#
#
#
#2008/03/03〜prototype
#2008/03/09〜module class化する。。。断念(^^;)
#2008/03/16 UNESC の自動化他
#2008/03/26 出力ファイルの複数化に暫定対応他
#2008/03/28 link_name 関連整理,doc_conf 整理,ERBのbinding処理追加,その他整理
#2008/05/30 @gsub_list (ハッシュ)によるテキスト置換を追加
#2008/09/28 クラス化実行により、全面書き換え
#2008/09/30 各種微調整
#2008/11/12 プロジェクトフォルダに %conf% %style% %plugin% が無い場合、カレントアイテムからも読み込むように修正
#2011/01/06 コメント類の整理
#2012/02/23 typo修正
#2012/03/23 CSS見直しに伴い各種微調整

#eval_start
require "tpz"
include Tpz
require 'erb'

org_path = File.dirname( __FILE__ )
$:.unshift( org_path )
require 'sunokodoc'

# デバッグ表示フラグ
Debug = true
#Debug = false



###Utility
#----html エスケープ処理
#ESCに要素を追加すれば、自動的に UNESC も生成される
#
#require "erb"
#include ERB::Util
#これで、実はできたりする。しかも、alias してあって、
# h hoge
#で使えたり(^^;)
#
ESC = {
  '&' => '&',
  '<' => '&lt;',
  '>' => '&gt;',
  '"' => '&quot;'
}

UNESC = Hash.new
for i in 0 .. ESC.size
  UNESC.store(ESC.values[i],ESC.keys[i])
end

def hreg( h ) #h:hash の keys 又は values の正規表現化
  reg = ''
  h.each do |s|
    reg << "(#{s})|"  #単純に()でグルーピング
  end
  return reg.gsub(/\|$/ , '')   #最後の余分な | を削除
end

def escape(str) #エスケープ
  str.gsub( Regexp.new( hreg( ESC.keys) ) ) {|s| ESC[s] }
end

def unescape(str) #アンエスケープ
  str.gsub( Regexp.new( hreg( ESC.values) ) ) {|s| UNESC[s] }
end


###
#既存クラス拡張
###
class String
  $KCODE = 's'
  require 'nkf'
  require 'jcode'

  def up  # 半角アルファベット→すべて大文字
    self.upcase
  end

  def down  # 半角アルファベット→すべて小文字
    self.downcase
  end

  def swap  # 半角アルファベット→大文字小文字反転
    self.swapcase
  end

  def za  # alphabet 半角アルファベット→全角
    self.tr("a-zA-Z","a-zA-Z")
  end

  def ha  # alphabet全角アルファベット→半角
    self.tr("a-zA-Z","a-zA-Z")
  end

  def zn  # numeric 半角数字→全角
    self.tr("0-9","0-9")
  end

  def hn  # numeric 全角数字→半角
    self.tr("0-9","0-9")
  end

  def zk  # kana 半角カナ→全角カナ
    NKF::nkf( '-SsX -m0', self )
  end

  def hk  # kana 全角カナ→半角カナ
    str = kanabreak(self)
    zenkana = "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜"
    a = zenkana.split(//)
    kanahash = {}
    a.each_index {|i| kanahash[a[i]] = i + 0xA1 }
    str.gsub!(/[#{zenkana}]/) { |c| kanahash[c].chr }
  end

  def zs  # symbol 半角記号類→全角
    hs = '!"#$%&\'()*+,-./:;<=>?@[]^_`{|}~'
    zs = '!”#$%&’()*+,−./:;<=>?@[]^_`{|}〜'
    self.tr('\\','¥').tr(hs,zs)
  end

  def hs  # symbol 全角記号類→半角
    hs = '!"#$%&\'()*+,-./:;<=>?@[]^_`{|}~'
    zs = '!”#$%&’()*+,−./:;<=>?@[]^_`{|}〜'
    self.tr('¥','\\').tr(zs,hs)
  end

private
  def kanabreak(str)
    daku = "ガギグゲゴザジズゼゾダヂヅデドバビブベボヴ"
    daku2 = "カキクケコサシスセソタチツテトハヒフヘホウ"
    handaku = "パピプペポ"
    handaku2 = "ハヒフヘホ"

    str.gsub!(/[#{daku}]/) {|c| c + "゛"}
    str.gsub!(/[#{handaku}]/) {|c| c + "゜"}
    str.tr!(daku,daku2)
    str.tr!(handaku,handaku2)
    str.tr!("ヵヶヰヱヮ","カケイエワ")
    return str
  end

end


class TpzItem
  #TpzItemクラスを拡張する

  def get_mid_item(message_id)
    # Message_id なアイテムを取得する。
    item = self
    while item do
      if item.message_id == message_id then
        return item
      end
      item = item.next
    end
  end

  def get_title_item(title)
    # title なアイテムを取得する。
    item = self
    while item do
      if item.title == title then
        return item
      end
      item = item.next
    end
  end

  def get_title_s_item(title)
    # 最初のスペースまでを有効タイトルとしたアイテムを取得する。
    item = self
    while item do
      if item.title.strip.split(/\s+/)[0] == title then
        return item
      end
      item = item.next
    end
  end

  def get_title_doc(title)
    # title な文書を取得する。
    item = self
    item.documents.each do |doc|
      if doc.title == title then
        return doc
      end
    end
  end

end


class TpzItemDocument
# def initialize
#   self.initialize
#   @doc_type = ''
# end

  def doc_type= (s)
    # 文書のタイプを決定する
    @doc_type = s
  end

  def doc_type
    # 文書のタイプを決定する
    if @doc_type == nil then
      case self.title
      when /^\%.*\%$/i
        type = 'pre'
      when /pre/i
        type = 'pre'
      when /wiki/i
        type = 'wiki'
      when /hiki/i
        type = 'wiki'
      when /html/i
        type = 'html'
      when /SunokoDoc/i
        type = 'SunokoDoc'
      else
        type = 'SunokoDoc'
      end
    else
      type = @doc_type
    end
    return type
  end

  def translate_text( type = @doc_type )
    # doc_type に応じたテキストに変換する
    text = self.text

    case type
      when ''
        text = text
      when  'html'
        text = text
      when  'pre'
        text = "<pre>\n#{escape(text)}\n</pre>\n"
      when 'wiki'
        text = SunokoDoc.new( text ).to_html  #SunokoDoc の標準はHikiDoc
      when  'SunokoDoc'   #<SunokoDoc>〜</SunokoDoc>を省略させたいので
        text = "<SunokoDoc>\n#{text}\n</SunokoDoc>\n"
        text = SunokoDoc.new( text ).to_html
      else
        text = SunokoDoc.new( text ).to_html
    end
    return text
  end

end


class Config
  def initialize
    load
    set_instance_variables
  end

  def plugin(plugin_src)
    instance_eval(plugin_src )
    reconf("%doc_conf%", tpz_current_project)   #%doc_conf% は plugin をロードしてからとする
    reconf("%add_header_footer%", tpz_current_project)   #%add_header_footer% は plugin をロードしてからとする
  end

  ############################################################
  #システム変数を定義する
  #文書毎に設定される
  def system_para(doc)
    #文書中に指定できる各種情報を定義する
    @Doc = doc            #この文書そのもの
    @DocTitle = doc.title #この文書の文書名
    @DocIndex = doc.index #この文書の文書番号(0〜)
    @DocText = doc.text   #この文書の生テキスト

    docs = doc.parent.documents
    next_index = doc.index + 1
    prev_index = doc.index - 1
    if next_index < docs.size then
      @NextDocTitle = docs[next_index].title  #次の文書の文書名
      @NextDocIndex = docs[next_index].index  #次の文書の文書番号(0〜)
    else
      @NextDocTitle = doc.title
      @NextDocIndex = doc.index
    end

    if prev_index >= 0 then
      @PrevDocTitle = docs[prev_index].title  #前の文書の文書名
      @PrevDocIndex = docs[prev_index].index  #前の文書の文書番号(0〜)
    else
      @PrevDocTitle = doc.title
      @PrevDocIndex = doc.index
    end

    item = doc.parent
    @Item = item      #この文書があるアイテムそのもの
    if item == nil then
      #何もしない
    else
      @ItemSubject = item.title #アイテムの件名
      @ItemMessageId = item.message_id  #アイテムのメッセージID
      if item.parent == nil then
        @ParentSubject =item.title #親アイテムの件名
        @ParentMessageId =item.message_id #親アイテムのメッセージID
      else
        @ParentSubject =item.parent.title #親アイテムの件名
        @ParentMessageId =item.parent.message_id #親アイテムのメッセージID
      end

      @ProjectSubject = item.project.title  #プロジェクトの件名
      @ProjectMessageId = item.project.message_id #プロジェクトのメッセージID

      if item.next == nil then
        @NextItemSubject = item.title
        @NextItemMessageId = item.message_id
        #@NextItemWikiLink = SunokoDoc.new("[[#{@NextItemSubject}|#link(#{@NextItemMessageId})]]").to_html
        #@NextItemWikiLink = @NextItemWikiLink.gsub(/(<p>)(.*?)(<\/p>$)/m){ ($2) }
      else
        @NextItemSubject = item.next.title  #次のアイテムの件名
        @NextItemMessageId = item.next.message_id #次のアイテムのメッセージID
        #@NextItemWikiLink = SunokoDoc.new("[[#{@NextItemSubject}|#link(#{@NextItemMessageId})]]").to_html
        #@NextItemWikiLink = @NextItemWikiLink.gsub(/(<p>)(.*?)(<\/p>$)/m){ ($2) }
      end

      if item.prev == nil then
        @PrevItemSubject = item.title
        @PrevItemMessageId = item.message_id
        #@PrevItemWikiLink = SunokoDoc.new("[[#{@PrevItemSubject}|#link(#{@PrevItemMessageId})]]").to_html
        #@PrevItemWikiLink = @PrevItemWikiLink.gsub(/(<p>)(.*?)(<\/p>$)/m){ ($2) }
      else
        @PrevItemSubject = item.prev.title  #前のアイテムの件名
        @PrevItemMessageId = item.prev.message_id #前のアイテムのメッセージID
        #@PrevItemWikiLink = SunokoDoc.new("[[#{@PrevItemSubject}|#link(#{@PrevItemMessageId})]]").to_html
        #@PrevItemWikiLink = @PrevItemWikiLink.gsub(/(<p>)(.*?)(<\/p>$)/m){ ($2) }
      end
    end
  end

  # %???% な文書をパラメータとする。アイテムごとに再定義可能。
  # 自分のアイテムにパラメータがない場合は、親に向かって算出する
  def reconf( doc_title, item=tpz_current_item )
    if /%.*%$/ !~ doc_title then
      p "パラメータ文書名が不適切です" if Debug
      return
    end

    src = ''
    while item do #%??%のあるアイテムを親に向かって算出する
      item_src = %Q|tpz_current_project.get_mid_item("#{item.message_id}")|
      begin
        src << %Q|eval(%q!#{item_src}.get_title_doc(doc_title).text!)\n| if item.get_title_doc(doc_title).text
      rescue
      end
      if item.project? then
        item = nil
      else
        item = item.parent
      end
    end

    srces = src.split(/\n/).reverse   #下位のアイテムでの %???% 優先とするので逆順にする
    text = ''
    srces.each do |s|     #各ソースを実行し、%???% テキストを算出する
      text << "#{eval(s)}"
    end

    eval(text) #最終的なパラメータテキストをevalしパラメータ設定
    set_instance_variables

  end

private
  def set_instance_variables
    instance_variables.each do |v|
      v.sub!( /@/, '' )
      instance_eval( <<-SRC
        def #{v}
          @#{v}
        end
        def #{v}=(p)
          @#{v} = p
        end
        SRC
      )
    end
  end

  def load
    #プロジェクト固有のパラメータ(必須)
    #最低限必要なパラメータをここで定義しておく
    #上書き可能なので、 %conf% 等で再定義しても問題ない
    @header = ""
    @footer = ""
    @docprint_all = false
    @folderprint_all = false
    @non_print_docs = []
    @pre_doc_list = {}

    begin
      eval( tpz_current_project.get_title_doc( "%style%" ).text )
    rescue
      begin
        eval( tpz_current_item.get_title_doc( "%style%" ).text )
      rescue
      end
    end
    begin
      eval( tpz_current_project.get_title_doc( "%conf%" ).text )
    rescue
      begin
        eval( tpz_current_item.get_title_doc( "%conf%" ).text )
      rescue
      end
    end

    #item毎に再定義可能なパラメータ。初期値をセット
    @item_header = ""
    @item_footer = ""
    @doc_header = ""
    @doc_footer = ""
    @gsub_list = {}

    #出力ファイル名のイニシャル定義
    ###ファイル名関係のパラメータ
    #基本的には各アイテムの %output_file_conf% にあることを期待している
    #ディレクトリのパラメータも用意してあるが、実際のリンク作成( toc プラグイン)は File.basename で実施
    # →フルパスだと、ローカルフォルダ、サーバーフォルダ等の変換も必要。
    # →css ファイル等も考慮しなければならない。
    # →css は、あまり散在させたくないので、あくまでも、
    #   htmlファイルのあるディレクトリ下の css ディレクトリにあると仮定する
    #
    #ファイル名の自動作成も検討したが、結局、自由に設定できないことから、
    #それならば、各アイテムごとに設定できるように変更した。
    @of_full = tpz_current_project.project_filename
    @of_dir  = File.dirname( @of_full ) + '\\'  #@of_dir を定義すれば、保存ディレクトリも指定可能ではある。。。
    @of_base = File.basename( @of_full )
    @of_ext  = File.extname( @of_full )
    @of      = @of_base.gsub(/#{@of_ext}$/, '')
    @of_name = @of_base.gsub(/#{@of_ext}$/, '') + '.html'   #%output_file_conf% にて 拡張子付で定義すること

    #一応再定義しておく
    reconf("%output_file_conf%",tpz_current_project)
    #reconf("%doc_conf%", tpz_current_item)   #%doc_conf% は plugin をロードしてからとする

    system_para(tpz_current_project.documents[0])
    set_of_id
  end

  ###各アイテム毎の出力ファイル名を、Hash で持つ
  #@of_id ハッシュ
  #   keys = 'アイテムmessage_id'
  #   values = 'ファイル名(フルパス)'
  #@of_uniq_array ファイル配列
  def set_of_id
    @of_id = Hash.new
    item = tpz_current_project
    while item do
      reconf( "%output_file_conf%", item )
      @output_file_name = @of_dir + @of_name        #実際の保存ファイル名(フルパス)
      @of_id[item.message_id] = @output_file_name
      item = item.next
    end
    @of_uniq_array = @of_id.values.uniq.sort

    @of_uniq_array.each do |f|
      print "【ファイル名:#{f}】\n" if Debug
    end
  end

end


class Plugin
  ###プラグインをロードする
  #プラグインパスは固定とする /plugin/
  def initialize( conf )
    @conf = conf

    #@conf のパラメータを同じく定義する
    @conf.instance_variables.each do |v|
      v.sub!( /@/, '' )
      instance_eval( <<-SRC
        @#{v} = @conf.#{v}    #インスタンス変数セット
        SRC
      )
    end

    read_plugin
    load_plugin
  end

  def src
    @plugin_src
  end

private
  def load_plugin
    instance_eval( @plugin_src )
  end

  def read_plugin
    @plugin_src = ''
    begin
      plugin_path = File.dirname( __FILE__ ) + "/plugin/*.rb"
      print "plugin_path = #{plugin_path}\n" if Debug
      Dir::glob( plugin_path ).sort.each do |file|
        open( file ) do |src|
          print "plugin file = #{file}\n" if Debug
          @plugin_src << src.read
        end
      end
    rescue Exception
    end

    begin
      @plugin_src << tpz_current_project.get_title_doc( "%plugin%" ).text
    rescue
      begin
        @plugin_src << tpz_current_item.get_title_doc( "%plugin%" ).text
      rescue
      end
    end
  end

end

#TaskPrize の ドキュメントクラスを対象にする
class Ho
  def initialize( conf, plugin, item=tpz_current_project)
    @conf = conf
    @plugin = plugin
    @item = item
    @doc = @item.documents[0] #一応設定しておく

    instance_eval( @plugin.src )
    set_para( @doc )
  end

  def set_para( doc )
    @doc = doc
    @conf.system_para(@doc)
    @conf.reconf( "%doc_conf%", @doc.parent )

    set_instance_variables

    @doc_type = @pre_doc_list[@doc.title] # %conf%で決定されている場合優先
    if @doc_type == nil
      @doc_type = @doc.doc_type           # 決定されていない場合、文書名規則で決定する
    end
  end

  def init_conf(item)
    @conf.reconf( "%conf%", item )
    set_instance_variables
  end

  def doc_html( doc )
    @doc = doc
    set_para( @doc )
    r = ''
    html = ''

    html <<  %Q|<div class="doc" >\n|
    html <<  "#{@doc_header}\n"

    title = @doc.title.gsub(/<(.*?)>/,'')  #タイトル内の<?>を削除
    if title == ''
      #r << %Q|<h1 class="doc_title">文書(#{@doc.index + 1})</h1>\n|
    else
      r << %Q|<h1 class="doc_title">#{escape( t2html( title ) )}</h1>\n|
    end

    r << %Q|#{@doc.translate_text(@doc_type)}\n|

    html <<  "#{doc_name( r2html( r ,binding ) )}\n"

    #@doc_footer でテキスト内容によるマクロ展開をしているものもあるかもしれないので、再度設定する。
    @conf.reconf( "%doc_conf%", @doc.parent )
    html <<  "#{@conf.doc_footer}\n"  #初期化が終わったあとなので、@doc_footerには反映していない。よって、@conf.doc_footer とする。
    html <<  %Q|</div>\n|
  end

  def item_html( item )
    @item = item
    set_para( @item.documents[0] )
#    init_conf( tpz_current_project )

    r = ""
    r << %Q|<a name="#{link_name(@item.message_id)}"></a>\n|
    r << %Q|<div class="item" >\n#{@item_header}\n|
    r << %Q|<h1 class="item_title">#{escape(t2html(@item.title))}</h1>\n|

    if @docprint_all then
      @item.documents.each do |doc|
        if @non_print_docs.index(doc.title) == nil then
          r << %Q|<a name="#{link_name(@item.message_id, doc.index)}"></a>\n|
          r << %Q|#{doc_html(doc)}\n|
        end
      end
    else
      r << %Q|#{doc_html(@item.documents[0])}\n|  #index=0 固定
    end
    r = %Q|#{ r }\n#{@item_footer}\n</div>|   #div 要調整
  end

private
  def set_instance_variables
    #@conf のパラメータを同じく定義する
    #特異メソッドを定義する必要がなければ、def は必要ない
    @conf.instance_variables.each do |v|
      v.sub!( /@/, '' )
      instance_eval( <<-SRC
        @#{v} = @conf.#{v}    #インスタンス変数セット
        def #{v}              #特異メソッド定義(必要ない?)
          @#{v}
        end
        def #{v}=(p)
          @#{v} = p
        end
        SRC
      )
    end
  end
end





#####################
#main処理
#####################

###itemのメッセージ番号よりファイル名を取得し、

#インスタンス作成
conf = Config.new
plugin = Plugin.new(conf)
conf.plugin(plugin.src)

h = Ho.new( conf, plugin)


#初期化をかねて @header を配列に入れる
#この処理あまりよろしくない???
html = ''
html_array = Array.new( h.of_uniq_array.size ){''}
html_array.each_with_index do |s, index| 
  file_title = File.basename(h.of_uniq_array[index]).gsub(/#{File.extname(h.of_uniq_array[index])}$/, '')
  print "【h.of_uniq_array[index] = #{h.of_uniq_array[index]}】\n"
  print "【file_title = #{file_title}】\n"
  print "【#{h.header}】\n"
  html_array[index] = s + ERB.new(h.header).result(binding)
end


items = tpz_selected_items
if tpz_focus_pain != 3  #エディットウィンドウ以外の場合選択アイテムを実行
  items.each do |item|
    if h.folderprint_all == true then
      top_level = item.level
      while item do #フォルダの場合、下部のアイテムも出力する
        file_name = h.of_id[item.message_id]
        index = h.of_uniq_array.index( file_name )
        html = html_array[index]
        html << %Q|#{h.item_html(item)}\n|
        html_array[index] = html
        item = item.next
        if item and item.level <= top_level then
          item = nil
        end
      end
    else
      file_name = h.of_id[item.message_id]
      index = h.of_uniq_array.index( file_name )
      html = html_array[index]
      html << %Q|#{h.item_html(item)}\n|
      html_array[index] = html
    end
  end
else                    #エディットウィンドウの場合、その文書のみ
  file_name = h.of_id[tpz_current_item.message_id]
  index = h.of_uniq_array.index( file_name )
  html = html_array[index]
  html << %Q|<a name="#{h.link_name(tpz_current_item.message_id, tpz_current_document.index)}"></a>\n|
  html << %Q|#{h.doc_html(tpz_current_document)}\n|
  html_array[index] = html
end

html_array.each_with_index do |s, index|
  html_array[index] = s +  ERB.new(h.footer).result(binding)
end

#@of_uniq_array ごとに出力する必要有り
h.of_uniq_array.each_with_index do |f, index|
  out_f = open(f,"w")
    out_f.write( html_array[index].zk)  #半角カナは全角カナに強制変換
  out_f.close
end

#eval_end

mailto:sunoko_at_avis.ne.jp // Last update:2012/03/23