sass 3.1.12 → 3.1.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.12
1
+ 3.1.13
@@ -53,6 +53,7 @@ module Sass
53
53
  # @param key [String]
54
54
  # @return [String] The path to the cache file.
55
55
  def path_to(key)
56
+ key = key.gsub(/[<>:\\|?*%]/) {|c| "%%%03d" % Sass::Util.ord(c)}
56
57
  File.join(cache_location, key)
57
58
  end
58
59
  end
@@ -1,7 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../sass'
2
2
  require 'sass/tree/node'
3
3
  require 'sass/scss/css_parser'
4
- require 'strscan'
5
4
 
6
5
  module Sass
7
6
  # This class converts CSS documents into Sass or SCSS templates.
@@ -1,4 +1,3 @@
1
- require 'strscan'
2
1
  require 'set'
3
2
  require 'digest/sha1'
4
3
  require 'sass/cache_stores'
@@ -219,7 +218,7 @@ module Sass
219
218
  # If you're compiling a single Sass file from the filesystem,
220
219
  # use \{Sass::Engine.for\_file}.
221
220
  # If you're compiling multiple files from the filesystem,
222
- # use {Sass::Plugin.
221
+ # use {Sass::Plugin}.
223
222
  #
224
223
  # @param template [String] The Sass template.
225
224
  # This template can be encoded using any encoding
@@ -565,7 +564,7 @@ WARNING
565
564
  end
566
565
 
567
566
  def parse_property_or_rule(line)
568
- scanner = StringScanner.new(line.text)
567
+ scanner = Sass::Util::MultibyteStringScanner.new(line.text)
569
568
  hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
570
569
  parser = Sass::SCSS::SassParser.new(scanner, @options[:filename], @line)
571
570
 
@@ -749,7 +748,7 @@ WARNING
749
748
  raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
750
749
  :line => @line + 1) unless line.children.empty?
751
750
 
752
- scanner = StringScanner.new(value)
751
+ scanner = Sass::Util::MultibyteStringScanner.new(value)
753
752
  values = []
754
753
 
755
754
  loop do
@@ -783,7 +782,7 @@ WARNING
783
782
  elsif uri
784
783
  Tree::DirectiveNode.new("@import #{uri}")
785
784
  elsif val =~ /^http:\/\//
786
- Tree::DirectiveNode.new("@import url(#{val})")
785
+ Tree::DirectiveNode.new("@import #{str}")
787
786
  else
788
787
  Tree::ImportNode.new(val)
789
788
  end
@@ -1,4 +1,3 @@
1
- require 'strscan'
2
1
  require 'sass/script/node'
3
2
  require 'sass/script/variable'
4
3
  require 'sass/script/funcall'
@@ -36,7 +36,7 @@ module Sass
36
36
  # @return [String] A string representation of the function call
37
37
  def inspect
38
38
  args = @args.map {|a| a.inspect}.join(', ')
39
- keywords = @keywords.sort_by {|k, v| k}.
39
+ keywords = Sass::Util.hash_to_a(@keywords).
40
40
  map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
41
41
  "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
42
42
  end
@@ -44,7 +44,7 @@ module Sass
44
44
  # @see Node#to_sass
45
45
  def to_sass(opts = {})
46
46
  args = @args.map {|a| a.to_sass(opts)}.join(', ')
47
- keywords = @keywords.sort_by {|k, v| k}.
47
+ keywords = Sass::Util.hash_to_a(@keywords).
48
48
  map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
49
49
  "#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
50
50
  end
@@ -1,7 +1,5 @@
1
1
  require 'sass/scss/rx'
2
2
 
3
- require 'strscan'
4
-
5
3
  module Sass
6
4
  module Script
7
5
  # The lexical analyzer for SassScript.
@@ -126,7 +124,7 @@ module Sass
126
124
  # @param options [{Symbol => Object}] An options hash;
127
125
  # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
128
126
  def initialize(str, line, offset, options)
129
- @scanner = str.is_a?(StringScanner) ? str : StringScanner.new(str)
127
+ @scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
130
128
  @line = line
131
129
  @offset = offset
132
130
  @options = options
@@ -55,26 +55,6 @@ The #options attribute is not set on this #{self.class}.
55
55
  MSG
56
56
  end
57
57
 
58
- # The SassScript `and` operation.
59
- #
60
- # @param other [Literal] The right-hand side of the operator
61
- # @return [Literal] The result of a logical and:
62
- # `other` if this literal isn't a false {Bool},
63
- # and this literal otherwise
64
- def and(other)
65
- to_bool ? other : self
66
- end
67
-
68
- # The SassScript `or` operation.
69
- #
70
- # @param other [Literal] The right-hand side of the operator
71
- # @return [Literal] The result of the logical or:
72
- # this literal if it isn't a false {Bool},
73
- # and `other` otherwise
74
- def or(other)
75
- to_bool ? self : other
76
- end
77
-
78
58
  # The SassScript `==` operation.
79
59
  # **Note that this returns a {Sass::Script::Bool} object,
80
60
  # not a Ruby boolean**.
@@ -72,6 +72,14 @@ module Sass::Script
72
72
  # @raise [Sass::SyntaxError] if the operation is undefined for the operands
73
73
  def _perform(environment)
74
74
  literal1 = @operand1.perform(environment)
75
+
76
+ # Special-case :and and :or to support short-circuiting.
77
+ if @operator == :and
78
+ return literal1.to_bool ? @operand2.perform(environment) : literal1
79
+ elsif @operator == :or
80
+ return literal1.to_bool ? literal1 : @operand2.perform(environment)
81
+ end
82
+
75
83
  literal2 = @operand2.perform(environment)
