sass 3.4.0.rc.2 → 3.4.0.rc.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1afd394b8f5238ff1c9946a07f19a8931388586a
4
- data.tar.gz: 4832e49d858cfd6602d9b1bd53b5e834c48ba86e
3
+ metadata.gz: 458976233a2583d8c1a41750182734aeef9a2798
4
+ data.tar.gz: 5e6bd1f3acb111febce42d566914f3b83b1a1964
5
5
  SHA512:
6
- metadata.gz: 3cfae679887b0e482a3a5f95bf20aab324acfade3778e8d3b8bd4ae6cec41a71b9e38618cbed02443226af2ed5815e0486de24384ff1e54f004e19c966022604
7
- data.tar.gz: cd272dda7c81e9a33885884080f462964db6312f36626df4f61867a4b939f5958d12562b6115438cd8bb15c6fcf5d8378b01952da0773366dbff9f3b1577ae22
6
+ metadata.gz: 73ad9f9ec78111165eca6460bf5e55baf828580ad272671ca4b31ad1cf6c1cf3566243ea034b7437d4b5a54acd34bf4657fdc3b6c5c383f9f2770901fccf4c09
7
+ data.tar.gz: 38e64c1843b8a98f1bb640764bd2830c5bf607d33630568585a3b838f11c7bba98bd390aef42e705eb6fee1bd9e3d8a25e5eadc0a5e5129561f3e35dc6410131
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2013 Hampton Catlin, Natalie Weizenbaum, and Chris Eppstein
1
+ Copyright (c) 2006-2014 Hampton Catlin, Natalie Weizenbaum, and Chris Eppstein
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.4.0.rc.2
1
+ 3.4.0.rc.3
@@ -1 +1 @@
1
- 26 July 2014 00:40:24 UTC
1
+ 02 August 2014 01:42:58 UTC
@@ -199,6 +199,7 @@ module Sass
199
199
  when :normal; options[:property_syntax] = :old
200
200
  end
201
201
  options[:sourcemap] = :auto if options[:sourcemap] == true
202
+ options[:sourcemap] = :none if options[:sourcemap] == false
202
203
 
203
204
  options
204
205
  end
@@ -36,19 +36,30 @@ module Sass::Plugin
36
36
  options.merge!(opts)
37
37
  end
38
38
 
39
- # Register a callback to be run after stylesheets are mass-updated.
39
+ # Register a callback to be run before stylesheets are mass-updated.
40
40
  # This is run whenever \{#update\_stylesheets} is called,
41
41
  # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
42
42
  # is enabled.
43
43
  #
44
- # @yield [individual_files]
45
- # @yieldparam individual_files [<(String, String)>]
46
- # Individual files to be updated, in addition to the directories
47
- # specified in the options.
44
+ # @yield [files]
45
+ # @yieldparam files [<(String, String, String)>]
46
+ # Individual files to be updated. Files in directories specified are included in this list.
48
47
  # The first element of each pair is the source file,
49
- # the second is the target CSS file.
48
+ # the second is the target CSS file,
49
+ # the third is the target sourcemap file.
50
50
  define_callback :updating_stylesheets
51
51
 
52
+ # Register a callback to be run after stylesheets are mass-updated.
53
+ # This is run whenever \{#update\_stylesheets} is called,
54
+ # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
55
+ # is enabled.
56
+ #
57
+ # @yield [updated_files]
58
+ # @yieldparam updated_files [<(String, String)>]
59
+ # Individual files that were updated.
60
+ # The first element of each pair is the source file, the second is the target CSS file.
61
+ define_callback :updated_stylesheets
62
+
52
63
  # Register a callback to be run after a single stylesheet is updated.
53
64
  # The callback is only run if the stylesheet is really updated;
54
65
  # if the CSS file is fresh, this won't be run.
@@ -67,6 +78,21 @@ module Sass::Plugin
67
78
  # The location of the sourcemap being generated, if any.
68
79
  define_callback :updated_stylesheet
69
80
 
81
+ # Register a callback to be run when compilation starts.
82
+ #
83
+ # In combination with on_updated_stylesheet, this could be used
84
+ # to collect compilation statistics like timing or to take a
85
+ # diff of the changes to the output file.
86
+ #
87
+ # @yield [template, css, sourcemap]
88
+ # @yieldparam template [String]
89
+ # The location of the Sass/SCSS file being updated.
90
+ # @yieldparam css [String]
91
+ # The location of the CSS file being generated.
92
+ # @yieldparam sourcemap [String]
93
+ # The location of the sourcemap being generated, if any.
94
+ define_callback :compilation_starting
95
+
70
96
  # Register a callback to be run when Sass decides not to update a stylesheet.
