html-conditional-comment 0.0.1

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