motion-markdown-it-plugins 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 932bce733bfd0e1070663a7b560a5e255817c85c
4
+ data.tar.gz: 54572d459acb95df6d00fd0736ee71f5ecc3691d
5
+ SHA512:
6
+ metadata.gz: 1bf53209e1279e1467415f9c9b8b91f59e57ec526edac36cad5b287993a8ea6b7409202490e84017b9487d98915e359a4012f59f232aaf568335546c5f9632b3
7
+ data.tar.gz: 9b67f602c172395eb74d7d9c2a114a6315df287eb3ae404c821eeb253c11fe0cd828e705bf2f2c17b59827ea47279d277a6882aa8cda0a6753b8d2d9769cbc91
@@ -0,0 +1,18 @@
1
+ # motion-markdown-it-plugins
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/motion-markdown-it-plugins.svg)](http://badge.fury.io/rb/motion-markdown-it-plugins)
4
+
5
+ Various useful plugins for use with `motion-markdown-it`. Works with Ruby and RubyMotion.
6
+
7
+ ## Plugins
8
+
9
+ Each one has a README with details
10
+
11
+ * [Abbreviations](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/abbr)
12
+ * [Checkbox/Tasklists](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/checkbox_replace)
13
+ * [Containers](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/container)
14
+ * [Definition Lists](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/deflist)
15
+ * [Insert](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/ins)
16
+ * [Mark](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/mark)
17
+ * [Subscript](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/sub)
18
+ * [Superscript](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/sup)
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ if defined?(Motion::Project::Config)
4
+
5
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
6
+ Motion::Project::App.setup do |app|
7
+ app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-markdown-it-plugins/**/*.rb")))
8
+ end
9
+
10
+ require 'motion-markdown-it'
11
+
12
+ else
13
+
14
+ require 'motion-markdown-it'
15
+ require 'motion-markdown-it-plugins/checkbox_replace/checkbox_replace'
16
+ require 'motion-markdown-it-plugins/deflist/deflist'
17
+ require 'motion-markdown-it-plugins/abbr/abbr'
18
+ require 'motion-markdown-it-plugins/ins/ins'
19
+ require 'motion-markdown-it-plugins/mark/mark'
20
+ require 'motion-markdown-it-plugins/sub/sub'
21
+ require 'motion-markdown-it-plugins/sup/sup'
22
+ require 'motion-markdown-it-plugins/container/container'
23
+
24
+ end
@@ -0,0 +1,130 @@
1
+ # Enclose abbreviations in <abbr> tags
2
+ #
3
+ # Based on Javascript version: https://github.com/markdown-it/markdown-it-abbr
4
+ #------------------------------------------------------------------------------
5
+ include MarkdownIt::Common::Utils
6
+
7
+ module MotionMarkdownItPlugins
8
+ class Abbr
9
+ PUNCT_CHARS = ' \n()[]\'".,!?-'
10
+ PUNCT_CAHRS_ESCAPED = PUNCT_CHARS.chars.map {|c| escapeRE(c)}.join
11
+
12
+ #------------------------------------------------------------------------------
13
+ def self.init_plugin(md)
14
+ md.block.ruler.before('reference', 'abbr_def',
15
+ lambda { |state, startLine, endLine, silent| Abbr.abbr_def(state, startLine, endLine, silent) },
16
+ {alt: ['', 'paragraph', 'reference']})
17
+ md.core.ruler.after('inline', 'abbr_replace', lambda { |state| Abbr.abbr_replace(state) })
18
+ end
19
+
20
+ #------------------------------------------------------------------------------
21
+ def self.abbr_def(state, startLine, endLine, silent)
22
+ pos = state.bMarks[startLine] + state.tShift[startLine]
23
+ max = state.eMarks[startLine]
24
+
25
+ return false if (pos + 2 >= max)
26
+ return false if (state.src.charCodeAt(pos) != 0x2A) # '*'
27
+ pos += 1
28
+ return false if (state.src.charCodeAt(pos) != 0x5B) # '['
29
+ pos += 1
30
+
31
+ labelStart = pos
32
+
33
+ while pos < max
34
+ ch = state.src.charCodeAt(pos)
35
+ if (ch == 0x5B) # '['
36
+ return false
37
+ elsif (ch == 0x5D) # ']'
38
+ labelEnd = pos
39
+ break
40
+ elsif (ch == 0x5C) # '\'
41
+ pos += 1
42
+ end
43
+ pos += 1
44
+ end
45
+
46
+ return false if (labelEnd < 0 || state.src.charCodeAt(labelEnd + 1) != 0x3A) # ':'
47
+ return true if (silent)
48
+
49
+ label = state.src.slice(labelStart...labelEnd).gsub(/\\(.)/, '\1')
50
+ title = state.src.slice((labelEnd + 2)...max).strip
51
+ return false if (title.length == 0)
52
+
53
+ state.env[:abbreviations] = {} if (!state.env[:abbreviations])
54
+ state.env[:abbreviations][label] = title if state.env[:abbreviations][label].nil?
55
+
56
+ state.line = startLine + 1
57
+ return true
58
+ end
59
+
60
+ #------------------------------------------------------------------------------
61
+ def self.abbr_replace(state)
62
+ blockTokens = state.tokens
63
+
64
+ return if (!state.env[:abbreviations])
65
+ if (!state.env[:abbrRegExp])
66
+ regText = "(^|[#{PUNCT_CAHRS_ESCAPED}])("
67
+ regText << state.env[:abbreviations].keys.sort {|a, b| b.length <=> a.length}.map {|x| escapeRE(x)}.join('|')
68
+ regText << ")($|[#{PUNCT_CAHRS_ESCAPED}])"
69
+
70
+ state.env[:abbrRegExp] = Regexp.new(regText)
71
+ end
72
+ reg = state.env[:abbrRegExp]
73
+
74
+ j = 0
75
+ l = blockTokens.length
76
+ while j < l
77
+ j += 1 and next if (blockTokens[j].type != 'inline')
78
+ tokens = blockTokens[j].children
79
+
80
+ # We scan from the end, to keep position when new tags added.
81
+ i = tokens.length - 1
82
+ while i >= 0
83
+ currentToken = tokens[i]
84
+ i -= 1 and next if (currentToken.type != 'text')
85
+
86
+ pos = 0
87
+ text = currentToken.content
88
+ lastIndex = 0
89
+ nodes = []
90
+
91
+ while ((m = reg.match(text, lastIndex)))
92
+ lastIndex = m.end(0)
93
+ if (lastIndex > pos)
94
+ token = MarkdownIt::Token.new('text', '', 0)
95
+ token.content = text.slice(pos...(m.begin(0) + m[1].length))
96
+ nodes.push(token)
97
+ end
98
+
99
+ token = MarkdownIt::Token.new('abbr_open', 'abbr', 1)
100
+ token.attrs = [ [ 'title', state.env[:abbreviations][m[2]] ] ]
101
+ nodes.push(token)
102
+
103
+ token = MarkdownIt::Token.new('text', '', 0)
104
+ token.content = m[2]
105
+ nodes.push(token)
106
+
107
+ token = MarkdownIt::Token.new('abbr_close', 'abbr', -1)
108
+ nodes.push(token)
109
+
110
+ pos = lastIndex - m[3].length
111
+ end
112
+
113
+ i -= 1 and next if nodes.empty?
114
+
115
+ if (pos < text.length)
116
+ token = MarkdownIt::Token.new('text', '', 0)
117
+ token.content = text.slice(pos..-1)
118
+ nodes.push(token)
119
+ end
120
+
121
+ # replace current node
122
+ blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes)
123
+ i -= 1
124
+ end
125
+ j += 1
126
+ end
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,103 @@
1
+ # Based on Javascript version: https://github.com/mcecot/markdown-it-checkbox
2
+ #------------------------------------------------------------------------------
3
+ include MarkdownIt::Common::Utils
4
+
5
+ module MotionMarkdownItPlugins
6
+ class CheckboxReplace
7
+ PATTERN = /\[(X|\s|\_|\-)\]\s(.*)/i
8
+
9
+ #------------------------------------------------------------------------------
10
+ def self.init_plugin(md, options)
11
+ check = CheckboxReplace.new(md, options)
12
+ md.core.ruler.push('checkbox', lambda { |state| check.checkbox(state) } )
13
+ end
14
+
15
+ #------------------------------------------------------------------------------
16
+ def initialize(md, options)
17
+ @lastId = 0
18
+ defaults = {divWrap: false, divClass: 'checkbox', idPrefix: 'checkbox'}
19
+ @options = defaults.merge(options)
20
+ end
21
+
22
+ #------------------------------------------------------------------------------
23
+ def createTokens(checked, label)
24
+ nodes = []
25
+
26
+ # <div class="checkbox">
27
+ if @options[:divWrap]
28
+ token = MarkdownIt::Token.new("checkbox_open", "div", 1)
29
+ token.attrs = [["class", @options[:divClass]]]
30
+ nodes.push token
31
+ end
32
+
33
+ # <input type="checkbox" id="checkbox{n}" checked="true">
34
+ id = "#{@options[:idPrefix]}#{@lastId}"
35
+ @lastId += 1
36
+ token = MarkdownIt::Token.new("checkbox_input", "input", 0)
37
+ token.attrs = [["type","checkbox"],["id",id]]
38
+ if (checked == true)
39
+ token.attrs.push ["checked","true"]
40
+ end
41
+ nodes.push token
42
+
43
+ # <label for="checkbox{n}">
44
+ token = MarkdownIt::Token.new("label_open", "label", 1)
45
+ token.attrs = [["for",id]]
46
+ nodes.push token
47
+
48
+ # content of label tag
49
+ token = MarkdownIt::Token.new("text", "", 0)
50
+ token.content = label
51
+ nodes.push token
52
+
53
+ # closing tags
54
+ nodes.push MarkdownIt::Token.new("label_close", "label", -1)
55
+ if @options[:divWrap]
56
+ nodes.push MarkdownIt::Token.new("checkbox_close", "div", -1)
57
+ end
58
+
59
+ return nodes
60
+ end
61
+
62
+ #------------------------------------------------------------------------------
63
+ def splitTextToken(original)
64
+
65
+ text = original.content
66
+ matches = text.match(PATTERN)
67
+
68
+ return original if matches == nil
69
+
70
+ value = matches[1]
71
+ label = matches[2]
72
+ checked = false
73
+ checked = true if (value == "X" || value == "x")
74
+
75
+ return createTokens(checked, label)
76
+ end
77
+
78
+ #------------------------------------------------------------------------------
79
+ def checkbox(state)
80
+ blockTokens = state.tokens
81
+ j = 0
82
+ l = blockTokens.length
83
+ while j < l
84
+ if blockTokens[j].type != "inline"
85
+ j += 1
86
+ next
87
+ end
88
+ tokens = blockTokens[j].children
89
+
90
+ # We scan from the end, to keep position when new tags added.
91
+ # Use reversed logic in links start/end match
92
+ i = tokens.length - 1
93
+ while i >= 0
94
+ token = tokens[i]
95
+ blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, splitTextToken(token))
96
+ i -= 1
97
+ end
98
+ j += 1
99
+ end
100
+ end
101
+ end
102
+ end
103
+
@@ -0,0 +1,154 @@
1
+ # Process block-level custom containers
2
+ #
3
+ # Based on Javascript version: https://github.com/markdown-it/markdown-it-container
4
+ #------------------------------------------------------------------------------
5
+
6
+ module MotionMarkdownItPlugins
7
+ class Container
8
+ extend MarkdownIt::Common::Utils
9
+
10
+ attr_accessor :render
11
+
12
+ #------------------------------------------------------------------------------
13
+ def self.init_plugin(md, name, options = {})
14
+ container_obj = Container.new(md, name, options)
15
+ md.block.ruler.before('fence', "container_#{name}",
16
+ lambda { |state, startLine, endLine, silent| container_obj.container(state, startLine, endLine, silent) },
17
+ {alt: [ '', 'paragraph', 'reference', 'blockquote', 'list' ]})
18
+
19
+ md.renderer.rules["container_#{name}_open"] = container_obj.render
20
+ md.renderer.rules["container_#{name}_close"] = container_obj.render
21
+ end
22
+
23
+ #------------------------------------------------------------------------------
24
+ def initialize(md, name, options)
25
+ @options = options || {}
26
+ @name = name
27
+ @min_markers = 3
28
+ @marker_str = @options[:marker] || ':'
29
+ @marker_char = @marker_str.charCodeAt(0)
30
+ @marker_len = @marker_str.length
31
+ @validate = @options[:validate] || lambda {|params| validateDefault(params) }
32
+ @render = @options[:render] || lambda {|tokens, idx, _options, env, renderer| renderDefault(tokens, idx, _options, env, renderer) }
33
+ end
34
+
35
+ #------------------------------------------------------------------------------
36
+ def validateDefault(params)
37
+ return params.strip.split(' ', 2)[0] == @name
38
+ end
39
+
40
+ #------------------------------------------------------------------------------
41
+ def renderDefault(tokens, idx, _options, env, renderer)
42
+
43
+ # add a class to the opening tag
44
+ if (tokens[idx].nesting == 1)
45
+ tokens[idx].attrPush([ 'class', @name ])
46
+ end
47
+
48
+ return renderer.renderToken(tokens, idx, _options, env, renderer)
49
+ end
50
+
51
+ #------------------------------------------------------------------------------
52
+ def container(state, startLine, endLine, silent)
53
+ auto_closed = false
54
+ start = state.bMarks[startLine] + state.tShift[startLine]
55
+ max = state.eMarks[startLine]
56
+
57
+ # Check out the first character quickly,
58
+ # this should filter out most of non-containers
59
+ return false if (@marker_char != state.src.charCodeAt(start))
60
+
61
+ # Check out the rest of the marker string
62
+ pos = start + 1
63
+ while pos <= max
64
+ break if (@marker_str[(pos - start) % @marker_len] != state.src[pos])
65
+ pos += 1
66
+ end
67
+
68
+ marker_count = ((pos - start) / @marker_len).floor
69
+ return false if (marker_count < @min_markers)
70
+ pos -= (pos - start) % @marker_len
71
+
72
+ markup = state.src.slice(start...pos)
73
+ params = state.src.slice(pos...max)
74
+ return false if (!@validate.call(params))
75
+
76
+ # Since start is found, we can report success here in validation mode
77
+ return true if (silent)
78
+
79
+ # Search for the end of the block
80
+ nextLine = startLine
81
+
82
+ while true
83
+ nextLine += 1
84
+ if (nextLine >= endLine)
85
+ # unclosed block should be autoclosed by end of document.
86
+ # also block seems to be autoclosed by end of parent
87
+ break
88
+ end
89
+
90
+ start = state.bMarks[nextLine] + state.tShift[nextLine]
91
+ max = state.eMarks[nextLine]
92
+
93
+ if (start < max && state.tShift[nextLine] < state.blkIndent)
94
+ # non-empty line with negative indent should stop the list:
95
+ # - ```
96
+ # test
97
+ break
98
+ end
99
+
100
+ next if (@marker_char != state.src.charCodeAt(start))
101
+
102
+ if (state.tShift[nextLine] - state.blkIndent >= 4)
103
+ # closing fence should be indented less than 4 spaces
104
+ next
105
+ end
106
+
107
+ pos = start + 1
108
+ while pos <= max
109
+ break if (@marker_str[(pos - start) % @marker_len] != state.src[pos])
110
+ pos += 1
111
+ end
112
+
113
+ # closing code fence must be at least as long as the opening one
114
+ next if (((pos - start).floor / @marker_len) < marker_count)
115
+
116
+ # make sure tail has spaces only
117
+ pos -= (pos - start) % @marker_len
118
+ pos = state.skipSpaces(pos)
119
+
120
+ next if (pos < max)
121
+
122
+ # found!
123
+ auto_closed = true
124
+ break
125
+ end
126
+
127
+ old_parent = state.parentType
128
+ old_line_max = state.lineMax
129
+ state.parentType = 'container'
130
+
131
+ # this will prevent lazy continuations from ever going past our end marker
132
+ state.lineMax = nextLine
133
+
134
+ token = state.push("container_#{@name}_open", 'div', 1)
135
+ token.markup = markup
136
+ token.block = true
137
+ token.info = params
138
+ token.map = [ startLine, nextLine ]
139
+
140
+ state.md.block.tokenize(state, startLine + 1, nextLine)
141
+
142
+ token = state.push("container_#{@name}_close", 'div', -1)
143
+ token.markup = state.src.slice(start...pos)
144
+ token.block = true
145
+
146
+ state.parentType = old_parent
147
+ state.lineMax = old_line_max
148
+ state.line = nextLine + (auto_closed ? 1 : 0)
149
+
150
+ return true
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,185 @@
1
+ # Process definition lists
2
+ #
3
+ # Based on Javascript version: https://github.com/markdown-it/markdown-it-deflist
4
+ #------------------------------------------------------------------------------
5
+ include MarkdownIt::Common::Utils
6
+
7
+ module MotionMarkdownItPlugins
8
+ class Deflist
9
+
10
+ #------------------------------------------------------------------------------
11
+ def self.init_plugin(md)
12
+ md.block.ruler.before('paragraph', 'deflist',
13
+ lambda { |state, startLine, endLine, silent| Deflist.deflist(state, startLine, endLine, silent) },
14
+ {alt: ['', 'paragraph', 'reference']})
15
+ end
16
+
17
+ # Search `[:~][\n ]`, returns next pos after marker on success
18
+ # or -1 on fail.
19
+ #------------------------------------------------------------------------------
20
+ def self.skipMarker(state, line)
21
+ start = state.bMarks[line] + state.tShift[line]
22
+ max = state.eMarks[line]
23
+
24
+ return -1 if (start >= max)
25
+
26
+ # Check bullet
27
+ marker = state.src.charCodeAt(start)
28
+ start += 1
29
+ return -1 if (marker != 0x7E && marker != 0x3A) # '~' ':'
30
+
31
+ pos = state.skipSpaces(start)
32
+
33
+ # require space after ":"
34
+ return -1 if (start == pos)
35
+
36
+ # no empty definitions, e.g. " : "
37
+ return -1 if (pos >= max)
38
+
39
+ return pos
40
+ end
41
+
42
+ #------------------------------------------------------------------------------
43
+ def self.markTightParagraphs(state, idx)
44
+ level = state.level + 2
45
+ i = idx + 2
46
+ l = state.tokens.length - 2
47
+ while i < l
48
+ if (state.tokens[i].level == level && state.tokens[i].type == 'paragraph_open')
49
+ state.tokens[i + 2].hidden = true
50
+ state.tokens[i].hidden = true
51
+ i += 2
52
+ end
53
+ i += 1
54
+ end
55
+ end
56
+
57
+ #------------------------------------------------------------------------------
58
+ def self.deflist(state, startLine, endLine, silent)
59
+ if silent
60
+ # quirk: validation mode validates a dd block only, not a whole deflist
61
+ return false if (state.ddIndent < 0)
62
+ return skipMarker(state, startLine) >= 0
63
+ end
64
+
65
+ nextLine = startLine + 1
66
+ if (state.isEmpty(nextLine))
67
+ nextLine += 1
68
+ return false if (nextLine > endLine)
69
+ end
70
+
71
+ return false if (state.tShift[nextLine] < state.blkIndent)
72
+ contentStart = skipMarker(state, nextLine)
73
+ return false if (contentStart < 0)
74
+
75
+ # Start list
76
+ listTokIdx = state.tokens.length
77
+ token = state.push('dl_open', 'dl', 1)
78
+ token.map = listLines = [ startLine, 0 ]
79
+
80
+ #--- Iterate list items
81
+
82
+ dtLine = startLine
83
+ ddLine = nextLine
84
+
85
+ # One definition list can contain multiple DTs,
86
+ # and one DT can be followed by multiple DDs.
87
+ #
88
+ # Thus, there is two loops here, and label is
89
+ # needed to break out of the second one
90
+ # OUTER:
91
+ while true
92
+ break_outer = false
93
+ tight = true
94
+ prevEmptyEnd = false
95
+
96
+ token = state.push('dt_open', 'dt', 1)
97
+ token.map = [ dtLine, dtLine ]
98
+
99
+ token = state.push('inline', '', 0)
100
+ token.map = [ dtLine, dtLine ]
101
+ token.content = state.getLines(dtLine, dtLine + 1, state.blkIndent, false).strip
102
+ token.children = []
103
+
104
+ token = state.push('dt_close', 'dt', -1)
105
+
106
+ while true
107
+ token = state.push('dd_open', 'dd', 1)
108
+ token.map = itemLines = [ nextLine, 0 ]
109
+
110
+ oldTight = state.tight
111
+ oldDDIndent = state.ddIndent
112
+ oldIndent = state.blkIndent
113
+ oldTShift = state.tShift[ddLine]
114
+ oldParentType = state.parentType
115
+ state.blkIndent = state.ddIndent = state.tShift[ddLine] + 2
116
+ state.tShift[ddLine] = contentStart - state.bMarks[ddLine]
117
+ state.tight = true
118
+ state.parentType = 'deflist'
119
+
120
+ state.md.block.tokenize(state, ddLine, endLine, true)
121
+
122
+ # If any of list item is tight, mark list as tight
123
+ if (!state.tight || prevEmptyEnd)
124
+ tight = false
125
+ end
126
+ # Item become loose if finish with empty line,
127
+ # but we should filter last element, because it means list finish
128
+ prevEmptyEnd = (state.line - ddLine) > 1 && state.isEmpty(state.line - 1)
129
+
130
+ state.tShift[ddLine] = oldTShift
131
+ state.tight = oldTight
132
+ state.parentType = oldParentType
133
+ state.blkIndent = oldIndent
134
+ state.ddIndent = oldDDIndent
135
+
136
+ token = state.push('dd_close', 'dd', -1)
137
+
138
+ itemLines[1] = nextLine = state.line
139
+
140
+ break_outer = true and break if (nextLine >= endLine)
141
+ break_outer = true and break if (state.tShift[nextLine] < state.blkIndent)
142
+ contentStart = skipMarker(state, nextLine)
143
+ break if (contentStart < 0)
144
+
145
+ ddLine = nextLine
146
+
147
+ # go to the next loop iteration:
148
+ # insert DD tag and repeat checking
149
+ end
150
+ break if break_outer
151
+
152
+ break if (nextLine >= endLine)
153
+ dtLine = nextLine
154
+
155
+ break if (state.isEmpty(dtLine))
156
+ break if (state.tShift[dtLine] < state.blkIndent)
157
+
158
+ ddLine = dtLine + 1
159
+ break if (ddLine >= endLine)
160
+ ddLine += 1 if (state.isEmpty(ddLine))
161
+ break if (ddLine >= endLine)
162
+
163
+ break if (state.tShift[ddLine] < state.blkIndent)
164
+ contentStart = skipMarker(state, ddLine)
165
+ break if (contentStart < 0)
166
+
167
+ # go to the next loop iteration:
168
+ # insert DT and DD tags and repeat checking
169
+ end
170
+
171
+ # Finilize list
172
+ token = state.push('dl_close', 'dl', -1)
173
+ listLines[1] = nextLine
174
+ state.line = nextLine
175
+
176
+ # mark paragraphs tight if needed
177
+ if (tight)
178
+ markTightParagraphs(state, listTokIdx)
179
+ end
180
+
181
+ return true
182
+ end
183
+
184
+ end
185
+ end