76
84
 
77
85
  begin
@@ -1,4 +1,3 @@
1
- require 'strscan'
2
1
  require 'set'
3
2
 
4
3
  module Sass
@@ -50,7 +49,7 @@ module Sass
50
49
  if @template.is_a?(StringScanner)
51
50
  @template
52
51
  else
53
- StringScanner.new(@template.gsub("\r", ""))
52
+ Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
54
53
  end
55
54
  end
56
55
 
@@ -906,7 +905,7 @@ MESSAGE
906
905
  if @throw_err
907
906
  throw :_sass_parser_error, err
908
907
  else
909
- @scanner = StringScanner.new(@scanner.string)
908
+ @scanner = Sass::Util::MultibyteStringScanner.new(@scanner.string)
910
909
  @scanner.pos = err[:pos]
911
910
  @line = err[:line]
912
911
  @expected = err[:expected]
@@ -287,6 +287,13 @@ module Sass
287
287
  # @return [Symbol]
288
288
  attr_reader :type
289
289
 
290
+ # Some psuedo-class-syntax selectors (`:after` and `:before)
291
+ # are actually considered pseudo-elements
292
+ # and must be at the end of the selector to function properly.
293
+ #
294
+ # @return [Array<String>]
295
+ FINAL_SELECTORS = %w[after before]
296
+
290
297
  # The name of the selector.
291
298
  #
292
299
  # @return [Array<String, Sass::Script::Node>]
@@ -312,6 +319,10 @@ module Sass
312
319
  @arg = arg
313
320
  end
314
321
 
322
+ def final?
323
+ type == :class && FINAL_SELECTORS.include?(name.first)
324
+ end
325
+
315
326
  # @see Selector#to_a
316
327
  def to_a
317
328
  res = [@type == :class ? ":" : "::"] + @name
@@ -319,8 +330,8 @@ module Sass
319
330
  res
320
331
  end
321
332
 
322
- # Returns `nil` if this is a pseudoclass selector
323
- # and `sels` contains a pseudoclass selector different than this one.
333
+ # Returns `nil` if this is a pseudoelement selector
334
+ # and `sels` contains a pseudoelement selector different than this one.
324
335
  #
325
336
  # @see Selector#unify
326
337
  def unify(sels)
@@ -328,6 +339,7 @@ module Sass
328
339
  sel.is_a?(Pseudo) && sel.type == :element &&
329
340
  (sel.name != self.name || sel.arg != self.arg)
330
341
  end
342
+ return sels + [self] if final?
331
343
  super
332
344
  end
333
345
  end
@@ -346,7 +358,7 @@ module Sass
346
358
  attr_reader :selector
347
359
 
348
360
  # @param [String] The name of the pseudoclass
349
- # @param [Selector::Sequence] The selector argument
361
+ # @param [Selector::CommaSequence] The selector argument
350
362
  def initialize(name, selector)
351
363
  @name = name
352
364
  @selector = selector
@@ -134,10 +134,11 @@ module Sass
134
134
  last_current.unshift(current.pop)
135
135
  end
136
136
  befores = Sass::Util.flatten(befores.map do |before|
137
- subweave(before, current).map {|seqs| seqs + last_current}
137
+ next [] unless sub = subweave(before, current)
138
+ sub.map {|seqs| seqs + last_current}
138
139
  end, 1)
139
- return befores if afters.empty?
140
140
  end
141
+ return befores
141
142
  end
142
143
 
143
144
  # This interweaves two lists of selectors,
@@ -149,14 +150,14 @@ module Sass
149
150
  # `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
150
151
  # and so on until `.baz .bang .foo .bar`.
151
152
  #
152
- # @overload def subweave(seq1, seq2)
153
153
  # @param seq1 [Array<SimpleSequence or String>]
154
154
  # @param seq2 [Array<SimpleSequence or String>]
155
155
  # @return [Array<Array<SimpleSequence or String>>]
156
- def subweave(seq1, seq2, cache = {})
156
+ def subweave(seq1, seq2)
157
157
  return [seq2] if seq1.empty?
158
158
  return [seq1] if seq2.empty?
159
159
 
160
+ return unless init = merge_initial_ops(seq1, seq2)
160
161
  seq1 = group_selectors(seq1)
161
162
  seq2 = group_selectors(seq2)
162
163
  lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
@@ -166,7 +167,7 @@ module Sass
166
167
  next s1 if subweave_superselector?(s2, s1)
167
168
  end
168
169
 
169
- diff = []
170
+ diff = [[init]]
170
171
  until lcs.empty?
171
172
  diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
172
173
  seq1.shift
@@ -178,6 +179,50 @@ module Sass
178
179
  Sass::Util.paths(diff).map {|p| p.flatten}
179
180
  end
180
181
 
