habaki 0.5.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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/ext/katana/extconf.rb +20 -0
  4. data/ext/katana/rb_katana.c +280 -0
  5. data/ext/katana/rb_katana.h +102 -0
  6. data/ext/katana/rb_katana_array.c +144 -0
  7. data/ext/katana/rb_katana_declaration.c +389 -0
  8. data/ext/katana/rb_katana_rule.c +461 -0
  9. data/ext/katana/rb_katana_selector.c +559 -0
  10. data/ext/katana/src/foundation.c +237 -0
  11. data/ext/katana/src/foundation.h +120 -0
  12. data/ext/katana/src/katana.h +590 -0
  13. data/ext/katana/src/katana.lex.c +4104 -0
  14. data/ext/katana/src/katana.lex.h +592 -0
  15. data/ext/katana/src/katana.tab.c +4422 -0
  16. data/ext/katana/src/katana.tab.h +262 -0
  17. data/ext/katana/src/parser.c +1563 -0
  18. data/ext/katana/src/parser.h +237 -0
  19. data/ext/katana/src/selector.c +659 -0
  20. data/ext/katana/src/selector.h +54 -0
  21. data/ext/katana/src/tokenizer.c +300 -0
  22. data/ext/katana/src/tokenizer.h +41 -0
  23. data/lib/habaki/charset_rule.rb +25 -0
  24. data/lib/habaki/declaration.rb +53 -0
  25. data/lib/habaki/declarations.rb +346 -0
  26. data/lib/habaki/error.rb +43 -0
  27. data/lib/habaki/font_face_rule.rb +24 -0
  28. data/lib/habaki/formal_syntax.rb +464 -0
  29. data/lib/habaki/formatter.rb +99 -0
  30. data/lib/habaki/import_rule.rb +34 -0
  31. data/lib/habaki/media_rule.rb +173 -0
  32. data/lib/habaki/namespace_rule.rb +31 -0
  33. data/lib/habaki/node.rb +52 -0
  34. data/lib/habaki/page_rule.rb +24 -0
  35. data/lib/habaki/qualified_name.rb +29 -0
  36. data/lib/habaki/rule.rb +48 -0
  37. data/lib/habaki/rules.rb +225 -0
  38. data/lib/habaki/selector.rb +98 -0
  39. data/lib/habaki/selectors.rb +49 -0
  40. data/lib/habaki/style_rule.rb +35 -0
  41. data/lib/habaki/stylesheet.rb +158 -0
  42. data/lib/habaki/sub_selector.rb +234 -0
  43. data/lib/habaki/sub_selectors.rb +42 -0
  44. data/lib/habaki/supports_rule.rb +65 -0
  45. data/lib/habaki/value.rb +321 -0
  46. data/lib/habaki/values.rb +86 -0
  47. data/lib/habaki/visitor/element.rb +50 -0
  48. data/lib/habaki/visitor/media.rb +22 -0
  49. data/lib/habaki/visitor/nokogiri_element.rb +56 -0
  50. data/lib/habaki.rb +39 -0
  51. metadata +190 -0
