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,78 @@
1
+
2
+
3
+ module CssTidy
4
+ class Tidy
5
+ attr_reader :input_size, :output_size
6
+
7
+ def initialize
8
+ @parser = Parser.new
9
+ end
10
+
11
+ def tidy(css, opts={})
12
+ @input_size = css.length
13
+ options = {
14
+ :downcase_selectors => true,
15
+ :downcase_properties => true,
16
+ :keep_special_comments => true, # comments that start with a !
17
+ :keep_ie5_comment_hack => true, # ie5 comment hack => /*\*/ stuff /**/
18
+ :keep_ie7_selector_comments => true, # empty comments in selectors => /**/ used for IE7 hack
19
+ :keep_selector_comments => false, # other comments in selectors
20
+ :keep_comments => false, # any other comments
21
+ :optimize_colors => true,
22
+ :fix_invalid_colors => false,
23
+ :optimize_decimals => true,
24
+ :optimize_zeros => true,
25
+ :optimize_font_weight => true,
26
+ :optimize_margin_padding => true,
27
+ :optimize_filters => true,
28
+ :optimize_urls => true,
29
+ :optimize_selectors => false,
30
+ :format => 0,
31
+ :line_length => 0,
32
+ }.merge(opts)
33
+
34
+ @stylesheet = @parser.parse(css)
35
+ optimize(options)
36
+
37
+ case options[:format]
38
+ when 1
39
+ format = :multi_line
40
+ else
41
+ format = :one_line
42
+ end
43
+
44
+ compressed_css = @stylesheet.to_s(format)
45
+
46
+ if (options[:line_length] > 0 && format == :one_line)
47
+ compressed_css = split_lines(compressed_css, options[:line_length])
48
+ end
49
+
50
+ @output_size = compressed_css.length
51
+
52
+ compressed_css
53
+ end
54
+
55
+ def optimize(options)
56
+ @stylesheet.optimize(options)
57
+ end
58
+
59
+ def split_lines(compressed_css, line_length)
60
+ css = compressed_css.clone
61
+ # Some source control tools don't like it when files containing lines longer
62
+ # than, say 8000 characters, are checked in. The linebreak option is used in
63
+ # that case to split long lines after a specific column.
64
+ startIndex = 0
65
+ index = 0
66
+ length = css.length
67
+ while (index < length)
68
+ index += 1
69
+ if (css[index - 1,1] === '}' && index - startIndex > line_length)
70
+ css = css.slice(0, index) + "\n" + css.slice(index, length)
71
+ startIndex = index
72
+ end
73
+ end
74
+ css
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,224 @@
1
+ # This is a Ruby port of the YUI CSS compressor
2
+ # See LICENSE for license information
3
+
4
+ module YuiCompressor
5
+ # Compress CSS rules using a variety of techniques
6
+
7
+ class Yui
8
+ attr_reader :input_size, :output_size
9
+ def initialize()
10
+ @preservedTokens = []
11
+ @comments = []
12
+ @input_size = 0
13
+ @output_size = 0
14
+ end
15
+
16
+ def compress(css, line_length=0)
17
+ @input_size = css.length
18
+
19
+ css = process_comments_and_strings(css)
20
+
21
+ # Normalize all whitespace strings to single spaces. Easier to work with that way.
22
+ css.gsub!(/\s+/, ' ')
23
+
24
+ # Remove the spaces before the things that should not have spaces before them.
25
+ # But, be careful not to turn "p :link {...}" into "p:link{...}"
26
+ # Swap out any pseudo-class colons with the token, and then swap back.
27
+ css.gsub!(/(?:^|\})[^\{:]+\s+:+[^\{]*\{/) do |match|
28
+ match.gsub(':', '___PSEUDOCLASSCOLON___')
29
+ end
30
+ css.gsub!(/\s+([!\{\};:>+\(\)\],])/, '\1')
31
+ css.gsub!(/([!\{\}:;>+\(\[,])\s+/, '\1')
32
+ css.gsub!('___PSEUDOCLASSCOLON___', ':')
33
+
34
+ # special case for IE
35
+ css.gsub!(/:first-(line|letter)(\{|,)/, ':first-\1 \2')
36
+
37
+ # no space after the end of a preserved comment
38
+ css.gsub!(/\*\/ /, '*/')
39
+
40
+ # If there is a @charset, then only allow one, and push to the top of the file.
41
+ css.gsub!(/^(.*)(@charset "[^"]*";)/i, '\2\1')
42
+ css.gsub!(/^(\s*@charset [^;]+;\s*)+/i, '\1')
43
+
44
+ # Put the space back in some cases, to support stuff like
45
+ # @media screen and (-webkit-min-device-pixel-ratio:0){
46
+ css.gsub!(/\band\(/i, "and (")
47
+
48
+ # remove unnecessary semicolons
49
+ css.gsub!(/;+\}/, '}')
50
+
51
+ # Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0.
52
+ css.gsub!(/([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)/i, '\1\2')
53
+
54
+ # Replace 0 0 0 0; with 0.
55
+ css.gsub!(/:(?:0 )+0(;|\})/, ':0\1')
56
+
57
+ # Restore background-position:0 0; if required
58
+ css.gsub!(/background-position:0(;|\})/i, 'background-position:0 0\1')
59
+
60
+ # Replace 0.6 with .6, but only when preceded by : or a space.
61
+ css.gsub!(/(:|\s)0+\.(\d+)/, '\1.\2')
62
+
63
+ # Shorten colors from rgb(51,102,153) to #336699
64
+ # This makes it more likely that it'll get further compressed in the next step.
65
+ css.gsub!(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match|
66
+ '#' << $1.scan(/\d+/).map{|n| n.to_i.to_s(16).rjust(2, '0') }.join
67
+ end
68
+
69
+ # Shorten colors from #AABBCC to #ABC. Note that we want to make sure
70
+ # the color is not preceded by either ", " or =. Indeed, the property
71
+ # filter: chroma(color="#FFFFFF");
72
+ # would become
73
+ # filter: chroma(color="#FFF");
74
+ # which makes the filter break in IE.
75
+ css.gsub!(/([^"'=\s])(\s?)\s*#([0-9a-f])\3([0-9a-f])\4([0-9a-f])\5/i, '\1\2#\3\4\5')
76
+
77
+ # shorter opacity IE filter
78
+ css.gsub!(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/i, "alpha(opacity=")
79
+
80
+ # Remove empty rules.
81
+ css.gsub!(/[^\};\{\/]+\{\}/, '')
82
+
83
+ if (line_length > 0)
84
+ # Some source control tools don't like it when files containing lines longer
85
+ # than, say 8000 characters, are checked in. The linebreak option is used in
86
+ # that case to split long lines after a specific column.
87
+ startIndex = 0
88
+ index = 0
89
+ length = css.length
90
+ while (index < length)
91
+ index += 1
92
+ if (css[index - 1,1] === '}' && index - startIndex > line_length)
93
+ css = css.slice(0, index) + "\n" + css.slice(index, length)
94
+ startIndex = index
95
+ end
96
+ end
97
+ end
98
+
99
+ # Replace multiple semi-colons in a row by a single one
100
+ # See SF bug #1980989
101
+ css.gsub!(/[;]+/, ';')
102
+
103
+ #restore preserved comments and strings
104
+ css = restore_preserved_comments_and_strings(css)
105
+
106
+ # top and tail whitespace
107
+ css.strip!
108
+
109
+ @output_size = css.length
110
+ css
111
+ end
112
+
113
+ def process_comments_and_strings(css_text)
114
+ css = css_text.clone
115
+
116
+ startIndex = 0
117
+ endIndex = 0
118
+ i = 0
119
+ max = 0
120
+ token = ''
121
+ totallen = css.length
122
+ placeholder = ''
123
+
124
+ # collect all comment blocks
125
+ while (startIndex = css.index(/\/\*/, startIndex))
126
+ endIndex = css.index(/\*\//, startIndex + 2)
127
+ unless endIndex
128
+ endIndex = totallen
129
+ end
130
+ token = css.slice(startIndex+2..endIndex-1)
131
+ @comments.push(token)
132
+ css = css.slice(0..startIndex+1).to_s + "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (@comments.length - 1).to_s + "___" + css.slice(endIndex, totallen).to_s
133
+ startIndex += 2
134
+ end
135
+
136
+ # preserve strings so their content doesn't get accidentally minified
137
+ css.gsub!(/("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/) do |match|
138
+ quote = match[0,1]
139
+ string = match.slice(1..-2)
140
+
141
+ # maybe the string contains a comment-like substring?
142
+ # one, maybe more? put'em back then
143
+ if string =~ /___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_/
144
+ @comments.each_index do |index|
145
+ string.gsub!(/___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_#{index.to_s}___/, @comments[index])
146
+ end
147
+ end
148
+
149
+ # minify alpha opacity in filter strings
150
+ string.gsub!(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/i, "alpha(opacity=")
151
+ @preservedTokens.push(string)
152
+
153
+ quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (@preservedTokens.length - 1).to_s + "___" + quote
154
+ end
155
+
156
+ # used to jump one index in loop
157
+ ie5_hack = false
158
+ # strings are safe, now wrestle the comments
159
+ @comments.each_index do |index|
160
+ if ie5_hack
161
+ ie5_hack = false
162
+ next
163
+ end
164
+
165
+ token = @comments[index]
166
+ placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + index.to_s + "___"
167
+
168
+ # ! in the first position of the comment means preserve
169
+ # so push to the preserved tokens keeping the !
170
+ if (token[0,1] === "!")
171
+ @preservedTokens.push(token)
172
+ css.gsub!( /#{placeholder}/i, "___YUICSSMIN_PRESERVED_TOKEN_" + (@preservedTokens.length - 1).to_s + "___")
173
+ next
174
+ end
175
+
176
+ # \ in the last position looks like hack for Mac/IE5
177
+ # shorten that to /*\*/ and the next one to /**/
178
+ if (token[-1,1] === "\\")
179
+ @preservedTokens.push("\\")
180
+ css.gsub!( /#{placeholder}/, "___YUICSSMIN_PRESERVED_TOKEN_" + (@preservedTokens.length - 1).to_s + "___")
181
+ # keep the next comment but remove its content
182
+ @preservedTokens.push("")
183
+ css.gsub!(/___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_#{index+1}___/, "___YUICSSMIN_PRESERVED_TOKEN_" + (@preservedTokens.length - 1).to_s + "___")
184
+ ie5_hack = true
185
+ next
186
+ end
187
+
188
+ # keep empty comments after child selectors (IE7 hack)
189
+ # e.g. html >/**/ body
190
+ if ((token.length === 0) && (startIndex = css.index( /#{placeholder}/)))
191
+ if (startIndex > 2)
192
+ if (css[startIndex - 3,1] === '>')
193
+ @preservedTokens.push("")
194
+ css.gsub!(/#{placeholder}/, "___YUICSSMIN_PRESERVED_TOKEN_" + (@preservedTokens.length - 1).to_s + "___")
195
+ end
196
+ end
197
+ end
198
+
199
+ # in all other cases kill the comment
200
+ css.gsub!( /\/\*#{placeholder}\*\//, "")
201
+ end
202
+
203
+ css
204
+ end
205
+
206
+ def restore_preserved_comments_and_strings(clean_css)
207
+ css = clean_css.clone
208
+ css_length = css.length
209
+ @preservedTokens.each_index do |index|
210
+ # slice these back into place rather than regex, because
211
+ # complex nested strings cause the replacement to fail
212
+ placeholder = "___YUICSSMIN_PRESERVED_TOKEN_#{index}___"
213
+ startIndex = css.index(placeholder, 0)
214
+ next unless startIndex # skip if nil
215
+ endIndex = startIndex + placeholder.length
216
+
217
+ css = css.slice(0..startIndex-1).to_s + @preservedTokens[index] + css.slice(endIndex, css_length).to_s
218
+ end
219
+
220
+ css
221
+ end
222
+ end
223
+
224
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: css_toolkit
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 3
9
+ version: "1.3"
10
+ platform: ruby
11
+ authors:
12
+ - Richard Hulse
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-11 00:00:00 +12:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Provides some tools to minify and compress CSS files - Ruby ports of the YUI CSS Compressor and CSS Tidy
22
+ email: rhulse@paradise.net.nz
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/css_toolkit.rb
31
+ - lib/modules/css_base.rb
32
+ - lib/modules/css_comment.rb
33
+ - lib/modules/css_declaration.rb
34
+ - lib/modules/css_import.rb
35
+ - lib/modules/css_media_set.rb
36
+ - lib/modules/css_misc.rb
37
+ - lib/modules/css_parser.rb
38
+ - lib/modules/css_properties.rb
39
+ - lib/modules/css_rule_set.rb
40
+ - lib/modules/css_stylesheet.rb
41
+ - lib/modules/css_tidy.rb
42
+ - lib/modules/yui_compressor.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/rhulse/ruby-css-toolkit
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.7
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: CSS minification
77
+ test_files: []
78
+