182
+ # Extracts initial selector operators (`"+"`, `">"`, `"~"`, and `"\n"`)
183
+ # from two sequences and merges them together into a single array of
184
+ # selector operators.
185
+ #
186
+ # @param seq1 [Array<SimpleSequence or String>]
187
+ # @param seq2 [Array<SimpleSequence or String>]
188
+ # @return [Array<String>, nil] If there are no operators in the merged
189
+ # sequence, this will be the empty array. If the operators cannot be
190
+ # merged, this will be nil.
191
+ def merge_initial_ops(seq1, seq2)
192
+ ops1, ops2 = [], []
193
+ ops1 << seq1.shift while seq1.first.is_a?(String)
194
+ ops2 << seq2.shift while seq2.first.is_a?(String)
195
+
196
+ newline = false
197
+ newline ||= !!ops1.shift if ops1.first == "\n"
198
+ newline ||= !!ops2.shift if ops2.first == "\n"
199
+
200
+ # If neither sequence is a subsequence of the other, they cannot be
201
+ # merged successfully
202
+ lcs = Sass::Util.lcs(ops1, ops2)
203
+ return unless lcs == ops1 || lcs == ops2
204
+ return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
205
+ end
206
+
207
+ # Takes initial subsequences of `seq1` and `seq2` and returns all
208
+ # orderings of those subsequences. The initial subsequences are determined
209
+ # by a block.
210
+ #
211
+ # Destructively removes the initial subsequences of `seq1` and `seq2`.
212
+ #
213
+ # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
214
+ # denoting the boundary of the initial subsequence), this would return
215
+ # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
216
+ # `(3 4 5)`.
217
+ #
218
+ # @param seq1 [Array]
219
+ # @param seq2 [Array]
220
+ # @yield [a] Used to determine when to cut off the initial subsequences.
221
+ # Called repeatedly for each sequence until it returns true.
222
+ # @yieldparam a [Array] A final subsequence of one input sequence after
223
+ # cutting off some initial subsequence.
224
+ # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
225
+ # here.
181
226
  def chunks(seq1, seq2)
182
227
  chunk1 = []
183
228
  chunk1 << seq1.shift until yield seq1
@@ -189,6 +234,15 @@ module Sass
189
234
  [chunk1 + chunk2, chunk2 + chunk1]
190
235
  end
191
236
 
237
+ # Groups a sequence into subsequences. The subsequences are determined by
238
+ # strings; adjacent non-string elements will be put into separate groups,
239
+ # but any element adjacent to a string will be grouped with that string.
240
+ #
241
+ # For example, `(A B "C" D E "F" G "H" "I" J)` will become `[(A) (B "C" D)
242
+ # (E "F" G "H" "I" J)]`.
243
+ #
244
+ # @param seq [Array]
245
+ # @return [Array<Array>]
192
246
  def group_selectors(seq)
193
247
  newseq = []
194
248
  tail = seq.dup
@@ -202,6 +256,12 @@ module Sass
202
256
  return newseq
203
257
  end
204
258
 
259
+ # Given two sequences of simple selectors, returns whether `sseq1` is a
260
+ # superselector of `sseq2`.
261
+ #
262
+ # @param sseq1 [Array<SimpleSelector or String>]
263
+ # @param sseq2 [Array<SimpleSelector or String>]
264
+ # @return [Boolean]
205
265
  def subweave_superselector?(sseq1, sseq2)
206
266
  if sseq1.size > 1
207
267
  # More complex selectors are never superselectors of less complex ones
@@ -79,7 +79,7 @@ module Sass
79
79
  sels_with_ix = Sass::Util.enum_with_index(sels)
80
80
  _, i =
81
81
  if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
82
- sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && sels.last.type == :element}
82
+ sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.final? || sels.last.type == :element)}
83
83
  else
84
84
  sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
85
85
  end
@@ -1,5 +1,3 @@
1
- require 'strscan'
2
-
3
1
  module Sass
4
2
  # This module contains functionality that's shared between Haml and Sass.
5
3
  module Shared
@@ -16,7 +14,7 @@ module Sass
16
14
  # @yieldparam scan [StringScanner] The scanner scanning through the string
17
15
  # @return [String] The text remaining in the scanner after all `#{`s have been processed
18
16
  def handle_interpolation(str)