71
97
  # In particular, the callback is run when Sass finds that
72
98
  # the template file and none of its dependencies
@@ -139,7 +165,8 @@ module Sass::Plugin
139
165
  define_callback :template_deleted
140
166
 
141
167
  # Register a callback to be run when Sass deletes a CSS file.
142
- # This happens when the corresponding Sass/SCSS file has been deleted.
168
+ # This happens when the corresponding Sass/SCSS file has been deleted
169
+ # and when the compiler cleans the output files.
143
170
  #
144
171
  # @yield [filename]
145
172
  # @yieldparam filename [String]
@@ -147,7 +174,8 @@ module Sass::Plugin
147
174
  define_callback :deleting_css
148
175
 
149
176
  # Register a callback to be run when Sass deletes a sourcemap file.
150
- # This happens when the corresponding Sass/SCSS file has been deleted.
177
+ # This happens when the corresponding Sass/SCSS file has been deleted
178
+ # and when the compiler cleans the output files.
151
179
  #
152
180
  # @yield [filename]
153
181
  # @yieldparam filename [String]
@@ -162,35 +190,72 @@ module Sass::Plugin
162
190
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
163
191
  # If it has, it updates the CSS file.
164
192
  #
165
- # @param individual_files [Array<(String, String)>]
193
+ # @param individual_files [Array<(String, String[, String])>]
166
194
  # A list of files to check for updates
167
195
  # **in addition to those specified by the
168
196
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
169
197
  # The first string in each pair is the location of the Sass/SCSS file,
170
198
  # the second is the location of the CSS file that it should be compiled to.
199
+ # The third string, if provided, is the location of the Sourcemap file.
171
200
  def update_stylesheets(individual_files = [])
172
- individual_files = individual_files.dup
173
201
  Sass::Plugin.checked_for_updates = true
174
202
  staleness_checker = StalenessChecker.new(engine_options)
175
203
 
176
- template_location_array.each do |template_location, css_location|
177
- Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
178
- # Get the relative path to the file
179
- name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
180
- css = css_filename(name, css_location)
181
- sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap] != :none
182
- individual_files << [file, css, sourcemap]
183
- end
184
- end
204
+ files = file_list(individual_files)
205
+ run_updating_stylesheets(files)
185
206
 
186
- individual_files.each do |file, css, sourcemap|
207
+ updated_stylesheets = []
208
+ files.each do |file, css, sourcemap|
187
209
  # TODO: Does staleness_checker need to check the sourcemap file as well?
188
210
  if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
211
+ # XXX For consistency, this should return the sourcemap too, but it would
212
+ # XXX be an API change.
213
+ updated_stylesheets << [file, css]
189
214
  update_stylesheet(file, css, sourcemap)
190
215
  else
191
216
  run_not_updating_stylesheet(file, css, sourcemap)
192
217
  end
193
218
  end
