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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/ext/katana/extconf.rb +20 -0
- data/ext/katana/rb_katana.c +280 -0
- data/ext/katana/rb_katana.h +102 -0
- data/ext/katana/rb_katana_array.c +144 -0
- data/ext/katana/rb_katana_declaration.c +389 -0
- data/ext/katana/rb_katana_rule.c +461 -0
- data/ext/katana/rb_katana_selector.c +559 -0
- data/ext/katana/src/foundation.c +237 -0
- data/ext/katana/src/foundation.h +120 -0
- data/ext/katana/src/katana.h +590 -0
- data/ext/katana/src/katana.lex.c +4104 -0
- data/ext/katana/src/katana.lex.h +592 -0
- data/ext/katana/src/katana.tab.c +4422 -0
- data/ext/katana/src/katana.tab.h +262 -0
- data/ext/katana/src/parser.c +1563 -0
- data/ext/katana/src/parser.h +237 -0
- data/ext/katana/src/selector.c +659 -0
- data/ext/katana/src/selector.h +54 -0
- data/ext/katana/src/tokenizer.c +300 -0
- data/ext/katana/src/tokenizer.h +41 -0
- data/lib/habaki/charset_rule.rb +25 -0
- data/lib/habaki/declaration.rb +53 -0
- data/lib/habaki/declarations.rb +346 -0
- data/lib/habaki/error.rb +43 -0
- data/lib/habaki/font_face_rule.rb +24 -0
- data/lib/habaki/formal_syntax.rb +464 -0
- data/lib/habaki/formatter.rb +99 -0
- data/lib/habaki/import_rule.rb +34 -0
- data/lib/habaki/media_rule.rb +173 -0
- data/lib/habaki/namespace_rule.rb +31 -0
- data/lib/habaki/node.rb +52 -0
- data/lib/habaki/page_rule.rb +24 -0
- data/lib/habaki/qualified_name.rb +29 -0
- data/lib/habaki/rule.rb +48 -0
- data/lib/habaki/rules.rb +225 -0
- data/lib/habaki/selector.rb +98 -0
- data/lib/habaki/selectors.rb +49 -0
- data/lib/habaki/style_rule.rb +35 -0
- data/lib/habaki/stylesheet.rb +158 -0
- data/lib/habaki/sub_selector.rb +234 -0
- data/lib/habaki/sub_selectors.rb +42 -0
- data/lib/habaki/supports_rule.rb +65 -0
- data/lib/habaki/value.rb +321 -0
- data/lib/habaki/values.rb +86 -0
- data/lib/habaki/visitor/element.rb +50 -0
- data/lib/habaki/visitor/media.rb +22 -0
- data/lib/habaki/visitor/nokogiri_element.rb +56 -0
- data/lib/habaki.rb +39 -0
- metadata +190 -0
@@ -0,0 +1,464 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Habaki
|
5
|
+
# property pattern matching
|
6
|
+
module FormalSyntax
|
7
|
+
class Node
|
8
|
+
attr_accessor :parent
|
9
|
+
attr_accessor :children
|
10
|
+
attr_accessor :type
|
11
|
+
attr_accessor :value
|
12
|
+
attr_accessor :occurence
|
13
|
+
|
14
|
+
attr_accessor :orig
|
15
|
+
|
16
|
+
def initialize(type = nil, value = nil, children = [], occurence = 1..1)
|
17
|
+
@type = type
|
18
|
+
@value = value
|
19
|
+
@children = children
|
20
|
+
@occurence = occurence
|
21
|
+
end
|
22
|
+
|
23
|
+
def push_children(child)
|
24
|
+
child.parent = self
|
25
|
+
@children << child
|
26
|
+
end
|
27
|
+
|
28
|
+
def traverse(&block)
|
29
|
+
block.call self
|
30
|
+
@children.each do |child|
|
31
|
+
child.traverse &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def occurence_from_children!
|
36
|
+
case @type
|
37
|
+
when :and
|
38
|
+
occ_min = 0
|
39
|
+
occ_max = 0
|
40
|
+
@children.each do |child|
|
41
|
+
occ_min += child.occurence.begin
|
42
|
+
occ_max += child.occurence.end
|
43
|
+
end
|
44
|
+
@occurence = Range.new(occ_min, occ_max)
|
45
|
+
when :or_and
|
46
|
+
@occurence = Range.new(1, @children.length)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def n
|
51
|
+
Float::INFINITY
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
str = ""
|
56
|
+
case @type
|
57
|
+
when :and
|
58
|
+
str += "[#{@children.map(&:to_s).join(" ")}]"
|
59
|
+
when :or
|
60
|
+
str += "[#{@children.map(&:to_s).join(" | ")}]"
|
61
|
+
when :or_and
|
62
|
+
str += "[#{@children.map(&:to_s).join(" || ")}]"
|
63
|
+
when :function
|
64
|
+
str += "#{@value}(#{@children.map(&:to_s).join(" ")})"
|
65
|
+
when :number
|
66
|
+
str += @value.to_s
|
67
|
+
when :token
|
68
|
+
str += @value
|
69
|
+
when :ref
|
70
|
+
str += "'#{@value}'"
|
71
|
+
when :type
|
72
|
+
str += "<#{@value}>"
|
73
|
+
else
|
74
|
+
str += @value
|
75
|
+
end
|
76
|
+
|
77
|
+
case @occurence.begin
|
78
|
+
when 0
|
79
|
+
case @occurence.end
|
80
|
+
when 1
|
81
|
+
str += "?"
|
82
|
+
when n
|
83
|
+
str += "*"
|
84
|
+
end
|
85
|
+
when 1
|
86
|
+
case @occurence.end
|
87
|
+
when 1
|
88
|
+
when n
|
89
|
+
str += "+"
|
90
|
+
else
|
91
|
+
str += "{#{@occurence.begin},#{@occurence.end}}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
str
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class FormalSyntaxError < StandardError
|
100
|
+
end
|
101
|
+
|
102
|
+
# format syntax tree parser
|
103
|
+
class Tree
|
104
|
+
attr_accessor :properties
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_all(data)
|
110
|
+
@properties = {}
|
111
|
+
data.each do |k, v|
|
112
|
+
begin
|
113
|
+
@properties[k] = Tree.parse(v)
|
114
|
+
rescue FormalSyntaxError => e
|
115
|
+
#STDERR.puts("#{k}: #{e}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.tree
|
122
|
+
@@tree ||= self.new.parse_all(Tree.tree_data)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.tree_data
|
126
|
+
@@tree_data ||= YAML.load_file(File.join(File.dirname(__FILE__), '../../data/formal_syntax.yml')).freeze
|
127
|
+
end
|
128
|
+
|
129
|
+
def property(prop)
|
130
|
+
@properties[prop]
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.n
|
134
|
+
Float::INFINITY
|
135
|
+
end
|
136
|
+
|
137
|
+
# formal syntax parser
|
138
|
+
def self.parse(str)
|
139
|
+
current_node = Node.new(:and)
|
140
|
+
current_node.orig = str
|
141
|
+
scanner = StringScanner.new(str.gsub("∞", "N"))
|
142
|
+
|
143
|
+
until scanner.eos?
|
144
|
+
case
|
145
|
+
when scanner.scan(/\[/)
|
146
|
+
prev_node = current_node
|
147
|
+
current_node = Node.new(:and)
|
148
|
+
current_node.parent = prev_node
|
149
|
+
when scanner.scan(/<([a-zA-Z0-9-]+)\(/)
|
150
|
+
prev_node = current_node
|
151
|
+
current_node = Node.new(:function_ref, scanner[1])
|
152
|
+
current_node.parent = prev_node
|
153
|
+
when scanner.scan(/([a-zA-Z0-9-]+)\(/)
|
154
|
+
prev_node = current_node
|
155
|
+
current_node = Node.new(:function, scanner[1])
|
156
|
+
current_node.parent = prev_node
|
157
|
+
when scanner.scan(/\?/)
|
158
|
+
prev_node = current_node.children.last
|
159
|
+
prev_node.occurence = 0..1
|
160
|
+
when scanner.scan(/\*/)
|
161
|
+
prev_node = current_node.children.last
|
162
|
+
prev_node.occurence = 0..n
|
163
|
+
when scanner.scan(/\+/)
|
164
|
+
prev_node = current_node.children.last
|
165
|
+
prev_node.occurence = 1..n
|
166
|
+
when scanner.scan(/\!/)
|
167
|
+
# at least one required
|
168
|
+
prev_node = current_node.children.last
|
169
|
+
prev_node.occurence = 1..n
|
170
|
+
when scanner.scan(/\#/)
|
171
|
+
# one or more comma separated
|
172
|
+
prev_node = current_node.children.last
|
173
|
+
# embed :type in :or
|
174
|
+
if prev_node.type == :type
|
175
|
+
emb_node = Node.new(:or)
|
176
|
+
emb_node.children << prev_node
|
177
|
+
emb_node.push_children(Node.new(:token, ","))
|
178
|
+
emb_node.occurence = 1..n
|
179
|
+
current_node.children[-1] = emb_node
|
180
|
+
else
|
181
|
+
prev_node.push_children(Node.new(:token, ","))
|
182
|
+
prev_node.occurence = 1..n
|
183
|
+
end
|
184
|
+
when scanner.scan(/\{(\d)\}/)
|
185
|
+
prev_node = current_node.children.last
|
186
|
+
prev_node.occurence = (scanner[1].to_i)..(scanner[1].to_i)
|
187
|
+
when scanner.scan(/\{(\d),(\d)\}/)
|
188
|
+
prev_node = current_node.children.last
|
189
|
+
prev_node.occurence = (scanner[1].to_i)..(scanner[2].to_i)
|
190
|
+
when scanner.scan(/\]/)
|
191
|
+
current_node.occurence_from_children!
|
192
|
+
if current_node.parent
|
193
|
+
prev_node = current_node
|
194
|
+
current_node = current_node.parent
|
195
|
+
current_node.push_children prev_node
|
196
|
+
else
|
197
|
+
raise FormalSyntaxError, "Formal syntax problem: #{current_node}"
|
198
|
+
end
|
199
|
+
when scanner.scan(/\)>?/)
|
200
|
+
if current_node.parent
|
201
|
+
prev_node = current_node
|
202
|
+
current_node = current_node.parent
|
203
|
+
current_node.push_children prev_node
|
204
|
+
else
|
205
|
+
raise FormalSyntaxError, "Formal syntax problem: #{current_node}"
|
206
|
+
end
|
207
|
+
when scanner.scan(/\s?(\/|,|:|;|%)\s?/)
|
208
|
+
current_node.push_children Node.new(:token, scanner[1])
|
209
|
+
when scanner.scan(/<([a-zA-Z0-9-]+)(\s\[\d,N?\d*\])?>/)
|
210
|
+
current_node.push_children Node.new(:type, scanner[1])
|
211
|
+
when scanner.scan(/([a-zA-Z-]+)/)
|
212
|
+
current_node.push_children Node.new(:ident, scanner[1]) #if scanner[1] != "inherit"
|
213
|
+
when scanner.scan(/([0-9]+)/)
|
214
|
+
current_node.push_children Node.new(:number, scanner[1].to_i)
|
215
|
+
when scanner.scan(/<?'([a-zA-Z-]+)'>?/)
|
216
|
+
current_node.push_children Node.new(:ref, scanner[1])
|
217
|
+
when scanner.scan(/'(..?)'/)
|
218
|
+
current_node.push_children Node.new(:token, scanner[1])
|
219
|
+
when scanner.scan(/\|\|/)
|
220
|
+
current_node.type = :or_and
|
221
|
+
when scanner.scan(/\|/)
|
222
|
+
current_node.type = :or
|
223
|
+
when scanner.scan(/\&\&/)
|
224
|
+
current_node.type = :and
|
225
|
+
else
|
226
|
+
result = scanner.scan(/.+/)
|
227
|
+
raise FormalSyntaxError, "Cannot parse formal syntax: #{result}"
|
228
|
+
end
|
229
|
+
scanner.scan(/\s+/)
|
230
|
+
end
|
231
|
+
current_node.occurence_from_children!
|
232
|
+
current_node
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# formal syntax matcher result
|
237
|
+
class Match
|
238
|
+
# @return [String]
|
239
|
+
attr_accessor :reference
|
240
|
+
# @return [FormalSyntax::Node]
|
241
|
+
attr_accessor :node
|
242
|
+
# @return [Value]
|
243
|
+
attr_accessor :value
|
244
|
+
|
245
|
+
def initialize(ref, node)
|
246
|
+
@reference = ref
|
247
|
+
@node = node
|
248
|
+
end
|
249
|
+
|
250
|
+
def to_s
|
251
|
+
"#{@reference}: #{@value} => #{@node}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# formal syntax matcher
|
256
|
+
class Matcher
|
257
|
+
# @return [Array<Match>]
|
258
|
+
attr_accessor :matches
|
259
|
+
attr_accessor :debug
|
260
|
+
|
261
|
+
# @param [Declaration] declaration
|
262
|
+
def initialize(declaration, tree = Tree.tree)
|
263
|
+
@declaration = declaration
|
264
|
+
@tree = tree
|
265
|
+
@reference = nil
|
266
|
+
@matches = []
|
267
|
+
@debug = false
|
268
|
+
@match = nil
|
269
|
+
end
|
270
|
+
|
271
|
+
# @return [Boolean]
|
272
|
+
def match
|
273
|
+
@idx = 0
|
274
|
+
node = @tree.properties[@declaration.property]
|
275
|
+
return false unless node
|
276
|
+
|
277
|
+
puts "MATCH #{node} (#{node.type}) #{node.occurence} WITH #{@declaration.to_s}" if @debug
|
278
|
+
@reference = @declaration.property
|
279
|
+
# always add inherit keyword
|
280
|
+
return true if @declaration.value == Habaki::Ident.new("inherit")
|
281
|
+
|
282
|
+
res = rec_match(node)
|
283
|
+
@matches.compact!
|
284
|
+
@matches.each_with_index do |match, idx|
|
285
|
+
match.value = @declaration.values[idx]
|
286
|
+
end
|
287
|
+
|
288
|
+
puts "MATCH? #{res} #{node.occurence}, #{@idx} / #{count_values}" if @debug
|
289
|
+
res && calc_occurence(node).include?(@idx) && @idx >= count_values
|
290
|
+
end
|
291
|
+
|
292
|
+
# @return [Boolean]
|
293
|
+
def match?
|
294
|
+
@match ||= match
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
def calc_occurence(node)
|
300
|
+
resolved_node = resolve_node(node)
|
301
|
+
if resolved_node.type == :or
|
302
|
+
occ_min = resolved_node.occurence.begin
|
303
|
+
occ_max = resolved_node.occurence.end
|
304
|
+
resolved_node.children.each do |child|
|
305
|
+
r_occ = calc_occurence(child)
|
306
|
+
#occ_min += r_occ.begin
|
307
|
+
occ_max += r_occ.end
|
308
|
+
end
|
309
|
+
Range.new(occ_min, occ_max)
|
310
|
+
else
|
311
|
+
resolved_node.occurence
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def resolve_function(value)
|
316
|
+
if value.is_a?(Habaki::Function)
|
317
|
+
case value.data
|
318
|
+
when "calc", "min", "max", "clamp"
|
319
|
+
Length.new("0", :px)
|
320
|
+
when "attr"
|
321
|
+
String.new("")
|
322
|
+
else
|
323
|
+
value
|
324
|
+
end
|
325
|
+
else
|
326
|
+
value
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def next_value
|
331
|
+
@idx += 1
|
332
|
+
end
|
333
|
+
|
334
|
+
def match_value_class(value, klass)
|
335
|
+
return false unless value.is_a?(klass)
|
336
|
+
|
337
|
+
next_value
|
338
|
+
true
|
339
|
+
end
|
340
|
+
|
341
|
+
def match_value_class_and_data(value, klass, node)
|
342
|
+
return false unless value.is_a?(klass)
|
343
|
+
|
344
|
+
if value.data == node.value
|
345
|
+
next_value
|
346
|
+
true
|
347
|
+
else
|
348
|
+
false
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def resolve_node(node)
|
353
|
+
return node if %w[percentage length angle number integer string custom-ident uri hexcolor hex-color].include?(node.value)
|
354
|
+
resolved_node = node
|
355
|
+
|
356
|
+
if node.type == :ref
|
357
|
+
@reference = node.value
|
358
|
+
ref_node = @tree.properties[node.value]
|
359
|
+
resolved_node = ref_node if ref_node
|
360
|
+
end
|
361
|
+
|
362
|
+
if node.type == :type
|
363
|
+
alias_node = @tree.properties["<#{node.value}>"]
|
364
|
+
resolved_node = alias_node if alias_node
|
365
|
+
end
|
366
|
+
|
367
|
+
if node.type == :function_ref
|
368
|
+
alias_node = @tree.properties["<#{node.value}()>"]
|
369
|
+
resolved_node = alias_node if alias_node
|
370
|
+
end
|
371
|
+
|
372
|
+
resolved_node
|
373
|
+
end
|
374
|
+
|
375
|
+
def save_state(node, res)
|
376
|
+
@matches[@idx] = Match.new(@reference, node) if res && !@matches[@idx]
|
377
|
+
end
|
378
|
+
|
379
|
+
def count_values
|
380
|
+
@declaration.values.length
|
381
|
+
end
|
382
|
+
|
383
|
+
def rec_match(node)
|
384
|
+
value = resolve_function(@declaration.values[@idx])
|
385
|
+
return false unless value
|
386
|
+
|
387
|
+
resolved_node = resolve_node(node)
|
388
|
+
|
389
|
+
match = false
|
390
|
+
loop = resolved_node.occurence.end > count_values ? count_values : (resolved_node.occurence.end - resolved_node.occurence.begin + 1)
|
391
|
+
|
392
|
+
puts "[#{@idx}/#{count_values}] #{resolved_node.occurence}/#{loop} #{value} => #{resolved_node} (#{resolved_node.type})" if @debug
|
393
|
+
|
394
|
+
loop.times do |i|
|
395
|
+
occ_match =
|
396
|
+
case resolved_node.type
|
397
|
+
when :or_and
|
398
|
+
res = false
|
399
|
+
resolved_node.children.each do |child|
|
400
|
+
tres = rec_match(child)
|
401
|
+
save_state(child, tres)
|
402
|
+
res ||= tres
|
403
|
+
end
|
404
|
+
res
|
405
|
+
when :and
|
406
|
+
founds = 0
|
407
|
+
resolved_node.children.each do |child|
|
408
|
+
res = rec_match(child)
|
409
|
+
save_state(child, res)
|
410
|
+
# puts "AND CHILD #{child.occurence} #{child}" if @debug
|
411
|
+
founds += 1 if res
|
412
|
+
end
|
413
|
+
# puts "AND #{founds} #{resolved_node.occurence} #{resolved_node.occurence.include?(founds)} #{resolved_node}" if @debug
|
414
|
+
resolved_node.occurence.include?(founds)
|
415
|
+
when :or
|
416
|
+
res = false
|
417
|
+
resolved_node.children.each do |child|
|
418
|
+
tres = rec_match(child)
|
419
|
+
save_state(child, tres)
|
420
|
+
res ||= tres
|
421
|
+
break if tres
|
422
|
+
end
|
423
|
+
res
|
424
|
+
when :number
|
425
|
+
match_value_class_and_data(value, Habaki::Number, resolved_node)
|
426
|
+
when :type
|
427
|
+
case resolved_node.value
|
428
|
+
when "percentage"
|
429
|
+
match_value_class(value, Habaki::Percentage)
|
430
|
+
when "length"
|
431
|
+
# 0 is acceptable too
|
432
|
+
(match_value_class(value, Habaki::Length) && value.unit) || (match_value_class(value, Habaki::Number) && value.to_f == 0.0)
|
433
|
+
when "angle"
|
434
|
+
match_value_class(value, Habaki::Angle)
|
435
|
+
when "number", "integer"
|
436
|
+
match_value_class(value, Habaki::Number)
|
437
|
+
when "string", "custom-ident"
|
438
|
+
match_value_class(value, Habaki::String) || match_value_class(value, Habaki::Ident)
|
439
|
+
when "uri", "url"
|
440
|
+
match_value_class(value, Habaki::Url)
|
441
|
+
when "hexcolor", "hex-color"
|
442
|
+
match_value_class(value, Habaki::HexColor)
|
443
|
+
else
|
444
|
+
false
|
445
|
+
end
|
446
|
+
when :ident
|
447
|
+
match_value_class_and_data(value, Habaki::Ident, resolved_node)
|
448
|
+
when :function
|
449
|
+
match_value_class_and_data(value, Habaki::Function, resolved_node)
|
450
|
+
when :token
|
451
|
+
match_value_class_and_data(value, Habaki::Operator, resolved_node)
|
452
|
+
else
|
453
|
+
false
|
454
|
+
end
|
455
|
+
|
456
|
+
match ||= occ_match
|
457
|
+
puts " MATCH OCC? #{i}/#{loop} #{value} : #{resolved_node} #{occ_match} #{match}" if @debug
|
458
|
+
end
|
459
|
+
match
|
460
|
+
end
|
461
|
+
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Habaki
|
2
|
+
# output formatting
|
3
|
+
module Formatter
|
4
|
+
class Base
|
5
|
+
# @return [Integer]
|
6
|
+
attr_accessor :level
|
7
|
+
|
8
|
+
def initialize(level = 0)
|
9
|
+
@level = level
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
def declaration_prefix
|
14
|
+
""
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String]
|
18
|
+
def declarations_prefix
|
19
|
+
""
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def declarations_join
|
24
|
+
""
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def declarations_suffix
|
29
|
+
""
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String]
|
33
|
+
def rules_prefix
|
34
|
+
""
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String]
|
38
|
+
def rules_join
|
39
|
+
""
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String]
|
43
|
+
def quote
|
44
|
+
"\""
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [self]
|
48
|
+
def +(num)
|
49
|
+
self.class.new(@level + num)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# default flat formatting
|
54
|
+
class Flat < Base
|
55
|
+
# @return [String]
|
56
|
+
def declarations_join
|
57
|
+
" "
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [String]
|
61
|
+
def rules_join
|
62
|
+
"\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# indented formatting
|
67
|
+
class Indented < Base
|
68
|
+
# @return [String]
|
69
|
+
def declaration_prefix
|
70
|
+
" " * @level
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [String]
|
74
|
+
def declarations_join
|
75
|
+
"\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [String]
|
79
|
+
def declarations_prefix
|
80
|
+
"\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [String]
|
84
|
+
def declarations_suffix
|
85
|
+
"\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [String]
|
89
|
+
def rules_prefix
|
90
|
+
" " * @level
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [String]
|
94
|
+
def rules_join
|
95
|
+
"\n\n"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Habaki
|
2
|
+
# Rule for @import
|
3
|
+
class ImportRule < Rule
|
4
|
+
# @return [String]
|
5
|
+
attr_accessor :href
|
6
|
+
# @return [MediaQueries]
|
7
|
+
attr_accessor :medias
|
8
|
+
|
9
|
+
# @param [String] href
|
10
|
+
def initialize(href = nil)
|
11
|
+
@href = href
|
12
|
+
@medias = MediaQueries.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Habaki::Stylesheet]
|
16
|
+
def stylesheet(base_dir: "")
|
17
|
+
Stylesheet.parse_file(base_dir+@href)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Formatter::Base] format
|
21
|
+
# @return [String]
|
22
|
+
def string(format = Formatter::Base.new)
|
23
|
+
"@import #{format.quote}#{@href}#{format.quote} #{@medias.string(format)};"
|
24
|
+
end
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
# @param [Katana::ImportRule] rule
|
28
|
+
# @return [void]
|
29
|
+
def read_from_katana(rule)
|
30
|
+
@href = rule.href
|
31
|
+
@medias = MediaQueries.read_from_katana(rule.medias)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|