css_inliner 0.1.1 → 0.2.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.
File without changes
data/bin/css-inliner CHANGED
@@ -6,6 +6,7 @@ opt = OptionParser.new
6
6
  Version = CSSInliner::VERSION
7
7
  options = {}
8
8
 
9
+ opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] htmlfile"
9
10
  opt.on('-o', '--output=OUTPUTFILE', 'file name to output result HTML') {|v| options[:output] = v}
10
11
  opt.on('-i [SUFFIX]', '--in-place', '--overwrite', 'inplace editing') do |v|
11
12
  options[:'in-place'] = true
@@ -23,5 +24,3 @@ elsif options[:output]
23
24
  else
24
25
  $stdout.write processed
25
26
  end
26
-
27
- exit 0
data/css_inliner.gemspec CHANGED
@@ -13,18 +13,18 @@ Gem::Specification.new do |s|
13
13
  into style attibute of HTML elements
14
14
  }
15
15
 
16
- # s.rubyforge_project = "css_inliner"
17
-
18
16
  s.files = `git ls-files`.split("\n")
19
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
19
  s.require_paths = ["lib"]
22
20
 
23
21
  s.add_runtime_dependency "nokogiri", '~> 1'
24
- s.add_runtime_dependency "css_parser", '~> 1'
25
- s.add_runtime_dependency "sass", '~> 3'
22
+ s.add_runtime_dependency "csspool", '~> 3'
23
+ s.add_runtime_dependency "bsearch"
26
24
 
27
25
  s.add_development_dependency "test-unit", '~> 2'
28
26
  s.add_development_dependency "cover_me", '~> 1'
29
27
  s.add_development_dependency "yard"
28
+ s.add_development_dependency "pry"
29
+ s.add_development_dependency "pry-doc"
30
30
  end
@@ -0,0 +1,58 @@
1
+ require 'csspool'
2
+
3
+ module CSSPool
4
+ module CSS
5
+ class << self
6
+ # Update declarations in base with ones in other
7
+ # @param [Array<Declaration>] base updated array of declarations
8
+ # @param [Array<Declaration>] other array of declarations
9
+ # @return [Array<Declaration>] base itself
10
+ def update_declarations(base, other)
11
+ other.each do |other_decl|
12
+ base_decls = base.find_all {|base_decl| base_decl.property == other_decl.property}
13
+ if base_decls.empty?
14
+ base << other_decl
15
+ else
16
+ base_decl = base_decls.pop
17
+ base_decls.each do |decl|
18
+ base_decl.update decl
19
+ end
20
+ base_decl.update other_decl
21
+ end
22
+ end
23
+ base
24
+ end
25
+
26
+ # Merge declarations in each argument
27
+ # @param [Array<Declaration>] base Array of declarations
28
+ # @param [Array<Declaration>] other Array of declarations
29
+ # @return [Aarray<Declaration>] merged array of declarations
30
+ def merge_declarations(base, other)
31
+ update_declarations base.dup, other
32
+ end
33
+ end
34
+
35
+ class Declaration
36
+ # @param [Declaration] other
37
+ # @return [Declaration] self
38
+ def update(other)
39
+ raise ArgumentError('different property') unless property == other.property
40
+ self.expressions = other.expressions if !important? or other.important?
41
+ self
42
+ end
43
+ alias merge! update
44
+
45
+ # @param [Declaration] other
46
+ # @return [Declaration] merged declaration
47
+ def merge(other)
48
+ dup.update other
49
+ end
50
+ end
51
+ end
52
+
53
+ module Visitors
54
+ class ToCSS
55
+ alias visit_CSSInliner_CSSDocument visit_CSSPool_CSS_Document
56
+ end
57
+ end
58
+ end
@@ -1,7 +1,35 @@
1
+ require 'English'
1
2
  require 'open-uri'
2
- require 'css_parser'
3
+ require 'bsearch'
4
+ require 'css_inliner/csspool'
3
5
 
4
6
  module CSSInliner
