csso-rails 0.0.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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
- };