219
+ run_updated_stylesheets(updated_stylesheets)
220
+ end
221
+
222
+ # Construct a list of files that might need to be compiled
223
+ # from the provided individual_files and the template_locations.
224
+ #
225
+ # Note: this method does not cache the results as they can change
226
+ # across invocations when sass files are added or removed.
227
+ #
228
+ # @param individual_files [Array<(String, String[, String])>]
229
+ # A list of files to check for updates
230
+ # **in addition to those specified by the
231
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
232
+ # The first string in each pair is the location of the Sass/SCSS file,
233
+ # the second is the location of the CSS file that it should be compiled to.
234
+ # The third string, if provided, is the location of the Sourcemap file.
235
+ # @return [Array<(String, String, String)>]
236
+ # A list of [sass_file, css_file, sourcemap_file] tuples similar
237
+ # to what was passed in, but expanded to include the current state
238
+ # of the directories being updated.
239
+ def file_list(individual_files = [])
240
+ files = individual_files.map do |tuple|
241
+ if tuple.size < 3
242
+ [tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
243
+ else
244
+ tuple
245
+ end
246
+ end
247
+
248
+ template_location_array.each do |template_location, css_location|
249
+ Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
250
+ # Get the relative path to the file
251
+ name = Sass::Util.pathname(file).relative_path_from(
252
+ Sass::Util.pathname(template_location.to_s)).to_s
253
+ css = css_filename(name, css_location)
254
+ sourcemap = Sass::Util.sourcemap_name(css) unless engine_options[:sourcemap] == :none
255
+ files << [file, css, sourcemap]
256
+ end
257
+ end
258
+ files
194
259
  end
195
260
 
196
261
  # Watches the template directory (or directories)
@@ -211,14 +276,19 @@ module Sass::Plugin
211
276
  # The version of Listen distributed with Sass is loaded by default,
212
277
  # but if another version has already been loaded that will be used instead.
213
278
  #
214
- # @param individual_files [Array<(String, String)>]
215
- # A list of files to watch for updates
279
+ # @param individual_files [Array<(String, String[, String])>]
280
+ # A list of files to check for updates
216
281
  # **in addition to those specified by the
217
282
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
218
283
  # The first string in each pair is the location of the Sass/SCSS file,
219
284
  # the second is the location of the CSS file that it should be compiled to.
220
- def watch(individual_files = [])
221
- update_stylesheets(individual_files)
285
+ # The third string, if provided, is the location of the Sourcemap file.
286
+ # @param options [Hash] The options that control how watching works.
287
+ # @option options [Boolean] :skip_initial_update
288
+ # Don't do an initial update when starting the watcher when true
289
+ def watch(individual_files = [], options = {})
290
+ options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
291
+ update_stylesheets(individual_files) unless options[:skip_initial_update]
222
292
 
223
293
  directories = watched_paths
224
294
  individual_files.each do |(source, _, _)|
@@ -235,7 +305,11 @@ module Sass::Plugin
235
305
 
236
306
  # TODO: Keep better track of what depends on what
237
307
  # so we don't have to run a global update every time anything changes.
238
- listener_args = directories + [{:relative_paths => false}]
308
+ # XXX The :additional_watch_paths option exists for Compass to use until
309
+ # a deprecated feature is removed. It may be removed without warning.
310
+ listener_args = directories +
311
+ Array(options[:additional_watch_paths]) +
312
+ [{:relative_paths => false}]
239
313
 
240
314
  # The native windows listener is much slower than the polling option, according to
241
315
  # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
@@ -248,6 +322,7 @@ module Sass::Plugin
248
322
 
249
323
  listener = create_listener(*listener_args) do |modified, added, removed|
250
324
  on_file_changed(individual_files, modified, added, removed)
325
+ yield(modified, added, removed) if block_given?
251
326
  end
252
327
 
253
328
  if poll && !Sass::Util.listen_geq_2?
@@ -267,6 +342,8 @@ module Sass::Plugin
267
342
  def engine_options(additional_options = {})
268
343
  opts = options.merge(additional_options)
269
344
  opts[:load_paths] = load_paths(opts)
345
+ options[:sourcemap] = :auto if options[:sourcemap] == true
346
+ options[:sourcemap] = :none if options[:sourcemap] == false
270
347
  opts
271
348
  end
272
349
 
@@ -275,12 +352,42 @@ module Sass::Plugin
275
352
  StalenessChecker.stylesheet_needs_update?(css_file, template_file)
276
353
  end
277
354
 
355
+ # Remove all output files that would be created by calling update_stylesheets, if they exist.
356
+ #
357
+ # This method runs the deleting_css and deleting_sourcemap callbacks for
358
+ # the files that are deleted.
359
+ #
360
+ # @param individual_files [Array<(String, String[, String])>]
361
+ # A list of files to check for updates
362
+ # **in addition to those specified by the
363
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
364
+ # The first string in each pair is the location of the Sass/SCSS file,
365
+ # the second is the location of the CSS file that it should be compiled to.
366
+ # The third string, if provided, is the location of the Sourcemap file.
367
+ def clean(individual_files = [])
368
+ file_list(individual_files).each do |(_, css_file, sourcemap_file)|
369
+ if File.exist?(css_file)
370
+ run_deleting_css css_file
371
+ File.delete(css_file)
372
+ end
373
+ if sourcemap_file && File.exist?(sourcemap_file)
374
+ run_deleting_sourcemap sourcemap_file
375
+ File.delete(sourcemap_file)
376
+ end
377
+ end
378
+ nil
379
+ end
380
+
278
381
  private
279
382
 
280
383
  def create_listener(*args, &block)
281
384
  Sass::Util.load_listen!
282
385
  if Sass::Util.listen_geq_2?
283
- Listen.to(*args, &block)
386
+ # Work around guard/listen#243.
387
+ options = args.pop if args.last.is_a?(Hash)
388
+ args.map do |dir|
389
+ Listen.to(dir, options, &block)
390
+ end
284
391
  else
285
392
  Listen::Listener.new(*args, &block)
286
393
  end
@@ -288,7 +395,7 @@ module Sass::Plugin
288
395
 
289
396
  def listen_to(listener)
290
397
  if Sass::Util.listen_geq_2?
291
- listener.start.join
398
+ listener.map {|l| l.start}.each {|thread| thread.join}
292
399
  else
293
400
  listener.start!
294
401
  end
@@ -328,6 +435,7 @@ module Sass::Plugin
328
435
  end
329
436
 
330
437
  removed.uniq.each do |f|
438
+ run_template_deleted(relative_to_pwd(f))
331
439
  if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
332
440
  recompile_required = true
333
441
  # This was a file we were watching explicitly and compiling to a particular location.
@@ -347,7 +455,6 @@ module Sass::Plugin
347
455
  end
348
456
  end
349
457
  end
350
- run_template_deleted(relative_to_pwd(f))
351
458
  end
352
459
 
353
460
  if recompile_required
@@ -371,6 +478,7 @@ module Sass::Plugin
371
478
  :filename => filename,
372
479
  :sourcemap_filename => sourcemap)
