my_wiki_generator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|