7
+ class CSSDocument < CSSPool::CSS::Document
8
+ # @return [Array<CSSPool::Selector>] Array of selectors in specificity order
9
+ attr_reader :sorted_selectors
10
+
11
+ def initialize
12
+ super
13
+ @sorted_selectors = []
14
+ end
15
+ end
16
+
17
+ class CSSDocumentHandler < CSSPool::CSS::DocumentHandler
18
+ def start_document
19
+ @document = CSSDocument.new
20
+ end
21
+
22
+ def start_selector selector_list
23
+ super
24
+ selector_list.each do |selector|
25
+ index = @document.sorted_selectors.bsearch_upper_boundary { |existing|
26
+ existing.specificity <=> selector.specificity
27
+ }
28
+ @document.sorted_selectors.insert index, selector
29
+ end
30
+ end
31
+ end
32
+
5
33
  class Extractor
6
34
  attr_reader :document
7
35
 
@@ -17,9 +45,11 @@ module CSSInliner
17
45
  end
18
46
 
19
47
  def extract_from_link(remove_link_element = true)
20
- @document.css('link[rel="stylesheet"]').inject([]) do |sources, link|
48
+ @document.css('link').inject([]) do |sources, link|
49
+ next unless link['rel'] == 'stylesheet'
21
50
  begin
22
- open(File.join(basedir, link['href'])) {|f| sources << f.read}
51
+ # To do: detect file encoding before open it(read only @charset value)
52
+ open(File.join(basedir, link['href']), 'r:BOM|UTF-8') {|f| sources << f.read}
23
53
  rescue Errno::ENOENT
24
54
  warn File.join(basedir, link['href']) + ' not found'
25
55
  end
@@ -37,17 +67,10 @@ module CSSInliner
37
67
  end
38
68
 
39
69
  def integrate(*sources)
40
- parser = CssParser::Parser.new
41
- parser.add_block!(sources * $/)
42
- rule_sets = parser.enum_for :each_rule_set
43
- i = 0
44
- rule_sets = rule_sets.sort_by {|rs| [rs.specificity, i += 1]}
45
- blank_rule_set = CssParser::RuleSet.new(nil, nil)
46
- rule_sets.inject(Hash.new(blank_rule_set)) do |rules, rs|
47
- sel = rs.selectors * ','
48
- rules[sel] = CssParser.merge rules[sel], rs
49
- rules
50
- end
70
+ source = sources.collect {|src| src * $RS}.join($RS)
71
+ source = 'book {}' if source.empty?
72
+ handler = CSSDocumentHandler.new
73
+ CSSPool::SAC::Parser.new(handler).parse(source)
51
74
  end
52
75
 
53
76
  def basedir
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'nokogiri'
3
3
  require 'css_inliner/extractor'
4
+ require 'css_inliner/csspool'
4
5
 
5
6
  module CSSInliner
6
7
  class Inliner
@@ -13,29 +14,27 @@ module CSSInliner
13
14
  end
14
15
 
15
16
  def inline
16
- original_style = {}
17
- @document.css('*[style]').each do |elem|
18
- original_style[elem] = elem['style']
17
+ original_styles = {}
18
+ @document.search('.//*[@style]').each do |elem|
19
+ original_styles[elem] = elem['style']
20
+ elem.remove_attribute('style')
19
21
  end
20
22
 
21
- styles = {}
22
- @extractor.extract.each_pair do |sel, rs|
23
+ css = @extractor.extract
24
+ css.sorted_selectors.each do |selector|
25
+ sel = selector.to_s
23
26
  next if sel =~ /@|:/
24
- body = @document.css('body')
25
- body.css(sel).each_with_index do |elem, i|
26
- styles[elem] = CssParser::RuleSet.new(nil, nil) unless styles[elem]
27
- styles[elem] = CssParser.merge styles[elem], rs
27
+ sel = 'body' if sel == '*' or sel == 'html'
28
+ @document.css(sel).each do |elem|
29
+ base = CSSPool.CSS("* {#{elem['style']}}").rule_sets.first.declarations
30
+ elem['style'] = CSSPool::CSS.update_declarations(base, selector.declarations).join
28
31
  end
29
32
  end
30
33
 
