less 2.3.1 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,7 +3,9 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  gem "therubyracer", "~> 0.11.0", :require => nil, :platforms => :ruby
6
- gem "therubyrhino", "~> 1.73.3", :require => nil, :platforms => :jruby
6
+ gem "therubyrhino", ">= 2.0.2", :require => nil, :platforms => :jruby
7
7
 
8
- gem "rake"
9
- gem "rspec", "~> 2.0"
8
+ group :development do
9
+ gem "rake", :require => nil
10
+ gem "rspec", "~> 2.0"
11
+ end
data/README.md CHANGED
@@ -1,37 +1,41 @@
1
- less.rb
2
- =======
1
+ # less.rb
3
2
 
4
3
  The **dynamic** stylesheet language.
5
4
 
6
5
  <http://lesscss.org>
7
6
 
8
- about
9
- -----
7
+ [![Build Status](https://secure.travis-ci.org/cowboyd/less.rb.png)](http://travis-ci.org/cowboyd/less.rb)
8
+
9
+ ## About
10
10
 
11
11
  These are Ruby bindings for the next generation LESS, which is implemented in JavaScript
12
12
 
13
13
  For more information, visit <http://lesscss.org>.
14
14
 
15
- usage
16
- ------
15
+ ## Usage
17
16
 
18
17
  less.rb exposes the `less.Parser` constructor to ruby code via `Less::Parser`. You can instate it
19
18
  context free:
20
19
 
21
- parser = Less::Parser.new
20
+ ```ruby
21
+ parser = Less::Parser.new
22
+ ```
22
23
 
23
24
  or with configuration options:
24
25
 
25
- parser = Less::Parser.new :paths => ['./lib', 'other/lib'], :filename => 'mystyles.less'
26
+ ```ruby
27
+ parser = Less::Parser.new :paths => ['./lib', 'other/lib'], :filename => 'mystyles.less'
28
+ ```
26
29
 
27
30
  Once you have a parser instantiated, you can parse code to get your AST !
28
31
 
29
- tree = parser.parse(".class {width: 1+1}") # => Less::Tree
30
- tree.to_css #=> .class {\n width: 2;\n}\n
31
- tree.to_css(:compress => true) #=> .class{width:2;}
32
+ ```ruby
33
+ tree = parser.parse(".class {width: 1+1}") # => Less::Tree
34
+ tree.to_css #=> .class {\n width: 2;\n}\n
35
+ tree.to_css(:compress => true) #=> .class{width:2;}
36
+ ```
32
37
 
33
- license
34
- -------
38
+ ## License
35
39
 
36
40
  less.rb is licensed under the same terms as less.js
37
41
 
@@ -6,8 +6,8 @@ rescue LoadError => e
6
6
  end
7
7
 
8
8
  require 'rhino/version'
9
- if Rhino::VERSION < '1.73.3'
10
- raise LoadError, "expected gem 'therubyrhino' '>= 1.73.3' but got '#{Rhino::VERSION}'"
9
+ if Rhino::VERSION < '2.0.2'
10
+ raise LoadError, "expected gem 'therubyrhino' '>= 2.0.2' but got '#{Rhino::VERSION}'"
11
11
  end
12
12
 
13
13
  module Less
@@ -26,7 +26,6 @@ module Less
26
26
  else
27
27
  apply_1_8_compatibility!
28
28
  end
29
- fix_memory_limit! @rhino_context
30
29
  globals.each { |key, val| @rhino_context[key] = val } if globals
31
30
  end
32
31
 
@@ -74,7 +73,7 @@ module Less
74
73
  if e.value && ( e.value['message'] || e.value['type'].is_a?(String) )
75
74
  raise Less::ParseError.new(e, e.value) # LessError
76
75
  end
77
- if e.unwrap.to_s =~ /missing closing `}`/
76
+ if e.unwrap.to_s =~ /missing closing `\}`/
78
77
  raise Less::ParseError.new(e.unwrap.to_s)
79
78
  end
80
79
  if e.message && e.message[0, 12] == "Syntax Error"
@@ -83,15 +82,6 @@ module Less
83
82
  raise Less::Error.new(e)
84
83
  end
85
84
  end
86
-
87
- # Disables bytecode compiling which limits you to 64K scripts
88
- def fix_memory_limit!(context)
89
- if context.respond_to?(:optimization_level=)
90
- context.optimization_level = -1
91
- else
92
- context.instance_eval { @native.setOptimizationLevel(-1) }
93
- end
94
- end
95
85
 
96
86
  def apply_1_8_compatibility!
97
87
  # TODO rather load ecma-5.js ...
@@ -77,7 +77,7 @@ module Less
77
77
  # }, env);
78
78
  #
79
79
  # comes back as value: RuntimeError !
80
- elsif e.value.to_s =~ /missing closing `}`/
80
+ elsif e.value.to_s =~ /missing closing `\}`/
81
81
  raise Less::ParseError.new(e.value.to_s)
82
82
  end
83
83
  raise Less::Error.new(e)
@@ -15,68 +15,84 @@ module Less
15
15
  @context['console'] = Console.new
16
16
  path = Pathname(__FILE__).dirname.join('js', 'lib')
17
17
  @environment = CommonJS::Environment.new(@context, :path => path.to_s)
18
- @environment.native('path', Path.new)
19
- @environment.native('util', Sys.new)
20
- @environment.native('url', Url.new)
21
- @environment.native('http', Http.new)
22
- @environment.native('fs', Fs.new)
23
-
18
+ @environment.native('path', Path)
19
+ @environment.native('util', Util)
20
+ @environment.native('fs', FS)
21
+ @environment.native('url', Url)
22
+ @environment.native('http', Http)
24
23
  end
25
24
 
26
25
  def require(module_id)
27
26
  @environment.require(module_id)
28
27
  end
29
28
 
30
- # stubbed JS modules required by less.js
29
+ # JS exports (required by less.js) :
30
+
31
+ class Process # :nodoc:
32
+ def exit(*args)
33
+ warn("JS process.exit(#{args.first}) called from: \n#{caller.join("\n")}")
34
+ end
35
+ end
36
+
37
+ class Console # :nodoc:
38
+ def log(*msgs)
39
+ puts msgs.join(', ')
40
+ end
41
+ end
42
+
43
+ # stubbed JS modules (required by less.js) :
31
44
 
32
- class Path
33
- def join(*components)
45
+ module Path # :nodoc:
46
+ def self.join(*components)
34
47
  # node.js expands path on join
35
48
  File.expand_path(File.join(*components))
36
49
  end
37
50
 
38
- def dirname(path)
51
+ def self.dirname(path)
39
52
  File.dirname(path)
40
53
  end
41
54
 
42
- def basename(path)
55
+ def self.basename(path)
43
56
  File.basename(path)
44
57
  end
58
+
59
+ def self.resolve(path)
60
+ File.basename(path)
61
+ end
62
+
45
63
  end
46
64
 
47
- class Sys
48
- def error(*errors)
65
+ module Util # :nodoc:
66
+
67
+ def self.error(*errors)
49
68
  raise errors.join(' ')
50
69
  end
70
+
71
+ def self.puts(*args)
72
+ args.each { |arg| STDOUT.puts(arg) }
73
+ end
74
+
51
75
  end
52
76
 
53
- class Fs
54
- def statSync(path)
77
+ module FS # :nodoc:
78
+
79
+ def self.statSync(path)
55
80
  File.stat(path)
56
81
  end
57
82
 
58
- def readFile(path, encoding, callback)
83
+ def self.readFile(path, encoding, callback)
59
84
  callback.call(nil, File.read(path))
60
85
  end
86
+
61
87
  end
62
88
 
63
- class Process
64
- def exit(*args)
65
- end
66
- end
67
-
68
- class Console
69
- def log(*msgs)
70
- puts msgs.join(', ')
71
- end
72
- end
73
-
74
- class Url
75
- def resolve(*args)
89
+ module Url # :nodoc:
90
+
91
+ def self.resolve(*args)
76
92
  URI.join(*args)
77
93
  end
78
94
 
79
- def parse(url_string)
95
+ def self.parse(url_string)
80
96
  u = URI.parse(url_string)
81
97
  result = {}
82
98
  result['protocol'] = u.scheme + ':' if u.scheme
@@ -88,10 +104,12 @@ module Less
88
104
  result['hash'] = '#' + u.fragment if u.fragment
89
105
  result
90
106
  end
107
+
91
108
  end
92
109
 
93
- class Http
94
- def get(options, callback)
110
+ module Http # :nodoc:
111
+
112
+ def self.get(options, callback)
95
113
  err = nil
96
114
  begin
97
115
  #less always sends options as an object, so no need to check for string
@@ -123,51 +141,52 @@ module Less
123
141
  http.start do |req|
124
142
  response = req.get(uri.to_s)
125
143
  end
126
- sr = ServerResponse.new(response.read_body, response.code.to_i)
127
- callback.call(sr)
144
+ callback.call ServerResponse.new(response.read_body, response.code.to_i)
128
145
  rescue => e
129
146
  err = e.message
130
147
  ensure
131
- ret = HttpNodeObj.new(err)
148
+ ret = HttpGetResult.new(err)
132
149
  end
133
150
  ret
134
151
  end
135
- end
136
-
137
- class HttpNodeObj
138
- attr_accessor :err
152
+
153
+ class HttpGetResult
154
+ attr_accessor :err
139
155
 
140
- def initialize(err)
141
- @err = err
142
- end
156
+ def initialize(err)
157
+ @err = err
158
+ end
143
159
 
144
- def on(event, callback)
145
- case event
146
- when 'error'
147
- callback.call(@err) if @err #only call when error exists
148
- else
149
- callback.call()
160
+ def on(event, callback)
161
+ case event
162
+ when 'error'
163
+ callback.call(@err) if @err #only call when error exists
164
+ else
165
+ callback.call()
166
+ end
150
167
  end
151
168
  end
152
- end
153
169
 
154
- class ServerResponse
155
- attr_accessor :statusCode
156
- attr_accessor :data #faked because ServerResponse acutally implements WriteableStream
170
+ class ServerResponse
171
+ attr_accessor :statusCode
172
+ attr_accessor :data #faked because ServerResponse acutally implements WriteableStream
157
173
 
158
- def initialize(data, status_code)
159
- @data = data
160
- @statusCode = status_code
161
- end
174
+ def initialize(data, status_code)
175
+ @data = data
176
+ @statusCode = status_code
177
+ end
162
178
 
163
- def on(event, callback)
164
- case event
165
- when 'data'
166
- callback.call(@data)
167
- else
168
- callback.call()
179
+ def on(event, callback)
180
+ case event
181
+ when 'data'
182
+ callback.call(@data)
183
+ else
184
+ callback.call()
185
+ end
169
186
  end
170
187
  end
188
+
171
189
  end
190
+
172
191
  end
173
192
  end
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  module Less
2
4
 
3
5
  # Convert lesscss source into an abstract syntax Tree
@@ -5,15 +7,52 @@ module Less
5
7
 
6
8
  # Construct and configure new Less::Parser
7
9
  #
8
- # @param [Hash] opts configuration options
9
- # @option opts [Array] :paths a list of directories to search when handling \@import statements
10
- # @option opts [String] :filename to associate with resulting parse trees (useful for generating errors)
10
+ # @param [Hash] options configuration options
11
+ # @option options [Array] :paths a list of directories to search when handling \@import statements
12
+ # @option options [String] :filename to associate with resulting parse trees (useful for generating errors)
13
+ # @option options [TrueClass, FalseClass] :compress
14
+ # @option options [TrueClass, FalseClass] :strictImports
15
+ # @option options [TrueClass, FalseClass] :relativeUrls
16
+ # @option options [String] :dumpLineNumbers one of 'mediaquery', 'comments', or 'all'
11
17
  def initialize(options = {})
12
- stringy = {}
13
- Less.defaults.merge(options).each do |k,v|
14
- stringy[k.to_s] = v.is_a?(Array) ? v.map(&:to_s) : v.to_s
18
+ # LeSS supported _env_ options :
19
+ #
20
+ # - paths (unmodified) - paths to search for imports on
21
+ # - optimization - optimization level (for the chunker)
22
+ # - mime (browser only) mime type for sheet import
23
+ # - contents (browser only)
24
+ # - strictImports
25
+ # - dumpLineNumbers - whether to dump line numbers
26
+ # - compress - whether to compress
27
+ # - processImports - whether to process imports. if false then imports will not be imported
28
+ # - relativeUrls (true/false) whether to adjust URL's to be relative
29
+ # - errback (error callback function)
30
+ # - rootpath string
31
+ # - entryPath string
32
+ # - files (internal) - list of files that have been imported, used for import-once
33
+ # - currentFileInfo (internal) - information about the current file -
34
+ # for error reporting and importing and making urls relative etc :
35
+ # this.currentFileInfo = {
36
+ # filename: filename,
37
+ # relativeUrls: this.relativeUrls,
38
+ # rootpath: options.rootpath || "",
39
+ # currentDirectory: entryPath,
40
+ # entryPath: entryPath,
41
+ # rootFilename: filename
42
+ # };
43
+ #
44
+ env = {}
45
+ Less.defaults.merge(options).each do |key, val|
46
+ env[key.to_s] =
47
+ case val
48
+ when Symbol, Pathname then val.to_s
49
+ when Array
50
+ val.map!(&:to_s) if key.to_sym == :paths # might contain Pathname-s
51
+ val # keep the original passed Array
52
+ else val # true/false/String/Method
53
+ end
15
54
  end
16
- @parser = Less::JavaScript.exec { Less['Parser'].new(stringy) }
55
+ @parser = Less::JavaScript.exec { Less['Parser'].new(env) }
17
56
  end
18
57
 
19
58
  # Convert `less` source into a abstract syntaxt tree
@@ -39,7 +78,7 @@ module Less
39
78
  end
40
79
 
41
80
  def imports
42
- Less::JavaScript.exec { @parser.imports.files.map{|file, _| file} }
81
+ Less::JavaScript.exec { @parser.imports.files.map { |file, _| file } }
43
82
  end
44
83
 
45
84
  private
@@ -1,3 +1,3 @@
1
1
  module Less
2
- VERSION = '2.3.1'
2
+ VERSION = '2.3.2'
3
3
  end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe Less::Loader do
5
+
6
+ describe 'eval console.log()' do
7
+
8
+ it 'should write message to $stdout' do
9
+ stdout = $stdout; io_stub = StringIO.new
10
+ begin
11
+ $stdout = io_stub
12
+ subject.environment.runtime.eval("console.log('log much?');")
13
+ ensure
14
+ $stdout = stdout
15
+ end
16
+ io_stub.string.should == "log much?\n"
17
+ end
18
+
19
+ it 'should write messages to $stdout' do
20
+ stdout = $stdout; io_stub = StringIO.new
21
+ begin
22
+ $stdout = io_stub
23
+ subject.environment.runtime.eval("console.log('1','2','3');")
24
+ ensure
25
+ $stdout = stdout
26
+ end
27
+ io_stub.string.should == "1, 2, 3\n"
28
+ end
29
+
30
+ end
31
+
32
+ describe 'eval process.exit()' do
33
+
34
+ process = Less::Loader::Process
35
+
36
+ it 'should not raise an error' do
37
+ # process = double("process")
38
+ process.any_instance.should_receive(:warn) do |msg|
39
+ msg.should match(/JS process\.exit\(-2\)/)
40
+ end
41
+ subject.environment.runtime.eval("process.exit(-2);")
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -27,6 +27,15 @@ describe Less::Parser do
27
27
  lambda { subject.parse('body { color: @a; }').to_css }.should raise_error(Less::ParseError, /variable @a is undefined/)
28
28
  end
29
29
 
30
+ describe "when configured with source mapping" do
31
+ subject { Less::Parser.new(:filename => 'one.less', :paths => [ cwd.join('one'), cwd.join('two') ], :dumpLineNumbers => 'mediaquery') }
32
+
33
+ it "prints source maps" do
34
+ subject.parse('@import "one.less"; @import "two.less";').to_css(:compress => false).gsub(/\n/,'').strip.should eql "@media -sass-debug-info{filename{font-family:file\\:\\/\\/one\\.less}line{font-family:\\000031}}.one { width: 1;}@media -sass-debug-info{filename{font-family:file\\:\\/\\/two\\.less}line{font-family:\\000031}}.two { width: 1;}"
35
+ end
36
+
37
+ end
38
+
30
39
  describe "when configured with multiple load paths" do
31
40
  subject { Less::Parser.new :paths => [ cwd.join('one'), cwd.join('two'), cwd.join('faulty') ] }
32
41
 
@@ -78,6 +87,22 @@ describe Less::Parser do
78
87
  end
79
88
  end
80
89
 
90
+ describe "relative urls" do
91
+
92
+ it "keeps relative imports when true" do
93
+ parser = Less::Parser.new :paths => [ cwd ], :relativeUrls => true
94
+ expected = "@import \"some/some.css\";\nbody {\n background: url('some/assets/logo.png');\n}\n"
95
+ expect( parser.parse('@import "some/some.less";').to_css ).to eql expected
96
+ end
97
+
98
+ it "does not keep relative imports when false" do
99
+ parser = Less::Parser.new :paths => [ cwd ], :relativeUrls => false
100
+ expected = "@import \"some.css\";\nbody {\n background: url('assets/logo.png');\n}\n"
101
+ expect( parser.parse('@import "some/some.less";').to_css ).to eql expected
102
+ end
103
+
104
+ end
105
+
81
106
  # NOTE: runs JS tests from less.js it's a replacement for less-test.js
82
107
  describe "less-test", :integration => true do
83
108
 
File without changes
@@ -0,0 +1,4 @@
1
+ @import "some.css";
2
+ body {
3
+ background: url('assets/logo.png');
4
+ }
metadata CHANGED
@@ -1,39 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: less
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.3.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Charles Lowell
8
9
  autorequire:
9
10
  bindir: bin
10
- cert_chain:
11
- - |
12
- -----BEGIN CERTIFICATE-----
13
- MIIDPjCCAiagAwIBAgIBADANBgkqhkiG9w0BAQUFADBFMRAwDgYDVQQDDAdjb3di
14
- b3lkMRwwGgYKCZImiZPyLGQBGRYMdGhlZnJvbnRzaWRlMRMwEQYKCZImiZPyLGQB
15
- GRYDbmV0MB4XDTEzMDEzMDIxMDYwNFoXDTE0MDEzMDIxMDYwNFowRTEQMA4GA1UE
16
- AwwHY293Ym95ZDEcMBoGCgmSJomT8ixkARkWDHRoZWZyb250c2lkZTETMBEGCgmS
17
- JomT8ixkARkWA25ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO45
18
- CUxpETDGYXjCCy2dMg/aIrdrTqBqQW5ZrzhHxF9EkcdmWFr0z/qMz0JSpZ3pF11Z
19
- KYaj5PaQQpjZfLPwbuiGGkuSWi+UAac//V18xo6S4lzRBjO+gpzG9f2AOzt9b+SR
20
- Uc8UhO7QBZ5edUDxMxw9QstD+U0YBAlzsPJbHuUOqdtxXmNQCds3ZnqTgZaIpdUy
21
- CSejtrukSmlthxFzwgMezYQhcYxmkl+Q475JUodnI6Pjc6nja/Or8Y6cEWiLgeUa
22
- a+efcPGLDEbwJC7TGRrvk8yassMByBEJ3XueTMzeqWFd+665ptciojYo6BvIAR0N
23
- iLwks0x567FZyS8SqTcCAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQUxVgR
24
- 5TUqf7Hd24ICb3g4FNbM7oYwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IB
25
- AQDdJj+NzZhiYXA56z0wzRUA/Fcf6CYqKB+RFRlPssDEcHTor5SnwdWgQof/gNLi
26
- Qel1Om4zO0Shcp89jxaUqtvEdYVhmyfc0vycHmemKttNBT734yMrEJtF8Hhy+Dnz
27
- 9CzixXLvgGaRH+mf3M0+l+zIDJJr2L+39L8cyTSSRnp/srfI8aSmJKhGshudBKoC
28
- Ty6Gi071pwoJXvdMaE/6iPy7bUzlndYdHyYuWSKaO9N47HqQ62oEnBraglw6ghoi
29
- UgImJlChAzCoDP9zi9tdm6jAr7ttF25R9PPYr11ILb7dYe3qUzlNlM6zJx/nb31b
30
- IhdyRVup4qLcqYSTPsm6u7VA
31
- -----END CERTIFICATE-----
32
- date: 2013-03-06 00:00:00.000000000 Z
11
+ cert_chain: []
12
+ date: 2013-04-16 00:00:00.000000000 Z
33
13
  dependencies:
34
14
  - !ruby/object:Gem::Dependency
35
15
  name: commonjs
36
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
37
18
  requirements:
38
19
  - - ~>
39
20
  - !ruby/object:Gem::Version
@@ -41,6 +22,7 @@ dependencies:
41
22
  type: :runtime
42
23
  prerelease: false
43
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
44
26
  requirements:
45
27
  - - ~>
46
28
  - !ruby/object:Gem::Version
@@ -71,8 +53,11 @@ files:
71
53
  - lib/less/parser.rb
72
54
  - lib/less/version.rb
73
55
  - spec/less/faulty/faulty.less
56
+ - spec/less/loader_spec.rb
74
57
  - spec/less/one/one.less
75
58
  - spec/less/parser_spec.rb
59
+ - spec/less/some/some.css
60
+ - spec/less/some/some.less
76
61
  - spec/less/two/two.less
77
62
  - spec/spec_helper.rb
78
63
  - lib/less/js/.gitignore
@@ -302,30 +287,40 @@ files:
302
287
  - lib/less/js/test/less/whitespace.less
303
288
  homepage: http://lesscss.org
304
289
  licenses: []
305
- metadata: {}
306
290
  post_install_message:
307
291
  rdoc_options: []
308
292
  require_paths:
309
293
  - lib
310
294
  required_ruby_version: !ruby/object:Gem::Requirement
295
+ none: false
311
296
  requirements:
312
- - - '>='
297
+ - - ! '>='
313
298
  - !ruby/object:Gem::Version
314
299
  version: '0'
300
+ segments:
301
+ - 0
302
+ hash: -993011360193130515
315
303
  required_rubygems_version: !ruby/object:Gem::Requirement
304
+ none: false
316
305
  requirements:
317
- - - '>='
306
+ - - ! '>='
318
307
  - !ruby/object:Gem::Version
319
308
  version: '0'
309
+ segments:
310
+ - 0
311
+ hash: -993011360193130515
320
312
  requirements: []
321
313
  rubyforge_project: less
322
- rubygems_version: 2.0.0
314
+ rubygems_version: 1.8.25
323
315
  signing_key:
324
- specification_version: 4
316
+ specification_version: 3
325
317
  summary: Leaner CSS, in your browser or Ruby (via less.js)
326
318
  test_files:
327
319
  - spec/less/faulty/faulty.less
320
+ - spec/less/loader_spec.rb
328
321
  - spec/less/one/one.less
329
322
  - spec/less/parser_spec.rb
323
+ - spec/less/some/some.css
324
+ - spec/less/some/some.less
330
325
  - spec/less/two/two.less
331
326
  - spec/spec_helper.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: adb48547dd24e2ed229ba0f5213d69d47bc62b0f
4
- data.tar.gz: 3ddf770c160c85f2674b385613036210a7755362
5
- SHA512:
6
- metadata.gz: 09be273542eed8947fd1fdd5ad408fb6507649ac1e52ec80de0fdd3629165a56fee71acd08688d6a74d28ad81354ecce65c92e36e3fa102326ff1eb4219141f4
7
- data.tar.gz: 2bde14ee364b7dcf7585a8be0f10d739df566fb18b6c0bb46dd2042933ffb9e82764904601bfc1445cb352c185a9e545f4e14df548f67c5049e83e55f2a02ae7