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

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