roadie 2.4.3 → 3.0.0

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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +10 -14
  4. data/.yardopts +1 -1
  5. data/Changelog.md +38 -5
  6. data/Gemfile +3 -4
  7. data/Guardfile +12 -1
  8. data/README.md +168 -164
  9. data/Rakefile +2 -19
  10. data/lib/roadie.rb +15 -68
  11. data/lib/roadie/asset_provider.rb +7 -58
  12. data/lib/roadie/asset_scanner.rb +92 -0
  13. data/lib/roadie/document.rb +103 -0
  14. data/lib/roadie/errors.rb +57 -0
  15. data/lib/roadie/filesystem_provider.rb +30 -60
  16. data/lib/roadie/inliner.rb +72 -217
  17. data/lib/roadie/markup_improver.rb +88 -0
  18. data/lib/roadie/null_provider.rb +13 -0
  19. data/lib/roadie/null_url_rewriter.rb +12 -0
  20. data/lib/roadie/provider_list.rb +71 -0
  21. data/lib/roadie/rspec.rb +1 -0
  22. data/lib/roadie/rspec/asset_provider.rb +49 -0
  23. data/lib/roadie/selector.rb +43 -18
  24. data/lib/roadie/style_attribute_builder.rb +25 -0
  25. data/lib/roadie/style_block.rb +32 -0
  26. data/lib/roadie/style_property.rb +93 -0
  27. data/lib/roadie/stylesheet.rb +65 -0
  28. data/lib/roadie/upgrade_guide.rb +36 -0
  29. data/lib/roadie/url_generator.rb +126 -0
  30. data/lib/roadie/url_rewriter.rb +84 -0
  31. data/lib/roadie/version.rb +1 -1
  32. data/roadie.gemspec +8 -11
  33. data/spec/fixtures/big_em.css +1 -0
  34. data/spec/fixtures/stylesheets/green.css +1 -0
  35. data/spec/integration_spec.rb +125 -95
  36. data/spec/lib/roadie/asset_scanner_spec.rb +153 -0
  37. data/spec/lib/roadie/css_not_found_spec.rb +17 -0
  38. data/spec/lib/roadie/document_spec.rb +123 -0
  39. data/spec/lib/roadie/filesystem_provider_spec.rb +44 -68
  40. data/spec/lib/roadie/inliner_spec.rb +105 -537
  41. data/spec/lib/roadie/markup_improver_spec.rb +78 -0
  42. data/spec/lib/roadie/null_provider_spec.rb +21 -0
  43. data/spec/lib/roadie/null_url_rewriter_spec.rb +19 -0
  44. data/spec/lib/roadie/provider_list_spec.rb +89 -0
  45. data/spec/lib/roadie/selector_spec.rb +15 -10
  46. data/spec/lib/roadie/style_attribute_builder_spec.rb +29 -0
  47. data/spec/lib/roadie/style_block_spec.rb +35 -0
  48. data/spec/lib/roadie/style_property_spec.rb +82 -0
  49. data/spec/lib/roadie/stylesheet_spec.rb +41 -0
  50. data/spec/lib/roadie/test_provider_spec.rb +29 -0
  51. data/spec/lib/roadie/url_generator_spec.rb +121 -0
  52. data/spec/lib/roadie/url_rewriter_spec.rb +79 -0
  53. data/spec/shared_examples/asset_provider.rb +11 -0
  54. data/spec/shared_examples/url_rewriter.rb +23 -0
  55. data/spec/spec_helper.rb +6 -60
  56. data/spec/support/have_attribute_matcher.rb +2 -2
  57. data/spec/support/have_node_matcher.rb +4 -4
  58. data/spec/support/have_selector_matcher.rb +3 -3
  59. data/spec/support/have_styling_matcher.rb +51 -15
  60. data/spec/support/test_provider.rb +13 -0
  61. metadata +86 -175
  62. data/Appraisals +0 -15
  63. data/gemfiles/rails_3.0.gemfile +0 -7
  64. data/gemfiles/rails_3.0.gemfile.lock +0 -123
  65. data/gemfiles/rails_3.1.gemfile +0 -7
  66. data/gemfiles/rails_3.1.gemfile.lock +0 -126
  67. data/gemfiles/rails_3.2.gemfile +0 -7
  68. data/gemfiles/rails_3.2.gemfile.lock +0 -124
  69. data/gemfiles/rails_4.0.gemfile +0 -7
  70. data/gemfiles/rails_4.0.gemfile.lock +0 -119
  71. data/lib/roadie/action_mailer_extensions.rb +0 -95
  72. data/lib/roadie/asset_pipeline_provider.rb +0 -28
  73. data/lib/roadie/css_file_not_found.rb +0 -22
  74. data/lib/roadie/railtie.rb +0 -39
  75. data/lib/roadie/style_declaration.rb +0 -42
  76. data/spec/fixtures/app/assets/stylesheets/integration.css +0 -10
  77. data/spec/fixtures/public/stylesheets/integration.css +0 -10
  78. data/spec/fixtures/views/integration_mailer/marketing.html.erb +0 -2
  79. data/spec/fixtures/views/integration_mailer/notification.html.erb +0 -8
  80. data/spec/fixtures/views/integration_mailer/notification.text.erb +0 -6
  81. data/spec/lib/roadie/action_mailer_extensions_spec.rb +0 -227
  82. data/spec/lib/roadie/asset_pipeline_provider_spec.rb +0 -65
  83. data/spec/lib/roadie/css_file_not_found_spec.rb +0 -29
  84. data/spec/lib/roadie/style_declaration_spec.rb +0 -49
  85. data/spec/lib/roadie_spec.rb +0 -101
  86. data/spec/shared_examples/asset_provider_examples.rb +0 -11
  87. data/spec/support/anonymous_mailer.rb +0 -21
  88. data/spec/support/change_url_options.rb +0 -5
  89. data/spec/support/parse_styling.rb +0 -25
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ module Roadie
5
+ describe MarkupImprover do
6
+ def improve(html)
7
+ dom = Nokogiri::HTML.parse html
8
+ MarkupImprover.new(dom, html).improve
9
+ dom
10
+ end
11
+
12
+ # JRuby up to at least 1.6.0 has a bug where the doctype of a document cannot be changed.
13
+ # See https://github.com/sparklemotion/nokogiri/issues/984
14
+ def pending_for_buggy_jruby
15
+ # No reason to check for version yet since no existing version has a fix.
16
+ skip "Pending until Nokogiri issue #984 is fixed and released" if defined?(JRuby)
17
+ end
18
+
19
+ describe "automatic doctype" do
20
+ it "inserts a HTML5 doctype if no doctype is present" do
21
+ pending_for_buggy_jruby
22
+ expect(improve("<html></html>").internal_subset.to_xml).to eq("<!DOCTYPE html>")
23
+ end
24
+
25
+ it "does not insert duplicate doctypes" do
26
+ html = improve('<!DOCTYPE html><html><body></body></html>').to_html
27
+ expect(html.scan('DOCTYPE').size).to eq(1)
28
+ end
29
+
30
+ it "leaves other doctypes alone" do
31
+ dtd = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">"
32
+ html = "#{dtd}<html></html>"
33
+ expect(improve(html).internal_subset.to_xml.strip).to eq(dtd)
34
+ end
35
+ end
36
+
37
+ describe "basic HTML structure" do
38
+ it "inserts a <html> element as the root" do
39
+ expect(improve("<h1>Hey!</h1>")).to have_selector("html h1")
40
+ expect(improve("<html></html>").css('html').size).to eq(1)
41
+ end
42
+
43
+ it "inserts <head> if not present" do
44
+ expect(improve('<html><body></body></html>')).to have_selector('html > head + body')
45
+ expect(improve('<html></html>')).to have_selector('html > head')
46
+ expect(improve('Foo')).to have_selector('html > head')
47
+ expect(improve('<html><head></head></html>').css('head').size).to eq(1)
48
+ end
49
+
50
+ it "inserts <body> if not present" do
51
+ expect(improve('<h1>Hey!</h1>')).to have_selector('html > body > h1')
52
+ expect(improve('<html><h1>Hey!</h1></html>')).to have_selector('html > body > h1')
53
+ expect(improve('<html><body><h1>Hey!</h1></body></html>').css('body').size).to eq(1)
54
+ end
55
+ end
56
+
57
+ describe "charset declaration" do
58
+ it "is inserted if missing" do
59
+ dom = improve('<html><head></head><body></body></html>')
60
+
61
+ expect(dom).to have_selector('head meta')
62
+ meta = dom.at_css('head meta')
63
+ expect(meta['http-equiv']).to eq('Content-Type')
64
+ expect(meta['content']).to eq('text/html; charset=UTF-8')
65
+ end
66
+
67
+ it "is left alone when predefined" do
68
+ expect(improve(<<-HTML).xpath('//meta')).to have(1).item
69
+ <html>
70
+ <head>
71
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
72
+ </head>
73
+ </html>
74
+ HTML
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'shared_examples/asset_provider'
4
+
5
+ module Roadie
6
+ describe NullProvider do
7
+ it_behaves_like "asset provider role"
8
+
9
+ def expect_empty_stylesheet(stylesheet)
10
+ expect(stylesheet).not_to be_nil
11
+ expect(stylesheet.name).to eq("(null)")
12
+ expect(stylesheet).to have(0).blocks
13
+ expect(stylesheet.to_s).to be_empty
14
+ end
15
+
16
+ it "finds an empty stylesheet for every name" do
17
+ expect_empty_stylesheet NullProvider.new.find_stylesheet("omg wtf bbq")
18
+ expect_empty_stylesheet NullProvider.new.find_stylesheet!("omg wtf bbq")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'shared_examples/url_rewriter'
4
+
5
+ module Roadie
6
+ describe NullUrlRewriter do
7
+ let(:generator) { double "URL generator" }
8
+ subject(:rewriter) { NullUrlRewriter.new(generator) }
9
+
10
+ it_behaves_like "url rewriter"
11
+
12
+ it "does nothing when transforming DOM" do
13
+ dom = double "DOM tree"
14
+ expect {
15
+ NullUrlRewriter.new(generator).transform_dom dom
16
+ }.to_not raise_error
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'roadie/rspec'
4
+
5
+ module Roadie
6
+ describe ProviderList do
7
+ let(:test_provider) { TestProvider.new }
8
+ subject(:provider) { ProviderList.new([test_provider]) }
9
+
10
+ it_behaves_like "roadie asset provider", valid_name: "valid", invalid_name: "invalid" do
11
+ let(:test_provider) { TestProvider.new "valid" => "" }
12
+ end
13
+
14
+ it "finds using all given providers" do
15
+ first = TestProvider.new "foo.css" => "foo { color: green; }"
16
+ second = TestProvider.new "bar.css" => "bar { color: green; }"
17
+ provider = ProviderList.new [first, second]
18
+
19
+ expect(provider.find_stylesheet("foo.css").to_s).to include "foo"
20
+ expect(provider.find_stylesheet("bar.css").to_s).to include "bar"
21
+ expect(provider.find_stylesheet("baz.css")).to be_nil
22
+ end
23
+
24
+ it "is enumerable" do
25
+ expect(provider).to be_kind_of(Enumerable)
26
+ expect(provider).to respond_to(:each)
27
+ expect(provider.each.to_a).to eq([test_provider])
28
+ end
29
+
30
+ it "has a size" do
31
+ expect(provider.size).to eq(1)
32
+ expect(provider).not_to be_empty
33
+ end
34
+
35
+ it "has a first and a last element" do
36
+ providers = [double("1"), double("2"), double("3")]
37
+ list = ProviderList.new(providers)
38
+ expect(list.first).to eq(providers.first)
39
+ expect(list.last).to eq(providers.last)
40
+ end
41
+
42
+ it "can have providers pushed and popped" do
43
+ other = double "Some other provider"
44
+
45
+ expect {
46
+ provider.push other
47
+ provider << other
48
+ }.to change(provider, :size).by(2)
49
+
50
+ expect {
51
+ expect(provider.pop).to eq(other)
52
+ }.to change(provider, :size).by(-1)
53
+ end
54
+
55
+ it "can have providers shifted and unshifted" do
56
+ other = double "Some other provider"
57
+
58
+ expect {
59
+ provider.unshift other
60
+ }.to change(provider, :size).by(1)
61
+
62
+ expect {
63
+ expect(provider.shift).to eq(other)
64
+ }.to change(provider, :size).by(-1)
65
+ end
66
+
67
+ describe "wrapping" do
68
+ it "creates provider lists with the arguments" do
69
+ expect(ProviderList.wrap(test_provider)).to be_instance_of(ProviderList)
70
+ expect(ProviderList.wrap(test_provider, test_provider).size).to eq(2)
71
+ end
72
+
73
+ it "flattens arrays" do
74
+ expect(ProviderList.wrap([test_provider, test_provider], test_provider).size).to eq(3)
75
+ expect(ProviderList.wrap([test_provider, test_provider]).size).to eq(2)
76
+ end
77
+
78
+ it "combines with providers from other lists" do
79
+ other_list = ProviderList.new([test_provider, test_provider])
80
+ expect(ProviderList.wrap(test_provider, other_list).size).to eq(3)
81
+ end
82
+
83
+ it "returns the passed list if only a single ProviderList is passed" do
84
+ other_list = ProviderList.new([test_provider])
85
+ expect(ProviderList.wrap(other_list)).to eql other_list
86
+ end
87
+ end
88
+ end
89
+ end
@@ -4,11 +4,11 @@ require 'spec_helper'
4
4
  module Roadie
