Module: Habaki::Shorthand

Included in:
Declarations
Defined in:
lib/habaki/declarations.rb

Overview

Constant Summary collapse

BORDER_PROPERTIES =
%w[border border-left border-right border-top border-bottom].freeze
DIMENSIONS =
[
  ['margin', %w[margin-top margin-right margin-bottom margin-left]],
  ['padding', %w[padding-top padding-right padding-bottom padding-left]],
  ['border-color', %w[border-top-color border-right-color border-bottom-color border-left-color]],
  ['border-style', %w[border-top-style border-right-style border-bottom-style border-left-style]],
  ['border-width', %w[border-top-width border-right-width border-bottom-width border-left-width]]
].freeze
PRECOMPUTED_SHORTHAND_PROPS =
{
  "background" => shorthand_properties("background"),
  "font" => shorthand_properties("font"),
  "list-style" => shorthand_properties("list-style"),
  "border" => shorthand_properties("border"),
  "border-top" => shorthand_properties("border-top"),
  "border-bottom" => shorthand_properties("border-bottom"),
  "border-left" => shorthand_properties("border-left"),
  "border-right" => shorthand_properties("border-right"),
  "border-width" => shorthand_properties("border-width"),
  "border-style" => shorthand_properties("border-style"),
  "border-color" => shorthand_properties("border-color"),
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.shorthand_properties(shorthand_property) ⇒ Object



163
164
165
166
167
168
169
# File 'lib/habaki/declarations.rb', line 163

def self.shorthand_properties(shorthand_property)
  nodes = []
  FormalSyntax::Tree.tree.property("--shorthand-"+shorthand_property).traverse do |node|
    nodes << {type: node.type, value: node.value, next_value: node.parent&.children&.last&.value} if [:ref, :token].include?(node.type)
  end
  nodes
end

Instance Method Details

#compute_dimensions_shorthand(values) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/habaki/declarations.rb', line 245

def compute_dimensions_shorthand(values)
  # All four sides are equal, returning single value
  return [:top] if values.values.uniq.count == 1

  # `/* top | right | bottom | left */`
  return [:top, :right, :bottom, :left] if values[:left] != values[:right]

  # Vertical are the same & horizontal are the same, `/* vertical | horizontal */`
  return [:top, :left] if values[:top] == values[:bottom]

  [:top, :left, :bottom]
end

#create_background_shorthand!Object

Looks for long format CSS background properties (e.g. background-color) and converts them into a shorthand CSS background property.

Leaves properties declared !important alone.



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/habaki/declarations.rb', line 136

def create_background_shorthand! # :nodoc:
  # When we have a background-size property we must separate it and distinguish it from
  # background-position by preceding it with a backslash. In this case we also need to
  # have a background-position property, so we set it if it's missing.
  # http://www.w3schools.com/cssref/css3_pr_background.asp
  if (declaration = find_by_property('background-size')) && !declaration.important
    add_by_property('background-position', Values.new([Percentage.new(0), Percentage.new(0)]))
  end

  create_shorthand_properties! 'background'
end

#create_border_shorthand!Object

Combine border-color, border-style and border-width into border Should be run after create_dimensions_shorthand!



122
123
124
125
126
127
128
129
130
# File 'lib/habaki/declarations.rb', line 122

def create_border_shorthand! # :nodoc:
  border_style_properties = %w[border-width border-style border-color]

  border_style_properties.each do |prop|
    create_shorthand_properties! prop unless has_property?(prop)
  end

  create_shorthand_properties! 'border' if border_style_properties.map{|prop| has_property?(prop)}.all?
end

#create_dimensions_shorthand!Object

Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width) and converts them into shorthand CSS properties.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/habaki/declarations.rb', line 219

def create_dimensions_shorthand! # :nodoc:
  return if length < 4

  DIMENSIONS.each do |property, dimensions|
    values = [:top, :right, :bottom, :left].each_with_index.with_object({}) do |(side, index), result|
      next unless (declaration = find_by_property(dimensions[index]))
      result[side] = declaration.value
    end

    # All four dimensions must be present
    next if values.length != dimensions.length

    new_values = Values.new(values.values_at(*compute_dimensions_shorthand(values)))
    unless new_values.empty?
      first_position = find_by_property(dimensions.first)&.position
      decl = add_by_property(property, new_values)
      decl.position = first_position
    end

    # Delete the longhand values
    dimensions.each do |prop|
      remove_by_property(prop)
    end
  end
end

#create_font_shorthand!Object

Looks for long format CSS font properties (e.g. font-weight) and tries to convert them into a shorthand CSS font property. All font properties must be present in order to create a shorthand declaration.



151
152
153
# File 'lib/habaki/declarations.rb', line 151

def create_font_shorthand! # :nodoc:
  create_shorthand_properties!("font", true)
end

#create_list_style_shorthand!Object

Looks for long format CSS list-style properties (e.g. list-style-type) and converts them into a shorthand CSS list-style property.

Leaves properties declared !important alone.



159
160
161
# File 'lib/habaki/declarations.rb', line 159

