csso-rails 0.0.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -6,4 +6,5 @@
6
6
  Gemfile.lock
7
7
  tmp
8
8
  .DS_Store
9
+ csso/
9
10
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
4
+
5
+ # gem "therubyracer"
data/README.md CHANGED
@@ -29,18 +29,26 @@ Please note than benchmark was taken in summer of 2012, since then things may ha
29
29
 
30
30
  ### In Rails 3.1+
31
31
  add `gem 'csso-rails'` to your gemfile, and that’s it!
32
+ (also you may want to add some javascript runtime for ExecJS to pick up, like `gem 'therubyracer'`)
32
33
 
33
34
  Upon including it becomes the default compressor even if sass is included too.
34
35
  More explicit way – set in config: `config.assets.css_compressor = :csso`.
35
36
 
37
+ ### Sprockets
38
+
39
+ If you use Sprockets without Rails:
40
+
41
+ ```ruby
42
+ require 'csso'
43
+ Csso::CssCompressor.register!
44
+ sprockets_env.css_compressor = :csso
45
+ ```
36
46
 
37
47
  ### In Plain Ruby
38
48
 
39
49
  ```ruby
40
- require 'rubygems'
41
- gem 'csso-rails'
42
- require 'csso'
43
- puts Csso.optimize("a{ color: #FF0000; }") # produces "a{color:red}"
50
+ require 'csso'
51
+ puts Csso.optimize("a{ color: #FF0000; }") # produces "a{color:red}"
44
52
  ```
45
53
 
46
54
  In _maniac mode_(`Csso.optimize(css, true)`, default for pipeline) CSS is processed several times until it stops getting lighter (there're cases when original csso does not do all optimizations for no reason).
data/Rakefile CHANGED
@@ -1,9 +1,45 @@
1
1
  require 'bundler'
2
2
  require 'bundler/setup'
3
- require "rspec/core/rake_task"
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:spec) do |t|
7
+ t.pattern = 'spec/**/*_spec.rb'
8
+ t.libs.push 'spec'
9
+ end
10
+
11
+ $:.push File.expand_path("../lib", __FILE__)
12
+ require 'csso/version'
4
13
 
5
14
  Bundler::GemHelper.install_tasks
6
- RSpec::Core::RakeTask.new(:spec)
7
15
 
8
16
  task :default => :spec
9
17
 
18
+
19
+ file 'csso' do
20
+ puts 'Fetching csso repo...'
21
+ `git clone --single-branch --depth 1 --no-hardlinks git://github.com/css/csso`
22
+ Dir.chdir('csso'){
23
+ puts 'Now making web-version, just in case.'
24
+ `rm web/csso.web.js; make web`
25
+ }
26
+ end
27
+
28
+ task :update_csso => :csso do
29
+ #??
30
+ Dir.chdir('csso'){
31
+ puts 'Updating csso...'
32
+ `git pull --rebase`
33
+ `rm web/csso.web.js; make web`
34
+ }
35
+ end
36
+
37
+ directory 'vendor/csso'
38
+ lib_template = 'lib/csso/csso.js.erb'
39
+ file Csso::CSSO_JS_LIB => [lib_template, 'csso', 'vendor/csso', 'csso/.git/HEAD', 'csso/.git/refs/heads/master'] do
40
+ `erb #{lib_template} > #{Csso::CSSO_JS_LIB}`
41
+ end
42
+
43
+ task :generate_files => [Csso::CSSO_JS_LIB]
44
+
45
+ task :build => :generate_files
data/csso-rails.gemspec CHANGED
@@ -15,17 +15,15 @@ Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "csso-rails"
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
- js_root = 'lib/csso/js'
19
- Dir.chdir(js_root) do
20
- s.files += `git ls-files`.split("\n").map {|f| File.join(js_root,f)}
21
- end
18
+ s.files += [Csso::CSSO_JS_LIB]
19
+
22
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
22
  s.require_paths = ["lib"]
25
23
 
26
- s.add_dependency "therubyracer", ">= 0.10.2"
27
- s.add_dependency "commonjs", "~> 0.2.0"
24
+ #TODO: loosen execjs dependency?
25
+ s.add_dependency 'execjs', '~>1.4'
28
26
 
29
- s.add_development_dependency "rake"
30
- s.add_development_dependency "rspec", "~> 2.0"
27
+ s.add_development_dependency 'rake'
28
+ s.add_development_dependency 'minitest'
31
29
  end
data/lib/csso-rails.rb CHANGED
@@ -1,7 +1,2 @@
1
1
  require 'csso'
2
-
3
- if defined?(Rails)
4
- require 'csso/rails'
5
- else
6
- require 'csso'
7
- end
2
+ require 'csso/rails' if defined?(Rails)
data/lib/csso.rb CHANGED
@@ -1,12 +1,10 @@
1
- require 'v8'
2
1
  require 'csso/version'
3
- require 'csso/utils'
4
- require 'csso/loader'
2
+ require 'csso/js_lib'
3
+ require 'csso/compressor'
5
4
 
6
5
  module Csso
7
6
 
8
- @loader = Csso::Loader.new
9
- @csso = @loader.require('cssoapi')
7
+ @csso = Csso::JsLib.new
10
8
 
11
9
  def self.js_api
12
10
  @csso
@@ -28,14 +26,10 @@ module Csso
28
26
 
29
27
 
30
28
  class Optimizer
31
- include CallJS
32
-
33
29
  def optimize(css, structural_optimization=true)
34
30
  return nil unless css.is_a?(String)
35
31
  return css if css.size <= 3
36
- calljs do
37
- Csso.js_api['justDoIt'].call(css, !structural_optimization)
38
- end
32
+ Csso.js_api.compress(css, structural_optimization)
39
33
  end
40
34
  end
41
35
 
data/lib/csso/rails.rb CHANGED
@@ -6,7 +6,7 @@ module Csso
6
6
 
7
7
  class Railtie < ::Rails::Railtie
8
8
  initializer "csso.environment", :after => "sprockets.environment" do
9
- CssCompressor.register
9
+ CssCompressor.register!
10
10
  end
11
11
 
12
12
  # saas-rails-3.2.4(and may be others) sets itself as default, ignoring config? => override :(
@@ -18,20 +18,4 @@ module Csso
18
18
 
19
19
  end
20
20
 
21
- class CssCompressor
22
- def compress(css)
23
- require 'csso'
24
- #TODO: settings?
25
- Csso.optimize(css, true)
26
- end
27
-
28
- def self.register
29
- if Sprockets.respond_to? :register_compressor
30
- Sprockets.register_compressor('text/css', COMPRESSOR_SYM, 'Csso::CssCompressor')
31
- else
32
- Sprockets::Compressors.register_css_compressor(COMPRESSOR_SYM, 'Csso::CssCompressor', :default => true)
33
- end
34
- end
35
- end
36
-
37
21
  end
data/lib/csso/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Csso
2
- VERSION = '0.0.3'
3
- CSSO_VERSION = '1.3.7'
2
+ VERSION = '0.2.0'
3
+ CSSO_JS_LIB = 'vendor/csso/csso.js'
4
4
  end
@@ -1,28 +1,35 @@
1
- require 'spec_helper'
1
+ require 'minitest/autorun'
2
+ require 'csso'
2
3
 
3
4
  describe Csso do
4
5
 
6
+ subject { Csso }
7
+
8
+ it "dummy test" do
9
+ 1.must_equal 1
10
+ end
11
+
5
12
  it "should optimize css" do
6
- subject.optimize("a {\ncolor: white; }").should == "a{color:#fff}"
13
+ subject.optimize("a {\ncolor: white; }").must_equal "a{color:#fff}"
7
14
  end
8
15
 
9
16
  it "should optimize structure" do
10
- subject.optimize("a {\ncolor: white; } a{color: red;}").should == "a{color:red}"
17
+ subject.optimize("a {\ncolor: white; } a{color: red;}").must_equal "a{color:red}"
11
18
  end
12
19
 
13
20
  it "should optimize structure" do
14
- pending "original csso is a bit broken at the moment"
21
+ skip "original csso is a bit broken at the moment"
15
22
  # FIXME: csso produces "a{color:#fff;color:red}" on this :(
16
- subject.optimize("a {\ncolor: white; } a{color: #ff0000;}").should == "a{color:red}"
23
+ subject.optimize("a {\ncolor: white; } a{color: #ff0000;}").must_equal "a{color:red}"
17
24
  end
18
25
 
19
26
  it "should optimize structure in maniac mode" do
