bivy 0.0.5
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/.gitignore +21 -0
- data/History +16 -0
- data/LICENSE +29 -0
- data/README.rdoc +37 -0
- data/Rakefile +43 -0
- data/TODO +12 -0
- data/VERSION +1 -0
- data/bin/bivy.rb +56 -0
- data/bin/pubmed_to_bivy.rb +78 -0
- data/doc/config.yaml +13 -0
- data/doc/src/default.css +126 -0
- data/doc/src/default.template +38 -0
- data/doc/src/tutorial/basic_flow.page +71 -0
- data/doc/src/tutorial/index.page +8 -0
- data/doc/src/tutorial/new_formats_and_media.page +83 -0
- data/jrn_abbrev/abbr_html.tgz +0 -0
- data/jrn_abbrev/abbr_to_journal.yaml +676 -0
- data/jrn_abbrev/download_abbrevs.rb +20 -0
- data/jrn_abbrev/for_ruby_class.rb +686 -0
- data/jrn_abbrev/html_to_yaml.rb +50 -0
- data/lib/bibliography.rb +144 -0
- data/lib/bivy.rb +4 -0
- data/lib/citation.rb +194 -0
- data/lib/format.rb +120 -0
- data/lib/format/acs.rb +88 -0
- data/lib/format/bioinformatics.rb +33 -0
- data/lib/format/bmc.rb +38 -0
- data/lib/format/jtp.rb +30 -0
- data/lib/format/mla.rb +50 -0
- data/lib/formatter.rb +276 -0
- data/lib/journal.rb +6 -0
- data/lib/journal/iso_to_full.yaml +1320 -0
- data/lib/journal/medline_to_full.yaml +7 -0
- data/lib/journal/medline_to_iso.yaml +45 -0
- data/lib/media.rb +88 -0
- data/lib/media/html.rb +65 -0
- data/lib/ooffice.rb +39 -0
- data/lib/pubmed.rb +209 -0
- data/lib/rtf.rb +217 -0
- data/old_stuff/old_list2refs.rb +103 -0
- data/old_stuff/pubmed2html.rb +119 -0
- data/old_stuff/pubmed_bib_write.rb +92 -0
- data/old_stuff/xml.tmp.xml +115 -0
- data/scripts/merge_bibs.rb +70 -0
- data/spec/bibliography_spec.rb +127 -0
- data/spec/citation_positions.odt +0 -0
- data/spec/formatter_spec.rb +14 -0
- data/spec/formatter_spec/cits_after.xml +2 -0
- data/spec/formatter_spec/cits_before.xml +2 -0
- data/spec/formatter_spec/content.xml +2 -0
- data/spec/ooffice_spec.rb +27 -0
- data/spec/pubmed_spec.rb +26 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/testfiles/doc1.odt +0 -0
- metadata +136 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
---
|
2
|
+
Nature: Nature
|
3
|
+
Biomed Mass Spectrom: Biomed. Mass Spectrom.
|
4
|
+
Nat Biotechnol: Nat. Biotechnol.
|
5
|
+
Anal. Chem.: Anal. Chem.
|
6
|
+
Nat Chem Biol: Nat. Chem. Biol.
|
7
|
+
J. Proteome Res.: J. Proteome Res.
|
8
|
+
Rapid Commun Mass Spectrom: Rapid Commun. Mass Spectrom.
|
9
|
+
KDD Workshop on Mining Temporal and Sequential Data: KDD Workshop MTSD
|
10
|
+
J Chromatogr B Analyt Technol Biomed Life Sci: J. Chromatogr., B
|
11
|
+
J. Chromatogr., A: J. Chromatogr., A
|
12
|
+
J Chemom: J. Chemom.
|
13
|
+
Genome Res: Genome Res.
|
14
|
+
Biomed. Mass Spectrom.: Biomed. Mass Spectrom.
|
15
|
+
SIAM J. Num. Anal.: SIAM J. Num. Anal.
|
16
|
+
Proteomics: Proteomics
|
17
|
+
J. Chemom.: J. Chemom.
|
18
|
+
Eur. Food Res. Technol.: Eur. Food Res. Technol.
|
19
|
+
Environ Sci Technol: Environ. Sci. Technol.
|
20
|
+
Analytica Chimica Acta: Anal. Chim. Acta
|
21
|
+
Nature Methods: Nature Methods
|
22
|
+
J Chromatogr A: J. Chromatogr., A
|
23
|
+
IEEE ASSP: IEEE ASSP
|
24
|
+
Nucleic Acids Res: Nucleic Acids Res.
|
25
|
+
Mol Cell Proteomics: Mol. Cell. Proteomics
|
26
|
+
J Proteome Res: J. Proteome Res.
|
27
|
+
J Mol Biol: J. Mol. Biol.
|
28
|
+
J. Am. Soc. Mass Spectrom.: J. Am. Soc. Mass Spectrom.
|
29
|
+
J. Mol. Biol.: J. Mol. Biol.
|
30
|
+
Bioinformatics: Bioinformatics
|
31
|
+
Anal Chem: Anal. Chem.
|
32
|
+
Mol Cell Proteomics: Mol. Cell. Proteomics
|
33
|
+
Cell Signal: Cell Signal.
|
34
|
+
Curr Opin Chem Biol: Curr. Opin. Chem. Biol.
|
35
|
+
Proc IEEE Comput Syst Bioinform Conf: Proc. IEEE Comput. Syst. Bioinform. Conf.
|
36
|
+
Lab Chip: Lab Chip
|
37
|
+
Clin J Oncol Nurs: Clin. J. Oncol. Nurs.
|
38
|
+
Mass Spectrom Rev: Mass Spectrom. Rev.
|
39
|
+
Nat Protoc: Nat. Protoc.
|
40
|
+
Conf Proc IEEE Eng Med Biol Soc: Conf. Proc. IEEE Eng. Med. Biol. Soc.
|
41
|
+
Curr Opin Oncol: Curr. Opin. Oncol.
|
42
|
+
J Mass Spectrom: J. Mass Spectrom.
|
43
|
+
Trends Pharmacol Sci: Trends Pharmacol. Sci.
|
44
|
+
Assay Drug Dev Technol: Assay Drug Dev. Technol.
|
45
|
+
Mol Pharmacol: Mol. Pharmacol.
|
data/lib/media.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
module Media
|
3
|
+
# note that you need to add the shortcut to module Format::MediaForwarding
|
4
|
+
# hash if you want to be able to access it!
|
5
|
+
|
6
|
+
# add to this class the conversion from the filename (as a symbol) to the
|
7
|
+
# properly capitalized classname. If the class name is just capitalized and
|
8
|
+
# all lower case, not necessary.
|
9
|
+
Symbol_to_class_string = { }
|
10
|
+
#:html => 'HTML'
|
11
|
+
|
12
|
+
def self.new(tp=:jtp)
|
13
|
+
require "media/#{tp}"
|
14
|
+
#puts( $".grep(/html/) )
|
15
|
+
klass_st = ((x = Symbol_to_class_string[tp]) ? x : tp.to_s.capitalize)
|
16
|
+
klass = Media.const_get(klass_st)
|
17
|
+
klass.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def header
|
21
|
+
end
|
22
|
+
|
23
|
+
def footer
|
24
|
+
end
|
25
|
+
|
26
|
+
def call_it(method, string)
|
27
|
+
if var = string
|
28
|
+
send(method, var.to_s)
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parenthesize(string)
|
35
|
+
'(' + string + ')'
|
36
|
+
end
|
37
|
+
|
38
|
+
def bracket(string)
|
39
|
+
'[' + string + ']'
|
40
|
+
end
|
41
|
+
|
42
|
+
def br(string)
|
43
|
+
call_it(:bracket, string)
|
44
|
+
end
|
45
|
+
|
46
|
+
def par(string)
|
47
|
+
call_it(:parenthesize, string)
|
48
|
+
end
|
49
|
+
|
50
|
+
# italicize
|
51
|
+
def i(string)
|
52
|
+
call_it(:italics, string)
|
53
|
+
end
|
54
|
+
|
55
|
+
# bold
|
56
|
+
def b(string)
|
57
|
+
call_it(:bold, string)
|
58
|
+
end
|
59
|
+
|
60
|
+
# underline
|
61
|
+
def u(string)
|
62
|
+
call_it(:underline, string)
|
63
|
+
end
|
64
|
+
|
65
|
+
def italics(string)
|
66
|
+
"<span style=\"font-style:italic;\">" + string + "</span>"
|
67
|
+
end
|
68
|
+
|
69
|
+
def bold(string)
|
70
|
+
"<span style=\"font-weight:bold;\">" + string + "</span>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def underline(string)
|
74
|
+
"<span style=\"text-decoration:underline;\">" + string + "</span>"
|
75
|
+
end
|
76
|
+
|
77
|
+
# create the final bibliography string in whatever media you desire
|
78
|
+
# the example here is html
|
79
|
+
def format(format_object, citations)
|
80
|
+
cts = citations.map do |cit|
|
81
|
+
" <li>" + format_object.format(cit) + "</li>"
|
82
|
+
end
|
83
|
+
"<ol>\n" + cts.join("\n") + "\n</ol>\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
|
data/lib/media/html.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
class Media::HTML
|
3
|
+
include Media
|
4
|
+
Media::Symbol_to_class_string[:html] = 'HTML'
|
5
|
+
|
6
|
+
def header
|
7
|
+
"<html><body>"
|
8
|
+
end
|
9
|
+
|
10
|
+
def footer
|
11
|
+
"</body></html>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def italics(string)
|
15
|
+
"<span style=\"font-style:italic;\">" + string + "</span>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def bold(string)
|
19
|
+
"<span style=\"font-weight:bold;\">" + string + "</span>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def underline(string)
|
23
|
+
"<span style=\"text-decoration:underline;\">" + string + "</span>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def list(citations_as_strings)
|
27
|
+
cts = citations_as_strings.map do |cit|
|
28
|
+
"\t<li>#{cit}</li>"
|
29
|
+
end
|
30
|
+
"<ol>\n" + cts.join("\n") + "\n</ol>\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
# expects opening and closing tags. Operates on last one.
|
34
|
+
# trailing text (outside a tag) is operated on if existing
|
35
|
+
# <tag>text</tag> => <tag>text.</tag>
|
36
|
+
# <tag>text</tag>more_text => '...more_text.'
|
37
|
+
# if the text already has a period, then no change
|
38
|
+
# method periodize (TODO: should alias, really)
|
39
|
+
def periodize(array_or_string)
|
40
|
+
if array_or_string.is_a?(Array)
|
41
|
+
array_or_string.map do |st|
|
42
|
+
periodize(st)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
st = array_or_string
|
46
|
+
if st[-1,1] == '>'
|
47
|
+
st.sub(/(.*)(<\/.*?>)/) do |v|
|
48
|
+
if $1[-1,1] =~ /[\.\?\!]/
|
49
|
+
$1 + $2
|
50
|
+
else
|
51
|
+
$1 + '.' + $2
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
if st[-1,1] =~ /[\.\?\!]/
|
56
|
+
st
|
57
|
+
else
|
58
|
+
st << '.'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/lib/ooffice.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
FU = FileUtils
|
3
|
+
|
4
|
+
class OpenOffice
|
5
|
+
|
6
|
+
# unzips the file, gives a string of the content xml and will replace it
|
7
|
+
# whatever you pass back (preferably a string;)
|
8
|
+
# requires write access to the directory where the file is located
|
9
|
+
# The next time you open the file, it will act like you've corrupted the
|
10
|
+
# file (the character count is probably off, etc) just let it clean it up
|
11
|
+
# for you!
|
12
|
+
# new_basename = base name of the new file (preferably <name>.odt)
|
13
|
+
def modify_content(filename, new_basename, &blk)
|
14
|
+
content_file = 'content.xml'
|
15
|
+
basename = File.basename(filename)
|
16
|
+
tmpdir = basename + ".unzip.tmp"
|
17
|
+
Dir.chdir(File.dirname(filename)) do
|
18
|
+
if File.exist?(tmpdir)
|
19
|
+
warn "#{tmpdir} already exists!"
|
20
|
+
warn "deleting contents of #{tmpdir}"
|
21
|
+
FU.rm_rf(tmpdir)
|
22
|
+
end
|
23
|
+
FU.mkpath(tmpdir)
|
24
|
+
FU.cp(basename, tmpdir)
|
25
|
+
Dir.chdir(tmpdir) do
|
26
|
+
print `unzip -q #{basename}`
|
27
|
+
string = IO.read(content_file)
|
28
|
+
replace_with = blk.call(string)
|
29
|
+
File.open(content_file,'w') {|fh| fh.print(replace_with) }
|
30
|
+
FU.rm(basename, :force => true)
|
31
|
+
to_include = Dir["*"]
|
32
|
+
print `zip -r -q #{new_basename} #{to_include.map {|v| "'" + v + "'" }.join(' ')}`
|
33
|
+
FU.mv new_basename, '..'
|
34
|
+
end
|
35
|
+
FU.rm_rf tmpdir
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/pubmed.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'iconv'
|
4
|
+
require 'citation'
|
5
|
+
|
6
|
+
# given the html page where the display is specified as xml
|
7
|
+
# extracts out the requested pieces
|
8
|
+
class PubMed < Citation::Article
|
9
|
+
|
10
|
+
attr_accessor :pmid
|
11
|
+
|
12
|
+
# also takes pmid=hash of values to set
|
13
|
+
def initialize(pmid=nil, identifier=nil)
|
14
|
+
@quotes = []
|
15
|
+
if pmid.is_a? Hash
|
16
|
+
########## THIS WHOLE MESS SHOULD BE ENCAPSULATED/INHERITED! but can't get
|
17
|
+
#inheritance with authors= working for some reason
|
18
|
+
@authors = []
|
19
|
+
pmid.each do |k,v|
|
20
|
+
if k == 'authors'
|
21
|
+
v.each do |auth|
|
22
|
+
if auth.is_a? String
|
23
|
+
authors.push( Citation::Author.from_s(auth) )
|
24
|
+
else
|
25
|
+
authors.push( auth )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
send("#{k}=".to_sym, v)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
############ <-- END MESS
|
33
|
+
else
|
34
|
+
@authors = []
|
35
|
+
@pmid = pmid
|
36
|
+
@bibtype = :article
|
37
|
+
if pmid
|
38
|
+
begin
|
39
|
+
url = query_builder(pmid)
|
40
|
+
xml_string = get_xml(url)
|
41
|
+
extract_attrs_from_xml(xml_string)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if identifier
|
45
|
+
@ident = identifier
|
46
|
+
else
|
47
|
+
if pmid
|
48
|
+
@ident = create_id
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# returns xml from online (parses html output). No internet connection gives nil
|
56
|
+
def get_xml(query)
|
57
|
+
handle = open(query)
|
58
|
+
xml = handle.read
|
59
|
+
handle.close
|
60
|
+
xml
|
61
|
+
end
|
62
|
+
|
63
|
+
# first author's last name + year collapsing any spaces
|
64
|
+
def create_id
|
65
|
+
(@authors[0].last.to_s + @year.to_s).sub(/\s+/,'')
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
st = "<#{self.class}:##{self.__id__} "
|
70
|
+
st << ( %w(authors ident quotes abstract journal_medline title year month vol issue pages).reject{|v| (v == :authors || v == :url)}.push(:bibtype).map {|v| ":#{v}=>#{send(v).inspect}"}.join(", ") )
|
71
|
+
st << " @authors=[#{authors.map{|g| g.inspect }.join(", ")}]"
|
72
|
+
st << ">"
|
73
|
+
st
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# Builds the query to ask for a citation given a pubmed id
|
78
|
+
# valid types are xml, medline, (...need to figure out others)
|
79
|
+
private
|
80
|
+
|
81
|
+
# returns pubmed query based on pubmed id with xml as the output type. Note that the xml is embedded in the page's html.
|
82
|
+
# Example: http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=pubmed&dopt=xml&list_uids=14654843&query_hl=6
|
83
|
+
#
|
84
|
+
|
85
|
+
# http://www.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=Pubmed&id=11283582&rettype=xml
|
86
|
+
def query_builder(pmid)
|
87
|
+
type = 'xml'
|
88
|
+
#base_url = 'http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?'
|
89
|
+
base_url = 'http://www.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?'
|
90
|
+
cgi_params = ['db=Pubmed', "rettype=#{type}", 'retmode=text', "id=#{pmid}"].join('&')
|
91
|
+
base_url + cgi_params
|
92
|
+
end
|
93
|
+
|
94
|
+
# get an xml element's text according to its path (assumes single element)
|
95
|
+
def get_e_text(element, path)
|
96
|
+
#element = @xml if element == nil
|
97
|
+
els = element.elements.to_a(path)
|
98
|
+
if els.size > 1
|
99
|
+
raise "More than one #{path}!"
|
100
|
+
elsif els.size == 0
|
101
|
+
return nil
|
102
|
+
else
|
103
|
+
begin
|
104
|
+
text = els[0].get_text.value
|
105
|
+
return text
|
106
|
+
rescue NoMethodError
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_author_list(xml)
|
113
|
+
auths = xml.elements.to_a("//PubmedArticle/MedlineCitation/Article/AuthorList/Author")
|
114
|
+
authors = auths.collect do |auth|
|
115
|
+
last_name = get_e_text(auth, "LastName")
|
116
|
+
initials = get_e_text(auth, "Initials")
|
117
|
+
## I think we are getting author names out in UTF-8 which is not being interpreted properly.
|
118
|
+
## Transform characters into something more standard, eh
|
119
|
+
begin
|
120
|
+
last_name = Iconv.new('iso-8859-15', 'utf-8').iconv(last_name)
|
121
|
+
rescue Iconv::IllegalSequence
|
122
|
+
last_name = "**BADCHARS**"
|
123
|
+
end
|
124
|
+
begin
|
125
|
+
initials = Iconv.new('iso-8859-15', 'utf-8').iconv(initials)
|
126
|
+
rescue Iconv::IllegalSequence
|
127
|
+
initials = "**BADINITS**"
|
128
|
+
end
|
129
|
+
Citation::Author.new(last_name, initials)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# if they are not set from the xml, tries to set from hashes or raises a
|
134
|
+
# RuntimeError
|
135
|
+
def set_journals_or_die(journal_medline)
|
136
|
+
error_messages = []
|
137
|
+
unless @journal_iso
|
138
|
+
if Journal::Medline_to_ISO.key?(journal_medline)
|
139
|
+
@journal_iso = Journal::Medline_to_ISO[journal_medline]
|
140
|
+
else
|
141
|
+
error_messages << "Expect key for '#{journal_medline}' in Journal::Medline_to_ISO"
|
142
|
+
error_messages << "(alter file journal/medline_to_iso.yaml)"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
unless @journal_full
|
147
|
+
if Journal::Medline_to_Full.key?(journal_medline)
|
148
|
+
@journal_full = Journal::Medline_to_Full[journal_medline]
|
149
|
+
else
|
150
|
+
error_messages << "Expect key for '#{journal_medline}' in Journal::Medline_to_Full"
|
151
|
+
error_messages << "(alter file journal/medline_to_full.yaml)"
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
if error_messages.size > 0
|
156
|
+
label = "******************************************************************"
|
157
|
+
error_messages.unshift label
|
158
|
+
error_messages.unshift ''
|
159
|
+
error_messages << "Aborting!"
|
160
|
+
error_messages << label
|
161
|
+
error_messages << ''
|
162
|
+
raise(error_messages.join("\n"))
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def extract_attrs_from_xml(xml_string)
|
168
|
+
xml = REXML::Document.new xml_string
|
169
|
+
art = "//PubmedArticle/MedlineCitation/Article/"
|
170
|
+
@title = get_e_text(xml, art + "ArticleTitle")
|
171
|
+
#puts "TITLE: "
|
172
|
+
#puts @title
|
173
|
+
@journal_medline = get_e_text(xml, "//PubmedArticle/MedlineCitation/MedlineJournalInfo/MedlineTA")
|
174
|
+
@journal_full = get_e_text(xml, art + 'Journal/Title')
|
175
|
+
@journal_iso = get_e_text(xml, art + 'Journal/ISOAbbreviation')
|
176
|
+
set_journals_or_die(@journal_medline)
|
177
|
+
|
178
|
+
#puts "THREE JOURNALS"
|
179
|
+
#puts @journal_medline
|
180
|
+
#puts @journal_full
|
181
|
+
#puts @journal_iso
|
182
|
+
@authors = get_author_list(xml)
|
183
|
+
iss = art + "Journal/JournalIssue/"
|
184
|
+
pdate = iss + "PubDate/"
|
185
|
+
@vol = get_e_text(xml, iss + "Volume")
|
186
|
+
@issue = get_e_text(xml, iss + "Issue")
|
187
|
+
@year = get_e_text(xml, pdate + "Year")
|
188
|
+
@month = get_e_text(xml, pdate + "Month")
|
189
|
+
@pages = get_e_text(xml, art + "Pagination/MedlinePgn") || '[Epub]'
|
190
|
+
@abstract = get_e_text(xml, art + "Abstract/AbstractText") || ''
|
191
|
+
end
|
192
|
+
|
193
|
+
# unnecessary now..
|
194
|
+
def pubmed_extract_xml_from_html(string)
|
195
|
+
html = ""
|
196
|
+
if string =~ /<dd><pre>(.*)<\/pre><\/dd>/m
|
197
|
+
html = $1
|
198
|
+
html.gsub!(/<\/?font.*?>/, '')
|
199
|
+
html.gsub!(/<\/?b.*?>/, '')
|
200
|
+
html.gsub!(/\</, '<')
|
201
|
+
html.gsub!(/\>/, '>')
|
202
|
+
html.gsub!(/\"/, '"')
|
203
|
+
end
|
204
|
+
html
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
end
|
209
|
+
|