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