my_wiki_generator 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/my_wiki/grep.txt +238 -0
- data/my_wiki/my_wiki_generator.rb +248 -0
- data/my_wiki/templates/POST_GENERATION_REMINDER +1 -0
- data/my_wiki/templates/app/controllers/application.rb +75 -0
- data/my_wiki/templates/app/controllers/content_history_controller.rb +49 -0
- data/my_wiki/templates/app/controllers/login_controller.rb +93 -0
- data/my_wiki/templates/app/controllers/my_wiki_admin_controller.rb +26 -0
- data/my_wiki/templates/app/controllers/my_wiki_controller.rb +250 -0
- data/my_wiki/templates/app/controllers/page_admin_controller.rb +51 -0
- data/my_wiki/templates/app/controllers/user_admin_controller.rb +34 -0
- data/my_wiki/templates/app/helpers/application_helper.rb +3 -0
- data/my_wiki/templates/app/helpers/content_history_helper.rb +2 -0
- data/my_wiki/templates/app/helpers/login_helper.rb +2 -0
- data/my_wiki/templates/app/helpers/my_wiki_admin_helper.rb +2 -0
- data/my_wiki/templates/app/helpers/my_wiki_helper.rb +94 -0
- data/my_wiki/templates/app/helpers/page_admin_helper.rb +2 -0
- data/my_wiki/templates/app/helpers/user_admin_helper.rb +2 -0
- data/my_wiki/templates/app/models/attachment.rb +55 -0
- data/my_wiki/templates/app/models/content.rb +32 -0
- data/my_wiki/templates/app/models/content_history.rb +49 -0
- data/my_wiki/templates/app/models/content_sweeper.rb +7 -0
- data/my_wiki/templates/app/models/my_wiki_mailer.rb +29 -0
- data/my_wiki/templates/app/models/role.rb +18 -0
- data/my_wiki/templates/app/models/setting.rb +7 -0
- data/my_wiki/templates/app/models/user.rb +60 -0
- data/my_wiki/templates/app/views/content_history/_form.rhtml +19 -0
- data/my_wiki/templates/app/views/content_history/edit.rhtml +9 -0
- data/my_wiki/templates/app/views/content_history/list.rhtml +27 -0
- data/my_wiki/templates/app/views/content_history/new.rhtml +8 -0
- data/my_wiki/templates/app/views/content_history/show.rhtml +21 -0
- data/my_wiki/templates/app/views/layouts/my_wiki.rhtml +105 -0
- data/my_wiki/templates/app/views/login/edit.rhtml +25 -0
- data/my_wiki/templates/app/views/login/login.rhtml +23 -0
- data/my_wiki/templates/app/views/login/logout.rhtml +10 -0
- data/my_wiki/templates/app/views/login/signup.rhtml +23 -0
- data/my_wiki/templates/app/views/login/welcome.rhtml +13 -0
- data/my_wiki/templates/app/views/my_wiki/_form.rhtml +11 -0
- data/my_wiki/templates/app/views/my_wiki/css.rhtml +224 -0
- data/my_wiki/templates/app/views/my_wiki/diff.rhtml +4 -0
- data/my_wiki/templates/app/views/my_wiki/edit.rhtml +34 -0
- data/my_wiki/templates/app/views/my_wiki/fileinfo.rhtml +22 -0
- data/my_wiki/templates/app/views/my_wiki/list.rhtml +1 -0
- data/my_wiki/templates/app/views/my_wiki/mails.rhtml +1 -0
- data/my_wiki/templates/app/views/my_wiki/new.rhtml +6 -0
- data/my_wiki/templates/app/views/my_wiki/recent.rhtml +3 -0
- data/my_wiki/templates/app/views/my_wiki/search.rhtml +5 -0
- data/my_wiki/templates/app/views/my_wiki/search_result.rhtml +10 -0
- data/my_wiki/templates/app/views/my_wiki/show.rhtml +31 -0
- data/my_wiki/templates/app/views/my_wiki_admin/index.rhtml +5 -0
- data/my_wiki/templates/app/views/my_wiki_admin/setting.rhtml +64 -0
- data/my_wiki/templates/app/views/my_wiki_mailer/inform.rhtml +3 -0
- data/my_wiki/templates/app/views/page_admin/_form.rhtml +22 -0
- data/my_wiki/templates/app/views/page_admin/edit.rhtml +9 -0
- data/my_wiki/templates/app/views/page_admin/list.rhtml +55 -0
- data/my_wiki/templates/app/views/page_admin/new.rhtml +8 -0
- data/my_wiki/templates/app/views/page_admin/show.rhtml +8 -0
- data/my_wiki/templates/app/views/user_admin/_form.rhtml +10 -0
- data/my_wiki/templates/app/views/user_admin/change_password.rhtml +9 -0
- data/my_wiki/templates/app/views/user_admin/list.rhtml +25 -0
- data/my_wiki/templates/app/views/user_admin/signup.rhtml +20 -0
- data/my_wiki/templates/components/admin/menu/menu.rhtml +5 -0
- data/my_wiki/templates/components/admin/menu_controller.rb +3 -0
- data/my_wiki/templates/components/list/list/list.rhtml +11 -0
- data/my_wiki/templates/components/list/list_controller.rb +11 -0
- data/my_wiki/templates/components/sidebar/sidebar/show.rhtml +7 -0
- data/my_wiki/templates/components/sidebar/sidebar_controller.rb +9 -0
- data/my_wiki/templates/config/routes.rb +35 -0
- data/my_wiki/templates/db/migrate/001_my_wiki_migration.rb +75 -0
- data/my_wiki/templates/lib/diff/lcs.rb +1105 -0
- data/my_wiki/templates/lib/diff/lcs/array.rb +21 -0
- data/my_wiki/templates/lib/diff/lcs/block.rb +51 -0
- data/my_wiki/templates/lib/diff/lcs/callbacks.rb +322 -0
- data/my_wiki/templates/lib/diff/lcs/change.rb +169 -0
- data/my_wiki/templates/lib/diff/lcs/hunk.rb +257 -0
- data/my_wiki/templates/lib/diff/lcs/ldiff.rb +226 -0
- data/my_wiki/templates/lib/diff/lcs/string.rb +19 -0
- data/my_wiki/templates/lib/login_system.rb +87 -0
- data/my_wiki/templates/lib/markup/simple_markup.rb +489 -0
- data/my_wiki/templates/lib/markup/simple_markup/fragments.rb +329 -0
- data/my_wiki/templates/lib/markup/simple_markup/inline.rb +338 -0
- data/my_wiki/templates/lib/markup/simple_markup/lines.rb +151 -0
- data/my_wiki/templates/lib/markup/simple_markup/preprocess.rb +68 -0
- data/my_wiki/templates/lib/markup/simple_markup/to_flow.rb +188 -0
- data/my_wiki/templates/lib/markup/simple_markup/to_html.rb +302 -0
- data/my_wiki/templates/lib/markup/simple_markup/to_latex.rb +333 -0
- data/my_wiki/templates/lib/my_wiki_plugin.rb +60 -0
- data/my_wiki/templates/lib/my_wiki_plugins/christel.rb +5 -0
- data/my_wiki/templates/lib/my_wiki_plugins/join.rb +3 -0
- data/my_wiki/templates/lib/my_wiki_plugins/link_to_attach.rb +17 -0
- data/my_wiki/templates/lib/my_wiki_plugins/recent.rb +8 -0
- data/my_wiki/templates/public/javascripts/my_wiki.js +8 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/back.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/back1.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/back2.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/foot.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/h1.gif +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/h1.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/menu.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/menu2.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/menu_c.jpg +0 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/my_wiki.css +336 -0
- data/my_wiki/templates/public/stylesheets/my_wiki/title.jpg +0 -0
- metadata +181 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
##########################################################################
|
|
2
|
+
#
|
|
3
|
+
# We store the lines we're working on as objects of class Line.
|
|
4
|
+
# These contain the text of the line, along with a flag indicating the
|
|
5
|
+
# line type, and an indentation level
|
|
6
|
+
|
|
7
|
+
module SM
|
|
8
|
+
|
|
9
|
+
class Line
|
|
10
|
+
INFINITY = 9999
|
|
11
|
+
|
|
12
|
+
BLANK = :BLANK
|
|
13
|
+
HEADING = :HEADING
|
|
14
|
+
LIST = :LIST
|
|
15
|
+
RULE = :RULE
|
|
16
|
+
PARAGRAPH = :PARAGRAPH
|
|
17
|
+
VERBATIM = :VERBATIM
|
|
18
|
+
|
|
19
|
+
# line type
|
|
20
|
+
attr_accessor :type
|
|
21
|
+
|
|
22
|
+
# The indentation nesting level
|
|
23
|
+
attr_accessor :level
|
|
24
|
+
|
|
25
|
+
# The contents
|
|
26
|
+
attr_accessor :text
|
|
27
|
+
|
|
28
|
+
# A prefix or parameter. For LIST lines, this is
|
|
29
|
+
# the text that introduced the list item (the label)
|
|
30
|
+
attr_accessor :param
|
|
31
|
+
|
|
32
|
+
# A flag. For list lines, this is the type of the list
|
|
33
|
+
attr_accessor :flag
|
|
34
|
+
|
|
35
|
+
# the number of leading spaces
|
|
36
|
+
attr_accessor :leading_spaces
|
|
37
|
+
|
|
38
|
+
# true if this line has been deleted from the list of lines
|
|
39
|
+
attr_accessor :deleted
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def initialize(text)
|
|
43
|
+
@text = text.dup
|
|
44
|
+
@deleted = false
|
|
45
|
+
|
|
46
|
+
# expand tabs
|
|
47
|
+
1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
|
|
48
|
+
|
|
49
|
+
# Strip trailing whitespace
|
|
50
|
+
@text.sub!(/\s+$/, '')
|
|
51
|
+
|
|
52
|
+
# and look for leading whitespace
|
|
53
|
+
if @text.length > 0
|
|
54
|
+
@text =~ /^(\s*)/
|
|
55
|
+
@leading_spaces = $1.length
|
|
56
|
+
else
|
|
57
|
+
@leading_spaces = INFINITY
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Return true if this line is blank
|
|
62
|
+
def isBlank?
|
|
63
|
+
@text.length.zero?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# stamp a line with a type, a level, a prefix, and a flag
|
|
67
|
+
def stamp(type, level, param="", flag=nil)
|
|
68
|
+
@type, @level, @param, @flag = type, level, param, flag
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Strip off the leading margin
|
|
73
|
+
#
|
|
74
|
+
|
|
75
|
+
def strip_leading(size)
|
|
76
|
+
if @text.size > size
|
|
77
|
+
@text[0,size] = ""
|
|
78
|
+
else
|
|
79
|
+
@text = ""
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def to_s
|
|
84
|
+
"#@type#@level: #@text"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
###############################################################################
|
|
89
|
+
#
|
|
90
|
+
# A container for all the lines
|
|
91
|
+
#
|
|
92
|
+
|
|
93
|
+
class Lines
|
|
94
|
+
include Enumerable
|
|
95
|
+
|
|
96
|
+
attr_reader :lines # for debugging
|
|
97
|
+
|
|
98
|
+
def initialize(lines)
|
|
99
|
+
@lines = lines
|
|
100
|
+
rewind
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def empty?
|
|
104
|
+
@lines.size.zero?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def each
|
|
108
|
+
@lines.each do |line|
|
|
109
|
+
yield line unless line.deleted
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# def [](index)
|
|
114
|
+
# @lines[index]
|
|
115
|
+
# end
|
|
116
|
+
|
|
117
|
+
def rewind
|
|
118
|
+
@nextline = 0
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def next
|
|
122
|
+
begin
|
|
123
|
+
res = @lines[@nextline]
|
|
124
|
+
@nextline += 1 if @nextline < @lines.size
|
|
125
|
+
end while res and res.deleted and @nextline < @lines.size
|
|
126
|
+
res
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def unget
|
|
130
|
+
@nextline -= 1
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def delete(a_line)
|
|
134
|
+
a_line.deleted = true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def normalize
|
|
138
|
+
margin = @lines.collect{|l| l.leading_spaces}.min
|
|
139
|
+
margin = 0 if margin == Line::INFINITY
|
|
140
|
+
@lines.each {|line| line.strip_leading(margin) } if margin > 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def as_text
|
|
144
|
+
@lines.map {|l| l.text}.join("\n")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def line_types
|
|
148
|
+
@lines.map {|l| l.type }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module SM
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Handle common directives that can occur in a block of text:
|
|
5
|
+
#
|
|
6
|
+
# : include : filename
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
class PreProcess
|
|
10
|
+
|
|
11
|
+
def initialize(input_file_name, include_path)
|
|
12
|
+
@input_file_name = input_file_name
|
|
13
|
+
@include_path = include_path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Look for common options in a chunk of text. Options that
|
|
17
|
+
# we don't handle are passed back to our caller
|
|
18
|
+
# as |directive, param|
|
|
19
|
+
|
|
20
|
+
def handle(text)
|
|
21
|
+
text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
|
|
22
|
+
prefix = $1
|
|
23
|
+
directive = $2.downcase
|
|
24
|
+
param = $3
|
|
25
|
+
|
|
26
|
+
case directive
|
|
27
|
+
when "include"
|
|
28
|
+
filename = param.split[0]
|
|
29
|
+
include_file(filename, prefix)
|
|
30
|
+
|
|
31
|
+
else
|
|
32
|
+
yield(directive, param)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#######
|
|
38
|
+
private
|
|
39
|
+
#######
|
|
40
|
+
|
|
41
|
+
# Include a file, indenting it correctly
|
|
42
|
+
|
|
43
|
+
def include_file(name, indent)
|
|
44
|
+
if (full_name = find_include_file(name))
|
|
45
|
+
content = File.open(full_name) {|f| f.read}
|
|
46
|
+
res = content.gsub(/^#?/, indent)
|
|
47
|
+
else
|
|
48
|
+
$stderr.puts "Couldn't find file to include: '#{name}'"
|
|
49
|
+
''
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Look for the given file in the directory containing the current
|
|
54
|
+
# file, and then in each of the directories specified in the
|
|
55
|
+
# RDOC_INCLUDE path
|
|
56
|
+
|
|
57
|
+
def find_include_file(name)
|
|
58
|
+
to_search = [ File.dirname(@input_file_name) ].concat @include_path
|
|
59
|
+
to_search.each do |dir|
|
|
60
|
+
full_name = File.join(dir, name)
|
|
61
|
+
stat = File.stat(full_name) rescue next
|
|
62
|
+
return full_name if stat.readable?
|
|
63
|
+
end
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
require 'markup/simple_markup/fragments'
|
|
2
|
+
require 'markup/simple_markup/inline'
|
|
3
|
+
require 'cgi'
|
|
4
|
+
|
|
5
|
+
module SM
|
|
6
|
+
|
|
7
|
+
module Flow
|
|
8
|
+
P = Struct.new(:body)
|
|
9
|
+
VERB = Struct.new(:body)
|
|
10
|
+
RULE = Struct.new(:width)
|
|
11
|
+
class LIST
|
|
12
|
+
attr_reader :type, :contents
|
|
13
|
+
def initialize(type)
|
|
14
|
+
@type = type
|
|
15
|
+
@contents = []
|
|
16
|
+
end
|
|
17
|
+
def <<(stuff)
|
|
18
|
+
@contents << stuff
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
LI = Struct.new(:label, :body)
|
|
22
|
+
H = Struct.new(:level, :text)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ToFlow
|
|
26
|
+
LIST_TYPE_TO_HTML = {
|
|
27
|
+
SM::ListBase::BULLET => [ "<ul>", "</ul>" ],
|
|
28
|
+
SM::ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
|
29
|
+
SM::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
|
30
|
+
SM::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
|
31
|
+
SM::ListBase::LABELED => [ "<dl>", "</dl>" ],
|
|
32
|
+
SM::ListBase::NOTE => [ "<table>", "</table>" ],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
InlineTag = Struct.new(:bit, :on, :off)
|
|
36
|
+
|
|
37
|
+
def initialize
|
|
38
|
+
init_tags
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Set up the standard mapping of attributes to HTML tags
|
|
43
|
+
#
|
|
44
|
+
def init_tags
|
|
45
|
+
@attr_tags = [
|
|
46
|
+
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
|
|
47
|
+
InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
|
|
48
|
+
InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# Add a new set of HTML tags for an attribute. We allow
|
|
54
|
+
# separate start and end tags for flexibility
|
|
55
|
+
#
|
|
56
|
+
def add_tag(name, start, stop)
|
|
57
|
+
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Given an HTML tag, decorate it with class information
|
|
62
|
+
# and the like if required. This is a no-op in the base
|
|
63
|
+
# class, but is overridden in HTML output classes that
|
|
64
|
+
# implement style sheets
|
|
65
|
+
|
|
66
|
+
def annotate(tag)
|
|
67
|
+
tag
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# Here's the client side of the visitor pattern
|
|
72
|
+
|
|
73
|
+
def start_accepting
|
|
74
|
+
@res = []
|
|
75
|
+
@list_stack = []
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def end_accepting
|
|
79
|
+
@res
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def accept_paragraph(am, fragment)
|
|
83
|
+
@res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def accept_verbatim(am, fragment)
|
|
87
|
+
@res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def accept_rule(am, fragment)
|
|
91
|
+
size = fragment.param
|
|
92
|
+
size = 10 if size > 10
|
|
93
|
+
@res << Flow::RULE.new(size)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def accept_list_start(am, fragment)
|
|
97
|
+
@list_stack.push(@res)
|
|
98
|
+
list = Flow::LIST.new(fragment.type)
|
|
99
|
+
@res << list
|
|
100
|
+
@res = list
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def accept_list_end(am, fragment)
|
|
104
|
+
@res = @list_stack.pop
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def accept_list_item(am, fragment)
|
|
108
|
+
@res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def accept_blank_line(am, fragment)
|
|
112
|
+
# @res << annotate("<p />") << "\n"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def accept_heading(am, fragment)
|
|
116
|
+
@res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
#######################################################################
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
#######################################################################
|
|
125
|
+
|
|
126
|
+
def on_tags(res, item)
|
|
127
|
+
attr_mask = item.turn_on
|
|
128
|
+
return if attr_mask.zero?
|
|
129
|
+
|
|
130
|
+
@attr_tags.each do |tag|
|
|
131
|
+
if attr_mask & tag.bit != 0
|
|
132
|
+
res << annotate(tag.on)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def off_tags(res, item)
|
|
138
|
+
attr_mask = item.turn_off
|
|
139
|
+
return if attr_mask.zero?
|
|
140
|
+
|
|
141
|
+
@attr_tags.reverse_each do |tag|
|
|
142
|
+
if attr_mask & tag.bit != 0
|
|
143
|
+
res << annotate(tag.off)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def convert_flow(flow)
|
|
149
|
+
res = ""
|
|
150
|
+
flow.each do |item|
|
|
151
|
+
case item
|
|
152
|
+
when String
|
|
153
|
+
res << convert_string(item)
|
|
154
|
+
when AttrChanger
|
|
155
|
+
off_tags(res, item)
|
|
156
|
+
on_tags(res, item)
|
|
157
|
+
when Special
|
|
158
|
+
res << convert_special(item)
|
|
159
|
+
else
|
|
160
|
+
raise "Unknown flow element: #{item.inspect}"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
res
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# some of these patterns are taken from SmartyPants...
|
|
167
|
+
|
|
168
|
+
def convert_string(item)
|
|
169
|
+
CGI.escapeHTML(item)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def convert_special(special)
|
|
173
|
+
handled = false
|
|
174
|
+
Attribute.each_name_of(special.type) do |name|
|
|
175
|
+
method_name = "handle_special_#{name}"
|
|
176
|
+
if self.respond_to? method_name
|
|
177
|
+
special.text = send(method_name, special)
|
|
178
|
+
handled = true
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
raise "Unhandled special: #{special}" unless handled
|
|
182
|
+
special.text
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
end
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
require 'markup/simple_markup/fragments'
|
|
2
|
+
require 'markup/simple_markup/inline'
|
|
3
|
+
|
|
4
|
+
require 'cgi'
|
|
5
|
+
|
|
6
|
+
module SM
|
|
7
|
+
|
|
8
|
+
class ToHtml
|
|
9
|
+
|
|
10
|
+
LIST_TYPE_TO_HTML = {
|
|
11
|
+
ListBase::BULLET => [ "<ul>", "</ul>" ],
|
|
12
|
+
ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
|
13
|
+
ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
|
14
|
+
ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
|
15
|
+
ListBase::LABELED => [ "<dl>", "</dl>" ],
|
|
16
|
+
ListBase::NOTE => [ "<table>", "</table>" ],
|
|
17
|
+
ListBase::TABLE => [ '<table class="table" cellspacing="1" border="0">', "</table>" ], # added by ando
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
InlineTag = Struct.new(:bit, :on, :off)
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
init_tags
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Set up the standard mapping of attributes to HTML tags
|
|
28
|
+
#
|
|
29
|
+
def init_tags
|
|
30
|
+
@attr_tags = [
|
|
31
|
+
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
|
|
32
|
+
InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
|
|
33
|
+
InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Add a new set of HTML tags for an attribute. We allow
|
|
39
|
+
# separate start and end tags for flexibility
|
|
40
|
+
#
|
|
41
|
+
def add_tag(name, start, stop)
|
|
42
|
+
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Given an HTML tag, decorate it with class information
|
|
47
|
+
# and the like if required. This is a no-op in the base
|
|
48
|
+
# class, but is overridden in HTML output classes that
|
|
49
|
+
# implement style sheets
|
|
50
|
+
|
|
51
|
+
def annotate(tag)
|
|
52
|
+
tag
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Here's the client side of the visitor pattern
|
|
57
|
+
|
|
58
|
+
def start_accepting
|
|
59
|
+
@res = ""
|
|
60
|
+
@in_list_entry = []
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def end_accepting
|
|
64
|
+
@res
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def accept_paragraph(am, fragment)
|
|
68
|
+
@res << annotate("<p>") + "\n"
|
|
69
|
+
@res << wrap(convert_flow(am.flow(fragment.txt)))
|
|
70
|
+
@res << annotate("</p>") + "\n"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def accept_verbatim(am, fragment)
|
|
74
|
+
@res << annotate("<pre>") + "\n"
|
|
75
|
+
@res << CGI.escapeHTML(fragment.txt)
|
|
76
|
+
@res << annotate("</pre>") << "\n"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def accept_rule(am, fragment)
|
|
80
|
+
size = fragment.param
|
|
81
|
+
size = 10 if size > 10
|
|
82
|
+
@res << "<hr size=\"#{size}\"></hr>"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def accept_list_start(am, fragment)
|
|
86
|
+
@res << html_list_name(fragment.type, true) <<"\n"
|
|
87
|
+
@in_list_entry.push false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def accept_list_end(am, fragment)
|
|
91
|
+
if tag = @in_list_entry.pop
|
|
92
|
+
@res << annotate(tag) << "\n"
|
|
93
|
+
end
|
|
94
|
+
@res << html_list_name(fragment.type, false) <<"\n"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def accept_list_item(am, fragment)
|
|
98
|
+
if tag = @in_list_entry.last
|
|
99
|
+
@res << annotate(tag) << "\n"
|
|
100
|
+
end
|
|
101
|
+
@res << list_item_start(am, fragment)
|
|
102
|
+
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
|
|
103
|
+
@in_list_entry[-1] = list_end_for(fragment.type)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def accept_blank_line(am, fragment)
|
|
107
|
+
# @res << annotate("<p />") << "\n"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def accept_heading(am, fragment)
|
|
111
|
+
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# This is a higher speed (if messier) version of wrap
|
|
115
|
+
|
|
116
|
+
def wrap(txt, line_len = 76)
|
|
117
|
+
res = ""
|
|
118
|
+
sp = 0
|
|
119
|
+
ep = txt.length
|
|
120
|
+
while sp < ep
|
|
121
|
+
# scan back for a space
|
|
122
|
+
p = sp + line_len - 1
|
|
123
|
+
if p >= ep
|
|
124
|
+
p = ep
|
|
125
|
+
else
|
|
126
|
+
while p > sp and txt[p] != ?\s
|
|
127
|
+
p -= 1
|
|
128
|
+
end
|
|
129
|
+
if p <= sp
|
|
130
|
+
p = sp + line_len
|
|
131
|
+
while p < ep and txt[p] != ?\s
|
|
132
|
+
p += 1
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
res << txt[sp...p] << "\n"
|
|
137
|
+
sp = p
|
|
138
|
+
sp += 1 while sp < ep and txt[sp] == ?\s
|
|
139
|
+
end
|
|
140
|
+
res
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
#######################################################################
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
#######################################################################
|
|
148
|
+
|
|
149
|
+
def on_tags(res, item)
|
|
150
|
+
attr_mask = item.turn_on
|
|
151
|
+
return if attr_mask.zero?
|
|
152
|
+
|
|
153
|
+
@attr_tags.each do |tag|
|
|
154
|
+
if attr_mask & tag.bit != 0
|
|
155
|
+
res << annotate(tag.on)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def off_tags(res, item)
|
|
161
|
+
attr_mask = item.turn_off
|
|
162
|
+
return if attr_mask.zero?
|
|
163
|
+
|
|
164
|
+
@attr_tags.reverse_each do |tag|
|
|
165
|
+
if attr_mask & tag.bit != 0
|
|
166
|
+
res << annotate(tag.off)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def convert_flow(flow)
|
|
172
|
+
res = ""
|
|
173
|
+
flow.each do |item|
|
|
174
|
+
case item
|
|
175
|
+
when String
|
|
176
|
+
res << convert_string(item)
|
|
177
|
+
when AttrChanger
|
|
178
|
+
off_tags(res, item)
|
|
179
|
+
on_tags(res, item)
|
|
180
|
+
when Special
|
|
181
|
+
res << convert_special(item)
|
|
182
|
+
else
|
|
183
|
+
raise "Unknown flow element: #{item.inspect}"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
res
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# some of these patterns are taken from SmartyPants...
|
|
190
|
+
|
|
191
|
+
def convert_string(item)
|
|
192
|
+
item = CGI.escapeHTML(item).
|
|
193
|
+
|
|
194
|
+
# convert -- to em-dash, (-- to en-dash)
|
|
195
|
+
gsub(/---?/, '—'). #gsub(/--/, '–').
|
|
196
|
+
|
|
197
|
+
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
|
|
198
|
+
gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…').
|
|
199
|
+
|
|
200
|
+
# convert single closing quote
|
|
201
|
+
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1’" }.
|
|
202
|
+
gsub(%r{\'(?=\W|s\b)}) { "’" }.
|
|
203
|
+
|
|
204
|
+
# convert single opening quote
|
|
205
|
+
gsub(/'/, '‘').
|
|
206
|
+
|
|
207
|
+
# convert double closing quote
|
|
208
|
+
gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1”" }.
|
|
209
|
+
|
|
210
|
+
# convert double opening quote
|
|
211
|
+
gsub(/'/, '“').
|
|
212
|
+
|
|
213
|
+
# convert copyright
|
|
214
|
+
gsub(/\(c\)/, '©').
|
|
215
|
+
|
|
216
|
+
# convert and registered trademark
|
|
217
|
+
gsub(/\(r\)/, '®')
|
|
218
|
+
|
|
219
|
+
# convert column separator (by ando)
|
|
220
|
+
if item =~ /^\|\|(.*)/
|
|
221
|
+
#item = item.sub(/^\|\|/, '').gsub('||', '</td><td>')
|
|
222
|
+
item = $1.gsub('||', '</td><td>')
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
item
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def convert_special(special)
|
|
229
|
+
handled = false
|
|
230
|
+
Attribute.each_name_of(special.type) do |name|
|
|
231
|
+
method_name = "handle_special_#{name}"
|
|
232
|
+
if self.respond_to? method_name
|
|
233
|
+
special.text = send(method_name, special)
|
|
234
|
+
handled = true
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
raise "Unhandled special: #{special}" unless handled
|
|
238
|
+
special.text
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def convert_heading(level, flow)
|
|
242
|
+
res =
|
|
243
|
+
annotate("<h#{level}>") +
|
|
244
|
+
convert_flow(flow) +
|
|
245
|
+
annotate("</h#{level}>\n")
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def html_list_name(list_type, is_open_tag)
|
|
249
|
+
tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
|
|
250
|
+
annotate(tags[ is_open_tag ? 0 : 1])
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def list_item_start(am, fragment)
|
|
254
|
+
case fragment.type
|
|
255
|
+
when ListBase::BULLET, ListBase::NUMBER
|
|
256
|
+
annotate("<li>")
|
|
257
|
+
|
|
258
|
+
when ListBase::UPPERALPHA
|
|
259
|
+
annotate("<li type=\"A\">")
|
|
260
|
+
|
|
261
|
+
when ListBase::LOWERALPHA
|
|
262
|
+
annotate("<li type=\"a\">")
|
|
263
|
+
|
|
264
|
+
when ListBase::LABELED
|
|
265
|
+
annotate("<dt>") +
|
|
266
|
+
convert_flow(am.flow(fragment.param)) +
|
|
267
|
+
annotate("</dt>") +
|
|
268
|
+
annotate("<dd>")
|
|
269
|
+
|
|
270
|
+
when ListBase::NOTE
|
|
271
|
+
annotate("<tr>") +
|
|
272
|
+
annotate("<td valign=\"top\">") +
|
|
273
|
+
convert_flow(am.flow(fragment.param)) +
|
|
274
|
+
annotate("</td>") +
|
|
275
|
+
annotate("<td>")
|
|
276
|
+
|
|
277
|
+
when ListBase::TABLE # added by ando
|
|
278
|
+
annotate("<tr><td>")
|
|
279
|
+
|
|
280
|
+
else
|
|
281
|
+
raise "Invalid list type"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def list_end_for(fragment_type)
|
|
286
|
+
case fragment_type
|
|
287
|
+
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
|
|
288
|
+
"</li>"
|
|
289
|
+
when ListBase::LABELED
|
|
290
|
+
"</dd>"
|
|
291
|
+
when ListBase::NOTE
|
|
292
|
+
"</td></tr>"
|
|
293
|
+
when ListBase::TABLE # added by ando
|
|
294
|
+
"</td></tr>"
|
|
295
|
+
else
|
|
296
|
+
raise "Invalid list type"
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
end
|