hamlit 2.9.1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +45 -0
- data/CHANGELOG.md +666 -0
- data/Gemfile +28 -0
- data/LICENSE.txt +44 -0
- data/README.md +146 -0
- data/REFERENCE.md +266 -0
- data/Rakefile +117 -0
- data/benchmark/boolean_attribute.haml +6 -0
- data/benchmark/class_attribute.haml +5 -0
- data/benchmark/common_attribute.haml +3 -0
- data/benchmark/data_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
- data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
- data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
- data/benchmark/dynamic_boolean_attribute.haml +4 -0
- data/benchmark/etc/attribute_builder.haml +5 -0
- data/benchmark/etc/real_sample.haml +888 -0
- data/benchmark/etc/real_sample.rb +11 -0
- data/benchmark/etc/static_analyzer.haml +1 -0
- data/benchmark/etc/string_interpolation.haml +2 -0
- data/benchmark/etc/tags.haml +3 -0
- data/benchmark/etc/tags_loop.haml +2 -0
- data/benchmark/ext/build_data.rb +17 -0
- data/benchmark/ext/build_id.rb +13 -0
- data/benchmark/id_attribute.haml +3 -0
- data/benchmark/plain.haml +4 -0
- data/benchmark/script.haml +4 -0
- data/benchmark/slim/LICENSE +21 -0
- data/benchmark/slim/context.rb +11 -0
- data/benchmark/slim/run-benchmarks.rb +94 -0
- data/benchmark/slim/view.erb +23 -0
- data/benchmark/slim/view.haml +18 -0
- data/benchmark/slim/view.slim +17 -0
- data/benchmark/utils/benchmark_ips_extension.rb +43 -0
- data/bin/bench +77 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/hamlit +6 -0
- data/ext/hamlit/extconf.rb +10 -0
- data/ext/hamlit/hamlit.c +553 -0
- data/ext/hamlit/hescape.c +108 -0
- data/ext/hamlit/hescape.h +20 -0
- data/hamlit.gemspec +45 -0
- data/lib/hamlit.rb +11 -0
- data/lib/hamlit/attribute_builder.rb +173 -0
- data/lib/hamlit/attribute_compiler.rb +123 -0
- data/lib/hamlit/attribute_parser.rb +110 -0
- data/lib/hamlit/cli.rb +130 -0
- data/lib/hamlit/compiler.rb +97 -0
- data/lib/hamlit/compiler/children_compiler.rb +112 -0
- data/lib/hamlit/compiler/comment_compiler.rb +36 -0
- data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
- data/lib/hamlit/compiler/script_compiler.rb +101 -0
- data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
- data/lib/hamlit/compiler/tag_compiler.rb +74 -0
- data/lib/hamlit/engine.rb +37 -0
- data/lib/hamlit/error.rb +15 -0
- data/lib/hamlit/escapable.rb +13 -0
- data/lib/hamlit/filters.rb +75 -0
- data/lib/hamlit/filters/base.rb +12 -0
- data/lib/hamlit/filters/cdata.rb +20 -0
- data/lib/hamlit/filters/coffee.rb +17 -0
- data/lib/hamlit/filters/css.rb +33 -0
- data/lib/hamlit/filters/erb.rb +10 -0
- data/lib/hamlit/filters/escaped.rb +22 -0
- data/lib/hamlit/filters/javascript.rb +33 -0
- data/lib/hamlit/filters/less.rb +20 -0
- data/lib/hamlit/filters/markdown.rb +10 -0
- data/lib/hamlit/filters/plain.rb +29 -0
- data/lib/hamlit/filters/preserve.rb +22 -0
- data/lib/hamlit/filters/ruby.rb +10 -0
- data/lib/hamlit/filters/sass.rb +15 -0
- data/lib/hamlit/filters/scss.rb +15 -0
- data/lib/hamlit/filters/text_base.rb +25 -0
- data/lib/hamlit/filters/tilt_base.rb +49 -0
- data/lib/hamlit/force_escapable.rb +29 -0
- data/lib/hamlit/helpers.rb +15 -0
- data/lib/hamlit/html.rb +14 -0
- data/lib/hamlit/identity.rb +13 -0
- data/lib/hamlit/object_ref.rb +30 -0
- data/lib/hamlit/parser.rb +49 -0
- data/lib/hamlit/parser/MIT-LICENSE +20 -0
- data/lib/hamlit/parser/README.md +30 -0
- data/lib/hamlit/parser/haml_buffer.rb +348 -0
- data/lib/hamlit/parser/haml_compiler.rb +553 -0
- data/lib/hamlit/parser/haml_error.rb +61 -0
- data/lib/hamlit/parser/haml_helpers.rb +727 -0
- data/lib/hamlit/parser/haml_options.rb +286 -0
- data/lib/hamlit/parser/haml_parser.rb +800 -0
- data/lib/hamlit/parser/haml_util.rb +288 -0
- data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
- data/lib/hamlit/rails_helpers.rb +51 -0
- data/lib/hamlit/rails_template.rb +58 -0
- data/lib/hamlit/railtie.rb +10 -0
- data/lib/hamlit/ruby_expression.rb +32 -0
- data/lib/hamlit/string_splitter.rb +88 -0
- data/lib/hamlit/template.rb +28 -0
- data/lib/hamlit/utils.rb +18 -0
- data/lib/hamlit/version.rb +4 -0
- metadata +360 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module Hamlit
|
2
|
+
class Compiler
|
3
|
+
class CommentCompiler
|
4
|
+
def compile(node, &block)
|
5
|
+
if node.value[:conditional]
|
6
|
+
compile_conditional_comment(node, &block)
|
7
|
+
else
|
8
|
+
compile_html_comment(node, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def compile_html_comment(node, &block)
|
15
|
+
if node.children.empty?
|
16
|
+
[:html, :comment, [:static, " #{node.value[:text]} "]]
|
17
|
+
else
|
18
|
+
[:html, :comment, yield(node)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def compile_conditional_comment(node, &block)
|
23
|
+
condition = node.value[:conditional]
|
24
|
+
if node.value[:conditional] =~ /\A\[(\[*[^\[\]]+\]*)\]/
|
25
|
+
condition = $1
|
26
|
+
end
|
27
|
+
|
28
|
+
if node.children.empty?
|
29
|
+
[:html, :condcomment, condition, [:static, " #{node.value[:text]} "]]
|
30
|
+
else
|
31
|
+
[:html, :condcomment, condition, yield(node)]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hamlit
|
3
|
+
class Compiler
|
4
|
+
class DoctypeCompiler
|
5
|
+
def initialize(options = {})
|
6
|
+
@format = options[:format]
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile(node)
|
10
|
+
case node.value[:type]
|
11
|
+
when 'xml'
|
12
|
+
xml_doctype
|
13
|
+
when ''
|
14
|
+
html_doctype(node)
|
15
|
+
else
|
16
|
+
[:html, :doctype, node.value[:type]]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def html_doctype(node)
|
23
|
+
version = node.value[:version] || :transitional
|
24
|
+
case @format
|
25
|
+
when :xhtml
|
26
|
+
[:html, :doctype, version]
|
27
|
+
when :html4
|
28
|
+
[:html, :doctype, :transitional]
|
29
|
+
when :html5
|
30
|
+
[:html, :doctype, :html]
|
31
|
+
else
|
32
|
+
[:html, :doctype, @format]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def xml_doctype
|
37
|
+
case @format
|
38
|
+
when :xhtml
|
39
|
+
[:static, "<?xml version='1.0' encoding='utf-8' ?>\n"]
|
40
|
+
else
|
41
|
+
[:multi]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'hamlit/ruby_expression'
|
3
|
+
require 'hamlit/string_splitter'
|
4
|
+
|
5
|
+
module Hamlit
|
6
|
+
class Compiler
|
7
|
+
class ScriptCompiler
|
8
|
+
def initialize(identity)
|
9
|
+
@identity = identity
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile(node, &block)
|
13
|
+
no_children = node.children.empty?
|
14
|
+
case
|
15
|
+
when no_children && node.value[:escape_interpolation]
|
16
|
+
compile_interpolated_plain(node)
|
17
|
+
when no_children && RubyExpression.string_literal?(node.value[:text])
|
18
|
+
delegate_optimization(node)
|
19
|
+
when no_children && Temple::StaticAnalyzer.static?(node.value[:text])
|
20
|
+
static_compile(node)
|
21
|
+
else
|
22
|
+
dynamic_compile(node, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# String-interpolated plain text must be compiled with this method
|
29
|
+
# because we have to escape only interpolated values.
|
30
|
+
def compile_interpolated_plain(node)
|
31
|
+
temple = [:multi]
|
32
|
+
StringSplitter.compile(node.value[:text]).each do |type, value|
|
33
|
+
case type
|
34
|
+
when :static
|
35
|
+
temple << [:static, value]
|
36
|
+
when :dynamic
|
37
|
+
temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
temple << [:newline]
|
41
|
+
end
|
42
|
+
|
43
|
+
# :dynamic is optimized in other filter: StringSplitter
|
44
|
+
def delegate_optimization(node)
|
45
|
+
[:multi,
|
46
|
+
[:escape, node.value[:escape_html], [:dynamic, node.value[:text]]],
|
47
|
+
[:newline],
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def static_compile(node)
|
52
|
+
str = eval(node.value[:text]).to_s
|
53
|
+
if node.value[:escape_html]
|
54
|
+
str = Hamlit::Utils.escape_html(str)
|
55
|
+
elsif node.value[:preserve]
|
56
|
+
str = ::Hamlit::HamlHelpers.find_and_preserve(str, %w(textarea pre code))
|
57
|
+
end
|
58
|
+
[:multi, [:static, str], [:newline]]
|
59
|
+
end
|
60
|
+
|
61
|
+
def dynamic_compile(node, &block)
|
62
|
+
var = @identity.generate
|
63
|
+
temple = compile_script_assign(var, node, &block)
|
64
|
+
temple << compile_script_result(var, node)
|
65
|
+
end
|
66
|
+
|
67
|
+
def compile_script_assign(var, node, &block)
|
68
|
+
if node.children.empty?
|
69
|
+
[:multi,
|
70
|
+
[:code, "#{var} = (#{node.value[:text]}"],
|
71
|
+
[:newline],
|
72
|
+
[:code, ')'],
|
73
|
+
]
|
74
|
+
else
|
75
|
+
[:multi,
|
76
|
+
[:block, "#{var} = #{node.value[:text]}",
|
77
|
+
[:multi, [:newline], yield(node)],
|
78
|
+
],
|
79
|
+
]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def compile_script_result(result, node)
|
84
|
+
if !node.value[:escape_html] && node.value[:preserve]
|
85
|
+
result = find_and_preserve(result)
|
86
|
+
else
|
87
|
+
result = "(#{result}).to_s"
|
88
|
+
end
|
89
|
+
[:escape, node.value[:escape_html], [:dynamic, result]]
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_and_preserve(code)
|
93
|
+
%Q[::Hamlit::HamlHelpers.find_and_preserve(#{code}, %w(textarea pre code))]
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape_html(temple)
|
97
|
+
[:escape, true, temple]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hamlit
|
3
|
+
class Compiler
|
4
|
+
class SilentScriptCompiler
|
5
|
+
def compile(node, &block)
|
6
|
+
if node.children.empty?
|
7
|
+
[:multi, [:code, node.value[:text]], [:newline]]
|
8
|
+
else
|
9
|
+
compile_with_children(node, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def compile_with_children(node, &block)
|
16
|
+
[:multi,
|
17
|
+
[:block, node.value[:text],
|
18
|
+
[:multi, [:newline], yield(node)],
|
19
|
+
],
|
20
|
+
]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'hamlit/parser/haml_util'
|
3
|
+
require 'hamlit/attribute_compiler'
|
4
|
+
require 'hamlit/string_splitter'
|
5
|
+
|
6
|
+
module Hamlit
|
7
|
+
class Compiler
|
8
|
+
class TagCompiler
|
9
|
+
def initialize(identity, options)
|
10
|
+
@autoclose = options[:autoclose]
|
11
|
+
@identity = identity
|
12
|
+
@attribute_compiler = AttributeCompiler.new(identity, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile(node, &block)
|
16
|
+
attrs = @attribute_compiler.compile(node)
|
17
|
+
contents = compile_contents(node, &block)
|
18
|
+
[:html, :tag, node.value[:name], attrs, contents]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def compile_contents(node, &block)
|
24
|
+
case
|
25
|
+
when !node.children.empty?
|
26
|
+
yield(node)
|
27
|
+
when node.value[:value].nil? && self_closing?(node)
|
28
|
+
nil
|
29
|
+
when node.value[:parse]
|
30
|
+
return compile_interpolated_plain(node) if node.value[:escape_interpolation]
|
31
|
+
return delegate_optimization(node) if RubyExpression.string_literal?(node.value[:value])
|
32
|
+
return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
|
33
|
+
|
34
|
+
var = @identity.generate
|
35
|
+
[:multi,
|
36
|
+
[:code, "#{var} = (#{node.value[:value]}"],
|
37
|
+
[:newline],
|
38
|
+
[:code, ')'],
|
39
|
+
[:escape, node.value[:escape_html], [:dynamic, var]]
|
40
|
+
]
|
41
|
+
else
|
42
|
+
[:static, node.value[:value]]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# :dynamic is optimized in other filters: StringSplitter or StaticAnalyzer
|
47
|
+
def delegate_optimization(node)
|
48
|
+
[:multi,
|
49
|
+
[:escape, node.value[:escape_html], [:dynamic, node.value[:value]]],
|
50
|
+
[:newline],
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
# We should handle interpolation here to escape only interpolated values.
|
55
|
+
def compile_interpolated_plain(node)
|
56
|
+
temple = [:multi]
|
57
|
+
StringSplitter.compile(node.value[:value]).each do |type, value|
|
58
|
+
case type
|
59
|
+
when :static
|
60
|
+
temple << [:static, value]
|
61
|
+
when :dynamic
|
62
|
+
temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
temple << [:newline]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self_closing?(node)
|
69
|
+
return true if @autoclose && @autoclose.include?(node.value[:name])
|
70
|
+
node.value[:self_closing]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'temple'
|
3
|
+
require 'hamlit/parser'
|
4
|
+
require 'hamlit/compiler'
|
5
|
+
require 'hamlit/escapable'
|
6
|
+
require 'hamlit/force_escapable'
|
7
|
+
require 'hamlit/html'
|
8
|
+
require 'hamlit/string_splitter'
|
9
|
+
|
10
|
+
module Hamlit
|
11
|
+
class Engine < Temple::Engine
|
12
|
+
define_options(
|
13
|
+
:buffer_class,
|
14
|
+
generator: Temple::Generators::ArrayBuffer,
|
15
|
+
format: :html,
|
16
|
+
attr_quote: "'",
|
17
|
+
escape_html: true,
|
18
|
+
escape_attrs: true,
|
19
|
+
autoclose: %w(area base basefont br col command embed frame
|
20
|
+
hr img input isindex keygen link menuitem meta
|
21
|
+
param source track wbr),
|
22
|
+
filename: "",
|
23
|
+
)
|
24
|
+
|
25
|
+
use Parser
|
26
|
+
use Compiler
|
27
|
+
use HTML
|
28
|
+
use StringSplitter
|
29
|
+
filter :StaticAnalyzer
|
30
|
+
use Escapable
|
31
|
+
use ForceEscapable
|
32
|
+
filter :ControlFlow
|
33
|
+
filter :MultiFlattener
|
34
|
+
filter :StaticMerger
|
35
|
+
use :Generator, -> { options[:generator] }
|
36
|
+
end
|
37
|
+
end
|
data/lib/hamlit/error.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hamlit
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :line
|
5
|
+
|
6
|
+
def initialize(message = nil, line = nil)
|
7
|
+
super(message)
|
8
|
+
@line = line
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class SyntaxError < Error; end
|
13
|
+
class InternalError < Error; end
|
14
|
+
class FilterNotFound < Error; end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'hamlit/utils'
|
3
|
+
|
4
|
+
module Hamlit
|
5
|
+
class Escapable < Temple::Filters::Escapable
|
6
|
+
def initialize(opts = {})
|
7
|
+
super
|
8
|
+
@escape_code = options[:escape_code] ||
|
9
|
+
"::Hamlit::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((%s))"
|
10
|
+
@escaper = eval("proc {|v| #{@escape_code % 'v'} }")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'hamlit/filters/base'
|
3
|
+
require 'hamlit/filters/text_base'
|
4
|
+
require 'hamlit/filters/tilt_base'
|
5
|
+
require 'hamlit/filters/coffee'
|
6
|
+
require 'hamlit/filters/css'
|
7
|
+
require 'hamlit/filters/erb'
|
8
|
+
require 'hamlit/filters/escaped'
|
9
|
+
require 'hamlit/filters/javascript'
|
10
|
+
require 'hamlit/filters/less'
|
11
|
+
require 'hamlit/filters/markdown'
|
12
|
+
require 'hamlit/filters/plain'
|
13
|
+
require 'hamlit/filters/preserve'
|
14
|
+
require 'hamlit/filters/ruby'
|
15
|
+
require 'hamlit/filters/sass'
|
16
|
+
require 'hamlit/filters/scss'
|
17
|
+
require 'hamlit/filters/cdata'
|
18
|
+
|
19
|
+
module Hamlit
|
20
|
+
class Filters
|
21
|
+
@registered = {}
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_reader :registered
|
25
|
+
|
26
|
+
def remove_filter(name)
|
27
|
+
registered.delete(name.to_s.downcase.to_sym)
|
28
|
+
if constants.map(&:to_s).include?(name.to_s)
|
29
|
+
remove_const name.to_sym
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def register(name, compiler)
|
36
|
+
registered[name] = compiler
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
register :coffee, Coffee
|
41
|
+
register :coffeescript, CoffeeScript
|
42
|
+
register :css, Css
|
43
|
+
register :erb, Erb
|
44
|
+
register :escaped, Escaped
|
45
|
+
register :javascript, Javascript
|
46
|
+
register :less, Less
|
47
|
+
register :markdown, Markdown
|
48
|
+
register :plain, Plain
|
49
|
+
register :preserve, Preserve
|
50
|
+
register :ruby, Ruby
|
51
|
+
register :sass, Sass
|
52
|
+
register :scss, Scss
|
53
|
+
register :cdata, Cdata
|
54
|
+
|
55
|
+
def initialize(options = {})
|
56
|
+
@options = options
|
57
|
+
@compilers = {}
|
58
|
+
end
|
59
|
+
|
60
|
+
def compile(node)
|
61
|
+
node.value[:text] ||= ''
|
62
|
+
find_compiler(node).compile(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def find_compiler(node)
|
68
|
+
name = node.value[:name].to_sym
|
69
|
+
compiler = Filters.registered[name]
|
70
|
+
raise FilterNotFound.new("FilterCompiler for '#{name}' was not found", node.line.to_i - 1) unless compiler
|
71
|
+
|
72
|
+
@compilers[name] ||= compiler.new(@options)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hamlit
|
3
|
+
class Filters
|
4
|
+
class Cdata < TextBase
|
5
|
+
def compile(node)
|
6
|
+
compile_cdata(node)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def compile_cdata(node)
|
12
|
+
temple = [:multi]
|
13
|
+
temple << [:static, "<![CDATA[\n"]
|
14
|
+
compile_text!(temple, node, ' ')
|
15
|
+
temple << [:static, "\n]]>"]
|
16
|
+
temple
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|