html-conditional-comment 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bf4b71499353f362569739053e61a2557c2a4cea
4
+ data.tar.gz: dba497e3d758323e16684aff985ad62e807ba564
5
+ SHA512:
6
+ metadata.gz: cbe607ef7e65c9b3fc5207b3f0e6d9275b638a897093e2d608bd9189614a98a9d789fee620d83a495df56437d087241054031cc57b75c7fec40f9b0be9f32d16
7
+ data.tar.gz: 5ab6afde82cf240722973804f7f43fff9889fe61b5929ff3d4d11a07e440d831dd20831505a7c71d98d5e7816a3f5e96c075c9cae5e7c5294d9b1e9bdbbbb514
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.byebug_history
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.13.7
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in html-conditional-comment.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Carson Reinke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # HTML Conditional Comment Evaluater, Parser, and Lexer
2
+
3
+ Evaluate, parse, and tokenizing HTML conditional comments using provided features and version. Allows for existing HTML to be maintained and only conditional comments to evaluated.
4
+
5
+ ## Why?
6
+
7
+ Conditional comments are really a legacy approach, however the application is still quite prevelant in the email marketing industry.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'html-conditional-comment'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install html-conditional-comment
24
+
25
+ ## Usage
26
+
27
+ * Evaluating:
28
+ `HtmlConditionalComment.to_string("What is the <!--[if gte mso 9]>Outlook<![endif]-->?", "mso", 9) => "What is the Outlook?"`
29
+
30
+ * Parsing: `HtmlConditionalComment.parse(...) => HtmlConditionalComment::Nodes::Nodes`
31
+
32
+ * Tokenizing: `HtmlConditionalComment.lex(...) => [[:open, "<!--["], [:if, "if"]...`
33
+
34
+ ## Contributing
35
+
36
+ Bug reports and pull requests are welcome on GitHub at https://github.com/carsonreinke/html-conditional-comment.
37
+
38
+
39
+ ## License
40
+
41
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "html-conditional-comment"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'html-conditional-comment/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "html-conditional-comment"
8
+ spec.version = HtmlConditionalComment::VERSION
9
+ spec.authors = ["Carson Reinke"]
10
+ spec.email = ["carson@reinke.co"]
11
+
12
+ spec.summary = %q{Parse HTML conditional comments}
13
+ spec.homepage = "https://github.com/carsonreinke/html-conditional-comment"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+ spec.add_development_dependency "byebug"
27
+ end
@@ -0,0 +1,35 @@
1
+ require 'html-conditional-comment/version'
2
+ require 'html-conditional-comment/lexer'
3
+ require 'html-conditional-comment/parser'
4
+ require 'html-conditional-comment/nodes'
5
+ require 'html-conditional-comment/visitor'
6
+ require 'html-conditional-comment/version_vector'
7
+
8
+ module HtmlConditionalComment
9
+ class << self
10
+ ##
11
+ # Tokenize the HTML into an array of tokens
12
+ #
13
+ def lex(html)
14
+ Lexer.new(html).tokenize()
15
+ end
16
+
17
+ ##
18
+ # Parse into tree of nodes the HTML
19
+ #
20
+ def parse(html)
21
+ Parser.new(self.lex(html)).parse()
22
+ end
23
+
24
+ ##
25
+ # Evaluate conditional comments in HTML using the supplied browser
26
+ # information and return a string
27
+ #
28
+ # * +features+ - String or Array of features of browser
29
+ # * +version+ - String, Integer, or Float representing version of the browser
30
+ #
31
+ def to_string(html, features, version)
32
+ self.parse(html).accept(Visitors::ToString.new(features, version))
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,108 @@
1
+ module HtmlConditionalComment
2
+ class TokenError < StandardError
3
+ def initialize(rest)
4
+ super("Invalid token \"#{rest.slice(0, 25)}\"")
5
+ end
6
+ end
7
+
8
+ ##
9
+ # Converts string into array of tokens. Token is an array, first element is
10
+ # symbol representing the token, second element is string value.
11
+ #
12
+ class Lexer
13
+ LESS_THAN = /lt/i
14
+ LESS_THAN_EQUAL = /lte/i
15
+ GREATER_THAN = /gt/i
16
+ GREATER_THAN_EQUAL = /gte/i
17
+ OPEN_PAREN = /\(/
18
+ CLOSE_PAREN = /\)/
19
+ NOT = /\!/
20
+ OR = /\|/
21
+ AND = /\&/
22
+ TRUE = /true/i
23
+ FALSE = /false/i
24
+ IF_STATEMENT = /if/i
25
+ ENDIF_STATEMENT = /endif/i
26
+ #Opening statement plus positive look ahead to avoid conflicts with other
27
+ #comments
28
+ OPEN = /<!(\-\-)?\[(?=(end)?if)/
29
+ CLOSE = /\](\-\-)?>/
30
+ WHITE_SPACE = /\s+/
31
+ FEATURE = /[a-z]+/i
32
+ VERSION_VECTOR = /\d+(\.[\d]+)?/
33
+
34
+ TOKENS = [
35
+ [:if, IF_STATEMENT],
36
+ [:endif, ENDIF_STATEMENT],
37
+
38
+ [:paren_open, OPEN_PAREN],
39
+ [:paren_close, CLOSE_PAREN],
40
+
41
+ [:operator_less_than_equal, LESS_THAN_EQUAL],
42
+ [:operator_less_than, LESS_THAN],
43
+ [:operator_greater_than_equal, GREATER_THAN_EQUAL],
44
+ [:operator_greater_than, GREATER_THAN],
45
+
46
+ [:operator_not, NOT],
47
+ [:operator_or, OR],
48
+ [:operator_and, AND],
49
+
50
+ [:boolean_true, TRUE],
51
+ [:boolean_false, FALSE],
52
+ [:feature, FEATURE],
53
+ [:version_vector, VERSION_VECTOR]
54
+ ]
55
+
56
+ def initialize(html_or_comment)
57
+ @scanner = StringScanner.new(html_or_comment)
58
+ end
59
+
60
+ def tokenize()
61
+ tokens = []
62
+ open = false
63
+
64
+ #Run until nothing left in string
65
+ until @scanner.eos?()
66
+ #Split between if the conditional comment has been opened or not
67
+ #State will help handle all the other HTML we don't care about
68
+ if open
69
+ @scanner.skip(WHITE_SPACE)
70
+ if token = @scanner.scan(CLOSE)
71
+ open = false
72
+ tokens << [:close, token]
73
+ else
74
+ #Go through token specs and scan and stop on first one
75
+ token = TOKENS.inject(nil) do |previous, spec|
76
+ t = @scanner.scan(spec[1])
77
+ unless t.nil?()
78
+ break [spec[0], t]
79
+ end
80
+ end
81
+ if token
82
+ tokens << token
83
+ else
84
+ raise TokenError.new(@scanner.rest())
85
+ end
86
+ end
87
+ #Closed (not opened) conditional comment
88
+ else
89
+ #Scan till we find an open token, if not done and use the rest
90
+ if match = @scanner.scan_until(OPEN)
91
+ open = true
92
+ #TODO Gross way to get up till scan has succeeded
93
+ match = match.slice(0..-(@scanner.matched.size() + 1))
94
+ tokens << [:html, match] unless match.empty?()
95
+ tokens << [:open, @scanner.matched]
96
+ else
97
+ tokens << [:html, @scanner.rest()] if @scanner.rest?()
98
+ break
99
+ end
100
+ end
101
+ end
102
+ @scanner.reset()
103
+
104
+ tokens
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,54 @@
1
+ require 'singleton'
2
+ require 'delegate'
3
+
4
+ module HtmlConditionalComment
5
+ module Nodes
6
+ module Node
7
+ def accept(visitor)
8
+ visitor.visit(self)
9
+ end
10
+ end
11
+
12
+ class NodeItem
13
+ include Node
14
+ end
15
+
16
+ class Nodes < Array
17
+ include Node
18
+ end
19
+
20
+ class ChildOperator < NodeItem
21
+ attr_accessor :child
22
+ end
23
+ class BranchOperator < NodeItem
24
+ attr_accessor :left, :right
25
+ end
26
+ class Comparison < ChildOperator; end
27
+ class Condition < BranchOperator; end
28
+
29
+
30
+ class Or < BranchOperator; end
31
+ class And < BranchOperator; end
32
+ class Not < ChildOperator; end
33
+
34
+ class Equal < Comparison; end
35
+ class LessThan < Comparison; end
36
+ class LessThanEqual < Comparison; end
37
+ class GreaterThan < Comparison; end
38
+ class GreaterThanEqual < Comparison; end
39
+
40
+ class Browser < NodeItem
41
+ attr_accessor :feature, :version_vector
42
+ end
43
+ class True < NodeItem
44
+ include Singleton
45
+ end
46
+ class False < NodeItem
47
+ include Singleton
48
+ end
49
+
50
+ class Html < NodeItem
51
+ attr_accessor :content
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,228 @@
1
+ module HtmlConditionalComment
2
+ class ParseError < StandardError
3
+ def initialize(msg, example)
4
+ super("#{msg} at \"#{example.slice(0, 25)}\"")
5
+ end
6
+ end
7
+
8
+ ##
9
+ #
10
+ # Parse tokens into a tree of nodes
11
+ #
12
+ # Pseudo grammar
13
+ #
14
+ #template = { html | statement }
15
+ #statement = "<!" , [ "--" ] , "if" , expression , "]" , [ "--" ] , ">" , template , "<!" , [ "--" ] , "endif" , "]" , [ "--" ] , ">"
16
+ #expression = term [ "|" , term ]
17
+ #term = factor [ "&" , factor ]
18
+ #factor = subexpression | "!" , factor | "(" , expression , ")"
19
+ #subexpression = [ operator ] browser | boolean
20
+ #operator = "gt" | "gte" | "lt" | "lte"
21
+ #boolean = "true" | "false"
22
+ #browser = feature [ version_vector ]
23
+ #
24
+ class Parser
25
+ def initialize(tokens)
26
+ @symbol = nil
27
+ @tokens = tokens
28
+ @max_pos = tokens.size() - 1
29
+ @pos = -1
30
+ end
31
+
32
+ def parse()
33
+ self.next()
34
+
35
+ nodes = template()
36
+
37
+ #Tokens left, syntax error
38
+ error() if @pos < @max_pos
39
+
40
+ nodes
41
+ end
42
+
43
+
44
+ protected
45
+ #Browser is combination of feature and optional version
46
+ def browser()
47
+ node = Nodes::Browser.new()
48
+ node.feature = @value
49
+ expect(:feature)
50
+
51
+ if current(:version_vector)
52
+ node.version_vector = VersionVector.new(@value)
53
+ accept(:version_vector)
54
+ else
55
+ node.version_vector = VersionVector.new(nil)
56
+ end
57
+
58
+ node
59
+ end
60
+
61
+ #True or false
62
+ def boolean()
63
+ if accept(:boolean_true)
64
+ Nodes::True.instance()
65
+ elsif accept(:boolean_false)
66
+ Nodes::False.instance()
67
+ else
68
+ error()
69
+ end
70
+ end
71
+
72
+ #Comparison operators
73
+ def operator()
74
+ if accept(:operator_less_than)
75
+ Nodes::LessThan.new()
76
+ elsif accept(:operator_less_than_equal)
77
+ Nodes::LessThanEqual.new()
78
+ elsif accept(:operator_greater_than)
79
+ Nodes::GreaterThan.new()
80
+ elsif accept(:operator_greater_than_equal)
81
+ Nodes::GreaterThanEqual.new()
82
+ else
83
+ error()
84
+ end
85
+ end
86
+
87
+ #Either a comparison with the browser, boolean, or simply just the browser
88
+ def subexpression()
89
+ node = nil
90
+
91
+ if current(:operator_less_than) || current(:operator_less_than_equal) ||
92
+ current(:operator_greater_than) || current(:operator_greater_than_equal)
93
+
94
+ node = operator()
95
+ node.child = browser()
96
+ elsif current(:boolean_true) || current(:boolean_false)
97
+ node = boolean()
98
+ else
99
+ #No comparison operator is assuming equals
100
+ node = Nodes::Equal.new()
101
+ node.child = browser()
102
+ end
103
+
104
+ node
105
+ end
106
+
107
+ #Negated self or paranthesised expression
108
+ def factor()
109
+ node = nil
110
+
111
+ if accept(:operator_not)
112
+ node = Nodes::Not.new()
113
+ node.child = factor()
114
+ elsif accept(:paren_open)
115
+ node = expression()
116
+ expect(:paren_close)
117
+ else
118
+ node = subexpression()
119
+ end
120
+
121
+ node
122
+ end
123
+
124
+ #And
125
+ def term()
126
+ node = factor()
127
+ while accept(:operator_and)
128
+ branch_node = Nodes::And.new()
129
+ branch_node.left = node
130
+ branch_node.right = factor()
131
+ node = branch_node
132
+ end
133
+
134
+ node
135
+ end
136
+
137
+ #Or
138
+ def expression()
139
+ node = term()
140
+ while accept(:operator_or)
141
+ branch_node = Nodes::Or.new()
142
+ branch_node.left = node
143
+ branch_node.right = term()
144
+ node = branch_node
145
+ end
146
+
147
+ node
148
+ end
149
+
150
+ def condition()
151
+ node = Nodes::Condition.new()
152
+
153
+ expect(:open)
154
+ expect(:if)
155
+ node.left = expression()
156
+ expect(:close)
157
+
158
+ unless current(:open) && peek(:endif)
159
+ node.right = template()
160
+ end
161
+
162
+ expect(:open)
163
+ expect(:endif)
164
+ expect(:close)
165
+
166
+ node
167
+ end
168
+
169
+ def html()
170
+ node = Nodes::Html.new()
171
+ node.content = @value
172
+ expect(:html)
173
+ node
174
+ end
175
+
176
+ def template()
177
+ nodes = Nodes::Nodes.new()
178
+
179
+ while current(:html) || (current(:open) && peek(:if))
180
+ nodes << if current(:html)
181
+ html()
182
+ elsif current(:open) && peek(:if)
183
+ condition()
184
+ end
185
+ end
186
+
187
+ nodes
188
+ end
189
+
190
+
191
+ protected
192
+ #Accept the symbol and move on or not
193
+ def accept(symbol)
194
+ if(symbol == @symbol)
195
+ self.next()
196
+ return true
197
+ else
198
+ return false
199
+ end
200
+ end
201
+
202
+ #Expect a current symbol or raise
203
+ def expect(symbol)
204
+ raise HtmlConditionalComment::ParseError.new("Expected #{symbol}, received #{@symbol}", @value) unless accept(symbol)
205
+ end
206
+
207
+ def current(symbol)
208
+ @symbol == symbol
209
+ end
210
+
211
+ def peek(symbol)
212
+ @tokens[@pos+1][0] == symbol
213
+ end
214
+
215
+ def next()
216
+ @pos += 1
217
+ #raise HtmlConditionalComment::ParserError.new('EOF') if @pos >= @max_pos
218
+ token = @tokens[@pos] || []
219
+ @symbol = token[0]
220
+ @value = token[1]
221
+ token
222
+ end
223
+
224
+ def error()
225
+ raise HtmlConditionalComment::ParseError.new("Syntax error", @value)
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,3 @@
1
+ module HtmlConditionalComment
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,42 @@
1
+ module HtmlConditionalComment
2
+ class VersionVector
3
+ include Comparable
4
+
5
+ DOT = /\./
6
+ DIGIT = /\d/
7
+
8
+ attr_accessor :string
9
+
10
+ def initialize(string)
11
+ @string = string.to_s() unless string.nil?()
12
+ end
13
+
14
+ def <=>(other)
15
+ #Force comparison class
16
+ other = VersionVector.new(other) unless other.is_a?(VersionVector)
17
+
18
+ return 0 if @string.nil?() || other.string.nil?()
19
+ return 0 if @string == other.string
20
+
21
+ #Normalize version array sizes
22
+ left, right = self.to_a(), other.to_a()
23
+ size = [left.size(), right.size()].min()
24
+ left.slice!(size..-1)
25
+ right.slice!(size..-1)
26
+
27
+ #Compare based on number
28
+ left.join.to_f() <=> right.join.to_f()
29
+ end
30
+
31
+ ##
32
+ # Split string into array of version numbers, major can be multiple digits,
33
+ # minor can only be a single digit
34
+ #
35
+ def to_a()
36
+ major, minors = @string.split(DOT)
37
+ versions = (minors || '').scan(DIGIT)
38
+ versions.unshift(major, '.')
39
+ versions
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,99 @@
1
+ module HtmlConditionalComment
2
+ class VisitError < StandardError
3
+ def initialize(klass)
4
+ super("Cannot visit #{klass}")
5
+ end
6
+ end
7
+
8
+ module Visitors
9
+ class Visitor
10
+ def initialize(features, version)
11
+ @features = features
12
+ @features = [@features] unless @features.is_a?(Enumerable)
13
+
14
+ @version = if version.is_a?(VersionVector)
15
+ version
16
+ else
17
+ VersionVector.new(version)
18
+ end
19
+ end
20
+
21
+ #Copied from https://blog.bigbinary.com/2013/07/07/visitor-pattern-and-double-dispatch.html
22
+ def visit(subject)
23
+ method_name = :"visit_#{(subject.class.name || '').gsub('::', '_')}"
24
+ __send__(method_name, subject)
25
+ end
26
+
27
+ #Provide method missing for better interpretation
28
+ def method_missing(method, args)
29
+ if method.to_s() =~ /^visit\_(.+)/
30
+ raise VisitError.new($1)
31
+ else
32
+ super(method, args)
33
+ end
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Evaluates conditions to boolean
39
+ #
40
+ class Eval < Visitor
41
+ protected
42
+ def visit_HtmlConditionalComment_Nodes_True(subject)
43
+ true
44
+ end
45
+ def visit_HtmlConditionalComment_Nodes_False(subject)
46
+ false
47
+ end
48
+ def visit_HtmlConditionalComment_Nodes_Browser(subject)
49
+ @features.include?(subject.feature)
50
+ end
51
+
52
+ def visit_HtmlConditionalComment_Nodes_Equal(subject)
53
+ subject.child.accept(self) && @version == subject.child.version_vector
54
+ end
55
+ def visit_HtmlConditionalComment_Nodes_LessThan(subject)
56
+ subject.child.accept(self) && @version < subject.child.version_vector
57
+ end
58
+ def visit_HtmlConditionalComment_Nodes_LessThanEqual(subject)
59
+ subject.child.accept(self) && @version <= subject.child.version_vector
60
+ end
61
+ def visit_HtmlConditionalComment_Nodes_GreaterThan(subject)
62
+ subject.child.accept(self) && @version > subject.child.version_vector
63
+ end
64
+ def visit_HtmlConditionalComment_Nodes_GreaterThanEqual(subject)
65
+ subject.child.accept(self) && @version >= subject.child.version_vector
66
+ end
67
+
68
+ def visit_HtmlConditionalComment_Nodes_Or(subject)
69
+ subject.left.accept(self) || subject.right.accept(self)
70
+ end
71
+ def visit_HtmlConditionalComment_Nodes_And(subject)
72
+ subject.left.accept(self) && subject.right.accept(self)
73
+ end
74
+ def visit_HtmlConditionalComment_Nodes_Not(subject)
75
+ !subject.child.accept(self)
76
+ end
77
+ end
78
+
79
+ ##
80
+ # Converts parser nodes to a string by evaluating each conditional comment
81
+ #
82
+ class ToString < Visitor
83
+ protected
84
+ def visit_HtmlConditionalComment_Nodes_Nodes(subject)
85
+ subject.map{|node| node.accept(self)}.join
86
+ end
87
+
88
+ def visit_HtmlConditionalComment_Nodes_Condition(subject)
89
+ if subject.left.accept(Eval.new(@features, @version))
90
+ subject.right.accept(self)
91
+ end
92
+ end
93
+
94
+ def visit_HtmlConditionalComment_Nodes_Html(subject)
95
+ subject.content
96
+ end
97
+ end
98
+ end
99
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: html-conditional-comment
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Carson Reinke
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - carson@reinke.co
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - html-conditional-comment.gemspec
85
+ - lib/html-conditional-comment.rb
86
+ - lib/html-conditional-comment/lexer.rb
87
+ - lib/html-conditional-comment/nodes.rb
88
+ - lib/html-conditional-comment/parser.rb
89
+ - lib/html-conditional-comment/version.rb
90
+ - lib/html-conditional-comment/version_vector.rb
91
+ - lib/html-conditional-comment/visitor.rb
92
+ homepage: https://github.com/carsonreinke/html-conditional-comment
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.6.8
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Parse HTML conditional comments
116
+ test_files: []