nested_array 2.4.0 → 3.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.
@@ -11,22 +11,19 @@ module NestedArray::Nested
11
11
  # Имена полей для получения/записи информации, чувствительны к string/symbol
12
12
  id: 'id',
13
13
  parent_id: 'parent_id',
14
+
14
15
  children: 'children',
15
16
  level: 'level',
16
17
 
17
18
  # Параметры для преобразования в nested
18
19
  hashed: false,
19
- add_level: false,
20
20
  root_id: nil,
21
+ branch_id: nil,
21
22
 
22
23
  # Параметры для преобразования в html
23
24
  tabulated: true,
24
25
  inline: false,
25
26
  tab: "\t",
26
- ul: '<ul>',
27
- _ul: '</ul>',
28
- li: '<li>',
29
- _li: '</li>',
30
27
 
31
28
  # Параматры для "склеивания" вложенных структур
32
29
  path_separator: '-=path_separator=-',
@@ -35,140 +32,75 @@ module NestedArray::Nested
35
32
  # Настройки формирования массива для опций тега <select>
36
33
  option_value: 'id', # Что брать в качестве значений при формировании опций селекта.
37
34
  option_text: 'name',
38
- }
39
- end
35
+ thin_option: false,
36
+ pseudographics: %w(┳ ━ ╸ ┣ ┗ &nbsp; ┃),
37
+ thin_pseudographics: %w(┬ ─ ╴ ├ └ &nbsp; │),
40
38
 
41
- #
42
- # Перебирает вложенную стуктуру.
43
- #
44
- def each_nested options={}
45
- options = NESTED_OPTIONS.merge options
46
- level = 0
47
- cache = []
48
- cache[level] = self.clone
49
- parents = []
50
- parents[level] = nil
51
- i = []
52
- i[level] = 0
53
- while level >= 0
54
- node = cache[level][i[level]]
55
- i[level]+= 1
56
- if node != nil
57
- is_last_children = cache[level][i[level]].blank?
58
-
59
- yield(node.clone, parents.clone, level, is_last_children, node.origin)
60
-
61
- if !node[options[:children]].nil? && node[options[:children]].length > 0
62
- level+= 1
63
- parents[level] = node.clone
64
- cache[level] = node[options[:children]]
65
- i[level] = 0
66
- end
67
- else
68
- parents[level] = nil
69
- level-= 1
70
- end
71
- end
72
- self
73
- end
74
-
75
- def each_nested! options={}
76
- options = NESTED_OPTIONS.merge options
77
- level = 0
78
- cache = []
79
- cache[level] = self
80
- parents = []
81
- parents[level] = nil
82
- i = []
83
- i[level] = 0
84
- while level >= 0
85
- node = cache[level][i[level]]
86
- i[level]+= 1
87
- if node != nil
88
- is_last_children = cache[level][i[level]].blank?
39
+ # Выводить html теги от раскрывающегося списка на основе тега details.
40
+ details: false,
89
41
 
90
- yield(node, parents, level, is_last_children, node.origin)
42
+ ul: '<ul>',
43
+ _ul: '</ul>',
44
+ li: '<li>',
45
+ _li: '</li>',
91
46
 
92
- if !node[options[:children]].nil? && node[options[:children]].length > 0
93
- level+= 1
94
- parents[level] = node
95
- cache[level] = node[options[:children]]
96
- i[level] = 0
97
- end
98
- else
99
- parents[level] = nil
100
- level-= 1
101
- end
102
- end
103
- self
47
+ uld: '<details><summary></summary><ul>',
48
+ uldo: '<details open><summary></summary><ul>',
49
+ _uld: '</ul></details>'
50
+ }
104
51
  end
105
52
 
106
- def to_nested options={}
53
+ # Преобразует во вложенную структуру на основании parent_id
54
+ def to_nested(options = {})
107
55
  options = NESTED_OPTIONS.merge options
56
+ # Зарезервированные поля узла.
108
57
  fields = {
109
58
  id: options[:id],
110
59
  parent_id: options[:parent_id],
111
60
  level: options[:level],
112
61
  children: options[:children],
113
62
  }
114
- fields.delete :level if !options[:add_level]
115
63
  cache = {}