5
5
  describe Selector do
6
6
  it "can be coerced into String" do
7
- ("I love " + Selector.new("html")).should == "I love html"
7
+ expect("I love " + Selector.new("html")).to eq("I love html")
8
8
  end
9
9
 
10
10
  it "can be inlined when simple" do
11
- Selector.new("html body #main p.class").should be_inlinable
11
+ expect(Selector.new("html body #main p.class")).to be_inlinable
12
12
  end
13
13
 
14
14
  it "cannot be inlined when containing pseudo functions" do
@@ -27,32 +27,37 @@ module Roadie
27
27
  p:disabled
28
28
  p:checked
29
29
  ].each do |bad_selector|
30
- Selector.new(bad_selector).should_not be_inlinable
30
+ expect(Selector.new(bad_selector)).not_to be_inlinable
31
31
  end
32
32
 
33
- Selector.new('p.active').should be_inlinable
33
+ expect(Selector.new('p.active')).to be_inlinable
34
34
  end
35
35
 
36
36
  it "cannot be inlined when containing pseudo elements" do
37
- Selector.new('p::some-element').should_not be_inlinable
37
+ expect(Selector.new('p::some-element')).not_to be_inlinable
38
38
  end
39
39
 
40
40
  it "cannot be inlined when selector is an at-rule" do