@@ -0,0 +1,321 @@
1
+ module Habaki
2
+ # abstract value type
3
+ class Value < Node
4
+ # @return [::String, Float]
5
+ attr_accessor :data
6
+
7
+ def initialize(data = nil)
8
+ @data = data
9
+ end
10
+
11
+ def ==(other)
12
+ to_s == other.to_s
13
+ end
14
+
15
+ def eql?(other)
16
+ to_s.eql?(other.to_s)
17
+ end
18
+
19
+ def hash
20
+ to_s.hash
21
+ end
22
+
23
+ # @param [Formatter::Base] format
24
+ # @return [String]
25
+ def string(format = Formatter::Base.new)
26
+ "#{@data}"
27
+ end
28
+
29
+ # @api private
30
+ # @param [Katana::Value] val
31
+ # @return [void]
32
+ def read_from_katana(val)
33
+ @data = val.value
34
+ end
35
+
36
+ private
37
+
38
+ def data_i_or_f
39
+ return 0 unless @data
40
+ @data.round == @data ? @data.round : @data
41
+ end
42
+ end
43
+
44
+ # <length> value type in px, pt etc
45
+ class Length < Value
46
+ # @return [Symbol]
47
+ attr_accessor :unit
48
+
49
+ def initialize(data = nil, unit = nil)
50
+ @data = data
51
+ @unit = unit
52
+ end
53
+
54
+ # is dimension absolute ?
55
+ # @return [Boolean]
56
+ def absolute?
57
+ [:px, :cm, :mm, :in, :pt, :pc].include?(@unit)
58
+ end
59
+
60
+ # is dimension relative ?
61
+ # @return [Boolean]
62
+ def relative?
63
+ [:em, :ex, :ch, :rem, :vw, :vh, :vmin, :vmax].include?(@unit)
64
+ end
65
+
66
+ # absolute value to pixel
67
+ # @return [Float, nil]
68
+ def to_px(ppi = 96)
69
+ case @unit
70
+ when :px
71
+ @data
72
+ when :cm
73
+ (@data / 2.54) * ppi
74
+ when :mm
75
+ ((@data / 10.0) / 2.54) * ppi
76
+ when :in
77
+ @data * ppi
78
+ when :pt
79
+ @data / (72.0 / ppi)
80
+ when :pc
81
+ (@data * 12.0) / (72.0 / ppi)
82
+ else
83
+ # relative
84
+ raise TypeError, "cannot convert relative #{to_s} to px"
85
+ end
86
+ end
87
+
88
+ # absolute value to em
89
+ # @return [Float, nil]
90
+ def to_em(default_px = 16.0)
91
+ return 0.0 if default_px == 0.0
92
+ if absolute?
93
+ to_px / default_px
94
+ else
95
+ if @unit == :em
96
+ @data
97
+ else
98
+ raise TypeError, "cannot convert #{to_s} to em"
99
+ end
100
+ end
101
+ end
102
+
103
+ # @return [Float]
104
+ def to_f
105
+ @data.is_a?(Float) ? @data : 0.0
106
+ end
107
+
108
+ # @return [Length]
109
+ def +(other)
110
+ case other
111
+ when Length
112
+ raise ArgumentError, "cannot addition with different units" unless @unit == other.unit
113
+ Length.new(@data + other.data, @unit)
114
+ else
115
+ raise ArgumentError, "cannot addition #{self.class} with #{other.class}"
116
+ end
117
+ end
118
+
119
+ # @return [Length]
120
+ def -(other)
121
+ case other
122
+ when Length
123
+ raise ArgumentError, "cannot substract with different units" unless @unit == other.unit
124
+ Length.new(@data - other.data, @unit)
125
+ else
126
+ raise ArgumentError, "cannot substract #{self.class} with #{other.class}"
127
+ end
128
+ end
129
+
130
+ # @return [Length]
131
+ def *(other)
132
+ case other
133
+ when Integer, Float
134
+ Length.new((@data * other).round(3), @unit)
135
+ when Percentage
136
+ Length.new((@data * other.data / 100.0).round(3), @unit)
137
+ else
138
+ raise ArgumentError, "cannot multiply #{self.class} with #{other.class}"
139
+ end
140
+ end
141
+
142
+ # @return [Length]
143
+ def /(other)
144
+ case other
145
+ when Integer, Float
146
+ Length.new((@data / other).round(3), @unit)
147
+ else
148
+ raise ArgumentError, "cannot divide #{self.class} with #{other.class}"
149
+ end
150
+ end
151
+
152
+ include Comparable
153
+
154
+ # @return [Integer]
155
+ def <=>(other)
156
+ raise ArgumentError, "cannot compare #{self.class} with #{other.class}" unless other.is_a?(Length)
157
+ if @unit == other.unit
158
+ @data <=> other.data
159
+ elsif absolute? && other.absolute?
160
+ to_px <=> other.to_px
161
+ else
162
+ nil
163
+ end
164
+ end
165
+
166
+ # @return [Boolean]
167
+ def ==(other)
168
+ return false unless other.is_a?(Length)
169
+ if @unit == other.unit
170
+ @data == other.data
171
+ elsif absolute? && other.absolute?
172
+ to_px == other.to_px
173
+ else
174
+ false
175
+ end
176
+ end
177
+
178
+ # @param [Formatter::Base] format
179
+ # @return [String]
180
+ def string(format = Formatter::Base.new)
181
+ @unit ? "#{data_i_or_f}#{@unit}" : @data
182
+ end
183
+
184
+ # @api private
185
+ # @return [void]
186
+ def read_from_katana(val)
187
+ @data = val.value
188
+ @unit = val.unit
189
+ @unit = nil if @unit == :dimension
190
+ end
191
+ end
192
+
193
+ # <angle> value type in deg, rad
194
+ class Angle < Value
195
+ # @return [Symbol]
196
+ attr_accessor :unit
197
+
198
+ def initialize(data = nil, unit = nil)
199
+ @data = data
200
+ @unit = unit
201
+ end
202
+
203
+ # @param [Formatter::Base] format
204
+ # @return [String]
205
+ def string(format = Formatter::Base.new)
206
+ @unit ? "#{data_i_or_f}#{@unit}" : @data
207
+ end
208
+
209
+ # @api private
210
+ # @return [void]
211
+ def read_from_katana(val)
212
+ @data = val.value
213
+ @unit = val.unit
214
+ @unit = nil if @unit == :dimension
215
+ end
216
+ end
217
+
218
+ # <percentage> value type
219
+ class Percentage < Value
220
+ # @return [Float]
221
+ def to_f
222
+ @data
223
+ end
224
+
225
+ # @param [Formatter::Base] format
226
+ # @return [String]
227
+ def string(format = Formatter::Base.new)
228
+ "#{data_i_or_f}%"
229
+ end
230
+ end
231
+
232
+ # <number> or <integer> value type
233
+ class Number < Value
234
+ # @return [Float]
235
+ def to_f
236
+ @data
237
+ end
238
+
239
+ # @return [Integer]
240
+ def to_i
241
+ @data.to_i
242
+ end
243
+
244
+ # @param [Formatter::Base] format
245
+ # @return [String]
246
+ def string(format = Formatter::Base.new)
247
+ "#{data_i_or_f}"
248
+ end
249
+ end
250
+
251
+ # <ident> value type
252
+ class Ident < Value
253
+ end
254
+
255
+ # <string> value type
256
+ class String < Value
257
+ # @param [Formatter::Base] format
258
+ # @return [String]
259
+ def string(format = Formatter::Base.new)
260
+ "#{format.quote}#{@data}#{format.quote}"
261
+ end
262
+ end
263
+
264
+ # operator , or /
265
+ class Operator < Value
266
+ end
267
+
268
+ # <hex-color> value type
269
+ class HexColor < Value
270
+ # @param [Formatter::Base] format
271
+ # @return [String]
272
+ def string(format = Formatter::Base.new)
273
+ "##{@data}"
274
+ end
275
+ end
276
+
277
+ class Function < Value
278
+ # @return [Values]
279
+ attr_accessor :args
280
+
281
+ # @param [Formatter::Base] format
282
+ # @return [String]
283
+ def string(format = Formatter::Base.new)
284
+ "#{@data}(#{@args.string(format)})"
285
+ end
286
+
287
+ # @api private
288
+ # @param [Katana::Value] val
289
+ # @return [void]
290
+ def read_from_katana(val)
291
+ @data = val.value.name.sub("(", "")
292
+ @args = Values.new
293
+ if val.value.args
294
+ @args = Values.read_from_katana(val.value.args)
295
+ end
296
+ end
297
+ end
298
+
299
+ # <url>/<uri> value type
300
+ class Url < Value
301
+ # is url of data type ?
302
+ # @return [Boolean]
303
+ def data_uri?
304
+ @data.start_with?("data:")
305
+ end
306
+
307
+ # @return [String]
308
+ def uri
309
+ @data
310
+ end
311
+
312
+ # @param [Formatter::Base] format
313
+ # @return [String]
314
+ def string(format = Formatter::Base.new)
315
+ "url(#{@data.include?(" ") ? "#{format.quote}#{@data}#{format.quote}" : @data})"
316
+ end
317
+ end
318
+
319
+ class UnicodeRange < Value
320
+ end
321
+ end
@@ -0,0 +1,86 @@
1
+ module Habaki
2
+ # Array of {Values}
3
+ class Values < NodeArray
4
+ # traverse values with optional class type
5
+ # @param [Class] klass
6
+ # @return [void]
7
+ def each_value(klass = nil, &block)
8
+ each do |value|
9
+ block.call value if !klass || value.is_a?(klass)
10
+ end
11
+ end
12
+
13
+ # remove value taking care of operator in list
14
+ # @param [Value] value
15
+ # @return [void]
16
+ def remove_value(value)
17
+ idx = index(value)
18
+ prev_val = at(idx - 1)
19
+ next_val = at(idx + 1)
20
+ if prev_val&.is_a?(Operator)
21
+ delete_at(idx)
22
+ delete_at(idx - 1)
23
+ elsif next_val&.is_a?(Operator)
24
+ delete_at(idx + 1)
25
+ delete_at(idx)
26
+ else
27
+ delete_at(idx)
28
+ end
29
+ end
30
+
31
+ # @param [Formatter::Base] format
32
+ # @return [String]
33
+ def string(format = Formatter::Base.new)
34
+ str = ""
35
+ each_cons(2) do |val|
36
+ str += val[0].string(format)
37
+ str += val[1].is_a?(Operator) || val[0].is_a?(Operator) ? "" : " "
38
+ end
39
+ str += last.string(format) if last
40
+ str
41
+ end
42
+
43
+ # @api private
44
+ # @param [Katana::Array<Katana::Value>] vals
45
+ # @return [void]
46
+ def read_from_katana(vals)
47
+ vals.each do |val|
48
+ push read_from_unit(val)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def read_from_unit(val)
55
+ case val.unit
56
+ when :px, :pt, :ex, :em, :mm, :cm, :in, :pc, :rem, :ch, :turn,
57
+ :vw, :vh, :vmin, :vmax, :dppx, :dpi, :dpcm, :fr, :dimension
58
+ Length.read_from_katana(val)
59
+ when :deg, :rad, :grad
60
+ Angle.read_from_katana(val)
61
+ when :percentage
62
+ Percentage.read_from_katana(val)
63
+ when :number
64
+ Number.read_from_katana(val)
65
+ when :ident
66
+ Ident.read_from_katana(val)
67
+ when :string
68
+ String.read_from_katana(val)
69
+ when :parser_hexcolor
70
+ HexColor.read_from_katana(val)
71
+ when :parser_function
72
+ Function.read_from_katana(val)
73
+ when :parser_operator
74
+ Operator.read_from_katana(val)
75
+ when :uri
76
+ Url.read_from_katana(val)
77
+ when :unicode_range
78
+ UnicodeRange.read_from_katana(val)
79
+ else
80
+ # fallback
81
+ Value.read_from_katana(val)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,50 @@
1
+ module Habaki
2
+ module Visitor
3
+ # @abstract CSS selector element visitor
4
+ class Element
5
+ # element tag name
6
+ # @return [String]
7
+ def tag_name
8
+ end
9
+
10
+ # element class name
11
+ # @return [String]
12
+ def class_name
13
+ end
14
+
15
+ # element id name
16
+ # @return [String]
17
+ def id_name
18
+ end
19
+
20
+ # element attribute
21
+ # @param [String] key
22
+ # @return [String]
23
+ def attr(key) end
24
+
25
+ # inner text
26
+ # @return [String]
27
+ def text
28
+ end
29
+
30
+ # element parent
31
+ # @return [Visitor::Element]
32
+ def parent
33
+ end
34
+
35
+ # element previous
36
+ # @return [Visitor::Element]
37
+ def previous
38
+ end
39
+
40
+ # @return [Array<Visitor::Element>]
41
+ def children
42
+ []
43
+ end
44
+
45
+ # traverse elements
46
+ def traverse &block
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ module Habaki
2
+ module Visitor
3
+ # media query visitor
4
+ class Media
5
+ # eg "print", "screen"
6
+ # @return [String]
7
+ attr_accessor :type
8
+ # width, in pixels
9
+ # @return [Integer]
10
+ attr_accessor :width
11
+ # height, in pixels
12
+ # @return [Integer]
13
+ attr_accessor :height
14
+
15
+ def initialize(type = "all", width = nil, height = nil)
16
+ @type = type
17
+ @width = width
18
+ @height = height
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ module Habaki
2
+ module Visitor
3
+ class NokogiriElement < Element
4
+ attr_accessor :element
5
+
6
+ def initialize(element)
7
+ @element = element
8
+ end
9
+
10
+ def tag_name
11
+ @element.name
12
+ end
13
+
14
+ def class_name
15
+ @element["class"]
16
+ end
17
+
18
+ def id_name
19
+ @element["id"]
20
+ end
21
+
22
+ def attr(key)
23
+ @element[key]
24
+ end
25
+
26
+ def text
27
+ @element.text
28
+ end
29
+
30
+ def parent
31
+ Visitor::NokogiriElement.new(@element.parent) if @element.respond_to?(:parent)
32
+ end
33
+
34
+ def previous
35
+ Visitor::NokogiriElement.new(@element.previous_element) if @element.respond_to?(:previous_element) && @element.previous_element
36
+ end
37
+
38
+ def children
39
+ @element.children.map do |child|
40
+ child.is_a?(Nokogiri::XML::Element) ? Visitor::NokogiriElement.new(child) : nil
41
+ end.compact
42
+ end
43
+
44
+ def traverse &block
45
+ @element.traverse do |el|
46
+ next unless el.is_a?(Nokogiri::XML::Element)
47
+ block.call Visitor::NokogiriElement.new(el)
48
+ end
49
+ end
50
+
51
+ def ==(other)
52
+ @element == other.element
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/habaki.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'set'
2
+ require 'katana/katana'
3
+
4
+ require 'habaki/formal_syntax'
5
+
6
+ require 'habaki/formatter'
7
+ require 'habaki/node'
8
+
9
+ require 'habaki/value'
10
+ require 'habaki/values'
11
+
12
+ require 'habaki/declaration'
13
+ require 'habaki/declarations'
14
+
15
+ require 'habaki/qualified_name'
16
+
17
+ require 'habaki/visitor/element'
18
+ require 'habaki/visitor/nokogiri_element'
19
+
20
+ require 'habaki/sub_selector'
21
+ require 'habaki/sub_selectors'
22
+ require 'habaki/selector'
23
+ require 'habaki/selectors'
24
+
25
+ require 'habaki/visitor/media'
26
+
27
+ require 'habaki/rule'
28
+ require 'habaki/charset_rule'
29
+ require 'habaki/namespace_rule'
30
+ require 'habaki/import_rule'
31
+ require 'habaki/font_face_rule'
32
+ require 'habaki/page_rule'
33
+ require 'habaki/style_rule'
34
+ require 'habaki/media_rule'
35
+ require 'habaki/supports_rule'
36
+ require 'habaki/rules'
37
+
38
+ require 'habaki/error'
39
+ require 'habaki/stylesheet'