116
64
  nested = options[:hashed] ? {} : []
117
65
  # Перебираем элементы в любом порядке!
118
- self.each do |value|
119
- origin = value
120
- value = value.serializable_hash if !value.is_a? Hash
66
+ self.each do |origin|
67
+ value = origin.is_a?(Hash) ? origin : origin.serializable_hash
121
68
  # 1. Если нет родителя текущего элемента, и текущий элемент не корневой, то:
122
69
  # 1.1 создадим родителя
123
70
  # 1.2 поместим в кэш
124
- if !(cache.key? value[fields[:parent_id]]) && (value[fields[:parent_id]] != options[:root_id])
71
+ if !(cache.key? value[options[:parent_id]]) && (value[options[:parent_id]] != options[:root_id])
125
72
  # 1.1
126
73
  temp = OpenStruct.new
127
- fields.each do |key, field|
128
- case key
129
- when :id
130
- temp[field] = value[fields[:parent_id]]
131
- when :children
132
- # не создаём поле
133
- else
134
- temp[field] = nil
135
- end
136
- end
74
+ temp[options[:id]] = value[options[:parent_id]]
75
+ temp[options[:parent_id]] = nil
76
+ temp[options[:level]] = nil
137
77
  # 1.2
138
- cache[value[fields[:parent_id]]] = temp
78
+ cache[value[options[:parent_id]]] = temp
139
79
  end
140
80
  # 2. Если текущий элемент уже был создан, значит он был чьим-то родителем, тогда:
141
- # 2.1 обновим в нем информацию
81
+ # 2.1 обновим в нем информацию о parent_id и другие не зарезервированные поля.
142
82
  # 2.2 поместим в родителя
143
- if cache.key? value[fields[:id]]
83
+ if cache.key? value[options[:id]]
144
84
  # 2.1
145
- fields.each do |key, field|
146
- case key
147
- when :id, :children
148
- # не обновляем информацию
149
- else
150
- cache[value[fields[:id]]][field] = value[field]
151
- end
152
- end
153
- value.keys.each do |field|
154
- cache[value[fields[:id]]][field] = value[field] if !(field.in? fields)
155
- end
156
- cache[value[fields[:id]]].origin = origin
85
+ cache[value[options[:id]]][options[:parent_id]] = value[options[:parent_id]]
86
+ cache[value[options[:id]]].origin = origin
157
87
  # 2.2
158
88
  # Если текущий элемент не корневой - поместим в родителя, беря его из кэш
159
- if value[fields[:parent_id]] != options[:root_id]
160
- cache[value[fields[:parent_id]]][fields[:children]] ||= options[:hashed] ? {} : []
89
+ if value[options[:parent_id]] != options[:root_id]
90
+ cache[value[options[:parent_id]]][options[:children]] ||= options[:hashed] ? {} : []
161
91
  if options[:hashed]
162
- cache[value[fields[:parent_id]]][fields[:children]][value[fields[:id]]] = nested[value[fields[:id]]]
92
+ cache[value[options[:parent_id]]][options[:children]][value[options[:id]]] = nested[value[options[:id]]]
163
93
  else
164
- cache[value[fields[:parent_id]]][fields[:children]] << cache[value[fields[:id]]]
94
+ cache[value[options[:parent_id]]][options[:children]] << cache[value[options[:id]]]
165
95
  end
166
96
  # иначе, текущий элемент корневой, поместим в nested
167
97
  else
168
- if options[:hashed]
169
- nested[value[fields[:id]]] = cache[value[fields[:id]]]
170
- else
171
- nested << cache[value[fields[:id]]]
98
+ if options[:branch_id].nil? || options[:branch_id] == value[options[:id]]
99
+ if options[:hashed]
100
+ nested[value[options[:id]]] = cache[value[options[:id]]]
101
+ else
102
+ nested << cache[value[options[:id]]]
103
+ end
172
104
  end
173
105
  end
174
106
  # 3. Иначе, текущий элемент не создан, тогда:
@@ -178,72 +110,65 @@ module NestedArray::Nested
178
110
  else
179
111
  # 3.1
180
112
  temp = OpenStruct.new