373
480
  mapping = nil
481
+ run_compilation_starting(filename, css, sourcemap)
374
482
  engine = Sass::Engine.for_file(filename, engine_opts)
375
483
  if sourcemap
376
484
  rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
@@ -628,10 +628,9 @@ module Sass::Script
628
628
 
629
629
  color_attrs = [[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
630
630
  if c.is_unit?("%")
631
- v = Sass::Util.check_range("$#{name}: Color value", 0..100, c, '%')
632
- v * 255 / 100.0
631
+ c.value * 255 / 100.0
633
632
  elsif c.unitless?
634
- Sass::Util.check_range("$#{name}: Color value", 0..255, c)
633
+ c.value
635
634
  else
636
635
  raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
637
636
  end
@@ -683,7 +682,6 @@ module Sass::Script
683
682
  assert_type color, :Color, :color
684
683
  assert_type alpha, :Number, :alpha
685
684
 
686
- Sass::Util.check_range('Alpha channel', 0..1, alpha)
687
685
  color.with(:alpha => alpha.value)
688
686
  when 4
689
687
  red, green, blue, alpha = args
@@ -741,11 +739,9 @@ module Sass::Script
741
739
  assert_type lightness, :Number, :lightness
742
740
  assert_type alpha, :Number, :alpha
743
741
 
744
- Sass::Util.check_range('Alpha channel', 0..1, alpha)
745
-
746
742
  h = hue.value
747
- s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
748
- l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
743
+ s = saturation.value
744
+ l = lightness.value
749
745
 
750
746
  # Don't store the string representation for function-created colors, both
751
747
  # because it's not very useful and because some functions aren't supported
@@ -1243,12 +1239,27 @@ module Sass::Script
1243
1239
  # same time
1244
1240
  def change_color(color, kwargs)
1245
1241
  assert_type color, :Color, :color
1246
- with = Sass::Util.to_hash(%w[red green blue hue saturation lightness alpha].map do |name|
1242
+ with = Sass::Util.map_hash(
1243
+ 'red' => ['Red value', 0..255],
1244
+ 'green' => ['Green value', 0..255],
1245
+ 'blue' => ['Blue value', 0..255],
1246
+ 'hue' => [],
1247
+ 'saturation' => ['Saturation', 0..100, '%'],
1248
+ 'lightness' => ['Lightness', 0..100, '%'],
1249
+ 'alpha' => ['Alpha channel', 0..1]
1250
+ ) do |name, (desc, range, unit)|
1247
1251
  val = kwargs.delete(name)
1248
1252
  next unless val
1249
1253
  assert_type val, :Number, name
1250
- [name.to_sym, val.value]
1251
- end)
1254
+
1255
+ if range
1256
+ val = Sass::Util.check_range(desc, range, val, unit)
1257
+ else
1258
+ val = val.value
1259
+ end
1260
+
1261
+ [name.to_sym, val]
1262
+ end
1252
1263
 
1253
1264
  unless kwargs.empty?
1254
1265
  name, val = kwargs.to_a.first
@@ -2609,11 +2620,7 @@ module Sass::Script
2609
2620
  assert_type amount, :Number, :amount
2610
2621
  Sass::Util.check_range('Amount', range, amount, units)
2611
2622
 
2612
- # TODO: is it worth restricting here,
2613
- # or should we do so in the Color constructor itself,
2614
- # and allow clipping in rgb() et al?
2615
- color.with(attr => Sass::Util.restrict(
2616
- color.send(attr).send(op, amount.value), range))
2623
+ color.with(attr => color.send(attr).send(op, amount.value))
2617
2624
  end
2618
2625
  end
2619
2626
  end
@@ -250,8 +250,14 @@ module Sass
250
250
  end
251
251
 
252
252
  def token
253
- if after_interpolation? && (interp_type = @interpolation_stack.pop)
254
- return string(interp_type, true)
253
+ if after_interpolation? && (interp = @interpolation_stack.pop)
254
+ interp_type, interp_value = interp
255
+ if interp_type == :special_fun
256
+ return special_fun_body(interp_value)
257
+ else
258
+ raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
259
+ return string(interp_value, true)
260
+ end
255
261
  end
256
262
 
257
263
  variable || string(:double, false) || string(:single, false) || number || id || color ||
@@ -289,7 +295,7 @@ MESSAGE
289
295
  if @scanner[2] == '#{' # '
290
296
  @scanner.pos -= 2 # Don't actually consume the #{
291
297
  @offset -= 2
292
- @interpolation_stack << re
298
+ @interpolation_stack << [:string, re]
293
299
  end
294
300
  str =
295
301
  if re == :uri
@@ -368,18 +374,34 @@ MESSAGE
368
374
  end
369
375
 
370
376
  def special_fun
371
- str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
372
- return unless str1
373
- str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
374
- c = str2.count("\n")
375
- old_line = @line
376
- old_offset = @offset
377
- @line += c
378
- @offset = c == 0 ? @offset + str2.size : str2[/\n([^\n]*)/, 1].size + 1
379
- [:special_fun,
380
- Sass::Util.merge_adjacent_strings(
381
- [str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
382
- str1.size + str2.size]
377
+ prefix = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
378
+ return unless prefix
379
+ special_fun_body(1, prefix)
380
+ end
381
+
382
+ def special_fun_body(parens, prefix = nil)
383
+ str = prefix || ''
384
+ while (scanned = scan(/.*?([()]|\#{)/m))
385
+ str << scanned
386
+ if scanned[-1] == ?(
387
+ parens += 1
388
+ next
389
+ elsif scanned[-1] == ?)
390
+ parens -= 1
391
+ next unless parens == 0
392
+ else
393
+ raise "[BUG] Unreachable" unless @scanner[1] == '#{' # '
394
+ str.slice!(-2..-1)
395
+ @scanner.pos -= 2 # Don't actually consume the #{
396
+ @offset -= 2
397
+ @interpolation_stack << [:special_fun, parens]
398
+ end
399
+
400
+ return [:special_fun, Sass::Script::Value::String.new(str)]
401
+ end
402
+
403
+ scan(/.*/)
404
+ expected!('")"')
383
405
  end
384
406
 
385
407
  def special_val
@@ -494,22 +494,14 @@ RUBY
494
494
  end
495
495
 
496
496
  def special_fun
497
- start_pos = source_position
498
- tok = try_tok(:special_fun)
499
- return paren unless tok
500
- first = literal_node(Script::Value::String.new(tok.value.first),
501
- start_pos, start_pos.after(tok.value.first))
502
- Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
503
- end_pos = i.source_range.end_pos
504
- end_pos = end_pos.after(r) if r
505
- node(
506
- Script::Tree::Interpolation.new(
507
- l, i,
508
- r && literal_node(Script::Value::String.new(r),
509
- i.source_range.end_pos, end_pos),
510
- false, false),
511
- start_pos, end_pos)
512
- end
497
+ first = try_tok(:special_fun)
498
+ return paren unless first
499
+ str = literal_node(first.value, first.source_range)
500
+ return str unless try_tok(:begin_interpolation)
501
+ mid = parse_interpolated
502
+ last = assert_expr(:special_fun)
503
+ node(Tree::Interpolation.new(str, mid, last, false, false),
504
+ first.source_range.start_pos)
513
505
  end
514
506
 
515
507
  def paren
@@ -571,6 +563,7 @@ RUBY
571
563
  :mixin_arglist => "mixin argument",
572
564
  :fn_arglist => "function argument",
573
565
  :splat => "...",
566
+ :special_fun => '")"',
574
567
  }
575
568
 
576
569
  def assert_expr(name, expected = nil)
@@ -196,9 +196,9 @@ module Sass::Script::Value
196
196
  # Constructs an RGB or HSL color object,
197
197
  # optionally with an alpha channel.
198
198
  #
199
- # The RGB values must be between 0 and 255.
200
- # The saturation and lightness values must be between 0 and 100.
201
- # The alpha value must be between 0 and 1.
199
+ # RGB values are clipped within 0 and 255.
200
+ # Saturation and lightness values are clipped within 0 and 100.
201
+ # The alpha value is clipped within 0 and 1.
202
202
  #
203
203
  # @raise [Sass::SyntaxError] if any color value isn't in the specified range
204
204
  #
@@ -258,17 +258,15 @@ module Sass::Script::Value
258
258
 
259
259
  [:red, :green, :blue].each do |k|
260
260
  next if @attrs[k].nil?
261
- @attrs[k] = @attrs[k].to_i
262
- Sass::Util.check_range("#{k.to_s.capitalize} value", 0..255, @attrs[k])
261
+ @attrs[k] = Sass::Util.restrict(@attrs[k].to_i, 0..255)
263
262
  end
264
263
 
265
264
  [:saturation, :lightness].each do |k|
266
265
  next if @attrs[k].nil?
267
- value = Number.new(@attrs[k], ['%']) # Get correct unit for error messages
268
- @attrs[k] = Sass::Util.check_range("#{k.to_s.capitalize}", 0..100, value, '%')
266
+ @attrs[k] = Sass::Util.restrict(@attrs[k], 0..100)
269
267
  end
270
268
 
271
- @attrs[:alpha] = Sass::Util.check_range("Alpha channel", 0..1, @attrs[:alpha])
269
+ @attrs[:alpha] = Sass::Util.restrict(@attrs[:alpha], 0..1)
272
270
  end
273
271
 
274
272
  # Create a new color from a valid CSS hex string.
@@ -44,7 +44,7 @@ module Sass
44
44
  str << scanner.matched
45
45
  count += 1 if scanner.matched[-1] == start
46
46
  count -= 1 if scanner.matched[-1] == finish
47
- return [str.strip, scanner.rest] if count == 0
47
+ return [str, scanner.rest] if count == 0
48
48
  end
49
49
  end
50
50
 
@@ -82,9 +82,18 @@ class CompilerTest < MiniTest::Test
82
82
 
83
83
  private
84
84
  def create_listener(*args, &on_filesystem_event)
85
- @fake_listener = FakeListener.new(*args, &on_filesystem_event)
86
- @fake_listener.on_start!(&run_during_start)
87
- @fake_listener
85
+ if Sass::Util.listen_geq_2?
86
+ options = args.pop if args.last.is_a?(Hash)
87
+ args.map do |dir|
88
+ @fake_listener = FakeListener.new(*args, &on_filesystem_event)
89
+ @fake_listener.on_start!(&run_during_start)
90
+ @fake_listener
91
+ end
92
+ else
93
+ @fake_listener = FakeListener.new(*args, &on_filesystem_event)
94
+ @fake_listener.on_start!(&run_during_start)
95
+ @fake_listener
96
+ end
88
97
  end
89
98
  end
90
99
 
@@ -90,9 +90,9 @@ class SassFunctionTest < MiniTest::Test
90
90
  assert_equal "#33cccc", evaluate("hsl($hue: 180, $saturation: 60%, $lightness: 50%)")
91
91
  end
92
92
 
93
- def test_hsl_checks_bounds
94
- assert_error_message("Saturation -114 must be between 0% and 100% for `hsl'", "hsl(10, -114, 12)");
95
- assert_error_message("Lightness 256% must be between 0% and 100% for `hsl'", "hsl(10, 10, 256%)");
93
+ def test_hsl_clamps_bounds
94
+ assert_equal("#1f1f1f", evaluate("hsl(10, -114, 12)"))
95
+ assert_equal("white", evaluate("hsl(10, 10, 256%)"))
96
96
  end
97
97
 
98
98
  def test_hsl_checks_types
@@ -108,11 +108,11 @@ class SassFunctionTest < MiniTest::Test
108
108
  assert_equal "rgba(51, 204, 204, 0.4)", evaluate("hsla($hue: 180, $saturation: 60%, $lightness: 50%, $alpha: 0.4)")
109
109
  end
110
110
 
111
- def test_hsla_checks_bounds
112
- assert_error_message("Saturation -114 must be between 0% and 100% for `hsla'", "hsla(10, -114, 12, 1)");
113
- assert_error_message("Lightness 256% must be between 0% and 100% for `hsla'", "hsla(10, 10, 256%, 0)");
114
- assert_error_message("Alpha channel -0.1 must be between 0 and 1 for `hsla'", "hsla(10, 10, 10, -0.1)");
115
- assert_error_message("Alpha channel 1.1 must be between 0 and 1 for `hsla'", "hsla(10, 10, 10, 1.1)");
111
+ def test_hsla_clamps_bounds
112
+ assert_equal("#1f1f1f", evaluate("hsla(10, -114, 12, 1)"))
113
+ assert_equal("rgba(255, 255, 255, 0)", evaluate("hsla(10, 10, 256%, 0)"))
114
+ assert_equal("rgba(28, 24, 23, 0)", evaluate("hsla(10, 10, 10, -0.1)"))
115
+ assert_equal("#1c1817", evaluate("hsla(10, 10, 10, 1.1)"))
116
116
  end
117
117
 
118
118
  def test_hsla_checks_types
@@ -212,26 +212,18 @@ class SassFunctionTest < MiniTest::Test
212
212
  assert_equal("springgreen", evaluate("rgb(0%, 100%, 50%)"))
213
213
  end
214
214
 
215
- def test_rgb_tests_bounds
216
- assert_error_message("$red: Color value 256 must be between 0 and 255 for `rgb'",
217
- "rgb(256, 1, 1)")
218
- assert_error_message("$green: Color value 256 must be between 0 and 255 for `rgb'",
219
- "rgb(1, 256, 1)")
220
- assert_error_message("$blue: Color value 256 must be between 0 and 255 for `rgb'",
221
- "rgb(1, 1, 256)")
222
- assert_error_message("$green: Color value 256 must be between 0 and 255 for `rgb'",
223
- "rgb(1, 256, 257)")
224
- assert_error_message("$red: Color value -1 must be between 0 and 255 for `rgb'",
225
- "rgb(-1, 1, 1)")
215
+ def test_rgb_clamps_bounds
216
+ assert_equal("#ff0101", evaluate("rgb(256, 1, 1)"))
217
+ assert_equal("#01ff01", evaluate("rgb(1, 256, 1)"))
218
+ assert_equal("#0101ff", evaluate("rgb(1, 1, 256)"))
219
+ assert_equal("#01ffff", evaluate("rgb(1, 256, 257)"))
220
+ assert_equal("#000101", evaluate("rgb(-1, 1, 1)"))
226
221
  end
227
222
 
228
- def test_rgb_test_percent_bounds
229
- assert_error_message("$red: Color value 100.1% must be between 0% and 100% for `rgb'",
230
- "rgb(100.1%, 0, 0)")
231
- assert_error_message("$green: Color value -0.1% must be between 0% and 100% for `rgb'",
232
- "rgb(0, -0.1%, 0)")
233
- assert_error_message("$blue: Color value 101% must be between 0% and 100% for `rgb'",
234
- "rgb(0, 0, 101%)")
223
+ def test_rgb_clamps_percent_bounds
224
+ assert_equal("red", evaluate("rgb(100.1%, 0, 0)"))
225
+ assert_equal("black", evaluate("rgb(0, -0.1%, 0)"))
226
+ assert_equal("blue", evaluate("rgb(0, 0, 101%)"))
235
227
  end
236
228
 
237
229
  def test_rgb_tests_types
@@ -247,21 +239,14 @@ class SassFunctionTest < MiniTest::Test
247
239
  assert_equal("rgba(0, 255, 127, 0)", evaluate("rgba($red: 0, $green: 255, $blue: 127, $alpha: 0)"))
248
240
  end
249
241
 
250
- def test_rgba_tests_bounds
251
- assert_error_message("$red: Color value 256 must be between 0 and 255 for `rgba'",
252
- "rgba(256, 1, 1, 0.3)")
253
- assert_error_message("$green: Color value 256 must be between 0 and 255 for `rgba'",
254
- "rgba(1, 256, 1, 0.3)")
255
- assert_error_message("$blue: Color value 256 must be between 0 and 255 for `rgba'",
256
- "rgba(1, 1, 256, 0.3)")
257
- assert_error_message("$green: Color value 256 must be between 0 and 255 for `rgba'",
258
- "rgba(1, 256, 257, 0.3)")
259
- assert_error_message("$red: Color value -1 must be between 0 and 255 for `rgba'",
260
- "rgba(-1, 1, 1, 0.3)")
261
- assert_error_message("Alpha channel -0.2 must be between 0 and 1 for `rgba'",
262
- "rgba(1, 1, 1, -0.2)")
263
- assert_error_message("Alpha channel 1.2 must be between 0 and 1 for `rgba'",
264
- "rgba(1, 1, 1, 1.2)")
242
+ def test_rgba_clamps_bounds
243
+ assert_equal("rgba(255, 1, 1, 0.3)", evaluate("rgba(256, 1, 1, 0.3)"))
244
+ assert_equal("rgba(1, 255, 1, 0.3)", evaluate("rgba(1, 256, 1, 0.3)"))
245
+ assert_equal("rgba(1, 1, 255, 0.3)", evaluate("rgba(1, 1, 256, 0.3)"))
246
+ assert_equal("rgba(1, 255, 255, 0.3)", evaluate("rgba(1, 256, 257, 0.3)"))
247
+ assert_equal("rgba(0, 1, 1, 0.3)", evaluate("rgba(-1, 1, 1, 0.3)"))
248
+ assert_equal("rgba(1, 1, 1, 0)", evaluate("rgba(1, 1, 1, -0.2)"))
249
+ assert_equal("#010101", evaluate("rgba(1, 1, 1, 1.2)"))
265
250
  end
266
251
 
267
252
  def test_rgba_tests_types
@@ -21,14 +21,14 @@ end
21
21
  class SassScriptTest < MiniTest::Test
22
22
  include Sass::Script
23
23
 
24
- def test_color_checks_input
25
- assert_raise_message(ArgumentError, "Blue value -1 must be between 0 and 255") {Sass::Script::Value::Color.new([1, 2, -1])}
26
- assert_raise_message(ArgumentError, "Red value 256 must be between 0 and 255") {Sass::Script::Value::Color.new([256, 2, 3])}
24
+ def test_color_clamps_input
25
+ assert_equal 0, Sass::Script::Value::Color.new([1, 2, -1]).blue
26
+ assert_equal 255, Sass::Script::Value::Color.new([256, 2, 3]).red
27
27
  end
28
28
 
29
- def test_color_checks_rgba_input
30
- assert_raise_message(ArgumentError, "Alpha channel 1.1 must be between 0 and 1") {Sass::Script::Value::Color.new([1, 2, 3, 1.1])}
31
- assert_raise_message(ArgumentError, "Alpha channel -0.1 must be between 0 and 1") {Sass::Script::Value::Color.new([1, 2, 3, -0.1])}
29
+ def test_color_clamps_rgba_input
30
+ assert_equal 1, Sass::Script::Value::Color.new([1, 2, 3, 1.1]).alpha
31
+ assert_equal 0, Sass::Script::Value::Color.new([1, 2, 3, -0.1]).alpha
32
32
  end
33
33
 
34
34
  def test_string_escapes
@@ -838,6 +838,23 @@ SCSS
838
838
  assert_equal 'foobar', resolve("'foo\\\nbar'")
839
839
  end
840
840
 
841
+ def test_unclosed_special_fun
842
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(foo()": expected ")", was ""') do
843
+ resolve("calc(foo()")
844
+ end
845
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(#{\')\'}": expected ")", was ""') do
846
+ resolve("calc(\#{')'}")
847
+ end
848
+ assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "calc(#{foo": expected "}", was ""') do
849
+ resolve("calc(\#{foo")
850
+ end
851
+ end
852
+
853
+ def test_special_fun_with_interpolation
854
+ assert_equal "calc())", resolve("calc(\#{')'})")
855
+ assert_equal "calc(# {foo})", resolve("calc(# {foo})")
856
+ end
857
+
841
858
  # Regression Tests
842
859
 
843
860
  def test_repeatedly_modified_color
@@ -790,6 +790,25 @@ SCSS
790
790
  CSS
791
791
  end
792
792
 
793
+ def test_multiline_interpolation_source_range
794
+ engine = Sass::Engine.new(<<-SCSS, cache: false, syntax: :scss)
795
+ p {
796
+ filter: progid:DXImageTransform(
797
+ '\#{123}');
798
+ }
799
+ SCSS
800
+
801
+ interpolated = engine.to_tree.children.
802
+ first.children.
803
+ first.value.children[1]
804
+ assert_equal interpolated.to_sass, "\#{123}"
805
+ range = interpolated.source_range
806
+ assert_equal 3, range.start_pos.line
807
+ assert_equal 12, range.start_pos.offset
808
+ assert_equal 3, range.end_pos.line
809
+ assert_equal 18, range.end_pos.offset
810
+ end
811
+
793
812
  def test_sources_array_is_uri_escaped
794
813
  map = Sass::Source::Map.new
795
814
  importer = Sass::Importers::Filesystem.new('.')
@@ -51,10 +51,8 @@ class ValueHelpersTest < MiniTest::Test
51
51
  assert_equal 0.5, color_with_alpha.alpha
52
52
  end
53
53
 
54
- def test_hex_color_alpha_enforces_0_to_1
55
- assert_raises ArgumentError do
56
- hex_color("FF007F", 50)
57
- end
54
+ def test_hex_color_alpha_clamps_0_to_1
55
+ assert_equal 1, hex_color("FF007F", 50).alpha
58
56
  end
59
57
 
60
58
  def test_hsl_color_without_alpha
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0.rc.2
4
+ version: 3.4.0.rc.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Natalie Weizenbaum
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-07-26 00:00:00.000000000 Z
13
+ date: 2014-08-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: yard