41
- Selector.new('@keyframes progress-bar-stripes').should_not be_inlinable
41
+ expect(Selector.new('@keyframes progress-bar-stripes')).not_to be_inlinable
42
42
  end
43
43
 
44
44
  it "has a calculated specificity" do
45
45
  selector = "html p.active.nice #main.deep-selector"
46
- Selector.new(selector).specificity.should == CssParser.calculate_specificity(selector)
46
+ expect(Selector.new(selector).specificity).to eq(CssParser.calculate_specificity(selector))
47
+ end
48
+
49
+ it "can be told about the specificity at initialization" do
50
+ selector = "html p.active.nice #main.deep-selector"
51
+ expect(Selector.new(selector, 1337).specificity).to eq(1337)
47
52
  end
48
53
 
49
54
  it "is equal to other selectors when they match the same things" do
50
- Selector.new("foo").should == Selector.new("foo ")
51
- Selector.new("foo").should_not == "foo"
55
+ expect(Selector.new("foo")).to eq(Selector.new("foo "))
56
+ expect(Selector.new("foo")).not_to eq("foo")
52
57
  end
53
58
 
54
59
  it "strips the given selector" do
55
- Selector.new(" foo \n").to_s.should == Selector.new("foo").to_s
60
+ expect(Selector.new(" foo \n").to_s).to eq(Selector.new("foo").to_s)
56
61
  end