def create_list_style_shorthand! # :nodoc:
  create_shorthand_properties! 'list-style'
end

#create_shorthand!Object

Create shorthand declarations (e.g. margin or font) whenever possible.



111
112
113
114
115
116
117
118
# File 'lib/habaki/declarations.rb', line 111

def create_shorthand!
  create_background_shorthand!
  create_dimensions_shorthand!
  # border must be shortened after dimensions
  create_border_shorthand!
  create_font_shorthand!
  create_list_style_shorthand!
end

#create_shorthand_properties!(shorthand_property, need_all = false) ⇒ Object

Combine several properties into a shorthand one



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/habaki/declarations.rb', line 186

def create_shorthand_properties!(shorthand_property, need_all = false)
  properties_to_delete = []
  new_values = []

  PRECOMPUTED_SHORTHAND_PROPS[shorthand_property].each do |node|
    case node[:type]
    when :ref
      decl = find_by_property(node[:value])
      if decl
        properties_to_delete << decl.property
        new_values += decl.values
      else
        return if need_all
      end
    when :token
      # only if next node property is present (line-height, background-size)
      new_values << Operator.new(node[:value]) if node[:next_value] && has_property?(node[:next_value])
    end
  end

  return if new_values.empty?

  first_position = find_by_property(properties_to_delete.first)&.position
  properties_to_delete.each do |property|
    remove_by_property(property)
  end

  new_decl = add_by_property(shorthand_property, new_values)
  new_decl.position = first_position
end

#expand_background_shorthand!Object

Convert shorthand background declarations (e.g. background: url("chess.png") gray 50% repeat fixed;) into their constituent parts.

See www.w3.org/TR/CSS21/colors.html#propdef-background



69
70
71
# File 'lib/habaki/declarations.rb', line 69

def expand_background_shorthand! # :nodoc:
  expand_shorthand_properties!("background")
end

#expand_border_shorthand!Object

Split shorthand border declarations (e.g. border: 1px red;) Additional splitting happens in expand_dimensions_shorthand!



26
27
28
29
30
# File 'lib/habaki/declarations.rb', line 26

def expand_border_shorthand! # :nodoc:
  BORDER_PROPERTIES.each do |k|
    expand_shorthand_properties!(k)
  end
end

#expand_dimensions_shorthand!Object

Split shorthand dimensional declarations (e.g. margin: 0px auto;) into their constituent parts. Handles margin, padding, border-color, border-style and border-width.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/habaki/declarations.rb', line 34

def expand_dimensions_shorthand! # :nodoc:
  DIMENSIONS.each do |property, (top, right, bottom, left)|
    next unless (declaration = find_by_property(property))

    case declaration.values.length
    when 1
      values = declaration.values * 4
    when 2
      values = declaration.values * 2
    when 3
      values = declaration.values
      values << declaration.values[1] # left = right
    when 4
      values = declaration.values
    else
      # "Habaki error: Cannot parse #{property}: #{declaration.values} (#{declaration.values.length} values)"
      next
    end

    replacement = [top, right, bottom, left].zip(values).to_h

    position = find_by_property(property)&.position
    remove_by_property(property)

    replacement.each do |short_prop, value|
      decl = add_by_property(short_prop, value)
      decl.position = position
    end
  end
end

#expand_font_shorthand!Object

Convert shorthand font declarations (e.g. font: 300 italic 11px/14px verdana, helvetica, sans-serif;) into their constituent parts.



75
76
77
# File 'lib/habaki/declarations.rb', line 75

def expand_font_shorthand! # :nodoc:
  expand_shorthand_properties!("font")
end

#expand_list_style_shorthand!Object

Convert shorthand list-style declarations (e.g. list-style: lower-alpha outside;) into their constituent parts.

See www.w3.org/TR/CSS21/generate.html#lists



83
84
85
# File 'lib/habaki/declarations.rb', line 83

def expand_list_style_shorthand! # :nodoc:
  expand_shorthand_properties!("list-style")
end

#expand_shorthand!Object

Split shorthand declarations (e.g. margin or font) into their constituent parts.



15
16
17
18
19
20
21
22
# File 'lib/habaki/declarations.rb', line 15

def expand_shorthand!
  # border must be expanded before dimensions
  expand_border_shorthand!
  expand_dimensions_shorthand!
  expand_font_shorthand!
  expand_background_shorthand!
  expand_list_style_shorthand!
end

#expand_shorthand_properties!(property) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/habaki/declarations.rb', line 87

def expand_shorthand_properties!(property)
  return unless (declaration = find_by_property(property))

  tmp_decl = Declaration.new("--shorthand-"+declaration.property, declaration.important)
  tmp_decl.values = declaration.values
  matcher = FormalSyntax::Matcher.new(tmp_decl)
  return unless matcher.match?

  props = {}
  matcher.matches.each do |match|
    next if match.value == Operator.new("/") # font-size/line-height
    props[match.reference] ||= Values.new
    props[match.reference] << match.value
  end

  props.each do |prop, values|
    new_decl = add_by_property(prop, values)
    new_decl.position = declaration.position
  end

  remove_by_property(property)
end