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.
@@ -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,17 @@
1
+ module CssTidy
2
+ class CssBase
3
+
4
+ def initialize(args)
5
+ end
6
+
7
+ def optimize(options)
8
+ end
9
+
10
+ def inspect(indent='')
11
+ end
12
+
13
+ def clear
14
+ end
15
+ end
16
+
17
+ 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