31
- original_style.each_pair do |elem, style|
32
- styles[elem] = CssParser::RuleSet.new(nil, nil) unless styles[elem]
33
- rs = CssParser::RuleSet.new(nil, style)
34
- styles[elem] = CssParser.merge styles[elem], rs
35
- end
36
-
37
- styles.each_pair do |elem, rule_set|
38
- elem['style'] = rule_set.declarations_to_s
34
+ original_styles.each_pair do |elem, style|
35
+ base = CSSPool.CSS("* {#{elem['style']}}").rule_sets.first.declarations
36
+ orig = CSSPool.CSS("* {#{style}}").rule_sets.first.declarations
37
+ elem['style'] = CSSPool::CSS.update_declarations(base, orig).join
39
38
  end
40
39
 
41
40
  @document
@@ -1,3 +1,3 @@
1
1
  module CSSInliner
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/css_inliner.rb CHANGED
@@ -2,6 +2,7 @@ require 'open-uri'
2
2
  require 'rubygems'
3
3
  require 'nokogiri'
4
4
  require 'css_inliner/version'
5
+ require 'css_inliner/csspool'
5
6
  require 'css_inliner/inliner'
6
7
 
7
8
  module CSSInliner
@@ -12,8 +13,8 @@ module CSSInliner
12
13
  # Returns whole document when nil
13
14
  # @return [String] HTML source
14
15
  def process(html, basedir = '.', element = nil)
15
- doc = html.instance_of? Nokogiri::XML::Document ? html : Nokogiri.HTML(html)
16
- doc = Inliner.new(html, basedir).inline
16
+ doc = html.instance_of?(Nokogiri::XML::Document) ? html : Nokogiri.XML(html)
17
+ doc = Inliner.new(doc, basedir).inline
17
18
  doc = doc.css(element)[0] if element
18
19
  doc.to_s
19
20
  end
@@ -615,7 +615,7 @@ div.method-source-code pre {
615
615
 
616
616
  * html #TB_overlay { /* ie6 hack */
617
617
  position: absolute;
618
- height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
618
+ /* height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); */
619
619
  }
620
620
 
621
621
  #TB_window {
@@ -632,7 +632,7 @@ div.method-source-code pre {
632
632
 
633
633
  * html #TB_window { /* ie6 hack */
634
634
  position: absolute;
635
- margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
635
+ /* margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); */
636
636
  }
637
637
 
638
638
  #TB_window img#TB_Image {
@@ -710,7 +710,7 @@ div.method-source-code pre {
710
710
 
711
711
  * html #TB_load { /* ie6 hack */
712
712
  position: absolute;
713
- margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
713
+ /* margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); */
714
714
  }
715
715
 
716
716
  #TB_HideSelect{
@@ -729,7 +729,7 @@ div.method-source-code pre {
729
729
 
730
730
  * html #TB_HideSelect { /* ie6 hack */
731
731
  position: absolute;
732
- height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
732
+ /* height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); */
733
733
  }
734
734
 
735
735
  #TB_iframeContent{
@@ -31,8 +31,6 @@ class CSSInlinerTest < CSSInlinerTestCase
31
31
  end
32
32
 
33
33
  def process(basedir)
34
- pend
35
-
36
34
  source = File.read(File.join(basedir, 'index.html'))
37
35
  inlined = File.read(File.join(basedir, 'index.inlined.html'))
38
36
  [inlined, CSSInliner.process(source, basedir)]
@@ -33,10 +33,8 @@ class TestExtractor < CSSInlinerTestCase
33
33
  color: gray ;
34
34
  }
35
35
  '
36
- expected = CssParser::Parser.new
37
- expected.add_block! src
38
- actual = CssParser::Parser.new
39
- actual.add_block! @extractor3.extract_from_style[0]
36
+ expected = CSSPool.CSS src
37
+ actual = CSSPool.CSS @extractor3.extract_from_style[0]
40
38
 
41
39
  assert_equal expected.to_s, actual.to_s
42
40
  end
@@ -69,11 +67,17 @@ a {
69
67
  }
70
68
  '
71
69
  ]
