sass 3.3.14 → 3.4.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +5 -5
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +0 -5
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +28 -39
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +419 -0
- data/lib/sass/features.rb +6 -0
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/importers/base.rb +5 -1
- data/lib/sass/importers/filesystem.rb +4 -21
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +32 -136
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +363 -39
- data/lib/sass/script/lexer.rb +68 -50
- data/lib/sass/script/parser.rb +29 -14
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +19 -1
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +32 -12
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +60 -14
- data/lib/sass/script/value/string.rb +53 -9
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +175 -319
- data/lib/sass/scss/rx.rb +14 -5
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector.rb +56 -193
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +91 -12
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +99 -31
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +101 -37
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +23 -9
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +1 -1
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +7 -1
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +11 -6
- data/lib/sass/tree/visitors/check_nesting.rb +3 -4
- data/lib/sass/tree/visitors/convert.rb +8 -17
- data/lib/sass/tree/visitors/cssize.rb +12 -24
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +43 -28
- data/lib/sass/tree/visitors/set_options.rb +5 -0
- data/lib/sass/tree/visitors/to_css.rb +14 -13
- data/lib/sass/util.rb +94 -90
- data/test/sass/cache_test.rb +1 -1
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +5 -14
- data/test/sass/conversion_test.rb +47 -1
- data/test/sass/css2sass_test.rb +3 -3
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +128 -191
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +234 -17
- data/test/sass/functions_test.rb +268 -213
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +12 -11
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -4
- data/test/sass/script_test.rb +202 -79
- data/test/sass/scss/css_test.rb +95 -25
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +363 -19
- data/test/sass/source_map_test.rb +48 -41
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -1
- data/test/sass/value_helpers_test.rb +3 -3
- data/test/test_helper.rb +2 -2
- metadata +30 -7
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
@@ -64,17 +64,16 @@ module Sass
|
|
64
64
|
filename.start_with?(root + File::SEPARATOR)
|
65
65
|
end
|
66
66
|
|
67
|
-
def public_url(name, sourcemap_directory
|
67
|
+
def public_url(name, sourcemap_directory)
|
68
|
+
file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
|
68
69
|
if sourcemap_directory.nil?
|
69
|
-
|
70
|
+
Sass::Util.file_uri_from_path(file_pathname)
|
70
71
|
else
|
71
|
-
file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
|
72
72
|
sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
|
73
73
|
begin
|
74
74
|
Sass::Util.file_uri_from_path(file_pathname.relative_path_from(sourcemap_pathname))
|
75
75
|
rescue ArgumentError # when a relative path cannot be constructed
|
76
|
-
|
77
|
-
nil
|
76
|
+
Sass::Util.file_uri_from_path(file_pathname)
|
78
77
|
end
|
79
78
|
end
|
80
79
|
end
|
@@ -196,22 +195,6 @@ WARNING
|
|
196
195
|
[dirname, basename, extension]
|
197
196
|
end
|
198
197
|
|
199
|
-
# Issues a warning about being unable to determine a public url.
|
200
|
-
#
|
201
|
-
# @param uri [String] A URI known to be valid for this importer.
|
202
|
-
# @return [NilClass] nil
|
203
|
-
def warn_about_public_url(uri)
|
204
|
-
@warnings_issued ||= Set.new
|
205
|
-
unless @warnings_issued.include?(uri)
|
206
|
-
Sass::Util.sass_warn <<WARNING
|
207
|
-
WARNING: Couldn't determine public URL for "#{uri}" while generating sourcemap.
|
208
|
-
Without a public URL, there's nothing for the source map to link to.
|
209
|
-
WARNING
|
210
|
-
@warnings_issued << uri
|
211
|
-
end
|
212
|
-
nil
|
213
|
-
end
|
214
|
-
|
215
198
|
private
|
216
199
|
|
217
200
|
def _find(dir, name, options)
|
data/lib/sass/media.rb
CHANGED
@@ -205,9 +205,6 @@ module Sass::Media
|
|
205
205
|
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
206
206
|
# @return [String]
|
207
207
|
def self._interp_to_src(interp, options)
|
208
|
-
interp.map
|
209
|
-
next r if r.is_a?(String)
|
210
|
-
"\#{#{r.to_sass(options)}}"
|
211
|
-
end.join
|
208
|
+
interp.map {|r| r.is_a?(String) ? r : r.to_sass(options)}.join
|
212
209
|
end
|
213
210
|
end
|
data/lib/sass/plugin/compiler.rb
CHANGED
@@ -36,30 +36,19 @@ module Sass::Plugin
|
|
36
36
|
options.merge!(opts)
|
37
37
|
end
|
38
38
|
|
39
|
-
# Register a callback to be run
|
39
|
+
# Register a callback to be run after stylesheets are mass-updated.
|
40
40
|
# This is run whenever \{#update\_stylesheets} is called,
|
41
41
|
# unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
|
42
42
|
# is enabled.
|
43
43
|
#
|
44
|
-
# @yield [
|
45
|
-
# @yieldparam
|
46
|
-
# Individual files to be updated
|
44
|
+
# @yield [individual_files]
|
45
|
+
# @yieldparam individual_files [<(String, String)>]
|
46
|
+
# Individual files to be updated, in addition to the directories
|
47
|
+
# specified in the options.
|
47
48
|
# The first element of each pair is the source file,
|
48
|
-
# the second is the target CSS file
|
49
|
-
# the third is the target sourcemap file.
|
49
|
+
# the second is the target CSS file.
|
50
50
|
define_callback :updating_stylesheets
|
51
51
|
|
52
|
-
# Register a callback to be run after stylesheets are mass-updated.
|
53
|
-
# This is run whenever \{#update\_stylesheets} is called,
|
54
|
-
# unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
|
55
|
-
# is enabled.
|
56
|
-
#
|
57
|
-
# @yield [updated_files]
|
58
|
-
# @yieldparam updated_files [<(String, String)>]
|
59
|
-
# Individual files that were updated.
|
60
|
-
# The first element of each pair is the source file, the second is the target CSS file.
|
61
|
-
define_callback :updated_stylesheets
|
62
|
-
|
63
52
|
# Register a callback to be run after a single stylesheet is updated.
|
64
53
|
# The callback is only run if the stylesheet is really updated;
|
65
54
|
# if the CSS file is fresh, this won't be run.
|
@@ -78,21 +67,6 @@ module Sass::Plugin
|
|
78
67
|
# The location of the sourcemap being generated, if any.
|
79
68
|
define_callback :updated_stylesheet
|
80
69
|
|
81
|
-
# Register a callback to be run when compilation starts.
|
82
|
-
#
|
83
|
-
# In combination with on_updated_stylesheet, this could be used
|
84
|
-
# to collect compilation statistics like timing or to take a
|
85
|
-
# diff of the changes to the output file.
|
86
|
-
#
|
87
|
-
# @yield [template, css, sourcemap]
|
88
|
-
# @yieldparam template [String]
|
89
|
-
# The location of the Sass/SCSS file being updated.
|
90
|
-
# @yieldparam css [String]
|
91
|
-
# The location of the CSS file being generated.
|
92
|
-
# @yieldparam sourcemap [String]
|
93
|
-
# The location of the sourcemap being generated, if any.
|
94
|
-
define_callback :compilation_starting
|
95
|
-
|
96
70
|
# Register a callback to be run when Sass decides not to update a stylesheet.
|
97
71
|
# In particular, the callback is run when Sass finds that
|
98
72
|
# the template file and none of its dependencies
|
@@ -165,8 +139,7 @@ module Sass::Plugin
|
|
165
139
|
define_callback :template_deleted
|
166
140
|
|
167
141
|
# Register a callback to be run when Sass deletes a CSS file.
|
168
|
-
# This happens when the corresponding Sass/SCSS file has been deleted
|
169
|
-
# and when the compiler cleans the output files.
|
142
|
+
# This happens when the corresponding Sass/SCSS file has been deleted.
|
170
143
|
#
|
171
144
|
# @yield [filename]
|
172
145
|
# @yieldparam filename [String]
|
@@ -174,8 +147,7 @@ module Sass::Plugin
|
|
174
147
|
define_callback :deleting_css
|
175
148
|
|
176
149
|
# Register a callback to be run when Sass deletes a sourcemap file.
|
177
|
-
# This happens when the corresponding Sass/SCSS file has been deleted
|
178
|
-
# and when the compiler cleans the output files.
|
150
|
+
# This happens when the corresponding Sass/SCSS file has been deleted.
|
179
151
|
#
|
180
152
|
# @yield [filename]
|
181
153
|
# @yieldparam filename [String]
|
@@ -190,72 +162,35 @@ module Sass::Plugin
|
|
190
162
|
# in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
|
191
163
|
# If it has, it updates the CSS file.
|
192
164
|
#
|
193
|
-
# @param individual_files [Array<(String, String
|
165
|
+
# @param individual_files [Array<(String, String)>]
|
194
166
|
# A list of files to check for updates
|
195
167
|
# **in addition to those specified by the
|
196
168
|
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
197
169
|
# The first string in each pair is the location of the Sass/SCSS file,
|
198
170
|
# the second is the location of the CSS file that it should be compiled to.
|
199
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
200
171
|
def update_stylesheets(individual_files = [])
|
172
|
+
individual_files = individual_files.dup
|
201
173
|
Sass::Plugin.checked_for_updates = true
|
202
174
|
staleness_checker = StalenessChecker.new(engine_options)
|
203
175
|
|
204
|
-
|
205
|
-
|
176
|
+
template_location_array.each do |template_location, css_location|
|
177
|
+
Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
|
178
|
+
# Get the relative path to the file
|
179
|
+
name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
|
180
|
+
css = css_filename(name, css_location)
|
181
|
+
sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap] != :none
|
182
|
+
individual_files << [file, css, sourcemap]
|
183
|
+
end
|
184
|
+
end
|
206
185
|
|
207
|
-
|
208
|
-
files.each do |file, css, sourcemap|
|
186
|
+
individual_files.each do |file, css, sourcemap|
|
209
187
|
# TODO: Does staleness_checker need to check the sourcemap file as well?
|
210
188
|
if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
|
211
|
-
# XXX For consistency, this should return the sourcemap too, but it would
|
212
|
-
# XXX be an API change.
|
213
|
-
updated_stylesheets << [file, css]
|
214
189
|
update_stylesheet(file, css, sourcemap)
|
215
190
|
else
|
216
191
|
run_not_updating_stylesheet(file, css, sourcemap)
|
217
192
|
end
|
218
193
|
end
|
219
|
-
run_updated_stylesheets(updated_stylesheets)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Construct a list of files that might need to be compiled
|
223
|
-
# from the provided individual_files and the template_locations.
|
224
|
-
#
|
225
|
-
# Note: this method does not cache the results as they can change
|
226
|
-
# across invocations when sass files are added or removed.
|
227
|
-
#
|
228
|
-
# @param individual_files [Array<(String, String[, String])>]
|
229
|
-
# A list of files to check for updates
|
230
|
-
# **in addition to those specified by the
|
231
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
232
|
-
# The first string in each pair is the location of the Sass/SCSS file,
|
233
|
-
# the second is the location of the CSS file that it should be compiled to.
|
234
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
235
|
-
# @return [Array<(String, String, String)>]
|
236
|
-
# A list of [sass_file, css_file, sourcemap_file] tuples similar
|
237
|
-
# to what was passed in, but expanded to include the current state
|
238
|
-
# of the directories being updated.
|
239
|
-
def file_list(individual_files = [])
|
240
|
-
files = individual_files.map do |tuple|
|
241
|
-
if tuple.size < 3
|
242
|
-
[tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
|
243
|
-
else
|
244
|
-
tuple
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
template_location_array.each do |template_location, css_location|
|
249
|
-
Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
|
250
|
-
# Get the relative path to the file
|
251
|
-
name = Sass::Util.pathname(file).relative_path_from(
|
252
|
-
Sass::Util.pathname(template_location.to_s)).to_s
|
253
|
-
css = css_filename(name, css_location)
|
254
|
-
sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap]
|
255
|
-
files << [file, css, sourcemap]
|
256
|
-
end
|
257
|
-
end
|
258
|
-
files
|
259
194
|
end
|
260
195
|
|
261
196
|
# Watches the template directory (or directories)
|
@@ -276,19 +211,14 @@ module Sass::Plugin
|
|
276
211
|
# The version of Listen distributed with Sass is loaded by default,
|
277
212
|
# but if another version has already been loaded that will be used instead.
|
278
213
|
#
|
279
|
-
# @param individual_files [Array<(String, String
|
280
|
-
# A list of files to
|
214
|
+
# @param individual_files [Array<(String, String)>]
|
215
|
+
# A list of files to watch for updates
|
281
216
|
# **in addition to those specified by the
|
282
217
|
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
283
218
|
# The first string in each pair is the location of the Sass/SCSS file,
|
284
219
|
# the second is the location of the CSS file that it should be compiled to.
|
285
|
-
|
286
|
-
|
287
|
-
# @option options [Boolean] :skip_initial_update
|
288
|
-
# Don't do an initial update when starting the watcher when true
|
289
|
-
def watch(individual_files = [], options = {})
|
290
|
-
options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
|
291
|
-
update_stylesheets(individual_files) unless options[:skip_initial_update]
|
220
|
+
def watch(individual_files = [])
|
221
|
+
update_stylesheets(individual_files)
|
292
222
|
|
293
223
|
directories = watched_paths
|
294
224
|
individual_files.each do |(source, _, _)|
|
@@ -305,11 +235,7 @@ module Sass::Plugin
|
|
305
235
|
|
306
236
|
# TODO: Keep better track of what depends on what
|
307
237
|
# so we don't have to run a global update every time anything changes.
|
308
|
-
|
309
|
-
# a deprecated feature is removed. It may be removed without warning.
|
310
|
-
listener_args = directories +
|
311
|
-
Array(options[:additional_watch_paths]) +
|
312
|
-
[{:relative_paths => false}]
|
238
|
+
listener_args = directories + [{:relative_paths => false}]
|
313
239
|
|
314
240
|
# The native windows listener is much slower than the polling option, according to
|
315
241
|
# https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
|
@@ -322,7 +248,6 @@ module Sass::Plugin
|
|
322
248
|
|
323
249
|
listener = create_listener(*listener_args) do |modified, added, removed|
|
324
250
|
on_file_changed(individual_files, modified, added, removed)
|
325
|
-
yield(modified, added, removed) if block_given?
|
326
251
|
end
|
327
252
|
|
328
253
|
if poll && !Sass::Util.listen_geq_2?
|
@@ -350,42 +275,12 @@ module Sass::Plugin
|
|
350
275
|
StalenessChecker.stylesheet_needs_update?(css_file, template_file)
|
351
276
|
end
|
352
277
|
|
353
|
-
# Remove all output files that would be created by calling update_stylesheets, if they exist.
|
354
|
-
#
|
355
|
-
# This method runs the deleting_css and deleting_sourcemap callbacks for
|
356
|
-
# the files that are deleted.
|
357
|
-
#
|
358
|
-
# @param individual_files [Array<(String, String[, String])>]
|
359
|
-
# A list of files to check for updates
|
360
|
-
# **in addition to those specified by the
|
361
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
362
|
-
# The first string in each pair is the location of the Sass/SCSS file,
|
363
|
-
# the second is the location of the CSS file that it should be compiled to.
|
364
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
365
|
-
def clean(individual_files = [])
|
366
|
-
file_list(individual_files).each do |(_, css_file, sourcemap_file)|
|
367
|
-
if File.exist?(css_file)
|
368
|
-
run_deleting_css css_file
|
369
|
-
File.delete(css_file)
|
370
|
-
end
|
371
|
-
if sourcemap_file && File.exist?(sourcemap_file)
|
372
|
-
run_deleting_sourcemap sourcemap_file
|
373
|
-
File.delete(sourcemap_file)
|
374
|
-
end
|
375
|
-
end
|
376
|
-
nil
|
377
|
-
end
|
378
|
-
|
379
278
|
private
|
380
279
|
|
381
280
|
def create_listener(*args, &block)
|
382
281
|
Sass::Util.load_listen!
|
383
282
|
if Sass::Util.listen_geq_2?
|
384
|
-
|
385
|
-
options = args.pop if args.last.is_a?(Hash)
|
386
|
-
args.map do |dir|
|
387
|
-
Listen.to(dir, options, &block)
|
388
|
-
end
|
283
|
+
Listen.to(*args, &block)
|
389
284
|
else
|
390
285
|
Listen::Listener.new(*args, &block)
|
391
286
|
end
|
@@ -393,7 +288,7 @@ module Sass::Plugin
|
|
393
288
|
|
394
289
|
def listen_to(listener)
|
395
290
|
if Sass::Util.listen_geq_2?
|
396
|
-
listener.
|
291
|
+
listener.start.join
|
397
292
|
else
|
398
293
|
listener.start!
|
399
294
|
end
|
@@ -433,7 +328,6 @@ module Sass::Plugin
|
|
433
328
|
end
|
434
329
|
|
435
330
|
removed.uniq.each do |f|
|
436
|
-
run_template_deleted(relative_to_pwd(f))
|
437
331
|
if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
|
438
332
|
recompile_required = true
|
439
333
|
# This was a file we were watching explicitly and compiling to a particular location.
|
@@ -453,6 +347,7 @@ module Sass::Plugin
|
|
453
347
|
end
|
454
348
|
end
|
455
349
|
end
|
350
|
+
run_template_deleted(relative_to_pwd(f))
|
456
351
|
end
|
457
352
|
|
458
353
|
if recompile_required
|
@@ -476,7 +371,6 @@ module Sass::Plugin
|
|
476
371
|
:filename => filename,
|
477
372
|
:sourcemap_filename => sourcemap)
|
478
373
|
mapping = nil
|
479
|
-
run_compilation_starting(filename, css, sourcemap)
|
480
374
|
engine = Sass::Engine.for_file(filename, engine_opts)
|
481
375
|
if sourcemap
|
482
376
|
rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
|
@@ -486,12 +380,14 @@ module Sass::Plugin
|
|
486
380
|
rescue StandardError => e
|
487
381
|
compilation_error_occured = true
|
488
382
|
run_compilation_error e, filename, css, sourcemap
|
489
|
-
|
383
|
+
raise e unless options[:full_exception]
|
384
|
+
rendered = Sass::SyntaxError.exception_to_css(e, options[:line] || 1)
|
490
385
|
end
|
491
386
|
|
492
387
|
write_file(css, rendered)
|
493
388
|
if mapping
|
494
|
-
write_file(sourcemap, mapping.to_json(
|
389
|
+
write_file(sourcemap, mapping.to_json(
|
390
|
+
:css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
|
495
391
|
end
|
496
392
|
run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
|
497
393
|
end
|
@@ -18,7 +18,7 @@ module Sass
|
|
18
18
|
end
|
19
19
|
|
20
20
|
return unless scan(STRING)
|
21
|
-
string_value = (@scanner[1] || @scanner[2])
|
21
|
+
string_value = Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
|
22
22
|
value = Script::Value::String.new(string_value, :string)
|
23
23
|
[:string, value]
|
24
24
|
end
|
@@ -193,8 +193,8 @@ module Sass::Script
|
|
193
193
|
# \{#map_merge map-merge($map1, $map2)}
|
194
194
|
# : Merges two maps together into a new map.
|
195
195
|
#
|
196
|
-
# \{#map_remove map-remove($map, $
|
197
|
-
# : Returns a new map with
|
196
|
+
# \{#map_remove map-remove($map, $keys...)}
|
197
|
+
# : Returns a new map with keys removed.
|
198
198
|
#
|
199
199
|
# \{#map_keys map-keys($map)}
|
200
200
|
# : Returns a list of all keys in a map.
|
@@ -208,6 +208,48 @@ module Sass::Script
|
|
208
208
|
# \{#keywords keywords($args)}
|
209
209
|
# : Returns the keywords passed to a function that takes variable arguments.
|
210
210
|
#
|
211
|
+
# ## Selector Functions
|
212
|
+
#
|
213
|
+
# Selector functions are very liberal in the formats they support
|
214
|
+
# for selector arguments. They can take a plain string, a list of
|
215
|
+
# lists as returned by `&` or anything in between:
|
216
|
+
#
|
217
|
+
# * A plain sring, such as `".foo .bar, .baz .bang"`.
|
218
|
+
# * A space-separated list of strings such as `(".foo" ".bar")`.
|
219
|
+
# * A comma-separated list of strings such as `(".foo .bar", ".baz .bang")`.
|
220
|
+
# * A comma-separated list of space-separated lists of strings such
|
221
|
+
# as `((".foo" ".bar"), (".baz" ".bang"))`.
|
222
|
+
#
|
223
|
+
# In general, selector functions allow placeholder selectors
|
224
|
+
# (`%foo`) but disallow parent-reference selectors (`&`).
|
225
|
+
#
|
226
|
+
# \{#selector_nest selector-nest($selectors...)}
|
227
|
+
# : Nests selector beneath one another like they would be nested in the
|
228
|
+
# stylesheet.
|
229
|
+
#
|
230
|
+
# \{#selector_append selector-append($selectors...)}
|
231
|
+
# : Appends selectors to one another without spaces in between.
|
232
|
+
#
|
233
|
+
# \{#selector_extend selector-extend($selector, $extendee, $extender)}
|
234
|
+
# : Extends `$extendee` with `$extender` within `$selector`.
|
235
|
+
#
|
236
|
+
# \{#selector_replace selector-replace($selector, $original, $replacement)}
|
237
|
+
# : Replaces `$original` with `$replacement` within `$selector`.
|
238
|
+
#
|
239
|
+
# \{#selector_unify selector-unify($selector1, $selector2)}
|
240
|
+
# : Unifies two selectors to produce a selector that matches
|
241
|
+
# elements matched by both.
|
242
|
+
#
|
243
|
+
# \{#is_superselector is-superselector($super, $sub)}
|
244
|
+
# : Returns whether `$super` matches all the elements `$sub` does, and
|
245
|
+
# possibly more.
|
246
|
+
#
|
247
|
+
# \{#simple_selectors simple-selectors($selector)}
|
248
|
+
# : Returns the simple selectors that comprise a compound selector.
|
249
|
+
#
|
250
|
+
# \{#selector_parse selector-parse($selector)}
|
251
|
+
# : Parses a selector into the format returned by `&`.
|
252
|
+
#
|
211
253
|
# ## Introspection Functions
|
212
254
|
#
|
213
255
|
# \{#feature_exists feature-exists($feature)}
|
@@ -477,7 +519,7 @@ module Sass::Script
|
|
477
519
|
def assert_type(value, type, name = nil)
|
478
520
|
klass = Sass::Script::Value.const_get(type)
|
479
521
|
return if value.is_a?(klass)
|
480
|
-
return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.
|
522
|
+
return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.value.empty?
|
481
523
|
err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
|
482
524
|
err = "$#{name.to_s.gsub('_', '-')}: " + err if name
|
483
525
|
raise ArgumentError.new(err)
|
@@ -594,6 +636,10 @@ module Sass::Script
|
|
594
636
|
raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
|
595
637
|
end
|
596
638
|
end
|
639
|
+
|
640
|
+
# Don't store the string representation for function-created colors, both
|
641
|
+
# because it's not very useful and because some functions aren't supported
|
642
|
+
# on older browsers.
|
597
643
|
Sass::Script::Value::Color.new(color_attrs)
|
598
644
|
end
|
599
645
|
declare :rgb, [:red, :green, :blue]
|
@@ -701,6 +747,9 @@ module Sass::Script
|
|
701
747
|
s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
|
702
748
|
l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
|
703
749
|
|
750
|
+
# Don't store the string representation for function-created colors, both
|
751
|
+
# because it's not very useful and because some functions aren't supported
|
752
|
+
# on older browsers.
|
704
753
|
Sass::Script::Value::Color.new(
|
705
754
|
:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
|
706
755
|
end
|
@@ -1269,8 +1318,8 @@ module Sass::Script
|
|
1269
1318
|
rgba << color1.alpha * p + color2.alpha * (1 - p)
|
1270
1319
|
rgb_color(*rgba)
|
1271
1320
|
end
|
1272
|
-
declare :mix, [:color1, :color2]
|
1273
|
-
declare :mix, [:color1, :color2, :weight]
|
1321
|
+
declare :mix, [:color1, :color2]
|
1322
|
+
declare :mix, [:color1, :color2, :weight]
|
1274
1323
|
|
1275
1324
|
# Converts a color to grayscale. This is identical to `desaturate(color,
|
1276
1325
|
# 100%)`.
|
@@ -1465,7 +1514,6 @@ module Sass::Script
|
|
1465
1514
|
end_at = number(-1) if end_at.nil?
|
1466
1515
|
assert_unit end_at, nil, "end-at"
|
1467
1516
|
|
1468
|
-
return Sass::Script::Value::String.new("", string.type) if end_at.value == 0
|
1469
1517
|
s = start_at.value > 0 ? start_at.value - 1 : start_at.value
|
1470
1518
|
e = end_at.value > 0 ? end_at.value - 1 : end_at.value
|
1471
1519
|
s = string.value.length + s if s < 0
|
@@ -1528,6 +1576,19 @@ module Sass::Script
|
|
1528
1576
|
|
1529
1577
|
# Returns whether a feature exists in the current Sass runtime.
|
1530
1578
|
#
|
1579
|
+
# The following features are supported:
|
1580
|
+
#
|
1581
|
+
# * `global-variable-exists` indicates that a local variable will shadow a
|
1582
|
+
# global variable unless `!global` is used.
|
1583
|
+
#
|
1584
|
+
# * `extend-selector-pseudoclass` indicates that `@extend` will reach into
|
1585
|
+
# selector pseudoclasses like `:not`.
|
1586
|
+
#
|
1587
|
+
# * `units-level-3` indicates full support for unit arithmetic using units
|
1588
|
+
# defined in the [Values and Units Level 3][] spec.
|
1589
|
+
#
|
1590
|
+
# [Values and Units Level 3]: http://www.w3.org/TR/css3-values/
|
1591
|
+
#
|
1531
1592
|
# @example
|
1532
1593
|
# feature-exists(some-feature-that-exists) => true
|
1533
1594
|
# feature-exists(what-is-this-i-dont-know) => false
|
@@ -1593,7 +1654,7 @@ module Sass::Script
|
|
1593
1654
|
assert_type number2, :Number, :number2
|
1594
1655
|
bool(number1.comparable_to?(number2))
|
1595
1656
|
end
|
1596
|
-
declare :comparable, [:number1, :number2]
|
1657
|
+
declare :comparable, [:number1, :number2]
|
1597
1658
|
|
1598
1659
|
# Converts a unitless number to a percentage.
|
1599
1660
|
#
|
@@ -1610,7 +1671,7 @@ module Sass::Script
|
|
1610
1671
|
end
|
1611
1672
|
number(number.value * 100, '%')
|
1612
1673
|
end
|
1613
|
-
declare :percentage, [:number]
|
1674
|
+
declare :percentage, [:number]
|
1614
1675
|
|
1615
1676
|
# Rounds a number to the nearest whole number.
|
1616
1677
|
#
|
@@ -1624,7 +1685,7 @@ module Sass::Script
|
|
1624
1685
|
def round(number)
|
1625
1686
|
numeric_transformation(number) {|n| n.round}
|
1626
1687
|
end
|
1627
|
-
declare :round, [:number]
|
1688
|
+
declare :round, [:number]
|
1628
1689
|
|
1629
1690
|
# Rounds a number up to the next whole number.
|
1630
1691
|
#
|
@@ -1638,7 +1699,7 @@ module Sass::Script
|
|
1638
1699
|
def ceil(number)
|
1639
1700
|
numeric_transformation(number) {|n| n.ceil}
|
1640
1701
|
end
|
1641
|
-
declare :ceil, [:number]
|
1702
|
+
declare :ceil, [:number]
|
1642
1703
|
|
1643
1704
|
# Rounds a number down to the previous whole number.
|
1644
1705
|
#
|
@@ -1652,7 +1713,7 @@ module Sass::Script
|
|
1652
1713
|
def floor(number)
|
1653
1714
|
numeric_transformation(number) {|n| n.floor}
|
1654
1715
|
end
|
1655
|
-
declare :floor, [:number]
|
1716
|
+
declare :floor, [:number]
|
1656
1717
|
|
1657
1718
|
# Returns the absolute value of a number.
|
1658
1719
|
#
|
@@ -1666,7 +1727,7 @@ module Sass::Script
|
|
1666
1727
|
def abs(number)
|
1667
1728
|
numeric_transformation(number) {|n| n.abs}
|
1668
1729
|
end
|
1669
|
-
declare :abs, [:number]
|
1730
|
+
declare :abs, [:number]
|
1670
1731
|
|
1671
1732
|
# Finds the minimum of several numbers. This function takes any number of
|
1672
1733
|
# arguments.
|
@@ -1894,8 +1955,7 @@ module Sass::Script
|
|
1894
1955
|
# 1-based index of `$value` in `$list`, or `null`
|
1895
1956
|
def index(list, value)
|
1896
1957
|
index = list.to_a.index {|e| e.eq(value).to_bool}
|
1897
|
-
|
1898
|
-
Sass::Script::Value::DeprecatedFalse.new(environment)
|
1958
|
+
index ? number(index + 1) : null
|
1899
1959
|
end
|
1900
1960
|
declare :index, [:list, :value]
|
1901
1961
|
|
@@ -1929,7 +1989,7 @@ module Sass::Script
|
|
1929
1989
|
# @raise [ArgumentError] if `$map` is not a map
|
1930
1990
|
def map_get(map, key)
|
1931
1991
|
assert_type map, :Map, :map
|
1932
|
-
to_h
|
1992
|
+
map.to_h[key] || null
|
1933
1993
|
end
|
1934
1994
|
declare :map_get, [:map, :key]
|
1935
1995
|
|
@@ -1953,27 +2013,28 @@ module Sass::Script
|
|
1953
2013
|
def map_merge(map1, map2)
|
1954
2014
|
assert_type map1, :Map, :map1
|
1955
2015
|
assert_type map2, :Map, :map2
|
1956
|
-
map(
|
2016
|
+
map(map1.to_h.merge(map2.to_h))
|
1957
2017
|
end
|
1958
2018
|
declare :map_merge, [:map1, :map2]
|
1959
2019
|
|
1960
|
-
# Returns a new map with
|
2020
|
+
# Returns a new map with keys removed.
|
1961
2021
|
#
|
1962
2022
|
# @example
|
1963
2023
|
# map-remove(("foo": 1, "bar": 2), "bar") => ("foo": 1)
|
2024
|
+
# map-remove(("foo": 1, "bar": 2, "baz": 3), "bar", "baz") => ("foo": 1)
|
1964
2025
|
# map-remove(("foo": 1, "bar": 2), "baz") => ("foo": 1, "bar": 2)
|
1965
|
-
# @overload map_remove($map, $
|
1966
|
-
# @param $map
|
1967
|
-
# @param $
|
2026
|
+
# @overload map_remove($map, $keys...)
|
2027
|
+
# @param $map [Sass::Script::Value::Map]
|
2028
|
+
# @param $keys [[Sass::Script::Value::Base]]
|
1968
2029
|
# @return [Sass::Script::Value::Map]
|
1969
2030
|
# @raise [ArgumentError] if `$map` is not a map
|
1970
|
-
def map_remove(map,
|
2031
|
+
def map_remove(map, *keys)
|
1971
2032
|
assert_type map, :Map, :map
|
1972
|
-
hash =
|
1973
|
-
hash.
|
2033
|
+
hash = map.to_h.dup
|
2034
|
+
hash.delete_if {|key, _| keys.include?(key)}
|
1974
2035
|
map(hash)
|
1975
2036
|
end
|
1976
|
-
declare :map_remove, [:map, :key]
|
2037
|
+
declare :map_remove, [:map, :key], :var_args => true
|
1977
2038
|
|
1978
2039
|
# Returns a list of all keys in a map.
|
1979
2040
|
#
|
@@ -1985,7 +2046,7 @@ module Sass::Script
|
|
1985
2046
|
# @raise [ArgumentError] if `$map` is not a map
|
1986
2047
|
def map_keys(map)
|
1987
2048
|
assert_type map, :Map, :map
|
1988
|
-
list(
|
2049
|
+
list(map.to_h.keys, :comma)
|
1989
2050
|
end
|
1990
2051
|
declare :map_keys, [:map]
|
1991
2052
|
|
@@ -2001,7 +2062,7 @@ module Sass::Script
|
|
2001
2062
|
# @raise [ArgumentError] if `$map` is not a map
|
2002
2063
|
def map_values(map)
|
2003
2064
|
assert_type map, :Map, :map
|
2004
|
-
list(
|
2065
|
+
list(map.to_h.values, :comma)
|
2005
2066
|
end
|
2006
2067
|
declare :map_values, [:map]
|
2007
2068
|
|
@@ -2017,7 +2078,7 @@ module Sass::Script
|
|
2017
2078
|
# @raise [ArgumentError] if `$map` is not a map
|
2018
2079
|
def map_has_key(map, key)
|
2019
2080
|
assert_type map, :Map, :map
|
2020
|
-
bool(
|
2081
|
+
bool(map.to_h.has_key?(key))
|
2021
2082
|
end
|
2022
2083
|
declare :map_has_key, [:map, :key]
|
2023
2084
|
|
@@ -2251,6 +2312,281 @@ module Sass::Script
|
|
2251
2312
|
declare :random, []
|
2252
2313
|
declare :random, [:limit]
|
2253
2314
|
|
2315
|
+
# Parses a user-provided selector into a list of lists of strings
|
2316
|
+
# as returned by `&`.
|
2317
|
+
#
|
2318
|
+
# @example
|
2319
|
+
# selector-parse(".foo .bar, .baz .bang") => ('.foo' '.bar', '.baz' '.bang')
|
2320
|
+
#
|
2321
|
+
# @overload selector_parse($selector)
|
2322
|
+
# @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
|
2323
|
+
# The selector to parse. This can be either a string, a list of
|
2324
|
+
# strings, or a list of lists of strings as returned by `&`.
|
2325
|
+
# @return [Sass::Script::Value::List]
|
2326
|
+
# A list of lists of strings representing `$selector`. This is
|
2327
|
+
# in the same format as a selector returned by `&`.
|
2328
|
+
def selector_parse(selector)
|
2329
|
+
parse_selector(selector, :selector).to_sass_script
|
2330
|
+
end
|
2331
|
+
declare :selector_parse, [:selector]
|
2332
|
+
|
2333
|
+
# Return a new selector with all selectors in `$selectors` nested beneath
|
2334
|
+
# one another as though they had been nested in the stylesheet as
|
2335
|
+
# `$selector1 { $selector2 { ... } }`.
|
2336
|
+
#
|
2337
|
+
# Unlike most selector functions, `selector-nest` allows the
|
2338
|
+
# parent selector `&` to be used in any selector but the first.
|
2339
|
+
#
|
2340
|
+
# @example
|
2341
|
+
# selector-nest(".foo", ".bar", ".baz") => .foo .bar .baz
|
2342
|
+
# selector-nest(".a .foo", ".b .bar") => .a .foo .b .bar
|
2343
|
+
# selector-nest(".foo", "&.bar") => .foo.bar
|
2344
|
+
#
|
2345
|
+
# @overload selector_nest($selectors...)
|
2346
|
+
# @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
|
2347
|
+
# The selectors to nest. At least one selector must be passed. Each of
|
2348
|
+
# these can be either a string, a list of strings, or a list of lists of
|
2349
|
+
# strings as returned by `&`.
|
2350
|
+
# @return [Sass::Script::Value::List]
|
2351
|
+
# A list of lists of strings representing the result of nesting
|
2352
|
+
# `$selectors`. This is in the same format as a selector returned by
|
2353
|
+
# `&`.
|
2354
|
+
def selector_nest(*selectors)
|
2355
|
+
if selectors.empty?
|
2356
|
+
raise ArgumentError.new("$selectors: At least one selector must be passed")
|
2357
|
+
end
|
2358
|
+
|
2359
|
+
parsed = [parse_selector(selectors.first, :selectors)]
|
2360
|
+
parsed += selectors[1..-1].map {|sel| parse_selector(sel, :selectors, !!:parse_parent_ref)}
|
2361
|
+
parsed.inject {|result, child| child.resolve_parent_refs(result)}.to_sass_script
|
2362
|
+
end
|
2363
|
+
declare :selector_nest, [], :var_args => true
|
2364
|
+
|
2365
|
+
# Return a new selector with all selectors in `$selectors` appended one
|
2366
|
+
# another as though they had been nested in the stylesheet as `$selector1 {
|
2367
|
+
# &$selector2 { ... } }`.
|
2368
|
+
#
|
2369
|
+
# @example
|
2370
|
+
# selector-append(".foo", ".bar", ".baz") => .foo.bar.baz
|
2371
|
+
# selector-append(".a .foo", ".b .bar") => "a .foo.b .bar"
|
2372
|
+
# selector-append(".foo", "-suffix") => ".foo-suffix"
|
2373
|
+
#
|
2374
|
+
# @overload selector_append($selectors...)
|
2375
|
+
# @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
|
2376
|
+
# The selectors to append. At least one selector must be passed. Each of
|
2377
|
+
# these can be either a string, a list of strings, or a list of lists of
|
2378
|
+
# strings as returned by `&`.
|
2379
|
+
# @return [Sass::Script::Value::List]
|
2380
|
+
# A list of lists of strings representing the result of appending
|
2381
|
+
# `$selectors`. This is in the same format as a selector returned by
|
2382
|
+
# `&`.
|
2383
|
+
# @raise [ArgumentError] if a selector could not be appended.
|
2384
|
+
def selector_append(*selectors)
|
2385
|
+
if selectors.empty?
|
2386
|
+
raise ArgumentError.new("$selectors: At least one selector must be passed")
|
2387
|
+
end
|
2388
|
+
|
2389
|
+
selectors.map {|sel| parse_selector(sel, :selectors)}.inject do |parent, child|
|
2390
|
+
child.members.each do |seq|
|
2391
|
+
sseq = seq.members.first
|
2392
|
+
unless sseq.is_a?(Sass::Selector::SimpleSequence)
|
2393
|
+
raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
|
2394
|
+
end
|
2395
|
+
|
2396
|
+
base = sseq.base
|
2397
|
+
case base
|
2398
|
+
when Sass::Selector::Universal
|
2399
|
+
raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
|
2400
|
+
when Sass::Selector::Element
|
2401
|
+
unless base.namespace.nil?
|
2402
|
+
raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
|
2403
|
+
end
|
2404
|
+
sseq.members[0] = Sass::Selector::Parent.new(base.name)
|
2405
|
+
else
|
2406
|
+
sseq.members.unshift Sass::Selector::Parent.new
|
2407
|
+
end
|
2408
|
+
end
|
2409
|
+
child.resolve_parent_refs(parent)
|
2410
|
+
end.to_sass_script
|
2411
|
+
end
|
2412
|
+
declare :selector_append, [], :var_args => true
|
2413
|
+
|
2414
|
+
# Returns a new version of `$selector` with `$extendee` extended
|
2415
|
+
# with `$extender`. This works just like the result of
|
2416
|
+
#
|
2417
|
+
# $selector { ... }
|
2418
|
+
# $extender { @extend $extendee }
|
2419
|
+
#
|
2420
|
+
# @example
|
2421
|
+
# selector-extend(".a .b", ".b", ".foo .bar") => .a .b, .a .foo .bar, .foo .a .bar
|
2422
|
+
#
|
2423
|
+
# @overload selector_extend($selector, $extendee, $extender)
|
2424
|
+
# @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
|
2425
|
+
# The selector within which `$extendee` is extended with
|
2426
|
+
# `$extender`. This can be either a string, a list of strings,
|
2427
|
+
# or a list of lists of strings as returned by `&`.
|
2428
|
+
# @param $extendee [Sass::Script::Value::String, Sass::Script::Value::List]
|
2429
|
+
# The selector being extended. This can be either a string, a
|
2430
|
+
# list of strings, or a list of lists of strings as returned
|
2431
|
+
# by `&`.
|
2432
|
+
# @param $extender [Sass::Script::Value::String, Sass::Script::Value::List]
|
2433
|
+
# The selector being injected into `$selector`. This can be
|
2434
|
+
# either a string, a list of strings, or a list of lists of
|
2435
|
+
# strings as returned by `&`.
|
2436
|
+
# @return [Sass::Script::Value::List]
|
2437
|
+
# A list of lists of strings representing the result of the
|
2438
|
+
# extension. This is in the same format as a selector returned
|
2439
|
+
# by `&`.
|
2440
|
+
# @raise [ArgumentError] if the extension fails
|
2441
|
+
def selector_extend(selector, extendee, extender)
|
2442
|
+
selector = parse_selector(selector, :selector)
|
2443
|
+
extendee = parse_selector(extendee, :extendee)
|
2444
|
+
extender = parse_selector(extender, :extender)
|
2445
|
+
|
2446
|
+
extends = Sass::Util::SubsetMap.new
|
2447
|
+
begin
|
2448
|
+
extender.populate_extends(extends, extendee)
|
2449
|
+
selector.do_extend(extends).to_sass_script
|
2450
|
+
rescue Sass::SyntaxError => e
|
2451
|
+
raise ArgumentError.new(e.to_s)
|
2452
|
+
end
|
2453
|
+
end
|
2454
|
+
declare :selector_extend, [:selector, :extendee, :extender]
|
2455
|
+
|
2456
|
+
# Replaces all instances of `$original` with `$replacement` in `$selector`
|
2457
|
+
#
|
2458
|
+
# This works by using `@extend` and throwing away the original
|
2459
|
+
# selector. This means that it can be used to do very advanced
|
2460
|
+
# replacements; see the examples below.
|
2461
|
+
#
|
2462
|
+
# @example
|
2463
|
+
# selector-replace(".foo .bar", ".bar", ".baz") => ".foo .baz"
|
2464
|
+
# selector-replace(".foo.bar.baz", ".foo.baz", ".qux") => ".foo.qux"
|
2465
|
+
#
|
2466
|
+
# @overload selector_replace($selector, $original, $replacement)
|
2467
|
+
# @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
|
2468
|
+
# The selector within which `$original` is replaced with
|
2469
|
+
# `$replacement`. This can be either a string, a list of
|
2470
|
+
# strings, or a list of lists of strings as returned by `&`.
|
2471
|
+
# @param $original [Sass::Script::Value::String, Sass::Script::Value::List]
|
2472
|
+
# The selector being replaced. This can be either a string, a
|
2473
|
+
# list of strings, or a list of lists of strings as returned
|
2474
|
+
# by `&`.
|
2475
|
+
# @param $replacement [Sass::Script::Value::String, Sass::Script::Value::List]
|
2476
|
+
# The selector that `$original` is being replaced with. This
|
2477
|
+
# can be either a string, a list of strings, or a list of
|
2478
|
+
# lists of strings as returned by `&`.
|
2479
|
+
# @return [Sass::Script::Value::List]
|
2480
|
+
# A list of lists of strings representing the result of the
|
2481
|
+
# extension. This is in the same format as a selector returned
|
2482
|
+
# by `&`.
|
2483
|
+
# @raise [ArgumentError] if the replacement fails
|
2484
|
+
def selector_replace(selector, original, replacement)
|
2485
|
+
selector = parse_selector(selector, :selector)
|
2486
|
+
original = parse_selector(original, :original)
|
2487
|
+
replacement = parse_selector(replacement, :replacement)
|
2488
|
+
|
2489
|
+
extends = Sass::Util::SubsetMap.new
|
2490
|
+
begin
|
2491
|
+
replacement.populate_extends(extends, original)
|
2492
|
+
selector.do_extend(extends, [], !!:replace).to_sass_script
|
2493
|
+
rescue Sass::SyntaxError => e
|
2494
|
+
raise ArgumentError.new(e.to_s)
|
2495
|
+
end
|
2496
|
+
end
|
2497
|
+
declare :selector_replace, [:selector, :original, :replacement]
|
2498
|
+
|
2499
|
+
# Unifies two selectors into a single selector that matches only
|
2500
|
+
# elements matched by both input selectors. Returns `null` if
|
2501
|
+
# there is no such selector.
|
2502
|
+
#
|
2503
|
+
# Like the selector unification done for `@extend`, this doesn't
|
2504
|
+
# guarantee that the output selector will match *all* elements
|
2505
|
+
# matched by both input selectors. For example, if `.a .b` is
|
2506
|
+
# unified with `.x .y`, `.a .x .b.y, .x .a .b.y` will be returned,
|
2507
|
+
# but `.a.x .b.y` will not. This avoids exponential output size
|
2508
|
+
# while matching all elements that are likely to exist in
|
2509
|
+
# practice.
|
2510
|
+
#
|
2511
|
+
# @example
|
2512
|
+
# selector-unify(".a", ".b") => .a.b
|
2513
|
+
# selector-unify(".a .b", ".x .y") => .a .x .b.y, .x .a .b.y
|
2514
|
+
# selector-unify(".a.b", ".b.c") => .a.b.c
|
2515
|
+
# selector-unify("#a", "#b") => null
|
2516
|
+
#
|
2517
|
+
# @overload selector_unify($selector1, $selector2)
|
2518
|
+
# @param $selector1 [Sass::Script::Value::String, Sass::Script::Value::List]
|
2519
|
+
# The first selector to be unified. This can be either a
|
2520
|
+
# string, a list of strings, or a list of lists of strings as
|
2521
|
+
# returned by `&`.
|
2522
|
+
# @param $selector2 [Sass::Script::Value::String, Sass::Script::Value::List]
|
2523
|
+
# The second selector to be unified. This can be either a
|
2524
|
+
# string, a list of strings, or a list of lists of strings as
|
2525
|
+
# returned by `&`.
|
2526
|
+
# @return [Sass::Script::Value::List, Sass::Script::Value::Null]
|
2527
|
+
# A list of lists of strings representing the result of the
|
2528
|
+
# unification, or null if no unification exists. This is in
|
2529
|
+
# the same format as a selector returned by `&`.
|
2530
|
+
def selector_unify(selector1, selector2)
|
2531
|
+
selector1 = parse_selector(selector1, :selector1)
|
2532
|
+
selector2 = parse_selector(selector2, :selector2)
|
2533
|
+
return null unless (unified = selector1.unify(selector2))
|
2534
|
+
unified.to_sass_script
|
2535
|
+
end
|
2536
|
+
declare :selector_unify, [:selector1, :selector2]
|
2537
|
+
|
2538
|
+
# Returns the [simple
|
2539
|
+
# selectors](http://dev.w3.org/csswg/selectors4/#simple) that
|
2540
|
+
# comprise the compound selector `$selector`.
|
2541
|
+
#
|
2542
|
+
# Note that `$selector` **must be** a [compound
|
2543
|
+
# selector](http://dev.w3.org/csswg/selectors4/#compound). That
|
2544
|
+
# means it cannot contain commas or spaces. It also means that
|
2545
|
+
# unlike other selector functions, this takes only strings, not
|
2546
|
+
# lists.
|
2547
|
+
#
|
2548
|
+
# @example
|
2549
|
+
# simple-selectors(".foo.bar") => ".foo", ".bar"
|
2550
|
+
# simple-selectors(".foo.bar.baz") => ".foo", ".bar", ".baz"
|
2551
|
+
#
|
2552
|
+
# @overload simple_selectors($selector)
|
2553
|
+
# @param $selector [Sass::Script::Value::String]
|
2554
|
+
# The compound selector whose simple selectors will be extracted.
|
2555
|
+
# @return [Sass::Script::Value::List]
|
2556
|
+
# A list of simple selectors in the compound selector.
|
2557
|
+
def simple_selectors(selector)
|
2558
|
+
selector = parse_compound_selector(selector, :selector)
|
2559
|
+
list(selector.members.map {|simple| unquoted_string(simple.to_s)}, :comma)
|
2560
|
+
end
|
2561
|
+
declare :simple_selectors, [:selector]
|
2562
|
+
|
2563
|
+
# Returns whether `$super` is a superselector of `$sub`. This means that
|
2564
|
+
# `$super` matches all the elements that `$sub` matches, as well as possibly
|
2565
|
+
# additional elements. In general, simpler selectors tend to be
|
2566
|
+
# superselectors of more complex oned.
|
2567
|
+
#
|
2568
|
+
# @example
|
2569
|
+
# is-superselector(".foo", ".foo.bar") => true
|
2570
|
+
# is-superselector(".foo.bar", ".foo") => false
|
2571
|
+
# is-superselector(".bar", ".foo .bar") => true
|
2572
|
+
# is-superselector(".foo .bar", ".bar") => true
|
2573
|
+
#
|
2574
|
+
# @overload is_superselector($super, $sub)
|
2575
|
+
# @param $super [Sass::Script::Value::String, Sass::Script::Value::List]
|
2576
|
+
# The potential superselector. This can be either a string, a list of
|
2577
|
+
# strings, or a list of lists of strings as returned by `&`.
|
2578
|
+
# @param $sub [Sass::Script::Value::String, Sass::Script::Value::List]
|
2579
|
+
# The potential subselector. This can be either a string, a list of
|
2580
|
+
# strings, or a list of lists of strings as returned by `&`.
|
2581
|
+
# @return [Sass::Script::Value::Bool]
|
2582
|
+
# Whether `$selector1` is a superselector of `$selector2`.
|
2583
|
+
def is_superselector(sup, sub)
|
2584
|
+
sup = parse_selector(sup, :super)
|
2585
|
+
sub = parse_selector(sub, :sub)
|
2586
|
+
bool(sup.superselector?(sub))
|
2587
|
+
end
|
2588
|
+
declare :is_superselector, [:super, :sub]
|
2589
|
+
|
2254
2590
|
private
|
2255
2591
|
|
2256
2592
|
# This method implements the pattern of transforming a numeric value into
|
@@ -2276,17 +2612,5 @@ module Sass::Script
|
|
2276
2612
|
color.with(attr => Sass::Util.restrict(
|
2277
2613
|
color.send(attr).send(op, amount.value), range))
|
2278
2614
|
end
|
2279
|
-
|
2280
|
-
def to_h(obj)
|
2281
|
-
return obj.to_h unless obj.is_a?(Sass::Script::Value::List) && obj.needs_map_warning?
|
2282
|
-
|
2283
|
-
fn_name = Sass::Util.caller_info.last.gsub('_', '-')
|
2284
|
-
Sass::Util.sass_warn <<WARNING + environment.stack.to_s.gsub(/^/, ' ')
|
2285
|
-
DEPRECATION WARNING: Passing lists of pairs to #{fn_name} is deprecated and will
|
2286
|
-
be removed in future versions of Sass. Use Sass maps instead. For details, see
|
2287
|
-
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#maps.
|
2288
|
-
WARNING
|
2289
|
-
obj.to_h
|
2290
|
-
end
|
2291
2615
|
end
|
2292
2616
|
end
|