sablon 0.0.21 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/Gemfile.lock +9 -9
- data/README.md +120 -11
- data/lib/sablon.rb +7 -1
- data/lib/sablon/configuration/configuration.rb +165 -0
- data/lib/sablon/configuration/html_tag.rb +99 -0
- data/lib/sablon/content.rb +12 -9
- data/lib/sablon/context.rb +27 -20
- data/lib/sablon/environment.rb +31 -0
- data/lib/sablon/html/ast.rb +290 -75
- data/lib/sablon/html/ast_builder.rb +90 -0
- data/lib/sablon/html/converter.rb +3 -123
- data/lib/sablon/numbering.rb +0 -5
- data/lib/sablon/operations.rb +11 -11
- data/lib/sablon/parser/mail_merge.rb +7 -6
- data/lib/sablon/processor/document.rb +9 -9
- data/lib/sablon/processor/numbering.rb +4 -4
- data/lib/sablon/template.rb +5 -4
- data/lib/sablon/version.rb +1 -1
- data/sablon.gemspec +3 -3
- data/test/configuration_test.rb +122 -0
- data/test/content_test.rb +7 -6
- data/test/context_test.rb +11 -11
- data/test/environment_test.rb +27 -0
- data/test/expression_test.rb +2 -2
- data/test/fixtures/html/html_test_content.html +174 -0
- data/test/fixtures/html_sample.docx +0 -0
- data/test/fixtures/xml/comment_block_and_comment_as_key.xml +31 -0
- data/test/html/ast_builder_test.rb +65 -0
- data/test/html/ast_test.rb +117 -0
- data/test/html/converter_test.rb +386 -87
- data/test/html/node_properties_test.rb +113 -0
- data/test/html_test.rb +10 -10
- data/test/mail_merge_parser_test.rb +3 -2
- data/test/processor/document_test.rb +20 -2
- data/test/section_properties_test.rb +1 -1
- data/test/support/html_snippets.rb +9 -0
- data/test/test_helper.rb +0 -1
- metadata +27 -7
data/lib/sablon/version.rb
CHANGED
data/sablon.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Sablon::VERSION
|
9
9
|
spec.authors = ["Yves Senn"]
|
10
10
|
spec.email = ["yves.senn@gmail.com"]
|
11
|
-
spec.summary = %q{docx
|
11
|
+
spec.summary = %q{docx template processor}
|
12
12
|
spec.description = %q{Sablon is a document template processor. At this time it works only with docx and MailMerge fields.}
|
13
13
|
spec.homepage = "http://github.com/senny/sablon"
|
14
14
|
spec.license = "MIT"
|
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
spec.add_runtime_dependency 'nokogiri', ">= 1.6.0"
|
23
|
-
spec.add_runtime_dependency 'rubyzip', ">= 1.1"
|
23
|
+
spec.add_runtime_dependency 'rubyzip', ">= 1.1.1"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler", ">= 1.6"
|
26
|
-
spec.add_development_dependency "rake", "~>
|
26
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
27
27
|
spec.add_development_dependency "minitest", "~> 5.4"
|
28
28
|
spec.add_development_dependency "xml-simple"
|
29
29
|
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class ConfigurationTest < Sablon::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@config = Sablon::Configuration.send(:new)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_register_tag
|
11
|
+
options = {
|
12
|
+
ast_class: :paragraph,
|
13
|
+
attributes: { dummy: 'value' },
|
14
|
+
properties: { pstyle: 'ListBullet' },
|
15
|
+
allowed_children: %i[_inline ol ul li]
|
16
|
+
}
|
17
|
+
# test initialization without type
|
18
|
+
tag = @config.register_html_tag(:test_tag, **options)
|
19
|
+
assert_equal @config.permitted_html_tags[:test_tag], tag
|
20
|
+
assert_equal tag.name, :test_tag
|
21
|
+
assert_equal tag.type, :inline
|
22
|
+
assert_equal tag.ast_class, Sablon::HTMLConverter::Paragraph
|
23
|
+
assert_equal tag.attributes, dummy: 'value'
|
24
|
+
assert_equal tag.properties, pstyle: 'ListBullet'
|
25
|
+
assert_equal tag.allowed_children, %i[_inline ol ul li]
|
26
|
+
|
27
|
+
# test initialization with type
|
28
|
+
tag = @config.register_html_tag('test_tag2', :block, **options)
|
29
|
+
assert_equal @config.permitted_html_tags[:test_tag2], tag
|
30
|
+
assert_equal tag.name, :test_tag2
|
31
|
+
assert_equal tag.type, :block
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_remove_tag
|
35
|
+
tag = @config.register_html_tag(:test)
|
36
|
+
assert_equal @config.remove_html_tag(:test), tag
|
37
|
+
assert_nil @config.permitted_html_tags[:test]
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_register_style_converter_on_existing_ast_class
|
41
|
+
converter = ->(v) { return "test-attr-#{v}" }
|
42
|
+
@config.register_style_converter(:run, 'my-test-attr', converter)
|
43
|
+
#
|
44
|
+
assert @config.defined_style_conversions[:run]['my-test-attr'], 'converter should be stored in hash'
|
45
|
+
assert_equal 'test-attr-123', @config.defined_style_conversions[:run]['my-test-attr'].call(123)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_register_style_converter_on_newast_class
|
49
|
+
converter = ->(v) { return "test-attr-#{v}" }
|
50
|
+
@config.register_style_converter(:unset_ast_class, 'my-test-attr', converter)
|
51
|
+
#
|
52
|
+
assert @config.defined_style_conversions[:unset_ast_class]['my-test-attr'], 'converter should be stored in hash'
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_remove_style_converter
|
56
|
+
converter = ->(v) { return "test-attr-#{v}" }
|
57
|
+
converter = @config.register_style_converter(:run, 'my-test-attr', converter)
|
58
|
+
#
|
59
|
+
assert_equal converter, @config.remove_style_converter(:run, 'my-test-attr')
|
60
|
+
assert_nil @config.defined_style_conversions[:run]['my-test-attr']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ConfigurationHTMLTagTest < Sablon::TestCase
|
65
|
+
# test basic instantiation of an HTMLTag
|
66
|
+
def test_html_tag_defaults
|
67
|
+
tag = Sablon::Configuration::HTMLTag.new(:a, :inline)
|
68
|
+
assert_equal tag.name, :a
|
69
|
+
assert_equal tag.type, :inline
|
70
|
+
assert_nil tag.ast_class
|
71
|
+
assert_equal tag.attributes, {}
|
72
|
+
assert_equal tag.properties, {}
|
73
|
+
assert_equal tag.allowed_children, %i[_inline ol ul]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Exercising more of the logic used to conform args into valid
|
77
|
+
def test_html_tag_full_init
|
78
|
+
args = ['a', 'inline', ast_class: Sablon::HTMLConverter::Run]
|
79
|
+
tag = Sablon::Configuration::HTMLTag.new(*args)
|
80
|
+
assert_equal tag.name, :a
|
81
|
+
assert_equal tag.type, :inline
|
82
|
+
assert_equal tag.ast_class, Sablon::HTMLConverter::Run
|
83
|
+
#
|
84
|
+
options = {
|
85
|
+
ast_class: :run,
|
86
|
+
attributes: { dummy: 'value1' },
|
87
|
+
properties: { dummy2: 'value2' },
|
88
|
+
allowed_children: 'text'
|
89
|
+
}
|
90
|
+
tag = Sablon::Configuration::HTMLTag.new('a', 'inline', **options)
|
91
|
+
#
|
92
|
+
assert_equal tag.name, :a
|
93
|
+
assert_equal tag.type, :inline
|
94
|
+
assert_equal tag.ast_class, Sablon::HTMLConverter::Run
|
95
|
+
assert_equal tag.attributes, dummy: 'value1'
|
96
|
+
assert_equal tag.properties, dummy2: 'value2'
|
97
|
+
assert_equal tag.allowed_children, [:text]
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_html_tag_init_block_without_class
|
101
|
+
e = assert_raises ArgumentError do
|
102
|
+
Sablon::Configuration::HTMLTag.new(:form, :block)
|
103
|
+
end
|
104
|
+
assert_equal "Block level tag form must have an AST class.", e.message
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_html_tag_allowed_children
|
108
|
+
# define different tags for testing
|
109
|
+
text = Sablon::Configuration::HTMLTag.new(:text, :inline)
|
110
|
+
div = Sablon::Configuration::HTMLTag.new(:div, :block, ast_class: :paragraph)
|
111
|
+
olist = Sablon::Configuration::HTMLTag.new(:ol, :block, ast_class: :paragraph, allowed_children: %i[_block])
|
112
|
+
|
113
|
+
# test default allowances
|
114
|
+
assert div.allowed_child?(text) # all inline elements allowed
|
115
|
+
assert div.allowed_child?(olist) # tag name is included even though it is bock leve
|
116
|
+
assert_equal div.allowed_child?(div), false # other block elms are not allowed
|
117
|
+
|
118
|
+
# test olist with allowances for all blocks but no inline
|
119
|
+
assert olist.allowed_child?(div) # all block elements allowed
|
120
|
+
assert_equal olist.allowed_child?(text), false # no inline elements
|
121
|
+
end
|
122
|
+
end
|
data/test/content_test.rb
CHANGED
@@ -76,6 +76,7 @@ module ContentTestSetup
|
|
76
76
|
@document = Nokogiri::XML.fragment(@template_text)
|
77
77
|
@paragraph = @document.children.first
|
78
78
|
@node = @document.css("span").first
|
79
|
+
@env = Sablon::Environment.new(nil)
|
79
80
|
end
|
80
81
|
|
81
82
|
private
|
@@ -88,7 +89,7 @@ class ContentStringTest < Sablon::TestCase
|
|
88
89
|
include ContentTestSetup
|
89
90
|
|
90
91
|
def test_single_line_string
|
91
|
-
Sablon.content(:string, "a normal string").append_to @paragraph, @node
|
92
|
+
Sablon.content(:string, "a normal string").append_to @paragraph, @node, @env
|
92
93
|
|
93
94
|
output = <<-XML.strip
|
94
95
|
<w:p><span>template</span><span>a normal string</span></w:p><w:p>AFTER</w:p>
|
@@ -97,7 +98,7 @@ class ContentStringTest < Sablon::TestCase
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def test_numeric_string
|
100
|
-
Sablon.content(:string, 42).append_to @paragraph, @node
|
101
|
+
Sablon.content(:string, 42).append_to @paragraph, @node, @env
|
101
102
|
|
102
103
|
output = <<-XML.strip
|
103
104
|
<w:p><span>template</span><span>42</span></w:p><w:p>AFTER</w:p>
|
@@ -106,7 +107,7 @@ class ContentStringTest < Sablon::TestCase
|
|
106
107
|
end
|
107
108
|
|
108
109
|
def test_string_with_newlines
|
109
|
-
Sablon.content(:string, "a\nmultiline\n\nstring").append_to @paragraph, @node
|
110
|
+
Sablon.content(:string, "a\nmultiline\n\nstring").append_to @paragraph, @node, @env
|
110
111
|
|
111
112
|
output = <<-XML.strip.gsub("\n", "")
|
112
113
|
<w:p>
|
@@ -125,7 +126,7 @@ class ContentStringTest < Sablon::TestCase
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def test_blank_string
|
128
|
-
Sablon.content(:string, "").append_to @paragraph, @node
|
129
|
+
Sablon.content(:string, "").append_to @paragraph, @node, @env
|
129
130
|
|
130
131
|
assert_xml_equal @template_text, @document
|
131
132
|
end
|
@@ -135,14 +136,14 @@ class ContentWordMLTest < Sablon::TestCase
|
|
135
136
|
include ContentTestSetup
|
136
137
|
|
137
138
|
def test_blank_word_ml
|
138
|
-
Sablon.content(:word_ml, "").append_to @paragraph, @node
|
139
|
+
Sablon.content(:word_ml, "").append_to @paragraph, @node, @env
|
139
140
|
|
140
141
|
assert_xml_equal "<w:p>AFTER</w:p>", @document
|
141
142
|
end
|
142
143
|
|
143
144
|
def test_inserts_word_ml_into_the_document
|
144
145
|
@word_ml = '<w:p><w:r><w:t xml:space="preserve">a </w:t></w:r></w:p>'
|
145
|
-
Sablon.content(:word_ml, @word_ml).append_to @paragraph, @node
|
146
|
+
Sablon.content(:word_ml, @word_ml).append_to @paragraph, @node, @env
|
146
147
|
|
147
148
|
output = <<-XML.strip.gsub("\n", "")
|
148
149
|
<w:p>
|
data/test/context_test.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require "test_helper"
|
3
3
|
|
4
|
-
class
|
4
|
+
class EnvironmentTest < Sablon::TestCase
|
5
5
|
def test_converts_symbol_keys_to_string_keys
|
6
|
-
|
7
|
-
assert_equal({"a"=>1, "b"=>{"c" =>2, "d"=>3}},
|
6
|
+
context = Sablon::Context.transform_hash(a: 1, b: { c: 2, "d" => 3 })
|
7
|
+
assert_equal({ "a" => 1, "b" => { "c" => 2, "d" => 3 } }, context)
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_recognizes_wordml_keys
|
11
|
-
|
12
|
-
assert_equal({ "mykey"=>Sablon.content(:word_ml, "<w:p><w:p>"),
|
13
|
-
"otherkey"=>"<nope>"},
|
11
|
+
context = Sablon::Context.transform_hash("word_ml:mykey" => "<w:p><w:p>", "otherkey" => "<nope>")
|
12
|
+
assert_equal({ "mykey" => Sablon.content(:word_ml, "<w:p><w:p>"),
|
13
|
+
"otherkey" => "<nope>"}, context)
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_recognizes_html_keys
|
17
|
-
|
18
|
-
assert_equal({ "mykey"=>Sablon.content(:html, "**yay**"),
|
19
|
-
"otherkey"=>"<nope>"},
|
17
|
+
context = Sablon::Context.transform_hash("html:mykey" => "**yay**", "otherkey" => "<nope>")
|
18
|
+
assert_equal({ "mykey" => Sablon.content(:html, "**yay**"),
|
19
|
+
"otherkey" => "<nope>"}, context)
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_does_not_wrap_html_and_wordml_with_nil_value
|
23
|
-
|
23
|
+
context = Sablon::Context.transform_hash("html:mykey" => nil, "word_ml:otherkey" => nil, "normalkey" => nil)
|
24
24
|
assert_equal({ "mykey" => nil,
|
25
25
|
"otherkey" => nil,
|
26
|
-
"normalkey" => nil},
|
26
|
+
"normalkey" => nil}, context)
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class EnvironmentTest < Sablon::TestCase
|
5
|
+
def test_transforms_internal_hash
|
6
|
+
context = Sablon::Context.transform_hash(a: 1, b: { c: 2, "d" => 3 })
|
7
|
+
env = Sablon::Environment.new(nil, a: 1, b: { c: 2, "d" => 3 })
|
8
|
+
#
|
9
|
+
assert_nil env.template
|
10
|
+
assert_equal context, env.context
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_alter_context
|
14
|
+
# set initial context
|
15
|
+
env = Sablon::Environment.new(nil, a: 1, b: { c: 2, "d" => 3 })
|
16
|
+
# alter context to change a single key and set a new one
|
17
|
+
env2 = env.alter_context(a: "a", e: "new-key")
|
18
|
+
assert_equal({ "a" => "a", "b" => { "c" => 2, "d" => 3 }, "e" => "new-key" }, env2.context)
|
19
|
+
# check that the old context was not modified
|
20
|
+
assert_equal({"a" => 1, "b" => { "c" => 2, "d" => 3 }}, env.context)
|
21
|
+
# check that numbering and template are the same references
|
22
|
+
assert env.template.equal?(env2.template), "#{env.template} != #{env2.template}"
|
23
|
+
assert env.numbering.equal?(env2.numbering), "#{env.numbering} != #{env2.numbering}"
|
24
|
+
# check that a new context reference was created
|
25
|
+
assert !env.context.equal?(env2.context), "#{env.context} == #{env2.context}"
|
26
|
+
end
|
27
|
+
end
|
data/test/expression_test.rb
CHANGED
@@ -55,7 +55,7 @@ class LookupOrMethodCallTest < Sablon::TestCase
|
|
55
55
|
def test_missing_receiver
|
56
56
|
user = OpenStruct.new(first_name: "Jack")
|
57
57
|
expr = Sablon::Expression.parse("user.address.line_1")
|
58
|
-
|
59
|
-
|
58
|
+
assert_nil expr.evaluate("user" => user)
|
59
|
+
assert_nil expr.evaluate({})
|
60
60
|
end
|
61
61
|
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
<h1>Sablon HTML insertion</h1>
|
2
|
+
|
3
|
+
<h2>Text</h2>
|
4
|
+
|
5
|
+
<div>
|
6
|
+
Lorem <strong>ipsum</strong> <em>dolor</em> <strong>sit</strong>
|
7
|
+
<em>amet</em>, <strong>consectetur adipiscing elit</strong>.
|
8
|
+
<em>Suspendisse a tempus turpis</em>. <span>Duis urna <b>justo</b>,
|
9
|
+
<i>vehicula</i> vitae ultricies vel, congue at sem.</span> Fusce turpis
|
10
|
+
turpis, aliquet id pulvinar aliquam, iaculis non elit. Nulla feugiat
|
11
|
+
lectus nulla, in dictum ipsum cursus ac. Quisque at odio neque.
|
12
|
+
Sed ac tortor iaculis, bibendum leo ut, malesuada velit. Donec iaculis
|
13
|
+
sed urna eget pharetra. <u>Praesent ornare fermentum turpis</u>, placerat
|
14
|
+
iaculis urna bibendum vitae. Nunc in quam consequat, tristique tellus in,
|
15
|
+
commodo turpis. Curabitur ullamcorper odio purus, lobortis egestas magna
|
16
|
+
laoreet vitae. Nunc fringilla velit ante, eu aliquam nisi cursus vitae.
|
17
|
+
Suspendisse sit amet dui <s><sup>egestas</sup>, <sub>volutpat</sub></s>
|
18
|
+
nisi vel, mattis justo. Nullam pellentesque, ipsum eget blandit pharetra,
|
19
|
+
augue elit <sup>aliquam <sub>mauris</sub></sup>, vel mollis nisl augue ut
|
20
|
+
<s>ipsum</s>.
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<h2>HTML Entities</h2>
|
24
|
+
<div>
|
25
|
+
All HTML entities should get passed through to the final doc <br/>
|
26
|
+
Less Than: < <br/>
|
27
|
+
Ampersand: & <br/>
|
28
|
+
Percent: % <br/>
|
29
|
+
One Quarter: ¼ <br/>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
|
33
|
+
<h2>Lists</h2>
|
34
|
+
|
35
|
+
<ol>
|
36
|
+
<li>
|
37
|
+
Vestibulum
|
38
|
+
<ol>
|
39
|
+
<li>ante ipsum primis </li>
|
40
|
+
</ol>
|
41
|
+
</li>
|
42
|
+
<li>
|
43
|
+
in faucibus orci luctus
|
44
|
+
<ol>
|
45
|
+
<li>et ultrices posuere cubilia Curae;
|
46
|
+
<ol>
|
47
|
+
<li>Aliquam vel dolor </li>
|
48
|
+
<li>sed sem maximus </li>
|
49
|
+
</ol>
|
50
|
+
</li>
|
51
|
+
<li>
|
52
|
+
fermentum in non odio.
|
53
|
+
<ol>
|
54
|
+
<li>Fusce hendrerit ornare mollis. </li>
|
55
|
+
</ol>
|
56
|
+
</li>
|
57
|
+
<li>Nunc scelerisque nibh nec turpis tempor pulvinar. </li>
|
58
|
+
</ol>
|
59
|
+
</li>
|
60
|
+
<li>Donec eros turpis, </li>
|
61
|
+
<li>
|
62
|
+
aliquet vel volutpat sit amet,
|
63
|
+
<ol>
|
64
|
+
<li>semper eu purus. </li>
|
65
|
+
<li>
|
66
|
+
Proin ac erat nec urna efficitur vulputate.
|
67
|
+
<ol>
|
68
|
+
<li>Quisque varius convallis ultricies. </li>
|
69
|
+
<li>Nullam vel fermentum eros. </li>
|
70
|
+
</ol>
|
71
|
+
</li>
|
72
|
+
</ol>
|
73
|
+
</li>
|
74
|
+
</ol>
|
75
|
+
|
76
|
+
<div>
|
77
|
+
Pellentesque nulla leo, auctor ornare erat sed, rhoncus congue diam.
|
78
|
+
Duis non porttitor nulla, ut eleifend enim. Pellentesque non tempor sem.
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<div>Mauris auctor egestas arcu, </div>
|
82
|
+
|
83
|
+
<ol>
|
84
|
+
<li>id venenatis nibh dignissim id. </li>
|
85
|
+
<li>In non placerat metus. </li>
|
86
|
+
</ol>
|
87
|
+
|
88
|
+
<ul>
|
89
|
+
<li>Nunc sed consequat metus. </li>
|
90
|
+
<li>Nulla consectetur lorem consequat, </li>
|
91
|
+
<li>malesuada dui at, lacinia lectus. </li>
|
92
|
+
</ul>
|
93
|
+
|
94
|
+
<ol>
|
95
|
+
<li>Aliquam efficitur </li>
|
96
|
+
<li>lorem a mauris feugiat, </li>
|
97
|
+
<li>at semper eros pellentesque. </li>
|
98
|
+
</ol>
|
99
|
+
|
100
|
+
<div>
|
101
|
+
Nunc lacus diam, consectetur ut odio sit amet, placerat pharetra erat.
|
102
|
+
Sed commodo ut sem id congue. Sed eget neque elit. Curabitur at erat tortor.
|
103
|
+
Maecenas eget sapien vitae est sagittis accumsan et nec orci. Integer
|
104
|
+
luctus at nisl eget venenatis. Nunc nunc eros, consectetur at tortor et,
|
105
|
+
tristique ultrices elit. Nulla in turpis nibh.
|
106
|
+
</div>
|
107
|
+
|
108
|
+
<ul>
|
109
|
+
<li>
|
110
|
+
Nam consectetur
|
111
|
+
<ul>
|
112
|
+
<li>venenatis tempor. </li>
|
113
|
+
</ul>
|
114
|
+
</li>
|
115
|
+
<li>
|
116
|
+
Aenean
|
117
|
+
<ul>
|
118
|
+
<li>blandit
|
119
|
+
<ul>
|
120
|
+
<li>porttitor massa,
|
121
|
+
<ul>
|
122
|
+
<li>non efficitur
|
123
|
+
<ul>
|
124
|
+
<li>metus. </li>
|
125
|
+
</ul>
|
126
|
+
</li>
|
127
|
+
</ul>
|
128
|
+
</li>
|
129
|
+
</ul>
|
130
|
+
</li>
|
131
|
+
</ul>
|
132
|
+
</li>
|
133
|
+
<li>Duis faucibus nunc nec venenatis faucibus. </li>
|
134
|
+
<li>Aliquam erat volutpat. </li>
|
135
|
+
</ul>
|
136
|
+
<div style="border: 5px double #FF00FF">
|
137
|
+
<strong>Quisque non neque ut lacus eleifend volutpat quis sed lacus.
|
138
|
+
<br />Praesent ultrices purus eu quam elementum, sit amet faucibus elit
|
139
|
+
interdum. In lectus orci,<br /> elementum quis dictum ac, porta ac ante.
|
140
|
+
Fusce tempus ac mauris id cursus. Phasellus a erat nulla. <em>Mauris dolor orci</em>,
|
141
|
+
malesuada auctor dignissim non, <u>posuere nec odio</u>. Etiam hendrerit
|
142
|
+
justo nec diam ullamcorper, nec blandit elit sodales.</strong>
|
143
|
+
</div>
|
144
|
+
|
145
|
+
|
146
|
+
<div style="text-align: both; background-color: #EAFEDA; vertical-align: top">
|
147
|
+
<span style="font-size: 24;">U</span>t eget auctor enim.
|
148
|
+
<span style="text-decoration: underline wavyDouble #123456">Quisque id
|
149
|
+
neque eu nibh feugiat <b style="text-decoration: line-through">imperdiet</b>
|
150
|
+
id ut dui.</span> Ut auctor libero eget <em style="text-decoration: emboss; color: #F3AADE">
|
151
|
+
massa tristique pharetra</em>. Cras tincidunt finibus sapien, ut maximus
|
152
|
+
tortor tempor at. <span style="background-color: #00FF00">Proin pulvinar
|
153
|
+
pretium</span> justo vitae malesuada. Suspendisse porta purus eget tortor
|
154
|
+
tincidunt vestibulum. Maecenas id egestas purus, quis vulputate
|
155
|
+
lacus. Quisque <span style="vertical-align: superscript">non</span>
|
156
|
+
<span style="vertical-align: subscript">eleifend</span> est.
|
157
|
+
</div>
|
158
|
+
|
159
|
+
<ul style="background-color: #F19F42">
|
160
|
+
<li>Item 1</li>
|
161
|
+
<li>Item 2</li>
|
162
|
+
<ul>
|
163
|
+
<li>Nested 1</li>
|
164
|
+
<li style="background-color: #FFAAAA">
|
165
|
+
Nested 2
|
166
|
+
<ul>
|
167
|
+
<li>Nested 2.1</li>
|
168
|
+
<li><span style="font-style: italic; font-weight: bold">Nested</span> 2.2</li>
|
169
|
+
<li>Nested 2.3</li>
|
170
|
+
</ul>
|
171
|
+
</li>
|
172
|
+
</ul>
|
173
|
+
<li>Item 3</li>
|
174
|
+
</ul>
|