181
- fields.each do |key, field|
182
- case key
183
- when :id
184
- temp[field] = value[field]
185
- when :parent_id
186
- temp[field] = value[field]
187
- when :children
188
- # ничего не делаем
189
- else
190
- temp[field] = value[field]
191
- end
192
- end
193
- value.keys.each do |field|
194
- temp[field] = value[field] if !(field.in? fields)
195
- end
113
+ temp[options[:id]] = value[options[:id]]
114
+ temp[options[:parent_id]] = value[options[:parent_id]]
115
+ temp[options[:level]] = nil
196
116
  temp.origin = origin
197
117
  # 3.2
198
- cache[value[fields[:id]]] = temp
118
+ cache[value[options[:id]]] = temp
199
119
  # 3.3
200
120
  # Если текущий элемент не корневой - поместим в родителя, беря его из кэш
201
- if value[fields[:parent_id]] != options[:root_id]
202
- cache[value[fields[:parent_id]]][fields[:children]] ||= options[:hashed] ? {} : []
121
+ if value[options[:parent_id]] != options[:root_id]
122
+ cache[value[options[:parent_id]]][options[:children]] ||= options[:hashed] ? {} : []
203
123
  if options[:hashed]
204
- cache[value[fields[:parent_id]]][fields[:children]][value[fields[:id]]] = cache[value[fields[:id]]]
124
+ cache[value[options[:parent_id]]][options[:children]][value[options[:id]]] = cache[value[options[:id]]]
205
125
  else
206
- cache[value[fields[:parent_id]]][fields[:children]] << cache[value[fields[:id]]]
126
+ cache[value[options[:parent_id]]][options[:children]] << cache[value[options[:id]]]
207
127
  end
208
128
  # иначе, текущий элемент корневой, поместим в nested
209
129
  else
210
- if options[:hashed]
211
- nested[value[fields[:id]]] = cache[value[fields[:id]]]
212
- else
213
- nested << cache[value[fields[:id]]]
130
+ if options[:branch_id].nil? || options[:branch_id] == value[options[:id]]
131
+ if options[:hashed]
132
+ nested[value[options[:id]]] = cache[value[options[:id]]]
133
+ else
134
+ nested << cache[value[options[:id]]]
135
+ end
214
136
  end
215
137
  end
216
138
  end
217
139
  end
218
- if options[:add_level]
219
- level = 0
220
- cache = []
221
- cache[level] = nested
222
- i = []
223
- i[level] = 0
224
- while level >= 0
225
- node = cache[level][i[level]]
226
- i[level]+= 1
227
- if node != nil
228
-
229
- node[options[:level]] = level
230
-
231
- if !node[options[:children]].nil? && node[options[:children]].length > 0
232
- level+= 1
233
- cache[level] = node[options[:children]]
234
- i[level] = 0
235
- end
236
- else
237
- level-= 1
140
+
141
+ # Добавление level к узлу.
142
+ level = 0
143
+ cache = []
144
+ cache[level] = nested
145
+ i = []
146
+ i[level] = 0
147
+ while level >= 0
148
+ node = cache[level][i[level]]
149
+ i[level] += 1
150
+ if node != nil
151
+
152
+ node[options[:level]] = level
153
+
154
+ if !node[options[:children]].nil? && node[options[:children]].length > 0
155
+ level += 1
156
+ cache[level] = node[options[:children]]
157
+ i[level] = 0
238
158
  end
159
+ else
160
+ level -= 1
239
161
  end
240
162
  end
163
+
241
164
  nested
242
165
  end
243
166
 
244
- def nested_to_html options={}
167
+ #
168
+ # Перебирает вложенную стуктуру.
169
+ #
170
+ def each_nested(options = {})
245
171
  options = NESTED_OPTIONS.merge options
246
- html = ''
247
172
  level = 0
248
173
  cache = []
249
174
  cache[level] = self.clone
@@ -251,63 +176,165 @@ module NestedArray::Nested
251
176
  parents[level] = nil
252
177
  i = []
253
178
  i[level] = 0
179
+ prev_level = nil
254
180
  while level >= 0
255
181
  node = cache[level][i[level]]
256
- i[level]+= 1
182
+ i[level] += 1
257
183
  if node != nil
