sass 3.3.0.alpha.69 → 3.3.0.alpha.88

Sign up to get free protection for your applications and to get access to all the features.
data/REVISION CHANGED
@@ -1 +1 @@
1
- 2ada675414fd60b696703b85500d3528d482c64a
1
+ c8154cc19b995b06961c60fb376d9c4821f8bde1
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.69
1
+ 3.3.0.alpha.88
@@ -1 +1 @@
1
- 05 January 2013 02:52:38 GMT
1
+ 18 February 2013 01:09:19 GMT
@@ -599,7 +599,7 @@ WARNING
599
599
  # which begin with ::,
600
600
  # as well as pseudo-classes
601
601
  # if we're using the new property syntax
602
- Tree::RuleNode.new(parse_interp(line.text))
602
+ Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
603
603
  else
604
604
  name_start_offset = line.offset + 1 # +1 for the leading ':'
605
605
  name, value = line.text.scan(PROPERTY_OLD)[0]
@@ -626,12 +626,12 @@ WARNING
626
626
  when DIRECTIVE_CHAR
627
627
  parse_directive(parent, line, root)
628
628
  when ESCAPE_CHAR
629
- Tree::RuleNode.new(parse_interp(line.text[1..-1]))
629
+ Tree::RuleNode.new(parse_interp(line.text[1..-1]), full_line_range(line))
630
630
  when MIXIN_DEFINITION_CHAR
631
631
  parse_mixin_definition(line)
632
632
  when MIXIN_INCLUDE_CHAR
633
633
  if line.text[1].nil? || line.text[1] == ?\s
634
- Tree::RuleNode.new(parse_interp(line.text))
634
+ Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
635
635
  else
636
636
  parse_mixin_include(line, root)
637
637
  end
@@ -650,11 +650,12 @@ WARNING
650
650
  @line, to_parser_offset(offset))
651
651
 
652
652
  unless res = parser.parse_interp_ident
653
- return Tree::RuleNode.new(parse_interp(line.text, line.offset))
653
+ parsed = parse_interp(line.text, line.offset)
654
+ return Tree::RuleNode.new(parsed, full_line_range(line))
654
655
  end
655
656
 
656
657
  ident_range = Sass::Source::Range.new(
657
- Sass::Source::Position.new(@line, to_parser_offset(offset)),
658
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
658
659
  Sass::Source::Position.new(@line, parser.offset),
659
660
  @options[:filename], @options[:importer])
660
661
  offset = parser.offset - 1
@@ -672,7 +673,14 @@ WARNING
672
673
  property
673
674
  else
674
675
  res.pop if comment
675
- Tree::RuleNode.new(res + parse_interp(scanner.rest))
676
+ scanner_start_pos = scanner.pos
677
+ interp_parsed = parse_interp(scanner.rest)
678
+ scanned_size = scanner.pos - scanner_start_pos
679
+ selector_range = Sass::Source::Range.new(
680
+ ident_range.start_pos,
681
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
682
+ @options[:filename], @options[:importer])
683
+ Tree::RuleNode.new(res + interp_parsed, selector_range)
676
684
  end
677
685
  end
678
686
 
@@ -733,7 +741,7 @@ WARNING
733
741
  type = if silent then :silent elsif loud then :loud else :normal end
734
742
  Tree::CommentNode.new(value, type)
735
743
  else
736
- Tree::RuleNode.new(parse_interp(line.text))
744
+ Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
737
745
  end
738
746
  end
739
747
 
@@ -778,7 +786,13 @@ WARNING
778
786
  :line => @line + 1) unless line.children.empty?