20
- subject.optimize("a {\ncolor: white; } a{color: #ff0000;}", true).should == "a{color:red}"
27
+ subject.optimize("a {\ncolor: white; } a{color: #ff0000;}", true).must_equal "a{color:red}"
21
28
  end
22
29
 
23
30
  it 'should produce no error on empty input' do
24
- subject.optimize(nil).should == nil
25
- subject.optimize("").should == ""
31
+ subject.optimize(nil).must_be_nil
32
+ subject.optimize("").must_equal ""
26
33
  end
27
34
 
28
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csso-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,32 +9,16 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-03 00:00:00.000000000 Z
12
+ date: 2013-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: therubyracer
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: 0.10.2
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: 0.10.2
30
- - !ruby/object:Gem::Dependency
31
- name: commonjs
15
+ name: execjs
32
16
  requirement: !ruby/object:Gem::Requirement
33
17
  none: false
34
18
  requirements:
35
19
  - - ~>
36
20
  - !ruby/object:Gem::Version
37
- version: 0.2.0
21
+ version: '1.4'
38
22
  type: :runtime
39
23
  prerelease: false
40
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +26,7 @@ dependencies:
42
26
  requirements:
43
27
  - - ~>
44
28
  - !ruby/object:Gem::Version
45
- version: 0.2.0
29
+ version: '1.4'
46
30
  - !ruby/object:Gem::Dependency
47
31
  name: rake
48
32
  requirement: !ruby/object:Gem::Requirement
@@ -60,21 +44,21 @@ dependencies:
60
44
  - !ruby/object:Gem::Version
61
45
  version: '0'
62
46
  - !ruby/object:Gem::Dependency
63
- name: rspec
47
+ name: minitest
64
48
  requirement: !ruby/object:Gem::Requirement
65
49
  none: false
66
50
  requirements:
67
- - - ~>
51
+ - - ! '>='
68
52
  - !ruby/object:Gem::Version
69
- version: '2.0'
53
+ version: '0'
70
54
  type: :development
71
55
  prerelease: false
72
56
  version_requirements: !ruby/object:Gem::Requirement
73
57
  none: false
74
58
  requirements:
75
- - - ~>
59
+ - - ! '>='
76
60
  - !ruby/object:Gem::Version
77
- version: '2.0'
61
+ version: '0'
78
62
  description: Invoke the CSSO from Ruby
79
63
  email:
80
64
  - vasilyfedoseyev@gmail.com
@@ -84,7 +68,6 @@ extensions: []
84
68
  extra_rdoc_files: []
85
69
  files:
86
70
  - .gitignore
87
- - .gitmodules
88
71
  - .rspec
89
72
  - Gemfile
90
73
  - README.md
@@ -93,18 +76,13 @@ files:
93
76
  - csso-rails.gemspec
94
77
  - lib/csso-rails.rb
95
78
  - lib/csso.rb
96
- - lib/csso/js/compressor.js
97
- - lib/csso/js/csso.js
98
- - lib/csso/js/cssoapi.js
99
- - lib/csso/js/gonzales.cssp.node.js
100
- - lib/csso/js/translator.js
101
- - lib/csso/js/util.js
102
- - lib/csso/loader.rb
79
+ - lib/csso/compressor.rb
80
+ - lib/csso/csso.js.erb
81
+ - lib/csso/js_lib.rb
103
82
  - lib/csso/rails.rb
104
- - lib/csso/utils.rb
105
83
  - lib/csso/version.rb
106
84
  - spec/csso/csso_spec.rb
107
- - spec/spec_helper.rb
85
+ - vendor/csso/csso.js
108
86
  homepage: https://github.com/Vasfed/csso-rails
109
87
  licenses: []
110
88
  post_install_message:
@@ -119,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
97
  version: '0'
120
98
  segments:
121
99
  - 0
122
- hash: -3516938475445365228
100
+ hash: -1786311212568268028
123
101
  required_rubygems_version: !ruby/object:Gem::Requirement
124
102
  none: false
125
103
  requirements:
@@ -128,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
106
  version: '0'
129
107
  segments:
130
108
  - 0
131
- hash: -3516938475445365228
109
+ hash: -1786311212568268028
132
110
  requirements: []
133
111
  rubyforge_project: csso-rails
134
112
  rubygems_version: 1.8.24
@@ -137,4 +115,3 @@ specification_version: 3
137
115
  summary: CSS Stylesheet optimizer/compressor for Rails
138
116
  test_files:
139
117
  - spec/csso/csso_spec.rb