184
+ clone_node = node.clone
185
+
186
+ # Текущий узел является последним ребёнком своего родителя:
187
+ clone_node.is_last_children = cache[level][i[level]].blank?
188
+ # Текущий узел имеет детей:
189
+ clone_node.is_has_children = !node[options[:children]].nil? && node[options[:children]].length > 0
190
+ # Текущий узел последний в дереве:
191
+ clone_node.is_last = clone_node.is_last_children && !clone_node.is_has_children && (0..(clone_node.level)).to_a.map{|l| cache[l][i[l]].blank?}.all?(true)
192
+
193
+ next_level = if clone_node.is_has_children
194
+ level + 1
195
+ elsif clone_node.is_last_children
196
+ nl = nil
197
+ (0..clone_node.level).to_a.reverse.each do |l|
198
+ if cache[l][i[l]].present?
199
+ nl = l
200
+ break
201
+ end
202
+ end
203
+ nl
204
+ else
205
+ level
206
+ end
207
+
208
+ clone_node.parents = parents.clone
209
+
210
+ # В текущем узле всегда есть li
211
+ clone_node.before = options[:li].html_safe
212
+ clone_node.li = clone_node.before
213
+ # Следующий уровень тот же? — текущий закрываем просто.
214
+ if next_level.present? && next_level == clone_node.level
215
+ clone_node._ = options[:_li].html_safe
216
+ end
217
+ # Следующий уровень понизится? - текущий закрываем сложно.
218
+ if next_level.present? && next_level < clone_node.level
219
+ clone_node._ = options[:_li]
220
+ (clone_node.level - next_level).times do |t|
221
+ clone_node._ += options[:details] ? options[:_uld] + options[:_li] : options[:_ul] + options[:_li]
222
+ end
223
+ clone_node._ = clone_node._.html_safe
224
+ end
225
+ # Следующий уровень повысится? — открываем подуровень.
226
+ if clone_node.is_has_children
227
+ clone_node.ul = if options[:details]
228
+ options[:uld].html_safe
229
+ else
230
+ options[:ul].html_safe
231
+ end
232
+ end
233
+ # Последний в дереве? — последние закрывающие теги.
234
+ if clone_node.is_last
235
+ clone_node._ = options[:_li]
236
+ clone_node.level.times do |t|
237
+ clone_node._ += options[:details] ? options[:_uld] + options[:_li] : options[:_ul] + options[:_li]
238
+ end
239
+ clone_node._ = clone_node._.html_safe
240
+ end
241
+
242
+ clone_node.define_singleton_method(:after) do |*args|
243
+ ret = ''
244
+ # Следующий уровень тот же? — текущий закрываем просто.
245
+ if next_level.present? && next_level == clone_node.level
246
+ ret += options[:_li]
247
+ end
248
+ # Следующий уровень понизится? - текущий закрываем сложно.
249
+ if next_level.present? && next_level < clone_node.level
250
+ ret += options[:_li]
251
+ (clone_node.level - next_level).times do |t|
252
+ ret += options[:details] ? options[:_uld] + options[:_li] : options[:_ul] + options[:_li]
253
+ end
254
+ end
255
+ # Следующий уровень повысится? — открываем подуровень.
256
+ if self.is_has_children
257
+ if options[:details]
258
+ ret += args.present? && args[0]&.[](:open) == true ? options[:uldo] : options[:uld]
259
+ else
260
+ ret += options[:ul]
261
+ end
262
+ end
263
+ # Последний в дереве? — последние закрывающие теги.
264
+ if self.is_last
265
+ ret += options[:_li]
266
+ self.level.times do |t|
267
+ ret += options[:details] ? options[:_uld] + options[:_li] : options[:_ul] + options[:_li]
268
+ end
269
+ end
270
+ ret.html_safe
271
+ end
258
272
 
259
- node_html, node_options = yield(node.clone, parents.clone, level)
260
- html+= options[:tab] * (level * 2 + 1) if options[:tabulated]
261
- html+= node_options&.[](:li) || options[:li]
262
- html+= node_html.to_s
273
+ yield(clone_node, clone_node.origin)
274
+
275
+ prev_level = node.level
263
276
 