57
62
  end
58
63
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module Roadie
4
+ describe StyleAttributeBuilder do
5
+ it "sorts the added properties" do
6
+ builder = StyleAttributeBuilder.new
7
+
8
+ builder << StyleProperty.new("color", "green", true, 1)
9
+ builder << StyleProperty.new("font-size", "110%", false, 15)
10
+ builder << StyleProperty.new("color", "red", false, 15)
11
+
12
+ expect(builder.attribute_string).to eq "font-size:110%;color:red;color:green !important"
13
+ end
14
+
15
+ it "preserves the order of added attributes with the same specificity" do
16
+ builder = StyleAttributeBuilder.new
17
+
18
+ builder << StyleProperty.new("color", "pink", false, 50)
19
+ builder << StyleProperty.new("color", "red", false, 50)
20
+ builder << StyleProperty.new("color", "green", false, 50)
21
+
22
+ # We need one different element to trigger the problem with Ruby's
23
+ # unstable sort
24
+ builder << StyleProperty.new("background", "white", false, 1)
25
+
26
+ expect(builder.attribute_string).to eq "background:white;color:pink;color:red;color:green"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ module Roadie
5
+ describe StyleBlock do
6
+ it "has a selector and a list of properties" do
7
+ properties = []
8
+ selector = double "Selector"
9
+
10
+ block = StyleBlock.new(selector, properties)
11
+ expect(block.selector).to eq(selector)
12
+ expect(block.properties).to eq(properties)
13
+ end
14
+
15
+ it "delegates #specificity to the selector" do
16
+ selector = double "Selector", specificity: 45
17
+ expect(StyleBlock.new(selector, []).specificity).to eq(45)
18
+ end
19
+
20
+ it "delegates #inlinable? to the selector" do
21
+ selector = double "Selector", inlinable?: "maybe"
22
+ expect(StyleBlock.new(selector, []).inlinable?).to eq("maybe")
23
+ end
24
+
25
+ it "delegates #selector_string to selector#to_s" do
26
+ selector = double "Selector", to_s: "yey"
27
+ expect(StyleBlock.new(selector, []).selector_string).to eq("yey")
28
+ end
29
+
30
+ it "has a string representation" do
31
+ properties = [double(to_s: "bar"), double(to_s: "baz")]
32
+ expect(StyleBlock.new(double(to_s: "foo"), properties).to_s).to eq("foo{bar;baz}")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ module Roadie
4
+ describe StyleProperty do
5
+ it "is initialized with a property, value, if it is marked as important, and the specificity" do
6
+ StyleProperty.new('color', 'green', true, 45).tap do |declaration|
7
+ expect(declaration.property).to eq('color')
8
+ expect(declaration.value).to eq('green')
9
+ expect(declaration).to be_important
10
+ expect(declaration.specificity).to eq(45)
11
+ end
12
+ end
13
+
14
+ describe "string representation" do
15
+ it "is the property and the value joined with a colon" do
16
+ expect(StyleProperty.new('color', 'green', false, 1).to_s).to eq('color:green')
17
+ expect(StyleProperty.new('font-size', '1.1em', false, 1).to_s).to eq('font-size:1.1em')
18
+ end
19
+
20
+ it "contains the !important flag when set" do
21
+ expect(StyleProperty.new('color', 'green', true, 1).to_s).to eq('color:green !important')
22
+ end
23
+ end
24
+
25
+ describe "comparing" do
26
+ def declaration(specificity, important = false)
27
+ StyleProperty.new('color', 'green', important, specificity)
28
+ end
29
+
30
+ it "compares on specificity" do
31
+ expect(declaration(5)).to eq(declaration(5))
32
+ expect(declaration(4)).to be < declaration(5)
33
+ expect(declaration(6)).to be > declaration(5)
34
+ end
35
+
36
+ context "with an important declaration" do
37
+ it "is less than the important declaration regardless of the specificity" do
38
+ expect(declaration(99, false)).to be < declaration(1, true)
39
+ end
40
+
41
+ it "compares like normal when both declarations are important" do
42
+ expect(declaration(5, true)).to eq(declaration(5, true))
43
+ expect(declaration(4, true)).to be < declaration(5, true)
44
+ expect(declaration(6, true)).to be > declaration(5, true)
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "parsing" do
50
+ def parsing(declaration, specificity)
51
+ property = StyleProperty.parse(declaration, specificity)
52
+ [property.property, property.value, property.important?, property.specificity]
53
+ end
54
+
55
+ it "understands simple declarations" do
56
+ expect(parsing("color: green", 1)).to eq(["color", "green", false, 1])
57
+ expect(parsing(" color:green; ", 1)).to eq(["color", "green", false, 1])
58
+ expect(parsing("color: green ", 1)).to eq(["color", "green", false, 1])
59
+ expect(parsing("color: green ; ", 1)).to eq(["color", "green", false, 1])
60
+ end
61
+
62
+ it "understands more complex values" do
63
+ expect(parsing("padding:0 1px 5rem 9%;", 89)).to eq(["padding", "0 1px 5rem 9%", false, 89])
64
+ end
65
+
66
+ it "understands more complex names" do
67
+ expect(parsing("font-size: 50%", 10)).to eq(["font-size", "50%", false, 10])
68
+ end
69
+
70
+ it "correctly reads !important declarations" do
71
+ expect(parsing("color: green !important", 1)).to eq(["color", "green", true, 1])
72
+ expect(parsing("color: green !important;", 1)).to eq(["color", "green", true, 1])
73
+ end
74
+
75
+ it "raises an error on unparseable declarations" do
76
+ expect {
77
+ parsing("I want a red apple!", 1)
78
+ }.to raise_error(Roadie::UnparseableDeclaration, /red apple/)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ module Roadie
5
+ describe Stylesheet do
6
+ it "is initialized with a name and CSS" do
7
+ stylesheet = Stylesheet.new("foo.css", "body { color: green; }")
8
+ expect(stylesheet.name).to eq("foo.css")
9
+ end
10
+
11
+ it "has a list of blocks" do
12
+ stylesheet = Stylesheet.new("foo.css", <<-CSS)
13
+ body { color: green !important; font-size: 200%; }
14
+ a, i { color: red; }
15
+ CSS
16
+ expect(stylesheet).to have(3).blocks
17
+ expect(stylesheet.blocks.map(&:to_s)).to eq([
18
+ "body{color:green !important;font-size:200%}",
19
+ "a{color:red}",
20
+ "i{color:red}",
21
+ ])
22
+ end
23
+
24
+ it "can iterate all inlinable blocks" do
25
+ inlinable = double(inlinable?: true, selector: "good", properties: "props")
26
+ bad = double(inlinable?: false, selector: "bad", properties: "props")
27
+
28
+ stylesheet = Stylesheet.new("example.css", "")
29
+ allow(stylesheet).to receive_messages blocks: [bad, inlinable, bad]
30
+
31
+ expect(stylesheet.each_inlinable_block.to_a).to eq([
32
+ ["good", "props"],
33
+ ])
34
+ end
35
+
36
+ it "has a string representation of the contents" do
37
+ stylesheet = Stylesheet.new("example.css", "body { color: green;}a{ color: red; font-size: small }")
38
+ expect(stylesheet.to_s).to eq("body{color:green}\na{color:red;font-size:small}")
39
+ end
40
+ end
41
+ end