72
- expected = {
73
- 'h2' => CssParser::RuleSet.new(nil, 'color: gray;'),
74
- 'h2 p' => CssParser::RuleSet.new(nil, 'color:blue'),
75
- 'a' => CssParser::RuleSet.new(nil, 'text-decoration: none;')
76
- }
77
- assert_equal expected.to_s, @extractor1.integrate(sources).to_s
70
+ expected = <<EOC
71
+ h2 {
72
+ color: gray;
73
+ }
74
+ h2 p {
75
+ color: blue;
76
+ }
77
+ a {
78
+ text-decoration: none;
79
+ }
80
+ EOC
81
+ assert_equal expected.chomp, @extractor1.integrate(sources).to_css.chomp
78
82
  end
79
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: css_inliner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-05 00:00:00.000000000Z
12
+ date: 2012-04-29 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &18228900 !ruby/object:Gem::Requirement
16
+ requirement: &12417000 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,32 +21,32 @@ dependencies:
21
21
  version: '1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *18228900
24
+ version_requirements: *12417000
25
25
  - !ruby/object:Gem::Dependency
26
- name: css_parser
27
- requirement: &17749120 !ruby/object:Gem::Requirement
26
+ name: csspool
27
+ requirement: &12415820 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
31
31
  - !ruby/object:Gem::Version
32
- version: '1'
32
+ version: '3'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *17749120
35
+ version_requirements: *12415820
36
36
  - !ruby/object:Gem::Dependency
37
- name: sass
38
- requirement: &17735720 !ruby/object:Gem::Requirement
37
+ name: bsearch
38
+ requirement: &12415220 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - ~>
41
+ - - ! '>='
42
42
  - !ruby/object:Gem::Version
43
- version: '3'
43
+ version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *17735720
46
+ version_requirements: *12415220
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: test-unit
49
- requirement: &17738800 !ruby/object:Gem::Requirement
49
+ requirement: &12414040 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '2'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *17738800
57
+ version_requirements: *12414040
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: cover_me
60
- requirement: &17741120 !ruby/object:Gem::Requirement
60
+ requirement: &12430900 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,32 @@ dependencies:
65
65
  version: '1'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *17741120
68
+ version_requirements: *12430900
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: yard
71
- requirement: &17743320 !ruby/object:Gem::Requirement
71
+ requirement: &12430380 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *12430380
80
+ - !ruby/object:Gem::Dependency
81
+ name: pry
82
+ requirement: &12429780 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *12429780
91
+ - !ruby/object:Gem::Dependency
92
+ name: pry-doc
93
+ requirement: &12429300 !ruby/object:Gem::Requirement
72
94
  none: false
73
95
  requirements:
74
96
  - - ! '>='
@@ -76,7 +98,7 @@ dependencies:
76
98
  version: '0'
77
99
  type: :development
78
100
  prerelease: false
79
- version_requirements: *17743320
101
+ version_requirements: *12429300
80
102
  description: ! "\n inline CSS from external file(s) and/or style elment(s) in head
81
103
  element\n into style attibute of HTML elements\n "
82
104
  email:
@@ -91,12 +113,13 @@ files:
91
113
  - .gitignore
92
114
  - Gemfile
93
115
  - LICENSE
94
- - README
116
+ - README.rdoc
95
117
  - Rakefile
96
118
  - bin/css-inliner
97
119
  - bin/css-inliner-multifile
98
120
  - css_inliner.gemspec
99
121
  - lib/css_inliner.rb
122
+ - lib/css_inliner/csspool.rb
100
123
  - lib/css_inliner/extractor.rb
101
124
  - lib/css_inliner/inliner.rb
102
125
  - lib/css_inliner/version.rb
@@ -244,7 +267,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
244
267
  version: '0'
245
268
  segments:
246
269
  - 0
247
- hash: 1616189382576311129
270
+ hash: -1019368120188778628
248
271
  required_rubygems_version: !ruby/object:Gem::Requirement
249
272
  none: false
250
273
  requirements:
@@ -253,7 +276,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
276
  version: '0'
254
277
  segments:
255
278
  - 0
256
- hash: 1616189382576311129
279
+ hash: -1019368120188778628
257
280
  requirements: []
258
281
  rubyforge_project:
259
282
  rubygems_version: 1.8.8