264
277
  if !node[options[:children]].nil? && node[options[:children]].length > 0
265
- level+= 1
266
- html+= "\n" if !options[:inline]
267
- html+= options[:tab] * (level * 2) if options[:tabulated]
268
- html+= node_options&.[](:ul) || options[:ul]
269
- html+= "\n" if !options[:inline]
270
- parents[level] = node.clone
278
+ level += 1
279
+ parents[level] = clone_node
271
280
  cache[level] = node[options[:children]]
272
281
  i[level] = 0
273
- else
274
- html+= options[:_li]
275
- html+= "\n" if !options[:inline]
276
282
  end
277
283
  else
278
284
  parents[level] = nil
279
- if level > 0
280
- html+= options[:tab] * (level * 2) if options[:tabulated]
281
- html+= options[:_ul]
282
- html+= "\n" if !options[:inline]
283
- html+= options[:tab] * (level * 2 - 1) if options[:tabulated]
284
- html+= options[:_li]
285
- html+= "\n" if !options[:inline]
285
+ level -= 1
286
+ end
287
+ end
288
+ self
289
+ end
290
+
291
+ def to_flat(options = {})
292
+ ret = []
293
+ options = NESTED_OPTIONS.merge options
294
+ level = 0
295
+ cache = []
296
+ cache[level] = self.clone
297
+ i = []
298
+ i[level] = 0
299
+ while level >= 0
300
+ node = cache[level][i[level]]
301
+ i[level] += 1
302
+ if node != nil
303
+ ret.push node.origin
304
+
305
+ if !node[options[:children]].nil? && node[options[:children]].length > 0
306
+ level += 1
307
+ cache[level] = node[options[:children]]
308
+ i[level] = 0
286
309
  end
287
- level-= 1
310
+ else
311
+ level -= 1
288
312
  end
289
313
  end
290
- html.html_safe
314
+ ret
291
315
  end
292
316
 
293
- #
294
- # Возвращает массив для формирования опций html-тега <select>
317
+ # Возвращает массив для формирования опций html-тега &lt;select&gt;
295
318
  # с псевдографикой, позволяющей вывести древовидную структуру.
319
+ #
296
320
  # ```
297
321
  # [['option_text1', 'option_value1'],['option_text2', 'option_value2'],…]
298
322
  # ```
299
- def nested_to_options options={}
323
+ def nested_to_options(origin_text, origin_value, options = {})
300
324
  options = NESTED_OPTIONS.merge options
301
325
  ret = []
326
+
302
327
  last = []
303
- each_nested do |node, parents, level, is_last|
304
- last[level+1] = is_last
305
- node_text = node[options[:option_text]]
306
- node_level = (1..level).map{|l| last[l] == true ? '&nbsp;' : '┃'}.join
307
- node_last = is_last ? '┗' : '┣'
308
- node_children = node[options[:children]].present? && node[options[:children]].length > 0 ? '┳' : '━'
309
- option_text = "#{node_level}#{node_last}#{node_children}╸".html_safe + "#{node_text}"
310
- option_value = node[options[:option_value]]
328
+ downhorizontal, horizontal, left, rightvertical, rightup, space, vertical = options[:thin_pseudographic] ? options[:thin_pseudographics] : options[:pseudographics]
329
+
330
+ each_nested do |node, origin|
331
+ last[node.level + 1] = node.is_last_children
332
+ node_text = origin.send(origin_text)
333
+ node_level = (1..node.level).map{|l| last[l] == true ? space : vertical}.join
334
+ node_last = node.is_last_children ? rightup : rightvertical
335
+ node_children = node[options[:children]].present? && node[options[:children]].length > 0 ? downhorizontal : horizontal
336
+ option_text = "#{node_level}#{node_last}#{node_children}#{left}".html_safe + "#{node_text}"
337
+ option_value = origin.send(origin_value)
311
338
  ret.push [option_text, option_value]
312
339
  end
313
340
  ret
@@ -1,3 +1,3 @@
1
1
  module NestedArray
2
- VERSION = "2.4.0"
2
+ VERSION = "3.0.1"
3
3
  end
data/lib/nested_array.rb CHANGED
@@ -1,17 +1,16 @@
1
1
  require "active_support/all"