140
- - spec/spec_helper.rb
data/.gitmodules DELETED
@@ -1,3 +0,0 @@
1
- [submodule "lib/less/js"]
2
- path = lib/less/js
3
- url = https://github.com/cloudhead/less.js.git
@@ -1,1471 +0,0 @@
1
- function TRBL(name, imp) {
2
- this.name = TRBL.extractMain(name);
3
- this.sides = {
4
- 'top': null,
5
- 'right': null,
6
- 'bottom': null,
7
- 'left': null
8
- };
9
- this.imp = imp ? 4 : 0;
10
- }
11
-
12
- TRBL.props = {
13
- 'margin': 1,
14
- 'margin-top': 1,
15
- 'margin-right': 1,
16
- 'margin-bottom': 1,
17
- 'margin-left': 1,
18
- 'padding': 1,
19
- 'padding-top': 1,
20
- 'padding-right': 1,
21
- 'padding-bottom': 1,
22
- 'padding-left': 1
23
- };
24
-
25
- TRBL.extractMain = function(name) {
26
- var i = name.indexOf('-');
27
- return i === -1 ? name : name.substr(0, i);
28
- };
29
-
30
- TRBL.prototype.impSum = function() {
31
- var imp = 0, n = 0;
32
- for (var k in this.sides) {
33
- if (this.sides[k]) {
34
- n++;
35
- if (this.sides[k].imp) imp++;
36
- }
37
- }
38
- return imp === n ? imp : 0;
39
- };
40
-
41
- TRBL.prototype.add = function(name, sValue, tValue, imp) {
42
- var s = this.sides,
43
- currentSide,
44
- i, x, side, a = [], last,
45
- imp = imp ? 1 : 0,
46
- wasUnary = false;
47
- if ((i = name.lastIndexOf('-')) !== -1) {
48
- side = name.substr(i + 1);
49
- if (side in s) {
50
- if (!(currentSide = s[side]) || (imp && !currentSide.imp)) {
51
- s[side] = { s: imp ? sValue.substring(0, sValue.length - 10) : sValue, t: [tValue[0]], imp: imp };
52
- if (tValue[0][1] === 'unary') s[side].t.push(tValue[1]);
53
- }
54
- return true;
55
- }
56
- } else if (name === this.name) {
57
- for (i = 0; i < tValue.length; i++) {
58
- x = tValue[i];
59
- last = a[a.length - 1];
60
- switch(x[1]) {
61
- case 'unary':
62
- a.push({ s: x[2], t: [x], imp: imp });
63
- wasUnary = true;
64
- break;
65
- case 'number':
66
- case 'ident':
67
- if (wasUnary) {
68
- last.t.push(x);
69
- last.s += x[2];
70
- } else {
71
- a.push({ s: x[2], t: [x], imp: imp });
72
- }
73
- wasUnary = false;
74
- break;
75
- case 'percentage':
76
- if (wasUnary) {
77
- last.t.push(x);
78
- last.s += x[2][2] + '%';
79
- } else {
80
- a.push({ s: x[2][2] + '%', t: [x], imp: imp });
81
- }
82
- wasUnary = false;
83
- break;
84
- case 'dimension':
85
- if (wasUnary) {
86
- last.t.push(x);
87
- last.s += x[2][2] + x[3][2];
88
- } else {
89
- a.push({ s: x[2][2] + x[3][2], t: [x], imp: imp });
90
- }
91
- wasUnary = false;
92
- break;
93
- case 's':
94
- case 'comment':
95
- case 'important':
96
- break;
97
- default:
98
- return false;
99
- }
100
- }
101
-
102
- if (a.length > 4) return false;
103
-
104
- if (!a[1]) a[1] = a[0];
105
- if (!a[2]) a[2] = a[0];
106
- if (!a[3]) a[3] = a[1];
107
-
108
- if (!s.top) s.top = a[0];
109
- if (!s.right) s.right = a[1];
110
- if (!s.bottom) s.bottom = a[2];
111
- if (!s.left) s.left = a[3];
112
-
113
- return true;
114
- }
115
- };
116
-
117
- TRBL.prototype.isOkToMinimize = function() {
118
- var s = this.sides,
119
- imp;
120
-
121
- if (!!(s.top && s.right && s.bottom && s.left)) {
122
- imp = s.top.imp + s.right.imp + s.bottom.imp + s.left.imp;
123
- return (imp === 0 || imp === 4 || imp === this.imp);
124
- }
125
- return false;
126
- };
127
-
128
- TRBL.prototype.getValue = function() {
129
- var s = this.sides,
130
- a = [s.top, s.right, s.bottom, s.left],
131
- r = [{}, 'value'];
132
-
133
- if (s.left.s === s.right.s) {
134
- a.length--;
135
- if (s.bottom.s === s.top.s) {
136
- a.length--;
137
- if (s.right.s === s.top.s) {
138
- a.length--;
139
- }
140
- }
141
- }
142
-
143
- for (var i = 0; i < a.length - 1; i++) {
144
- r = r.concat(a[i].t);
145
- r.push([{ s: ' ' }, 's', ' ']);
146
- }
147
- r = r.concat(a[i].t);
148
-
149
- if (this.impSum()) r.push([{ s: '!important'}, 'important']);
150
-
151
- return r;
152
- };
153
-
154
- TRBL.prototype.getProperty = function() {
155
- return [{ s: this.name }, 'property', [{ s: this.name }, 'ident', this.name]];
156
- };
157
-
158
- TRBL.prototype.getString = function() {
159
- var p = this.getProperty(),
160
- v = this.getValue().slice(2),
161
- r = p[0].s + ':';
162
-
163
- for (var i = 0; i < v.length; i++) r += v[i][0].s;
164
-
165
- return r;
166
- };
167
-
168
- function CSSOCompressor() {}
169
-
170
- CSSOCompressor.prototype.init = function() {
171
- this.props = {};
172
- this.shorts = {};
173
- this.shorts2 = {};
174
-
175
- this.ccrules = {}; // clean comment rules — special case to resolve ambiguity
176
- this.crules = {}; // compress rules
177
- this.prules = {}; // prepare rules
178
- this.frrules = {}; // freeze ruleset rules
179
- this.msrules = {}; // mark shorthands rules
180
- this.csrules = {}; // clean shorthands rules
181
- this.rbrules = {}; // restructure block rules
182
- this.rjrules = {}; // rejoin ruleset rules
183
- this.rrrules = {}; // restructure ruleset rules
184
- this.frules = {}; // finalize rules
185
-
186
- this.initRules(this.crules, this.defCCfg);
187
- this.initRules(this.ccrules, this.cleanCfg);
188
- this.initRules(this.frrules, this.frCfg);
189
- this.initRules(this.prules, this.preCfg);
190
- this.initRules(this.msrules, this.msCfg);
191
- this.initRules(this.csrules, this.csCfg);
192
- this.initRules(this.rbrules, this.defRBCfg);
193
- this.initRules(this.rjrules, this.defRJCfg);
194
- this.initRules(this.rrrules, this.defRRCfg);
195
- this.initRules(this.frules, this.defFCfg);
196
-
197
- this.shortGroupID = 0;
198
- this.lastShortGroupID = 0;
199
- this.lastShortSelector = 0;
200
- };
201
-
202
- CSSOCompressor.prototype.initRules = function(r, cfg) {
203
- var o = this.order,
204
- p = this.profile,
205
- x, i, k,
206
- t = [];
207
-
208
- for (i = 0; i < o.length; i++) if (o[i] in cfg) t.push(o[i]);
209
-
210
- if (!t.length) t = o;
211
- for (i = 0; i < t.length; i++) {
212
- x = p[t[i]];
213
- for (k in x) r[k] ? r[k].push(t[i]) : r[k] = [t[i]];
214
- }
215
- };
216
-
217
- CSSOCompressor.prototype.cleanCfg = {
218
- 'cleanComment': 1
219
- };
220
-
221
- CSSOCompressor.prototype.defCCfg = {
222
- 'cleanCharset': 1,
223
- 'cleanImport': 1,
224
- 'cleanWhitespace': 1,
225
- 'cleanDecldelim': 1,
226
- 'compressNumber': 1,
227
- 'cleanUnary': 1,
228
- 'compressColor': 1,
229
- 'compressDimension': 1,
230
- 'compressString': 1,
231
- 'compressFontWeight': 1,
232
- 'compressFont': 1,
233
- 'compressBackground': 1,
234
- 'cleanEmpty': 1
235
- };
236
-
237
- CSSOCompressor.prototype.defRBCfg = {
238
- 'restructureBlock': 1
239
- };
240
-
241
- CSSOCompressor.prototype.defRJCfg = {
242
- 'rejoinRuleset': 1,
243
- 'cleanEmpty': 1
244
- };
245
-
246
- CSSOCompressor.prototype.defRRCfg = {
247
- 'restructureRuleset': 1,
248
- 'cleanEmpty': 1
249
- };
250
-
251
- CSSOCompressor.prototype.defFCfg = {
252
- 'cleanEmpty': 1,
253
- 'delimSelectors': 1,
254
- 'delimBlocks': 1
255
- };
256
-
257
- CSSOCompressor.prototype.preCfg = {
258
- 'destroyDelims': 1,
259
- 'preTranslate': 1
260
- };
261
-
262
- CSSOCompressor.prototype.msCfg = {
263
- 'markShorthands': 1
264
- };
265
-
266
- CSSOCompressor.prototype.frCfg = {
267
- 'freezeRulesets': 1
268
- };
269
-
270
- CSSOCompressor.prototype.csCfg = {
271
- 'cleanShorthands': 1,
272
- 'cleanEmpty': 1
273
- };
274
-
275
- CSSOCompressor.prototype.order = [
276
- 'cleanCharset',
277
- 'cleanImport',
278
- 'cleanComment',
279
- 'cleanWhitespace',
280
- 'compressNumber',
281
- 'cleanUnary',
282
- 'compressColor',
283
- 'compressDimension',
284
- 'compressString',
285
- 'compressFontWeight',
286
- 'compressFont',
287
- 'compressBackground',
288
- 'freezeRulesets',
289
- 'destroyDelims',
290
- 'preTranslate',
291
- 'markShorthands',
292
- 'cleanShorthands',
293
- 'restructureBlock',
294
- 'rejoinRuleset',
295
- 'restructureRuleset',
296
- 'cleanEmpty',
297
- 'delimSelectors',
298
- 'delimBlocks'
299
- ];
300
-
301
- CSSOCompressor.prototype.profile = {
302
- 'cleanCharset': {
303
- 'atrules': 1
304
- },
305
- 'cleanImport': {
306
- 'atrules': 1
307
- },
308
- 'cleanWhitespace': {
309
- 's': 1
310
- },
311
- 'compressNumber': {
312
- 'number': 1
313
- },
314
- 'cleanUnary': {
315
- 'unary': 1
316
- },
317
- 'compressColor': {
318
- 'vhash': 1,
319
- 'funktion': 1,
320
- 'ident': 1
321
- },
322
- 'compressDimension': {
323
- 'dimension': 1
324
- },
325
- 'compressString': {
326
- 'string': 1
327
- },
328
- 'compressFontWeight': {
329
- 'declaration': 1
330
- },
331
- 'compressFont': {
332
- 'declaration': 1
333
- },
334
- 'compressBackground': {
335
- 'declaration': 1
336
- },
337
- 'cleanComment': {
338
- 'comment': 1
339
- },
340
- 'cleanDecldelim': {
341
- 'block': 1
342
- },
343
- 'cleanEmpty': {
344
- 'ruleset': 1,
345
- 'atruleb': 1,
346
- 'atruler': 1
347
- },
348
- 'destroyDelims': {
349
- 'decldelim': 1,
350
- 'delim': 1
351
- },
352
- 'preTranslate': {
353
- 'declaration': 1,
354
- 'property': 1,
355
- 'simpleselector': 1,
356
- 'filter': 1,
357
- 'value': 1,
358
- 'number': 1,
359
- 'percentage': 1,
360
- 'dimension': 1,
361
- 'ident': 1
362
- },
363
- 'restructureBlock': {
364
- 'block': 1
365
- },
366
- 'rejoinRuleset': {
367
- 'ruleset': 1
368
- },
369
- 'restructureRuleset': {
370
- 'ruleset': 1
371
- },
372
- 'delimSelectors': {
373
- 'selector': 1
374
- },
375
- 'delimBlocks': {
376
- 'block': 1
377
- },
378
- 'markShorthands': {
379
- 'block': 1
380
- },
381
- 'cleanShorthands': {
382
- 'declaration': 1
383
- },
384
- 'freezeRulesets': {
385
- 'ruleset': 1
386
- }
387
- };
388
-
389
- CSSOCompressor.prototype.isContainer = function(o) {
390
- if (Array.isArray(o)) {
391
- for (var i = 0; i < o.length; i++) if (Array.isArray(o[i])) return true;
392
- }
393
- };
394
-
395
- CSSOCompressor.prototype.process = function(rules, token, container, i, path) {
396
- var rule = token[1];
397
- if (rule && rules[rule]) {
398
- var r = rules[rule],
399
- x1 = token, x2,
400
- o = this.order, k;
401
- for (var k = 0; k < r.length; k++) {
402
- x2 = this[r[k]](x1, rule, container, i, path);
403
- if (x2 === null) return null;
404
- else if (x2 !== undefined) x1 = x2;
405
- }
406
- }
407
- return x1;
408
- };
409
-
410
- CSSOCompressor.prototype.compress = function(tree, ro) {
411
- tree = tree || ['stylesheet'];
412
- this.init();
413
- this.info = true;
414
-
415
- var x = (typeof tree[0] !== 'string') ? tree : this.injectInfo([tree])[0],
416
- l0, l1 = 100000000000, ls,
417
- x0, x1, xs,
418
- protectedComment = this.findProtectedComment(tree);
419
-
420
- // compression without restructure
421
- x = this.walk(this.ccrules, x, '/0');
422
- x = this.walk(this.crules, x, '/0');
423
- x = this.walk(this.prules, x, '/0');
424
- x = this.walk(this.frrules, x, '/0');
425
-
426
- ls = translator.translate(cleanInfo(x)).length;
427
-
428
- if (!ro) { // restructure ON
429
- xs = this.copyArray(x);
430
- x = this.walk(this.rjrules, x, '/0');
431
- this.disjoin(x);
432
- x = this.walk(this.msrules, x, '/0');
433
- x = this.walk(this.csrules, x, '/0');
434
- x = this.walk(this.rbrules, x, '/0');
435
- do {
436
- l0 = l1;
437
- x0 = this.copyArray(x);
438
- x = this.walk(this.rjrules, x, '/0');
439
- x = this.walk(this.rrrules, x, '/0');
440
- l1 = translator.translate(cleanInfo(x)).length;
441
- x1 = this.copyArray(x);
442
- } while (l0 > l1);
443
- if (ls < l0 && ls < l1) x = xs;
444
- else if (l0 < l1) x = x0;
445
- }
446
-
447
- x = this.walk(this.frules, x, '/0');
448
-
449
- if (protectedComment) x.splice(2, 0, protectedComment);
450
-
451
- return x;
452
- };
453
-
454
- CSSOCompressor.prototype.findProtectedComment = function(tree) {
455
- var token;
456
- for (var i = 2; i < tree.length; i++) {
457
- token = tree[i];
458
- if (token[1] === 'comment' && token[2].length > 0 && token[2].charAt(0) === '!') return token;
459
- if (token[1] !== 's') return;
460
- }
461
- };
462
-
463
- CSSOCompressor.prototype.injectInfo = function(token) {
464
- var t;
465
- for (var i = token.length - 1; i > -1; i--) {
466
- t = token[i];
467
- if (t && Array.isArray(t)) {
468
- if (this.isContainer(t)) t = this.injectInfo(t);
469
- t.splice(0, 0, {});
470
- }
471
- }
472
- return token;
473
- };
474
-
475
- CSSOCompressor.prototype.disjoin = function(container) {
476
- var t, s, r, sr;
477
-
478
- for (var i = container.length - 1; i > -1; i--) {
479
- t = container[i];
480
- if (t && Array.isArray(t)) {
481
- if (t[1] === 'ruleset') {
482
- t[0].shortGroupID = this.shortGroupID++;
483
- s = t[2];
484
- if (s.length > 3) {
485
- sr = s.slice(0, 2);
486
- for (var k = s.length - 1; k > 1; k--) {
487
- r = this.copyArray(t);
488
- r[2] = sr.concat([s[k]]);
489
- r[2][0].s = s[k][0].s;
490
- container.splice(i + 1, 0, r);
491
- }
492
- container.splice(i, 1);
493
- }
494
- }
495
- }
496
- if (this.isContainer(t)) this.disjoin(t);
497
- }
498
- };
499
-
500
- CSSOCompressor.prototype.walk = function(rules, container, path) {
501
- var t, x;
502
- for (var i = container.length - 1; i > -1; i--) {
503
- t = container[i];
504
- if (t && Array.isArray(t)) {
505
- t[0].parent = container;
506
- if (this.isContainer(t)) t = this.walk(rules, t, path + '/' + i); // go inside
507
- if (t === null) container.splice(i, 1);
508
- else {
509
- if (x = this.process(rules, t, container, i, path)) container[i] = x; // compressed not null
510
- else if (x === null) container.splice(i, 1); // null is the mark to delete token
511
- }
512
- }
513
- }
514
- return container.length ? container : null;
515
- };
516
-
517
- CSSOCompressor.prototype.freezeRulesets = function(token, rule, container, i) {
518
- var info = token[0],
519
- selector = token[2];
520
-
521
- info.freeze = this.freezeNeeded(selector);
522
- info.freezeID = this.selectorSignature(selector);
523
- info.pseudoID = this.composePseudoID(selector);
524
- this.markSimplePseudo(selector);
525
-
526
- return token;
527
- };
528
-
529
- CSSOCompressor.prototype.markSimplePseudo = function(selector) {
530
- var ss, sg = {};
531
-
532
- for (var i = 2; i < selector.length; i++) {
533
- ss = selector[i];
534
- ss[0].pseudo = this.containsPseudo(ss);
535
- ss[0].sg = sg;
536
- sg[ss[0].s] = 1;
537
- }
538
- };
539
-
540
- CSSOCompressor.prototype.composePseudoID = function(selector) {
541
- var a = [], ss;
542
-
543
- for (var i = 2; i < selector.length; i++) {
544
- ss = selector[i];
545
- if (this.containsPseudo(ss)) {
546
- a.push(ss[0].s);
547
- }
548
- }
549
-
550
- a.sort();
551
-
552
- return a.join(',');
553
- };
554
-
555
- CSSOCompressor.prototype.containsPseudo = function(sselector) {
556
- for (var j = 2; j < sselector.length; j++) {
557
- switch (sselector[j][1]) {
558
- case 'pseudoc':
559
- case 'pseudoe':
560
- case 'nthselector':
561
- if (!(sselector[j][2][2] in this.notFPClasses)) return true;
562
- }
563
- }
564
- };
565
-
566
- CSSOCompressor.prototype.selectorSignature = function(selector) {
567
- var a = [];
568
-
569
- for (var i = 2; i < selector.length; i++) {
570
- a.push(translator.translate(cleanInfo(selector[i])));
571
- }
572
-
573
- a.sort();
574
-
575
- return a.join(',');
576
- };
577
-
578
- CSSOCompressor.prototype.pseudoSelectorSignature = function(selector, exclude) {
579
- var a = [], b = {}, ss, wasExclude = false;
580
- exclude = exclude || {};
581
-
582
- for (var i = 2; i < selector.length; i++) {
583
- ss = selector[i];
584
- for (var j = 2; j < ss.length; j++) {
585
- switch (ss[j][1]) {
586
- case 'pseudoc':
587
- case 'pseudoe':
588
- case 'nthselector':
589
- if (!(ss[j][2][2] in exclude)) b[ss[j][2][2]] = 1;
590
- else wasExclude = true;
591
- break;
592
- }
593
- }
594
- }
595
-
596
- for (var k in b) a.push(k);
597
-
598
- a.sort();
599
-
600
- return a.join(',') + wasExclude;
601
- };
602
-
603
- CSSOCompressor.prototype.notFPClasses = {
604
- 'link': 1,
605
- 'visited': 1,
606
- 'hover': 1,
607
- 'active': 1,
608
- 'first-letter': 1,
609
- 'first-line': 1
610
- };
611
-
612
- CSSOCompressor.prototype.notFPElements = {
613
- 'first-letter': 1,
614
- 'first-line': 1
615
- };
616
-
617
- CSSOCompressor.prototype.freezeNeeded = function(selector) {
618
- var ss;
619
- for (var i = 2; i < selector.length; i++) {
620
- ss = selector[i];
621
- for (var j = 2; j < ss.length; j++) {
622
- switch (ss[j][1]) {
623
- case 'pseudoc':
624
- if (!(ss[j][2][2] in this.notFPClasses)) return true;
625
- break;
626
- case 'pseudoe':
627
- if (!(ss[j][2][2] in this.notFPElements)) return true;
628
- break;
629
- case 'nthselector':
630
- return true;
631
- break;
632
- }
633
- }
634
- }
635
- return false;
636
- };
637
-
638
- CSSOCompressor.prototype.cleanCharset = function(token, rule, container, i) {
639
- if (token[2][2][2] === 'charset') {
640
- for (i = i - 1; i > 1; i--) {
641
- if (container[i][1] !== 's' && container[i][1] !== 'comment') return null;
642
- }
643
- }
644
- };
645
-
646
- CSSOCompressor.prototype.cleanImport = function(token, rule, container, i) {
647
- var x;
648
- for (i = i - 1; i > 1; i--) {
649
- x = container[i][1];
650
- if (x !== 's' && x !== 'comment') {
651
- if (x === 'atrules') {
652
- x = container[i][2][2][2];
653
- if (x !== 'import' && x !== 'charset') return null;
654
- } else return null;
655
- }
656
- }
657
- };
658
-
659
- CSSOCompressor.prototype.cleanComment = function(token, rule, container, i) {
660
- var pr = ((container[1] === 'braces' && i === 4) ||
661
- (container[1] !== 'braces' && i === 2)) ? null : container[i - 1][1],
662
- nr = i === container.length - 1 ? null : container[i + 1][1];
663
-
664
- if (nr !== null && pr !== null) {
665
- if (this._cleanComment(nr) || this._cleanComment(pr)) return null;
666
- } else return null;
667
- };
668
-
669
- CSSOCompressor.prototype._cleanComment = function(r) {
670
- switch(r) {
671
- case 's':
672
- case 'operator':
673
- case 'attrselector':
674
- case 'block':
675
- case 'decldelim':
676
- case 'ruleset':
677
- case 'declaration':
678
- case 'atruleb':
679
- case 'atrules':
680
- case 'atruler':
681
- case 'important':
682
- case 'nth':
683
- case 'combinator':
684
- return true;
685
- }
686
- };
687
-
688
- CSSOCompressor.prototype.nextToken = function(container, type, i, exactly) {
689
- var t, r;
690
- for (; i < container.length; i++) {
691
- t = container[i];
692
- if (Array.isArray(t)) {
693
- r = t[1];
694
- if (r === type) return t;
695
- else if (exactly && r !== 's') return;
696
- }
697
- }
698
- };
699
-
700
- CSSOCompressor.prototype.cleanWhitespace = function(token, rule, container, i) {
701
- var pr = ((container[1] === 'braces' && i === 4) ||
702
- (container[1] !== 'braces' && i === 2)) ? null : container[i - 1][1],
703
- nr = i === container.length - 1 ? null : container[i + 1][1];
704
-
705
- if (nr === 'unknown') token[2] = '\n';
706
- else {
707
- if (!(container[1] === 'atrulerq' && !pr) && !this.issue16(container, i)) {
708
- if (nr !== null && pr !== null) {
709
- if (this._cleanWhitespace(nr, false) || this._cleanWhitespace(pr, true)) return null;
710
- } else return null;
711
- }
712
-
713
- token[2] = ' ';
714
- }
715
-
716
- return token;
717
- };
718
-
719
- // See https://github.com/afelix/csso/issues/16
720
- CSSOCompressor.prototype.issue16 = function(container, i) {
721
- return (i !== 2 && i !== container.length - 1 && container[i - 1][1] === 'uri');
722
- };
723
-
724
- CSSOCompressor.prototype._cleanWhitespace = function(r, left) {
725
- switch(r) {
726
- case 's':
727
- case 'operator':
728
- case 'attrselector':
729
- case 'block':
730
- case 'decldelim':
731
- case 'ruleset':
732
- case 'declaration':
733
- case 'atruleb':
734
- case 'atrules':
735
- case 'atruler':
736
- case 'important':
737
- case 'nth':
738
- case 'combinator':
739
- return true;
740
- }
741
- if (left) {
742
- switch(r) {
743
- case 'funktion':
744
- case 'braces':
745
- case 'uri':
746
- return true;
747
- }
748
- }
749
- };
750
-
751
- CSSOCompressor.prototype.cleanDecldelim = function(token) {
752
- for (var i = token.length - 1; i > 1; i--) {
753
- if (token[i][1] === 'decldelim' &&
754
- token[i + 1][1] !== 'declaration') token.splice(i, 1);
755
- }
756
- if (token[2][1] === 'decldelim') token.splice(2, 1);
757
- return token;
758
- };
759
-
760
- CSSOCompressor.prototype.compressNumber = function(token, rule, container, i) {
761
- var x = token[2];
762
-
763
- if (/^0*/.test(x)) x = x.replace(/^0+/, '');
764
- if (/\.0*$/.test(x)) x = x.replace(/\.0*$/, '');
765
- if (/\..*[1-9]+0+$/.test(x)) x = x.replace(/0+$/, '');
766
- if (x === '.' || x === '') x = '0';
767
-
768
- token[2] = x;
769
- token[0].s = x;
770
- return token;
771
- };
772
-
773
- CSSOCompressor.prototype.findDeclaration = function(token) {
774
- var parent = token;
775
- while ((parent = parent[0].parent) && parent[1] !== 'declaration');
776
- return parent;
777
- };
778
-
779
- CSSOCompressor.prototype.cleanUnary = function(token, rule, container, i) {
780
- var next = container[i + 1];
781
- if (next && next[1] === 'number' && next[2] === '0') return null;
782
- return token;
783
- };
784
-
785
- CSSOCompressor.prototype.compressColor = function(token, rule, container, i) {
786
- switch(rule) {
787
- case 'vhash':
788
- return this.compressHashColor(token);
789
- case 'funktion':
790
- return this.compressFunctionColor(token);
791
- case 'ident':
792
- return this.compressIdentColor(token, rule, container, i);
793
- }
794
- };
795
-
796
- CSSOCompressor.prototype.compressIdentColor = function(token, rule, container) {
797
- var map = { 'yellow': 'ff0',
798
- 'fuchsia': 'f0f',
799
- 'white': 'fff',
800
- 'black': '000',
801
- 'blue': '00f',
802
- 'aqua': '0ff' },
803
- allow = { 'value': 1, 'functionBody': 1 },
804
- _x = token[2].toLowerCase();
805
-
806
- if (container[1] in allow && _x in map) return [{}, 'vhash', map[_x]];
807
- };
808
-
809
- CSSOCompressor.prototype.compressHashColor = function(token) {
810
- return this._compressHashColor(token[2], token[0]);
811
- };
812
-
813
- CSSOCompressor.prototype._compressHashColor = function(x, info) {
814
- var map = { 'f00': 'red',
815
- 'c0c0c0': 'silver',
816
- '808080': 'gray',
817
- '800000': 'maroon',
818
- '800080': 'purple',
819
- '008000': 'green',
820
- '808000': 'olive',
821
- '000080': 'navy',
822
- '008080': 'teal'},
823
- _x = x;
824
- x = x.toLowerCase();
825
-
826
- if (x.length === 6 &&
827
- x.charAt(0) === x.charAt(1) &&
828
- x.charAt(2) === x.charAt(3) &&
829
- x.charAt(4) === x.charAt(5)) x = x.charAt(0) + x.charAt(2) + x.charAt(4);
830
-
831
- return map[x] ? [info, 'string', map[x]] : [info, 'vhash', (x.length < _x.length ? x : _x)];
832
- };
833
-
834
- CSSOCompressor.prototype.compressFunctionColor = function(token) {
835
- var i, v = [], t, h = '', body;
836
-
837
- if (token[2][2] === 'rgb') {
838
- body = token[3];
839
- for (i = 2; i < body.length; i++) {
840
- t = body[i][1];
841
- if (t === 'number') v.push(body[i]);
842
- else if (t !== 'operator') { v = []; break }
843
- }
844
- if (v.length === 3) {
845
- h += (t = Number(v[0][2]).toString(16)).length === 1 ? '0' + t : t;
846
- h += (t = Number(v[1][2]).toString(16)).length === 1 ? '0' + t : t;
847
- h += (t = Number(v[2][2]).toString(16)).length === 1 ? '0' + t : t;
848
- if (h.length === 6) return this._compressHashColor(h, {});
849
- }
850
- }
851
- };
852
-
853
- CSSOCompressor.prototype.compressDimension = function(token) {
854
- var declaration;
855
- if (token[2][2] === '0') {
856
- if (token[3][2] === 's' && (declaration = this.findDeclaration(token))) {
857
- var declName = declaration[2][2][2];
858
- if (declName === '-moz-transition') return; // https://github.com/css/csso/issues/82
859
- if (declName === '-moz-animation' || declName === 'animation') return; // https://github.com/css/csso/issues/100
860
- }
861
- return token[2];
862
- }
863
- };
864
-
865
- CSSOCompressor.prototype.compressString = function(token, rule, container) {
866
- var s = token[2], r = '', c;
867
- for (var i = 0; i < s.length; i++) {
868
- c = s.charAt(i);
869
- if (c === '\\' && s.charAt(i + 1) === '\n') i++;
870
- else r += c;
871
- }
872
- // if (container[1] === 'attrib' && /^('|")[a-zA-Z0-9]*('|")$/.test(r)) {
873
- // r = r.substring(1, r.length - 1);
874
- // }
875
- if (s.length !== r.length) return [{}, 'string', r];
876
- };
877
-
878
- CSSOCompressor.prototype.compressFontWeight = function(token) {
879
- var p = token[2],
880
- v = token[3];
881
- if (p[2][2].indexOf('font-weight') !== -1 && v[2][1] === 'ident') {
882
- if (v[2][2] === 'normal') v[2] = [{}, 'number', '400'];
883
- else if (v[2][2] === 'bold') v[2] = [{}, 'number', '700'];
884
- return token;
885
- }
886
- };
887
-
888
- CSSOCompressor.prototype.compressFont = function(token) {
889
- var p = token[2],
890
- v = token[3],
891
- i, x, t;
892
- if (/font$/.test(p[2][2]) && v.length) {
893
- v.splice(2, 0, [{}, 's', '']);
894
- for (i = v.length - 1; i > 2; i--) {
895
- x = v[i];
896
- if (x[1] === 'ident') {
897
- x = x[2];
898
- if (x === 'bold') v[i] = [{}, 'number', '700'];
899
- else if (x === 'normal') {
900
- t = v[i - 1];
901
- if (t[1] === 'operator' && t[2] === '/') v.splice(--i, 2);
902
- else v.splice(i, 1);
903
- if (v[i - 1][1] === 's') v.splice(--i, 1);
904
- }
905
- else if (x === 'medium' && v[i + 1] && v[i + 1][2] !== '/') {
906
- v.splice(i, 1);
907
- if (v[i - 1][1] === 's') v.splice(--i, 1);
908
- }
909
- }
910
- }
911
- if (v.length > 2 && v[2][1] === 's') v.splice(2, 1);
912
- if (v.length === 2) v.push([{}, 'ident', 'normal']);
913
- return token;
914
- }
915
- };
916
-
917
- CSSOCompressor.prototype.compressBackground = function(token) {
918
- var p = token[2],
919
- v = token[3],
920
- i, x, t,
921
- n = v[v.length - 1][1] === 'important' ? 3 : 2;
922
- if (/background$/.test(p[2][2]) && v.length) {
923
- v.splice(2, 0, [{}, 's', '']);
924
- for (i = v.length - 1; i > n; i--) {
925
- x = v[i];
926
- if (x[1] === 'ident') {
927
- x = x[2];
928
- if (x === 'transparent' || x === 'none' || x === 'repeat' || x === 'scroll') {
929
- v.splice(i, 1);
930
- if (v[i - 1][1] === 's') v.splice(--i, 1);
931
- }
932
- }
933
- }
934
- if (v.length > 2 && v[2][1] === 's') v.splice(2, 1);
935
- if (v.length === 2) v.splice(2, 0, [{}, 'number', '0'], [{}, 's', ' '], [{}, 'number', '0']);
936
- return token;
937
- }
938
- };
939
-
940
- CSSOCompressor.prototype.cleanEmpty = function(token, rule) {
941
- switch(rule) {
942
- case 'ruleset':
943
- if (token[3].length === 2) return null;
944
- break;
945
- case 'atruleb':
946
- if (token[token.length - 1].length < 3) return null;
947
- break;
948
- case 'atruler':
949
- if (token[4].length < 3) return null;
950
- break;
951
- }
952
- };
953
-
954
- CSSOCompressor.prototype.destroyDelims = function() {
955
- return null;
956
- };
957
-
958
- CSSOCompressor.prototype.preTranslate = function(token) {
959
- token[0].s = translator.translate(cleanInfo(token));
960
- return token;
961
- };
962
-
963
- CSSOCompressor.prototype.markShorthands = function(token, rule, container, j, path) {
964
- if (container[1] === 'ruleset') {
965
- var selector = container[2][2][0].s,
966
- freeze = container[0].freeze,
967
- freezeID = container[0].freezeID;
968
- } else {
969
- var selector = '',
970
- freeze = false,
971
- freezeID = 'fake';
972
- }
973
- var x, p, v, imp, s, key, sh,
974
- pre = this.pathUp(path) + '/' + (freeze ? '&' + freezeID + '&' : '') + selector + '/',
975
- createNew, shortsI, shortGroupID = container[0].shortGroupID;
976
-
977
- for (var i = token.length - 1; i > -1; i--) {
978
- createNew = true;
979
- x = token[i];
980
- if (x[1] === 'declaration') {
981
- v = x[3];
982
- imp = v[v.length - 1][1] === 'important';
983
- p = x[2][0].s;
984
- x[0].id = path + '/' + i;
985
- if (p in TRBL.props) {
986
- key = pre + TRBL.extractMain(p);
987
- var shorts = this.shorts2[key] || [];
988
- shortsI = shorts.length === 0 ? 0 : shorts.length - 1;
989
-
990
- if (!this.lastShortSelector || selector === this.lastShortSelector || shortGroupID === this.lastShortGroupID) {
991
- if (shorts.length) {
992
- sh = shorts[shortsI];
993
- //if (imp && !sh.imp) sh.invalid = true;
994
- createNew = false;
995
- }
996
- }
997
-
998
- if (createNew) {
999
- x[0].replaceByShort = true;
1000
- x[0].shorthandKey = { key: key, i: shortsI };
1001
- sh = new TRBL(p, imp);
1002
- shorts.push(sh);
1003
- }
1004
-
1005
- if (!sh.invalid) {
1006
- x[0].removeByShort = true;
1007
- x[0].shorthandKey = { key: key, i: shortsI };
1008
- sh.add(p, v[0].s, v.slice(2), imp);
1009
- }
1010
-
1011
- this.shorts2[key] = shorts;
1012
-
1013
- this.lastShortSelector = selector;
1014
- this.lastShortGroupID = shortGroupID;
1015
- }
1016
- }
1017
- }
1018
-
1019
-
1020
- return token;
1021
- };
1022
-
1023
- CSSOCompressor.prototype.cleanShorthands = function(token) {
1024
- if (token[0].removeByShort || token[0].replaceByShort) {
1025
- var s, t, sKey = token[0].shorthandKey;
1026
-
1027
- s = this.shorts2[sKey.key][sKey.i];
1028
-
1029
- if (!s.invalid && s.isOkToMinimize()) {
1030
- if (token[0].replaceByShort) {
1031
- t = [{}, 'declaration', s.getProperty(), s.getValue()];
1032
- t[0].s = translator.translate(cleanInfo(t));
1033
- return t;
1034
- } else return null;
1035
- }
1036
- }
1037
- };
1038
-
1039
- CSSOCompressor.prototype.dontRestructure = {
1040
- 'src': 1, // https://github.com/afelix/csso/issues/50
1041
- 'clip': 1, // https://github.com/afelix/csso/issues/57
1042
- 'display': 1 // https://github.com/afelix/csso/issues/71
1043
- };
1044
-
1045
- CSSOCompressor.prototype.restructureBlock = function(token, rule, container, j, path) {
1046
- if (container[1] === 'ruleset') {
1047
- var props = this.props,
1048
- isPseudo = container[2][2][0].pseudo,
1049
- selector = container[2][2][0].s,
1050
- freeze = container[0].freeze,
1051
- freezeID = container[0].freezeID,
1052
- pseudoID = container[0].pseudoID,
1053
- sg = container[2][2][0].sg;
1054
- } else {
1055
- var props = {},
1056
- isPseudo = false,
1057
- selector = '',
1058
- freeze = false,
1059
- freezeID = 'fake',
1060
- pseudoID = 'fake',
1061
- sg = {};
1062
- }
1063
-
1064
- var x, p, v, imp, t,
1065
- pre = this.pathUp(path) + '/' + selector + '/',
1066
- ppre;
1067
-
1068
- for (var i = token.length - 1; i > -1; i--) {
1069
- x = token[i];
1070
- if (x[1] === 'declaration') {
1071
- v = x[3];
1072
- imp = v[v.length - 1][1] === 'important';
1073
- p = x[2][0].s;
1074
- ppre = this.buildPPre(pre, p, v, x, freeze);
1075
- x[0].id = path + '/' + i;
1076
- if (!this.dontRestructure[p] && (t = props[ppre])) {
1077
- if ((isPseudo && freezeID === t.freezeID) || // pseudo from equal selectors group
1078
- (!isPseudo && pseudoID === t.pseudoID) || // not pseudo from equal pseudo signature group
1079
- (isPseudo && pseudoID === t.pseudoID && this.hashInHash(sg, t.sg))) { // pseudo from covered selectors group
1080
- if (imp && !t.imp) {
1081
- props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg,
1082
- freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID };
1083
- this.deleteProperty(t.block, t.id);
1084
- } else {
1085
- token.splice(i, 1);
1086
- }
1087
- }
1088
- } else if (this.needless(p, props, pre, imp, v, x, freeze)) {
1089
- token.splice(i, 1);
1090
- } else {
1091
- props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg,
1092
- freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID };
1093
- }
1094
- }
1095
- }
1096
- return token;
1097
- };
1098
-
1099
- CSSOCompressor.prototype.buildPPre = function(pre, p, v, d, freeze) {
1100
- var fp = freeze ? 'ft:' : 'ff:';
1101
- if (p.indexOf('background') !== -1) return fp + pre + d[0].s;
1102
-
1103
- var _v = v.slice(2),
1104
- colorMark = [
1105
- 0, // ident, vhash, rgb
1106
- 0, // hsl
1107
- 0, // hsla
1108
- 0 // rgba
1109
- ],
1110
- vID = '';
1111
-
1112
- for (var i = 0; i < _v.length; i++) {
1113
- if (!vID) vID = this.getVendorIDFromToken(_v[i]);
1114
- switch(_v[i][1]) {
1115
- case 'vhash':
1116
- case 'ident':
1117
- colorMark[0] = 1; break;
1118
- case 'funktion':
1119
- switch(_v[i][2][2]) {
1120
- case 'rgb':
1121
- colorMark[0] = 1; break;
1122
- case 'hsl':
1123
- colorMark[1] = 1; break;
1124
- case 'hsla':
1125
- colorMark[2] = 1; break;
1126
- case 'rgba':
1127
- colorMark[3] = 1; break;
1128
- }
1129
- break;
1130
- }
1131
- }
1132
-
1133
- return fp + pre + p + colorMark.join('') + (vID ? vID : '');
1134
- };
1135
-
1136
- CSSOCompressor.prototype.vendorID = {
1137
- '-o-': 'o',
1138
- '-moz-': 'm',
1139
- '-webkit-': 'w',
1140
- '-ms-': 'i',
1141
- '-epub-': 'e',
1142
- '-apple-': 'a',
1143
- '-xv-': 'x',
1144
- '-wap-': 'p'
1145
- };
1146
-
1147
- CSSOCompressor.prototype.getVendorIDFromToken = function(token) {
1148
- var vID;
1149
- switch(token[1]) {
1150
- case 'ident':
1151
- if (vID = this.getVendorFromString(token[2])) return this.vendorID[vID];
1152
- break;
1153
- case 'funktion':
1154
- if (vID = this.getVendorFromString(token[2][2])) return this.vendorID[vID];
1155
- break;
1156
- }
1157
- };
1158
-
1159
- CSSOCompressor.prototype.getVendorFromString = function(string) {
1160
- var vendor = string.charAt(0), i;
1161
- if (vendor === '-') {
1162
- if ((i = string.indexOf('-', 2)) !== -1) return string.substr(0, i + 1);
1163
- }
1164
- return '';
1165
- };
1166
-
1167
- CSSOCompressor.prototype.deleteProperty = function(block, id) {
1168
- var d;
1169
- for (var i = block.length - 1; i > 1; i--) {
1170
- d = block[i];
1171
- if (Array.isArray(d) && d[1] === 'declaration' && d[0].id === id) {
1172
- block.splice(i, 1);
1173
- return;
1174
- }
1175
- }
1176
- };
1177
-
1178
- CSSOCompressor.prototype.nlTable = {
1179
- 'border-width': ['border'],
1180
- 'border-style': ['border'],
1181
- 'border-color': ['border'],
1182
- 'border-top': ['border'],
1183
- 'border-right': ['border'],
1184
- 'border-bottom': ['border'],
1185
- 'border-left': ['border'],
1186
- 'border-top-width': ['border-top', 'border-width', 'border'],
1187
- 'border-right-width': ['border-right', 'border-width', 'border'],
1188
- 'border-bottom-width': ['border-bottom', 'border-width', 'border'],
1189
- 'border-left-width': ['border-left', 'border-width', 'border'],
1190
- 'border-top-style': ['border-top', 'border-style', 'border'],
1191
- 'border-right-style': ['border-right', 'border-style', 'border'],
1192
- 'border-bottom-style': ['border-bottom', 'border-style', 'border'],
1193
- 'border-left-style': ['border-left', 'border-style', 'border'],
1194
- 'border-top-color': ['border-top', 'border-color', 'border'],
1195
- 'border-right-color': ['border-right', 'border-color', 'border'],
1196
- 'border-bottom-color': ['border-bottom', 'border-color', 'border'],
1197
- 'border-left-color': ['border-left', 'border-color', 'border'],
1198
- 'margin-top': ['margin'],
1199
- 'margin-right': ['margin'],
1200
- 'margin-bottom': ['margin'],
1201
- 'margin-left': ['margin'],
1202
- 'padding-top': ['padding'],
1203
- 'padding-right': ['padding'],
1204
- 'padding-bottom': ['padding'],
1205
- 'padding-left': ['padding'],
1206
- 'font-style': ['font'],
1207
- 'font-variant': ['font'],
1208
- 'font-weight': ['font'],
1209
- 'font-size': ['font'],
1210
- 'font-family': ['font'],
1211
- 'list-style-type': ['list-style'],
1212
- 'list-style-position': ['list-style'],
1213
- 'list-style-image': ['list-style']
1214
- };
1215
-
1216
- CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d, freeze) {
1217
- var hack = name.charAt(0);
1218
- if (hack === '*' || hack === '_' || hack === '$') name = name.substr(1);
1219
- else if (hack === '/' && name.charAt(1) === '/') {
1220
- hack = '//';
1221
- name = name.substr(2);
1222
- } else hack = '';
1223
-
1224
- var vendor = this.getVendorFromString(name),
1225
- prop = name.substr(vendor.length),
1226
- x, t, ppre;
1227
-
1228
- if (prop in this.nlTable) {
1229
- x = this.nlTable[prop];
1230
- for (var i = 0; i < x.length; i++) {
1231
- ppre = this.buildPPre(pre, hack + vendor + x[i], v, d, freeze);
1232
- if (t = props[ppre]) return (!imp || t.imp);
1233
- }
1234
- }
1235
- };
1236
-
1237
- CSSOCompressor.prototype.rejoinRuleset = function(token, rule, container, i) {
1238
- var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1],
1239
- ps = p ? p[2].slice(2) : [],
1240
- pb = p ? p[3].slice(2) : [],
1241
- ts = token[2].slice(2),
1242
- tb = token[3].slice(2),
1243
- ph, th, r;
1244
-
1245
- if (!tb.length) return null;
1246
-
1247
- if (ps.length && pb.length) {
1248
- if (token[1] !== p[1]) return;
1249
- // try to join by selectors
1250
- ph = this.getHash(ps);
1251
- th = this.getHash(ts);
1252
-
1253
- if (this.equalHash(th, ph)) {
1254
- p[3] = p[3].concat(token[3].splice(2));
1255
- return null;
1256
- }
1257
- if (this.okToJoinByProperties(token, p)) {
1258
- // try to join by properties
1259
- r = this.analyze(token, p);
1260
- if (!r.ne1.length && !r.ne2.length) {
1261
- p[2] = this.cleanSelector(p[2].concat(token[2].splice(2)));
1262
- p[2][0].s = translator.translate(cleanInfo(p[2]));
1263
- return null;
1264
- }
1265
- }
1266
- }
1267
- };
1268
-
1269
- CSSOCompressor.prototype.okToJoinByProperties = function(r0, r1) {
1270
- var i0 = r0[0], i1 = r1[0];
1271
-
1272
- // same frozen ruleset
1273
- if (i0.freezeID === i1.freezeID) return true;
1274
-
1275
- // same pseudo-classes in selectors
1276
- if (i0.pseudoID === i1.pseudoID) return true;
1277
-
1278
- // different frozen rulesets
1279
- if (i0.freeze && i1.freeze) {
1280
- return this.pseudoSelectorSignature(r0[2], this.allowedPClasses) === this.pseudoSelectorSignature(r1[2], this.allowedPClasses);
1281
- }
1282
-
1283
- // is it frozen at all?
1284
- return !(i0.freeze || i1.freeze);
1285
- };
1286
-
1287
- CSSOCompressor.prototype.allowedPClasses = {
1288
- 'after': 1,
1289
- 'before': 1
1290
- };
1291
-
1292
- CSSOCompressor.prototype.containsOnlyAllowedPClasses = function(selector) {
1293
- var ss;
1294
- for (var i = 2; i < selector.length; i++) {
1295
- ss = selector[i];
1296
- for (var j = 2; j < ss.length; j++) {
1297
- if (ss[j][1] == 'pseudoc' || ss[j][1] == 'pseudoe') {
1298
- if (!(ss[j][2][2] in this.allowedPClasses)) return false;
1299
- }
1300
- }
1301
- }
1302
- return true;
1303
- };
1304
-
1305
- CSSOCompressor.prototype.restructureRuleset = function(token, rule, container, i) {
1306
- var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1],
1307
- ps = p ? p[2].slice(2) : [],
1308
- pb = p ? p[3].slice(2) : [],
1309
- tb = token[3].slice(2),
1310
- r, nr;
1311
-
1312
- if (!tb.length) return null;
1313
-
1314
- if (ps.length && pb.length) {
1315
- if (token[1] !== p[1]) return;
1316
- // try to join by properties
1317
- r = this.analyze(token, p);
1318
-
1319
- if (r.eq.length && (r.ne1.length || r.ne2.length)) {
1320
- if (r.ne1.length && !r.ne2.length) { // p in token
1321
- var ns = token[2].slice(2), // TODO: copypaste
1322
- nss = translator.translate(cleanInfo(token[2])),
1323
- sl = nss.length + // selector length
1324
- ns.length - 1, // delims length
1325
- bl = this.calcLength(r.eq) + // declarations length
1326
- r.eq.length - 1; // decldelims length
1327
- if (sl < bl) {
1328
- p[2] = this.cleanSelector(p[2].concat(token[2].slice(2)));
1329
- token[3].splice(2);
1330
- token[3] = token[3].concat(r.ne1);
1331
- return token;
1332
- }
1333
- } else if (r.ne2.length && !r.ne1.length) { // token in p
1334
- var ns = p[2].slice(2),
1335
- nss = translator.translate(cleanInfo(p[2])),
1336
- sl = nss.length + // selector length
1337
- ns.length - 1, // delims length
1338
- bl = this.calcLength(r.eq) + // declarations length
1339
- r.eq.length - 1; // decldelims length
1340
- if (sl < bl) {
1341
- token[2] = this.cleanSelector(p[2].concat(token[2].slice(2)));
1342
- p[3].splice(2);
1343
- p[3] = p[3].concat(r.ne2);
1344
- return token;
1345
- }
1346
- } else { // extract equal block?
1347
- var ns = this.cleanSelector(p[2].concat(token[2].slice(2))),
1348
- nss = translator.translate(cleanInfo(ns)),
1349
- rl = nss.length + // selector length
1350
- ns.length - 1 + // delims length
1351
- 2, // braces length
1352
- bl = this.calcLength(r.eq) + // declarations length
1353
- r.eq.length - 1; // decldelims length
1354
-
1355
- if (bl >= rl) { // ok, it's good enough to extract
1356
- ns[0].s = nss;
1357
- nr = [{f:0, l:0}, 'ruleset', ns, [{f:0,l:0}, 'block'].concat(r.eq)];
1358
- token[3].splice(2);
1359
- token[3] = token[3].concat(r.ne1);
1360
- p[3].splice(2);
1361
- p[3] = p[3].concat(r.ne2);
1362
- container.splice(i, 0, nr);
1363
- return nr;
1364
- }
1365
- }
1366
- }
1367
- }
1368
- };
1369
-
1370
- CSSOCompressor.prototype.calcLength = function(tokens) {
1371
- var r = 0;
1372
- for (var i = 0; i < tokens.length; i++) r += tokens[i][0].s.length;
1373
- return r;
1374
- };
1375
-
1376
- CSSOCompressor.prototype.cleanSelector = function(token) {
1377
- if (token.length === 2) return null;
1378
- var h = {}, s;
1379
- for (var i = 2; i < token.length; i++) {
1380
- s = token[i][0].s;
1381
- if (s in h) token.splice(i, 1), i--;
1382
- else h[s] = 1;
1383
- }
1384
-
1385
- return token;
1386
- };
1387
-
1388
- CSSOCompressor.prototype.analyze = function(r1, r2) {
1389
- var r = { eq: [], ne1: [], ne2: [] };
1390
-
1391
- if (r1[1] !== r2[1]) return r;
1392
-
1393
- var b1 = r1[3], b2 = r2[3],
1394
- d1 = b1.slice(2), d2 = b2.slice(2),
1395
- h1, h2, i, x;
1396
-
1397
- h1 = this.getHash(d1);
1398
- h2 = this.getHash(d2);
1399
-
1400
- for (i = 0; i < d1.length; i++) {
1401
- x = d1[i];
1402
- if (x[0].s in h2) r.eq.push(x);
1403
- else r.ne1.push(x);
1404
- }
1405
-
1406
- for (i = 0; i < d2.length; i++) {
1407
- x = d2[i];
1408
- if (!(x[0].s in h1)) r.ne2.push(x);
1409
- }
1410
-
1411
- return r;
1412
- };
1413
-
1414
- CSSOCompressor.prototype.equalHash = function(h0, h1) {
1415
- var k;
1416
- for (k in h0) if (!(k in h1)) return false;
1417
- for (k in h1) if (!(k in h0)) return false;
1418
- return true;
1419
- };
1420
-
1421
- CSSOCompressor.prototype.getHash = function(tokens) {
1422
- var r = {};
1423
- for (var i = 0; i < tokens.length; i++) r[tokens[i][0].s] = 1;
1424
- return r;
1425
- };
1426
-
1427
- CSSOCompressor.prototype.hashInHash = function(h0, h1) {
1428
- for (var k in h0) if (!(k in h1)) return false;
1429
- return true;
1430
- };
1431
-
1432
- CSSOCompressor.prototype.delimSelectors = function(token) {
1433
- for (var i = token.length - 1; i > 2; i--) {
1434
- token.splice(i, 0, [{}, 'delim']);
1435
- }
1436
- };
1437
-
1438
- CSSOCompressor.prototype.delimBlocks = function(token) {
1439
- for (var i = token.length - 1; i > 2; i--) {
1440
- token.splice(i, 0, [{}, 'decldelim']);
1441
- }
1442
- };
1443
-
1444
- CSSOCompressor.prototype.copyArray = function(a) {
1445
- var r = [], t;
1446
-
1447
- for (var i = 0; i < a.length; i++) {
1448
- t = a[i];
1449
- if (Array.isArray(t)) r.push(this.copyArray(t));
1450
- else if (typeof t === 'object') r.push(this.copyObject(t));
1451
- else r.push(t);
1452
- }
1453
-
1454
- return r;
1455
- };
1456
-
1457
- CSSOCompressor.prototype.copyObject = function(o) {
1458
- var r = {};
1459
- for (var k in o) r[k] = o[k];
1460
- return r;
1461
- };
1462
-
1463
- CSSOCompressor.prototype.pathUp = function(path) {
1464
- return path.substr(0, path.lastIndexOf('/'));
1465
- };
1466
- var translator = require('./translator.js').translator(),
1467
- cleanInfo = require('./util.js').cleanInfo;
1468
-
1469
- exports.compress = function(tree, ro) {
1470
- return new CSSOCompressor().compress(tree, ro);
1471
- };