cataract 0.2.3 → 0.2.5
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 +4 -4
- data/.rubocop.yml +8 -3
- data/BENCHMARKS.md +50 -32
- data/CHANGELOG.md +21 -1
- data/Gemfile +3 -0
- data/ext/cataract/cataract.c +219 -112
- data/ext/cataract/cataract.h +5 -1
- data/ext/cataract/css_parser.c +875 -50
- data/ext/cataract/flatten.c +233 -91
- data/ext/cataract/shorthand_expander.c +7 -0
- data/lib/cataract/at_rule.rb +2 -1
- data/lib/cataract/constants.rb +10 -0
- data/lib/cataract/error.rb +49 -0
- data/lib/cataract/import_resolver.rb +18 -87
- data/lib/cataract/import_statement.rb +29 -5
- data/lib/cataract/media_query.rb +98 -0
- data/lib/cataract/pure/byte_constants.rb +15 -0
- data/lib/cataract/pure/flatten.rb +127 -15
- data/lib/cataract/pure/parser.rb +800 -271
- data/lib/cataract/pure/serializer.rb +216 -115
- data/lib/cataract/pure.rb +8 -7
- data/lib/cataract/rule.rb +9 -5
- data/lib/cataract/stylesheet.rb +345 -101
- data/lib/cataract/stylesheet_scope.rb +10 -7
- data/lib/cataract/version.rb +1 -1
- data/lib/cataract.rb +5 -8
- data/lib/tasks/profile.rake +210 -0
- metadata +5 -2
- data/lib/cataract/pure/imports.rb +0 -268
|
@@ -15,31 +15,55 @@ module Cataract
|
|
|
15
15
|
#
|
|
16
16
|
# @example Import with media query
|
|
17
17
|
# @import "mobile.css" screen and (max-width: 768px);
|
|
18
|
-
# # => ImportStatement(url: "mobile.css",
|
|
18
|
+
# # => ImportStatement(url: "mobile.css", media_query_id: 0)
|
|
19
19
|
#
|
|
20
20
|
# @attr [Integer] id The import's position in the source (0-indexed)
|
|
21
21
|
# @attr [String] url The URL to import (without quotes or url() wrapper)
|
|
22
|
-
# @attr [
|
|
22
|
+
# @attr [String, nil] media The media query string (e.g., "print", "screen and (max-width: 768px)"), or nil
|
|
23
|
+
# @attr [Integer, nil] media_query_id The MediaQuery ID, or nil if no media query
|
|
23
24
|
# @attr [Boolean] resolved Whether this import has been resolved/processed
|
|
24
|
-
ImportStatement = Struct.new(:id, :url, :media, :resolved) unless const_defined?(:ImportStatement)
|
|
25
|
+
ImportStatement = Struct.new(:id, :url, :media, :media_query_id, :resolved) unless const_defined?(:ImportStatement)
|
|
25
26
|
|
|
26
27
|
class ImportStatement
|
|
28
|
+
# Factory method for creating ImportStatement in tests.
|
|
29
|
+
# Uses keyword arguments to avoid positional parameter confusion.
|
|
30
|
+
#
|
|
31
|
+
# @param id [Integer] Import ID (position in source)
|
|
32
|
+
# @param url [String] URL to import
|
|
33
|
+
# @param media [String, nil] Media query string (e.g., "print", "screen and (max-width: 768px)")
|
|
34
|
+
# @param media_query_id [Integer, nil] MediaQuery ID
|
|
35
|
+
# @param resolved [Boolean] Whether import has been resolved
|
|
36
|
+
# @return [ImportStatement] New import statement instance
|
|
37
|
+
#
|
|
38
|
+
# @example Create an import with keyword arguments
|
|
39
|
+
# ImportStatement.make(
|
|
40
|
+
# id: 0,
|
|
41
|
+
# url: 'styles.css',
|
|
42
|
+
# media: nil,
|
|
43
|
+
# media_query_id: nil,
|
|
44
|
+
# resolved: false
|
|
45
|
+
# )
|
|
46
|
+
def self.make(id:, url:, media: nil, media_query_id: nil, resolved: false)
|
|
47
|
+
new(id, url, media, media_query_id, resolved)
|
|
48
|
+
end
|
|
49
|
+
|
|
27
50
|
# Compare two ImportStatement objects for equality.
|
|
28
51
|
# Two imports are equal if they have the same URL and media query.
|
|
29
|
-
# The ID is ignored as it's an implementation detail.
|
|
52
|
+
# The import ID is ignored as it's an implementation detail.
|
|
30
53
|
#
|
|
31
54
|
# @param other [Object] Object to compare with
|
|
32
55
|
# @return [Boolean] true if equal, false otherwise
|
|
33
56
|
def ==(other)
|
|
34
57
|
return false unless other.is_a?(ImportStatement)
|
|
35
58
|
|
|
59
|
+
# Compare by media string (for unparsed imports) or media_query_id (for resolved imports)
|
|
36
60
|
url == other.url && media == other.media
|
|
37
61
|
end
|
|
38
62
|
|
|
39
63
|
alias eql? ==
|
|
40
64
|
|
|
41
65
|
# Generate hash code for ImportStatement.
|
|
42
|
-
# Uses URL and media
|
|
66
|
+
# Uses URL and media string (ignores import ID position).
|
|
43
67
|
#
|
|
44
68
|
# @return [Integer] Hash code
|
|
45
69
|
def hash
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cataract
|
|
4
|
+
# MediaQuery represents a CSS media query constraint.
|
|
5
|
+
#
|
|
6
|
+
# Media queries are stored in the Stylesheet and referenced by Rules via media_query_id.
|
|
7
|
+
# This allows efficient tracking of which rules apply to which media contexts.
|
|
8
|
+
#
|
|
9
|
+
# @example Access media query properties
|
|
10
|
+
# mq = MediaQuery.new(0, :screen, "(min-width: 768px)")
|
|
11
|
+
# mq.id #=> 0
|
|
12
|
+
# mq.type #=> :screen
|
|
13
|
+
# mq.conditions #=> "(min-width: 768px)"
|
|
14
|
+
# mq.text #=> "screen and (min-width: 768px)"
|
|
15
|
+
#
|
|
16
|
+
# @attr [Integer] id Unique identifier for this media query within the stylesheet
|
|
17
|
+
# @attr [Symbol] type Primary media type (:screen, :print, :all, etc.)
|
|
18
|
+
# @attr [String, nil] conditions Additional conditions like "(min-width: 768px)", or nil if none
|
|
19
|
+
MediaQuery = Struct.new(:id, :type, :conditions) do
|
|
20
|
+
# Create a MediaQuery with keyword arguments for readability.
|
|
21
|
+
#
|
|
22
|
+
# @param id [Integer] Unique ID for this media query
|
|
23
|
+
# @param type [Symbol] Primary media type
|
|
24
|
+
# @param conditions [String, nil] Optional conditions
|
|
25
|
+
# @return [MediaQuery] New media query instance
|
|
26
|
+
#
|
|
27
|
+
# @example Create a simple media query
|
|
28
|
+
# MediaQuery.make(id: 0, type: :screen, conditions: nil)
|
|
29
|
+
#
|
|
30
|
+
# @example Create a media query with conditions
|
|
31
|
+
# MediaQuery.make(id: 1, type: :screen, conditions: "(min-width: 768px)")
|
|
32
|
+
def self.make(id:, type:, conditions: nil)
|
|
33
|
+
new(id, type, conditions)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get the full media query text.
|
|
37
|
+
#
|
|
38
|
+
# Reconstructs the media query string from type and conditions.
|
|
39
|
+
#
|
|
40
|
+
# @return [String] Full media query text
|
|
41
|
+
#
|
|
42
|
+
# @example Simple media query
|
|
43
|
+
# mq = MediaQuery.new(0, :screen, nil)
|
|
44
|
+
# mq.text #=> "screen"
|
|
45
|
+
#
|
|
46
|
+
# @example Media query with conditions
|
|
47
|
+
# mq = MediaQuery.new(0, :screen, "(min-width: 768px)")
|
|
48
|
+
# mq.text #=> "screen and (min-width: 768px)"
|
|
49
|
+
def text
|
|
50
|
+
if conditions
|
|
51
|
+
# If type is :all, just return conditions (don't say "all and ...")
|
|
52
|
+
type == :all ? conditions : "#{type} and #{conditions}"
|
|
53
|
+
else
|
|
54
|
+
type.to_s
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Compare media queries for equality based on type and conditions.
|
|
59
|
+
#
|
|
60
|
+
# Two media queries are equal if they have the same type and conditions.
|
|
61
|
+
# IDs are not considered since they're internal identifiers.
|
|
62
|
+
#
|
|
63
|
+
# @param other [Object] Object to compare with
|
|
64
|
+
# @return [Boolean] true if media queries match
|
|
65
|
+
def ==(other)
|
|
66
|
+
case other
|
|
67
|
+
when MediaQuery
|
|
68
|
+
type == other.type && conditions == other.conditions
|
|
69
|
+
else
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
alias_method :eql?, :==
|
|
74
|
+
|
|
75
|
+
# Generate hash code for this media query.
|
|
76
|
+
#
|
|
77
|
+
# Hash is based on type and conditions to match equality semantics.
|
|
78
|
+
#
|
|
79
|
+
# @return [Integer] hash code
|
|
80
|
+
def hash
|
|
81
|
+
[self.class, type, conditions].hash
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get a human-readable representation.
|
|
85
|
+
#
|
|
86
|
+
# @return [String] String representation
|
|
87
|
+
def to_s
|
|
88
|
+
text
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get detailed inspection string.
|
|
92
|
+
#
|
|
93
|
+
# @return [String] Inspection string
|
|
94
|
+
def inspect
|
|
95
|
+
"#<MediaQuery id=#{id} type=#{type.inspect} conditions=#{conditions.inspect}>"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -46,11 +46,26 @@ module Cataract
|
|
|
46
46
|
BYTE_BANG = 33 # '!'
|
|
47
47
|
BYTE_PERCENT = 37 # '%'
|
|
48
48
|
BYTE_SLASH_FWD = 47 # '/' (also defined as BYTE_SLASH above)
|
|
49
|
+
BYTE_EQUALS = 61 # '='
|
|
50
|
+
BYTE_CARET = 94 # '^'
|
|
51
|
+
BYTE_DOLLAR = 36 # '$'
|
|
52
|
+
BYTE_PIPE = 124 # '|'
|
|
49
53
|
|
|
50
54
|
# Specific lowercase letters (for keyword matching)
|
|
51
55
|
BYTE_LOWER_U = 117 # 'u'
|
|
52
56
|
BYTE_LOWER_R = 114 # 'r'
|
|
53
57
|
BYTE_LOWER_L = 108 # 'l'
|
|
58
|
+
BYTE_LOWER_D = 100 # 'd'
|
|
59
|
+
BYTE_LOWER_T = 116 # 't'
|
|
60
|
+
BYTE_LOWER_N = 110 # 'n'
|
|
61
|
+
|
|
62
|
+
# Specific uppercase letters (for case-insensitive matching)
|
|
63
|
+
BYTE_UPPER_U = 85 # 'U'
|
|
64
|
+
BYTE_UPPER_R = 82 # 'R'
|
|
65
|
+
BYTE_UPPER_L = 76 # 'L'
|
|
66
|
+
BYTE_UPPER_D = 68 # 'D'
|
|
67
|
+
BYTE_UPPER_T = 84 # 'T'
|
|
68
|
+
BYTE_UPPER_N = 78 # 'N'
|
|
54
69
|
|
|
55
70
|
# Letter ranges (a-z, A-Z)
|
|
56
71
|
BYTE_LOWER_A = 97 # 'a'
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# Pure Ruby CSS flatten implementation
|
|
4
4
|
# NO REGEXP ALLOWED - use string manipulation only
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
# This module contains internal methods for flattening CSS (merging rules with the
|
|
8
|
+
# same selector, expanding/recreating shorthands). These methods are called by
|
|
9
|
+
# Cataract.flatten and should not be used directly except for expand_shorthand
|
|
10
|
+
# which is part of the public API. The main public API is Cataract.flatten.
|
|
5
11
|
|
|
6
12
|
module Cataract
|
|
7
13
|
module Flatten
|
|
@@ -115,6 +121,24 @@ module Cataract
|
|
|
115
121
|
LIST_STYLE_PROPERTIES = [PROP_LIST_STYLE_TYPE, PROP_LIST_STYLE_POSITION, PROP_LIST_STYLE_IMAGE].freeze
|
|
116
122
|
BORDER_ALL = (BORDER_WIDTHS + BORDER_STYLES + BORDER_COLORS).freeze
|
|
117
123
|
|
|
124
|
+
# Shorthand property lookup (Hash is faster than Array#include? or Set)
|
|
125
|
+
# Used for fast-path check to avoid calling expand_shorthand for non-shorthands
|
|
126
|
+
SHORTHAND_PROPERTIES = {
|
|
127
|
+
'margin' => true,
|
|
128
|
+
'padding' => true,
|
|
129
|
+
'border' => true,
|
|
130
|
+
'border-top' => true,
|
|
131
|
+
'border-right' => true,
|
|
132
|
+
'border-bottom' => true,
|
|
133
|
+
'border-left' => true,
|
|
134
|
+
'border-width' => true,
|
|
135
|
+
'border-style' => true,
|
|
136
|
+
'border-color' => true,
|
|
137
|
+
'font' => true,
|
|
138
|
+
'background' => true,
|
|
139
|
+
'list-style' => true
|
|
140
|
+
}.freeze
|
|
141
|
+
|
|
118
142
|
# List style keywords
|
|
119
143
|
LIST_STYLE_POSITION_KEYWORDS = %w[inside outside].freeze
|
|
120
144
|
|
|
@@ -154,27 +178,47 @@ module Cataract
|
|
|
154
178
|
# Expand shorthands in regular rules only (AtRules don't have declarations)
|
|
155
179
|
# NOTE: Using manual each + concat instead of .flat_map for performance.
|
|
156
180
|
# The concise form (.flat_map) is ~5-10% slower depending on number of shorthands to expand.
|
|
181
|
+
# NOTE: Fast-path check for shorthands (Hash lookup) avoids calling expand_shorthand
|
|
182
|
+
# for declarations that are not shorthands (~20% faster than calling method unconditionally).
|
|
157
183
|
regular_rules.each do |rule|
|
|
158
184
|
expanded = []
|
|
159
185
|
rule.declarations.each do |decl|
|
|
160
|
-
|
|
186
|
+
if SHORTHAND_PROPERTIES[decl.property]
|
|
187
|
+
expanded.concat(expand_shorthand(decl))
|
|
188
|
+
else
|
|
189
|
+
expanded << decl
|
|
190
|
+
end
|
|
161
191
|
end
|
|
162
192
|
rule.declarations.replace(expanded)
|
|
163
193
|
end
|
|
164
194
|
|
|
165
195
|
merged_rules = []
|
|
166
196
|
|
|
167
|
-
#
|
|
168
|
-
#
|
|
197
|
+
# Group by (selector, media_query_id) instead of just selector
|
|
198
|
+
# Rules with same selector but different media contexts should NOT be merged
|
|
169
199
|
# NOTE: Using manual each instead of .group_by to avoid intermediate hash allocation.
|
|
170
|
-
|
|
171
|
-
by_selector = {}
|
|
200
|
+
by_selector_and_media = {}
|
|
172
201
|
regular_rules.each do |rule|
|
|
173
|
-
|
|
202
|
+
media_query_id = rule.media_query_id
|
|
203
|
+
key = [rule.selector, media_query_id]
|
|
204
|
+
(by_selector_and_media[key] ||= []) << rule
|
|
174
205
|
end
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
|
|
207
|
+
# Track old rule ID to new merged rule index mapping (only for rules in media queries)
|
|
208
|
+
old_to_new_id = {}
|
|
209
|
+
by_selector_and_media.each do |(_selector, media_query_id), rules|
|
|
210
|
+
merged_rule = flatten_rules_for_selector(rules.first.selector, rules)
|
|
211
|
+
next unless merged_rule
|
|
212
|
+
|
|
213
|
+
# Only build mapping for rules that are in media queries
|
|
214
|
+
if media_query_id
|
|
215
|
+
new_index = merged_rules.length
|
|
216
|
+
|
|
217
|
+
rules.each do |old_rule|
|
|
218
|
+
old_to_new_id[old_rule.id] = new_index
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
merged_rules << merged_rule
|
|
178
222
|
end
|
|
179
223
|
|
|
180
224
|
# Recreate shorthands where possible
|
|
@@ -195,11 +239,55 @@ module Cataract
|
|
|
195
239
|
# Add passthrough AtRules to output
|
|
196
240
|
merged_rules.concat(at_rules)
|
|
197
241
|
|
|
242
|
+
# Rebuild media_index from rules' media_query_id
|
|
243
|
+
# This ensures media_index is consistent with the MediaQuery objects
|
|
244
|
+
media_queries = stylesheet.instance_variable_get(:@media_queries)
|
|
245
|
+
media_query_lists = stylesheet.instance_variable_get(:@_media_query_lists)
|
|
246
|
+
new_media_index = {}
|
|
247
|
+
|
|
248
|
+
# Build reverse map: media_query_id => list_id (one-time cost)
|
|
249
|
+
mq_id_to_list_id = {}
|
|
250
|
+
media_query_lists.each do |list_id, mq_ids|
|
|
251
|
+
mq_ids.each { |mq_id| mq_id_to_list_id[mq_id] = list_id }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
merged_rules.each do |rule|
|
|
255
|
+
next unless rule.is_a?(Rule) && rule.media_query_id
|
|
256
|
+
|
|
257
|
+
# Check if this rule's media_query_id is part of a list
|
|
258
|
+
list_id = mq_id_to_list_id[rule.media_query_id]
|
|
259
|
+
|
|
260
|
+
if list_id
|
|
261
|
+
# This rule is in a compound media query (e.g., "@media screen, print")
|
|
262
|
+
# Index it under ALL media types in the list
|
|
263
|
+
mq_ids = media_query_lists[list_id]
|
|
264
|
+
mq_ids.each do |mq_id|
|
|
265
|
+
mq = media_queries[mq_id]
|
|
266
|
+
next unless mq
|
|
267
|
+
|
|
268
|
+
media_type = mq.type
|
|
269
|
+
new_media_index[media_type] ||= []
|
|
270
|
+
new_media_index[media_type] << rule.id
|
|
271
|
+
end
|
|
272
|
+
else
|
|
273
|
+
# Single media query - just index under its type
|
|
274
|
+
mq = media_queries[rule.media_query_id]
|
|
275
|
+
next unless mq
|
|
276
|
+
|
|
277
|
+
media_type = mq.type
|
|
278
|
+
new_media_index[media_type] ||= []
|
|
279
|
+
new_media_index[media_type] << rule.id
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Deduplicate arrays once at the end
|
|
284
|
+
new_media_index.each_value(&:uniq!)
|
|
285
|
+
|
|
198
286
|
# Create result stylesheet
|
|
199
287
|
if mutate
|
|
200
288
|
stylesheet.instance_variable_set(:@rules, merged_rules)
|
|
201
|
-
|
|
202
|
-
|
|
289
|
+
stylesheet.instance_variable_set(:@media_index, new_media_index)
|
|
290
|
+
# @media_queries and @_media_query_lists stay the same - preserved from input
|
|
203
291
|
# Update selector lists with divergence tracking
|
|
204
292
|
stylesheet.instance_variable_set(:@_selector_lists, selector_lists)
|
|
205
293
|
stylesheet
|
|
@@ -207,7 +295,9 @@ module Cataract
|
|
|
207
295
|
# Create new Stylesheet with merged rules
|
|
208
296
|
result = Stylesheet.new
|
|
209
297
|
result.instance_variable_set(:@rules, merged_rules)
|
|
210
|
-
result.instance_variable_set(:@media_index,
|
|
298
|
+
result.instance_variable_set(:@media_index, new_media_index)
|
|
299
|
+
result.instance_variable_set(:@media_queries, media_queries)
|
|
300
|
+
result.instance_variable_set(:@_media_query_lists, media_query_lists)
|
|
211
301
|
result.instance_variable_set(:@charset, stylesheet.charset)
|
|
212
302
|
result.instance_variable_set(:@_selector_lists, selector_lists)
|
|
213
303
|
result
|
|
@@ -294,6 +384,9 @@ module Cataract
|
|
|
294
384
|
selector_list_ids.uniq!
|
|
295
385
|
selector_list_id = selector_list_ids.size == 1 ? selector_list_ids.first : nil
|
|
296
386
|
|
|
387
|
+
# All rules being merged have the same media_query_id (they were grouped by it)
|
|
388
|
+
media_query_id = rules.first.media_query_id
|
|
389
|
+
|
|
297
390
|
# Create merged rule
|
|
298
391
|
Rule.new(
|
|
299
392
|
0, # ID will be updated later
|
|
@@ -302,7 +395,8 @@ module Cataract
|
|
|
302
395
|
rules.first.specificity, # Use first rule's specificity
|
|
303
396
|
nil, # No parent after flattening
|
|
304
397
|
nil, # No nesting style after flattening
|
|
305
|
-
selector_list_id # Preserve if all rules share same ID
|
|
398
|
+
selector_list_id, # Preserve if all rules share same ID
|
|
399
|
+
media_query_id # Preserve media context
|
|
306
400
|
)
|
|
307
401
|
end
|
|
308
402
|
|
|
@@ -321,7 +415,7 @@ module Cataract
|
|
|
321
415
|
# @param decl [Declaration] Declaration to expand
|
|
322
416
|
# @return [Array<Declaration>] Array of expanded longhand declarations
|
|
323
417
|
# @api private
|
|
324
|
-
def self.
|
|
418
|
+
def self.expand_shorthand(decl)
|
|
325
419
|
case decl.property
|
|
326
420
|
when 'margin'
|
|
327
421
|
expand_margin(decl)
|
|
@@ -1133,7 +1227,13 @@ module Cataract
|
|
|
1133
1227
|
parts << attachment if attachment
|
|
1134
1228
|
end
|
|
1135
1229
|
|
|
1136
|
-
|
|
1230
|
+
# If all properties are defaults, the shorthand value would be empty
|
|
1231
|
+
# In this case, use "none" which is equivalent to all-default background
|
|
1232
|
+
shorthand_value = if parts.empty?
|
|
1233
|
+
'none'
|
|
1234
|
+
else
|
|
1235
|
+
parts.join(' ')
|
|
1236
|
+
end
|
|
1137
1237
|
|
|
1138
1238
|
# Remove individual properties and append shorthand
|
|
1139
1239
|
# Note: We append rather than insert at original position to match C implementation behavior
|
|
@@ -1236,5 +1336,17 @@ module Cataract
|
|
|
1236
1336
|
d1.important == d2.important
|
|
1237
1337
|
end
|
|
1238
1338
|
end
|
|
1339
|
+
|
|
1340
|
+
# Mark all methods except flatten and expand_shorthand as private
|
|
1341
|
+
private_class_method :flatten_rules_for_selector, :calculate_specificity,
|
|
1342
|
+
:expand_margin, :expand_padding, :parse_four_sides, :split_on_whitespace,
|
|
1343
|
+
:expand_border, :expand_border_side, :expand_border_width, :expand_border_style,
|
|
1344
|
+
:expand_border_color, :parse_border_value, :is_border_width?, :is_border_style?,
|
|
1345
|
+
:expand_font, :is_font_size?, :is_font_style?, :is_font_variant?, :is_font_weight?,
|
|
1346
|
+
:expand_background, :starts_with_url?, :is_position_value?, :expand_list_style,
|
|
1347
|
+
:recreate_shorthands!, :recreate_margin!, :recreate_padding!, :check_all_same?,
|
|
1348
|
+
:recreate_border!, :recreate_border_width!, :recreate_border_style!, :recreate_border_color!,
|
|
1349
|
+
:optimize_four_sides, :recreate_font!, :recreate_background!, :recreate_list_style!,
|
|
1350
|
+
:update_selector_lists_for_divergence!, :declarations_equal?
|
|
1239
1351
|
end
|
|
1240
1352
|
end
|