19
- scan = StringScanner.new(str)
17
+ scan = Sass::Util::MultibyteStringScanner.new(str)
20
18
  yield scan while scan.scan(/(.*?)(\\*)\#\{/m)
21
19
  scan.rest
22
20
  end
@@ -40,7 +38,7 @@ module Sass
40
38
  # `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
41
39
  def balance(scanner, start, finish, count = 0)
42
40
  str = ''
43
- scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
41
+ scanner = Sass::Util::MultibyteStringScanner.new(scanner) unless scanner.is_a? StringScanner
44
42
  regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
45
43
  while scanner.scan(regexp)
46
44
  str << scanner.matched
@@ -177,7 +177,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
177
177
  def visit_mixin(node)
178
178
  unless node.args.empty? && node.keywords.empty?
179
179
  args = node.args.map {|a| a.to_sass(@options)}.join(", ")
180
- keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
180
+ keywords = Sass::Util.hash_to_a(node.keywords).
181
+ map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
181
182
  arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
182
183
  end
183
184
  "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
@@ -52,12 +52,26 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
52
52
  def visit_root(node)
53
53
  yield
54
54
 
55
- # In Ruby 1.9 we can make all @charset nodes invisible
56
- # and infer the final @charset from the encoding of the final string.
57
- if Sass::Util.ruby1_8? && parent.nil?
58
- charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
59
- node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
60
- node.children.unshift charset if charset
55
+ if parent.nil?
56
+ # In Ruby 1.9 we can make all @charset nodes invisible
57
+ # and infer the final @charset from the encoding of the final string.
58
+ if Sass::Util.ruby1_8?
59
+ charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
60
+ node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
61
+ node.children.unshift charset if charset
62
+ end
63
+
64
+ imports = Sass::Util.extract!(node.children) do |c|
65
+ c.is_a?(Sass::Tree::DirectiveNode) && c.value =~ /^@import /i
66
+ end
67
+ charset_and_index = Sass::Util.ruby1_8? &&
68
+ node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
69
+ if charset_and_index
70
+ index = charset_and_index.last
71
+ node.children = node.children[0..index] + imports + node.children[index+1..-1]
72
+ else
73
+ node.children = imports + node.children
74
+ end
61
75
  end
62
76
 
63
77
  return node, @extends
@@ -226,6 +226,8 @@ END
226
226
  # Runs SassScript interpolation in the selector,
227
227
  # and then parses the result into a {Sass::Selector::CommaSequence}.
228
228
  def visit_rule(node)
229
+ rule = node.rule
230
+ rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
229
231
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
230
232
  node.parsed_rules ||= parser.parse_selector
231
233
  if node.options[:trace_selectors]
@@ -270,6 +272,15 @@ END
270
272
 
271
273
  def visit_directive(node)
272
274
  if node.value['#{']
275
+ if node.value =~ /^@import (?!url\()/
276
+ Sass::Util.sass_warn <<WARNING
277
+ DEPRECATION WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
278
+ @import directives using \#{} interpolation will need to use url() in Sass 3.2.
279
+ For example:
280
+
281
+ @import url("http://\#{$url}/style.css");
282
+ WARNING
283
+ end
273
284
  node.value = run_interp(Sass::Engine.parse_interp(node.value, node.line, 0, node.options))
274
285
  end
275
286
  yield
@@ -67,8 +67,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
67
67
  end
68
68
 
69
69
  def visit_directive(node)
70
+ was_in_directive = @in_directive
70
71
  return node.value + ";" unless node.has_children
71
72
  return node.value + " {}" if node.children.empty?
73
+ @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
72
74
  result = if node.style == :compressed
73
75
  "#{node.value}{"
74
76
  else
@@ -101,6 +103,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
101
103
  else
102
104
  (node.style == :expanded ? "\n" : " ") + "}\n"
103
105
  end
106
+ ensure
107
+ @in_directive = was_in_directive
104
108
  end
105
109
 
106
110
  def visit_media(node)
@@ -132,7 +136,11 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
132
136
 
133
137
  joined_rules = node.resolved_rules.members.map do |seq|
134
138
  rule_part = seq.to_a.join
135
- rule_part.gsub!(/\s*([^,])\s*\n\s*/m, '\1 ') if node.style == :compressed
139
+ if node.style == :compressed
140
+ rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
141
+ rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
142
+ rule_part.strip!
143
+ end
136
144
  rule_part
137
145
  end.join(rule_separator)
138
146
 
@@ -144,7 +152,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
144
152
  old_spaces = ' ' * @tabs
145
153
  spaces = ' ' * (@tabs + 1)
146
154
  if node.style != :compressed
147
- if node.options[:debug_info]
155
+ if node.options[:debug_info] && !@in_directive
148
156
  to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
149
157
  elsif node.options[:trace_selectors]
150
158
  to_return << "#{old_spaces}/* "
@@ -190,7 +198,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
190
198
 
191
199
  def debug_info_rule(debug_info, options)
192
200
  node = Sass::Tree::DirectiveNode.new("@media -sass-debug-info")
193
- debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
201
+ Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
194
202
  rule = Sass::Tree::RuleNode.new([""])
195
203
  rule.resolved_rules = Sass::Selector::CommaSequence.new(
196
204
  [Sass::Selector::Sequence.new(
@@ -2,7 +2,6 @@ require 'erb'
2
2
  require 'set'
3
3
  require 'enumerator'
4
4
  require 'stringio'
5
- require 'strscan'
6
5
  require 'rbconfig'
7
6
 
8
7
  require 'sass/root'
@@ -219,6 +218,20 @@ module Sass
219
218
  lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
220
219
  end
221
220
 
221
+ # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
222
+ # with the following exceptions:
223
+ #
224
+ # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
225
+ # * In Ruby 1.9 when running tests, this is ordered in the same way it would
226
+ # be under Ruby 1.8 (sorted key order rather than insertion order).
227
+ #
228
+ # @param hash [Hash]
229
+ # @return [Array]
230
+ def hash_to_a(hash)
231
+ return has.to_a unless ruby1_8? || defined?(Test::Unit)
232
+ return hash.sort_by {|k, v| k}
233
+ end
234
+
222
235
  # Returns information about the caller of the previous method.
223
236
  #
224
237
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
@@ -562,6 +575,24 @@ MSG
562
575
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
563
576
  end
564
577
 
578
+ # Destructively removes all elements from an array that match a block, and
579
+ # returns the removed elements.
580
+ #
581
+ # @param array [Array] The array from which to remove elements.
582
+ # @yield [el] Called for each element.
583
+ # @yieldparam el [*] The element to test.
584
+ # @yieldreturn [Boolean] Whether or not to extract the element.
585
+ # @return [Array] The extracted elements.
586
+ def extract!(array)
587
+ out = []
588
+ array.reject! do |e|
589
+ next false unless yield e
590
+ out << e
591
+ true
592
+ end
593
+ out
594
+ end
595
+
565
596
  # Returns the ASCII code of the given character.
566
597
  #
567
598
  # @param c [String] All characters but the first are ignored.
@@ -719,3 +750,5 @@ MSG
719
750
  end
720
751
  end
721
752
  end
753
+
754
+ require 'sass/util/multibyte_string_scanner'
@@ -0,0 +1,134 @@
1
+ require 'strscan'
2
+
3
+ if Sass::Util.ruby1_8?
4
+ Sass::Util::MultibyteStringScanner = StringScanner
5
+ else
6
+ # A wrapper of the native StringScanner class that works correctly with
7
+ # multibyte character encodings. The native class deals only in bytes, not
8
+ # characters, for methods like [#pos] and [#matched_size]. This class deals
9
+ # only in characters, instead.
10
+ class Sass::Util::MultibyteStringScanner < StringScanner
11
+ def self.new(str)
12
+ return StringScanner.new(str) if str.ascii_only?
13
+ super
14
+ end
15
+
16
+ def initialize(str)
17
+ super
18
+ @mb_pos = 0
19
+ @mb_matched_size = nil
20
+ @mb_last_pos = nil
21
+ end
22
+
23
+ alias_method :byte_pos, :pos
24
+ alias_method :byte_matched_size, :matched_size
25
+
26
+ def check(pattern); _match super; end
27
+ def check_until(pattern); _matched super; end
28
+ def getch; _forward _match super; end
29
+ def match?(pattern); _size check(pattern); end
30
+ def matched_size; @mb_matched_size; end
31
+ def peek(len); string[@mb_pos, len]; end
32
+ alias_method :peep, :peek
33
+ def pos; @mb_pos; end
34
+ alias_method :pointer, :pos
35
+ def rest_size; rest.size; end
36
+ def scan(pattern); _forward _match super; end
37
+ def scan_until(pattern); _forward _matched super; end
38
+ def skip(pattern); _size scan(pattern); end
39
+ def skip_until(pattern); _matched _size scan_until(pattern); end
40
+
41
+ def get_byte
42
+ raise "MultibyteStringScanner doesn't support #get_byte."
43
+ end
44
+
45
+ def getbyte
46
+ raise "MultibyteStringScanner doesn't support #getbyte."
47
+ end
48
+
49
+ def pos=(n)
50
+ @mb_last_pos = nil
51
+
52
+ # We set position kind of a lot during parsing, so we want it to be as
53
+ # efficient as possible. This is complicated by the fact that UTF-8 is a
54
+ # variable-length encoding, so it's difficult to find the byte length that
55
+ # corresponds to a given character length.
56
+ #
57
+ # Our heuristic here is to try to count the fewest possible characters. So
58
+ # if the new position is close to the current one, just count the
59
+ # characters between the two; if the new position is closer to the
60
+ # beginning of the string, just count the characters from there.
61
+ if @mb_pos - n < @mb_pos / 2
62
+ # New position is close to old position
63
+ byte_delta = @mb_pos > n ? -string[n...@mb_pos].bytesize : string[@mb_pos...n].bytesize
64
+ super(byte_pos + byte_delta)
65
+ else
66
+ # New position is close to BOS
67
+ super(string[0...n].bytesize)
68
+ end
69
+ @mb_pos = n
70
+ end
71
+
72
+ def reset
73
+ @mb_pos = 0
74
+ @mb_matched_size = nil
75
+ @mb_last_pos = nil
76
+ super
77
+ end
78
+
79
+ def scan_full(pattern, advance_pointer_p, return_string_p)
80
+ res = _match super(pattern, advance_pointer_p, true)
81
+ _forward res if advance_pointer_p
82
+ return res if return_string_p
83
+ end
84
+
85
+ def search_full(pattern, advance_pointer_p, return_string_p)
86
+ res = super(pattern, advance_pointer_p, true)
87
+ _forward res if advance_pointer_p
88
+ _matched((res if return_string_p))
89
+ end
90
+
91
+ def string=(str)
92
+ @mb_pos = 0
93
+ @mb_matched_size = nil
94
+ @mb_last_pos = nil
95
+ super
96
+ end
97
+
98
+ def terminate
99
+ @mb_pos = string.size
100
+ @mb_matched_size = nil
101
+ @mb_last_pos = nil
102
+ super
103
+ end
104
+ alias_method :clear, :terminate
105
+
106
+ def unscan
107
+ super
108
+ @mb_pos = @mb_last_pos
109
+ @mb_last_pos = @mb_matched_size = nil
110
+ end
111
+
112
+ private
113
+
114
+ def _size(str)
115
+ str && str.size
116
+ end
117
+
118
+ def _match(str)
119
+ @mb_matched_size = str && str.size
120
+ str
121
+ end
122
+
123
+ def _matched(res)
124
+ _match matched
125
+ res
126
+ end
127
+
128
+ def _forward(str)
129
+ @mb_last_pos = @mb_pos
130
+ @mb_pos += str.size if str
131
+ str
132
+ end
133
+ end
134
+ end
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- require 'test_helper'
4
- require 'sass/test_helper'
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require File.dirname(__FILE__) + '/test_helper'
5
5
  require 'sass/engine'
6
6
  require 'stringio'
7
7
  require 'mock_importer'
@@ -608,13 +608,21 @@ CSS
608
608
  end
609
609
 
610
610
  def test_http_import
611
- assert_equal("@import url(http://fonts.googleapis.com/css?family=Droid+Sans);\n",
611
+ assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
612
612
  render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
613
613
  end
614
614
 
615
- def test_http_import_with_interpolation
616
- assert_equal("@import url(http://fonts.googleapis.com/css?family=Droid+Sans);\n",
617
- render("$family: unquote(\"Droid+Sans\")\n@import \"http://fonts.googleapis.com/css?family=\#{$family}\"\n"))
615
+ def test_import_with_interpolation
616
+ assert_warning(<<WARNING) do
617
+ DEPRECATION WARNING on line 2 of test_import_with_interpolation_inline.sass:
618
+ @import directives using \#{} interpolation will need to use url() in Sass 3.2.
619
+ For example:
620
+
621
+ @import url("http://\#{$url}/style.css");
622
+ WARNING
623
+ assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
624
+ render("$family: unquote(\"Droid+Sans\")\n@import \"http://fonts.googleapis.com/css?family=\#{$family}\"\n"))
625
+ end
618
626
  assert_equal("@import url(\"http://fonts.googleapis.com/css?family=Droid+Sans\");\n",
619
627
  render("$family: unquote(\"Droid+Sans\")\n@import url(\"http://fonts.googleapis.com/css?family=\#{$family}\")\n"))
620
628
  end
@@ -1006,6 +1014,23 @@ foo
1006
1014
  SASS
1007
1015
  end
1008
1016
 
1017
+ def test_debug_info_in_keyframes
1018
+ assert_equal(<<CSS, render(<<SASS, :debug_info => true))
1019
+ @-webkit-keyframes warm {
1020
+ from {
1021
+ color: black; }
1022
+
1023
+ to {
1024
+ color: red; } }
1025
+ CSS
1026
+ @-webkit-keyframes warm
1027
+ from
1028
+ color: black
1029
+ to
1030
+ color: red
1031
+ SASS
1032
+ end
1033
+
1009
1034
  def test_empty_first_line
1010
1035
  assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
1011
1036
  end
@@ -2448,6 +2473,15 @@ CSS
2448
2473
  SASS
2449
2474
  end
2450
2475
 
2476
+ def test_selector_compression
2477
+ assert_equal <<CSS, render(<<SASS, :style => :compressed)
2478
+ a>b,c+d,:-moz-any(e,f,g){h:i}
2479
+ CSS
2480
+ a > b, c + d, :-moz-any(e, f, g)
2481
+ h: i
2482
+ SASS
2483
+ end
2484
+
2451
2485
  # Encodings
2452
2486
 
2453
2487
  unless Sass::Util.ruby1_8?
@@ -2560,6 +2594,31 @@ CSS
2560
2594
  a: b
2561
2595
  SASS
2562
2596
  end
2597
+
2598
+ # Encoding Regression Test
2599
+
2600
+ def test_multibyte_prop_name
2601
+ assert_equal(<<CSS, render(<<SASS))
2602
+ @charset "UTF-8";
2603
+ #bar {
2604
+ cölor: blue; }
2605
+ CSS
2606
+ #bar
2607
+ cölor: blue
2608
+ SASS
2609
+ end
2610
+
2611
+ def test_multibyte_and_interpolation
2612
+ assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
2613
+ #bar {
2614
+ background: a 0%; }
2615
+ CSS
2616
+ #bar {
2617
+ // 
2618
+ background: \#{a} 0%;
2619
+ }
2620
+ SCSS
2621
+ end
2563
2622
  end
2564
2623
 
2565
2624
  def test_original_filename_set
@@ -741,6 +741,22 @@ SCSS
741
741
  CSS
742
742
  :foo.baz {a: b}
743
743
  :bar {@extend .baz}
744
+ SCSS
745
+
746
+ assert_equal <<CSS, render(<<SCSS)
747
+ .baz:foo, :foo:after {
748
+ a: b; }
749
+ CSS
750
+ .baz:foo {a: b}
751
+ :after {@extend .baz}
752
+ SCSS
753
+
754
+ assert_equal <<CSS, render(<<SCSS)
755
+ .baz:after, :foo:after {
756
+ a: b; }
757
+ CSS
758
+ .baz:after {a: b}
759
+ :foo {@extend .baz}
744
760
  SCSS
745
761
 
746
762
  assert_equal <<CSS, render(<<SCSS)
@@ -1339,6 +1355,78 @@ CSS
1339
1355
  SCSS
1340
1356
  end
1341
1357
 
1358
+ # Regression Tests
1359
+
1360
+ def test_duplicated_selector_with_newlines
1361
+ assert_equal(<<CSS, render(<<SCSS))
1362
+ .example-1-1,
1363
+ .example-1-2,
1364
+ .my-page-1 .my-module-1-1,
1365
+ .example-1-3 {
1366
+ a: b; }
1367
+ CSS
1368
+ .example-1-1,
1369
+ .example-1-2,
1370
+ .example-1-3 {
1371
+ a: b;
1372
+ }
1373
+
1374
+ .my-page-1 .my-module-1-1 {@extend .example-1-2}
1375
+ SCSS
1376
+ end
1377
+
1378
+ def test_nested_selector_with_child_selector_hack_extendee
1379
+ assert_equal <<CSS, render(<<SCSS)
1380
+ > .foo, > foo bar {
1381
+ a: b; }
1382
+ CSS
1383
+ > .foo {a: b}
1384
+ foo bar {@extend .foo}
1385
+ SCSS
1386
+ end
1387
+
1388
+ def test_nested_selector_with_child_selector_hack_extender
1389
+ assert_equal <<CSS, render(<<SCSS)
1390
+ .foo .bar, > .foo foo bar, > foo .foo bar {
1391
+ a: b; }
1392
+ CSS
1393
+ .foo .bar {a: b}
1394
+ > foo bar {@extend .bar}
1395
+ SCSS
1396
+ end
1397
+
1398
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee
1399
+ assert_equal <<CSS, render(<<SCSS)
1400
+ > .foo, > foo bar {
1401
+ a: b; }
1402
+ CSS
1403
+ > .foo {a: b}
1404
+ > foo bar {@extend .foo}
1405
+ SCSS
1406
+ end
1407
+
1408
+ def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
1409
+ assert_equal <<CSS, render(<<SCSS)
1410
+ ~ .foo {
1411
+ a: b; }
1412
+ CSS
1413
+ ~ .foo {a: b}
1414
+ > foo bar {@extend .foo}
1415
+ SCSS
1416
+ end
1417
+
1418
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
1419
+ assert_equal <<CSS, render(<<SCSS)
1420
+ > .foo, > flip,
1421
+ > foo bar {
1422
+ a: b; }
1423
+ CSS
1424
+ > .foo {a: b}
1425
+ flip,
1426
+ > foo bar {@extend .foo}
1427
+ SCSS
1428
+ end
1429
+
1342
1430
  private
1343
1431
 
1344
1432
  def render(sass, options = {})
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -22,8 +24,6 @@ body { font: Arial; background: blue; }
22
24
  #content.user.show #container.top #column.right { width: 600px; }
23
25
  #content.user.show #container.bottom { background: brown; }
24
26
 
25
- @import url(basic.css);
26
- @import url(../results/complex.css);
27
27
  #foo { background-color: #bbaaff; }
28
28
 
29
29
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
24
26
  #content.user.show #container.top #column.right { width: 600px; }
25
27
  #content.user.show #container.bottom { background: brown; }
26
28
 
27
- @import url(basic.css);
28
- @import url(../results/complex.css);
29
29
  #foo { background-color: #bbaaff; }
30
30
 
31
31
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -1,4 +1,5 @@
1
1
  @charset "UTF-8";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: щ; }
@@ -1,4 +1,5 @@
1
1
  @charset "IBM866";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: �; }
