motion-kramdown 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +84 -0
- data/lib/kramdown/compatibility.rb +36 -0
- data/lib/kramdown/converter/base.rb +259 -0
- data/lib/kramdown/converter/html.rb +461 -0
- data/lib/kramdown/converter/kramdown.rb +423 -0
- data/lib/kramdown/converter/latex.rb +600 -0
- data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
- data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
- data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
- data/lib/kramdown/converter/pdf.rb +624 -0
- data/lib/kramdown/converter/remove_html_tags.rb +53 -0
- data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
- data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
- data/lib/kramdown/converter/toc.rb +69 -0
- data/lib/kramdown/converter.rb +69 -0
- data/lib/kramdown/document.rb +144 -0
- data/lib/kramdown/element.rb +515 -0
- data/lib/kramdown/error.rb +17 -0
- data/lib/kramdown/options.rb +584 -0
- data/lib/kramdown/parser/base.rb +130 -0
- data/lib/kramdown/parser/gfm.rb +55 -0
- data/lib/kramdown/parser/html.rb +575 -0
- data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
- data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
- data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
- data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
- data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
- data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
- data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
- data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
- data/lib/kramdown/parser/kramdown/eob.rb +26 -0
- data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
- data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
- data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
- data/lib/kramdown/parser/kramdown/header.rb +59 -0
- data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
- data/lib/kramdown/parser/kramdown/html.rb +160 -0
- data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
- data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
- data/lib/kramdown/parser/kramdown/link.rb +139 -0
- data/lib/kramdown/parser/kramdown/list.rb +256 -0
- data/lib/kramdown/parser/kramdown/math.rb +54 -0
- data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
- data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
- data/lib/kramdown/parser/kramdown/table.rb +171 -0
- data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
- data/lib/kramdown/parser/kramdown.rb +359 -0
- data/lib/kramdown/parser/markdown.rb +56 -0
- data/lib/kramdown/parser.rb +27 -0
- data/lib/kramdown/utils/configurable.rb +44 -0
- data/lib/kramdown/utils/entities.rb +347 -0
- data/lib/kramdown/utils/html.rb +75 -0
- data/lib/kramdown/utils/ordered_hash.rb +87 -0
- data/lib/kramdown/utils/string_scanner.rb +74 -0
- data/lib/kramdown/utils/unidecoder.rb +51 -0
- data/lib/kramdown/utils.rb +58 -0
- data/lib/kramdown/version.rb +15 -0
- data/lib/kramdown.rb +10 -0
- data/lib/motion-kramdown.rb +47 -0
- data/lib/rubymotion/encodings.rb +37 -0
- data/lib/rubymotion/rexml_shim.rb +25 -0
- data/lib/rubymotion/set.rb +1349 -0
- data/lib/rubymotion/version.rb +6 -0
- data/spec/document_tree.rb +48 -0
- data/spec/gfm_to_html.rb +95 -0
- data/spec/helpers/it_behaves_like.rb +27 -0
- data/spec/helpers/option_file.rb +46 -0
- data/spec/helpers/spec_options.rb +37 -0
- data/spec/helpers/tidy.rb +12 -0
- data/spec/html_to_html.rb +40 -0
- data/spec/html_to_kramdown_to_html.rb +46 -0
- data/spec/kramdown_to_xxx.rb +40 -0
- data/spec/test_location.rb +203 -0
- data/spec/test_string_scanner_kramdown.rb +19 -0
- data/spec/text_to_kramdown_to_html.rb +52 -0
- data/spec/text_to_latex.rb +33 -0
- metadata +164 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown which is licensed under the MIT.
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
|
10
|
+
module Kramdown::Converter::SyntaxHighlighter
|
11
|
+
|
12
|
+
# Uses Coderay to highlight code blocks and code spans.
|
13
|
+
module Coderay
|
14
|
+
|
15
|
+
begin
|
16
|
+
# RM require 'coderay'
|
17
|
+
|
18
|
+
# Highlighting via coderay is available if this constant is +true+.
|
19
|
+
# RM AVAILABLE = true
|
20
|
+
# RM rescue LoadError
|
21
|
+
AVAILABLE = false # :nodoc:
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.call(converter, text, lang, type, _unused_opts)
|
25
|
+
return nil unless converter.options[:enable_coderay]
|
26
|
+
|
27
|
+
if type == :span && lang
|
28
|
+
::CodeRay.scan(text, lang.to_sym).html(options(converter, :span)).chomp
|
29
|
+
elsif type == :block && (lang || options(converter, :default_lang))
|
30
|
+
lang = (lang || options(converter, :default_lang)).to_sym
|
31
|
+
::CodeRay.scan(text, lang).html(options(converter, :block)).chomp << "\n"
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.options(converter, type)
|
38
|
+
prepare_options(converter)
|
39
|
+
converter.data[:syntax_highlighter_coderay][type]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.prepare_options(converter)
|
43
|
+
return if converter.data.key?(:syntax_highlighter_coderay)
|
44
|
+
|
45
|
+
cache = converter.data[:syntax_highlighter_coderay] = {}
|
46
|
+
|
47
|
+
opts = converter.options[:syntax_highlighter_opts].dup
|
48
|
+
span_opts = (opts.delete(:span) || {}).dup
|
49
|
+
block_opts = (opts.delete(:block) || {}).dup
|
50
|
+
[span_opts, block_opts].each do |hash|
|
51
|
+
hash.keys.each do |k|
|
52
|
+
hash[k.kind_of?(String) ? Kramdown::Options.str_to_sym(k) : k] = hash.delete(k)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
cache[:default_lang] = converter.options[:coderay_default_lang] || opts.delete(:default_lang)
|
57
|
+
cache[:span] = {
|
58
|
+
:css => converter.options[:coderay_css]
|
59
|
+
}.update(opts).update(span_opts).update(:wrap => :span)
|
60
|
+
cache[:block] = {
|
61
|
+
:wrap => converter.options[:coderay_wrap],
|
62
|
+
:line_numbers => converter.options[:coderay_line_numbers],
|
63
|
+
:line_number_start => converter.options[:coderay_line_number_start],
|
64
|
+
:tab_width => converter.options[:coderay_tab_width],
|
65
|
+
:bold_every => converter.options[:coderay_bold_every],
|
66
|
+
:css => converter.options[:coderay_css]
|
67
|
+
}.update(opts).update(block_opts)
|
68
|
+
|
69
|
+
[:css, :wrap, :line_numbers].each do |key|
|
70
|
+
[:block, :span].each do |type|
|
71
|
+
cache[type][key] = cache[type][key].to_sym if cache[type][key].kind_of?(String)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown which is licensed under the MIT.
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
|
10
|
+
module Kramdown::Converter::SyntaxHighlighter
|
11
|
+
|
12
|
+
# Uses Rouge which is CSS-compatible to Pygments to highlight code blocks and code spans.
|
13
|
+
module Rouge
|
14
|
+
|
15
|
+
begin
|
16
|
+
# RM require 'rouge'
|
17
|
+
|
18
|
+
# Highlighting via Rouge is available if this constant is +true+.
|
19
|
+
# RM AVAILABLE = true
|
20
|
+
# RM rescue LoadError, SyntaxError
|
21
|
+
AVAILABLE = false # :nodoc:
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.call(converter, text, lang, type, _unused_opts)
|
25
|
+
opts = converter.options[:syntax_highlighter_opts].dup
|
26
|
+
lexer = ::Rouge::Lexer.find_fancy(lang || opts[:default_lang], text)
|
27
|
+
return nil unless lexer
|
28
|
+
|
29
|
+
opts[:wrap] = false if type == :span
|
30
|
+
|
31
|
+
formatter = ::Rouge::Formatters::HTML.new(opts)
|
32
|
+
formatter.format(lexer.lex(text))
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown which is licensed under the MIT.
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
|
10
|
+
# RM require 'rexml/parsers/baseparser'
|
11
|
+
|
12
|
+
module Kramdown
|
13
|
+
|
14
|
+
module Converter
|
15
|
+
|
16
|
+
# Converts a Kramdown::Document to an element tree that represents the table of contents.
|
17
|
+
#
|
18
|
+
# The returned tree consists of Element objects of type :toc where the root element is just used
|
19
|
+
# as container object. Each :toc element contains as value the wrapped :header element and under
|
20
|
+
# the attribute key :id the header ID that should be used (note that this ID may not exist in
|
21
|
+
# the wrapped element).
|
22
|
+
#
|
23
|
+
# Since the TOC tree consists of special :toc elements, one cannot directly feed this tree to
|
24
|
+
# other converters!
|
25
|
+
class Toc < Base
|
26
|
+
|
27
|
+
def initialize(root, options)
|
28
|
+
super
|
29
|
+
@toc = Element.new(:toc)
|
30
|
+
@stack = []
|
31
|
+
@options[:template] = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
def convert(el)
|
35
|
+
if el.type == :header && in_toc?(el)
|
36
|
+
attr = el.attr.dup
|
37
|
+
attr['id'] = generate_id(el.options[:raw_text]) if @options[:auto_ids] && !attr['id']
|
38
|
+
add_to_toc(el, attr['id']) if attr['id']
|
39
|
+
else
|
40
|
+
el.children.each {|child| convert(child)}
|
41
|
+
end
|
42
|
+
@toc
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def add_to_toc(el, id)
|
48
|
+
toc_element = Element.new(:toc, el, :id => id)
|
49
|
+
|
50
|
+
success = false
|
51
|
+
while !success
|
52
|
+
if @stack.empty?
|
53
|
+
@toc.children << toc_element
|
54
|
+
@stack << toc_element
|
55
|
+
success = true
|
56
|
+
elsif @stack.last.value.options[:level] < el.options[:level]
|
57
|
+
@stack.last.children << toc_element
|
58
|
+
@stack << toc_element
|
59
|
+
success = true
|
60
|
+
else
|
61
|
+
@stack.pop
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown which is licensed under the MIT.
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
|
10
|
+
# RM require 'kramdown/utils'
|
11
|
+
|
12
|
+
module Kramdown
|
13
|
+
|
14
|
+
# This module contains all available converters, i.e. classes that take a root Element and convert
|
15
|
+
# it to a specific output format. The result is normally a string. For example, the
|
16
|
+
# Converter::Html module converts an element tree into valid HTML.
|
17
|
+
#
|
18
|
+
# Converters use the Base class for common functionality (like applying a template to the output)
|
19
|
+
# \- see its API documentation for how to create a custom converter class.
|
20
|
+
module Converter
|
21
|
+
|
22
|
+
# RM autoload :Base, 'kramdown/converter/base'
|
23
|
+
# RM autoload :Html, 'kramdown/converter/html'
|
24
|
+
# RM autoload :Latex, 'kramdown/converter/latex'
|
25
|
+
# RM autoload :Kramdown, 'kramdown/converter/kramdown'
|
26
|
+
# RM autoload :Toc, 'kramdown/converter/toc'
|
27
|
+
# RM autoload :RemoveHtmlTags, 'kramdown/converter/remove_html_tags'
|
28
|
+
# RM autoload :Pdf, 'kramdown/converter/pdf'
|
29
|
+
|
30
|
+
extend ::Kramdown::Utils::Configurable
|
31
|
+
|
32
|
+
configurable(:syntax_highlighter)
|
33
|
+
|
34
|
+
# RM ["Coderay", "Rouge"].each do |klass_name|
|
35
|
+
# RM kn_down = klass_name.downcase.intern
|
36
|
+
# RM add_syntax_highlighter(kn_down) do |converter, text, lang, type, opts|
|
37
|
+
# RM require "kramdown/converter/syntax_highlighter/#{kn_down}"
|
38
|
+
# RM klass = ::Kramdown::Utils.deep_const_get("::Kramdown::Converter::SyntaxHighlighter::#{klass_name}")
|
39
|
+
# RM if klass::AVAILABLE
|
40
|
+
# RM add_syntax_highlighter(kn_down, klass)
|
41
|
+
# RM else
|
42
|
+
# RM add_syntax_highlighter(kn_down) {|*args| nil}
|
43
|
+
# RM end
|
44
|
+
# RM syntax_highlighter(kn_down).call(converter, text, lang, type, opts)
|
45
|
+
# RM end
|
46
|
+
# RM end
|
47
|
+
# RM
|
48
|
+
# RM configurable(:math_engine)
|
49
|
+
# RM
|
50
|
+
# RM require 'kramdown/converter/math_engine/mathjax'
|
51
|
+
# RM add_math_engine(:mathjax, ::Kramdown::Converter::MathEngine::Mathjax)
|
52
|
+
# RM
|
53
|
+
# RM ["Ritex", "Itex2MML"].each do |klass_name|
|
54
|
+
# RM kn_down = klass_name.downcase.intern
|
55
|
+
# RM add_math_engine(kn_down) do |converter, el, opts|
|
56
|
+
# RM require "kramdown/converter/math_engine/#{kn_down}"
|
57
|
+
# RM klass = ::Kramdown::Utils.deep_const_get("::Kramdown::Converter::MathEngine::#{klass_name}")
|
58
|
+
# RM if klass::AVAILABLE
|
59
|
+
# RM add_math_engine(kn_down, klass)
|
60
|
+
# RM else
|
61
|
+
# RM add_math_engine(kn_down) {|*args| nil}
|
62
|
+
# RM end
|
63
|
+
# RM math_engine(kn_down).call(converter, el, opts)
|
64
|
+
# RM end
|
65
|
+
# RM end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown which is licensed under the MIT.
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
# = kramdown
|
10
|
+
#
|
11
|
+
# kramdown is yet-another-markdown-parser but fast, pure Ruby, using a strict syntax definition and
|
12
|
+
# supporting several common extensions.
|
13
|
+
#
|
14
|
+
# The kramdown library is mainly written to support the kramdown-to-HTML conversion chain. However,
|
15
|
+
# due to its flexibility it supports other input and output formats as well. Here is a list of the
|
16
|
+
# supported formats:
|
17
|
+
#
|
18
|
+
# * input formats: kramdown (a Markdown superset), Markdown, HTML
|
19
|
+
# * output formats: HTML, kramdown, LaTeX (and therefore PDF)
|
20
|
+
#
|
21
|
+
# All the documentation on the available input and output formats is available at
|
22
|
+
# http://kramdown.gettalong.org.
|
23
|
+
#
|
24
|
+
# == Usage
|
25
|
+
#
|
26
|
+
# kramdown has a basic *Cloth API, so using kramdown is as easy as
|
27
|
+
#
|
28
|
+
# require 'kramdown'
|
29
|
+
#
|
30
|
+
# Kramdown::Document.new(text).to_html
|
31
|
+
#
|
32
|
+
# For detailed information have a look at the Kramdown::Document class.
|
33
|
+
#
|
34
|
+
# == License
|
35
|
+
#
|
36
|
+
# MIT - see the COPYING file.
|
37
|
+
|
38
|
+
|
39
|
+
# RM require 'kramdown/compatibility'
|
40
|
+
#
|
41
|
+
# RM require 'kramdown/version'
|
42
|
+
# RM require 'kramdown/element'
|
43
|
+
# RM require 'kramdown/error'
|
44
|
+
# RM require 'kramdown/parser'
|
45
|
+
# RM require 'kramdown/converter'
|
46
|
+
# RM require 'kramdown/options'
|
47
|
+
# RM require 'kramdown/utils'
|
48
|
+
|
49
|
+
module Kramdown
|
50
|
+
|
51
|
+
# Return the data directory for kramdown.
|
52
|
+
def self.data_dir
|
53
|
+
unless defined?(@@data_dir)
|
54
|
+
# RM require 'rbconfig'
|
55
|
+
@@data_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'kramdown'))
|
56
|
+
@@data_dir = File.expand_path(File.join(Config::CONFIG["datadir"], "kramdown")) if !File.exists?(@@data_dir)
|
57
|
+
raise "kramdown data directory not found! This is a bug, please report it!" unless File.directory?(@@data_dir)
|
58
|
+
end
|
59
|
+
@@data_dir
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# The main interface to kramdown.
|
64
|
+
#
|
65
|
+
# This class provides a one-stop-shop for using kramdown to convert text into various output
|
66
|
+
# formats. Use it like this:
|
67
|
+
#
|
68
|
+
# require 'kramdown'
|
69
|
+
# doc = Kramdown::Document.new('This *is* some kramdown text')
|
70
|
+
# puts doc.to_html
|
71
|
+
#
|
72
|
+
# The #to_html method is a shortcut for using the Converter::Html class. See #method_missing for
|
73
|
+
# more information.
|
74
|
+
#
|
75
|
+
# The second argument to the ::new method is an options hash for customizing the behaviour of the
|
76
|
+
# used parser and the converter. See ::new for more information!
|
77
|
+
class Document
|
78
|
+
|
79
|
+
# The root Element of the element tree. It is immediately available after the ::new method has
|
80
|
+
# been called.
|
81
|
+
attr_accessor :root
|
82
|
+
|
83
|
+
# The options hash which holds the options for parsing/converting the Kramdown document.
|
84
|
+
attr_reader :options
|
85
|
+
|
86
|
+
# An array of warning messages. It is filled with warnings during the parsing phase (i.e. in
|
87
|
+
# ::new) and the conversion phase.
|
88
|
+
attr_reader :warnings
|
89
|
+
|
90
|
+
|
91
|
+
# Create a new Kramdown document from the string +source+ and use the provided +options+. The
|
92
|
+
# options that can be used are defined in the Options module.
|
93
|
+
#
|
94
|
+
# The special options key :input can be used to select the parser that should parse the
|
95
|
+
# +source+. It has to be the name of a class in the Kramdown::Parser module. For example, to
|
96
|
+
# select the kramdown parser, one would set the :input key to +Kramdown+. If this key is not
|
97
|
+
# set, it defaults to +Kramdown+.
|
98
|
+
#
|
99
|
+
# The +source+ is immediately parsed by the selected parser so that the root element is
|
100
|
+
# immediately available and the output can be generated.
|
101
|
+
def initialize(source, options = {})
|
102
|
+
@options = Options.merge(options).freeze
|
103
|
+
parser = (options[:input] || 'kramdown').to_s
|
104
|
+
parser = parser[0..0].upcase + parser[1..-1]
|
105
|
+
try_require('parser', parser)
|
106
|
+
if Parser.const_defined?(parser)
|
107
|
+
@root, @warnings = Parser.const_get(parser).parse(source, @options)
|
108
|
+
else
|
109
|
+
raise Kramdown::Error.new("kramdown has no parser to handle the specified input format: #{options[:input]}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Check if a method is invoked that begins with +to_+ and if so, try to instantiate a converter
|
114
|
+
# class (i.e. a class in the Kramdown::Converter module) and use it for converting the document.
|
115
|
+
#
|
116
|
+
# For example, +to_html+ would instantiate the Kramdown::Converter::Html class.
|
117
|
+
def method_missing(id, *attr, &block)
|
118
|
+
if id.to_s =~ /^to_(\w+)$/ && (name = Utils.camelize($1)) &&
|
119
|
+
try_require('converter', name) && Converter.const_defined?(name)
|
120
|
+
output, warnings = Converter.const_get(name).convert(@root, @options)
|
121
|
+
@warnings.concat(warnings)
|
122
|
+
output
|
123
|
+
else
|
124
|
+
super
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def inspect #:nodoc:
|
129
|
+
"<KD:Document: options=#{@options.inspect} root=#{@root.inspect} warnings=#{@warnings.inspect}>"
|
130
|
+
end
|
131
|
+
|
132
|
+
# Try requiring a parser or converter class and don't raise an error if the file is not found.
|
133
|
+
def try_require(type, name)
|
134
|
+
# RM require("kramdown/#{type}/#{Utils.snake_case(name)}")
|
135
|
+
# RM true
|
136
|
+
# RM rescue LoadError
|
137
|
+
true
|
138
|
+
end
|
139
|
+
protected :try_require
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|