css_toolkit 1.3
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.
- data/lib/css_toolkit.rb +21 -0
- data/lib/modules/css_base.rb +17 -0
- data/lib/modules/css_comment.rb +42 -0
- data/lib/modules/css_declaration.rb +149 -0
- data/lib/modules/css_import.rb +24 -0
- data/lib/modules/css_media_set.rb +88 -0
- data/lib/modules/css_misc.rb +149 -0
- data/lib/modules/css_parser.rb +463 -0
- data/lib/modules/css_properties.rb +195 -0
- data/lib/modules/css_rule_set.rb +197 -0
- data/lib/modules/css_stylesheet.rb +119 -0
- data/lib/modules/css_tidy.rb +78 -0
- data/lib/modules/yui_compressor.rb +224 -0
- metadata +78 -0
data/lib/css_toolkit.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require 'modules/yui_compressor'
|
4
|
+
require 'modules/css_parser'
|
5
|
+
require 'modules/css_tidy'
|
6
|
+
|
7
|
+
module CssToolkit
|
8
|
+
include YuiCompressor
|
9
|
+
VERSION = "1.3"
|
10
|
+
|
11
|
+
def yui_compressor(css, line_length=0)
|
12
|
+
yui = Yui.new()
|
13
|
+
yui.compress(css, line_length)
|
14
|
+
end
|
15
|
+
|
16
|
+
def css_tidy(css, opts={})
|
17
|
+
tidy = Tidy.new()
|
18
|
+
tidy.tidy(css, line_length)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module CssTidy
|
2
|
+
|
3
|
+
class Comment < CssBase
|
4
|
+
attr_accessor :text, :printable
|
5
|
+
|
6
|
+
def initialize(text='')
|
7
|
+
@text = text
|
8
|
+
@printable = true
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def << (text)
|
13
|
+
@text << text
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s(format=nil)
|
17
|
+
if @printable
|
18
|
+
"/*#{@text}*/"
|
19
|
+
else
|
20
|
+
''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# special comments start with a ! and should not be deleted
|
25
|
+
def is_special?
|
26
|
+
text[0,1] == '!'
|
27
|
+
end
|
28
|
+
|
29
|
+
# looks for a \ at the end of the comment, indicating
|
30
|
+
# that it is a the start of an IE5 hack
|
31
|
+
# why anyone is still doing this, is beyond me! :-)
|
32
|
+
def is_ie5_hack?
|
33
|
+
text[-1,1] == '\\'
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
@text = ''
|
38
|
+
@printable = false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module CssTidy
|
2
|
+
|
3
|
+
class Declaration < CssBase
|
4
|
+
attr_accessor :property, :value
|
5
|
+
|
6
|
+
# sets the values
|
7
|
+
# we assume that any extraneous whitespace has been striped
|
8
|
+
def initialize(property, value)
|
9
|
+
@property = property
|
10
|
+
@value = value
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s(format=:one_line)
|
15
|
+
unless @property.empty? && @value.empty?
|
16
|
+
"#{@property}:#{@value}"
|
17
|
+
else
|
18
|
+
''
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def optimize_colors
|
23
|
+
# Shorten colors from rgb(51,102,153) to #336699
|
24
|
+
# This makes it more likely that it'll get further compressed in the next step.
|
25
|
+
@value.gsub!(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match|
|
26
|
+
'#' << $1.scan(/\d+/).map{|n| n.to_i.to_s(16).rjust(2, '0') }.join
|
27
|
+
end
|
28
|
+
|
29
|
+
# Shorten colors from #AABBCC to #ABC. Note that we want to make sure
|
30
|
+
# the color is not preceded by either ", " or =. Indeed, the property
|
31
|
+
# filter: chroma(color="#FFFFFF");
|
32
|
+
# would become
|
33
|
+
# filter: chroma(color="#FFF");
|
34
|
+
# which makes the filter break in IE.
|
35
|
+
if @value !~ /["'=]/
|
36
|
+
@value.gsub!(/#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3/i, '#\1\2\3')
|
37
|
+
CssTidy::SHORTEN_COLORS.each do |from,to|
|
38
|
+
@value.gsub!(/#{from}/i, to)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def fix_invalid_colors
|
44
|
+
if @value !~ /["'=]/
|
45
|
+
CssTidy::INVALID_COLORS.each do |from,to|
|
46
|
+
@value.gsub!(/#{from}/i, to)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def optimize_zeros
|
52
|
+
# Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0.
|
53
|
+
@value.gsub!(/(\s|\A)([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)/i, '\1\2')
|
54
|
+
|
55
|
+
if @property =~ /background(-position)?/i
|
56
|
+
@value.gsub!(/(0 )+0/, '0 0')
|
57
|
+
else
|
58
|
+
# Replace 0 0 0 0; with 0.
|
59
|
+
@value.gsub!(/\A(?:0 )+0\Z/, '0')
|
60
|
+
end
|
61
|
+
# Replace 0.6 with .6, but only when it is the first rule or preceded by a space.
|
62
|
+
@value.gsub!(/(\s|\A)0+\.(\d+)/, '\1.\2')
|
63
|
+
end
|
64
|
+
|
65
|
+
def optimize_filters
|
66
|
+
# shorter opacity IE filter
|
67
|
+
@value.gsub!(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/i, "alpha(opacity=")
|
68
|
+
end
|
69
|
+
|
70
|
+
def optimize_punctuation
|
71
|
+
@value.gsub!(/\s+([!+\(\)\],])/, '\1')
|
72
|
+
@value.gsub!(/([!+\(\[,])\s+/, '\1')
|
73
|
+
end
|
74
|
+
|
75
|
+
def optimize_font_weight
|
76
|
+
if @property =~ /font/
|
77
|
+
@value.gsub!(/bold/, '700')
|
78
|
+
@value.gsub!(/normal/, '400')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def optimize_mp_shorthands
|
83
|
+
if SHORTHANDS.has_key?(@property)
|
84
|
+
important = important! ? '!important' : ''
|
85
|
+
values = @value.split(/\s+/)
|
86
|
+
values
|
87
|
+
case values.length
|
88
|
+
when 4
|
89
|
+
# 4 to 1 margin:5px 5px 5px 5px => margin:5px
|
90
|
+
if values[0] == values[1] && values[0] == values[2] && values[0] == values[3]
|
91
|
+
@value = "#{values[0]}"
|
92
|
+
# 4 to 3 margin:0 0 10px 0 => margin:0 0 10px
|
93
|
+
elsif values[0] == values[2] && values[1] == values[3]
|
94
|
+
@value = "#{values[0]} #{values[1]}"
|
95
|
+
# 4 to 2 margin:5px 0 5px 0 => margin:5px 0
|
96
|
+
elsif values[1] == values[3]
|
97
|
+
@value = "#{values[0]} #{values[1]} #{values[2]}"
|
98
|
+
end
|
99
|
+
when 3
|
100
|
+
# 3 to 1 margin:5px 5px 5px => margin:5px
|
101
|
+
if values[0] == values[1] && values[0] == values[2]
|
102
|
+
@value = "#{values[0]}"
|
103
|
+
# 3 to 2 margin:0 10px 0 => margin:0 10px
|
104
|
+
elsif values[0] == values[2]
|
105
|
+
@value = "#{values[0]} #{values[1]}"
|
106
|
+
end
|
107
|
+
when 2
|
108
|
+
# 2 to 1 margin:5px 5px => margin:5px
|
109
|
+
if values[0] == values[1]
|
110
|
+
@value = "#{values[0]}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
@value << important
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def optimize_urls
|
118
|
+
# remove the quotes - they are optional
|
119
|
+
@value.gsub!(/url\(('|")(.+?)('|")\)/, 'url(\2)')
|
120
|
+
end
|
121
|
+
|
122
|
+
def downcase_property
|
123
|
+
@property.downcase! unless @property.frozen?
|
124
|
+
end
|
125
|
+
|
126
|
+
def == (other)
|
127
|
+
@property == other.property && @value == other.value
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect(indent='')
|
131
|
+
puts indent + @property + ':' + @value
|
132
|
+
end
|
133
|
+
|
134
|
+
# remove !important and return true if it was replaced
|
135
|
+
def important!
|
136
|
+
@value.gsub!(/!important/, '')
|
137
|
+
end
|
138
|
+
|
139
|
+
def important?
|
140
|
+
@value =~ /!important/
|
141
|
+
end
|
142
|
+
|
143
|
+
def clear
|
144
|
+
@property = ''
|
145
|
+
@value = ''
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CssTidy
|
2
|
+
|
3
|
+
class Import < CssBase
|
4
|
+
attr_accessor :import
|
5
|
+
|
6
|
+
def initialize(text='')
|
7
|
+
@import = text
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s(format=nil)
|
12
|
+
unless @import.empty?
|
13
|
+
"@import #{@import};"
|
14
|
+
else
|
15
|
+
''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear
|
20
|
+
@import = ''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module CssTidy
|
2
|
+
|
3
|
+
# media sets live as nodes in a stylesheet
|
4
|
+
# when a set is added to a style sheet, the sheet
|
5
|
+
# adds all new nodes to the last mediaset
|
6
|
+
#
|
7
|
+
class MediaSet < CssBase
|
8
|
+
attr_accessor :at_media
|
9
|
+
|
10
|
+
def initialize(at_media)
|
11
|
+
# nodes can contain:
|
12
|
+
# * ruleset
|
13
|
+
# * comment
|
14
|
+
@nodes = []
|
15
|
+
@at_media = at_media
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def << (object)
|
20
|
+
@nodes << object
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s(format=:one_line, indent='')
|
24
|
+
css = "#{@at_media}{" + ((format == :multi_line) ? "\n" : '')
|
25
|
+
@nodes.each do |node|
|
26
|
+
css << indent + node.to_s(format) + ((format == :multi_line) ? "\n" : '')
|
27
|
+
end
|
28
|
+
css << '}' + ((format == :multi_line) ? "\n" : '')
|
29
|
+
css
|
30
|
+
end
|
31
|
+
|
32
|
+
def optimize(options)
|
33
|
+
return nil if @nodes.empty? || @at_media.empty?
|
34
|
+
# clean up self first
|
35
|
+
@at_media.gsub!(/\*\/\s+\/\*/, '*//*')
|
36
|
+
|
37
|
+
# then do kids
|
38
|
+
keep_next_comment = false
|
39
|
+
|
40
|
+
@nodes.each_with_index do |node, idx|
|
41
|
+
if node.class == CssTidy::Comment
|
42
|
+
if node.is_special? && options[:keep_special_comments]
|
43
|
+
next # do nothing
|
44
|
+
elsif node.is_ie5_hack? && options[:keep_ie5_comment_hack]
|
45
|
+
node.text = '\\' # replace it
|
46
|
+
keep_next_comment = true
|
47
|
+
elsif keep_next_comment
|
48
|
+
node.text = '' # replace it
|
49
|
+
keep_next_comment = false
|
50
|
+
else
|
51
|
+
node.printable = false # don't print this one
|
52
|
+
end
|
53
|
+
end
|
54
|
+
node.optimize(options)
|
55
|
+
end
|
56
|
+
|
57
|
+
if options[:optimize_selectors]
|
58
|
+
nodes_to_remove = []
|
59
|
+
length = @nodes.length
|
60
|
+
@nodes.each_with_index do |node, index|
|
61
|
+
if node.class == CssTidy::RuleSet
|
62
|
+
idx = index
|
63
|
+
# Check if properties also exist in another RuleSet
|
64
|
+
while idx < length -1
|
65
|
+
idx += 1 # start at the next one
|
66
|
+
# just Rulsets
|
67
|
+
if @nodes[idx].class == CssTidy::RuleSet
|
68
|
+
if ! node.empty? && node == @nodes[idx]
|
69
|
+
node += @nodes[idx]
|
70
|
+
nodes_to_remove << idx
|
71
|
+
@nodes[idx].clear
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear
|
82
|
+
@nodes = []
|
83
|
+
@at_media = ''
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module CssTidy
|
2
|
+
|
3
|
+
SHORTEN_COLORS = {
|
4
|
+
'white' => '#fff',
|
5
|
+
'black' => '#000',
|
6
|
+
'fuchsia' => '#f0f',
|
7
|
+
'yellow' => '#ff0',
|
8
|
+
'#f00' => 'red',
|
9
|
+
'#800000' => 'maroon',
|
10
|
+
'#ffa500' => 'orange',
|
11
|
+
'#808000' => 'olive',
|
12
|
+
'#800080' => 'purple',
|
13
|
+
'#008000' => 'green',
|
14
|
+
'#000080' => 'navy',
|
15
|
+
'#008080' => 'teal',
|
16
|
+
'#c0c0c0' => 'silver',
|
17
|
+
'#808080' => 'gray'
|
18
|
+
}
|
19
|
+
|
20
|
+
# A list of non-W3C color names which get replaced by their hex-codes
|
21
|
+
INVALID_COLORS = {
|
22
|
+
'aliceblue' => '#F0F8FF',
|
23
|
+
'antiquewhite' => '#FAEBD7',
|
24
|
+
'aquamarine' => '#7FFFD4',
|
25
|
+
'azure' => '#F0FFFF',
|
26
|
+
'beige' => '#F5F5DC',
|
27
|
+
'bisque' => '#FFE4C4',
|
28
|
+
'blanchedalmond' => '#FFEBCD',
|
29
|
+
'blueviolet' => '#8A2BE2',
|
30
|
+
'brown' => '#A52A2A',
|
31
|
+
'burlywood' => '#DEB887',
|
32
|
+
'cadetblue' => '#5F9EA0',
|
33
|
+
'chartreuse' => '#7FFF00',
|
34
|
+
'chocolate' => '#D2691E',
|
35
|
+
'coral' => '#FF7F50',
|
36
|
+
'cornflowerblue' => '#6495ED',
|
37
|
+
'cornsilk' => '#FFF8DC',
|
38
|
+
'crimson' => '#DC143C',
|
39
|
+
'cyan' => '#00FFFF',
|
40
|
+
'darkblue' => '#00008B',
|
41
|
+
'darkcyan' => '#008B8B',
|
42
|
+
'darkgoldenrod' => '#B8860B',
|
43
|
+
'darkgray' => '#A9A9A9',
|
44
|
+
'darkgreen' => '#006400',
|
45
|
+
'darkkhaki' => '#BDB76B',
|
46
|
+
'darkmagenta' => '#8B008B',
|
47
|
+
'darkolivegreen' => '#556B2F',
|
48
|
+
'darkorange' => '#FF8C00',
|
49
|
+
'darkorchid' => '#9932CC',
|
50
|
+
'darkred' => '#8B0000',
|
51
|
+
'darksalmon' => '#E9967A',
|
52
|
+
'darkseagreen' => '#8FBC8F',
|
53
|
+
'darkslateblue' => '#483D8B',
|
54
|
+
'darkslategray' => '#2F4F4F',
|
55
|
+
'darkturquoise' => '#00CED1',
|
56
|
+
'darkviolet' => '#9400D3',
|
57
|
+
'deeppink' => '#FF1493',
|
58
|
+
'deepskyblue' => '#00BFFF',
|
59
|
+
'dimgray' => '#696969',
|
60
|
+
'dodgerblue' => '#1E90FF',
|
61
|
+
'feldspar' => '#D19275',
|
62
|
+
'firebrick' => '#B22222',
|
63
|
+
'floralwhite' => '#FFFAF0',
|
64
|
+
'forestgreen' => '#228B22',
|
65
|
+
'gainsboro' => '#DCDCDC',
|
66
|
+
'ghostwhite' => '#F8F8FF',
|
67
|
+
'gold' => '#FFD700',
|
68
|
+
'goldenrod' => '#DAA520',
|
69
|
+
'greenyellow' => '#ADFF2F',
|
70
|
+
'honeydew' => '#F0FFF0',
|
71
|
+
'hotpink' => '#FF69B4',
|
72
|
+
'indianred' => '#CD5C5C',
|
73
|
+
'indigo' => '#4B0082',
|
74
|
+
'ivory' => '#FFFFF0',
|
75
|
+
'khaki' => '#F0E68C',
|
76
|
+
'lavender' => '#E6E6FA',
|
77
|
+
'lavenderblush' => '#FFF0F5',
|
78
|
+
'lawngreen' => '#7CFC00',
|
79
|
+
'lemonchiffon' => '#FFFACD',
|
80
|
+
'lightblue' => '#ADD8E6',
|
81
|
+
'lightcoral' => '#F08080',
|
82
|
+
'lightcyan' => '#E0FFFF',
|
83
|
+
'lightgoldenrodyellow' => '#FAFAD2',
|
84
|
+
'lightgrey' => '#D3D3D3',
|
85
|
+
'lightgreen' => '#90EE90',
|
86
|
+
'lightpink' => '#FFB6C1',
|
87
|
+
'lightsalmon' => '#FFA07A',
|
88
|
+
'lightseagreen' => '#20B2AA',
|
89
|
+
'lightskyblue' => '#87CEFA',
|
90
|
+
'lightslateblue' => '#8470FF',
|
91
|
+
'lightslategray' => '#778899',
|
92
|
+
'lightsteelblue' => '#B0C4DE',
|
93
|
+
'lightyellow' => '#FFFFE0',
|
94
|
+
'limegreen' => '#32CD32',
|
95
|
+
'linen' => '#FAF0E6',
|
96
|
+
'magenta' => '#FF00FF',
|
97
|
+
'mediumaquamarine' => '#66CDAA',
|
98
|
+
'mediumblue' => '#0000CD',
|
99
|
+
'mediumorchid' => '#BA55D3',
|
100
|
+
'mediumpurple' => '#9370D8',
|
101
|
+
'mediumseagreen' => '#3CB371',
|
102
|
+
'mediumslateblue' => '#7B68EE',
|
103
|
+
'mediumspringgreen' => '#00FA9A',
|
104
|
+
'mediumturquoise' => '#48D1CC',
|
105
|
+
'mediumvioletred' => '#C71585',
|
106
|
+
'midnightblue' => '#191970',
|
107
|
+
'mintcream' => '#F5FFFA',
|
108
|
+
'mistyrose' => '#FFE4E1',
|
109
|
+
'moccasin' => '#FFE4B5',
|
110
|
+
'navajowhite' => '#FFDEAD',
|
111
|
+
'oldlace' => '#FDF5E6',
|
112
|
+
'olivedrab' => '#6B8E23',
|
113
|
+
'orangered' => '#FF4500',
|
114
|
+
'orchid' => '#DA70D6',
|
115
|
+
'palegoldenrod' => '#EEE8AA',
|
116
|
+
'palegreen' => '#98FB98',
|
117
|
+
'paleturquoise' => '#AFEEEE',
|
118
|
+
'palevioletred' => '#D87093',
|
119
|
+
'papayawhip' => '#FFEFD5',
|
120
|
+
'peachpuff' => '#FFDAB9',
|
121
|
+
'peru' => '#CD853F',
|
122
|
+
'pink' => '#FFC0CB',
|
123
|
+
'plum' => '#DDA0DD',
|
124
|
+
'powderblue' => '#B0E0E6',
|
125
|
+
'rosybrown' => '#BC8F8F',
|
126
|
+
'royalblue' => '#4169E1',
|
127
|
+
'saddlebrown' => '#8B4513',
|
128
|
+
'salmon' => '#FA8072',
|
129
|
+
'sandybrown' => '#F4A460',
|
130
|
+
'seagreen' => '#2E8B57',
|
131
|
+
'seashell' => '#FFF5EE',
|
132
|
+
'sienna' => '#A0522D',
|
133
|
+
'skyblue' => '#87CEEB',
|
134
|
+
'slateblue' => '#6A5ACD',
|
135
|
+
'slategray' => '#708090',
|
136
|
+
'snow' => '#FFFAFA',
|
137
|
+
'springgreen' => '#00FF7F',
|
138
|
+
'steelblue' => '#4682B4',
|
139
|
+
'tan' => '#D2B48C',
|
140
|
+
'thistle' => '#D8BFD8',
|
141
|
+
'tomato' => '#FF6347',
|
142
|
+
'turquoise' => '#40E0D0',
|
143
|
+
'violet' => '#EE82EE',
|
144
|
+
'violetred' => '#D02090',
|
145
|
+
'wheat' => '#F5DEB3',
|
146
|
+
'whitesmoke' => '#F5F5F5',
|
147
|
+
'yellowgreen' => '#9ACD32',
|
148
|
+
}
|
149
|
+
end
|