2
- require "nested_array/version"
3
2
 
3
+ require_relative "nested_array/version"
4
4
  require_relative "nested_array/nested"
5
+ require_relative "nested_array/engine" # Подключаем engine отдельно
5
6
 
6
7
  module NestedArray
7
-
8
8
  class Array < ::Array
9
-
10
9
  include NestedArray::Nested
11
10
  end
12
11
  end
13
12
 
13
+ # Monkey-patch стандартного Array
14
14
  class Array
15
-
16
15
  include ::NestedArray::Nested
17
16
  end
data/nested_array.gemspec CHANGED
@@ -29,9 +29,19 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  # Specify which files should be added to the gem when it is released.
31
31
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
33
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
- end
32
+
33
+ # spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
34
+ # `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
35
+ # end
36
+ spec.files = Dir[
37
+ "lib/**/*",
38
+ "vendor/assets/**/*",
39
+ "exe/**/*",
40
+ "README*",
41
+ "LICENSE*",
42
+ "*.gemspec"
43
+ ]
44
+
35
45
  spec.bindir = "exe"
36
46
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
47
  spec.require_paths = ["lib"]
@@ -0,0 +1,92 @@
1
+ .nested_array-details {
2
+ font-size: 1em;
3
+ margin: 0;
4
+ padding: 0 0 0 6px;
5
+ list-style-type: none;
6
+ line-height: 1.5em;
7
+ }
8
+ .nested_array-details ul {
9
+ margin: 0;
10
+ padding: 0 0 0 6px;
11
+ list-style-type: none;
12
+ }
13
+ .nested_array-details li {
14
+ margin: 0;
15
+ padding: 0 0 0 30px;
16
+ border-left: solid 1px gray;
17
+
18
+ position: relative;
19
+ }
20
+ .nested_array-details li > * {
21
+ margin-top: 0;
22
+ margin-bottom: 0;
23
+ }
24
+ .nested_array-details details {
25
+ position: relative;
26
+ top: 0;
27
+ /* -li.padding + ((li:before.width - 1) / 2) - ((summary.width - 1) / 2) */
28
+ /* -30 + ((27 - 1) / 2) - ((13 - 1) / 2) = -23 */
29
+ left: -23px;
30
+ }
31
+ .nested_array-details details::before {
32
+ content: none;
33
+ }
34
+ .nested_array-details details[open]::before {
35
+ content: '';
36
+ display: block;
37
+ position: absolute;
38
+ width: 1px;
39
+ height: 1em;
40
+ background-color: grey;
41
+ top: -0.75em;
42
+ left: 6px;
43
+ }
44
+ .nested_array-details summary {
45
+ position: absolute;
46
+ width: 13px;
47
+ height: 13px;
48
+ box-sizing: border-box;
49
+ top: calc(-0.75em - 6px);
50
+ left: auto;
51
+ list-style-type: none;
52
+ border: solid 1px gray;
53
+ cursor:pointer;
54
+ background:
55
+ linear-gradient(#000, #000),
56
+ linear-gradient(#000, #000),
57
+ #FFF;
58
+ background-position: center;
59
+ background-size: 7px 1px, 1px 7px;
60
+ background-repeat: no-repeat;
61
+ }
62
+ .nested_array-details details[open] > summary {
63
+ background:
64
+ linear-gradient(#000, #000),
65
+ #FFF;
66
+ background-position: center;
67
+ background-size: 7px 1px, 1px 7px;
68
+ background-repeat: no-repeat;
69
+ }
70
+ .nested_array-details li::before {
71
+ content: '';
72
+ display: block;
73
+ border-bottom: solid 1px gray;
74
+ position: absolute;
75
+ width: 27px;
76
+ height: 0.75em;
77
+ left: -1px;
78
+ top: 0;
79
+ }
80
+ .nested_array-details li:last-child {
81
+ border-left: 1px solid transparent;
82
+ }
83
+ .nested_array-details li:last-child::before {
84
+ border-left: solid 1px gray;
85
+ }
86
+
87
+ .nested_array-select {
88
+ option {
89
+ padding: 0;
90
+ font-family: monospace;
91
+ }
92
+ }