css_toolkit 1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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