779
787
  optional = !!value.gsub!(/\s+#{Sass::SCSS::RX::OPTIONAL}$/, '')
780
788
  offset = line.offset + line.text.index(value).to_i
781
- Tree::ExtendNode.new(parse_interp(value, offset), optional)
789
+ interp_parsed = parse_interp(value, offset)
790
+ selector_range = Sass::Source::Range.new(
791
+ Sass::Source::Position.new(@line, to_parser_offset(offset)),
792
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
793
+ @options[:filename], @options[:importer]
794
+ )
795
+ Tree::ExtendNode.new(interp_parsed, optional, selector_range)
782
796
  when 'warn'
783
797
  raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
784
798
  raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
@@ -1030,6 +1044,13 @@ WARNING
1030
1044
  offset + 1
1031
1045
  end
1032
1046
 
1047
+ def full_line_range(line)
1048
+ Sass::Source::Range.new(
1049
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
1050
+ Sass::Source::Position.new(@line, to_parser_offset(line.offset) + line.text.length),
1051
+ @options[:filename], @options[:importer])
1052
+ end
1053
+
1033
1054
  # It's important that this have strings (at least)
1034
1055
  # at the beginning, the end, and between each Script::Node.
1035
1056
  #
@@ -4,7 +4,7 @@ module Sass::Script
4
4
  #
5
5
  # $color = hsl(120deg, 100%, 50%)
6
6
  #
7
- # and it will call {Sass::Script::Functions#hsl}.
7
+ # and it will call {Functions#hsl}.
8
8
  #
9
9
  # The following functions are provided:
10
10
  #
@@ -110,6 +110,24 @@ module Sass::Script
110
110
  # \{#quote quote($string)}
111
111
  # : Adds quotes to a string.
112
112
  #
113
+ # \{#str_length str-length($string)}
114
+ # : Returns the number of characters in a string.
115
+ #
116
+ # \{#str_insert str-insert($string, $insert, $index)}
117
+ # : Inserts the second string into the first string at the specified index.
118
+ #
119
+ # \{#str_index str-index($string, $substring)}
120
+ # : Returns the index where a substring is found in another string or 0 if not found.
121
+ #
122
+ # \{#str_extract str-slice($string, $start, $end)}
123
+ # : Extracts a substring of characters from $string
124
+ #
125
+ # \{#to_upper_case to-upper-case($string)}
126
+ # : Converts a string to upper case.
127
+ #
128
+ # \{#to_lower_case to-lower-case($string)}
129
+ # : Converts a string to lower case.
130
+ #
113
131
  # ## Number Functions
114
132
  #
115
133
  # \{#percentage percentage($value)}
@@ -184,14 +202,14 @@ module Sass::Script
184
202
  # {declare} can also allow your function to take arbitrary keyword arguments.
185
203
  #
186
204
  # There are a few things to keep in mind when modifying this module.
187
- # First of all, the arguments passed are {Sass::Script::Literal} objects.
205
+ # First of all, the arguments passed are {Literal} objects.
188
206
  # Literal objects are also expected to be returned.
189
207
  # This means that Ruby values must be unwrapped and wrapped.
190
208
  #
191
- # Most Literal objects support the {Sass::Script::Literal#value value} accessor
209
+ # Most Literal objects support the {Literal#value value} accessor
192
210
  # for getting their Ruby values.
193
- # Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
194
- # {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
211
+ # Color objects, though, must be accessed using {Color#rgb rgb},
212
+ # {Color#red red}, {Color#blue green}, or {Color#blue blue}.
195
213
  #
196
214
  # Second, making Ruby functions accessible from Sass introduces the temptation
197
215
  # to do things like database access within stylesheets.
@@ -250,7 +268,7 @@ module Sass::Script
250
268
  # Whether the function accepts other keyword arguments
251
269
  # in addition to those in `:args`.
252
270
  # If this is true, the Ruby function will be passed a hash from strings
253
- # to {Sass::Script::Literal}s as the last argument.
271
+ # to {Literal}s as the last argument.
254
272
  # In addition, if this is true and `:var_args` is not,
255
273
  # Sass will ensure that the last argument passed is a hash.
256
274
  #
@@ -329,15 +347,63 @@ module Sass::Script
329
347
  # @example
330
348
  # assert_type value, :String
331
349
  # assert_type value, :Number
332
- # @param value [Sass::Script::Literal] A SassScript value
350
+ # @param value [Literal] A SassScript value
333
351
  # @param type [Symbol] The name of the type the value is expected to be
334
352
  # @param name [String, nil] The name of the argument.
353
+ # @raise [ArgumentError] if value is not of the correct type.
335
354
  def assert_type(value, type, name = nil)
336
355
  return if value.is_a?(Sass::Script.const_get(type))
337
356
  err = "#{value.inspect} is not a #{type.to_s.downcase}"
338
357
  err = "$#{name}: " + err if name
339
358
  raise ArgumentError.new(err)
340
359
  end
360
+
361
+ # Asserts that the unit of the number is as expected.
362
+ #
363
+ # @example
364
+ # assert_unit number, "px"
365
+ # assert_unit number, nil
366
+ # @param number [Number] The number to be validated.
367
+ # @param unit [::String]
368
+ # The unit that the number must have.
369
+ # If nil, the number must be unitless.
370
+ # @param name [::String] The name of the parameter being validated.
371
+ # @raise [ArgumentError] if number is not of the correct unit or is not a number.
372
+ def assert_unit(number, unit, name = nil)
373
+ assert_type number, :Number, name
374
+ return if number.is_unit?(unit)
375
+ if unit
376
+ if name
377
+ raise ArgumentError.new("Expected $#{name} to have a unit of #{unit} but got #{number}")
378
+ else
379
+ raise ArgumentError.new("Expected #{number} to have a unit of #{unit}")
380
+ end
381
+ else
382
+ if name
383
+ raise ArgumentError.new("Expected $#{name} to be unitless but got #{number}")
384
+ else
385
+ raise ArgumentError.new("Expected #{number} to be unitless")
386
+ end
387
+ end
388
+ end
389
+
390
+ # Asserts that the value is an integer.
391
+ #
392
+ # @example
393
+ # assert_unit number, "px"
394
+ # assert_unit number, nil
395
+ # @param number [Literal] The literal to be validated.
396
+ # @param name [::String] The name of the parameter being validated.
397
+ # @raise [ArgumentError] if number is not an integer or is not a number.
398
+ def assert_integer(number, name = nil)
399
+ assert_type number, :Number, name
400
+ return if number.int?
401
+ if name
402
+ raise ArgumentError.new("Expected $#{name} to be an integer but got #{number}")
403
+ else
404
+ raise ArgumentError.new("Expected #{number} to be an integer")
405
+ end
406
+ end
341
407
  end
342
408
 
343
409
  class << self
@@ -377,11 +443,13 @@ module Sass::Script
377
443
 
378
444
  Color.new([red, green, blue].map do |c|
379
445
  v = c.value
380
- if c.numerator_units == ["%"] && c.denominator_units.empty?
446
+ if c.is_unit?("%")
381
447
  v = Sass::Util.check_range("Color value", 0..100, c, '%')
382
448
  v * 255 / 100.0
383
- else
449
+ elsif c.unitless?
384
450
  Sass::Util.check_range("Color value", 0..255, c)
451
+ else
452
+ raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
385
453
  end
386
454
  end)
387
455
  end
@@ -874,11 +942,8 @@ module Sass::Script
874
942
 
875
943
  next unless val = kwargs.delete(name)
876
944
  assert_type val, :Number, name
877
- if !(val.numerator_units == ['%'] && val.denominator_units.empty?)
878
- raise ArgumentError.new("$#{name}: Amount #{val} must be a % (e.g. #{val.value}%)")
879
- else
880
- Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
881
- end
945
+ assert_unit val, '%', name
946
+ Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
882
947
 
883
948
  current = color.send(name)
884
949
  scale = val.value/100.0
@@ -1083,6 +1148,138 @@ module Sass::Script
1083
1148
  end
1084
1149
  declare :quote, [:string]
1085
1150
 
1151
+ # Returns the number of characters in a string.
1152
+ #
1153
+ # @return [Number]
1154
+ # @raise [ArgumentError] if `string` isn't a string
1155
+ # @example
1156
+ # str-length("foo") => 3
1157
+ def str_length(string)
1158
+ assert_type string, :String
1159
+ Sass::Script::Number.new(string.value.size)
1160
+ end
1161
+ declare :str_length, [:string]
1162
+
1163
+ # Inserts a string into another string.
1164
+ #
1165
+ # Inserts the `$insert` string into the `$original` before the character at
1166
+ # the given `$index`.
1167
+ #
1168
+ # @param [String] original The string that will receive the insertion.
1169
+ # @param [String] insert The string that will be inserted.
1170
+ # @param [Number] index
1171
+ # The position where inserted string will start.
1172
+ # Negative indices count from the end of the original string.
1173
+ #
1174
+ # @return [String] A new string
1175
+ # @raise [ArgumentError] if `$original` isn't a string, `$insert` isn't a string, or `$index` isn't a number.
1176
+ # @example
1177
+ # str-insert("abcd", "X", 1) => "Xabcd"
1178
+ # str-insert("abcd", "X", 4) => "abcXd"
1179
+ # str-insert("abcd", "X", 100) => "abcdX"
1180
+ # str-insert("abcd", "X", -100) => "Xabcd"
1181
+ def str_insert(original, insert, index)
1182
+ assert_type original, :String, "original"
1183
+ assert_type insert, :String, "insert"
1184
+ assert_integer index, "index"
1185
+ assert_unit index, nil, "index"
1186
+ insertion_point = index.value > 0 ? [index.value - 1, original.value.size].min : [index.value, -original.value.size - 1].max
1187
+ Sass::Script::String.new(original.value.dup.insert(insertion_point, insert.value), original.type)
1188
+ end
1189
+ declare :str_insert, [:original, :insert, :index]
1190
+
1191
+ # Starting at the left, finds the index of the first location
1192
+ # where `substring` is found in `string`.
1193
+ #
1194
+ # @return [Number] The index of the substring, or 0 if not found.
1195
+ # @raise [ArgumentError] if `original` isn't a string, `insert` isn't a string, or `index` isn't a number.
1196
+ # @param string The string to search
1197
+ # @param substring The string to search for
1198
+ # @example
1199
+ # str-index(abcd, a) => 1
1200
+ # str-index(abcd, ab) => 1
1201
+ # str-index(abcd, X) => 0
1202
+ # str-index(abcd, c) => 3
1203
+ def str_index(string, substring)
1204
+ assert_type string, :String
1205
+ assert_type substring, :String
1206
+ index = string.value.index(substring.value) || -1
1207
+ Sass::Script::Number.new(index + 1)
1208
+ end
1209
+ declare :str_index, [:string, :substring]
1210
+
1211
+
1212
+ # Slice a substring from `string` from `start-at` index to `end-at` index.
1213
+ #
1214
+ # @return [String] A new string
1215
+ # @param start_at
1216
+ # The index (inclusive) of the first character to slice out of the string.
1217
+ # If negative, counts from the end of the string.
1218
+ # @param end_at
1219
+ # The index (inclusive) of the last character to slice out of the string.
1220
+ # @overload str_slice(string, start_at)
1221
+ # Slice starting at `start_at` to the end of the string.
1222
+ # @overload str_slice(string, start_at, end_at)
1223
+ # Slice starting at `start_at` to `end_at`
1224
+ # @raise [ArgumentError] if `string` isn't a string or `start_at` and `end_at` aren't unitless numbers
1225
+ # @example
1226
+ # str-slice(abcd, 2, 3) => bc
1227
+ # str-slice(abcd, 2 ) => bcd
1228
+ # str-slice(abcd, -2 ) => cd
1229
+ # str-slice(abcd, 2, -2) => bc
1230
+ # str-slice("abcd", 3, -3) => ""
1231
+ # str-slice(abcd, 1, 1) => a
1232
+ # str-slice(abcd, 1, 2) => ab
1233
+ # str-slice(abcd, 1, 4) => abcd
1234
+ # str-slice(abcd, -100, 4) => abcd
1235
+ # str-slice(abcd, 1, 100) => abcd
1236
+ # str-slice("abcd", 2, 1) => ""
1237
+ # str-slice("abcd", 2, 3) => "bc"
1238
+ def str_slice(string, start_at, end_at = nil)
1239
+ assert_type string, :String
1240
+ assert_unit start_at, nil, "start-at"
1241
+
1242
+ end_at = Sass::Script::Number.new(-1)if end_at.nil?
1243
+ assert_unit end_at, nil, "end-at"
1244
+
1245
+ s = start_at.value > 0 ? start_at.value - 1 : start_at.value
1246
+ e = end_at.value > 0 ? end_at.value - 1 : end_at.value
1247
+ s = string.value.length + s if s < 0
1248
+ s = 0 if s < 0
1249
+ e = string.value.length + e if e < 0
1250
+ e = 0 if s < 0
1251
+ extracted = string.value.slice(s..e)
1252
+ Sass::Script::String.new(extracted || "", string.type)
1253
+ end
1254
+ declare :str_slice, [:string, :start_at]
1255
+ declare :str_slice, [:string, :start_at, :end_at]
1256
+
1257
+ # Convert a string to upper case
1258
+ #
1259
+ # @return [String]
1260
+ # @raise [ArgumentError] if `string` isn't a string
1261
+ # @example
1262
+ # to-upper-case(abcd) => ABCD
1263
+ # to-upper-case("abcd") => "ABCD"
1264
+ def to_upper_case(string)
1265
+ assert_type string, :String
1266
+ Sass::Script::String.new(string.value.upcase, string.type)
1267
+ end
1268
+ declare :to_upper_case, [:string]
1269
+
1270
+ # Convert a string to lower case
1271
+ #
1272
+ # @return [String]
1273
+ # @raise [ArgumentError] if `string` isn't a string
1274
+ # @example
1275
+ # to-lower-case(ABCD) => abcd
1276
+ # to-lower-case("ABCD") => "abcd"
1277
+ def to_lower_case(string)
1278
+ assert_type string, :String
1279
+ Sass::Script::String.new(string.value.downcase, string.type)
1280
+ end
1281
+ declare :to_lower_case, [:string]
1282
+
1086
1283
  # Inspects the type of the argument, returning it as an unquoted string.
1087
1284
  #
1088
1285
  # @example
@@ -57,9 +57,11 @@ module Sass::Script
57
57
  NO_UNITS = []
58
58
 
59
59
  # @param value [Numeric] The value of the number
60
- # @param numerator_units [Array<String>] See \{#numerator\_units}
61
- # @param denominator_units [Array<String>] See \{#denominator\_units}
60
+ # @param numerator_units [::String, Array<::String>] See \{#numerator\_units}
61
+ # @param denominator_units [::String, Array<::String>] See \{#denominator\_units}
62
62
  def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
63
+ numerator_units = [numerator_units] if numerator_units.is_a?(::String)
64
+ denominator_units = [denominator_units] if denominator_units.is_a?(::String)
63
65
  super(value)
64
66
  @numerator_units = numerator_units
65
67
  @denominator_units = denominator_units
@@ -285,6 +287,24 @@ module Sass::Script
285
287
  @numerator_units.empty? && @denominator_units.empty?
286
288
  end
287
289
 
290
+ # Checks whether the number has the numerator unit specified.
291
+ #
292
+ # @example
293
+ # number = Sass::Script::Number.new(10, "px")
294
+ # number.is_unit?("px") => true
295
+ # number.is_unit?(nil) => false
296
+ #
297
+ # @param number [Number] The number to check
298
+ # @param unit [::String, nil] The unit the number should have or nil if the number should be unitless.
299
+ # @see Number#unitless? The unitless? method may be more readable.
300
+ def is_unit?(unit)
301
+ if unit
302
+ denominator_units.size == 0 && numerator_units.size == 1 && numerator_units.first == unit
303
+ else
304
+ unitless?
305
+ end
306
+ end
307
+
288
308
  # @return [Boolean] Whether or not this number has units that can be represented in CSS
289
309
  # (that is, zero or one \{#numerator\_units}).
290
310
  def legal_units?
@@ -311,10 +311,10 @@ module Sass
311
311
  end
312
312
 
313
313
  def extend_directive(start_pos)
314
- selector = expr!(:selector_sequence)
314
+ selector, selector_range = expr!(:selector_sequence)
315
315
  optional = tok(OPTIONAL)
316
316
  ss
317
- node(Sass::Tree::ExtendNode.new(selector, !!optional), start_pos)
317
+ node(Sass::Tree::ExtendNode.new(selector, !!optional, selector_range), start_pos)
318
318
  end
319
319
 
320
320
  def import_directive(start_pos)
@@ -536,8 +536,9 @@ module Sass
536
536
 
537
537
  def ruleset
538
538
  start_pos = source_position
539
- return unless rules = selector_sequence
540
- block(node(Sass::Tree::RuleNode.new(rules.flatten.compact), start_pos), :ruleset)
539
+ rules, source_range = selector_sequence
540
+ return unless rules
541
+ block(node(Sass::Tree::RuleNode.new(rules.flatten.compact, source_range), start_pos), :ruleset)
541
542
  end
542
543
 
543
544
  def block(node, context)
@@ -607,8 +608,9 @@ module Sass
607
608
  end
608
609
 
609
610
  def selector_sequence
611
+ start_pos = source_position
610
612
  if sel = tok(STATIC_SELECTOR, true)
611
- return [sel]
613
+ return [sel], range(start_pos)
612
614
  end
613
615
 
614
616
  rules = []
@@ -624,7 +626,7 @@ module Sass
624
626
  ws = ''
625
627
  end
626
628
  end
627
- rules
629
+ return rules, range(start_pos)
628
630
  end
629
631
 
630
632
  def selector
@@ -680,6 +682,8 @@ module Sass
680
682
  def simple_selector_sequence
681
683
  # Returning expr by default allows for stuff like
682
684
  # http://www.w3.org/TR/css3-animations/#keyframes-
685
+
686
+ start_pos = source_position
683
687
  return expr(!:allow_var) unless e = element_name || id_selector ||
684
688
  class_selector || placeholder_selector || attrib || pseudo ||
685
689
  parent_selector || interpolation_selector
@@ -708,7 +712,7 @@ module Sass
708
712
  end
709
713
  end
710
714
 
711
- Selector::SimpleSequence.new(res, tok(/!/))
715
+ Selector::SimpleSequence.new(res, tok(/!/), range(start_pos))
712
716
  end
713
717
 
714
718
  def parent_selector
@@ -1038,7 +1042,7 @@ MESSAGE
1038
1042
  def self.sass_script_parser; @sass_script_parser; end
1039
1043
 
1040
1044
  def sass_script(*args)
1041
- parser = self.class.sass_script_parser.new(@scanner, @line, @offset, :filename => @filename)
1045
+ parser = self.class.sass_script_parser.new(@scanner, @line, @offset, :filename => @filename, :importer => @importer)
1042
1046
  result = parser.send(*args)
1043
1047
  unless @strs.empty?
1044
1048
  # Convert to CSS manually so that comments are ignored.