sass 3.1.12 → 3.1.13

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