@@ -1,4 +1,5 @@
1
1
  @charset "IBM866";
2
+ @import url(foo.css);
2
3
  .foo { a: b; }
3
4
 
4
5
  .bar { a: �; }
@@ -1,3 +1,5 @@
1
+ @import url(basic.css);
2
+ @import url(../results/complex.css);
1
3
  imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
2
4
 
3
5
  body { font: Arial; background: blue; }
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
24
26
  #content.user.show #container.top #column.right { width: 600px; }
25
27
  #content.user.show #container.bottom { background: brown; }
26
28
 
27
- @import url(basic.css);
28
- @import url(../results/complex.css);
29
29
  #foo { background-color: #bbaaff; }
30
30
 
31
31
  nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
@@ -432,6 +432,11 @@ SASS
432
432
  assert_raise_message(Sass::SyntaxError, "wrong number of arguments (1 for 0) for `arg-error'") {resolve("arg-error(1)")}
433
433
  end
434
434
 
435
+ def test_boolean_ops_short_circuit
436
+ assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Bool.new(false)))
437
+ assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Bool.new(true)))
438
+ end
439
+
435
440
  # Regression Tests
436
441
 
437
442
  def test_funcall_has_higher_precedence_than_color_name
@@ -1224,11 +1224,11 @@ SCSS
1224
1224
 
1225
1225
 
1226
1226
  def test_newlines_removed_from_selectors_when_compressed
1227
- assert_equal <<CSS, render(<<SCSS, :style=>:compressed)
1227
+ assert_equal <<CSS, render(<<SCSS, :style => :compressed)
1228
1228
  z a,z b{display:block}
1229
1229
  CSS
1230
- a,
1231
- b {
1230
+ a
1231
+ , b {
1232
1232
  z & {
1233
1233
  display: block;
1234
1234
  }
@@ -1,6 +1,8 @@
1
1
  .foo
2
2
  a: b
3
3
 
4
+ @import "foo.css"
5
+
4
6
  // Even though the imported file is in IBM866,
5
7
  // since the root file is in UTF-8/ASCII
6
8
  // the output will end up being UTF-8.
@@ -1,4 +1,6 @@
1
1
  .foo
2
2
  a: b
3
3
 
4
+ @import "foo.css"
5
+
4
6
  @import "imported_charset_ibm866"
@@ -3,6 +3,8 @@
3
3
  .foo
4
4
  a: b
5
5
 
6
+ @import "foo.css"
7
+
6
8
  // Even though the imported file is in UTF-8,
7
9
  // since the root file is in IBM866
8
10
  // the output will end up being IBM866.
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../../test_helper'
4
+
5
+ unless Sass::Util.ruby1_8?
6
+ class MultibyteStringScannerTest < Test::Unit::TestCase
7
+ def setup
8
+ @scanner = Sass::Util::MultibyteStringScanner.new("cölorfül")
9
+ end
10
+
11
+ def test_initial
12
+ assert_scanner_state 0, 0, nil, nil
13
+ end
14
+
15
+ def test_check
16
+ assert_equal 'cö', @scanner.check(/../)
17
+ assert_scanner_state 0, 0, 2, 3
18
+ assert_equal 0, @scanner.pos
19
+ assert_equal 0, @scanner.pos
20
+ assert_equal 2, @scanner.matched_size
21
+ assert_equal 3, @scanner.byte_matched_size
22
+ end
23
+
24
+ def test_check_until
25
+ assert_equal 'cölorfü', @scanner.check_until(/f./)
26
+ assert_scanner_state 0, 0, 2, 3
27
+ end
28
+
29
+ def test_getch
30
+ assert_equal 'c', @scanner.getch
31
+ assert_equal 'ö', @scanner.getch
32
+ assert_scanner_state 2, 3, 1, 2
33
+ end
34
+
35
+ def test_match?
36
+ assert_equal 2, @scanner.match?(/../)
37
+ assert_scanner_state 0, 0, 2, 3
38
+ end
39
+
40
+ def test_peek
41
+ assert_equal 'cö', @scanner.peek(2)
42
+ assert_scanner_state 0, 0, nil, nil
43
+ end
44
+
45
+ def test_rest_size
46
+ assert_equal 'cö', @scanner.scan(/../)
47
+ assert_equal 6, @scanner.rest_size
48
+ end
49
+
50
+ def test_scan
51
+ assert_equal 'cö', @scanner.scan(/../)
52
+ assert_scanner_state 2, 3, 2, 3
53
+ end
54
+
55
+ def test_scan_until
56
+ assert_equal 'cölorfü', @scanner.scan_until(/f./)
57
+ assert_scanner_state 7, 9, 2, 3
58
+ end
59
+
60
+ def test_skip
61
+ assert_equal 2, @scanner.skip(/../)
62
+ assert_scanner_state 2, 3, 2, 3
63
+ end
64
+
65
+ def test_skip_until
66
+ assert_equal 7, @scanner.skip_until(/f./)
67
+ assert_scanner_state 7, 9, 2, 3
68
+ end
69
+
70
+ def test_set_pos
71
+ @scanner.pos = 7
72
+ assert_scanner_state 7, 9, nil, nil
73
+ @scanner.pos = 6
74
+ assert_scanner_state 6, 7, nil, nil
75
+ @scanner.pos = 1
76
+ assert_scanner_state 1, 1, nil, nil
77
+ end
78
+
79
+ def test_reset
80
+ @scanner.scan(/../)
81
+ @scanner.reset
82
+ assert_scanner_state 0, 0, nil, nil
83
+ end
84
+
85
+ def test_scan_full
86
+ assert_equal 'cö', @scanner.scan_full(/../, true, true)
87
+ assert_scanner_state 2, 3, 2, 3
88
+
89
+ @scanner.reset
90
+ assert_equal 'cö', @scanner.scan_full(/../, false, true)
91
+ assert_scanner_state 0, 0, 2, 3
92
+
93
+ @scanner.reset
94
+ assert_nil @scanner.scan_full(/../, true, false)
95
+ assert_scanner_state 2, 3, 2, 3
96
+
97
+ @scanner.reset
98
+ assert_nil @scanner.scan_full(/../, false, false)
99
+ assert_scanner_state 0, 0, 2, 3
100
+ end
101
+
102
+ def test_search_full
103
+ assert_equal 'cölorfü', @scanner.search_full(/f./, true, true)
104
+ assert_scanner_state 7, 9, 2, 3
105
+
106
+ @scanner.reset
107
+ assert_equal 'cölorfü', @scanner.search_full(/f./, false, true)
108
+ assert_scanner_state 0, 0, 2, 3
109
+
110
+ @scanner.reset
111
+ assert_nil @scanner.search_full(/f./, true, false)
112
+ assert_scanner_state 7, 9, 2, 3
113
+
114
+ @scanner.reset
115
+ assert_nil @scanner.search_full(/f./, false, false)
116
+ assert_scanner_state 0, 0, 2, 3
117
+ end
118
+
119
+ def test_set_string
120
+ @scanner.scan(/../)
121
+ @scanner.string = 'föóbâr'
122
+ assert_scanner_state 0, 0, nil, nil
123
+ end
124
+
125
+ def test_terminate
126
+ @scanner.scan(/../)
127
+ @scanner.terminate
128
+ assert_scanner_state 8, 10, nil, nil
129
+ end
130
+
131
+ def test_unscan
132
+ @scanner.scan(/../)
133
+ @scanner.scan_until(/f./)
134
+ @scanner.unscan
135
+ assert_scanner_state 2, 3, nil, nil
136
+ end
137
+
138
+ private
139
+
140
+ def assert_scanner_state(pos, byte_pos, matched_size, byte_matched_size)
141
+ assert_equal pos, @scanner.pos, 'pos'
142
+ assert_equal byte_pos, @scanner.byte_pos, 'byte_pos'
143
+ assert_equal matched_size, @scanner.matched_size, 'matched_size'
144
+ assert_equal byte_matched_size, @scanner.byte_matched_size, 'byte_matched_size'
145
+ end
146
+ end
147
+ end
@@ -159,6 +159,12 @@ class UtilTest < Test::Unit::TestCase
159
159
  enum_cons(%w[foo bar baz], 2).map {|s1, s2| "#{s1}#{s2}"})
160
160
  end
161
161
 
162
+ def test_extract
163
+ arr = [1, 2, 3, 4, 5]
164
+ assert_equal([1, 3, 5], extract!(arr) {|e| e % 2 == 1})
165
+ assert_equal([2, 4], arr)
166
+ end
167
+
162
168
  def test_ord
163
169
  assert_equal(102, ord("f"))
164
170
  assert_equal(98, ord("bar"))
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 3
8
8
  - 1
9
- - 12
10
- version: 3.1.12
9
+ - 13
10
+ version: 3.1.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nathan Weizenbaum
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2011-12-16 00:00:00 -08:00
20
+ date: 2012-02-03 00:00:00 -08:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
@@ -116,6 +116,7 @@ files:
116
116
  - lib/sass/root.rb
117
117
  - lib/sass/importers.rb
118
118
  - lib/sass/logger.rb
119
+ - lib/sass/util/multibyte_string_scanner.rb
119
120
  - lib/sass/util/subset_map.rb
120
121
  - lib/sass/scss.rb
121
122
  - lib/sass/scss/static_parser.rb
@@ -257,6 +258,7 @@ files:
257
258
  - test/sass/conversion_test.rb
258
259
  - test/sass/script_test.rb
259
260
  - test/sass/util/subset_map_test.rb
261
+ - test/sass/util/multibyte_string_scanner_test.rb
260
262
  - test/sass/callbacks_test.rb
261
263
  - test/sass/importer_test.rb
262
264
  - test/sass/scss/css_test.rb
@@ -356,6 +358,7 @@ test_files:
356
358
  - test/sass/conversion_test.rb
357
359
  - test/sass/script_test.rb
358
360
  - test/sass/util/subset_map_test.rb
361
+ - test/sass/util/multibyte_string_scanner_test.rb
359
362
  - test/sass/callbacks_test.rb
360
363
  - test/sass/importer_test.rb
361
364
  - test/sass/scss/css_test.rb