css_inliner 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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