# -*- coding: utf-8; -*- # # Wiki_style.rb: Wiki style for tDiary 2.x format. $Revision: 1.32 $ # # if you want to use this style, add @style into tdiary.conf below: # # @style = 'Wiki' # # Copyright (C) 2003, TADA Tadashi # Copyright (C) 2005, Kazuhiko # You can distribute this under GPL. # require 'hikidoc' module TDiary class WikiSection attr_reader :subtitle attr_reader :categories, :stripped_subtitle attr_reader :subtitle_to_html, :stripped_subtitle_to_html, :body_to_html attr_accessor :author, :last_modified def initialize( fragment, author = nil ) @author = author if fragment[0] == ?! then @subtitle, @body = fragment.split( /\n/, 2 ) @subtitle.sub!( /^\!\s*/, '' ) else @subtitle = nil @body = fragment.dup end @body = @body || '' @body.sub!( /[\n\r]+\Z/, '' ) @body << "\n\n" @categories = get_categories @stripped_subtitle = strip_subtitle @subtitle_to_html = @subtitle ? to_html( "!#{@subtitle}" ) : '' @body_to_html = to_html( @body ) @html = @subtitle_to_html + "\n" + @body_to_html + "\n" @subtitle_to_html = strip_headings( @subtitle_to_html ) @body_to_html = strip_headings( @body_to_html ) @stripped_subtitle_to_html = @stripped_subtitle ? strip_headings( to_html( "!#{@stripped_subtitle}" ) ) : nil end def subtitle=(subtitle) cat_str = "" @categories.each {|cat| cat_str << "[#{cat}]" } cat_str << " " unless cat_str.empty? @subtitle = subtitle ? (cat_str + subtitle) : nil @stripped_subtitle = strip_subtitle end def body @body.dup end def body=(str) @body = str end def categories=(categories) @categories = categories cat_str = "" categories.each {|cat| cat_str << "[#{cat}]" } cat_str << " " unless cat_str.empty? @subtitle = @subtitle ? (cat_str + @stripped_subtitle) : nil @stripped_subtitle = strip_subtitle end def to_src r = '' r << "! #{@subtitle}\n" if @subtitle r << @body end def html4( date, idx, opt ) r = %Q[
\n] % idx r << %Q[<%=section_enter_proc( Time::at( #{date.to_i} ) )%>\n] r << do_html4( date, idx, opt ) r << %Q[<%=section_leave_proc( Time::at( #{date.to_i} ) )%>\n] r << "
\n" end def do_html4( date, idx, opt ) strdump = lambda{|s| s.dump.gsub( /%/, '\\\\045' ) } r = @html.lstrip r.sub!( %r!

(.*?)

!m ) do "

<%= subtitle_proc( Time::at( #{date.to_i} ), #{strdump.call $1} ) %>

" end or r.sub!( %r!^

(.+?)

$!m ) do "

<%= subtitle_proc( Time::at( #{date.to_i} ), #{strdump.call $1} ) %>

" end serial = [nil, nil, nil, '%02d'%idx, '00', '00'] dumped_param = strdump.call "#{date.strftime( '%Y%m%d' )}#p#{'%02d' % idx}" header_t = Struct.new(:tag, :level) bqlevel = 0 r.gsub!( %r!()|]*>!i ) do bqlevel += 1 if $1 bqlevel -= 1 if $2 next $& if bqlevel != 0 or $3.nil? h = header_t.new($&.dup, $3.to_i) serial[h.level, serial.length-h.level] = Array.new(serial.length-h.level){|i| i==0 ? serial[h.level].succ : '00' } frag = 'p' + serial[3 .. h.level].join('.') %!#{h.tag} ! end r.gsub( /<(\/)?tdiary-section>/, '<\\1p>' ) end def chtml( date, idx, opt ) r = %Q[<%=section_enter_proc( Time::at( #{date.to_i} ) )%>\n] r << do_html4( date, idx, opt ) r << %Q[<%=section_leave_proc( Time::at( #{date.to_i} ) )%>\n] end def to_s to_src end private def valid_plugin_syntax?(code) lambda { # $SAFE = 4 eval( "BEGIN {return true}\n#{code}", nil, "(plugin)", 0 ) }.call rescue SyntaxError # this lambda call # returns true in particular case on ruby-1.8.6 (maybe bug) # such that after eval("BEGIN {return true}\n_/a:a"). # returns nil on ruby-1.9.0 and ruby-1.8.6(most cases). lambda { eval('') }.call false end def to_html( string ) html = HikiDoc::to_html( string, :level => 3, :use_wiki_name => false, :allow_bracket_inline_image => false, :plugin_syntax => method(:valid_plugin_syntax?) ).strip html.gsub!( %r!\{\{(.+?)\}\}!m ) do "<%=#{CGI.unescapeHTML($1)}\n%>" end html.gsub!( %r!
\{\{(.+?)\}\}
!m ) do "

<%=#{CGI.unescapeHTML($1)}\n%>

" end # 変数 uに関する注意。 # kwプラグインの第一引数が HTMLエスケープされているべきものか不明。 # おそらく誰もエスケープしないだろうと想定するが、そうすると、HTML # から抜き出されたテキストである $1に加えられている、ユーザーの関 # 知しない変形(escape_html_param)をキャンセルする必要がある。 # 変数 kに関する注意。 # 1. myプラグインの第二引数は HTML(=戻り値にそのまま埋め込まれる). # 2. kwプラグインの第二引数はプレインテキスト(=エスケープされる)。 # 3. 二重ブラケットリンクのラベル部(“|”の左側)はモディファイア記法 # (""..."", ==...==など)が有効なので、$2は HTMLタグを含みうる。 # a. kwプラグインの第二引数を事前に hikihtml.unescape_htmlするのは # 不具合くさい見た目になる二重エスケープ(&quot;など)を嫌っただけ。 hikihtml = HikiDoc::HTMLOutput.new html.gsub!( %r!(.*?)! ) do k, u = $2, hikihtml.unescape_html($1) if /^(\d{4}|\d{6}|\d{8}|\d{8}-\d+)\D*([pctf]\d+(?:[-.]\d+)*)?$/ =~ u then %Q[<%=my '#{$1}#{$2}', '#{escape_quote k}' %>] elsif /:/ =~ u scheme, path = u.split( /:/, 2 ) if /\A(?:https?|ftp|mailto|javascript)\z/ =~ scheme u.sub!( %r@\A(?!mailto|javascript)\w+:(?!//)@, '' ) %Q[#{k}] elsif ( k == u ) %Q[<%=kw '#{escape_quote u}'%>] else %Q[<%=kw '#{escape_quote u}', '#{escape_quote hikihtml.unescape_html k}'%>] end elsif k.empty? %Q[#{CGI.escapeHTML u}] elsif u.empty? %Q[<%=kw '#{escape_quote k}'%>] elsif k == u %Q[<%=kw '#{escape_quote u}', '#{escape_quote hikihtml.unescape_html k}'%>] else %Q[#{k}] end end html end def escape_quote( s ) s.gsub( /'/, "\\\\'" ) end def strip_headings( string ) html = string html.sub!( /\A

/, '' ) html.sub!( %r|

\z|, '' ) html.empty? ? nil : html end def get_categories return [] unless @subtitle cat = /^(\[([^\[]+?)\])+/.match(@subtitle).to_a[0] return [] unless cat cat.scan(/\[(.*?)\]/).collect do |c| c[0].split(/,/) end.flatten end def strip_subtitle return nil unless @subtitle r = @subtitle.sub(/^(\[[^\[]+?\])+\s*/,'') if r == "" nil else r end end end class WikiDiary include DiaryBase include CategorizableDiary def initialize( date, title, body, modified = Time::now, sections_modified = nil, sections_author = nil ) init_diary replace( date, title, body ) @last_modified = modified @sections.each_with_index{|section, i| section.last_modified = sections_modified[i] if sections_modified section.author = sections_author[i] if sections_author } end def style 'Wiki' end def replace( date, title, body ) set_date( date ) set_title( title ) @sections = [] append( body ) end def append( body, author = nil ) # body1 is a section starts without subtitle. # body2 are sections starts with subtitle. if /(.*?)(^![^!].*)/m =~ body body1 = $1 body2 = $2 elsif /^![^!]/ !~ body body1 = body body2 = '' else body1 = '' body2 = body end unless body1.empty? current_section = @sections.pop if current_section then body1 = "#{current_section.to_src.sub( /\n+\Z/, '' )}\n\n#{body1}" end @sections << WikiSection::new( body1, author ) end section = nil body2.each_line do |l| case l when /^\![^!]/ @sections << WikiSection::new( section, author ) if section section = l else section = '' unless section section << l end end @sections << WikiSection::new( section, author ) if section @last_modified = Time::now self end def each_section @sections.each do |section| yield section end end def add_section(subtitle, body) sec = WikiSection::new("\n") sec.subtitle = subtitle sec.body = body @sections << sec @sections.size end def delete_section(index) @sections.delete_at(index - 1) end def to_src r = '' each_section do |section| r << section.to_src end r end def to_html( opt, mode = :HTML ) case mode when :CHTML to_chtml( opt ) else to_html4( opt ) end end def to_html4( opt ) r = '' idx = 1 each_section do |section| r << section.html4( date, idx, opt ) idx += 1 end r end def to_chtml( opt ) r = '' idx = 1 each_section do |section| r << section.chtml( date, idx, opt ) idx += 1 end r end def to_s "date=#{date.strftime('%Y%m%d')}, title=#{title}, body=[#{@sections.join('][')}]" end end end # Local Variables: # mode: ruby # indent-tabs-mode: t # tab-width: 3 # ruby-indent-level: 3 # End: