Version Description
(30th Mar 2022) = * Bug: Fixed issue with submenu font-wieght * Bug: Fixed issue with svg icons * Enhancement: Updated scssphp to latest version (1.10.2)
Download this release
Release Info
Developer | expresstech |
Plugin | Responsive Menu |
Version | 4.1.10 |
Comparing to | |
See all releases |
Code changes from version 4.1.9 to 4.1.10
- readme.txt +12 -4
- responsive-menu.php +2 -2
- v4.0.0/assets/scss/main.scss +1 -0
- v4.0.0/inc/classes/class-control-manager.php +31 -16
- v4.0.0/inc/classes/class-editor.php +14 -9
- v4.0.0/inc/classes/class-style-manager.php +6 -0
- v4.0.0/inc/classes/class-ui-manager.php +9 -3
- v4.0.0/libs/scssphp/composer.json +1 -1
- v4.0.0/libs/scssphp/composer.lock +23 -9
- v4.0.0/libs/scssphp/vendor/bin/pscss +0 -14
- v4.0.0/libs/scssphp/vendor/composer/ClassLoader.php +40 -4
- v4.0.0/libs/scssphp/vendor/composer/InstalledVersions.php +337 -0
- v4.0.0/libs/scssphp/vendor/composer/autoload_classmap.php +1 -0
- v4.0.0/libs/scssphp/vendor/composer/autoload_real.php +7 -2
- v4.0.0/libs/scssphp/vendor/composer/autoload_static.php +5 -0
- v4.0.0/libs/scssphp/vendor/composer/installed.json +82 -64
- v4.0.0/libs/scssphp/vendor/composer/installed.php +32 -0
- v4.0.0/libs/scssphp/vendor/composer/platform_check.php +26 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/README.md +34 -9
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/bin/pscss +83 -54
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/composer.json +72 -12
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/phpcs.xml.dist +12 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/scss.inc.php +15 -28
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Base/Range.php +14 -4
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block.php +10 -7
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/AtRootBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/CallableBlock.php +45 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ContentBlock.php +38 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/DirectiveBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/EachBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ElseBlock.php +27 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ElseifBlock.php +32 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ForBlock.php +47 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/IfBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/MediaBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/NestedPropertyBlock.php +37 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/WhileBlock.php +32 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Cache.php +47 -14
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Colors.php +20 -14
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/CompilationResult.php +69 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler.php +4122 -1552
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler/CachedResult.php +77 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler/Environment.php +26 -3
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/CompilerException.php +4 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/ParserException.php +38 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/RangeException.php +4 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/SassException.php +7 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/SassScriptException.php +32 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/ServerException.php +6 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter.php +67 -21
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Compact.php +7 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Compressed.php +5 -3
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Crunched.php +9 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Debug.php +7 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Expanded.php +6 -2
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Nested.php +14 -5
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/OutputBlock.php +12 -9
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/LoggerInterface.php +48 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/QuietLogger.php +27 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/StreamLogger.php +60 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Node.php +6 -3
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Node/Number.php +567 -153
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/OutputStyle.php +9 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Parser.php +1273 -476
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/Base64.php +7 -4
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/Base64VLQ.php +16 -11
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/SourceMapGenerator.php +61 -19
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Type.php +138 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Util.php +119 -5
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Util/Path.php +77 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/ValueConverter.php +95 -0
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Version.php +2 -1
- v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Warn.php +84 -0
- v4.0.0/templates/rmp-editor.php +27 -23
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: expresstech, responsivemenu, imvarunkmr, surajkumarsingh, infosate
|
|
3 |
Tags: responsive, mega menu, navigation, mobile, hamburger
|
4 |
Requires at least: 3.6
|
5 |
Tested up to: 5.9
|
6 |
-
Stable tag: 4.1.
|
7 |
Requires PHP: 5.6
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
@@ -11,7 +11,8 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
|
11 |
Highly customisable Responsive Menu plugin with 150+ options. No coding knowledge needed to design it exactly as you want.
|
12 |
|
13 |
== Description ==
|
14 |
-
|
|
|
15 |
|
16 |
Highly customisable Responsive Menu Plugin for WordPress. With over 150 customisable options you get a combination of 22,500 options! No coding experience or knowledge is needed with an easy to use interface you can get it looking exactly as you want with minimal fuss.
|
17 |
|
@@ -73,9 +74,11 @@ For more reasons to go Pro, please visit [this page](https://responsive.menu#why
|
|
73 |
|
74 |
[youtube https://www.youtube.com/watch?v=aj6ba0tGKPg]
|
75 |
|
76 |
-
🌐Our
|
|
|
|
|
77 |
|
78 |
-
|
79 |
|
80 |
⏱️ [Quiz and Survey Master](https://wordpress.org/plugins/quiz-master-next/) - Best WordPress Quiz Plugin to create engaging quizzes, surveys, & exams using WordPress and convert your website into a lead generation machine.
|
81 |
|
@@ -117,6 +120,11 @@ To view our FAQ, please go to [https://responsive.menu/faq/](https://responsive.
|
|
117 |
|
118 |
== Changelog ==
|
119 |
|
|
|
|
|
|
|
|
|
|
|
120 |
= 4.1.9 (8th Mar 2022) =
|
121 |
* Enhancement: Fixed issues with admin bar settings
|
122 |
|
3 |
Tags: responsive, mega menu, navigation, mobile, hamburger
|
4 |
Requires at least: 3.6
|
5 |
Tested up to: 5.9
|
6 |
+
Stable tag: 4.1.10
|
7 |
Requires PHP: 5.6
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
Highly customisable Responsive Menu plugin with 150+ options. No coding knowledge needed to design it exactly as you want.
|
12 |
|
13 |
== Description ==
|
14 |
+
|
15 |
+
Try Demo ➡️ [Get a personal sandbox demo with Response Menu](https://instawp.io/plugins/responsive-menu)
|
16 |
|
17 |
Highly customisable Responsive Menu Plugin for WordPress. With over 150 customisable options you get a combination of 22,500 options! No coding experience or knowledge is needed with an easy to use interface you can get it looking exactly as you want with minimal fuss.
|
18 |
|
74 |
|
75 |
[youtube https://www.youtube.com/watch?v=aj6ba0tGKPg]
|
76 |
|
77 |
+
🌐Our themes and plugins
|
78 |
+
|
79 |
+
If you like this plugin, consider exploring our other themes and plugins:
|
80 |
|
81 |
+
🧱 [Gutena](https://wordpress.org/themes/gutena/) - Block Based Theme for WordPress includes over 50 free block patterns with new patterns added every week.
|
82 |
|
83 |
⏱️ [Quiz and Survey Master](https://wordpress.org/plugins/quiz-master-next/) - Best WordPress Quiz Plugin to create engaging quizzes, surveys, & exams using WordPress and convert your website into a lead generation machine.
|
84 |
|
120 |
|
121 |
== Changelog ==
|
122 |
|
123 |
+
= 4.1.10 (30th Mar 2022) =
|
124 |
+
* Bug: Fixed issue with submenu font-wieght
|
125 |
+
* Bug: Fixed issue with svg icons
|
126 |
+
* Enhancement: Updated scssphp to latest version (1.10.2)
|
127 |
+
|
128 |
= 4.1.9 (8th Mar 2022) =
|
129 |
* Enhancement: Fixed issues with admin bar settings
|
130 |
|
responsive-menu.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
Plugin Name: Responsive Menu
|
5 |
Plugin URI: https://expresstech.io
|
6 |
Description: Highly Customisable Responsive Menu Plugin for WordPress
|
7 |
-
Version: 4.1.
|
8 |
Author: ExpressTech
|
9 |
Text Domain: responsive-menu
|
10 |
Author URI: https://responsive.menu
|
@@ -16,7 +16,7 @@ Tags: responsive, menu, responsive menu, mega menu, max mega menu, max menu
|
|
16 |
* Constant as plugin version.
|
17 |
*/
|
18 |
if ( ! defined( 'RMP_PLUGIN_VERSION' ) ) {
|
19 |
-
define( 'RMP_PLUGIN_VERSION', '4.1.
|
20 |
}
|
21 |
|
22 |
define( 'RESPONSIVE_MENU_URL', plugin_dir_url( __FILE__ ) );
|
4 |
Plugin Name: Responsive Menu
|
5 |
Plugin URI: https://expresstech.io
|
6 |
Description: Highly Customisable Responsive Menu Plugin for WordPress
|
7 |
+
Version: 4.1.10
|
8 |
Author: ExpressTech
|
9 |
Text Domain: responsive-menu
|
10 |
Author URI: https://responsive.menu
|
16 |
* Constant as plugin version.
|
17 |
*/
|
18 |
if ( ! defined( 'RMP_PLUGIN_VERSION' ) ) {
|
19 |
+
define( 'RMP_PLUGIN_VERSION', '4.1.10' );
|
20 |
}
|
21 |
|
22 |
define( 'RESPONSIVE_MENU_URL', plugin_dir_url( __FILE__ ) );
|
v4.0.0/assets/scss/main.scss
CHANGED
@@ -448,6 +448,7 @@
|
|
448 |
font-family: inherit;
|
449 |
}
|
450 |
|
|
|
451 |
color: $submenu_item_text_color;
|
452 |
text-align: $submenu_item_text_alignment;
|
453 |
background-color: $submenu_item_background_color;
|
448 |
font-family: inherit;
|
449 |
}
|
450 |
|
451 |
+
font-weight: $submenu_font_weight;
|
452 |
color: $submenu_item_text_color;
|
453 |
text-align: $submenu_item_text_alignment;
|
454 |
background-color: $submenu_item_background_color;
|
v4.0.0/inc/classes/class-control-manager.php
CHANGED
@@ -740,6 +740,12 @@ class Control_Manager {
|
|
740 |
return;
|
741 |
}
|
742 |
|
|
|
|
|
|
|
|
|
|
|
|
|
743 |
$group_classes = '';
|
744 |
|
745 |
if ( ! empty( $param['group_classes'] ) ) {
|
@@ -802,9 +808,9 @@ class Control_Manager {
|
|
802 |
<div class="rmp-icon-picker-placeholder">
|
803 |
<span>
|
804 |
<?php
|
805 |
-
$svg_placeholder =
|
806 |
-
if (
|
807 |
-
echo wp_kses( $svg_placeholder
|
808 |
}
|
809 |
?>
|
810 |
</span>
|
@@ -845,7 +851,11 @@ class Control_Manager {
|
|
845 |
if ( empty( $param ) ) {
|
846 |
return;
|
847 |
}
|
848 |
-
|
|
|
|
|
|
|
|
|
849 |
$group_classes = '';
|
850 |
|
851 |
if ( ! empty( $param['group_classes'] ) ) {
|
@@ -899,9 +909,9 @@ class Control_Manager {
|
|
899 |
<div class="rmp-image-picker-placeholder">
|
900 |
<span>
|
901 |
<?php
|
902 |
-
$svg_placeholder =
|
903 |
-
if (
|
904 |
-
echo wp_kses( $svg_placeholder
|
905 |
}
|
906 |
?>
|
907 |
</span>
|
@@ -1107,31 +1117,36 @@ class Control_Manager {
|
|
1107 |
* @return HTML
|
1108 |
*/
|
1109 |
protected function get_device_options() {
|
|
|
|
|
|
|
|
|
|
|
1110 |
?>
|
1111 |
<div class="rmp-device-switcher-holder">
|
1112 |
<a target="_blank" rel="noopener" class="upgrade-tooltip" href="<?php echo esc_url( $this->pro_plugin_url ); ?>" > PRO </a>
|
1113 |
<ul class="select rmp-device-switcher" >
|
1114 |
<li data-device="mobile">
|
1115 |
<?php
|
1116 |
-
$svg_mobile =
|
1117 |
-
if (
|
1118 |
-
echo wp_kses( $svg_mobile
|
1119 |
}
|
1120 |
?>
|
1121 |
</li>
|
1122 |
<li data-device="tablet">
|
1123 |
<?php
|
1124 |
-
$svg_tablet =
|
1125 |
-
if (
|
1126 |
-
echo wp_kses( $svg_tablet
|
1127 |
}
|
1128 |
?>
|
1129 |
</li>
|
1130 |
<li data-device="desktop">
|
1131 |
<?php
|
1132 |
-
$svg_desktop =
|
1133 |
-
if (
|
1134 |
-
echo wp_kses( $svg_desktop
|
1135 |
}
|
1136 |
?>
|
1137 |
</li>
|
740 |
return;
|
741 |
}
|
742 |
|
743 |
+
global $wp_filesystem;
|
744 |
+
if ( empty( $wp_filesystem ) ) {
|
745 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
746 |
+
}
|
747 |
+
WP_Filesystem();
|
748 |
+
|
749 |
$group_classes = '';
|
750 |
|
751 |
if ( ! empty( $param['group_classes'] ) ) {
|
808 |
<div class="rmp-icon-picker-placeholder">
|
809 |
<span>
|
810 |
<?php
|
811 |
+
$svg_placeholder = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/image-placeholder.svg' );
|
812 |
+
if ( $svg_placeholder ) {
|
813 |
+
echo wp_kses( $svg_placeholder, rmp_allow_svg_html_tags() );
|
814 |
}
|
815 |
?>
|
816 |
</span>
|
851 |
if ( empty( $param ) ) {
|
852 |
return;
|
853 |
}
|
854 |
+
global $wp_filesystem;
|
855 |
+
if ( empty( $wp_filesystem ) ) {
|
856 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
857 |
+
}
|
858 |
+
WP_Filesystem();
|
859 |
$group_classes = '';
|
860 |
|
861 |
if ( ! empty( $param['group_classes'] ) ) {
|
909 |
<div class="rmp-image-picker-placeholder">
|
910 |
<span>
|
911 |
<?php
|
912 |
+
$svg_placeholder = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/image-placeholder.svg' );
|
913 |
+
if ( $svg_placeholder ) {
|
914 |
+
echo wp_kses( $svg_placeholder, rmp_allow_svg_html_tags() );
|
915 |
}
|
916 |
?>
|
917 |
</span>
|
1117 |
* @return HTML
|
1118 |
*/
|
1119 |
protected function get_device_options() {
|
1120 |
+
global $wp_filesystem;
|
1121 |
+
if ( empty( $wp_filesystem ) ) {
|
1122 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
1123 |
+
}
|
1124 |
+
WP_Filesystem();
|
1125 |
?>
|
1126 |
<div class="rmp-device-switcher-holder">
|
1127 |
<a target="_blank" rel="noopener" class="upgrade-tooltip" href="<?php echo esc_url( $this->pro_plugin_url ); ?>" > PRO </a>
|
1128 |
<ul class="select rmp-device-switcher" >
|
1129 |
<li data-device="mobile">
|
1130 |
<?php
|
1131 |
+
$svg_mobile = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/mobile.svg' );
|
1132 |
+
if ( $svg_mobile ) {
|
1133 |
+
echo wp_kses( $svg_mobile, rmp_allow_svg_html_tags() );
|
1134 |
}
|
1135 |
?>
|
1136 |
</li>
|
1137 |
<li data-device="tablet">
|
1138 |
<?php
|
1139 |
+
$svg_tablet = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/tablet.svg' );
|
1140 |
+
if ( $svg_tablet ) {
|
1141 |
+
echo wp_kses( $svg_tablet, rmp_allow_svg_html_tags() );
|
1142 |
}
|
1143 |
?>
|
1144 |
</li>
|
1145 |
<li data-device="desktop">
|
1146 |
<?php
|
1147 |
+
$svg_desktop = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/desktop.svg' );
|
1148 |
+
if ( $svg_desktop ) {
|
1149 |
+
echo wp_kses( $svg_desktop, rmp_allow_svg_html_tags() );
|
1150 |
}
|
1151 |
?>
|
1152 |
</li>
|
v4.0.0/inc/classes/class-editor.php
CHANGED
@@ -105,6 +105,11 @@ class Editor {
|
|
105 |
* @param HTML.
|
106 |
*/
|
107 |
public function footer_section() {
|
|
|
|
|
|
|
|
|
|
|
108 |
?>
|
109 |
<div id="rmp-editor-footer" class="rmp-editor-footer">
|
110 |
|
@@ -141,9 +146,9 @@ class Editor {
|
|
141 |
|
142 |
<button type="button" id="rmp-preview-mobile" class=" rmp-device-preview rmp-preview-mobile active" aria-pressed="1" data-device="mobile">
|
143 |
<?php
|
144 |
-
$svg_mobile =
|
145 |
-
if (
|
146 |
-
echo wp_kses( $svg_mobile
|
147 |
}
|
148 |
?>
|
149 |
<span class="screen-reader-text">
|
@@ -153,9 +158,9 @@ class Editor {
|
|
153 |
|
154 |
<button type="button" id="rmp-preview-tablet" class="rmp-preview-tablet rmp-device-preview" aria-pressed="" data-device="tablet">
|
155 |
<?php
|
156 |
-
$svg_tablet =
|
157 |
-
if (
|
158 |
-
echo wp_kses( $svg_tablet
|
159 |
}
|
160 |
?>
|
161 |
<span class="screen-reader-text">
|
@@ -165,9 +170,9 @@ class Editor {
|
|
165 |
|
166 |
<button type="button" id="rmp-preview-desktop" class="rmp-preview-desktop rmp-device-preview" aria-pressed="" data-device="desktop">
|
167 |
<?php
|
168 |
-
$svg_desktop =
|
169 |
-
if (
|
170 |
-
echo wp_kses( $svg_desktop
|
171 |
}
|
172 |
?>
|
173 |
<span class="screen-reader-text">
|
105 |
* @param HTML.
|
106 |
*/
|
107 |
public function footer_section() {
|
108 |
+
global $wp_filesystem;
|
109 |
+
if ( empty( $wp_filesystem ) ) {
|
110 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
111 |
+
}
|
112 |
+
WP_Filesystem();
|
113 |
?>
|
114 |
<div id="rmp-editor-footer" class="rmp-editor-footer">
|
115 |
|
146 |
|
147 |
<button type="button" id="rmp-preview-mobile" class=" rmp-device-preview rmp-preview-mobile active" aria-pressed="1" data-device="mobile">
|
148 |
<?php
|
149 |
+
$svg_mobile = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/mobile.svg' );
|
150 |
+
if ( $svg_mobile ) {
|
151 |
+
echo wp_kses( $svg_mobile, rmp_allow_svg_html_tags() );
|
152 |
}
|
153 |
?>
|
154 |
<span class="screen-reader-text">
|
158 |
|
159 |
<button type="button" id="rmp-preview-tablet" class="rmp-preview-tablet rmp-device-preview" aria-pressed="" data-device="tablet">
|
160 |
<?php
|
161 |
+
$svg_tablet = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/tablet.svg' );
|
162 |
+
if ( $svg_tablet ) {
|
163 |
+
echo wp_kses( $svg_tablet, rmp_allow_svg_html_tags() );
|
164 |
}
|
165 |
?>
|
166 |
<span class="screen-reader-text">
|
170 |
|
171 |
<button type="button" id="rmp-preview-desktop" class="rmp-preview-desktop rmp-device-preview" aria-pressed="" data-device="desktop">
|
172 |
<?php
|
173 |
+
$svg_desktop = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/desktop.svg' );
|
174 |
+
if ( $svg_desktop ) {
|
175 |
+
echo wp_kses( $svg_desktop, rmp_allow_svg_html_tags() );
|
176 |
}
|
177 |
?>
|
178 |
<span class="screen-reader-text">
|
v4.0.0/inc/classes/class-style-manager.php
CHANGED
@@ -1064,6 +1064,11 @@ class Style_Manager {
|
|
1064 |
$submenu_item_font_size = $options['submenu_font_size'];
|
1065 |
}
|
1066 |
|
|
|
|
|
|
|
|
|
|
|
1067 |
$submenu_item_font_size_unit = '';
|
1068 |
if ( ! empty( $options['submenu_font_size_unit'] ) ) {
|
1069 |
$submenu_item_font_size_unit = $options['submenu_font_size_unit'];
|
@@ -1412,6 +1417,7 @@ class Style_Manager {
|
|
1412 |
'submenu_current_item_border_color_hover' => $submenu_current_item_border_color_hover,
|
1413 |
|
1414 |
'submenu_item_font_size' => $submenu_item_font_size,
|
|
|
1415 |
'submenu_item_font_size_unit' => $submenu_item_font_size_unit,
|
1416 |
'submenu_item_font_family' => $submenu_item_font_family,
|
1417 |
'submenu_item_text_alignment' => $submenu_item_text_alignment,
|
1064 |
$submenu_item_font_size = $options['submenu_font_size'];
|
1065 |
}
|
1066 |
|
1067 |
+
$submenu_font_weight = '';
|
1068 |
+
if ( ! empty( $options['submenu_font_weight'] ) ) {
|
1069 |
+
$submenu_font_weight = $options['submenu_font_weight'];
|
1070 |
+
}
|
1071 |
+
|
1072 |
$submenu_item_font_size_unit = '';
|
1073 |
if ( ! empty( $options['submenu_font_size_unit'] ) ) {
|
1074 |
$submenu_item_font_size_unit = $options['submenu_font_size_unit'];
|
1417 |
'submenu_current_item_border_color_hover' => $submenu_current_item_border_color_hover,
|
1418 |
|
1419 |
'submenu_item_font_size' => $submenu_item_font_size,
|
1420 |
+
'submenu_font_weight' => $submenu_font_weight,
|
1421 |
'submenu_item_font_size_unit' => $submenu_item_font_size_unit,
|
1422 |
'submenu_item_font_family' => $submenu_item_font_family,
|
1423 |
'submenu_item_text_alignment' => $submenu_item_text_alignment,
|
v4.0.0/inc/classes/class-ui-manager.php
CHANGED
@@ -162,6 +162,12 @@ class UI_Manager {
|
|
162 |
return;
|
163 |
}
|
164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
$item_class = '';
|
166 |
if ( ! empty( $tab_attr['item_class'] ) ) {
|
167 |
$item_class = $tab_attr['item_class'];
|
@@ -187,9 +193,9 @@ class UI_Manager {
|
|
187 |
?>
|
188 |
<span class="rmp-tab-item-icon">
|
189 |
<?php
|
190 |
-
$svg_icon =
|
191 |
-
if (
|
192 |
-
echo wp_kses( $svg_icon
|
193 |
}
|
194 |
?>
|
195 |
</span>
|
162 |
return;
|
163 |
}
|
164 |
|
165 |
+
global $wp_filesystem;
|
166 |
+
if ( empty( $wp_filesystem ) ) {
|
167 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
168 |
+
}
|
169 |
+
WP_Filesystem();
|
170 |
+
|
171 |
$item_class = '';
|
172 |
if ( ! empty( $tab_attr['item_class'] ) ) {
|
173 |
$item_class = $tab_attr['item_class'];
|
193 |
?>
|
194 |
<span class="rmp-tab-item-icon">
|
195 |
<?php
|
196 |
+
$svg_icon = $wp_filesystem->get_contents( $tab_attr['item_header']['item_svg_icon'] );
|
197 |
+
if ( $svg_icon ) {
|
198 |
+
echo wp_kses( $svg_icon, rmp_allow_svg_html_tags() );
|
199 |
}
|
200 |
?>
|
201 |
</span>
|
v4.0.0/libs/scssphp/composer.json
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
{
|
2 |
"require": {
|
3 |
-
"scssphp/scssphp": "^1.
|
4 |
}
|
5 |
}
|
1 |
{
|
2 |
"require": {
|
3 |
+
"scssphp/scssphp": "^1.10.2"
|
4 |
}
|
5 |
}
|
v4.0.0/libs/scssphp/composer.lock
CHANGED
@@ -4,20 +4,20 @@
|
|
4 |
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
5 |
"This file is @generated automatically"
|
6 |
],
|
7 |
-
"content-hash": "
|
8 |
"packages": [
|
9 |
{
|
10 |
"name": "scssphp/scssphp",
|
11 |
-
"version": "
|
12 |
"source": {
|
13 |
"type": "git",
|
14 |
"url": "https://github.com/scssphp/scssphp.git",
|
15 |
-
"reference": "
|
16 |
},
|
17 |
"dist": {
|
18 |
"type": "zip",
|
19 |
-
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/
|
20 |
-
"reference": "
|
21 |
"shasum": ""
|
22 |
},
|
23 |
"require": {
|
@@ -26,11 +26,20 @@
|
|
26 |
"php": ">=5.6.0"
|
27 |
},
|
28 |
"require-dev": {
|
29 |
-
"
|
|
|
|
|
30 |
"squizlabs/php_codesniffer": "~3.5",
|
31 |
-
"
|
|
|
|
|
|
|
32 |
"zurb/foundation": "~6.5"
|
33 |
},
|
|
|
|
|
|
|
|
|
34 |
"bin": [
|
35 |
"bin/pscss"
|
36 |
],
|
@@ -65,7 +74,11 @@
|
|
65 |
"scss",
|
66 |
"stylesheet"
|
67 |
],
|
68 |
-
"
|
|
|
|
|
|
|
|
|
69 |
}
|
70 |
],
|
71 |
"packages-dev": [],
|
@@ -75,5 +88,6 @@
|
|
75 |
"prefer-stable": false,
|
76 |
"prefer-lowest": false,
|
77 |
"platform": [],
|
78 |
-
"platform-dev": []
|
|
|
79 |
}
|
4 |
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
5 |
"This file is @generated automatically"
|
6 |
],
|
7 |
+
"content-hash": "1ea5523c517849d7d79d7278b95713b7",
|
8 |
"packages": [
|
9 |
{
|
10 |
"name": "scssphp/scssphp",
|
11 |
+
"version": "v1.10.2",
|
12 |
"source": {
|
13 |
"type": "git",
|
14 |
"url": "https://github.com/scssphp/scssphp.git",
|
15 |
+
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46"
|
16 |
},
|
17 |
"dist": {
|
18 |
"type": "zip",
|
19 |
+
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/387f4f4abf5d99f16be16314c5ab856f81c82f46",
|
20 |
+
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46",
|
21 |
"shasum": ""
|
22 |
},
|
23 |
"require": {
|
26 |
"php": ">=5.6.0"
|
27 |
},
|
28 |
"require-dev": {
|
29 |
+
"bamarni/composer-bin-plugin": "^1.4",
|
30 |
+
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
|
31 |
+
"sass/sass-spec": "*",
|
32 |
"squizlabs/php_codesniffer": "~3.5",
|
33 |
+
"symfony/phpunit-bridge": "^5.1",
|
34 |
+
"thoughtbot/bourbon": "^7.0",
|
35 |
+
"twbs/bootstrap": "~5.0",
|
36 |
+
"twbs/bootstrap4": "4.6.1",
|
37 |
"zurb/foundation": "~6.5"
|
38 |
},
|
39 |
+
"suggest": {
|
40 |
+
"ext-iconv": "Can be used as fallback when ext-mbstring is not available",
|
41 |
+
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
|
42 |
+
},
|
43 |
"bin": [
|
44 |
"bin/pscss"
|
45 |
],
|
74 |
"scss",
|
75 |
"stylesheet"
|
76 |
],
|
77 |
+
"support": {
|
78 |
+
"issues": "https://github.com/scssphp/scssphp/issues",
|
79 |
+
"source": "https://github.com/scssphp/scssphp/tree/v1.10.2"
|
80 |
+
},
|
81 |
+
"time": "2022-03-02T21:15:09+00:00"
|
82 |
}
|
83 |
],
|
84 |
"packages-dev": [],
|
88 |
"prefer-stable": false,
|
89 |
"prefer-lowest": false,
|
90 |
"platform": [],
|
91 |
+
"platform-dev": [],
|
92 |
+
"plugin-api-version": "2.1.0"
|
93 |
}
|
v4.0.0/libs/scssphp/vendor/bin/pscss
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
#!/usr/bin/env sh
|
2 |
-
|
3 |
-
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../scssphp/scssphp/bin" && pwd)
|
4 |
-
|
5 |
-
if [ -d /proc/cygdrive ]; then
|
6 |
-
case $(which php) in
|
7 |
-
$(readlink -n /proc/cygdrive)/*)
|
8 |
-
# We are in Cygwin using Windows php, so the path must be translated
|
9 |
-
dir=$(cygpath -m "$dir");
|
10 |
-
;;
|
11 |
-
esac
|
12 |
-
fi
|
13 |
-
|
14 |
-
"${dir}/pscss" "$@"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
v4.0.0/libs/scssphp/vendor/composer/ClassLoader.php
CHANGED
@@ -37,11 +37,13 @@ namespace Composer\Autoload;
|
|
37 |
*
|
38 |
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
-
* @see
|
41 |
-
* @see
|
42 |
*/
|
43 |
class ClassLoader
|
44 |
{
|
|
|
|
|
45 |
// PSR-4
|
46 |
private $prefixLengthsPsr4 = array();
|
47 |
private $prefixDirsPsr4 = array();
|
@@ -57,10 +59,17 @@ class ClassLoader
|
|
57 |
private $missingClasses = array();
|
58 |
private $apcuPrefix;
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
public function getPrefixes()
|
61 |
{
|
62 |
if (!empty($this->prefixesPsr0)) {
|
63 |
-
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
64 |
}
|
65 |
|
66 |
return array();
|
@@ -300,6 +309,17 @@ class ClassLoader
|
|
300 |
public function register($prepend = false)
|
301 |
{
|
302 |
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
}
|
304 |
|
305 |
/**
|
@@ -308,13 +328,17 @@ class ClassLoader
|
|
308 |
public function unregister()
|
309 |
{
|
310 |
spl_autoload_unregister(array($this, 'loadClass'));
|
|
|
|
|
|
|
|
|
311 |
}
|
312 |
|
313 |
/**
|
314 |
* Loads the given class or interface.
|
315 |
*
|
316 |
* @param string $class The name of the class
|
317 |
-
* @return
|
318 |
*/
|
319 |
public function loadClass($class)
|
320 |
{
|
@@ -323,6 +347,8 @@ class ClassLoader
|
|
323 |
|
324 |
return true;
|
325 |
}
|
|
|
|
|
326 |
}
|
327 |
|
328 |
/**
|
@@ -367,6 +393,16 @@ class ClassLoader
|
|
367 |
return $file;
|
368 |
}
|
369 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
private function findFileWithExtension($class, $ext)
|
371 |
{
|
372 |
// PSR-4 lookup
|
37 |
*
|
38 |
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see https://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see https://www.php-fig.org/psr/psr-4/
|
42 |
*/
|
43 |
class ClassLoader
|
44 |
{
|
45 |
+
private $vendorDir;
|
46 |
+
|
47 |
// PSR-4
|
48 |
private $prefixLengthsPsr4 = array();
|
49 |
private $prefixDirsPsr4 = array();
|
59 |
private $missingClasses = array();
|
60 |
private $apcuPrefix;
|
61 |
|
62 |
+
private static $registeredLoaders = array();
|
63 |
+
|
64 |
+
public function __construct($vendorDir = null)
|
65 |
+
{
|
66 |
+
$this->vendorDir = $vendorDir;
|
67 |
+
}
|
68 |
+
|
69 |
public function getPrefixes()
|
70 |
{
|
71 |
if (!empty($this->prefixesPsr0)) {
|
72 |
+
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
73 |
}
|
74 |
|
75 |
return array();
|
309 |
public function register($prepend = false)
|
310 |
{
|
311 |
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
312 |
+
|
313 |
+
if (null === $this->vendorDir) {
|
314 |
+
return;
|
315 |
+
}
|
316 |
+
|
317 |
+
if ($prepend) {
|
318 |
+
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
319 |
+
} else {
|
320 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
321 |
+
self::$registeredLoaders[$this->vendorDir] = $this;
|
322 |
+
}
|
323 |
}
|
324 |
|
325 |
/**
|
328 |
public function unregister()
|
329 |
{
|
330 |
spl_autoload_unregister(array($this, 'loadClass'));
|
331 |
+
|
332 |
+
if (null !== $this->vendorDir) {
|
333 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
334 |
+
}
|
335 |
}
|
336 |
|
337 |
/**
|
338 |
* Loads the given class or interface.
|
339 |
*
|
340 |
* @param string $class The name of the class
|
341 |
+
* @return true|null True if loaded, null otherwise
|
342 |
*/
|
343 |
public function loadClass($class)
|
344 |
{
|
347 |
|
348 |
return true;
|
349 |
}
|
350 |
+
|
351 |
+
return null;
|
352 |
}
|
353 |
|
354 |
/**
|
393 |
return $file;
|
394 |
}
|
395 |
|
396 |
+
/**
|
397 |
+
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
398 |
+
*
|
399 |
+
* @return self[]
|
400 |
+
*/
|
401 |
+
public static function getRegisteredLoaders()
|
402 |
+
{
|
403 |
+
return self::$registeredLoaders;
|
404 |
+
}
|
405 |
+
|
406 |
private function findFileWithExtension($class, $ext)
|
407 |
{
|
408 |
// PSR-4 lookup
|
v4.0.0/libs/scssphp/vendor/composer/InstalledVersions.php
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer;
|
14 |
+
|
15 |
+
use Composer\Autoload\ClassLoader;
|
16 |
+
use Composer\Semver\VersionParser;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* This class is copied in every Composer installed project and available to all
|
20 |
+
*
|
21 |
+
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
22 |
+
*
|
23 |
+
* To require it's presence, you can require `composer-runtime-api ^2.0`
|
24 |
+
*/
|
25 |
+
class InstalledVersions
|
26 |
+
{
|
27 |
+
private static $installed;
|
28 |
+
private static $canGetVendors;
|
29 |
+
private static $installedByVendor = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
33 |
+
*
|
34 |
+
* @return string[]
|
35 |
+
* @psalm-return list<string>
|
36 |
+
*/
|
37 |
+
public static function getInstalledPackages()
|
38 |
+
{
|
39 |
+
$packages = array();
|
40 |
+
foreach (self::getInstalled() as $installed) {
|
41 |
+
$packages[] = array_keys($installed['versions']);
|
42 |
+
}
|
43 |
+
|
44 |
+
if (1 === \count($packages)) {
|
45 |
+
return $packages[0];
|
46 |
+
}
|
47 |
+
|
48 |
+
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Returns a list of all package names with a specific type e.g. 'library'
|
53 |
+
*
|
54 |
+
* @param string $type
|
55 |
+
* @return string[]
|
56 |
+
* @psalm-return list<string>
|
57 |
+
*/
|
58 |
+
public static function getInstalledPackagesByType($type)
|
59 |
+
{
|
60 |
+
$packagesByType = array();
|
61 |
+
|
62 |
+
foreach (self::getInstalled() as $installed) {
|
63 |
+
foreach ($installed['versions'] as $name => $package) {
|
64 |
+
if (isset($package['type']) && $package['type'] === $type) {
|
65 |
+
$packagesByType[] = $name;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
return $packagesByType;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Checks whether the given package is installed
|
75 |
+
*
|
76 |
+
* This also returns true if the package name is provided or replaced by another package
|
77 |
+
*
|
78 |
+
* @param string $packageName
|
79 |
+
* @param bool $includeDevRequirements
|
80 |
+
* @return bool
|
81 |
+
*/
|
82 |
+
public static function isInstalled($packageName, $includeDevRequirements = true)
|
83 |
+
{
|
84 |
+
foreach (self::getInstalled() as $installed) {
|
85 |
+
if (isset($installed['versions'][$packageName])) {
|
86 |
+
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
return false;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Checks whether the given package satisfies a version constraint
|
95 |
+
*
|
96 |
+
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
97 |
+
*
|
98 |
+
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
99 |
+
*
|
100 |
+
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
101 |
+
* @param string $packageName
|
102 |
+
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
103 |
+
* @return bool
|
104 |
+
*/
|
105 |
+
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
106 |
+
{
|
107 |
+
$constraint = $parser->parseConstraints($constraint);
|
108 |
+
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
109 |
+
|
110 |
+
return $provided->matches($constraint);
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Returns a version constraint representing all the range(s) which are installed for a given package
|
115 |
+
*
|
116 |
+
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
117 |
+
* whether a given version of a package is installed, and not just whether it exists
|
118 |
+
*
|
119 |
+
* @param string $packageName
|
120 |
+
* @return string Version constraint usable with composer/semver
|
121 |
+
*/
|
122 |
+
public static function getVersionRanges($packageName)
|
123 |
+
{
|
124 |
+
foreach (self::getInstalled() as $installed) {
|
125 |
+
if (!isset($installed['versions'][$packageName])) {
|
126 |
+
continue;
|
127 |
+
}
|
128 |
+
|
129 |
+
$ranges = array();
|
130 |
+
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
131 |
+
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
132 |
+
}
|
133 |
+
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
134 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
135 |
+
}
|
136 |
+
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
137 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
138 |
+
}
|
139 |
+
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
140 |
+
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
141 |
+
}
|
142 |
+
|
143 |
+
return implode(' || ', $ranges);
|
144 |
+
}
|
145 |
+
|
146 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $packageName
|
151 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
152 |
+
*/
|
153 |
+
public static function getVersion($packageName)
|
154 |
+
{
|
155 |
+
foreach (self::getInstalled() as $installed) {
|
156 |
+
if (!isset($installed['versions'][$packageName])) {
|
157 |
+
continue;
|
158 |
+
}
|
159 |
+
|
160 |
+
if (!isset($installed['versions'][$packageName]['version'])) {
|
161 |
+
return null;
|
162 |
+
}
|
163 |
+
|
164 |
+
return $installed['versions'][$packageName]['version'];
|
165 |
+
}
|
166 |
+
|
167 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @param string $packageName
|
172 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
173 |
+
*/
|
174 |
+
public static function getPrettyVersion($packageName)
|
175 |
+
{
|
176 |
+
foreach (self::getInstalled() as $installed) {
|
177 |
+
if (!isset($installed['versions'][$packageName])) {
|
178 |
+
continue;
|
179 |
+
}
|
180 |
+
|
181 |
+
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
182 |
+
return null;
|
183 |
+
}
|
184 |
+
|
185 |
+
return $installed['versions'][$packageName]['pretty_version'];
|
186 |
+
}
|
187 |
+
|
188 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @param string $packageName
|
193 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
194 |
+
*/
|
195 |
+
public static function getReference($packageName)
|
196 |
+
{
|
197 |
+
foreach (self::getInstalled() as $installed) {
|
198 |
+
if (!isset($installed['versions'][$packageName])) {
|
199 |
+
continue;
|
200 |
+
}
|
201 |
+
|
202 |
+
if (!isset($installed['versions'][$packageName]['reference'])) {
|
203 |
+
return null;
|
204 |
+
}
|
205 |
+
|
206 |
+
return $installed['versions'][$packageName]['reference'];
|
207 |
+
}
|
208 |
+
|
209 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @param string $packageName
|
214 |
+
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
215 |
+
*/
|
216 |
+
public static function getInstallPath($packageName)
|
217 |
+
{
|
218 |
+
foreach (self::getInstalled() as $installed) {
|
219 |
+
if (!isset($installed['versions'][$packageName])) {
|
220 |
+
continue;
|
221 |
+
}
|
222 |
+
|
223 |
+
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
224 |
+
}
|
225 |
+
|
226 |
+
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @return array
|
231 |
+
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
|
232 |
+
*/
|
233 |
+
public static function getRootPackage()
|
234 |
+
{
|
235 |
+
$installed = self::getInstalled();
|
236 |
+
|
237 |
+
return $installed[0]['root'];
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Returns the raw installed.php data for custom implementations
|
242 |
+
*
|
243 |
+
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
244 |
+
* @return array[]
|
245 |
+
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
|
246 |
+
*/
|
247 |
+
public static function getRawData()
|
248 |
+
{
|
249 |
+
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
250 |
+
|
251 |
+
if (null === self::$installed) {
|
252 |
+
// only require the installed.php file if this file is loaded from its dumped location,
|
253 |
+
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
254 |
+
if (substr(__DIR__, -8, 1) !== 'C') {
|
255 |
+
self::$installed = include __DIR__ . '/installed.php';
|
256 |
+
} else {
|
257 |
+
self::$installed = array();
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
return self::$installed;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
266 |
+
*
|
267 |
+
* @return array[]
|
268 |
+
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
|
269 |
+
*/
|
270 |
+
public static function getAllRawData()
|
271 |
+
{
|
272 |
+
return self::getInstalled();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Lets you reload the static array from another file
|
277 |
+
*
|
278 |
+
* This is only useful for complex integrations in which a project needs to use
|
279 |
+
* this class but then also needs to execute another project's autoloader in process,
|
280 |
+
* and wants to ensure both projects have access to their version of installed.php.
|
281 |
+
*
|
282 |
+
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
283 |
+
* the data it needs from this class, then call reload() with
|
284 |
+
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
285 |
+
* the project in which it runs can then also use this class safely, without
|
286 |
+
* interference between PHPUnit's dependencies and the project's dependencies.
|
287 |
+
*
|
288 |
+
* @param array[] $data A vendor/composer/installed.php data set
|
289 |
+
* @return void
|
290 |
+
*
|
291 |
+
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
|
292 |
+
*/
|
293 |
+
public static function reload($data)
|
294 |
+
{
|
295 |
+
self::$installed = $data;
|
296 |
+
self::$installedByVendor = array();
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @return array[]
|
301 |
+
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
|
302 |
+
*/
|
303 |
+
private static function getInstalled()
|
304 |
+
{
|
305 |
+
if (null === self::$canGetVendors) {
|
306 |
+
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
307 |
+
}
|
308 |
+
|
309 |
+
$installed = array();
|
310 |
+
|
311 |
+
if (self::$canGetVendors) {
|
312 |
+
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
313 |
+
if (isset(self::$installedByVendor[$vendorDir])) {
|
314 |
+
$installed[] = self::$installedByVendor[$vendorDir];
|
315 |
+
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
316 |
+
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
317 |
+
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
318 |
+
self::$installed = $installed[count($installed) - 1];
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
if (null === self::$installed) {
|
325 |
+
// only require the installed.php file if this file is loaded from its dumped location,
|
326 |
+
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
327 |
+
if (substr(__DIR__, -8, 1) !== 'C') {
|
328 |
+
self::$installed = require __DIR__ . '/installed.php';
|
329 |
+
} else {
|
330 |
+
self::$installed = array();
|
331 |
+
}
|
332 |
+
}
|
333 |
+
$installed[] = self::$installed;
|
334 |
+
|
335 |
+
return $installed;
|
336 |
+
}
|
337 |
+
}
|
v4.0.0/libs/scssphp/vendor/composer/autoload_classmap.php
CHANGED
@@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
|
|
6 |
$baseDir = dirname($vendorDir);
|
7 |
|
8 |
return array(
|
|
|
9 |
);
|
6 |
$baseDir = dirname($vendorDir);
|
7 |
|
8 |
return array(
|
9 |
+
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
10 |
);
|
v4.0.0/libs/scssphp/vendor/composer/autoload_real.php
CHANGED
@@ -13,19 +13,24 @@ class ComposerAutoloaderInit63d42ceb3ded2b5ffe0e0f4c4066ef1c
|
|
13 |
}
|
14 |
}
|
15 |
|
|
|
|
|
|
|
16 |
public static function getLoader()
|
17 |
{
|
18 |
if (null !== self::$loader) {
|
19 |
return self::$loader;
|
20 |
}
|
21 |
|
|
|
|
|
22 |
spl_autoload_register(array('ComposerAutoloaderInit63d42ceb3ded2b5ffe0e0f4c4066ef1c', 'loadClassLoader'), true, true);
|
23 |
-
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
spl_autoload_unregister(array('ComposerAutoloaderInit63d42ceb3ded2b5ffe0e0f4c4066ef1c', 'loadClassLoader'));
|
25 |
|
26 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
if ($useStaticLoader) {
|
28 |
-
|
29 |
|
30 |
call_user_func(\Composer\Autoload\ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::getInitializer($loader));
|
31 |
} else {
|
13 |
}
|
14 |
}
|
15 |
|
16 |
+
/**
|
17 |
+
* @return \Composer\Autoload\ClassLoader
|
18 |
+
*/
|
19 |
public static function getLoader()
|
20 |
{
|
21 |
if (null !== self::$loader) {
|
22 |
return self::$loader;
|
23 |
}
|
24 |
|
25 |
+
require __DIR__ . '/platform_check.php';
|
26 |
+
|
27 |
spl_autoload_register(array('ComposerAutoloaderInit63d42ceb3ded2b5ffe0e0f4c4066ef1c', 'loadClassLoader'), true, true);
|
28 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
29 |
spl_autoload_unregister(array('ComposerAutoloaderInit63d42ceb3ded2b5ffe0e0f4c4066ef1c', 'loadClassLoader'));
|
30 |
|
31 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
32 |
if ($useStaticLoader) {
|
33 |
+
require __DIR__ . '/autoload_static.php';
|
34 |
|
35 |
call_user_func(\Composer\Autoload\ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::getInitializer($loader));
|
36 |
} else {
|
v4.0.0/libs/scssphp/vendor/composer/autoload_static.php
CHANGED
@@ -20,11 +20,16 @@ class ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c
|
|
20 |
),
|
21 |
);
|
22 |
|
|
|
|
|
|
|
|
|
23 |
public static function getInitializer(ClassLoader $loader)
|
24 |
{
|
25 |
return \Closure::bind(function () use ($loader) {
|
26 |
$loader->prefixLengthsPsr4 = ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::$prefixLengthsPsr4;
|
27 |
$loader->prefixDirsPsr4 = ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::$prefixDirsPsr4;
|
|
|
28 |
|
29 |
}, null, ClassLoader::class);
|
30 |
}
|
20 |
),
|
21 |
);
|
22 |
|
23 |
+
public static $classMap = array (
|
24 |
+
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
25 |
+
);
|
26 |
+
|
27 |
public static function getInitializer(ClassLoader $loader)
|
28 |
{
|
29 |
return \Closure::bind(function () use ($loader) {
|
30 |
$loader->prefixLengthsPsr4 = ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::$prefixLengthsPsr4;
|
31 |
$loader->prefixDirsPsr4 = ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::$prefixDirsPsr4;
|
32 |
+
$loader->classMap = ComposerStaticInit63d42ceb3ded2b5ffe0e0f4c4066ef1c::$classMap;
|
33 |
|
34 |
}, null, ClassLoader::class);
|
35 |
}
|
v4.0.0/libs/scssphp/vendor/composer/installed.json
CHANGED
@@ -1,65 +1,83 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
"
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
"dist": {
|
12 |
-
"type": "zip",
|
13 |
-
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/4363ddce8d750f055c436833dd77d83517946532",
|
14 |
-
"reference": "4363ddce8d750f055c436833dd77d83517946532",
|
15 |
-
"shasum": ""
|
16 |
-
},
|
17 |
-
"require": {
|
18 |
-
"ext-ctype": "*",
|
19 |
-
"ext-json": "*",
|
20 |
-
"php": ">=5.6.0"
|
21 |
-
},
|
22 |
-
"require-dev": {
|
23 |
-
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
|
24 |
-
"squizlabs/php_codesniffer": "~3.5",
|
25 |
-
"twbs/bootstrap": "~4.3",
|
26 |
-
"zurb/foundation": "~6.5"
|
27 |
-
},
|
28 |
-
"time": "2020-04-21T15:53:32+00:00",
|
29 |
-
"bin": [
|
30 |
-
"bin/pscss"
|
31 |
-
],
|
32 |
-
"type": "library",
|
33 |
-
"installation-source": "dist",
|
34 |
-
"autoload": {
|
35 |
-
"psr-4": {
|
36 |
-
"ScssPhp\\ScssPhp\\": "src/"
|
37 |
-
}
|
38 |
-
},
|
39 |
-
"notification-url": "https://packagist.org/downloads/",
|
40 |
-
"license": [
|
41 |
-
"MIT"
|
42 |
-
],
|
43 |
-
"authors": [
|
44 |
-
{
|
45 |
-
"name": "Anthon Pang",
|
46 |
-
"email": "apang@softwaredevelopment.ca",
|
47 |
-
"homepage": "https://github.com/robocoder"
|
48 |
},
|
49 |
-
{
|
50 |
-
"
|
51 |
-
"
|
52 |
-
"
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
"
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"packages": [
|
3 |
+
{
|
4 |
+
"name": "scssphp/scssphp",
|
5 |
+
"version": "v1.10.2",
|
6 |
+
"version_normalized": "1.10.2.0",
|
7 |
+
"source": {
|
8 |
+
"type": "git",
|
9 |
+
"url": "https://github.com/scssphp/scssphp.git",
|
10 |
+
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
},
|
12 |
+
"dist": {
|
13 |
+
"type": "zip",
|
14 |
+
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/387f4f4abf5d99f16be16314c5ab856f81c82f46",
|
15 |
+
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46",
|
16 |
+
"shasum": ""
|
17 |
+
},
|
18 |
+
"require": {
|
19 |
+
"ext-ctype": "*",
|
20 |
+
"ext-json": "*",
|
21 |
+
"php": ">=5.6.0"
|
22 |
+
},
|
23 |
+
"require-dev": {
|
24 |
+
"bamarni/composer-bin-plugin": "^1.4",
|
25 |
+
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
|
26 |
+
"sass/sass-spec": "*",
|
27 |
+
"squizlabs/php_codesniffer": "~3.5",
|
28 |
+
"symfony/phpunit-bridge": "^5.1",
|
29 |
+
"thoughtbot/bourbon": "^7.0",
|
30 |
+
"twbs/bootstrap": "~5.0",
|
31 |
+
"twbs/bootstrap4": "4.6.1",
|
32 |
+
"zurb/foundation": "~6.5"
|
33 |
+
},
|
34 |
+
"suggest": {
|
35 |
+
"ext-iconv": "Can be used as fallback when ext-mbstring is not available",
|
36 |
+
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
|
37 |
+
},
|
38 |
+
"time": "2022-03-02T21:15:09+00:00",
|
39 |
+
"bin": [
|
40 |
+
"bin/pscss"
|
41 |
+
],
|
42 |
+
"type": "library",
|
43 |
+
"installation-source": "dist",
|
44 |
+
"autoload": {
|
45 |
+
"psr-4": {
|
46 |
+
"ScssPhp\\ScssPhp\\": "src/"
|
47 |
+
}
|
48 |
+
},
|
49 |
+
"notification-url": "https://packagist.org/downloads/",
|
50 |
+
"license": [
|
51 |
+
"MIT"
|
52 |
+
],
|
53 |
+
"authors": [
|
54 |
+
{
|
55 |
+
"name": "Anthon Pang",
|
56 |
+
"email": "apang@softwaredevelopment.ca",
|
57 |
+
"homepage": "https://github.com/robocoder"
|
58 |
+
},
|
59 |
+
{
|
60 |
+
"name": "Cédric Morin",
|
61 |
+
"email": "cedric@yterium.com",
|
62 |
+
"homepage": "https://github.com/Cerdic"
|
63 |
+
}
|
64 |
+
],
|
65 |
+
"description": "scssphp is a compiler for SCSS written in PHP.",
|
66 |
+
"homepage": "http://scssphp.github.io/scssphp/",
|
67 |
+
"keywords": [
|
68 |
+
"css",
|
69 |
+
"less",
|
70 |
+
"sass",
|
71 |
+
"scss",
|
72 |
+
"stylesheet"
|
73 |
+
],
|
74 |
+
"support": {
|
75 |
+
"issues": "https://github.com/scssphp/scssphp/issues",
|
76 |
+
"source": "https://github.com/scssphp/scssphp/tree/v1.10.2"
|
77 |
+
},
|
78 |
+
"install-path": "../scssphp/scssphp"
|
79 |
+
}
|
80 |
+
],
|
81 |
+
"dev": true,
|
82 |
+
"dev-package-names": []
|
83 |
+
}
|
v4.0.0/libs/scssphp/vendor/composer/installed.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php return array(
|
2 |
+
'root' => array(
|
3 |
+
'pretty_version' => 'dev-master',
|
4 |
+
'version' => 'dev-master',
|
5 |
+
'type' => 'library',
|
6 |
+
'install_path' => __DIR__ . '/../../',
|
7 |
+
'aliases' => array(),
|
8 |
+
'reference' => '93878f5bdc00aaa000cdaab9278b8b7b2eb5f483',
|
9 |
+
'name' => '__root__',
|
10 |
+
'dev' => true,
|
11 |
+
),
|
12 |
+
'versions' => array(
|
13 |
+
'__root__' => array(
|
14 |
+
'pretty_version' => 'dev-master',
|
15 |
+
'version' => 'dev-master',
|
16 |
+
'type' => 'library',
|
17 |
+
'install_path' => __DIR__ . '/../../',
|
18 |
+
'aliases' => array(),
|
19 |
+
'reference' => '93878f5bdc00aaa000cdaab9278b8b7b2eb5f483',
|
20 |
+
'dev_requirement' => false,
|
21 |
+
),
|
22 |
+
'scssphp/scssphp' => array(
|
23 |
+
'pretty_version' => 'v1.10.2',
|
24 |
+
'version' => '1.10.2.0',
|
25 |
+
'type' => 'library',
|
26 |
+
'install_path' => __DIR__ . '/../scssphp/scssphp',
|
27 |
+
'aliases' => array(),
|
28 |
+
'reference' => '387f4f4abf5d99f16be16314c5ab856f81c82f46',
|
29 |
+
'dev_requirement' => false,
|
30 |
+
),
|
31 |
+
),
|
32 |
+
);
|
v4.0.0/libs/scssphp/vendor/composer/platform_check.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// platform_check.php @generated by Composer
|
4 |
+
|
5 |
+
$issues = array();
|
6 |
+
|
7 |
+
if (!(PHP_VERSION_ID >= 50600)) {
|
8 |
+
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
|
9 |
+
}
|
10 |
+
|
11 |
+
if ($issues) {
|
12 |
+
if (!headers_sent()) {
|
13 |
+
header('HTTP/1.1 500 Internal Server Error');
|
14 |
+
}
|
15 |
+
if (!ini_get('display_errors')) {
|
16 |
+
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
17 |
+
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
18 |
+
} elseif (!headers_sent()) {
|
19 |
+
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
trigger_error(
|
23 |
+
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
24 |
+
E_USER_ERROR
|
25 |
+
);
|
26 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
# scssphp
|
2 |
-
### <
|
3 |
|
4 |
-
|
5 |
[![License](https://poser.pugx.org/scssphp/scssphp/license)](https://packagist.org/packages/scssphp/scssphp)
|
6 |
|
7 |
`scssphp` is a compiler for SCSS written in PHP.
|
8 |
|
9 |
-
Checkout the homepage, <
|
10 |
|
11 |
## Running Tests
|
12 |
|
@@ -23,7 +23,7 @@ There are several tests in the `tests/` directory:
|
|
23 |
* `FailingTest.php` contains tests reported in Github issues that demonstrate compatibility bugs.
|
24 |
* `InputTest.php` compiles every `.scss` file in the `tests/inputs` directory
|
25 |
then compares to the respective `.css` file in the `tests/outputs` directory.
|
26 |
-
* `
|
27 |
|
28 |
When changing any of the tests in `tests/inputs`, the tests will most likely
|
29 |
fail because the output has changed. Once you verify that the output is correct
|
@@ -31,16 +31,41 @@ you can run the following command to rebuild all the tests:
|
|
31 |
|
32 |
BUILD=1 vendor/bin/phpunit tests
|
33 |
|
34 |
-
This will compile all the tests, and save results into `tests/outputs`.
|
|
|
35 |
|
36 |
-
To enable the `
|
37 |
|
38 |
-
|
39 |
|
40 |
## Coding Standard
|
41 |
|
42 |
-
`scssphp` source conforms to [
|
43 |
|
44 |
Run the following command from the root directory to check the code for "sniffs".
|
45 |
|
46 |
-
vendor/bin/phpcs --standard=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# scssphp
|
2 |
+
### <https://scssphp.github.io/scssphp>
|
3 |
|
4 |
+
![Build](https://github.com/scssphp/scssphp/workflows/CI/badge.svg)
|
5 |
[![License](https://poser.pugx.org/scssphp/scssphp/license)](https://packagist.org/packages/scssphp/scssphp)
|
6 |
|
7 |
`scssphp` is a compiler for SCSS written in PHP.
|
8 |
|
9 |
+
Checkout the homepage, <https://scssphp.github.io/scssphp>, for directions on how to use.
|
10 |
|
11 |
## Running Tests
|
12 |
|
23 |
* `FailingTest.php` contains tests reported in Github issues that demonstrate compatibility bugs.
|
24 |
* `InputTest.php` compiles every `.scss` file in the `tests/inputs` directory
|
25 |
then compares to the respective `.css` file in the `tests/outputs` directory.
|
26 |
+
* `SassSpecTest.php` extracts tests from the `sass/sass-spec` repository.
|
27 |
|
28 |
When changing any of the tests in `tests/inputs`, the tests will most likely
|
29 |
fail because the output has changed. Once you verify that the output is correct
|
31 |
|
32 |
BUILD=1 vendor/bin/phpunit tests
|
33 |
|
34 |
+
This will compile all the tests, and save results into `tests/outputs`. It also
|
35 |
+
updates the list of excluded specs from sass-spec.
|
36 |
|
37 |
+
To enable the full `sass-spec` compatibility tests:
|
38 |
|
39 |
+
TEST_SASS_SPEC=1 vendor/bin/phpunit tests
|
40 |
|
41 |
## Coding Standard
|
42 |
|
43 |
+
`scssphp` source conforms to [PSR12](https://www.php-fig.org/psr/psr-12/).
|
44 |
|
45 |
Run the following command from the root directory to check the code for "sniffs".
|
46 |
|
47 |
+
vendor/bin/phpcs --standard=PSR12 --extensions=php bin src tests *.php
|
48 |
+
|
49 |
+
## Static Analysis
|
50 |
+
|
51 |
+
`scssphp` uses [phpstan](https://phpstan.org/) for static analysis.
|
52 |
+
|
53 |
+
Run the following command from the root directory to analyse the codebase:
|
54 |
+
|
55 |
+
make phpstan
|
56 |
+
|
57 |
+
As most of the codebase is composed of legacy code which cannot be type-checked
|
58 |
+
fully, the setup contains a baseline file with all errors we want to ignore. In
|
59 |
+
particular, we ignore all errors related to not specifying the types inside arrays
|
60 |
+
when these arrays correspond to the representation of Sass values and Sass AST nodes
|
61 |
+
in the parser and compiler.
|
62 |
+
When contributing, the proper process to deal with static analysis is the following:
|
63 |
+
|
64 |
+
1. Make your change in the codebase
|
65 |
+
2. Run `make phpstan`
|
66 |
+
3. Fix errors reported by phpstan when possible
|
67 |
+
4. Repeat step 2 and 3 until nothing gets fixed anymore at step 3
|
68 |
+
5. Run `make phpstan-baseline` to regenerate the phpstan baseline
|
69 |
+
|
70 |
+
Additions to the baseline will be reviewed to avoid ignoring errors that should have
|
71 |
+
been fixed.
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/bin/pscss
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
#!/usr/bin/env php
|
2 |
<?php
|
|
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
@@ -19,26 +20,26 @@ if (version_compare(PHP_VERSION, '5.6') < 0) {
|
|
19 |
include __DIR__ . '/../scss.inc.php';
|
20 |
|
21 |
use ScssPhp\ScssPhp\Compiler;
|
|
|
|
|
22 |
use ScssPhp\ScssPhp\Parser;
|
23 |
use ScssPhp\ScssPhp\Version;
|
24 |
|
25 |
$style = null;
|
26 |
-
$loadPaths =
|
27 |
-
$precision = null;
|
28 |
$dumpTree = false;
|
29 |
$inputFile = null;
|
30 |
$changeDir = false;
|
31 |
-
$debugInfo = false;
|
32 |
-
$lineNumbers = false;
|
33 |
-
$ignoreErrors = false;
|
34 |
$encoding = false;
|
35 |
$sourceMap = false;
|
|
|
|
|
36 |
|
37 |
/**
|
38 |
* Parse argument
|
39 |
*
|
40 |
-
* @param
|
41 |
-
* @param
|
42 |
*
|
43 |
* @return string|null
|
44 |
*/
|
@@ -61,25 +62,29 @@ function parseArgument(&$i, $options) {
|
|
61 |
}
|
62 |
}
|
63 |
|
|
|
|
|
64 |
for ($i = 1; $i < $argc; $i++) {
|
65 |
if ($argv[$i] === '-?' || $argv[$i] === '-h' || $argv[$i] === '--help') {
|
66 |
$exe = $argv[0];
|
67 |
|
68 |
$HELP = <<<EOT
|
69 |
-
Usage: $exe [options] [input-file]
|
70 |
|
71 |
Options include:
|
72 |
|
73 |
--help Show this message [-h, -?]
|
74 |
-
--continue-on-error
|
75 |
-
--debug-info
|
76 |
-
--dump-tree Dump formatted parse tree [-T]
|
77 |
--iso8859-1 Use iso8859-1 encoding instead of default utf-8
|
78 |
-
--line-numbers
|
79 |
--load-path=PATH Set import path [-I]
|
80 |
-
--precision=N
|
81 |
--sourcemap Create source map file
|
82 |
-
--
|
|
|
|
|
83 |
--version Print the version [-v]
|
84 |
|
85 |
EOT;
|
@@ -90,13 +95,15 @@ EOT;
|
|
90 |
exit(Version::VERSION . "\n");
|
91 |
}
|
92 |
|
|
|
93 |
if ($argv[$i] === '--continue-on-error') {
|
94 |
-
|
95 |
continue;
|
96 |
}
|
97 |
|
|
|
98 |
if ($argv[$i] === '-g' || $argv[$i] === '--debug-info') {
|
99 |
-
|
100 |
continue;
|
101 |
}
|
102 |
|
@@ -105,8 +112,9 @@ EOT;
|
|
105 |
continue;
|
106 |
}
|
107 |
|
|
|
108 |
if ($argv[$i] === '--line-numbers' || $argv[$i] === '--line-comments') {
|
109 |
-
|
110 |
continue;
|
111 |
}
|
112 |
|
@@ -115,6 +123,16 @@ EOT;
|
|
115 |
continue;
|
116 |
}
|
117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
if ($argv[$i] === '-T' || $argv[$i] === '--dump-tree') {
|
119 |
$dumpTree = true;
|
120 |
continue;
|
@@ -130,34 +148,25 @@ EOT;
|
|
130 |
$value = parseArgument($i, array('-I', '--load-path'));
|
131 |
|
132 |
if (isset($value)) {
|
133 |
-
$loadPaths = $value;
|
134 |
continue;
|
135 |
}
|
136 |
|
|
|
137 |
$value = parseArgument($i, array('-p', '--precision'));
|
138 |
|
139 |
if (isset($value)) {
|
140 |
-
|
141 |
continue;
|
142 |
}
|
143 |
|
144 |
-
|
145 |
-
$inputFile = $argv[$i];
|
146 |
-
continue;
|
147 |
-
}
|
148 |
}
|
149 |
|
150 |
|
151 |
-
if ($
|
|
|
152 |
$data = file_get_contents($inputFile);
|
153 |
-
|
154 |
-
$newWorkingDir = dirname(realpath($inputFile));
|
155 |
-
$oldWorkingDir = getcwd();
|
156 |
-
|
157 |
-
if ($oldWorkingDir !== $newWorkingDir) {
|
158 |
-
$changeDir = chdir($newWorkingDir);
|
159 |
-
$inputFile = basename($inputFile);
|
160 |
-
}
|
161 |
} else {
|
162 |
$data = '';
|
163 |
|
@@ -171,45 +180,65 @@ if ($dumpTree) {
|
|
171 |
|
172 |
print_r(json_decode(json_encode($parser->parse($data)), true));
|
173 |
|
|
|
|
|
174 |
exit();
|
175 |
}
|
176 |
|
177 |
$scss = new Compiler();
|
178 |
|
179 |
-
if ($debugInfo) {
|
180 |
-
$scss->setLineNumberStyle(Compiler::DEBUG_INFO);
|
181 |
-
}
|
182 |
-
|
183 |
-
if ($lineNumbers) {
|
184 |
-
$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
|
185 |
-
}
|
186 |
-
|
187 |
-
if ($ignoreErrors) {
|
188 |
-
$scss->setIgnoreErrors($ignoreErrors);
|
189 |
-
}
|
190 |
-
|
191 |
if ($loadPaths) {
|
192 |
-
$scss->setImportPaths(
|
193 |
-
}
|
194 |
-
|
195 |
-
if ($precision) {
|
196 |
-
$scss->setNumberPrecision($precision);
|
197 |
}
|
198 |
|
199 |
if ($style) {
|
200 |
-
$
|
|
|
|
|
|
|
|
|
|
|
201 |
}
|
202 |
|
|
|
|
|
|
|
203 |
if ($sourceMap) {
|
204 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
}
|
206 |
|
207 |
if ($encoding) {
|
208 |
$scss->setEncoding($encoding);
|
209 |
}
|
210 |
|
211 |
-
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
-
if ($
|
214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
}
|
1 |
#!/usr/bin/env php
|
2 |
<?php
|
3 |
+
|
4 |
/**
|
5 |
* SCSSPHP
|
6 |
*
|
20 |
include __DIR__ . '/../scss.inc.php';
|
21 |
|
22 |
use ScssPhp\ScssPhp\Compiler;
|
23 |
+
use ScssPhp\ScssPhp\Exception\SassException;
|
24 |
+
use ScssPhp\ScssPhp\OutputStyle;
|
25 |
use ScssPhp\ScssPhp\Parser;
|
26 |
use ScssPhp\ScssPhp\Version;
|
27 |
|
28 |
$style = null;
|
29 |
+
$loadPaths = [];
|
|
|
30 |
$dumpTree = false;
|
31 |
$inputFile = null;
|
32 |
$changeDir = false;
|
|
|
|
|
|
|
33 |
$encoding = false;
|
34 |
$sourceMap = false;
|
35 |
+
$embedSources = false;
|
36 |
+
$embedSourceMap = false;
|
37 |
|
38 |
/**
|
39 |
* Parse argument
|
40 |
*
|
41 |
+
* @param int $i
|
42 |
+
* @param string[] $options
|
43 |
*
|
44 |
* @return string|null
|
45 |
*/
|
62 |
}
|
63 |
}
|
64 |
|
65 |
+
$arguments = [];
|
66 |
+
|
67 |
for ($i = 1; $i < $argc; $i++) {
|
68 |
if ($argv[$i] === '-?' || $argv[$i] === '-h' || $argv[$i] === '--help') {
|
69 |
$exe = $argv[0];
|
70 |
|
71 |
$HELP = <<<EOT
|
72 |
+
Usage: $exe [options] [input-file] [output-file]
|
73 |
|
74 |
Options include:
|
75 |
|
76 |
--help Show this message [-h, -?]
|
77 |
+
--continue-on-error [deprecated] Ignored
|
78 |
+
--debug-info [deprecated] Ignored [-g]
|
79 |
+
--dump-tree [deprecated] Dump formatted parse tree [-T]
|
80 |
--iso8859-1 Use iso8859-1 encoding instead of default utf-8
|
81 |
+
--line-numbers [deprecated] Ignored [--line-comments]
|
82 |
--load-path=PATH Set import path [-I]
|
83 |
+
--precision=N [deprecated] Ignored. (default 10) [-p]
|
84 |
--sourcemap Create source map file
|
85 |
+
--embed-sources Embed source file contents in source maps
|
86 |
+
--embed-source-map Embed the source map contents in CSS (default if writing to stdout)
|
87 |
+
--style=FORMAT Set the output style (compressed or expanded) [-s, -t]
|
88 |
--version Print the version [-v]
|
89 |
|
90 |
EOT;
|
95 |
exit(Version::VERSION . "\n");
|
96 |
}
|
97 |
|
98 |
+
// Keep parsing --continue-on-error to avoid BC breaks for scripts using it
|
99 |
if ($argv[$i] === '--continue-on-error') {
|
100 |
+
// TODO report it as a warning ?
|
101 |
continue;
|
102 |
}
|
103 |
|
104 |
+
// Keep parsing it to avoid BC breaks for scripts using it
|
105 |
if ($argv[$i] === '-g' || $argv[$i] === '--debug-info') {
|
106 |
+
// TODO report it as a warning ?
|
107 |
continue;
|
108 |
}
|
109 |
|
112 |
continue;
|
113 |
}
|
114 |
|
115 |
+
// Keep parsing it to avoid BC breaks for scripts using it
|
116 |
if ($argv[$i] === '--line-numbers' || $argv[$i] === '--line-comments') {
|
117 |
+
// TODO report it as a warning ?
|
118 |
continue;
|
119 |
}
|
120 |
|
123 |
continue;
|
124 |
}
|
125 |
|
126 |
+
if ($argv[$i] === '--embed-sources') {
|
127 |
+
$embedSources = true;
|
128 |
+
continue;
|
129 |
+
}
|
130 |
+
|
131 |
+
if ($argv[$i] === '--embed-source-map') {
|
132 |
+
$embedSourceMap = true;
|
133 |
+
continue;
|
134 |
+
}
|
135 |
+
|
136 |
if ($argv[$i] === '-T' || $argv[$i] === '--dump-tree') {
|
137 |
$dumpTree = true;
|
138 |
continue;
|
148 |
$value = parseArgument($i, array('-I', '--load-path'));
|
149 |
|
150 |
if (isset($value)) {
|
151 |
+
$loadPaths[] = $value;
|
152 |
continue;
|
153 |
}
|
154 |
|
155 |
+
// Keep parsing --precision to avoid BC breaks for scripts using it
|
156 |
$value = parseArgument($i, array('-p', '--precision'));
|
157 |
|
158 |
if (isset($value)) {
|
159 |
+
// TODO report it as a warning ?
|
160 |
continue;
|
161 |
}
|
162 |
|
163 |
+
$arguments[] = $argv[$i];
|
|
|
|
|
|
|
164 |
}
|
165 |
|
166 |
|
167 |
+
if (isset($arguments[0]) && file_exists($arguments[0])) {
|
168 |
+
$inputFile = $arguments[0];
|
169 |
$data = file_get_contents($inputFile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
} else {
|
171 |
$data = '';
|
172 |
|
180 |
|
181 |
print_r(json_decode(json_encode($parser->parse($data)), true));
|
182 |
|
183 |
+
fwrite(STDERR, 'Warning: the --dump-tree option is deprecated. Use proper debugging tools instead.');
|
184 |
+
|
185 |
exit();
|
186 |
}
|
187 |
|
188 |
$scss = new Compiler();
|
189 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
if ($loadPaths) {
|
191 |
+
$scss->setImportPaths($loadPaths);
|
|
|
|
|
|
|
|
|
192 |
}
|
193 |
|
194 |
if ($style) {
|
195 |
+
if ($style === OutputStyle::COMPRESSED || $style === OutputStyle::EXPANDED) {
|
196 |
+
$scss->setOutputStyle($style);
|
197 |
+
} else {
|
198 |
+
fwrite(STDERR, "WARNING: the $style style is deprecated.\n");
|
199 |
+
$scss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\' . ucfirst($style));
|
200 |
+
}
|
201 |
}
|
202 |
|
203 |
+
$outputFile = isset($arguments[1]) ? $arguments[1] : null;
|
204 |
+
$sourceMapFile = null;
|
205 |
+
|
206 |
if ($sourceMap) {
|
207 |
+
$sourceMapOptions = array(
|
208 |
+
'outputSourceFiles' => $embedSources,
|
209 |
+
);
|
210 |
+
if ($embedSourceMap || $outputFile === null) {
|
211 |
+
$scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
|
212 |
+
} else {
|
213 |
+
$sourceMapFile = $outputFile . '.map';
|
214 |
+
$sourceMapOptions['sourceMapWriteTo'] = $sourceMapFile;
|
215 |
+
$sourceMapOptions['sourceMapURL'] = basename($sourceMapFile);
|
216 |
+
$sourceMapOptions['sourceMapBasepath'] = getcwd();
|
217 |
+
$sourceMapOptions['sourceMapFilename'] = basename($outputFile);
|
218 |
+
|
219 |
+
$scss->setSourceMap(Compiler::SOURCE_MAP_FILE);
|
220 |
+
}
|
221 |
+
|
222 |
+
$scss->setSourceMapOptions($sourceMapOptions);
|
223 |
}
|
224 |
|
225 |
if ($encoding) {
|
226 |
$scss->setEncoding($encoding);
|
227 |
}
|
228 |
|
229 |
+
try {
|
230 |
+
$result = $scss->compileString($data, $inputFile);
|
231 |
+
} catch (SassException $e) {
|
232 |
+
fwrite(STDERR, 'Error: '.$e->getMessage()."\n");
|
233 |
+
exit(1);
|
234 |
+
}
|
235 |
|
236 |
+
if ($outputFile) {
|
237 |
+
file_put_contents($outputFile, $result->getCss());
|
238 |
+
|
239 |
+
if ($sourceMapFile !== null && $result->getSourceMap() !== null) {
|
240 |
+
file_put_contents($sourceMapFile, $result->getSourceMap());
|
241 |
+
}
|
242 |
+
} else {
|
243 |
+
echo $result->getCss();
|
244 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/composer.json
CHANGED
@@ -30,22 +30,82 @@
|
|
30 |
"ext-json": "*",
|
31 |
"ext-ctype": "*"
|
32 |
},
|
|
|
|
|
|
|
|
|
33 |
"require-dev": {
|
|
|
|
|
|
|
34 |
"squizlabs/php_codesniffer": "~3.5",
|
35 |
-
"
|
36 |
-
"
|
|
|
|
|
37 |
"zurb/foundation": "~6.5"
|
38 |
},
|
39 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
"bin": ["bin/pscss"],
|
41 |
-
"
|
42 |
-
"
|
43 |
-
|
44 |
-
"
|
45 |
-
|
46 |
-
"/.travis.yml",
|
47 |
-
"/phpunit.xml.dist",
|
48 |
-
"/tests"
|
49 |
-
]
|
50 |
}
|
51 |
}
|
30 |
"ext-json": "*",
|
31 |
"ext-ctype": "*"
|
32 |
},
|
33 |
+
"suggest": {
|
34 |
+
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv",
|
35 |
+
"ext-iconv": "Can be used as fallback when ext-mbstring is not available"
|
36 |
+
},
|
37 |
"require-dev": {
|
38 |
+
"bamarni/composer-bin-plugin": "^1.4",
|
39 |
+
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
|
40 |
+
"sass/sass-spec": "*",
|
41 |
"squizlabs/php_codesniffer": "~3.5",
|
42 |
+
"symfony/phpunit-bridge": "^5.1",
|
43 |
+
"thoughtbot/bourbon": "^7.0",
|
44 |
+
"twbs/bootstrap": "~5.0",
|
45 |
+
"twbs/bootstrap4": "4.6.1",
|
46 |
"zurb/foundation": "~6.5"
|
47 |
},
|
48 |
+
"repositories": [
|
49 |
+
{
|
50 |
+
"type": "package",
|
51 |
+
"package": {
|
52 |
+
"name": "sass/sass-spec",
|
53 |
+
"version": "2022.02.24",
|
54 |
+
"source": {
|
55 |
+
"type": "git",
|
56 |
+
"url": "https://github.com/sass/sass-spec.git",
|
57 |
+
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba"
|
58 |
+
},
|
59 |
+
"dist": {
|
60 |
+
"type": "zip",
|
61 |
+
"url": "https://api.github.com/repos/sass/sass-spec/zipball/f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
62 |
+
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
63 |
+
"shasum": ""
|
64 |
+
}
|
65 |
+
}
|
66 |
+
},
|
67 |
+
{
|
68 |
+
"type": "package",
|
69 |
+
"package": {
|
70 |
+
"name": "thoughtbot/bourbon",
|
71 |
+
"version": "v7.0.0",
|
72 |
+
"source": {
|
73 |
+
"type": "git",
|
74 |
+
"url": "https://github.com/thoughtbot/bourbon.git",
|
75 |
+
"reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3"
|
76 |
+
},
|
77 |
+
"dist": {
|
78 |
+
"type": "zip",
|
79 |
+
"url": "https://api.github.com/repos/thoughtbot/bourbon/zipball/fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
|
80 |
+
"reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
|
81 |
+
"shasum": ""
|
82 |
+
}
|
83 |
+
}
|
84 |
+
},
|
85 |
+
{
|
86 |
+
"type": "package",
|
87 |
+
"package": {
|
88 |
+
"name": "twbs/bootstrap4",
|
89 |
+
"version": "v4.6.1",
|
90 |
+
"source": {
|
91 |
+
"type": "git",
|
92 |
+
"url": "https://github.com/twbs/bootstrap.git",
|
93 |
+
"reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10"
|
94 |
+
},
|
95 |
+
"dist": {
|
96 |
+
"type": "zip",
|
97 |
+
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
|
98 |
+
"reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
|
99 |
+
"shasum": ""
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
],
|
104 |
"bin": ["bin/pscss"],
|
105 |
+
"config": {
|
106 |
+
"sort-packages": true,
|
107 |
+
"allow-plugins": {
|
108 |
+
"bamarni/composer-bin-plugin": true
|
109 |
+
}
|
|
|
|
|
|
|
|
|
110 |
}
|
111 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/phpcs.xml.dist
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<ruleset name="PSR12 (adapted for PHP 5.6+)">
|
3 |
+
<rule ref="PSR12">
|
4 |
+
<!-- Ignore this PHP 7.1+ sniff as long as we support PHP 5.6+ -->
|
5 |
+
<exclude name="PSR12.Properties.ConstantVisibility.NotFound"/>
|
6 |
+
|
7 |
+
<!-- This sniff doesn't ignore comment blocks -->
|
8 |
+
<!--
|
9 |
+
<exclude name="Generic.Files.LineLength"/>
|
10 |
+
-->
|
11 |
+
</rule>
|
12 |
+
</ruleset>
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/scss.inc.php
CHANGED
@@ -1,34 +1,21 @@
|
|
1 |
<?php
|
|
|
2 |
if (version_compare(PHP_VERSION, '5.6') < 0) {
|
3 |
throw new \Exception('scssphp requires PHP 5.6 or above');
|
4 |
}
|
5 |
|
6 |
-
if (! class_exists('ScssPhp\ScssPhp\Version'
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
include_once __DIR__ . '/src/Formatter/Crunched.php';
|
21 |
-
include_once __DIR__ . '/src/Formatter/Debug.php';
|
22 |
-
include_once __DIR__ . '/src/Formatter/Expanded.php';
|
23 |
-
include_once __DIR__ . '/src/Formatter/Nested.php';
|
24 |
-
include_once __DIR__ . '/src/Formatter/OutputBlock.php';
|
25 |
-
include_once __DIR__ . '/src/Node.php';
|
26 |
-
include_once __DIR__ . '/src/Node/Number.php';
|
27 |
-
include_once __DIR__ . '/src/Parser.php';
|
28 |
-
include_once __DIR__ . '/src/SourceMap/Base64.php';
|
29 |
-
include_once __DIR__ . '/src/SourceMap/Base64VLQ.php';
|
30 |
-
include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php';
|
31 |
-
include_once __DIR__ . '/src/Type.php';
|
32 |
-
include_once __DIR__ . '/src/Util.php';
|
33 |
-
include_once __DIR__ . '/src/Version.php';
|
34 |
}
|
1 |
<?php
|
2 |
+
|
3 |
if (version_compare(PHP_VERSION, '5.6') < 0) {
|
4 |
throw new \Exception('scssphp requires PHP 5.6 or above');
|
5 |
}
|
6 |
|
7 |
+
if (! class_exists('ScssPhp\ScssPhp\Version')) {
|
8 |
+
spl_autoload_register(function ($class) {
|
9 |
+
if (0 !== strpos($class, 'ScssPhp\ScssPhp\\')) {
|
10 |
+
// Not a ScssPhp class
|
11 |
+
return;
|
12 |
+
}
|
13 |
+
|
14 |
+
$subClass = substr($class, strlen('ScssPhp\ScssPhp\\'));
|
15 |
+
$path = __DIR__ . '/src/' . str_replace('\\', '/', $subClass) . '.php';
|
16 |
+
|
17 |
+
if (file_exists($path)) {
|
18 |
+
require $path;
|
19 |
+
}
|
20 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Base/Range.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,17 +16,26 @@ namespace ScssPhp\ScssPhp\Base;
|
|
15 |
* Range
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class Range
|
20 |
{
|
|
|
|
|
|
|
21 |
public $first;
|
|
|
|
|
|
|
|
|
22 |
public $last;
|
23 |
|
24 |
/**
|
25 |
* Initialize range
|
26 |
*
|
27 |
-
* @param
|
28 |
-
* @param
|
29 |
*/
|
30 |
public function __construct($first, $last)
|
31 |
{
|
@@ -36,9 +46,9 @@ class Range
|
|
36 |
/**
|
37 |
* Test for inclusion in range
|
38 |
*
|
39 |
-
* @param
|
40 |
*
|
41 |
-
* @return
|
42 |
*/
|
43 |
public function includes($value)
|
44 |
{
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Range
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class Range
|
23 |
{
|
24 |
+
/**
|
25 |
+
* @var float|int
|
26 |
+
*/
|
27 |
public $first;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var float|int
|
31 |
+
*/
|
32 |
public $last;
|
33 |
|
34 |
/**
|
35 |
* Initialize range
|
36 |
*
|
37 |
+
* @param int|float $first
|
38 |
+
* @param int|float $last
|
39 |
*/
|
40 |
public function __construct($first, $last)
|
41 |
{
|
46 |
/**
|
47 |
* Test for inclusion in range
|
48 |
*
|
49 |
+
* @param int|float $value
|
50 |
*
|
51 |
+
* @return bool
|
52 |
*/
|
53 |
public function includes($value)
|
54 |
{
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,16 +16,18 @@ namespace ScssPhp\ScssPhp;
|
|
15 |
* Block
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class Block
|
20 |
{
|
21 |
/**
|
22 |
-
* @var string
|
23 |
*/
|
24 |
public $type;
|
25 |
|
26 |
/**
|
27 |
-
* @var
|
28 |
*/
|
29 |
public $parent;
|
30 |
|
@@ -34,22 +37,22 @@ class Block
|
|
34 |
public $sourceName;
|
35 |
|
36 |
/**
|
37 |
-
* @var
|
38 |
*/
|
39 |
public $sourceIndex;
|
40 |
|
41 |
/**
|
42 |
-
* @var
|
43 |
*/
|
44 |
public $sourceLine;
|
45 |
|
46 |
/**
|
47 |
-
* @var
|
48 |
*/
|
49 |
public $sourceColumn;
|
50 |
|
51 |
/**
|
52 |
-
* @var array
|
53 |
*/
|
54 |
public $selectors;
|
55 |
|
@@ -64,7 +67,7 @@ class Block
|
|
64 |
public $children;
|
65 |
|
66 |
/**
|
67 |
-
* @var
|
68 |
*/
|
69 |
public $selfParent;
|
70 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Block
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class Block
|
23 |
{
|
24 |
/**
|
25 |
+
* @var string|null
|
26 |
*/
|
27 |
public $type;
|
28 |
|
29 |
/**
|
30 |
+
* @var Block|null
|
31 |
*/
|
32 |
public $parent;
|
33 |
|
37 |
public $sourceName;
|
38 |
|
39 |
/**
|
40 |
+
* @var int
|
41 |
*/
|
42 |
public $sourceIndex;
|
43 |
|
44 |
/**
|
45 |
+
* @var int
|
46 |
*/
|
47 |
public $sourceLine;
|
48 |
|
49 |
/**
|
50 |
+
* @var int
|
51 |
*/
|
52 |
public $sourceColumn;
|
53 |
|
54 |
/**
|
55 |
+
* @var array|null
|
56 |
*/
|
57 |
public $selectors;
|
58 |
|
67 |
public $children;
|
68 |
|
69 |
/**
|
70 |
+
* @var Block|null
|
71 |
*/
|
72 |
public $selfParent;
|
73 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/AtRootBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class AtRootBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var array|null
|
25 |
+
*/
|
26 |
+
public $selector;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array|null
|
30 |
+
*/
|
31 |
+
public $with;
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_AT_ROOT;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/CallableBlock.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Compiler\Environment;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class CallableBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
public $name;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array|null
|
30 |
+
*/
|
31 |
+
public $args;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var Environment|null
|
35 |
+
*/
|
36 |
+
public $parentEnv;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param string $type
|
40 |
+
*/
|
41 |
+
public function __construct($type)
|
42 |
+
{
|
43 |
+
$this->type = $type;
|
44 |
+
}
|
45 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ContentBlock.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Compiler\Environment;
|
17 |
+
use ScssPhp\ScssPhp\Type;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @internal
|
21 |
+
*/
|
22 |
+
class ContentBlock extends Block
|
23 |
+
{
|
24 |
+
/**
|
25 |
+
* @var array|null
|
26 |
+
*/
|
27 |
+
public $child;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var Environment|null
|
31 |
+
*/
|
32 |
+
public $scope;
|
33 |
+
|
34 |
+
public function __construct()
|
35 |
+
{
|
36 |
+
$this->type = Type::T_INCLUDE;
|
37 |
+
}
|
38 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/DirectiveBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class DirectiveBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var string|array
|
25 |
+
*/
|
26 |
+
public $name;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string|array|null
|
30 |
+
*/
|
31 |
+
public $value;
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_DIRECTIVE;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/EachBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class EachBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var string[]
|
25 |
+
*/
|
26 |
+
public $vars = [];
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
public $list;
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_EACH;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ElseBlock.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class ElseBlock extends Block
|
22 |
+
{
|
23 |
+
public function __construct()
|
24 |
+
{
|
25 |
+
$this->type = Type::T_ELSE;
|
26 |
+
}
|
27 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ElseifBlock.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class ElseifBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
public $cond;
|
27 |
+
|
28 |
+
public function __construct()
|
29 |
+
{
|
30 |
+
$this->type = Type::T_ELSEIF;
|
31 |
+
}
|
32 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/ForBlock.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class ForBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
public $var;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
public $start;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var array
|
35 |
+
*/
|
36 |
+
public $end;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var bool
|
40 |
+
*/
|
41 |
+
public $until;
|
42 |
+
|
43 |
+
public function __construct()
|
44 |
+
{
|
45 |
+
$this->type = Type::T_FOR;
|
46 |
+
}
|
47 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/IfBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class IfBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
public $cond;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array<ElseifBlock|ElseBlock>
|
30 |
+
*/
|
31 |
+
public $cases = [];
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_IF;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/MediaBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class MediaBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var string|array|null
|
25 |
+
*/
|
26 |
+
public $value;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array|null
|
30 |
+
*/
|
31 |
+
public $queryList;
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_MEDIA;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/NestedPropertyBlock.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class NestedPropertyBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var bool
|
25 |
+
*/
|
26 |
+
public $hasValue;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
public $prefix;
|
32 |
+
|
33 |
+
public function __construct()
|
34 |
+
{
|
35 |
+
$this->type = Type::T_NESTED_PROPERTY;
|
36 |
+
}
|
37 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Block/WhileBlock.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Block;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Block;
|
16 |
+
use ScssPhp\ScssPhp\Type;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @internal
|
20 |
+
*/
|
21 |
+
class WhileBlock extends Block
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
public $cond;
|
27 |
+
|
28 |
+
public function __construct()
|
29 |
+
{
|
30 |
+
$this->type = Type::T_WHILE;
|
31 |
+
}
|
32 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Cache.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,6 +13,7 @@
|
|
12 |
namespace ScssPhp\ScssPhp;
|
13 |
|
14 |
use Exception;
|
|
|
15 |
|
16 |
/**
|
17 |
* The scss cache manager.
|
@@ -28,30 +30,54 @@ use Exception;
|
|
28 |
* SCSS cache
|
29 |
*
|
30 |
* @author Cedric Morin <cedric@yterium.com>
|
|
|
|
|
31 |
*/
|
32 |
class Cache
|
33 |
{
|
34 |
const CACHE_VERSION = 1;
|
35 |
|
36 |
-
|
|
|
|
|
|
|
|
|
37 |
public static $cacheDir = false;
|
38 |
|
39 |
-
|
|
|
|
|
|
|
|
|
40 |
public static $prefix = 'scssphp_';
|
41 |
|
42 |
-
|
|
|
|
|
|
|
|
|
43 |
public static $forceRefresh = false;
|
44 |
|
45 |
-
|
|
|
|
|
|
|
|
|
46 |
public static $gcLifetime = 604800;
|
47 |
|
48 |
-
|
|
|
|
|
|
|
|
|
49 |
protected static $refreshed = [];
|
50 |
|
51 |
/**
|
52 |
* Constructor
|
53 |
*
|
54 |
* @param array $options
|
|
|
|
|
55 |
*/
|
56 |
public function __construct($options)
|
57 |
{
|
@@ -83,10 +109,10 @@ class Cache
|
|
83 |
* Get the cached result of $operation on $what,
|
84 |
* which is known as dependant from the content of $options
|
85 |
*
|
86 |
-
* @param string
|
87 |
-
* @param mixed
|
88 |
-
* @param array
|
89 |
-
* @param
|
90 |
*
|
91 |
* @return mixed
|
92 |
*
|
@@ -96,12 +122,14 @@ class Cache
|
|
96 |
{
|
97 |
$fileCache = self::$cacheDir . self::cacheName($operation, $what, $options);
|
98 |
|
99 |
-
if (
|
|
|
100 |
isset(self::$refreshed[$fileCache]))) && file_exists($fileCache)
|
101 |
) {
|
102 |
$cacheTime = filemtime($fileCache);
|
103 |
|
104 |
-
if (
|
|
|
105 |
$cacheTime + self::$gcLifetime > time()
|
106 |
) {
|
107 |
$c = file_get_contents($fileCache);
|
@@ -124,6 +152,8 @@ class Cache
|
|
124 |
* @param mixed $what
|
125 |
* @param mixed $value
|
126 |
* @param array $options
|
|
|
|
|
127 |
*/
|
128 |
public function setCache($operation, $what, $value, $options = [])
|
129 |
{
|
@@ -153,6 +183,7 @@ class Cache
|
|
153 |
{
|
154 |
$t = [
|
155 |
'version' => self::CACHE_VERSION,
|
|
|
156 |
'operation' => $operation,
|
157 |
'what' => $what,
|
158 |
'options' => $options
|
@@ -169,6 +200,8 @@ class Cache
|
|
169 |
/**
|
170 |
* Check that the cache dir exists and is writeable
|
171 |
*
|
|
|
|
|
172 |
* @throws \Exception
|
173 |
*/
|
174 |
public static function checkCacheDir()
|
@@ -177,9 +210,7 @@ class Cache
|
|
177 |
self::$cacheDir = rtrim(self::$cacheDir, '/') . '/';
|
178 |
|
179 |
if (! is_dir(self::$cacheDir)) {
|
180 |
-
|
181 |
-
throw new Exception('Cache directory couldn\'t be created: ' . self::$cacheDir);
|
182 |
-
}
|
183 |
}
|
184 |
|
185 |
if (! is_writable(self::$cacheDir)) {
|
@@ -189,6 +220,8 @@ class Cache
|
|
189 |
|
190 |
/**
|
191 |
* Delete unused cached files
|
|
|
|
|
192 |
*/
|
193 |
public static function cleanCache()
|
194 |
{
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp;
|
14 |
|
15 |
use Exception;
|
16 |
+
use ScssPhp\ScssPhp\Version;
|
17 |
|
18 |
/**
|
19 |
* The scss cache manager.
|
30 |
* SCSS cache
|
31 |
*
|
32 |
* @author Cedric Morin <cedric@yterium.com>
|
33 |
+
*
|
34 |
+
* @internal
|
35 |
*/
|
36 |
class Cache
|
37 |
{
|
38 |
const CACHE_VERSION = 1;
|
39 |
|
40 |
+
/**
|
41 |
+
* directory used for storing data
|
42 |
+
*
|
43 |
+
* @var string|false
|
44 |
+
*/
|
45 |
public static $cacheDir = false;
|
46 |
|
47 |
+
/**
|
48 |
+
* prefix for the storing data
|
49 |
+
*
|
50 |
+
* @var string
|
51 |
+
*/
|
52 |
public static $prefix = 'scssphp_';
|
53 |
|
54 |
+
/**
|
55 |
+
* force a refresh : 'once' for refreshing the first hit on a cache only, true to never use the cache in this hit
|
56 |
+
*
|
57 |
+
* @var bool|string
|
58 |
+
*/
|
59 |
public static $forceRefresh = false;
|
60 |
|
61 |
+
/**
|
62 |
+
* specifies the number of seconds after which data cached will be seen as 'garbage' and potentially cleaned up
|
63 |
+
*
|
64 |
+
* @var int
|
65 |
+
*/
|
66 |
public static $gcLifetime = 604800;
|
67 |
|
68 |
+
/**
|
69 |
+
* array of already refreshed cache if $forceRefresh==='once'
|
70 |
+
*
|
71 |
+
* @var array<string, bool>
|
72 |
+
*/
|
73 |
protected static $refreshed = [];
|
74 |
|
75 |
/**
|
76 |
* Constructor
|
77 |
*
|
78 |
* @param array $options
|
79 |
+
*
|
80 |
+
* @phpstan-param array{cacheDir?: string, prefix?: string, forceRefresh?: string} $options
|
81 |
*/
|
82 |
public function __construct($options)
|
83 |
{
|
109 |
* Get the cached result of $operation on $what,
|
110 |
* which is known as dependant from the content of $options
|
111 |
*
|
112 |
+
* @param string $operation parse, compile...
|
113 |
+
* @param mixed $what content key (e.g., filename to be treated)
|
114 |
+
* @param array $options any option that affect the operation result on the content
|
115 |
+
* @param int|null $lastModified last modified timestamp
|
116 |
*
|
117 |
* @return mixed
|
118 |
*
|
122 |
{
|
123 |
$fileCache = self::$cacheDir . self::cacheName($operation, $what, $options);
|
124 |
|
125 |
+
if (
|
126 |
+
((self::$forceRefresh === false) || (self::$forceRefresh === 'once' &&
|
127 |
isset(self::$refreshed[$fileCache]))) && file_exists($fileCache)
|
128 |
) {
|
129 |
$cacheTime = filemtime($fileCache);
|
130 |
|
131 |
+
if (
|
132 |
+
(\is_null($lastModified) || $cacheTime > $lastModified) &&
|
133 |
$cacheTime + self::$gcLifetime > time()
|
134 |
) {
|
135 |
$c = file_get_contents($fileCache);
|
152 |
* @param mixed $what
|
153 |
* @param mixed $value
|
154 |
* @param array $options
|
155 |
+
*
|
156 |
+
* @return void
|
157 |
*/
|
158 |
public function setCache($operation, $what, $value, $options = [])
|
159 |
{
|
183 |
{
|
184 |
$t = [
|
185 |
'version' => self::CACHE_VERSION,
|
186 |
+
'scssphpVersion' => Version::VERSION,
|
187 |
'operation' => $operation,
|
188 |
'what' => $what,
|
189 |
'options' => $options
|
200 |
/**
|
201 |
* Check that the cache dir exists and is writeable
|
202 |
*
|
203 |
+
* @return void
|
204 |
+
*
|
205 |
* @throws \Exception
|
206 |
*/
|
207 |
public static function checkCacheDir()
|
210 |
self::$cacheDir = rtrim(self::$cacheDir, '/') . '/';
|
211 |
|
212 |
if (! is_dir(self::$cacheDir)) {
|
213 |
+
throw new Exception('Cache directory doesn\'t exist: ' . self::$cacheDir);
|
|
|
|
|
214 |
}
|
215 |
|
216 |
if (! is_writable(self::$cacheDir)) {
|
220 |
|
221 |
/**
|
222 |
* Delete unused cached files
|
223 |
+
*
|
224 |
+
* @return void
|
225 |
*/
|
226 |
public static function cleanCache()
|
227 |
{
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Colors.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,6 +16,8 @@ namespace ScssPhp\ScssPhp;
|
|
15 |
* CSS Colors
|
16 |
*
|
17 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class Colors
|
20 |
{
|
@@ -23,12 +26,13 @@ class Colors
|
|
23 |
*
|
24 |
* @see http://www.w3.org/TR/css3-color
|
25 |
*
|
26 |
-
* @var array
|
27 |
*/
|
28 |
protected static $cssColors = [
|
29 |
'aliceblue' => '240,248,255',
|
30 |
'antiquewhite' => '250,235,215',
|
31 |
'aqua' => '0,255,255',
|
|
|
32 |
'aquamarine' => '127,255,212',
|
33 |
'azure' => '240,255,255',
|
34 |
'beige' => '245,245,220',
|
@@ -46,13 +50,12 @@ class Colors
|
|
46 |
'cornflowerblue' => '100,149,237',
|
47 |
'cornsilk' => '255,248,220',
|
48 |
'crimson' => '220,20,60',
|
49 |
-
'cyan' => '0,255,255',
|
50 |
'darkblue' => '0,0,139',
|
51 |
'darkcyan' => '0,139,139',
|
52 |
'darkgoldenrod' => '184,134,11',
|
53 |
'darkgray' => '169,169,169',
|
54 |
-
'darkgreen' => '0,100,0',
|
55 |
'darkgrey' => '169,169,169',
|
|
|
56 |
'darkkhaki' => '189,183,107',
|
57 |
'darkmagenta' => '139,0,139',
|
58 |
'darkolivegreen' => '85,107,47',
|
@@ -75,14 +78,15 @@ class Colors
|
|
75 |
'floralwhite' => '255,250,240',
|
76 |
'forestgreen' => '34,139,34',
|
77 |
'fuchsia' => '255,0,255',
|
|
|
78 |
'gainsboro' => '220,220,220',
|
79 |
'ghostwhite' => '248,248,255',
|
80 |
'gold' => '255,215,0',
|
81 |
'goldenrod' => '218,165,32',
|
82 |
'gray' => '128,128,128',
|
|
|
83 |
'green' => '0,128,0',
|
84 |
'greenyellow' => '173,255,47',
|
85 |
-
'grey' => '128,128,128',
|
86 |
'honeydew' => '240,255,240',
|
87 |
'hotpink' => '255,105,180',
|
88 |
'indianred' => '205,92,92',
|
@@ -98,8 +102,8 @@ class Colors
|
|
98 |
'lightcyan' => '224,255,255',
|
99 |
'lightgoldenrodyellow' => '250,250,210',
|
100 |
'lightgray' => '211,211,211',
|
101 |
-
'lightgreen' => '144,238,144',
|
102 |
'lightgrey' => '211,211,211',
|
|
|
103 |
'lightpink' => '255,182,193',
|
104 |
'lightsalmon' => '255,160,122',
|
105 |
'lightseagreen' => '32,178,170',
|
@@ -111,7 +115,6 @@ class Colors
|
|
111 |
'lime' => '0,255,0',
|
112 |
'limegreen' => '50,205,50',
|
113 |
'linen' => '250,240,230',
|
114 |
-
'magenta' => '255,0,255',
|
115 |
'maroon' => '128,0,0',
|
116 |
'mediumaquamarine' => '102,205,170',
|
117 |
'mediumblue' => '0,0,205',
|
@@ -145,7 +148,6 @@ class Colors
|
|
145 |
'plum' => '221,160,221',
|
146 |
'powderblue' => '176,224,230',
|
147 |
'purple' => '128,0,128',
|
148 |
-
'rebeccapurple' => '102,51,153',
|
149 |
'red' => '255,0,0',
|
150 |
'rosybrown' => '188,143,143',
|
151 |
'royalblue' => '65,105,225',
|
@@ -167,7 +169,6 @@ class Colors
|
|
167 |
'teal' => '0,128,128',
|
168 |
'thistle' => '216,191,216',
|
169 |
'tomato' => '255,99,71',
|
170 |
-
'transparent' => '0,0,0,0',
|
171 |
'turquoise' => '64,224,208',
|
172 |
'violet' => '238,130,238',
|
173 |
'wheat' => '245,222,179',
|
@@ -175,6 +176,8 @@ class Colors
|
|
175 |
'whitesmoke' => '245,245,245',
|
176 |
'yellow' => '255,255,0',
|
177 |
'yellowgreen' => '154,205,50',
|
|
|
|
|
178 |
];
|
179 |
|
180 |
/**
|
@@ -182,7 +185,7 @@ class Colors
|
|
182 |
*
|
183 |
* @param string $colorName
|
184 |
*
|
185 |
-
* @return
|
186 |
*/
|
187 |
public static function colorNameToRGBa($colorName)
|
188 |
{
|
@@ -201,10 +204,10 @@ class Colors
|
|
201 |
/**
|
202 |
* Reverse conversion : from RGBA to a color name if possible
|
203 |
*
|
204 |
-
* @param
|
205 |
-
* @param
|
206 |
-
* @param
|
207 |
-
* @param
|
208 |
*
|
209 |
* @return string|null
|
210 |
*/
|
@@ -226,7 +229,10 @@ class Colors
|
|
226 |
foreach (static::$cssColors as $name => $rgb_str) {
|
227 |
$rgb_str = explode(',', $rgb_str);
|
228 |
|
229 |
-
if (
|
|
|
|
|
|
|
230 |
$reverseColorTable[\intval($rgb_str[0])][\intval($rgb_str[1])][\intval($rgb_str[2])] = $name;
|
231 |
}
|
232 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* CSS Colors
|
17 |
*
|
18 |
* @author Leaf Corcoran <leafot@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class Colors
|
23 |
{
|
26 |
*
|
27 |
* @see http://www.w3.org/TR/css3-color
|
28 |
*
|
29 |
+
* @var array<string, string>
|
30 |
*/
|
31 |
protected static $cssColors = [
|
32 |
'aliceblue' => '240,248,255',
|
33 |
'antiquewhite' => '250,235,215',
|
34 |
'aqua' => '0,255,255',
|
35 |
+
'cyan' => '0,255,255',
|
36 |
'aquamarine' => '127,255,212',
|
37 |
'azure' => '240,255,255',
|
38 |
'beige' => '245,245,220',
|
50 |
'cornflowerblue' => '100,149,237',
|
51 |
'cornsilk' => '255,248,220',
|
52 |
'crimson' => '220,20,60',
|
|
|
53 |
'darkblue' => '0,0,139',
|
54 |
'darkcyan' => '0,139,139',
|
55 |
'darkgoldenrod' => '184,134,11',
|
56 |
'darkgray' => '169,169,169',
|
|
|
57 |
'darkgrey' => '169,169,169',
|
58 |
+
'darkgreen' => '0,100,0',
|
59 |
'darkkhaki' => '189,183,107',
|
60 |
'darkmagenta' => '139,0,139',
|
61 |
'darkolivegreen' => '85,107,47',
|
78 |
'floralwhite' => '255,250,240',
|
79 |
'forestgreen' => '34,139,34',
|
80 |
'fuchsia' => '255,0,255',
|
81 |
+
'magenta' => '255,0,255',
|
82 |
'gainsboro' => '220,220,220',
|
83 |
'ghostwhite' => '248,248,255',
|
84 |
'gold' => '255,215,0',
|
85 |
'goldenrod' => '218,165,32',
|
86 |
'gray' => '128,128,128',
|
87 |
+
'grey' => '128,128,128',
|
88 |
'green' => '0,128,0',
|
89 |
'greenyellow' => '173,255,47',
|
|
|
90 |
'honeydew' => '240,255,240',
|
91 |
'hotpink' => '255,105,180',
|
92 |
'indianred' => '205,92,92',
|
102 |
'lightcyan' => '224,255,255',
|
103 |
'lightgoldenrodyellow' => '250,250,210',
|
104 |
'lightgray' => '211,211,211',
|
|
|
105 |
'lightgrey' => '211,211,211',
|
106 |
+
'lightgreen' => '144,238,144',
|
107 |
'lightpink' => '255,182,193',
|
108 |
'lightsalmon' => '255,160,122',
|
109 |
'lightseagreen' => '32,178,170',
|
115 |
'lime' => '0,255,0',
|
116 |
'limegreen' => '50,205,50',
|
117 |
'linen' => '250,240,230',
|
|
|
118 |
'maroon' => '128,0,0',
|
119 |
'mediumaquamarine' => '102,205,170',
|
120 |
'mediumblue' => '0,0,205',
|
148 |
'plum' => '221,160,221',
|
149 |
'powderblue' => '176,224,230',
|
150 |
'purple' => '128,0,128',
|
|
|
151 |
'red' => '255,0,0',
|
152 |
'rosybrown' => '188,143,143',
|
153 |
'royalblue' => '65,105,225',
|
169 |
'teal' => '0,128,128',
|
170 |
'thistle' => '216,191,216',
|
171 |
'tomato' => '255,99,71',
|
|
|
172 |
'turquoise' => '64,224,208',
|
173 |
'violet' => '238,130,238',
|
174 |
'wheat' => '245,222,179',
|
176 |
'whitesmoke' => '245,245,245',
|
177 |
'yellow' => '255,255,0',
|
178 |
'yellowgreen' => '154,205,50',
|
179 |
+
'rebeccapurple' => '102,51,153',
|
180 |
+
'transparent' => '0,0,0,0',
|
181 |
];
|
182 |
|
183 |
/**
|
185 |
*
|
186 |
* @param string $colorName
|
187 |
*
|
188 |
+
* @return int[]|null
|
189 |
*/
|
190 |
public static function colorNameToRGBa($colorName)
|
191 |
{
|
204 |
/**
|
205 |
* Reverse conversion : from RGBA to a color name if possible
|
206 |
*
|
207 |
+
* @param int $r
|
208 |
+
* @param int $g
|
209 |
+
* @param int $b
|
210 |
+
* @param int|float $a
|
211 |
*
|
212 |
* @return string|null
|
213 |
*/
|
229 |
foreach (static::$cssColors as $name => $rgb_str) {
|
230 |
$rgb_str = explode(',', $rgb_str);
|
231 |
|
232 |
+
if (
|
233 |
+
\count($rgb_str) == 3 &&
|
234 |
+
! isset($reverseColorTable[\intval($rgb_str[0])][\intval($rgb_str[1])][\intval($rgb_str[2])])
|
235 |
+
) {
|
236 |
$reverseColorTable[\intval($rgb_str[0])][\intval($rgb_str[1])][\intval($rgb_str[2])] = $name;
|
237 |
}
|
238 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/CompilationResult.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp;
|
14 |
+
|
15 |
+
class CompilationResult
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
private $css;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string|null
|
24 |
+
*/
|
25 |
+
private $sourceMap;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var string[]
|
29 |
+
*/
|
30 |
+
private $includedFiles;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param string $css
|
34 |
+
* @param string|null $sourceMap
|
35 |
+
* @param string[] $includedFiles
|
36 |
+
*/
|
37 |
+
public function __construct($css, $sourceMap, array $includedFiles)
|
38 |
+
{
|
39 |
+
$this->css = $css;
|
40 |
+
$this->sourceMap = $sourceMap;
|
41 |
+
$this->includedFiles = $includedFiles;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @return string
|
46 |
+
*/
|
47 |
+
public function getCss()
|
48 |
+
{
|
49 |
+
return $this->css;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @return string[]
|
54 |
+
*/
|
55 |
+
public function getIncludedFiles()
|
56 |
+
{
|
57 |
+
return $this->includedFiles;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* The sourceMap content, if it was generated
|
62 |
+
*
|
63 |
+
* @return null|string
|
64 |
+
*/
|
65 |
+
public function getSourceMap()
|
66 |
+
{
|
67 |
+
return $this->sourceMap;
|
68 |
+
}
|
69 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,17 +13,31 @@
|
|
12 |
namespace ScssPhp\ScssPhp;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Base\Range;
|
15 |
-
use ScssPhp\ScssPhp\Block;
|
16 |
-
use ScssPhp\ScssPhp\
|
17 |
-
use ScssPhp\ScssPhp\
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
use ScssPhp\ScssPhp\Compiler\Environment;
|
19 |
use ScssPhp\ScssPhp\Exception\CompilerException;
|
|
|
|
|
|
|
|
|
|
|
20 |
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
21 |
-
use ScssPhp\ScssPhp\
|
|
|
|
|
22 |
use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
|
23 |
-
use ScssPhp\ScssPhp\
|
24 |
-
use ScssPhp\ScssPhp\Parser;
|
25 |
-
use ScssPhp\ScssPhp\Util;
|
26 |
|
27 |
/**
|
28 |
* The scss compiler and parser.
|
@@ -55,15 +70,35 @@ use ScssPhp\ScssPhp\Util;
|
|
55 |
* SCSS compiler
|
56 |
*
|
57 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
58 |
*/
|
59 |
class Compiler
|
60 |
{
|
|
|
|
|
|
|
61 |
const LINE_COMMENTS = 1;
|
|
|
|
|
|
|
62 |
const DEBUG_INFO = 2;
|
63 |
|
|
|
|
|
|
|
64 |
const WITH_RULE = 1;
|
|
|
|
|
|
|
65 |
const WITH_MEDIA = 2;
|
|
|
|
|
|
|
66 |
const WITH_SUPPORTS = 4;
|
|
|
|
|
|
|
67 |
const WITH_ALL = 7;
|
68 |
|
69 |
const SOURCE_MAP_NONE = 0;
|
@@ -71,7 +106,7 @@ class Compiler
|
|
71 |
const SOURCE_MAP_FILE = 2;
|
72 |
|
73 |
/**
|
74 |
-
* @var array
|
75 |
*/
|
76 |
protected static $operatorNames = [
|
77 |
'+' => 'add',
|
@@ -87,11 +122,10 @@ class Compiler
|
|
87 |
|
88 |
'<=' => 'lte',
|
89 |
'>=' => 'gte',
|
90 |
-
'<=>' => 'cmp',
|
91 |
];
|
92 |
|
93 |
/**
|
94 |
-
* @var array
|
95 |
*/
|
96 |
protected static $namespaces = [
|
97 |
'special' => '%',
|
@@ -101,7 +135,9 @@ class Compiler
|
|
101 |
|
102 |
public static $true = [Type::T_KEYWORD, 'true'];
|
103 |
public static $false = [Type::T_KEYWORD, 'false'];
|
|
|
104 |
public static $NaN = [Type::T_KEYWORD, 'NaN'];
|
|
|
105 |
public static $Infinity = [Type::T_KEYWORD, 'Infinity'];
|
106 |
public static $null = [Type::T_NULL];
|
107 |
public static $nullString = [Type::T_STRING, '', []];
|
@@ -112,80 +148,235 @@ class Compiler
|
|
112 |
public static $emptyString = [Type::T_STRING, '"', []];
|
113 |
public static $with = [Type::T_KEYWORD, 'with'];
|
114 |
public static $without = [Type::T_KEYWORD, 'without'];
|
|
|
115 |
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
protected $importCache = [];
|
|
|
|
|
|
|
|
|
118 |
protected $importedFiles = [];
|
|
|
|
|
|
|
|
|
|
|
119 |
protected $userFunctions = [];
|
|
|
|
|
|
|
120 |
protected $registeredVars = [];
|
|
|
|
|
|
|
121 |
protected $registeredFeatures = [
|
122 |
'extend-selector-pseudoclass' => false,
|
123 |
'at-error' => true,
|
124 |
-
'units-level-3' =>
|
125 |
'global-variable-shadowing' => false,
|
126 |
];
|
127 |
|
|
|
|
|
|
|
128 |
protected $encoding = null;
|
|
|
|
|
|
|
|
|
129 |
protected $lineNumberStyle = null;
|
130 |
|
|
|
|
|
|
|
|
|
131 |
protected $sourceMap = self::SOURCE_MAP_NONE;
|
|
|
|
|
|
|
|
|
|
|
132 |
protected $sourceMapOptions = [];
|
133 |
|
134 |
/**
|
135 |
-
* @var
|
|
|
|
|
|
|
|
|
|
|
136 |
*/
|
137 |
-
protected $formatter
|
138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
protected $rootEnv;
|
|
|
|
|
|
|
140 |
protected $rootBlock;
|
141 |
|
142 |
/**
|
143 |
* @var \ScssPhp\ScssPhp\Compiler\Environment
|
144 |
*/
|
145 |
protected $env;
|
|
|
|
|
|
|
146 |
protected $scope;
|
|
|
|
|
|
|
147 |
protected $storeEnv;
|
|
|
|
|
|
|
|
|
|
|
148 |
protected $charsetSeen;
|
|
|
|
|
|
|
149 |
protected $sourceNames;
|
150 |
|
|
|
|
|
|
|
151 |
protected $cache;
|
152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
protected $indentLevel;
|
|
|
|
|
|
|
154 |
protected $extends;
|
|
|
|
|
|
|
155 |
protected $extendsMap;
|
156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
protected $parser;
|
|
|
|
|
|
|
158 |
protected $sourceIndex;
|
|
|
|
|
|
|
159 |
protected $sourceLine;
|
|
|
|
|
|
|
160 |
protected $sourceColumn;
|
161 |
-
|
|
|
|
|
162 |
protected $shouldEvaluate;
|
|
|
|
|
|
|
|
|
163 |
protected $ignoreErrors;
|
|
|
|
|
|
|
164 |
protected $ignoreCallStackMessage = false;
|
165 |
|
|
|
|
|
|
|
166 |
protected $callStack = [];
|
167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
/**
|
169 |
* Constructor
|
170 |
*
|
171 |
* @param array|null $cacheOptions
|
|
|
172 |
*/
|
173 |
public function __construct($cacheOptions = null)
|
174 |
{
|
175 |
-
$this->parsedFiles = [];
|
176 |
$this->sourceNames = [];
|
177 |
|
178 |
if ($cacheOptions) {
|
179 |
$this->cache = new Cache($cacheOptions);
|
|
|
|
|
|
|
180 |
}
|
181 |
|
182 |
-
$this->
|
183 |
}
|
184 |
|
185 |
/**
|
186 |
* Get compiler options
|
187 |
*
|
188 |
-
* @return array
|
|
|
|
|
189 |
*/
|
190 |
public function getCompileOptions()
|
191 |
{
|
@@ -196,54 +387,97 @@ class Compiler
|
|
196 |
'encoding' => $this->encoding,
|
197 |
'sourceMap' => serialize($this->sourceMap),
|
198 |
'sourceMapOptions' => $this->sourceMapOptions,
|
199 |
-
'formatter' => $this->
|
|
|
200 |
];
|
201 |
|
202 |
return $options;
|
203 |
}
|
204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
/**
|
206 |
* Set an alternative error output stream, for testing purpose only
|
207 |
*
|
208 |
* @param resource $handle
|
|
|
|
|
|
|
|
|
209 |
*/
|
210 |
public function setErrorOuput($handle)
|
211 |
{
|
212 |
-
|
|
|
|
|
213 |
}
|
214 |
|
215 |
/**
|
216 |
* Compile scss
|
217 |
*
|
218 |
-
* @
|
219 |
-
*
|
220 |
-
* @param string $code
|
221 |
-
* @param string $path
|
222 |
*
|
223 |
* @return string
|
|
|
|
|
|
|
|
|
224 |
*/
|
225 |
public function compile($code, $path = null)
|
226 |
{
|
227 |
-
|
228 |
-
$cacheKey = ($path ? $path : "(stdin)") . ":" . md5($code);
|
229 |
-
$compileOptions = $this->getCompileOptions();
|
230 |
-
$cache = $this->cache->getCache("compile", $cacheKey, $compileOptions);
|
231 |
|
232 |
-
|
233 |
-
// check if any dependency file changed before accepting the cache
|
234 |
-
foreach ($cache['dependencies'] as $file => $mtime) {
|
235 |
-
if (! is_file($file) || filemtime($file) !== $mtime) {
|
236 |
-
unset($cache);
|
237 |
-
break;
|
238 |
-
}
|
239 |
-
}
|
240 |
|
241 |
-
|
242 |
-
|
243 |
-
|
|
|
|
|
|
|
|
|
|
|
244 |
}
|
245 |
}
|
246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
|
248 |
$this->indentLevel = -1;
|
249 |
$this->extends = [];
|
@@ -254,68 +488,151 @@ class Compiler
|
|
254 |
$this->env = null;
|
255 |
$this->scope = null;
|
256 |
$this->storeEnv = null;
|
257 |
-
$this->charsetSeen = null;
|
258 |
$this->shouldEvaluate = null;
|
259 |
$this->ignoreCallStackMessage = false;
|
|
|
|
|
|
|
260 |
|
261 |
-
$
|
262 |
-
|
263 |
-
|
|
|
|
|
|
|
|
|
|
|
264 |
|
265 |
-
|
266 |
-
|
267 |
-
|
|
|
268 |
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
|
273 |
-
|
|
|
|
|
|
|
274 |
|
275 |
-
|
276 |
-
|
277 |
-
$
|
278 |
-
$this->
|
279 |
-
}
|
280 |
-
|
281 |
}
|
282 |
-
}
|
283 |
|
284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
|
286 |
-
|
287 |
-
$sourceMap = $sourceMapGenerator->generateJson();
|
288 |
-
$sourceMapUrl = null;
|
289 |
|
290 |
-
|
291 |
-
case self::SOURCE_MAP_INLINE:
|
292 |
-
$sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap));
|
293 |
-
break;
|
294 |
|
295 |
-
|
296 |
-
|
297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
}
|
|
|
|
|
|
|
|
|
|
|
299 |
|
300 |
-
|
|
|
301 |
}
|
302 |
|
|
|
|
|
303 |
if ($this->cache && isset($cacheKey) && isset($compileOptions)) {
|
304 |
-
$
|
305 |
-
|
306 |
-
|
307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
|
309 |
-
|
|
|
|
|
|
|
310 |
}
|
311 |
|
312 |
-
return
|
313 |
}
|
314 |
|
315 |
/**
|
316 |
* Instantiate parser
|
317 |
*
|
318 |
-
* @param string $path
|
319 |
*
|
320 |
* @return \ScssPhp\ScssPhp\Parser
|
321 |
*/
|
@@ -328,11 +645,11 @@ class Compiler
|
|
328 |
// Otherwise, the CSS will be rendered as-is. It can even be extended!
|
329 |
$cssOnly = false;
|
330 |
|
331 |
-
if (substr($path,
|
332 |
$cssOnly = true;
|
333 |
}
|
334 |
|
335 |
-
$parser = new Parser($path, \count($this->sourceNames), $this->encoding, $this->cache, $cssOnly);
|
336 |
|
337 |
$this->sourceNames[] = $path;
|
338 |
$this->addParsedFile($path);
|
@@ -346,7 +663,7 @@ class Compiler
|
|
346 |
* @param array $target
|
347 |
* @param array $origin
|
348 |
*
|
349 |
-
* @return
|
350 |
*/
|
351 |
protected function isSelfExtend($target, $origin)
|
352 |
{
|
@@ -362,16 +679,14 @@ class Compiler
|
|
362 |
/**
|
363 |
* Push extends
|
364 |
*
|
365 |
-
* @param
|
366 |
* @param array $origin
|
367 |
* @param array|null $block
|
|
|
|
|
368 |
*/
|
369 |
protected function pushExtends($target, $origin, $block)
|
370 |
{
|
371 |
-
if ($this->isSelfExtend($target, $origin)) {
|
372 |
-
return;
|
373 |
-
}
|
374 |
-
|
375 |
$i = \count($this->extends);
|
376 |
$this->extends[] = [$target, $origin, $block];
|
377 |
|
@@ -387,14 +702,14 @@ class Compiler
|
|
387 |
/**
|
388 |
* Make output block
|
389 |
*
|
390 |
-
* @param string
|
391 |
-
* @param
|
392 |
*
|
393 |
* @return \ScssPhp\ScssPhp\Formatter\OutputBlock
|
394 |
*/
|
395 |
protected function makeOutputBlock($type, $selectors = null)
|
396 |
{
|
397 |
-
$out = new OutputBlock;
|
398 |
$out->type = $type;
|
399 |
$out->lines = [];
|
400 |
$out->children = [];
|
@@ -407,9 +722,9 @@ class Compiler
|
|
407 |
$out->sourceLine = $this->env->block->sourceLine;
|
408 |
$out->sourceColumn = $this->env->block->sourceColumn;
|
409 |
} else {
|
410 |
-
$out->sourceName
|
411 |
-
$out->sourceLine
|
412 |
-
$out->sourceColumn =
|
413 |
}
|
414 |
|
415 |
return $out;
|
@@ -419,18 +734,23 @@ class Compiler
|
|
419 |
* Compile root
|
420 |
*
|
421 |
* @param \ScssPhp\ScssPhp\Block $rootBlock
|
|
|
|
|
422 |
*/
|
423 |
protected function compileRoot(Block $rootBlock)
|
424 |
{
|
425 |
$this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT);
|
426 |
|
427 |
$this->compileChildrenNoReturn($rootBlock->children, $this->scope);
|
|
|
428 |
$this->flattenSelectors($this->scope);
|
429 |
$this->missingSelectors();
|
430 |
}
|
431 |
|
432 |
/**
|
433 |
* Report missing selectors
|
|
|
|
|
434 |
*/
|
435 |
protected function missingSelectors()
|
436 |
{
|
@@ -450,7 +770,7 @@ class Compiler
|
|
450 |
$origin = $this->collapseSelectors($origin);
|
451 |
|
452 |
$this->sourceLine = $block[Parser::SOURCE_LINE];
|
453 |
-
$this->
|
454 |
}
|
455 |
}
|
456 |
|
@@ -459,6 +779,8 @@ class Compiler
|
|
459 |
*
|
460 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
461 |
* @param string $parentKey
|
|
|
|
|
462 |
*/
|
463 |
protected function flattenSelectors(OutputBlock $block, $parentKey = null)
|
464 |
{
|
@@ -502,6 +824,7 @@ class Compiler
|
|
502 |
}
|
503 |
|
504 |
if ($placeholderSelector && 0 === \count($block->selectors) && null !== $parentKey) {
|
|
|
505 |
unset($block->parent->children[$parentKey]);
|
506 |
|
507 |
return;
|
@@ -514,7 +837,7 @@ class Compiler
|
|
514 |
}
|
515 |
|
516 |
/**
|
517 |
-
* Glue parts of :not( or :nth-child( ... that are in general
|
518 |
*
|
519 |
* @param array $parts
|
520 |
*
|
@@ -531,10 +854,11 @@ class Compiler
|
|
531 |
} else {
|
532 |
// a selector part finishing with a ) is the last part of a :not( or :nth-child(
|
533 |
// and need to be joined to this
|
534 |
-
if (
|
|
|
535 |
\strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false
|
536 |
) {
|
537 |
-
while (\count($new)>1 && substr($new[\count($new) - 1], -1) !== '(') {
|
538 |
$part = array_pop($new) . $part;
|
539 |
}
|
540 |
$new[\count($new) - 1] .= $part;
|
@@ -550,10 +874,12 @@ class Compiler
|
|
550 |
/**
|
551 |
* Match extends
|
552 |
*
|
553 |
-
* @param array
|
554 |
-
* @param array
|
555 |
-
* @param
|
556 |
-
* @param
|
|
|
|
|
557 |
*/
|
558 |
protected function matchExtends($selector, &$out, $from = 0, $initial = true)
|
559 |
{
|
@@ -598,7 +924,7 @@ class Compiler
|
|
598 |
}
|
599 |
}
|
600 |
|
601 |
-
if (\count($nonBreakableBefore)
|
602 |
$k--;
|
603 |
}
|
604 |
|
@@ -684,12 +1010,13 @@ class Compiler
|
|
684 |
* @param string $part
|
685 |
* @param array $matches
|
686 |
*
|
687 |
-
* @return
|
688 |
*/
|
689 |
protected function isPseudoSelector($part, &$matches)
|
690 |
{
|
691 |
-
if (
|
692 |
-
|
|
|
693 |
) {
|
694 |
return true;
|
695 |
}
|
@@ -706,6 +1033,8 @@ class Compiler
|
|
706 |
*
|
707 |
* @param array $out
|
708 |
* @param array $extended
|
|
|
|
|
709 |
*/
|
710 |
protected function pushOrMergeExtentedSelector(&$out, $extended)
|
711 |
{
|
@@ -713,7 +1042,8 @@ class Compiler
|
|
713 |
$single = reset($extended);
|
714 |
$part = reset($single);
|
715 |
|
716 |
-
if (
|
|
|
717 |
\in_array($matchesExtended[1], [ 'slotted' ])
|
718 |
) {
|
719 |
$prev = end($out);
|
@@ -723,11 +1053,12 @@ class Compiler
|
|
723 |
$single = reset($prev);
|
724 |
$part = reset($single);
|
725 |
|
726 |
-
if (
|
|
|
727 |
$matchesPrev[1] === $matchesExtended[1]
|
728 |
) {
|
729 |
$extended = explode($matchesExtended[1] . '(', $matchesExtended[0], 2);
|
730 |
-
$extended[1] = $matchesPrev[2] .
|
731 |
$extended = implode($matchesExtended[1] . '(', $extended);
|
732 |
$extended = [ [ $extended ]];
|
733 |
array_pop($out);
|
@@ -741,11 +1072,11 @@ class Compiler
|
|
741 |
/**
|
742 |
* Match extends single
|
743 |
*
|
744 |
-
* @param array
|
745 |
-
* @param array
|
746 |
-
* @param
|
747 |
*
|
748 |
-
* @return
|
749 |
*/
|
750 |
protected function matchExtendsSingle($rawSingle, &$outOrigin, $initial = true)
|
751 |
{
|
@@ -787,14 +1118,15 @@ class Compiler
|
|
787 |
}
|
788 |
}
|
789 |
|
790 |
-
if (
|
|
|
791 |
$this->isPseudoSelector($part, $matches) &&
|
792 |
! \in_array($matches[1], [ 'not' ])
|
793 |
) {
|
794 |
$buffer = $matches[2];
|
795 |
$parser = $this->parserFactory(__METHOD__);
|
796 |
|
797 |
-
if ($parser->parseSelector($buffer, $subSelectors)) {
|
798 |
foreach ($subSelectors as $ksub => $subSelector) {
|
799 |
$subExtended = [];
|
800 |
$this->matchExtends($subSelector, $subExtended, 0, false);
|
@@ -809,7 +1141,7 @@ class Compiler
|
|
809 |
|
810 |
$subSelectorsExtended = implode(', ', $subSelectorsExtended);
|
811 |
$singleExtended = $single;
|
812 |
-
$singleExtended[$k] = str_replace(
|
813 |
$outOrigin[] = [ $singleExtended ];
|
814 |
$found = true;
|
815 |
}
|
@@ -834,14 +1166,15 @@ class Compiler
|
|
834 |
|
835 |
foreach ($origin as $j => $new) {
|
836 |
// prevent infinite loop when target extends itself
|
837 |
-
if ($this->isSelfExtend($single, $origin)) {
|
838 |
return false;
|
839 |
}
|
840 |
|
841 |
$replacement = end($new);
|
842 |
|
843 |
// Extending a decorated tag with another tag is not possible.
|
844 |
-
if (
|
|
|
845 |
preg_match('/^[a-z0-9]+$/i', $replacement[0])
|
846 |
) {
|
847 |
unset($origin[$j]);
|
@@ -912,11 +1245,13 @@ class Compiler
|
|
912 |
$wasTag = false;
|
913 |
$pseudo = [];
|
914 |
|
915 |
-
while (\count($other) && strpos(end($other), ':')===0) {
|
916 |
array_unshift($pseudo, array_pop($other));
|
917 |
}
|
918 |
|
919 |
foreach ([array_reverse($base), array_reverse($other)] as $single) {
|
|
|
|
|
920 |
foreach ($single as $part) {
|
921 |
if (preg_match('/^[\[:]/', $part)) {
|
922 |
$out[] = $part;
|
@@ -924,14 +1259,15 @@ class Compiler
|
|
924 |
} elseif (preg_match('/^[\.#]/', $part)) {
|
925 |
array_unshift($out, $part);
|
926 |
$wasTag = false;
|
927 |
-
} elseif (preg_match('/^[^_-]/', $part)) {
|
928 |
$tag[] = $part;
|
929 |
$wasTag = true;
|
930 |
} elseif ($wasTag) {
|
931 |
$tag[\count($tag) - 1] .= $part;
|
932 |
} else {
|
933 |
-
$out
|
934 |
}
|
|
|
935 |
}
|
936 |
}
|
937 |
|
@@ -950,14 +1286,18 @@ class Compiler
|
|
950 |
* Compile media
|
951 |
*
|
952 |
* @param \ScssPhp\ScssPhp\Block $media
|
|
|
|
|
953 |
*/
|
954 |
protected function compileMedia(Block $media)
|
955 |
{
|
|
|
956 |
$this->pushEnv($media);
|
957 |
|
958 |
$mediaQueries = $this->compileMediaQuery($this->multiplyMedia($this->env));
|
959 |
|
960 |
-
if (! empty($mediaQueries)
|
|
|
961 |
$previousScope = $this->scope;
|
962 |
$parentScope = $this->mediaParent($this->scope);
|
963 |
|
@@ -974,7 +1314,8 @@ class Compiler
|
|
974 |
foreach ($media->children as $child) {
|
975 |
$type = $child[0];
|
976 |
|
977 |
-
if (
|
|
|
978 |
$type !== Type::T_MEDIA &&
|
979 |
$type !== Type::T_DIRECTIVE &&
|
980 |
$type !== Type::T_IMPORT
|
@@ -985,7 +1326,7 @@ class Compiler
|
|
985 |
}
|
986 |
|
987 |
if ($needsWrap) {
|
988 |
-
$wrapped = new Block;
|
989 |
$wrapped->sourceName = $media->sourceName;
|
990 |
$wrapped->sourceIndex = $media->sourceIndex;
|
991 |
$wrapped->sourceLine = $media->sourceLine;
|
@@ -996,30 +1337,6 @@ class Compiler
|
|
996 |
$wrapped->children = $media->children;
|
997 |
|
998 |
$media->children = [[Type::T_BLOCK, $wrapped]];
|
999 |
-
|
1000 |
-
if (isset($this->lineNumberStyle)) {
|
1001 |
-
$annotation = $this->makeOutputBlock(Type::T_COMMENT);
|
1002 |
-
$annotation->depth = 0;
|
1003 |
-
|
1004 |
-
$file = $this->sourceNames[$media->sourceIndex];
|
1005 |
-
$line = $media->sourceLine;
|
1006 |
-
|
1007 |
-
switch ($this->lineNumberStyle) {
|
1008 |
-
case static::LINE_COMMENTS:
|
1009 |
-
$annotation->lines[] = '/* line ' . $line
|
1010 |
-
. ($file ? ', ' . $file : '')
|
1011 |
-
. ' */';
|
1012 |
-
break;
|
1013 |
-
|
1014 |
-
case static::DEBUG_INFO:
|
1015 |
-
$annotation->lines[] = '@media -sass-debug-info{'
|
1016 |
-
. ($file ? 'filename{font-family:"' . $file . '"}' : '')
|
1017 |
-
. 'line{font-family:' . $line . '}}';
|
1018 |
-
break;
|
1019 |
-
}
|
1020 |
-
|
1021 |
-
$this->scope->children[] = $annotation;
|
1022 |
-
}
|
1023 |
}
|
1024 |
|
1025 |
$this->compileChildrenNoReturn($media->children, $this->scope);
|
@@ -1053,20 +1370,33 @@ class Compiler
|
|
1053 |
/**
|
1054 |
* Compile directive
|
1055 |
*
|
1056 |
-
* @param
|
1057 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
|
|
|
|
1058 |
*/
|
1059 |
protected function compileDirective($directive, OutputBlock $out)
|
1060 |
{
|
1061 |
if (\is_array($directive)) {
|
1062 |
-
$
|
|
|
1063 |
|
1064 |
if (! empty($directive[1])) {
|
1065 |
$s .= ' ' . $this->compileValue($directive[1]);
|
1066 |
}
|
|
|
|
|
|
|
|
|
|
|
1067 |
|
1068 |
-
|
|
|
|
|
|
|
|
|
1069 |
} else {
|
|
|
1070 |
$s = '@' . $directive->name;
|
1071 |
|
1072 |
if (! empty($directive->value)) {
|
@@ -1081,20 +1411,39 @@ class Compiler
|
|
1081 |
}
|
1082 |
}
|
1083 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1084 |
/**
|
1085 |
* Compile at-root
|
1086 |
*
|
1087 |
* @param \ScssPhp\ScssPhp\Block $block
|
|
|
|
|
1088 |
*/
|
1089 |
protected function compileAtRoot(Block $block)
|
1090 |
{
|
|
|
1091 |
$env = $this->pushEnv($block);
|
1092 |
$envs = $this->compactEnv($env);
|
1093 |
list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null);
|
1094 |
|
1095 |
// wrap inline selector
|
1096 |
if ($block->selector) {
|
1097 |
-
$wrapped = new Block;
|
1098 |
$wrapped->sourceName = $block->sourceName;
|
1099 |
$wrapped->sourceIndex = $block->sourceIndex;
|
1100 |
$wrapped->sourceLine = $block->sourceLine;
|
@@ -1110,8 +1459,11 @@ class Compiler
|
|
1110 |
}
|
1111 |
|
1112 |
$selfParent = $block->selfParent;
|
|
|
1113 |
|
1114 |
-
if (
|
|
|
|
|
1115 |
isset($block->parent->selectors) && $block->parent->selectors
|
1116 |
) {
|
1117 |
$selfParent = $block->parent;
|
@@ -1119,13 +1471,15 @@ class Compiler
|
|
1119 |
|
1120 |
$this->env = $this->filterWithWithout($envs, $with, $without);
|
1121 |
|
|
|
1122 |
$saveScope = $this->scope;
|
1123 |
$this->scope = $this->filterScopeWithWithout($saveScope, $with, $without);
|
1124 |
|
1125 |
// propagate selfParent to the children where they still can be useful
|
1126 |
$this->compileChildrenNoReturn($block->children, $this->scope, $selfParent);
|
1127 |
|
1128 |
-
$this->scope
|
|
|
1129 |
$this->scope = $saveScope;
|
1130 |
$this->env = $this->extractEnv($envs);
|
1131 |
|
@@ -1133,25 +1487,26 @@ class Compiler
|
|
1133 |
}
|
1134 |
|
1135 |
/**
|
1136 |
-
* Filter at-root scope depending
|
1137 |
*
|
1138 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1139 |
* @param array $with
|
1140 |
* @param array $without
|
1141 |
*
|
1142 |
-
* @return
|
1143 |
*/
|
1144 |
protected function filterScopeWithWithout($scope, $with, $without)
|
1145 |
{
|
1146 |
$filteredScopes = [];
|
1147 |
$childStash = [];
|
1148 |
|
1149 |
-
if ($scope->type ===
|
1150 |
return $scope;
|
1151 |
}
|
|
|
1152 |
|
1153 |
// start from the root
|
1154 |
-
while ($scope->parent && $scope->parent->type !==
|
1155 |
array_unshift($childStash, $scope);
|
1156 |
$scope = $scope->parent;
|
1157 |
}
|
@@ -1212,11 +1567,11 @@ class Compiler
|
|
1212 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1213 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $previousScope
|
1214 |
*
|
1215 |
-
* @return
|
1216 |
*/
|
1217 |
protected function completeScope($scope, $previousScope)
|
1218 |
{
|
1219 |
-
if (! $scope->type &&
|
1220 |
$scope->selectors = $this->findScopeSelectors($previousScope, $scope->depth);
|
1221 |
}
|
1222 |
|
@@ -1233,7 +1588,7 @@ class Compiler
|
|
1233 |
* Find a selector by the depth node in the scope
|
1234 |
*
|
1235 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1236 |
-
* @param
|
1237 |
*
|
1238 |
* @return array
|
1239 |
*/
|
@@ -1257,9 +1612,11 @@ class Compiler
|
|
1257 |
/**
|
1258 |
* Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later
|
1259 |
*
|
1260 |
-
* @param array $withCondition
|
1261 |
*
|
1262 |
* @return array
|
|
|
|
|
1263 |
*/
|
1264 |
protected function compileWith($withCondition)
|
1265 |
{
|
@@ -1268,9 +1625,21 @@ class Compiler
|
|
1268 |
$without = ['rule' => true];
|
1269 |
|
1270 |
if ($withCondition) {
|
1271 |
-
if ($
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1272 |
$without = []; // cancel the default
|
1273 |
-
$list = $this->coerceList($
|
1274 |
|
1275 |
foreach ($list[2] as $item) {
|
1276 |
$keyword = $this->compileStringContent($this->coerceString($item));
|
@@ -1279,9 +1648,10 @@ class Compiler
|
|
1279 |
}
|
1280 |
}
|
1281 |
|
1282 |
-
|
|
|
1283 |
$without = []; // cancel the default
|
1284 |
-
$list = $this->coerceList($
|
1285 |
|
1286 |
foreach ($list[2] as $item) {
|
1287 |
$keyword = $this->compileStringContent($this->coerceString($item));
|
@@ -1297,11 +1667,13 @@ class Compiler
|
|
1297 |
/**
|
1298 |
* Filter env stack
|
1299 |
*
|
1300 |
-
* @param
|
1301 |
* @param array $with
|
1302 |
* @param array $without
|
1303 |
*
|
1304 |
-
* @return
|
|
|
|
|
1305 |
*/
|
1306 |
protected function filterWithWithout($envs, $with, $without)
|
1307 |
{
|
@@ -1329,7 +1701,7 @@ class Compiler
|
|
1329 |
* @param array $with
|
1330 |
* @param array $without
|
1331 |
*
|
1332 |
-
* @return
|
1333 |
*/
|
1334 |
protected function isWith($block, $with, $without)
|
1335 |
{
|
@@ -1339,8 +1711,9 @@ class Compiler
|
|
1339 |
}
|
1340 |
|
1341 |
if ($block->type === Type::T_DIRECTIVE) {
|
|
|
1342 |
if (isset($block->name)) {
|
1343 |
-
return $this->testWithWithout($block->name, $with, $without);
|
1344 |
} elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) {
|
1345 |
return $this->testWithWithout($m[1], $with, $without);
|
1346 |
} else {
|
@@ -1356,7 +1729,7 @@ class Compiler
|
|
1356 |
$s = reset($s);
|
1357 |
}
|
1358 |
|
1359 |
-
if (\is_object($s) && $s instanceof
|
1360 |
return $this->testWithWithout('keyframes', $with, $without);
|
1361 |
}
|
1362 |
}
|
@@ -1374,12 +1747,11 @@ class Compiler
|
|
1374 |
* @param array $with
|
1375 |
* @param array $without
|
1376 |
*
|
1377 |
-
* @return
|
1378 |
* true if the block should be kept, false to reject
|
1379 |
*/
|
1380 |
protected function testWithWithout($what, $with, $without)
|
1381 |
{
|
1382 |
-
|
1383 |
// if without, reject only if in the list (or 'all' is in the list)
|
1384 |
if (\count($without)) {
|
1385 |
return (isset($without[$what]) || isset($without['all'])) ? false : true;
|
@@ -1394,7 +1766,9 @@ class Compiler
|
|
1394 |
* Compile keyframe block
|
1395 |
*
|
1396 |
* @param \ScssPhp\ScssPhp\Block $block
|
1397 |
-
* @param
|
|
|
|
|
1398 |
*/
|
1399 |
protected function compileKeyframeBlock(Block $block, $selectors)
|
1400 |
{
|
@@ -1408,10 +1782,12 @@ class Compiler
|
|
1408 |
|
1409 |
$this->scope = $this->makeOutputBlock($block->type, $selectors);
|
1410 |
$this->scope->depth = 1;
|
|
|
1411 |
$this->scope->parent->children[] = $this->scope;
|
1412 |
|
1413 |
$this->compileChildrenNoReturn($block->children, $this->scope);
|
1414 |
|
|
|
1415 |
$this->scope = $this->scope->parent;
|
1416 |
$this->env = $this->extractEnv($envs);
|
1417 |
|
@@ -1423,9 +1799,12 @@ class Compiler
|
|
1423 |
*
|
1424 |
* @param \ScssPhp\ScssPhp\Block $block
|
1425 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
|
|
|
|
1426 |
*/
|
1427 |
protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out)
|
1428 |
{
|
|
|
1429 |
$prefix = $this->compileValue($block->prefix) . '-';
|
1430 |
|
1431 |
$nested = $this->makeOutputBlock($block->type);
|
@@ -1444,6 +1823,7 @@ class Compiler
|
|
1444 |
break;
|
1445 |
|
1446 |
case Type::T_NESTED_PROPERTY:
|
|
|
1447 |
array_unshift($child[1]->prefix[2], $prefix);
|
1448 |
break;
|
1449 |
}
|
@@ -1456,18 +1836,21 @@ class Compiler
|
|
1456 |
* Compile nested block
|
1457 |
*
|
1458 |
* @param \ScssPhp\ScssPhp\Block $block
|
1459 |
-
* @param
|
|
|
|
|
1460 |
*/
|
1461 |
protected function compileNestedBlock(Block $block, $selectors)
|
1462 |
{
|
1463 |
$this->pushEnv($block);
|
1464 |
|
1465 |
$this->scope = $this->makeOutputBlock($block->type, $selectors);
|
|
|
1466 |
$this->scope->parent->children[] = $this->scope;
|
1467 |
|
1468 |
// wrap assign children in a block
|
1469 |
// except for @font-face
|
1470 |
-
if (
|
1471 |
// need wrapping?
|
1472 |
$needWrapping = false;
|
1473 |
|
@@ -1479,7 +1862,7 @@ class Compiler
|
|
1479 |
}
|
1480 |
|
1481 |
if ($needWrapping) {
|
1482 |
-
$wrapped = new Block;
|
1483 |
$wrapped->sourceName = $block->sourceName;
|
1484 |
$wrapped->sourceIndex = $block->sourceIndex;
|
1485 |
$wrapped->sourceLine = $block->sourceLine;
|
@@ -1496,6 +1879,7 @@ class Compiler
|
|
1496 |
|
1497 |
$this->compileChildrenNoReturn($block->children, $this->scope);
|
1498 |
|
|
|
1499 |
$this->scope = $this->scope->parent;
|
1500 |
|
1501 |
$this->popEnv();
|
@@ -1518,39 +1902,19 @@ class Compiler
|
|
1518 |
* @see Compiler::compileChild()
|
1519 |
*
|
1520 |
* @param \ScssPhp\ScssPhp\Block $block
|
|
|
|
|
1521 |
*/
|
1522 |
protected function compileBlock(Block $block)
|
1523 |
{
|
1524 |
$env = $this->pushEnv($block);
|
|
|
1525 |
$env->selectors = $this->evalSelectors($block->selectors);
|
1526 |
|
1527 |
$out = $this->makeOutputBlock(null);
|
1528 |
|
1529 |
-
|
1530 |
-
|
1531 |
-
$annotation->depth = 0;
|
1532 |
-
|
1533 |
-
$file = $this->sourceNames[$block->sourceIndex];
|
1534 |
-
$line = $block->sourceLine;
|
1535 |
-
|
1536 |
-
switch ($this->lineNumberStyle) {
|
1537 |
-
case static::LINE_COMMENTS:
|
1538 |
-
$annotation->lines[] = '/* line ' . $line
|
1539 |
-
. ($file ? ', ' . $file : '')
|
1540 |
-
. ' */';
|
1541 |
-
break;
|
1542 |
-
|
1543 |
-
case static::DEBUG_INFO:
|
1544 |
-
$annotation->lines[] = '@media -sass-debug-info{'
|
1545 |
-
. ($file ? 'filename{font-family:"' . $file . '"}' : '')
|
1546 |
-
. 'line{font-family:' . $line . '}}';
|
1547 |
-
break;
|
1548 |
-
}
|
1549 |
-
|
1550 |
-
$this->scope->children[] = $annotation;
|
1551 |
-
}
|
1552 |
-
|
1553 |
-
$this->scope->children[] = $out;
|
1554 |
|
1555 |
if (\count($block->children)) {
|
1556 |
$out->selectors = $this->multiplySelectors($env, $block->selfParent);
|
@@ -1567,6 +1931,7 @@ class Compiler
|
|
1567 |
|
1568 |
// and revert for the following children of the same block
|
1569 |
if ($selfParentSelectors) {
|
|
|
1570 |
$block->selfParent->selectors = $selfParentSelectors;
|
1571 |
}
|
1572 |
}
|
@@ -1578,10 +1943,10 @@ class Compiler
|
|
1578 |
/**
|
1579 |
* Compile the value of a comment that can have interpolation
|
1580 |
*
|
1581 |
-
* @param array
|
1582 |
-
* @param
|
1583 |
*
|
1584 |
-
* @return
|
1585 |
*/
|
1586 |
protected function compileCommentValue($value, $pushEnv = false)
|
1587 |
{
|
@@ -1592,17 +1957,16 @@ class Compiler
|
|
1592 |
$this->pushEnv();
|
1593 |
}
|
1594 |
|
1595 |
-
$ignoreCallStackMessage = $this->ignoreCallStackMessage;
|
1596 |
-
$this->ignoreCallStackMessage = true;
|
1597 |
-
|
1598 |
try {
|
1599 |
$c = $this->compileValue($value[2]);
|
1600 |
-
} catch (
|
|
|
|
|
|
|
|
|
1601 |
// ignore error in comment compilation which are only interpolation
|
1602 |
}
|
1603 |
|
1604 |
-
$this->ignoreCallStackMessage = $ignoreCallStackMessage;
|
1605 |
-
|
1606 |
if ($pushEnv) {
|
1607 |
$this->popEnv();
|
1608 |
}
|
@@ -1615,12 +1979,15 @@ class Compiler
|
|
1615 |
* Compile root level comment
|
1616 |
*
|
1617 |
* @param array $block
|
|
|
|
|
1618 |
*/
|
1619 |
protected function compileComment($block)
|
1620 |
{
|
1621 |
$out = $this->makeOutputBlock(Type::T_COMMENT);
|
1622 |
$out->lines[] = $this->compileCommentValue($block, true);
|
1623 |
|
|
|
1624 |
$this->scope->children[] = $out;
|
1625 |
}
|
1626 |
|
@@ -1635,15 +2002,25 @@ class Compiler
|
|
1635 |
{
|
1636 |
$this->shouldEvaluate = false;
|
1637 |
|
1638 |
-
$
|
|
|
|
|
|
|
|
|
1639 |
|
1640 |
// after evaluating interpolates, we might need a second pass
|
1641 |
if ($this->shouldEvaluate) {
|
1642 |
-
$selectors = $this->
|
1643 |
$buffer = $this->collapseSelectors($selectors);
|
1644 |
$parser = $this->parserFactory(__METHOD__);
|
1645 |
|
1646 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1647 |
$selectors = array_map([$this, 'evalSelector'], $newSelectors);
|
1648 |
}
|
1649 |
}
|
@@ -1657,6 +2034,8 @@ class Compiler
|
|
1657 |
* @param array $selector
|
1658 |
*
|
1659 |
* @return array
|
|
|
|
|
1660 |
*/
|
1661 |
protected function evalSelector($selector)
|
1662 |
{
|
@@ -1669,6 +2048,8 @@ class Compiler
|
|
1669 |
* @param array $part
|
1670 |
*
|
1671 |
* @return array
|
|
|
|
|
1672 |
*/
|
1673 |
protected function evalSelectorPart($part)
|
1674 |
{
|
@@ -1676,13 +2057,14 @@ class Compiler
|
|
1676 |
if (\is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) {
|
1677 |
$p = $this->compileValue($p);
|
1678 |
|
1679 |
-
// force re-evaluation
|
1680 |
-
if (
|
1681 |
$this->shouldEvaluate = true;
|
1682 |
}
|
1683 |
-
} elseif (
|
1684 |
-
($
|
1685 |
-
|
|
|
1686 |
) {
|
1687 |
$p = substr($p, 1, -1);
|
1688 |
}
|
@@ -1694,14 +2076,44 @@ class Compiler
|
|
1694 |
/**
|
1695 |
* Collapse selectors
|
1696 |
*
|
1697 |
-
* @param array
|
1698 |
-
* @param boolean $selectorFormat
|
1699 |
-
* if false return a collapsed string
|
1700 |
-
* if true return an array description of a structured selector
|
1701 |
*
|
1702 |
* @return string
|
1703 |
*/
|
1704 |
-
protected function collapseSelectors($selectors
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1705 |
{
|
1706 |
$parts = [];
|
1707 |
|
@@ -1719,7 +2131,7 @@ class Compiler
|
|
1719 |
}
|
1720 |
);
|
1721 |
|
1722 |
-
if ($
|
1723 |
if (\count($output)) {
|
1724 |
$output[\count($output) - 1] .= ' ' . $compound;
|
1725 |
} else {
|
@@ -1735,43 +2147,36 @@ class Compiler
|
|
1735 |
}
|
1736 |
}
|
1737 |
|
1738 |
-
|
1739 |
-
|
1740 |
-
$o = [Type::T_STRING, '', [$o]];
|
1741 |
-
}
|
1742 |
-
|
1743 |
-
$output = [Type::T_LIST, ' ', $output];
|
1744 |
-
} else {
|
1745 |
-
$output = implode(' ', $output);
|
1746 |
}
|
1747 |
|
1748 |
-
$parts[] = $output;
|
1749 |
-
}
|
1750 |
-
|
1751 |
-
if ($selectorFormat) {
|
1752 |
-
$parts = [Type::T_LIST, ',', $parts];
|
1753 |
-
} else {
|
1754 |
-
$parts = implode(', ', $parts);
|
1755 |
}
|
1756 |
|
1757 |
-
return $parts;
|
1758 |
}
|
1759 |
|
1760 |
/**
|
1761 |
* Parse down the selector and revert [self] to "&" before a reparsing
|
1762 |
*
|
1763 |
-
* @param array
|
|
|
1764 |
*
|
1765 |
* @return array
|
1766 |
*/
|
1767 |
-
protected function
|
1768 |
{
|
1769 |
foreach ($selectors as &$part) {
|
1770 |
if (\is_array($part)) {
|
1771 |
if ($part === [Type::T_SELF]) {
|
1772 |
-
$
|
|
|
|
|
|
|
|
|
1773 |
} else {
|
1774 |
-
$part = $this->
|
1775 |
}
|
1776 |
}
|
1777 |
}
|
@@ -1791,7 +2196,8 @@ class Compiler
|
|
1791 |
$joined = [];
|
1792 |
|
1793 |
foreach ($single as $part) {
|
1794 |
-
if (
|
|
|
1795 |
! \is_string($part) ||
|
1796 |
preg_match('/[\[.:#%]/', $part)
|
1797 |
) {
|
@@ -1864,7 +2270,7 @@ class Compiler
|
|
1864 |
*
|
1865 |
* @param array $selector
|
1866 |
*
|
1867 |
-
* @return
|
1868 |
*/
|
1869 |
protected function hasSelectorPlaceholder($selector)
|
1870 |
{
|
@@ -1883,6 +2289,11 @@ class Compiler
|
|
1883 |
return false;
|
1884 |
}
|
1885 |
|
|
|
|
|
|
|
|
|
|
|
1886 |
protected function pushCallStack($name = '')
|
1887 |
{
|
1888 |
$this->callStack[] = [
|
@@ -1896,12 +2307,15 @@ class Compiler
|
|
1896 |
if (\count($this->callStack) > 25000) {
|
1897 |
// not displayed but you can var_dump it to deep debug
|
1898 |
$msg = $this->callStackMessage(true, 100);
|
1899 |
-
$msg =
|
1900 |
|
1901 |
-
$this->
|
1902 |
}
|
1903 |
}
|
1904 |
|
|
|
|
|
|
|
1905 |
protected function popCallStack()
|
1906 |
{
|
1907 |
array_pop($this->callStack);
|
@@ -1914,7 +2328,7 @@ class Compiler
|
|
1914 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
1915 |
* @param string $traceName
|
1916 |
*
|
1917 |
-
* @return array|null
|
1918 |
*/
|
1919 |
protected function compileChildren($stms, OutputBlock $out, $traceName = '')
|
1920 |
{
|
@@ -1936,13 +2350,15 @@ class Compiler
|
|
1936 |
}
|
1937 |
|
1938 |
/**
|
1939 |
-
* Compile children and throw exception if unexpected
|
1940 |
*
|
1941 |
* @param array $stms
|
1942 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
1943 |
* @param \ScssPhp\ScssPhp\Block $selfParent
|
1944 |
* @param string $traceName
|
1945 |
*
|
|
|
|
|
1946 |
* @throws \Exception
|
1947 |
*/
|
1948 |
protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent = null, $traceName = '')
|
@@ -1954,7 +2370,7 @@ class Compiler
|
|
1954 |
$stm[1]->selfParent = $selfParent;
|
1955 |
$ret = $this->compileChild($stm, $out);
|
1956 |
$stm[1]->selfParent = null;
|
1957 |
-
} elseif ($selfParent && \in_array($stm[0], [
|
1958 |
$stm['selfParent'] = $selfParent;
|
1959 |
$ret = $this->compileChild($stm, $out);
|
1960 |
unset($stm['selfParent']);
|
@@ -1963,10 +2379,7 @@ class Compiler
|
|
1963 |
}
|
1964 |
|
1965 |
if (isset($ret)) {
|
1966 |
-
$this->
|
1967 |
-
$this->popCallStack();
|
1968 |
-
|
1969 |
-
return;
|
1970 |
}
|
1971 |
}
|
1972 |
|
@@ -1975,7 +2388,7 @@ class Compiler
|
|
1975 |
|
1976 |
|
1977 |
/**
|
1978 |
-
* evaluate media query : compile internal value keeping the structure
|
1979 |
*
|
1980 |
* @param array $queryList
|
1981 |
*
|
@@ -1996,7 +2409,8 @@ class Compiler
|
|
1996 |
|
1997 |
// the parser had no mean to know if media type or expression if it was an interpolation
|
1998 |
// so you need to reparse if the T_MEDIA_TYPE looks like anything else a media type
|
1999 |
-
if (
|
|
|
2000 |
(strpos($value, '(') !== false ||
|
2001 |
strpos($value, ')') !== false ||
|
2002 |
strpos($value, ':') !== false ||
|
@@ -2017,7 +2431,7 @@ class Compiler
|
|
2017 |
$queryString = $this->compileMediaQuery([$queryList[$kql]]);
|
2018 |
$queryString = reset($queryString);
|
2019 |
|
2020 |
-
if (strpos($queryString, '@media ') === 0) {
|
2021 |
$queryString = substr($queryString, 7);
|
2022 |
$queries = [];
|
2023 |
|
@@ -2044,14 +2458,14 @@ class Compiler
|
|
2044 |
*
|
2045 |
* @param array $queryList
|
2046 |
*
|
2047 |
-
* @return
|
2048 |
*/
|
2049 |
protected function compileMediaQuery($queryList)
|
2050 |
{
|
2051 |
$start = '@media ';
|
2052 |
$default = trim($start);
|
2053 |
$out = [];
|
2054 |
-
$current =
|
2055 |
|
2056 |
foreach ($queryList as $query) {
|
2057 |
$type = null;
|
@@ -2090,7 +2504,7 @@ class Compiler
|
|
2090 |
$out[] = $start . $current;
|
2091 |
}
|
2092 |
|
2093 |
-
$current =
|
2094 |
$type = null;
|
2095 |
$parts = [];
|
2096 |
}
|
@@ -2265,7 +2679,7 @@ class Compiler
|
|
2265 |
}
|
2266 |
|
2267 |
// t1 == t2, neither m1 nor m2 are "not"
|
2268 |
-
return [empty($m1)? $m2 : $m1, $t1];
|
2269 |
}
|
2270 |
|
2271 |
/**
|
@@ -2273,25 +2687,27 @@ class Compiler
|
|
2273 |
*
|
2274 |
* @param array $rawPath
|
2275 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2276 |
-
* @param
|
2277 |
*
|
2278 |
-
* @return
|
2279 |
*/
|
2280 |
protected function compileImport($rawPath, OutputBlock $out, $once = false)
|
2281 |
{
|
2282 |
if ($rawPath[0] === Type::T_STRING) {
|
2283 |
$path = $this->compileStringContent($rawPath);
|
2284 |
|
2285 |
-
if ($path = $this->findImport($path)) {
|
2286 |
-
|
2287 |
-
|
2288 |
-
|
|
|
|
|
2289 |
}
|
2290 |
|
2291 |
return true;
|
2292 |
}
|
2293 |
|
2294 |
-
$this->appendRootDirective('@import ' . $this->
|
2295 |
|
2296 |
return false;
|
2297 |
}
|
@@ -2304,7 +2720,7 @@ class Compiler
|
|
2304 |
|
2305 |
foreach ($rawPath[2] as $path) {
|
2306 |
if ($path[0] !== Type::T_STRING) {
|
2307 |
-
$this->appendRootDirective('@import ' . $this->
|
2308 |
|
2309 |
return false;
|
2310 |
}
|
@@ -2317,19 +2733,68 @@ class Compiler
|
|
2317 |
return true;
|
2318 |
}
|
2319 |
|
2320 |
-
$this->appendRootDirective('@import ' . $this->
|
2321 |
|
2322 |
return false;
|
2323 |
}
|
2324 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2325 |
|
2326 |
/**
|
2327 |
* Append a root directive like @import or @charset as near as the possible from the source code
|
2328 |
* (keeping before comments, @import and @charset coming before in the source code)
|
2329 |
*
|
2330 |
-
* @param string
|
2331 |
-
* @param
|
2332 |
-
* @param array
|
|
|
|
|
2333 |
*/
|
2334 |
protected function appendRootDirective($line, $out, $allowed = [Type::T_COMMENT])
|
2335 |
{
|
@@ -2359,7 +2824,7 @@ class Compiler
|
|
2359 |
// insert the directive as a comment
|
2360 |
$child = $this->makeOutputBlock(Type::T_COMMENT);
|
2361 |
$child->lines[] = $line;
|
2362 |
-
$child->sourceName = $this->sourceNames[$this->sourceIndex];
|
2363 |
$child->sourceLine = $this->sourceLine;
|
2364 |
$child->sourceColumn = $this->sourceColumn;
|
2365 |
|
@@ -2377,25 +2842,20 @@ class Compiler
|
|
2377 |
*
|
2378 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2379 |
* @param string $type
|
2380 |
-
* @param string
|
|
|
|
|
2381 |
*/
|
2382 |
protected function appendOutputLine(OutputBlock $out, $type, $line)
|
2383 |
{
|
2384 |
$outWrite = &$out;
|
2385 |
|
2386 |
-
if ($type === Type::T_COMMENT) {
|
2387 |
-
$parent = $out->parent;
|
2388 |
-
|
2389 |
-
if (end($parent->children) !== $out) {
|
2390 |
-
$outWrite = &$parent->children[\count($parent->children) - 1];
|
2391 |
-
}
|
2392 |
-
}
|
2393 |
-
|
2394 |
// check if it's a flat output or not
|
2395 |
if (\count($out->children)) {
|
2396 |
$lastChild = &$out->children[\count($out->children) - 1];
|
2397 |
|
2398 |
-
if (
|
|
|
2399 |
\is_null($lastChild->selectors) &&
|
2400 |
! \count($lastChild->children)
|
2401 |
) {
|
@@ -2419,7 +2879,7 @@ class Compiler
|
|
2419 |
* @param array $child
|
2420 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2421 |
*
|
2422 |
-
* @return array
|
2423 |
*/
|
2424 |
protected function compileChild($child, OutputBlock $out)
|
2425 |
{
|
@@ -2427,18 +2887,19 @@ class Compiler
|
|
2427 |
$this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null;
|
2428 |
$this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1;
|
2429 |
$this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1;
|
2430 |
-
} elseif (\is_array($child) && isset($child[1]->sourceLine)) {
|
2431 |
$this->sourceIndex = $child[1]->sourceIndex;
|
2432 |
$this->sourceLine = $child[1]->sourceLine;
|
2433 |
$this->sourceColumn = $child[1]->sourceColumn;
|
2434 |
} elseif (! empty($out->sourceLine) && ! empty($out->sourceName)) {
|
2435 |
$this->sourceLine = $out->sourceLine;
|
2436 |
-
$
|
2437 |
$this->sourceColumn = $out->sourceColumn;
|
2438 |
|
2439 |
-
if ($
|
2440 |
-
$
|
2441 |
}
|
|
|
2442 |
}
|
2443 |
|
2444 |
switch ($child[0]) {
|
@@ -2471,10 +2932,6 @@ class Compiler
|
|
2471 |
break;
|
2472 |
|
2473 |
case Type::T_CHARSET:
|
2474 |
-
if (! $this->charsetSeen) {
|
2475 |
-
$this->charsetSeen = true;
|
2476 |
-
$this->appendRootDirective('@charset ' . $this->compileValue($child[1]) . ';', $out);
|
2477 |
-
}
|
2478 |
break;
|
2479 |
|
2480 |
case Type::T_CUSTOM_PROPERTY:
|
@@ -2547,7 +3004,7 @@ class Compiler
|
|
2547 |
break;
|
2548 |
}
|
2549 |
|
2550 |
-
if ($compiledName === 'font'
|
2551 |
// this is the case if more than one font is given: example: "font: 400 1em/1.3 arial,helvetica"
|
2552 |
// we need to handle the first list element
|
2553 |
$shorthandValue=&$value[2][0];
|
@@ -2563,7 +3020,7 @@ class Compiler
|
|
2563 |
$divider = $this->reduce($divider, true);
|
2564 |
}
|
2565 |
|
2566 |
-
if (\intval($divider->
|
2567 |
$revert = false;
|
2568 |
}
|
2569 |
}
|
@@ -2576,9 +3033,10 @@ class Compiler
|
|
2576 |
if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') {
|
2577 |
if ($maxShorthandDividers > 0) {
|
2578 |
$revert = true;
|
|
|
2579 |
// if the list of values is too long, this has to be a shorthand,
|
2580 |
// otherwise it could be a real division
|
2581 |
-
if (\is_null($maxListElements)
|
2582 |
if ($shorthandDividerNeedsUnit) {
|
2583 |
$divider = $item[3];
|
2584 |
|
@@ -2586,7 +3044,7 @@ class Compiler
|
|
2586 |
$divider = $this->reduce($divider, true);
|
2587 |
}
|
2588 |
|
2589 |
-
if (\intval($divider->
|
2590 |
$revert = false;
|
2591 |
}
|
2592 |
}
|
@@ -2637,6 +3095,7 @@ class Compiler
|
|
2637 |
case Type::T_MIXIN:
|
2638 |
case Type::T_FUNCTION:
|
2639 |
list(, $block) = $child;
|
|
|
2640 |
// the block need to be able to go up to it's parent env to resolve vars
|
2641 |
$block->parentEnv = $this->getStoreEnv();
|
2642 |
$this->set(static::$namespaces[$block->type] . $block->name, $block, true);
|
@@ -2644,16 +3103,42 @@ class Compiler
|
|
2644 |
|
2645 |
case Type::T_EXTEND:
|
2646 |
foreach ($child[1] as $sel) {
|
|
|
|
|
|
|
|
|
|
|
|
|
2647 |
$results = $this->evalSelectors([$sel]);
|
2648 |
|
2649 |
foreach ($results as $result) {
|
|
|
|
|
|
|
|
|
2650 |
// only use the first one
|
2651 |
-
$result =
|
2652 |
$selectors = $out->selectors;
|
2653 |
|
2654 |
if (! $selectors && isset($child['selfParent'])) {
|
2655 |
$selectors = $this->multiplySelectors($this->env, $child['selfParent']);
|
2656 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2657 |
|
2658 |
$this->pushExtends($result, $selectors, $child);
|
2659 |
}
|
@@ -2662,14 +3147,16 @@ class Compiler
|
|
2662 |
|
2663 |
case Type::T_IF:
|
2664 |
list(, $if) = $child;
|
|
|
2665 |
|
2666 |
if ($this->isTruthy($this->reduce($if->cond, true))) {
|
2667 |
return $this->compileChildren($if->children, $out);
|
2668 |
}
|
2669 |
|
2670 |
foreach ($if->cases as $case) {
|
2671 |
-
if (
|
2672 |
-
$case
|
|
|
2673 |
) {
|
2674 |
return $this->compileChildren($case->children, $out);
|
2675 |
}
|
@@ -2678,6 +3165,7 @@ class Compiler
|
|
2678 |
|
2679 |
case Type::T_EACH:
|
2680 |
list(, $each) = $child;
|
|
|
2681 |
|
2682 |
$list = $this->coerceList($this->reduce($each->list), ',', true);
|
2683 |
|
@@ -2697,17 +3185,11 @@ class Compiler
|
|
2697 |
$ret = $this->compileChildren($each->children, $out);
|
2698 |
|
2699 |
if ($ret) {
|
2700 |
-
|
2701 |
-
|
2702 |
-
|
2703 |
-
$this->backPropagateEnv($store, $each->vars);
|
2704 |
-
|
2705 |
-
return $ret;
|
2706 |
-
}
|
2707 |
|
2708 |
-
|
2709 |
-
break;
|
2710 |
-
}
|
2711 |
}
|
2712 |
}
|
2713 |
$store = $this->env->store;
|
@@ -2718,65 +3200,54 @@ class Compiler
|
|
2718 |
|
2719 |
case Type::T_WHILE:
|
2720 |
list(, $while) = $child;
|
|
|
2721 |
|
2722 |
while ($this->isTruthy($this->reduce($while->cond, true))) {
|
2723 |
$ret = $this->compileChildren($while->children, $out);
|
2724 |
|
2725 |
if ($ret) {
|
2726 |
-
|
2727 |
-
return $ret;
|
2728 |
-
}
|
2729 |
-
|
2730 |
-
if ($ret[1]) {
|
2731 |
-
break;
|
2732 |
-
}
|
2733 |
}
|
2734 |
}
|
2735 |
break;
|
2736 |
|
2737 |
case Type::T_FOR:
|
2738 |
list(, $for) = $child;
|
|
|
2739 |
|
2740 |
-
$
|
2741 |
-
$
|
2742 |
|
2743 |
-
|
2744 |
-
$this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr());
|
2745 |
|
2746 |
-
|
2747 |
-
|
2748 |
|
2749 |
-
$
|
2750 |
-
$start = $start[1];
|
2751 |
-
$end = $end[1];
|
2752 |
|
2753 |
$d = $start < $end ? 1 : -1;
|
2754 |
|
2755 |
$this->pushEnv();
|
2756 |
|
2757 |
for (;;) {
|
2758 |
-
if (
|
|
|
2759 |
($for->until && $start == $end)
|
2760 |
) {
|
2761 |
break;
|
2762 |
}
|
2763 |
|
2764 |
-
$this->set($for->var, new
|
2765 |
$start += $d;
|
2766 |
|
2767 |
$ret = $this->compileChildren($for->children, $out);
|
2768 |
|
2769 |
if ($ret) {
|
2770 |
-
|
2771 |
-
|
2772 |
-
|
2773 |
-
$this->backPropagateEnv($store, [$for->var]);
|
2774 |
-
return $ret;
|
2775 |
-
}
|
2776 |
|
2777 |
-
|
2778 |
-
break;
|
2779 |
-
}
|
2780 |
}
|
2781 |
}
|
2782 |
|
@@ -2786,12 +3257,6 @@ class Compiler
|
|
2786 |
|
2787 |
break;
|
2788 |
|
2789 |
-
case Type::T_BREAK:
|
2790 |
-
return [Type::T_CONTROL, true];
|
2791 |
-
|
2792 |
-
case Type::T_CONTINUE:
|
2793 |
-
return [Type::T_CONTROL, false];
|
2794 |
-
|
2795 |
case Type::T_RETURN:
|
2796 |
return $this->reduce($child[1], true);
|
2797 |
|
@@ -2806,10 +3271,11 @@ class Compiler
|
|
2806 |
$mixin = $this->get(static::$namespaces['mixin'] . $name, false);
|
2807 |
|
2808 |
if (! $mixin) {
|
2809 |
-
$this->
|
2810 |
-
break;
|
2811 |
}
|
2812 |
|
|
|
|
|
2813 |
$callingScope = $this->getStoreEnv();
|
2814 |
|
2815 |
// push scope, apply args
|
@@ -2820,7 +3286,7 @@ class Compiler
|
|
2820 |
// and assign this fake parent to childs
|
2821 |
$selfParent = null;
|
2822 |
|
2823 |
-
if (isset($child['selfParent']) && isset($child['selfParent']->selectors)) {
|
2824 |
$selfParent = $child['selfParent'];
|
2825 |
} else {
|
2826 |
$parentSelectors = $this->multiplySelectors($this->env);
|
@@ -2830,7 +3296,7 @@ class Compiler
|
|
2830 |
$parent->selectors = $parentSelectors;
|
2831 |
|
2832 |
foreach ($mixin->children as $k => $child) {
|
2833 |
-
if (isset($child[1]) &&
|
2834 |
$mixin->children[$k][1]->parent = $parent;
|
2835 |
}
|
2836 |
}
|
@@ -2864,10 +3330,10 @@ class Compiler
|
|
2864 |
if (! empty($mixin->parentEnv)) {
|
2865 |
$this->env->declarationScopeParent = $mixin->parentEnv;
|
2866 |
} else {
|
2867 |
-
$this->
|
2868 |
}
|
2869 |
|
2870 |
-
$this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker .
|
2871 |
|
2872 |
$this->popEnv();
|
2873 |
break;
|
@@ -2907,54 +3373,58 @@ class Compiler
|
|
2907 |
case Type::T_DEBUG:
|
2908 |
list(, $value) = $child;
|
2909 |
|
2910 |
-
$fname = $this->sourceNames[$this->sourceIndex];
|
2911 |
$line = $this->sourceLine;
|
2912 |
-
$value = $this->
|
2913 |
|
2914 |
-
|
2915 |
break;
|
2916 |
|
2917 |
case Type::T_WARN:
|
2918 |
list(, $value) = $child;
|
2919 |
|
2920 |
-
$fname = $this->sourceNames[$this->sourceIndex];
|
2921 |
$line = $this->sourceLine;
|
2922 |
-
$value = $this->
|
2923 |
|
2924 |
-
|
2925 |
break;
|
2926 |
|
2927 |
case Type::T_ERROR:
|
2928 |
list(, $value) = $child;
|
2929 |
|
2930 |
-
$fname = $this->sourceNames[$this->sourceIndex];
|
2931 |
$line = $this->sourceLine;
|
2932 |
$value = $this->compileValue($this->reduce($value, true));
|
2933 |
|
2934 |
-
$this->
|
2935 |
-
break;
|
2936 |
-
|
2937 |
-
case Type::T_CONTROL:
|
2938 |
-
$this->throwError('@break/@continue not permitted in this scope');
|
2939 |
-
break;
|
2940 |
|
2941 |
default:
|
2942 |
-
$this->
|
2943 |
}
|
|
|
|
|
2944 |
}
|
2945 |
|
2946 |
/**
|
2947 |
* Reduce expression to string
|
2948 |
*
|
2949 |
* @param array $exp
|
|
|
2950 |
*
|
2951 |
* @return array
|
2952 |
*/
|
2953 |
-
protected function expToString($exp)
|
2954 |
{
|
2955 |
-
list(, $op, $left, $right,
|
|
|
|
|
2956 |
|
2957 |
-
$
|
|
|
|
|
|
|
|
|
2958 |
|
2959 |
if ($whiteLeft) {
|
2960 |
$content[] = ' ';
|
@@ -2968,17 +3438,21 @@ class Compiler
|
|
2968 |
|
2969 |
$content[] = $this->reduce($right);
|
2970 |
|
|
|
|
|
|
|
|
|
2971 |
return [Type::T_STRING, '', $content];
|
2972 |
}
|
2973 |
|
2974 |
/**
|
2975 |
* Is truthy?
|
2976 |
*
|
2977 |
-
* @param array $value
|
2978 |
*
|
2979 |
-
* @return
|
2980 |
*/
|
2981 |
-
|
2982 |
{
|
2983 |
return $value !== static::$false && $value !== static::$null;
|
2984 |
}
|
@@ -2988,7 +3462,7 @@ class Compiler
|
|
2988 |
*
|
2989 |
* @param string $value
|
2990 |
*
|
2991 |
-
* @return
|
2992 |
*/
|
2993 |
protected function isImmediateRelationshipCombinator($value)
|
2994 |
{
|
@@ -3000,7 +3474,7 @@ class Compiler
|
|
3000 |
*
|
3001 |
* @param array $value
|
3002 |
*
|
3003 |
-
* @return
|
3004 |
*/
|
3005 |
protected function shouldEval($value)
|
3006 |
{
|
@@ -3022,15 +3496,15 @@ class Compiler
|
|
3022 |
/**
|
3023 |
* Reduce value
|
3024 |
*
|
3025 |
-
* @param array
|
3026 |
-
* @param
|
3027 |
*
|
3028 |
-
* @return
|
3029 |
*/
|
3030 |
protected function reduce($value, $inExp = false)
|
3031 |
{
|
3032 |
-
if (
|
3033 |
-
return
|
3034 |
}
|
3035 |
|
3036 |
switch ($value[0]) {
|
@@ -3047,8 +3521,9 @@ class Compiler
|
|
3047 |
}
|
3048 |
|
3049 |
// special case: looks like css shorthand
|
3050 |
-
if (
|
3051 |
-
|
|
|
3052 |
($right[0] === Type::T_NUMBER && ! $right->unitless()))
|
3053 |
) {
|
3054 |
return $this->expToString($value);
|
@@ -3063,76 +3538,24 @@ class Compiler
|
|
3063 |
$ucLType = ucfirst($ltype);
|
3064 |
$ucRType = ucfirst($rtype);
|
3065 |
|
|
|
|
|
3066 |
// this tries:
|
3067 |
// 1. op[op name][left type][right type]
|
3068 |
-
// 2. op[left type][right type] (passing the op as first arg
|
3069 |
// 3. op[op name]
|
3070 |
-
$fn = "op${ucOpName}${ucLType}${ucRType}"
|
3071 |
-
|
3072 |
-
|
3073 |
-
|
3074 |
-
|
3075 |
-
|
3076 |
-
|
3077 |
-
|
3078 |
-
|
3079 |
-
) {
|
3080 |
-
$coerceUnit = false;
|
3081 |
-
|
3082 |
-
if (! isset($genOp) &&
|
3083 |
-
$left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER
|
3084 |
-
) {
|
3085 |
-
$coerceUnit = true;
|
3086 |
-
|
3087 |
-
switch ($opName) {
|
3088 |
-
case 'mul':
|
3089 |
-
$targetUnit = $left[2];
|
3090 |
-
|
3091 |
-
foreach ($right[2] as $unit => $exp) {
|
3092 |
-
$targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
|
3093 |
-
}
|
3094 |
-
break;
|
3095 |
-
|
3096 |
-
case 'div':
|
3097 |
-
$targetUnit = $left[2];
|
3098 |
-
|
3099 |
-
foreach ($right[2] as $unit => $exp) {
|
3100 |
-
$targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
|
3101 |
-
}
|
3102 |
-
break;
|
3103 |
-
|
3104 |
-
case 'mod':
|
3105 |
-
$targetUnit = $left[2];
|
3106 |
-
break;
|
3107 |
-
|
3108 |
-
default:
|
3109 |
-
$targetUnit = $left->unitless() ? $right[2] : $left[2];
|
3110 |
-
}
|
3111 |
-
|
3112 |
-
$baseUnitLeft = $left->isNormalizable();
|
3113 |
-
$baseUnitRight = $right->isNormalizable();
|
3114 |
-
|
3115 |
-
if ($baseUnitLeft && $baseUnitRight && $baseUnitLeft === $baseUnitRight) {
|
3116 |
-
$left = $left->normalize();
|
3117 |
-
$right = $right->normalize();
|
3118 |
-
}
|
3119 |
-
}
|
3120 |
-
|
3121 |
-
$shouldEval = $inParens || $inExp;
|
3122 |
-
|
3123 |
-
if (isset($passOp)) {
|
3124 |
-
$out = $this->$fn($op, $left, $right, $shouldEval);
|
3125 |
-
} else {
|
3126 |
-
$out = $this->$fn($left, $right, $shouldEval);
|
3127 |
-
}
|
3128 |
-
|
3129 |
-
if (isset($out)) {
|
3130 |
-
if ($coerceUnit && $out[0] === Type::T_NUMBER) {
|
3131 |
-
$out = $out->coerce($targetUnit);
|
3132 |
-
}
|
3133 |
|
3134 |
-
|
3135 |
-
|
3136 |
}
|
3137 |
|
3138 |
return $this->expToString($value);
|
@@ -3143,13 +3566,13 @@ class Compiler
|
|
3143 |
$inExp = $inExp || $this->shouldEval($exp);
|
3144 |
$exp = $this->reduce($exp);
|
3145 |
|
3146 |
-
if ($exp
|
3147 |
switch ($op) {
|
3148 |
case '+':
|
3149 |
-
return
|
3150 |
|
3151 |
case '-':
|
3152 |
-
return
|
3153 |
}
|
3154 |
}
|
3155 |
|
@@ -3174,6 +3597,14 @@ class Compiler
|
|
3174 |
foreach ($value[2] as &$item) {
|
3175 |
$item = $this->reduce($item);
|
3176 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3177 |
|
3178 |
return $value;
|
3179 |
|
@@ -3190,7 +3621,7 @@ class Compiler
|
|
3190 |
|
3191 |
case Type::T_STRING:
|
3192 |
foreach ($value[2] as &$item) {
|
3193 |
-
if (\is_array($item) || $item instanceof
|
3194 |
$item = $this->reduce($item);
|
3195 |
}
|
3196 |
}
|
@@ -3201,7 +3632,7 @@ class Compiler
|
|
3201 |
$value[1] = $this->reduce($value[1]);
|
3202 |
|
3203 |
if ($inExp) {
|
3204 |
-
return $value
|
3205 |
}
|
3206 |
|
3207 |
return $value;
|
@@ -3210,8 +3641,9 @@ class Compiler
|
|
3210 |
return $this->fncall($value[1], $value[2]);
|
3211 |
|
3212 |
case Type::T_SELF:
|
3213 |
-
$
|
3214 |
-
$selfSelector = $this->
|
|
|
3215 |
|
3216 |
return $selfSelector;
|
3217 |
|
@@ -3223,161 +3655,426 @@ class Compiler
|
|
3223 |
/**
|
3224 |
* Function caller
|
3225 |
*
|
3226 |
-
* @param string $
|
3227 |
-
* @param array
|
3228 |
*
|
3229 |
-
* @return array|
|
3230 |
*/
|
3231 |
-
protected function fncall($
|
3232 |
{
|
3233 |
-
//
|
3234 |
-
if (
|
3235 |
-
|
3236 |
-
}
|
3237 |
|
3238 |
-
|
3239 |
-
|
3240 |
-
|
|
|
3241 |
}
|
3242 |
|
3243 |
-
//
|
3244 |
-
$
|
|
|
|
|
3245 |
|
3246 |
-
|
3247 |
-
|
3248 |
-
|
|
|
3249 |
}
|
3250 |
-
}
|
3251 |
|
3252 |
-
|
3253 |
-
|
3254 |
|
3255 |
-
|
3256 |
-
|
3257 |
-
|
3258 |
-
* @param string $name
|
3259 |
-
*
|
3260 |
-
* @return string
|
3261 |
-
*/
|
3262 |
-
protected function normalizeName($name)
|
3263 |
-
{
|
3264 |
-
return str_replace('-', '_', $name);
|
3265 |
-
}
|
3266 |
|
3267 |
-
/**
|
3268 |
-
* Normalize value
|
3269 |
-
*
|
3270 |
-
* @param array $value
|
3271 |
-
*
|
3272 |
-
* @return array
|
3273 |
-
*/
|
3274 |
-
public function normalizeValue($value)
|
3275 |
-
{
|
3276 |
-
$value = $this->coerceForExpression($this->reduce($value));
|
3277 |
|
3278 |
-
switch ($
|
3279 |
-
|
3280 |
-
|
|
|
3281 |
|
3282 |
-
|
3283 |
-
|
3284 |
-
|
|
|
3285 |
|
3286 |
-
|
3287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3288 |
}
|
|
|
3289 |
|
3290 |
-
if (!
|
3291 |
-
|
3292 |
}
|
3293 |
|
3294 |
-
return $
|
3295 |
-
|
3296 |
-
case Type::T_STRING:
|
3297 |
-
return [$value[0], '"', [$this->compileStringContent($value)]];
|
3298 |
-
|
3299 |
-
case Type::T_NUMBER:
|
3300 |
-
return $value->normalize();
|
3301 |
-
|
3302 |
-
case Type::T_INTERPOLATE:
|
3303 |
-
return [Type::T_KEYWORD, $this->compileValue($value)];
|
3304 |
|
3305 |
default:
|
3306 |
-
return
|
3307 |
}
|
3308 |
}
|
3309 |
|
3310 |
/**
|
3311 |
-
*
|
3312 |
-
*
|
3313 |
-
* @param
|
3314 |
-
* @param array $right
|
3315 |
*
|
3316 |
-
* @return
|
3317 |
*/
|
3318 |
-
protected function
|
3319 |
{
|
3320 |
-
|
3321 |
-
|
|
|
3322 |
|
3323 |
-
|
3324 |
-
|
3325 |
-
|
3326 |
-
* @param array $left
|
3327 |
-
* @param array $right
|
3328 |
-
*
|
3329 |
-
* @return \ScssPhp\ScssPhp\Node\Number
|
3330 |
-
*/
|
3331 |
-
protected function opMulNumberNumber($left, $right)
|
3332 |
-
{
|
3333 |
-
return new Node\Number($left[1] * $right[1], $left[2]);
|
3334 |
-
}
|
3335 |
|
3336 |
-
|
3337 |
-
|
3338 |
-
|
3339 |
-
|
3340 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3341 |
*
|
3342 |
-
* @return
|
3343 |
*/
|
3344 |
-
protected function
|
3345 |
{
|
3346 |
-
return
|
3347 |
}
|
3348 |
|
3349 |
/**
|
3350 |
-
*
|
3351 |
*
|
3352 |
-
* @
|
3353 |
-
*
|
|
|
3354 |
*
|
3355 |
-
* @return array
|
3356 |
*/
|
3357 |
-
|
3358 |
{
|
3359 |
-
|
3360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3361 |
}
|
|
|
3362 |
|
3363 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3364 |
}
|
3365 |
|
3366 |
/**
|
3367 |
-
*
|
3368 |
*
|
3369 |
-
* @param
|
3370 |
-
* @param
|
3371 |
*
|
3372 |
-
* @return
|
3373 |
*/
|
3374 |
-
protected function
|
3375 |
{
|
3376 |
-
|
3377 |
-
|
3378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3379 |
|
3380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3381 |
}
|
3382 |
|
3383 |
/**
|
@@ -3416,11 +4113,11 @@ class Compiler
|
|
3416 |
/**
|
3417 |
* Boolean and
|
3418 |
*
|
3419 |
-
* @param array
|
3420 |
-
* @param array
|
3421 |
-
* @param
|
3422 |
*
|
3423 |
-
* @return array|null
|
3424 |
*/
|
3425 |
protected function opAnd($left, $right, $shouldEval)
|
3426 |
{
|
@@ -3444,11 +4141,11 @@ class Compiler
|
|
3444 |
/**
|
3445 |
* Boolean or
|
3446 |
*
|
3447 |
-
* @param array
|
3448 |
-
* @param array
|
3449 |
-
* @param
|
3450 |
*
|
3451 |
-
* @return array|null
|
3452 |
*/
|
3453 |
protected function opOr($left, $right, $shouldEval)
|
3454 |
{
|
@@ -3480,6 +4177,15 @@ class Compiler
|
|
3480 |
*/
|
3481 |
protected function opColorColor($op, $left, $right)
|
3482 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3483 |
$out = [Type::T_COLOR];
|
3484 |
|
3485 |
foreach ([1, 2, 3] as $i) {
|
@@ -3500,13 +4206,16 @@ class Compiler
|
|
3500 |
break;
|
3501 |
|
3502 |
case '%':
|
|
|
|
|
|
|
|
|
3503 |
$out[] = $lval % $rval;
|
3504 |
break;
|
3505 |
|
3506 |
case '/':
|
3507 |
if ($rval == 0) {
|
3508 |
-
$this->
|
3509 |
-
break 2;
|
3510 |
}
|
3511 |
|
3512 |
$out[] = (int) ($lval / $rval);
|
@@ -3519,8 +4228,7 @@ class Compiler
|
|
3519 |
return $this->opNeq($left, $right);
|
3520 |
|
3521 |
default:
|
3522 |
-
$this->
|
3523 |
-
break 2;
|
3524 |
}
|
3525 |
}
|
3526 |
|
@@ -3538,13 +4246,21 @@ class Compiler
|
|
3538 |
*
|
3539 |
* @param string $op
|
3540 |
* @param array $left
|
3541 |
-
* @param
|
3542 |
*
|
3543 |
* @return array
|
3544 |
*/
|
3545 |
-
protected function opColorNumber($op, $left, $right)
|
3546 |
{
|
3547 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3548 |
|
3549 |
return $this->opColorColor(
|
3550 |
$op,
|
@@ -3557,14 +4273,22 @@ class Compiler
|
|
3557 |
* Compare number and color
|
3558 |
*
|
3559 |
* @param string $op
|
3560 |
-
* @param
|
3561 |
* @param array $right
|
3562 |
*
|
3563 |
* @return array
|
3564 |
*/
|
3565 |
-
protected function opNumberColor($op, $left, $right)
|
3566 |
{
|
3567 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3568 |
|
3569 |
return $this->opColorColor(
|
3570 |
$op,
|
@@ -3576,8 +4300,8 @@ class Compiler
|
|
3576 |
/**
|
3577 |
* Compare number1 == number2
|
3578 |
*
|
3579 |
-
* @param array $left
|
3580 |
-
* @param array $right
|
3581 |
*
|
3582 |
* @return array
|
3583 |
*/
|
@@ -3597,8 +4321,8 @@ class Compiler
|
|
3597 |
/**
|
3598 |
* Compare number1 != number2
|
3599 |
*
|
3600 |
-
* @param array $left
|
3601 |
-
* @param array $right
|
3602 |
*
|
3603 |
* @return array
|
3604 |
*/
|
@@ -3616,70 +4340,81 @@ class Compiler
|
|
3616 |
}
|
3617 |
|
3618 |
/**
|
3619 |
-
* Compare number1
|
3620 |
*
|
3621 |
-
* @param
|
3622 |
-
* @param
|
3623 |
*
|
3624 |
* @return array
|
3625 |
*/
|
3626 |
-
protected function
|
3627 |
{
|
3628 |
-
return $this->toBool($left
|
3629 |
}
|
3630 |
|
3631 |
/**
|
3632 |
-
* Compare number1
|
3633 |
*
|
3634 |
-
* @param
|
3635 |
-
* @param
|
3636 |
*
|
3637 |
* @return array
|
3638 |
*/
|
3639 |
-
protected function
|
3640 |
{
|
3641 |
-
return $this->toBool(
|
3642 |
}
|
3643 |
|
3644 |
/**
|
3645 |
-
* Compare number1
|
3646 |
*
|
3647 |
-
* @param
|
3648 |
-
* @param
|
3649 |
*
|
3650 |
* @return array
|
3651 |
*/
|
3652 |
-
protected function
|
3653 |
{
|
3654 |
-
return $this->toBool($left
|
3655 |
}
|
3656 |
|
3657 |
/**
|
3658 |
-
* Compare number1
|
3659 |
*
|
3660 |
-
* @param
|
3661 |
-
* @param
|
3662 |
*
|
3663 |
* @return array
|
3664 |
*/
|
3665 |
-
protected function
|
3666 |
{
|
3667 |
-
return $this->toBool($left
|
3668 |
}
|
3669 |
|
3670 |
/**
|
3671 |
-
*
|
3672 |
*
|
3673 |
-
* @param
|
3674 |
-
* @param
|
3675 |
*
|
3676 |
-
* @return
|
3677 |
*/
|
3678 |
-
protected function
|
3679 |
{
|
3680 |
-
|
|
|
3681 |
|
3682 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3683 |
}
|
3684 |
|
3685 |
/**
|
@@ -3687,7 +4422,7 @@ class Compiler
|
|
3687 |
*
|
3688 |
* @api
|
3689 |
*
|
3690 |
-
* @param
|
3691 |
*
|
3692 |
* @return array
|
3693 |
*/
|
@@ -3696,6 +4431,53 @@ class Compiler
|
|
3696 |
return $thing ? static::$true : static::$false;
|
3697 |
}
|
3698 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3699 |
/**
|
3700 |
* Compiles a primitive value into a CSS property value.
|
3701 |
*
|
@@ -3709,17 +4491,22 @@ class Compiler
|
|
3709 |
*
|
3710 |
* @api
|
3711 |
*
|
3712 |
-
* @param array $value
|
|
|
3713 |
*
|
3714 |
-
* @return string
|
3715 |
*/
|
3716 |
-
public function compileValue($value)
|
3717 |
{
|
3718 |
$value = $this->reduce($value);
|
3719 |
|
|
|
|
|
|
|
|
|
3720 |
switch ($value[0]) {
|
3721 |
case Type::T_KEYWORD:
|
3722 |
-
return $value[1];
|
3723 |
|
3724 |
case Type::T_COLOR:
|
3725 |
// [1] - red component (either number for a %)
|
@@ -3743,7 +4530,7 @@ class Compiler
|
|
3743 |
}
|
3744 |
|
3745 |
if (is_numeric($alpha)) {
|
3746 |
-
$a = new
|
3747 |
} else {
|
3748 |
$a = $alpha;
|
3749 |
}
|
@@ -3771,65 +4558,111 @@ class Compiler
|
|
3771 |
|
3772 |
return $h;
|
3773 |
|
3774 |
-
case Type::T_NUMBER:
|
3775 |
-
return $value->output($this);
|
3776 |
-
|
3777 |
case Type::T_STRING:
|
3778 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3779 |
|
3780 |
case Type::T_FUNCTION:
|
3781 |
-
$args = ! empty($value[2]) ? $this->compileValue($value[2]) : '';
|
3782 |
|
3783 |
return "$value[1]($args)";
|
3784 |
|
|
|
|
|
|
|
|
|
|
|
3785 |
case Type::T_LIST:
|
3786 |
$value = $this->extractInterpolation($value);
|
3787 |
|
3788 |
if ($value[0] !== Type::T_LIST) {
|
3789 |
-
return $this->compileValue($value);
|
3790 |
}
|
3791 |
|
3792 |
list(, $delim, $items) = $value;
|
3793 |
-
$pre = $post =
|
3794 |
|
3795 |
if (! empty($value['enclosing'])) {
|
3796 |
switch ($value['enclosing']) {
|
3797 |
case 'parent':
|
3798 |
-
//$pre =
|
3799 |
-
//$post =
|
3800 |
break;
|
3801 |
case 'forced_parent':
|
3802 |
-
$pre =
|
3803 |
-
$post =
|
3804 |
break;
|
3805 |
case 'bracket':
|
3806 |
case 'forced_bracket':
|
3807 |
-
$pre =
|
3808 |
-
$post =
|
3809 |
break;
|
3810 |
}
|
3811 |
}
|
3812 |
|
|
|
|
|
3813 |
$prefix_value = '';
|
|
|
3814 |
if ($delim !== ' ') {
|
3815 |
$prefix_value = ' ';
|
3816 |
}
|
3817 |
|
3818 |
$filtered = [];
|
3819 |
|
|
|
3820 |
foreach ($items as $item) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3821 |
if ($item[0] === Type::T_NULL) {
|
3822 |
continue;
|
3823 |
}
|
|
|
|
|
|
|
|
|
|
|
3824 |
|
3825 |
-
$compiled = $this->compileValue($item);
|
3826 |
if ($prefix_value && \strlen($compiled)) {
|
3827 |
$compiled = $prefix_value . $compiled;
|
3828 |
}
|
|
|
3829 |
$filtered[] = $compiled;
|
3830 |
}
|
3831 |
|
3832 |
-
return $pre . substr(implode(
|
3833 |
|
3834 |
case Type::T_MAP:
|
3835 |
$keys = $value[1];
|
@@ -3837,7 +4670,7 @@ class Compiler
|
|
3837 |
$filtered = [];
|
3838 |
|
3839 |
for ($i = 0, $s = \count($keys); $i < $s; $i++) {
|
3840 |
-
$filtered[$this->compileValue($keys[$i])] = $this->compileValue($values[$i]);
|
3841 |
}
|
3842 |
|
3843 |
array_walk($filtered, function (&$value, $key) {
|
@@ -3857,8 +4690,9 @@ class Compiler
|
|
3857 |
$delim .= ' ';
|
3858 |
}
|
3859 |
|
3860 |
-
$left = \count($left[2]) > 0
|
3861 |
-
$this->compileValue($left) . $delim . $whiteLeft
|
|
|
3862 |
|
3863 |
$delim = $right[1];
|
3864 |
|
@@ -3867,14 +4701,18 @@ class Compiler
|
|
3867 |
}
|
3868 |
|
3869 |
$right = \count($right[2]) > 0 ?
|
3870 |
-
$whiteRight . $delim . $this->compileValue($right) : '';
|
3871 |
|
3872 |
-
return $left . $this->compileValue($interpolate) . $right;
|
3873 |
|
3874 |
case Type::T_INTERPOLATE:
|
3875 |
// strip quotes if it's a string
|
3876 |
$reduced = $this->reduce($value[1]);
|
3877 |
|
|
|
|
|
|
|
|
|
3878 |
switch ($reduced[0]) {
|
3879 |
case Type::T_LIST:
|
3880 |
$reduced = $this->extractInterpolation($reduced);
|
@@ -3896,14 +4734,12 @@ class Compiler
|
|
3896 |
continue;
|
3897 |
}
|
3898 |
|
3899 |
-
$
|
3900 |
-
|
3901 |
-
|
3902 |
-
$filtered[] = $
|
3903 |
-
} elseif ($temp[0] === Type::T_KEYWORD) {
|
3904 |
-
$filtered[] = $temp[1];
|
3905 |
} else {
|
3906 |
-
$filtered[] = $this->compileValue($
|
3907 |
}
|
3908 |
}
|
3909 |
|
@@ -3911,14 +4747,14 @@ class Compiler
|
|
3911 |
break;
|
3912 |
|
3913 |
case Type::T_STRING:
|
3914 |
-
$reduced = [Type::
|
3915 |
break;
|
3916 |
|
3917 |
case Type::T_NULL:
|
3918 |
$reduced = [Type::T_KEYWORD, ''];
|
3919 |
}
|
3920 |
|
3921 |
-
return $this->compileValue($reduced);
|
3922 |
|
3923 |
case Type::T_NULL:
|
3924 |
return 'null';
|
@@ -3927,7 +4763,29 @@ class Compiler
|
|
3927 |
return $this->compileCommentValue($value);
|
3928 |
|
3929 |
default:
|
3930 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3931 |
}
|
3932 |
}
|
3933 |
|
@@ -3937,26 +4795,50 @@ class Compiler
|
|
3937 |
* @param array $list
|
3938 |
*
|
3939 |
* @return string
|
|
|
|
|
3940 |
*/
|
3941 |
protected function flattenList($list)
|
3942 |
{
|
|
|
|
|
3943 |
return $this->compileValue($list);
|
3944 |
}
|
3945 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3946 |
/**
|
3947 |
* Compile string content
|
3948 |
*
|
3949 |
* @param array $string
|
|
|
3950 |
*
|
3951 |
* @return string
|
3952 |
*/
|
3953 |
-
protected function compileStringContent($string)
|
3954 |
{
|
3955 |
$parts = [];
|
3956 |
|
3957 |
foreach ($string[2] as $part) {
|
3958 |
-
if (\is_array($part) || $part instanceof
|
3959 |
-
$parts[] = $this->compileValue($part);
|
3960 |
} else {
|
3961 |
$parts[] = $part;
|
3962 |
}
|
@@ -4020,8 +4902,8 @@ class Compiler
|
|
4020 |
$prevSelectors = $selectors;
|
4021 |
$selectors = [];
|
4022 |
|
4023 |
-
foreach ($
|
4024 |
-
foreach ($
|
4025 |
if ($selfParentSelectors) {
|
4026 |
foreach ($selfParentSelectors as $selfParent) {
|
4027 |
// if no '&' in the selector, each call will give same result, only add once
|
@@ -4041,16 +4923,21 @@ class Compiler
|
|
4041 |
|
4042 |
$selectors = array_values($selectors);
|
4043 |
|
|
|
|
|
|
|
|
|
|
|
4044 |
return $selectors;
|
4045 |
}
|
4046 |
|
4047 |
/**
|
4048 |
* Join selectors; looks for & to replace, or append parent before child
|
4049 |
*
|
4050 |
-
* @param array
|
4051 |
-
* @param array
|
4052 |
-
* @param
|
4053 |
-
* @param array
|
4054 |
|
4055 |
* @return array
|
4056 |
*/
|
@@ -4116,7 +5003,8 @@ class Compiler
|
|
4116 |
*/
|
4117 |
protected function multiplyMedia(Environment $env = null, $childQueries = null)
|
4118 |
{
|
4119 |
-
if (
|
|
|
4120 |
! empty($env->block->type) && $env->block->type !== Type::T_MEDIA
|
4121 |
) {
|
4122 |
return $childQueries;
|
@@ -4127,6 +5015,8 @@ class Compiler
|
|
4127 |
return $this->multiplyMedia($env->parent, $childQueries);
|
4128 |
}
|
4129 |
|
|
|
|
|
4130 |
$parentQueries = isset($env->block->queryList)
|
4131 |
? $env->block->queryList
|
4132 |
: [[[Type::T_MEDIA_VALUE, $env->block->value]]];
|
@@ -4162,9 +5052,11 @@ class Compiler
|
|
4162 |
/**
|
4163 |
* Convert env linked list to stack
|
4164 |
*
|
4165 |
-
* @param
|
4166 |
*
|
4167 |
-
* @return
|
|
|
|
|
4168 |
*/
|
4169 |
protected function compactEnv(Environment $env)
|
4170 |
{
|
@@ -4178,9 +5070,11 @@ class Compiler
|
|
4178 |
/**
|
4179 |
* Convert env stack to singly linked list
|
4180 |
*
|
4181 |
-
* @param
|
4182 |
*
|
4183 |
-
* @return
|
|
|
|
|
4184 |
*/
|
4185 |
protected function extractEnv($envs)
|
4186 |
{
|
@@ -4201,7 +5095,7 @@ class Compiler
|
|
4201 |
*/
|
4202 |
protected function pushEnv(Block $block = null)
|
4203 |
{
|
4204 |
-
$env = new Environment;
|
4205 |
$env->parent = $this->env;
|
4206 |
$env->parentStore = $this->storeEnv;
|
4207 |
$env->store = [];
|
@@ -4216,6 +5110,8 @@ class Compiler
|
|
4216 |
|
4217 |
/**
|
4218 |
* Pop environment
|
|
|
|
|
4219 |
*/
|
4220 |
protected function popEnv()
|
4221 |
{
|
@@ -4226,8 +5122,10 @@ class Compiler
|
|
4226 |
/**
|
4227 |
* Propagate vars from a just poped Env (used in @each and @for)
|
4228 |
*
|
4229 |
-
* @param array
|
4230 |
-
* @param null|
|
|
|
|
|
4231 |
*/
|
4232 |
protected function backPropagateEnv($store, $excludedVars = null)
|
4233 |
{
|
@@ -4253,9 +5151,11 @@ class Compiler
|
|
4253 |
*
|
4254 |
* @param string $name
|
4255 |
* @param mixed $value
|
4256 |
-
* @param
|
4257 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
4258 |
* @param mixed $valueUnreduced
|
|
|
|
|
4259 |
*/
|
4260 |
protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null)
|
4261 |
{
|
@@ -4279,6 +5179,8 @@ class Compiler
|
|
4279 |
* @param mixed $value
|
4280 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
4281 |
* @param mixed $valueUnreduced
|
|
|
|
|
4282 |
*/
|
4283 |
protected function setExisting($name, $value, Environment $env, $valueUnreduced = null)
|
4284 |
{
|
@@ -4337,6 +5239,8 @@ class Compiler
|
|
4337 |
* @param mixed $value
|
4338 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
4339 |
* @param mixed $valueUnreduced
|
|
|
|
|
4340 |
*/
|
4341 |
protected function setRaw($name, $value, Environment $env, $valueUnreduced = null)
|
4342 |
{
|
@@ -4350,12 +5254,12 @@ class Compiler
|
|
4350 |
/**
|
4351 |
* Get variable
|
4352 |
*
|
4353 |
-
* @
|
4354 |
*
|
4355 |
* @param string $name
|
4356 |
-
* @param
|
4357 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
4358 |
-
* @param
|
4359 |
*
|
4360 |
* @return mixed|null
|
4361 |
*/
|
@@ -4409,7 +5313,7 @@ class Compiler
|
|
4409 |
}
|
4410 |
|
4411 |
if ($shouldThrow) {
|
4412 |
-
$this->
|
4413 |
}
|
4414 |
|
4415 |
// found nothing
|
@@ -4422,7 +5326,7 @@ class Compiler
|
|
4422 |
* @param string $name
|
4423 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
4424 |
*
|
4425 |
-
* @return
|
4426 |
*/
|
4427 |
protected function has($name, Environment $env = null)
|
4428 |
{
|
@@ -4433,6 +5337,8 @@ class Compiler
|
|
4433 |
* Inject variables
|
4434 |
*
|
4435 |
* @param array $args
|
|
|
|
|
4436 |
*/
|
4437 |
protected function injectVariables(array $args)
|
4438 |
{
|
@@ -4447,7 +5353,7 @@ class Compiler
|
|
4447 |
$name = substr($name, 1);
|
4448 |
}
|
4449 |
|
4450 |
-
if (! $parser->parseValue($strValue, $value)) {
|
4451 |
$value = $this->coerceValue($strValue);
|
4452 |
}
|
4453 |
|
@@ -4455,16 +5361,59 @@ class Compiler
|
|
4455 |
}
|
4456 |
}
|
4457 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4458 |
/**
|
4459 |
* Set variables
|
4460 |
*
|
4461 |
* @api
|
4462 |
*
|
4463 |
* @param array $variables
|
|
|
|
|
|
|
|
|
4464 |
*/
|
4465 |
public function setVariables(array $variables)
|
4466 |
{
|
4467 |
-
|
|
|
|
|
4468 |
}
|
4469 |
|
4470 |
/**
|
@@ -4473,6 +5422,8 @@ class Compiler
|
|
4473 |
* @api
|
4474 |
*
|
4475 |
* @param string $name
|
|
|
|
|
4476 |
*/
|
4477 |
public function unsetVariable($name)
|
4478 |
{
|
@@ -4494,13 +5445,15 @@ class Compiler
|
|
4494 |
/**
|
4495 |
* Adds to list of parsed files
|
4496 |
*
|
4497 |
-
* @
|
4498 |
*
|
4499 |
-
* @param string $path
|
|
|
|
|
4500 |
*/
|
4501 |
public function addParsedFile($path)
|
4502 |
{
|
4503 |
-
if (
|
4504 |
$this->parsedFiles[realpath($path)] = filemtime($path);
|
4505 |
}
|
4506 |
}
|
@@ -4508,12 +5461,12 @@ class Compiler
|
|
4508 |
/**
|
4509 |
* Returns list of parsed files
|
4510 |
*
|
4511 |
-
* @
|
4512 |
-
*
|
4513 |
-
* @return array
|
4514 |
*/
|
4515 |
public function getParsedFiles()
|
4516 |
{
|
|
|
4517 |
return $this->parsedFiles;
|
4518 |
}
|
4519 |
|
@@ -4523,6 +5476,8 @@ class Compiler
|
|
4523 |
* @api
|
4524 |
*
|
4525 |
* @param string|callable $path
|
|
|
|
|
4526 |
*/
|
4527 |
public function addImportPath($path)
|
4528 |
{
|
@@ -4536,11 +5491,24 @@ class Compiler
|
|
4536 |
*
|
4537 |
* @api
|
4538 |
*
|
4539 |
-
* @param string|array $path
|
|
|
|
|
4540 |
*/
|
4541 |
public function setImportPaths($path)
|
4542 |
{
|
4543 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4544 |
}
|
4545 |
|
4546 |
/**
|
@@ -4548,11 +5516,43 @@ class Compiler
|
|
4548 |
*
|
4549 |
* @api
|
4550 |
*
|
4551 |
-
* @param
|
|
|
|
|
|
|
|
|
4552 |
*/
|
4553 |
public function setNumberPrecision($numberPrecision)
|
4554 |
{
|
4555 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4556 |
}
|
4557 |
|
4558 |
/**
|
@@ -4561,10 +5561,21 @@ class Compiler
|
|
4561 |
* @api
|
4562 |
*
|
4563 |
* @param string $formatterName
|
|
|
|
|
|
|
|
|
|
|
|
|
4564 |
*/
|
4565 |
public function setFormatter($formatterName)
|
4566 |
{
|
4567 |
-
$
|
|
|
|
|
|
|
|
|
|
|
4568 |
}
|
4569 |
|
4570 |
/**
|
@@ -4573,10 +5584,34 @@ class Compiler
|
|
4573 |
* @api
|
4574 |
*
|
4575 |
* @param string $lineNumberStyle
|
|
|
|
|
|
|
|
|
4576 |
*/
|
4577 |
public function setLineNumberStyle($lineNumberStyle)
|
4578 |
{
|
4579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4580 |
}
|
4581 |
|
4582 |
/**
|
@@ -4584,7 +5619,11 @@ class Compiler
|
|
4584 |
*
|
4585 |
* @api
|
4586 |
*
|
4587 |
-
* @param
|
|
|
|
|
|
|
|
|
4588 |
*/
|
4589 |
public function setSourceMap($sourceMap)
|
4590 |
{
|
@@ -4597,6 +5636,10 @@ class Compiler
|
|
4597 |
* @api
|
4598 |
*
|
4599 |
* @param array $sourceMapOptions
|
|
|
|
|
|
|
|
|
4600 |
*/
|
4601 |
public function setSourceMapOptions($sourceMapOptions)
|
4602 |
{
|
@@ -4608,13 +5651,23 @@ class Compiler
|
|
4608 |
*
|
4609 |
* @api
|
4610 |
*
|
4611 |
-
* @param string
|
4612 |
-
* @param callable
|
4613 |
-
* @param
|
|
|
|
|
4614 |
*/
|
4615 |
-
public function registerFunction($name, $
|
4616 |
{
|
4617 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4618 |
}
|
4619 |
|
4620 |
/**
|
@@ -4623,6 +5676,8 @@ class Compiler
|
|
4623 |
* @api
|
4624 |
*
|
4625 |
* @param string $name
|
|
|
|
|
4626 |
*/
|
4627 |
public function unregisterFunction($name)
|
4628 |
{
|
@@ -4635,9 +5690,15 @@ class Compiler
|
|
4635 |
* @api
|
4636 |
*
|
4637 |
* @param string $name
|
|
|
|
|
|
|
|
|
4638 |
*/
|
4639 |
public function addFeature($name)
|
4640 |
{
|
|
|
|
|
4641 |
$this->registeredFeatures[$name] = true;
|
4642 |
}
|
4643 |
|
@@ -4646,13 +5707,28 @@ class Compiler
|
|
4646 |
*
|
4647 |
* @param string $path
|
4648 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
|
|
|
|
4649 |
*/
|
4650 |
protected function importFile($path, OutputBlock $out)
|
4651 |
{
|
4652 |
-
$this->pushCallStack('import '
|
4653 |
// see if tree is cached
|
4654 |
$realPath = realpath($path);
|
4655 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4656 |
if (isset($this->importCache[$realPath])) {
|
4657 |
$this->handleImportLoop($realPath);
|
4658 |
|
@@ -4665,62 +5741,109 @@ class Compiler
|
|
4665 |
$this->importCache[$realPath] = $tree;
|
4666 |
}
|
4667 |
|
4668 |
-
$
|
|
|
4669 |
|
4670 |
-
array_unshift($this->importPaths, $pi['dirname']);
|
4671 |
$this->compileChildrenNoReturn($tree->children, $out);
|
4672 |
-
|
4673 |
$this->popCallStack();
|
4674 |
}
|
4675 |
|
4676 |
/**
|
4677 |
-
*
|
4678 |
*
|
4679 |
-
* @
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4680 |
*
|
4681 |
* @param string $url
|
4682 |
*
|
4683 |
-
* @return
|
4684 |
*/
|
4685 |
-
public function
|
4686 |
{
|
4687 |
-
|
|
|
4688 |
|
4689 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4690 |
|
4691 |
-
|
4692 |
-
|
4693 |
-
|
4694 |
|
4695 |
-
|
4696 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4697 |
|
4698 |
-
|
4699 |
-
|
|
|
4700 |
}
|
|
|
|
|
4701 |
|
4702 |
-
|
4703 |
-
|
4704 |
-
|
4705 |
-
|
4706 |
-
|
4707 |
}
|
4708 |
}
|
4709 |
|
4710 |
foreach ($this->importPaths as $dir) {
|
4711 |
if (\is_string($dir)) {
|
4712 |
-
|
4713 |
-
|
4714 |
-
|
4715 |
-
|
4716 |
-
substr($dir, -1) !== '/' &&
|
4717 |
-
substr($full, 0, 1) !== '/'
|
4718 |
-
) ? '/' : '';
|
4719 |
-
$full = $dir . $separator . $full;
|
4720 |
-
|
4721 |
-
if (is_file($file = $full)) {
|
4722 |
-
return $file;
|
4723 |
-
}
|
4724 |
}
|
4725 |
} elseif (\is_callable($dir)) {
|
4726 |
// check custom callback for import path
|
@@ -4732,103 +5855,336 @@ class Compiler
|
|
4732 |
}
|
4733 |
}
|
4734 |
|
4735 |
-
if ($
|
4736 |
-
|
4737 |
-
|
|
|
|
|
|
|
|
|
4738 |
}
|
4739 |
}
|
4740 |
|
4741 |
-
|
4742 |
}
|
4743 |
|
4744 |
/**
|
4745 |
-
*
|
4746 |
-
*
|
4747 |
-
* @api
|
4748 |
*
|
4749 |
-
* @
|
4750 |
*/
|
4751 |
-
|
4752 |
{
|
4753 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4754 |
}
|
4755 |
|
4756 |
/**
|
4757 |
-
*
|
4758 |
-
*
|
4759 |
-
* @api
|
4760 |
*
|
4761 |
-
* @
|
4762 |
-
*
|
4763 |
-
* @return \ScssPhp\ScssPhp\Compiler
|
4764 |
*/
|
4765 |
-
|
4766 |
{
|
4767 |
-
$
|
|
|
|
|
4768 |
|
4769 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4770 |
}
|
4771 |
|
4772 |
/**
|
4773 |
-
*
|
4774 |
-
*
|
4775 |
-
* @api
|
4776 |
-
*
|
4777 |
-
* @param string $msg Message with optional sprintf()-style vararg parameters
|
4778 |
*
|
4779 |
-
* @
|
4780 |
*/
|
4781 |
-
|
4782 |
{
|
4783 |
-
|
4784 |
-
|
4785 |
-
|
|
|
4786 |
|
4787 |
-
if (
|
4788 |
-
$
|
4789 |
}
|
4790 |
|
4791 |
-
|
4792 |
-
|
4793 |
-
$column = $this->sourceColumn;
|
4794 |
|
4795 |
-
|
4796 |
-
|
4797 |
-
|
|
|
|
|
|
|
|
|
|
|
4798 |
|
4799 |
-
|
4800 |
|
4801 |
-
|
|
|
|
|
4802 |
|
4803 |
-
|
4804 |
-
|
4805 |
-
}
|
4806 |
}
|
4807 |
|
4808 |
-
|
4809 |
}
|
4810 |
|
4811 |
/**
|
4812 |
-
*
|
4813 |
-
*
|
4814 |
-
* @param boolean $all
|
4815 |
-
* @param null $limit
|
4816 |
*
|
4817 |
-
* @return string
|
4818 |
*/
|
4819 |
-
|
4820 |
{
|
4821 |
-
$
|
4822 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4823 |
|
4824 |
if ($this->callStack) {
|
4825 |
foreach (array_reverse($this->callStack) as $call) {
|
4826 |
if ($all || (isset($call['n']) && $call['n'])) {
|
4827 |
-
$msg =
|
4828 |
$msg .= (isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
|
4829 |
-
? $this->sourceNames[$call[Parser::SOURCE_INDEX]]
|
4830 |
: '(unknown file)');
|
4831 |
-
$msg .=
|
4832 |
|
4833 |
$callStackMsg[] = $msg;
|
4834 |
|
@@ -4847,6 +6203,8 @@ class Compiler
|
|
4847 |
*
|
4848 |
* @param string $name
|
4849 |
*
|
|
|
|
|
4850 |
* @throws \Exception
|
4851 |
*/
|
4852 |
protected function handleImportLoop($name)
|
@@ -4858,9 +6216,12 @@ class Compiler
|
|
4858 |
|
4859 |
$file = $this->sourceNames[$env->block->sourceIndex];
|
4860 |
|
|
|
|
|
|
|
|
|
4861 |
if (realpath($file) === $name) {
|
4862 |
-
$this->
|
4863 |
-
break;
|
4864 |
}
|
4865 |
}
|
4866 |
}
|
@@ -4868,19 +6229,17 @@ class Compiler
|
|
4868 |
/**
|
4869 |
* Call SCSS @function
|
4870 |
*
|
4871 |
-
* @param
|
4872 |
-
* @param array
|
4873 |
-
* @param array $returnValue
|
4874 |
*
|
4875 |
-
* @return
|
4876 |
*/
|
4877 |
-
protected function callScssFunction($
|
4878 |
{
|
4879 |
-
$func = $this->get(static::$namespaces['function'] . $name, false);
|
4880 |
-
|
4881 |
if (! $func) {
|
4882 |
-
return
|
4883 |
}
|
|
|
4884 |
|
4885 |
$this->pushEnv();
|
4886 |
|
@@ -4890,7 +6249,7 @@ class Compiler
|
|
4890 |
}
|
4891 |
|
4892 |
// throw away lines and children
|
4893 |
-
$tmp = new OutputBlock;
|
4894 |
$tmp->lines = [];
|
4895 |
$tmp->children = [];
|
4896 |
|
@@ -4899,66 +6258,57 @@ class Compiler
|
|
4899 |
if (! empty($func->parentEnv)) {
|
4900 |
$this->env->declarationScopeParent = $func->parentEnv;
|
4901 |
} else {
|
4902 |
-
$this->
|
4903 |
}
|
4904 |
|
4905 |
-
$ret = $this->compileChildren($func->children, $tmp, $this->env->marker .
|
4906 |
|
4907 |
$this->popEnv();
|
4908 |
|
4909 |
-
|
4910 |
-
|
4911 |
-
return true;
|
4912 |
}
|
4913 |
|
4914 |
/**
|
4915 |
* Call built-in and registered (PHP) functions
|
4916 |
*
|
4917 |
* @param string $name
|
|
|
|
|
4918 |
* @param array $args
|
4919 |
-
* @param array $returnValue
|
4920 |
*
|
4921 |
-
* @return
|
4922 |
*/
|
4923 |
-
protected function callNativeFunction($name, $
|
4924 |
{
|
4925 |
-
|
4926 |
-
$
|
4927 |
-
$libName = null;
|
4928 |
|
4929 |
-
if (
|
4930 |
-
|
4931 |
-
list($f, $prototype) = $this->userFunctions[$name];
|
4932 |
-
} elseif (($f = $this->getBuiltinFunction($name)) && \is_callable($f)) {
|
4933 |
-
$libName = $f[1];
|
4934 |
-
$prototype = isset(static::$$libName) ? static::$$libName : null;
|
4935 |
-
} else {
|
4936 |
-
return false;
|
4937 |
}
|
|
|
4938 |
|
4939 |
-
|
4940 |
-
|
4941 |
-
if ($name !== 'if' && $name !== 'call') {
|
4942 |
-
$inExp = true;
|
4943 |
-
|
4944 |
-
if ($name === 'join') {
|
4945 |
-
$inExp = false;
|
4946 |
-
}
|
4947 |
-
|
4948 |
foreach ($sorted as &$val) {
|
4949 |
-
|
|
|
|
|
4950 |
}
|
4951 |
}
|
4952 |
|
4953 |
-
$returnValue = \call_user_func($
|
4954 |
|
4955 |
if (! isset($returnValue)) {
|
4956 |
-
return
|
4957 |
}
|
4958 |
|
4959 |
-
$returnValue
|
|
|
|
|
4960 |
|
4961 |
-
|
|
|
|
|
4962 |
}
|
4963 |
|
4964 |
/**
|
@@ -4970,6 +6320,22 @@ class Compiler
|
|
4970 |
*/
|
4971 |
protected function getBuiltinFunction($name)
|
4972 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4973 |
$libName = 'lib' . preg_replace_callback(
|
4974 |
'/_(.)/',
|
4975 |
function ($m) {
|
@@ -4977,18 +6343,31 @@ class Compiler
|
|
4977 |
},
|
4978 |
ucfirst($name)
|
4979 |
);
|
|
|
|
|
4980 |
|
4981 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4982 |
}
|
4983 |
|
4984 |
/**
|
4985 |
* Sorts keyword arguments
|
4986 |
*
|
4987 |
* @param string $functionName
|
4988 |
-
* @param array $prototypes
|
4989 |
* @param array $args
|
4990 |
*
|
4991 |
-
* @return array
|
4992 |
*/
|
4993 |
protected function sortNativeFunctionArgs($functionName, $prototypes, $args)
|
4994 |
{
|
@@ -4998,16 +6377,18 @@ class Compiler
|
|
4998 |
$keyArgs = [];
|
4999 |
$posArgs = [];
|
5000 |
|
|
|
|
|
|
|
|
|
5001 |
// separate positional and keyword arguments
|
5002 |
foreach ($args as $arg) {
|
5003 |
list($key, $value) = $arg;
|
5004 |
|
5005 |
-
$key
|
5006 |
-
|
5007 |
-
if (empty($key)) {
|
5008 |
$posArgs[] = empty($arg[2]) ? $value : $arg;
|
5009 |
} else {
|
5010 |
-
$keyArgs[$key] = $value;
|
5011 |
}
|
5012 |
}
|
5013 |
|
@@ -5018,276 +6399,479 @@ class Compiler
|
|
5018 |
if (\in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) {
|
5019 |
// notation 100 127 255 / 0 is in fact a simple list of 4 values
|
5020 |
foreach ($args as $k => $arg) {
|
|
|
|
|
|
|
5021 |
if ($arg[1][0] === Type::T_LIST && \count($arg[1][2]) === 3) {
|
5022 |
-
$
|
5023 |
-
|
5024 |
-
if ($last[0] === Type::T_EXPRESSION && $last[1] === '/') {
|
5025 |
-
array_pop($arg[1][2]);
|
5026 |
-
$arg[1][2][] = $last[2];
|
5027 |
-
$arg[1][2][] = $last[3];
|
5028 |
-
$args[$k] = $arg;
|
5029 |
-
}
|
5030 |
}
|
5031 |
}
|
5032 |
}
|
5033 |
|
5034 |
-
$
|
5035 |
|
5036 |
if (! \is_array(reset($prototypes))) {
|
5037 |
$prototypes = [$prototypes];
|
5038 |
}
|
5039 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5040 |
$keyArgs = [];
|
5041 |
|
5042 |
-
|
5043 |
-
|
5044 |
-
$exceptionMessage = '';
|
5045 |
|
5046 |
-
|
5047 |
-
|
|
|
|
|
|
|
5048 |
|
5049 |
-
|
5050 |
-
|
5051 |
-
$
|
5052 |
-
|
5053 |
|
5054 |
-
|
5055 |
-
|
|
|
5056 |
|
5057 |
-
|
5058 |
-
|
5059 |
-
$default = [Type::T_KEYWORD, 'null'];
|
5060 |
-
} else {
|
5061 |
-
if (\is_null($parser)) {
|
5062 |
-
$parser = $this->parserFactory(__METHOD__);
|
5063 |
-
}
|
5064 |
|
5065 |
-
|
5066 |
-
|
5067 |
-
|
5068 |
|
5069 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5070 |
|
5071 |
-
|
5072 |
-
|
5073 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5074 |
}
|
|
|
5075 |
|
5076 |
-
|
|
|
|
|
|
|
5077 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5078 |
|
5079 |
-
|
5080 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5081 |
|
5082 |
-
|
5083 |
-
|
|
|
|
|
|
|
5084 |
|
5085 |
-
|
5086 |
-
foreach ($prototype as $i => $p) {
|
5087 |
-
$name = explode(':', $p)[0];
|
5088 |
|
5089 |
-
|
5090 |
-
|
5091 |
-
|
5092 |
}
|
5093 |
|
5094 |
-
//
|
5095 |
-
|
5096 |
-
|
|
|
5097 |
}
|
|
|
5098 |
|
5099 |
-
|
5100 |
-
|
|
|
5101 |
|
5102 |
-
|
5103 |
-
|
5104 |
-
$name = explode(':', $p)[0];
|
5105 |
|
5106 |
-
|
5107 |
-
|
5108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5109 |
|
5110 |
-
|
5111 |
-
|
5112 |
-
|
5113 |
-
|
|
|
|
|
5114 |
}
|
5115 |
-
|
5116 |
-
|
5117 |
-
|
|
|
5118 |
}
|
5119 |
-
$this->ignoreCallStackMessage = $ignoreCallStackMessage;
|
5120 |
}
|
5121 |
|
5122 |
-
if ($
|
5123 |
-
|
5124 |
}
|
5125 |
|
5126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5127 |
}
|
5128 |
|
5129 |
/**
|
5130 |
-
*
|
5131 |
*
|
5132 |
-
* @param array
|
5133 |
-
* @param
|
5134 |
-
* @param
|
5135 |
-
* @param
|
5136 |
-
* only used if $storeInEnv = false
|
5137 |
*
|
5138 |
-
* @return
|
5139 |
*
|
5140 |
-
* @throws
|
|
|
|
|
5141 |
*/
|
5142 |
-
|
5143 |
{
|
5144 |
-
$
|
5145 |
|
5146 |
-
|
5147 |
-
|
5148 |
-
}
|
5149 |
|
5150 |
-
|
5151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5152 |
|
5153 |
-
|
5154 |
-
|
5155 |
}
|
5156 |
|
5157 |
-
$
|
5158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5159 |
|
5160 |
-
|
5161 |
-
|
|
|
|
|
5162 |
|
5163 |
-
|
5164 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5165 |
}
|
|
|
5166 |
|
5167 |
-
|
5168 |
-
|
5169 |
-
|
5170 |
-
|
5171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5172 |
|
5173 |
-
|
5174 |
-
|
5175 |
-
if (! empty($arg[0])) {
|
5176 |
$hasKeywordArgument = true;
|
5177 |
|
5178 |
-
$
|
5179 |
-
|
5180 |
-
foreach (array_keys($args) as $an) {
|
5181 |
-
if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) {
|
5182 |
-
$name = $an;
|
5183 |
-
break;
|
5184 |
-
}
|
5185 |
-
}
|
5186 |
-
}
|
5187 |
|
5188 |
-
if (
|
5189 |
-
|
5190 |
-
$deferredKeywordArgs[$name] = $arg[1];
|
5191 |
-
} else {
|
5192 |
-
$this->throwError("Mixin or function doesn't have an argument named $%s.", $arg[0][1]);
|
5193 |
-
break;
|
5194 |
-
}
|
5195 |
-
} elseif ($args[$name][0] < \count($remaining)) {
|
5196 |
-
$this->throwError("The argument $%s was passed both by position and by name.", $arg[0][1]);
|
5197 |
-
break;
|
5198 |
-
} else {
|
5199 |
-
$keywordArgs[$name] = $arg[1];
|
5200 |
}
|
5201 |
-
|
|
|
|
|
|
|
|
|
5202 |
$val = $this->reduce($arg[1], true);
|
|
|
5203 |
|
5204 |
if ($val[0] === Type::T_LIST) {
|
5205 |
-
foreach ($val[2] as $
|
5206 |
-
if (
|
5207 |
-
|
5208 |
-
|
5209 |
-
|
5210 |
-
|
5211 |
-
|
5212 |
-
|
5213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5214 |
}
|
5215 |
|
5216 |
-
|
5217 |
-
|
5218 |
-
|
5219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5220 |
}
|
|
|
|
|
|
|
|
|
5221 |
} else {
|
5222 |
if (\is_null($splatSeparator)) {
|
5223 |
$splatSeparator = $val[1];
|
5224 |
}
|
5225 |
|
5226 |
-
$
|
5227 |
-
}
|
5228 |
-
}
|
5229 |
-
} elseif ($val[0]
|
5230 |
-
|
5231 |
-
|
5232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5233 |
|
5234 |
-
|
5235 |
-
|
5236 |
-
foreach (array_keys($args) as $an) {
|
5237 |
-
if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) {
|
5238 |
-
$name = $an;
|
5239 |
-
break;
|
5240 |
-
}
|
5241 |
-
}
|
5242 |
-
}
|
5243 |
|
5244 |
-
|
5245 |
-
|
5246 |
-
|
5247 |
-
$keywordArgs[$name] = $item;
|
5248 |
-
}
|
5249 |
-
} else {
|
5250 |
-
if (\is_null($splatSeparator)) {
|
5251 |
-
$splatSeparator = $val[1];
|
5252 |
-
}
|
5253 |
|
5254 |
-
|
5255 |
-
|
5256 |
-
|
5257 |
-
} else {
|
5258 |
-
$remaining[] = $val;
|
5259 |
-
}
|
5260 |
-
} elseif ($hasKeywordArgument) {
|
5261 |
-
$this->throwError('Positional arguments must come before keyword arguments.');
|
5262 |
-
break;
|
5263 |
} else {
|
5264 |
-
$
|
5265 |
}
|
5266 |
}
|
5267 |
|
5268 |
-
|
5269 |
-
list($i, $name, $default, $isVariable) = $arg;
|
5270 |
|
5271 |
-
|
5272 |
-
$val = [Type::T_LIST, \is_null($splatSeparator) ? ',' : $splatSeparator , [], $isVariable];
|
5273 |
|
5274 |
-
|
5275 |
-
$val[2][] = $remaining[$i];
|
5276 |
-
}
|
5277 |
|
5278 |
-
|
5279 |
-
|
5280 |
-
|
5281 |
-
|
5282 |
-
$val = $remaining[$i];
|
5283 |
-
} elseif (isset($keywordArgs[$name])) {
|
5284 |
-
$val = $keywordArgs[$name];
|
5285 |
-
} elseif (! empty($default)) {
|
5286 |
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
5287 |
} else {
|
5288 |
-
$this->
|
5289 |
-
break;
|
5290 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
5291 |
|
5292 |
if ($storeInEnv) {
|
5293 |
$this->set($name, $this->reduce($val, true), true, $env);
|
@@ -5300,12 +6884,13 @@ class Compiler
|
|
5300 |
$storeEnv->store = $env->store;
|
5301 |
}
|
5302 |
|
5303 |
-
foreach ($
|
5304 |
-
list($
|
5305 |
|
5306 |
-
if (
|
5307 |
continue;
|
5308 |
}
|
|
|
5309 |
|
5310 |
if ($storeInEnv) {
|
5311 |
$this->set($name, $this->reduce($default, true), true);
|
@@ -5317,16 +6902,77 @@ class Compiler
|
|
5317 |
return $output;
|
5318 |
}
|
5319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5320 |
/**
|
5321 |
* Coerce a php value into a scss one
|
5322 |
*
|
5323 |
* @param mixed $value
|
5324 |
*
|
5325 |
-
* @return array
|
5326 |
*/
|
5327 |
protected function coerceValue($value)
|
5328 |
{
|
5329 |
-
if (\is_array($value) || $value instanceof
|
5330 |
return $value;
|
5331 |
}
|
5332 |
|
@@ -5339,7 +6985,7 @@ class Compiler
|
|
5339 |
}
|
5340 |
|
5341 |
if (is_numeric($value)) {
|
5342 |
-
return new
|
5343 |
}
|
5344 |
|
5345 |
if ($value === '') {
|
@@ -5357,40 +7003,66 @@ class Compiler
|
|
5357 |
}
|
5358 |
|
5359 |
/**
|
5360 |
-
*
|
5361 |
*
|
5362 |
-
* @param array $item
|
5363 |
*
|
5364 |
-
* @return array
|
5365 |
*/
|
5366 |
-
|
5367 |
{
|
|
|
|
|
|
|
|
|
5368 |
if ($item[0] === Type::T_MAP) {
|
5369 |
return $item;
|
5370 |
}
|
5371 |
|
5372 |
-
if (
|
5373 |
-
$item[
|
5374 |
-
$item[2] ===
|
5375 |
) {
|
5376 |
return static::$emptyMap;
|
5377 |
}
|
5378 |
|
5379 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5380 |
}
|
5381 |
|
5382 |
/**
|
5383 |
* Coerce something to list
|
5384 |
*
|
5385 |
-
* @param array
|
5386 |
-
* @param string
|
5387 |
-
* @param
|
5388 |
*
|
5389 |
* @return array
|
5390 |
*/
|
5391 |
protected function coerceList($item, $delim = ',', $removeTrailingNull = false)
|
5392 |
{
|
5393 |
-
if (
|
|
|
|
|
|
|
|
|
5394 |
// remove trailing null from the list
|
5395 |
if ($removeTrailingNull && end($item[2]) === static::$null) {
|
5396 |
array_pop($item[2]);
|
@@ -5399,7 +7071,7 @@ class Compiler
|
|
5399 |
return $item;
|
5400 |
}
|
5401 |
|
5402 |
-
if (
|
5403 |
$keys = $item[1];
|
5404 |
$values = $item[2];
|
5405 |
$list = [];
|
@@ -5408,37 +7080,25 @@ class Compiler
|
|
5408 |
$key = $keys[$i];
|
5409 |
$value = $values[$i];
|
5410 |
|
5411 |
-
switch ($key[0]) {
|
5412 |
-
case Type::T_LIST:
|
5413 |
-
case Type::T_MAP:
|
5414 |
-
case Type::T_STRING:
|
5415 |
-
case Type::T_NULL:
|
5416 |
-
break;
|
5417 |
-
|
5418 |
-
default:
|
5419 |
-
$key = [Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))];
|
5420 |
-
break;
|
5421 |
-
}
|
5422 |
-
|
5423 |
$list[] = [
|
5424 |
Type::T_LIST,
|
5425 |
-
'',
|
5426 |
[$key, $value]
|
5427 |
];
|
5428 |
}
|
5429 |
|
5430 |
-
return [Type::T_LIST, ',', $list];
|
5431 |
}
|
5432 |
|
5433 |
-
return [Type::T_LIST,
|
5434 |
}
|
5435 |
|
5436 |
/**
|
5437 |
* Coerce color for expression
|
5438 |
*
|
5439 |
-
* @param array $value
|
5440 |
*
|
5441 |
-
* @return array|
|
5442 |
*/
|
5443 |
protected function coerceForExpression($value)
|
5444 |
{
|
@@ -5452,12 +7112,17 @@ class Compiler
|
|
5452 |
/**
|
5453 |
* Coerce value to color
|
5454 |
*
|
5455 |
-
* @param array $value
|
|
|
5456 |
*
|
5457 |
* @return array|null
|
5458 |
*/
|
5459 |
protected function coerceColor($value, $inRGBFunction = false)
|
5460 |
{
|
|
|
|
|
|
|
|
|
5461 |
switch ($value[0]) {
|
5462 |
case Type::T_COLOR:
|
5463 |
for ($i = 1; $i <= 3; $i++) {
|
@@ -5543,7 +7208,7 @@ class Compiler
|
|
5543 |
if ($color[3] === 255) {
|
5544 |
$color[3] = 1; // fully opaque
|
5545 |
} else {
|
5546 |
-
$color[3] = round($color[3] / 255,
|
5547 |
}
|
5548 |
}
|
5549 |
|
@@ -5566,10 +7231,10 @@ class Compiler
|
|
5566 |
}
|
5567 |
|
5568 |
/**
|
5569 |
-
* @param
|
5570 |
-
* @param
|
5571 |
*
|
5572 |
-
* @return
|
5573 |
*/
|
5574 |
protected function compileRGBAValue($value, $isAlpha = false)
|
5575 |
{
|
@@ -5581,40 +7246,31 @@ class Compiler
|
|
5581 |
}
|
5582 |
|
5583 |
/**
|
5584 |
-
* @param mixed
|
5585 |
-
* @param
|
5586 |
-
* @param
|
5587 |
-
* @param
|
5588 |
-
* @param boolean $clamp
|
5589 |
-
* @param boolean $modulo
|
5590 |
*
|
5591 |
-
* @return
|
5592 |
*/
|
5593 |
-
protected function compileColorPartValue($value, $min, $max, $isInt = true
|
5594 |
{
|
5595 |
if (! is_numeric($value)) {
|
5596 |
if (\is_array($value)) {
|
5597 |
$reduced = $this->reduce($value);
|
5598 |
|
5599 |
-
if (
|
5600 |
$value = $reduced;
|
5601 |
}
|
5602 |
}
|
5603 |
|
5604 |
-
if (
|
5605 |
-
|
5606 |
-
|
5607 |
-
|
5608 |
-
$
|
5609 |
-
|
5610 |
-
|
5611 |
-
switch ($unit) {
|
5612 |
-
case '%':
|
5613 |
-
$num *= $max / 100;
|
5614 |
-
break;
|
5615 |
-
default:
|
5616 |
-
break;
|
5617 |
-
}
|
5618 |
}
|
5619 |
|
5620 |
$value = $num;
|
@@ -5628,18 +7284,7 @@ class Compiler
|
|
5628 |
$value = round($value);
|
5629 |
}
|
5630 |
|
5631 |
-
|
5632 |
-
$value = min($max, max($min, $value));
|
5633 |
-
}
|
5634 |
-
|
5635 |
-
if ($modulo) {
|
5636 |
-
$value = $value % $max;
|
5637 |
-
|
5638 |
-
// still negative?
|
5639 |
-
while ($value < $min) {
|
5640 |
-
$value += $max;
|
5641 |
-
}
|
5642 |
-
}
|
5643 |
|
5644 |
return $value;
|
5645 |
}
|
@@ -5650,34 +7295,72 @@ class Compiler
|
|
5650 |
/**
|
5651 |
* Coerce value to string
|
5652 |
*
|
5653 |
-
* @param array $value
|
5654 |
*
|
5655 |
-
* @return array
|
5656 |
*/
|
5657 |
protected function coerceString($value)
|
5658 |
{
|
5659 |
if ($value[0] === Type::T_STRING) {
|
|
|
|
|
5660 |
return $value;
|
5661 |
}
|
5662 |
|
5663 |
return [Type::T_STRING, '', [$this->compileValue($value)]];
|
5664 |
}
|
5665 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5666 |
/**
|
5667 |
* Coerce value to a percentage
|
5668 |
*
|
5669 |
-
* @param array $value
|
5670 |
*
|
5671 |
-
* @return
|
|
|
|
|
5672 |
*/
|
5673 |
protected function coercePercent($value)
|
5674 |
{
|
5675 |
-
|
5676 |
-
|
5677 |
-
|
|
|
|
|
5678 |
}
|
5679 |
|
5680 |
-
return $value
|
5681 |
}
|
5682 |
|
5683 |
return 0;
|
@@ -5688,21 +7371,24 @@ class Compiler
|
|
5688 |
*
|
5689 |
* @api
|
5690 |
*
|
5691 |
-
* @param array $value
|
|
|
5692 |
*
|
5693 |
* @return array
|
5694 |
*
|
5695 |
-
* @throws
|
5696 |
*/
|
5697 |
-
public function assertMap($value)
|
5698 |
{
|
5699 |
-
$
|
5700 |
|
5701 |
-
if ($
|
5702 |
-
$this->
|
|
|
|
|
5703 |
}
|
5704 |
|
5705 |
-
return $
|
5706 |
}
|
5707 |
|
5708 |
/**
|
@@ -5710,7 +7396,7 @@ class Compiler
|
|
5710 |
*
|
5711 |
* @api
|
5712 |
*
|
5713 |
-
* @param array $value
|
5714 |
*
|
5715 |
* @return array
|
5716 |
*
|
@@ -5719,30 +7405,55 @@ class Compiler
|
|
5719 |
public function assertList($value)
|
5720 |
{
|
5721 |
if ($value[0] !== Type::T_LIST) {
|
5722 |
-
$this->
|
5723 |
}
|
|
|
5724 |
|
5725 |
return $value;
|
5726 |
}
|
5727 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5728 |
/**
|
5729 |
* Assert value is a color
|
5730 |
*
|
5731 |
* @api
|
5732 |
*
|
5733 |
-
* @param array $value
|
|
|
5734 |
*
|
5735 |
* @return array
|
5736 |
*
|
5737 |
-
* @throws
|
5738 |
*/
|
5739 |
-
public function assertColor($value)
|
5740 |
{
|
5741 |
if ($color = $this->coerceColor($value)) {
|
5742 |
return $color;
|
5743 |
}
|
5744 |
|
5745 |
-
$this->
|
|
|
|
|
5746 |
}
|
5747 |
|
5748 |
/**
|
@@ -5750,21 +7461,64 @@ class Compiler
|
|
5750 |
*
|
5751 |
* @api
|
5752 |
*
|
5753 |
-
* @param array $value
|
|
|
5754 |
*
|
5755 |
-
* @return
|
5756 |
*
|
5757 |
-
* @throws
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5758 |
*/
|
5759 |
-
public function
|
5760 |
{
|
5761 |
-
|
5762 |
-
|
|
|
5763 |
}
|
5764 |
|
5765 |
-
return $value
|
5766 |
}
|
5767 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5768 |
/**
|
5769 |
* Make sure a color's components don't go out of bounds
|
5770 |
*
|
@@ -5782,6 +7536,10 @@ class Compiler
|
|
5782 |
if ($c[$i] > 255) {
|
5783 |
$c[$i] = 255;
|
5784 |
}
|
|
|
|
|
|
|
|
|
5785 |
}
|
5786 |
|
5787 |
return $c;
|
@@ -5790,11 +7548,11 @@ class Compiler
|
|
5790 |
/**
|
5791 |
* Convert RGB to HSL
|
5792 |
*
|
5793 |
-
* @
|
5794 |
*
|
5795 |
-
* @param
|
5796 |
-
* @param
|
5797 |
-
* @param
|
5798 |
*
|
5799 |
* @return array
|
5800 |
*/
|
@@ -5819,12 +7577,12 @@ class Compiler
|
|
5819 |
$h = 60 * ($green - $blue) / $d;
|
5820 |
} elseif ($green == $max) {
|
5821 |
$h = 60 * ($blue - $red) / $d + 120;
|
5822 |
-
}
|
5823 |
$h = 60 * ($red - $green) / $d + 240;
|
5824 |
}
|
5825 |
}
|
5826 |
|
5827 |
-
return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
|
5828 |
}
|
5829 |
|
5830 |
/**
|
@@ -5853,7 +7611,7 @@ class Compiler
|
|
5853 |
}
|
5854 |
|
5855 |
if ($h * 3 < 2) {
|
5856 |
-
return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
|
5857 |
}
|
5858 |
|
5859 |
return $m1;
|
@@ -5862,11 +7620,11 @@ class Compiler
|
|
5862 |
/**
|
5863 |
* Convert HSL to RGB
|
5864 |
*
|
5865 |
-
* @
|
5866 |
*
|
5867 |
-
* @param
|
5868 |
-
* @param
|
5869 |
-
* @param
|
5870 |
*
|
5871 |
* @return array
|
5872 |
*/
|
@@ -5883,35 +7641,133 @@ class Compiler
|
|
5883 |
$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
|
5884 |
$m1 = $l * 2 - $m2;
|
5885 |
|
5886 |
-
$r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
|
5887 |
$g = $this->hueToRGB($m1, $m2, $h) * 255;
|
5888 |
-
$b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;
|
5889 |
|
5890 |
$out = [Type::T_COLOR, $r, $g, $b];
|
5891 |
|
5892 |
return $out;
|
5893 |
}
|
5894 |
|
5895 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5896 |
|
5897 |
-
|
5898 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5899 |
{
|
5900 |
-
$
|
5901 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
5902 |
|
5903 |
-
|
5904 |
-
|
5905 |
-
|
5906 |
-
$
|
5907 |
} else {
|
5908 |
-
$
|
5909 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5910 |
|
5911 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5912 |
}
|
5913 |
|
5914 |
-
|
|
|
|
|
|
|
|
|
5915 |
}
|
5916 |
|
5917 |
protected static $libIf = ['condition', 'if-true', 'if-false:'];
|
@@ -5931,11 +7787,8 @@ class Compiler
|
|
5931 |
{
|
5932 |
list($list, $value) = $args;
|
5933 |
|
5934 |
-
if (
|
5935 |
-
|
5936 |
-
}
|
5937 |
-
|
5938 |
-
if ($list[0] === Type::T_MAP ||
|
5939 |
$list[0] === Type::T_STRING ||
|
5940 |
$list[0] === Type::T_KEYWORD ||
|
5941 |
$list[0] === Type::T_INTERPOLATE
|
@@ -5943,7 +7796,23 @@ class Compiler
|
|
5943 |
$list = $this->coerceList($list, ' ');
|
5944 |
}
|
5945 |
|
5946 |
-
if ($list[0] !== Type::T_LIST) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5947 |
return static::$null;
|
5948 |
}
|
5949 |
|
@@ -5955,7 +7824,7 @@ class Compiler
|
|
5955 |
|
5956 |
$key = array_search($this->normalizeValue($value), $values);
|
5957 |
|
5958 |
-
return false === $key ? static::$null : $key + 1;
|
5959 |
}
|
5960 |
|
5961 |
protected static $libRgb = [
|
@@ -5964,6 +7833,14 @@ class Compiler
|
|
5964 |
['channels'],
|
5965 |
['red', 'green', 'blue'],
|
5966 |
['red', 'green', 'blue', 'alpha'] ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5967 |
protected function libRgb($args, $kwargs, $funcName = 'rgb')
|
5968 |
{
|
5969 |
switch (\count($args)) {
|
@@ -5977,7 +7854,7 @@ class Compiler
|
|
5977 |
$color = [Type::T_COLOR, $args[0], $args[1], $args[2]];
|
5978 |
|
5979 |
if (! $color = $this->coerceColor($color)) {
|
5980 |
-
$color = [Type::T_STRING, '', [$funcName .'(', $args[0], ', ', $args[1], ', ', $args[2], ')']];
|
5981 |
}
|
5982 |
|
5983 |
return $color;
|
@@ -5993,7 +7870,7 @@ class Compiler
|
|
5993 |
[$funcName . '(', $color[1], ', ', $color[2], ', ', $color[3], ', ', $alpha, ')']];
|
5994 |
}
|
5995 |
} else {
|
5996 |
-
$color = [Type::T_STRING, '', [$funcName . '(', $args[0], ')']];
|
5997 |
}
|
5998 |
break;
|
5999 |
|
@@ -6022,32 +7899,125 @@ class Compiler
|
|
6022 |
return $this->libRgb($args, $kwargs, 'rgba');
|
6023 |
}
|
6024 |
|
6025 |
-
|
6026 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6027 |
{
|
6028 |
-
$color = $this->assertColor($args[0]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6029 |
|
6030 |
-
|
6031 |
-
|
6032 |
-
$val = $this->assertNumber($args[$iarg]);
|
6033 |
|
6034 |
-
|
6035 |
-
|
|
|
|
|
6036 |
}
|
|
|
|
|
|
|
|
|
|
|
6037 |
|
6038 |
-
|
|
|
6039 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6040 |
}
|
6041 |
|
6042 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6043 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
6044 |
|
6045 |
-
|
6046 |
-
|
6047 |
-
$val = $this->assertNumber($args[$iarg]);
|
6048 |
-
$hsl[$ihsl] = \call_user_func($fn, $hsl[$ihsl], $val, $iarg);
|
6049 |
-
}
|
6050 |
}
|
|
|
|
|
6051 |
|
6052 |
$rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
|
6053 |
|
@@ -6058,58 +8028,54 @@ class Compiler
|
|
6058 |
$color = $rgb;
|
6059 |
}
|
6060 |
|
|
|
|
|
|
|
|
|
|
|
6061 |
return $color;
|
6062 |
}
|
6063 |
|
6064 |
-
protected static $libAdjustColor = [
|
6065 |
-
'color', 'red:null', 'green:null', 'blue:null',
|
6066 |
-
'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
|
6067 |
-
];
|
6068 |
protected function libAdjustColor($args)
|
6069 |
{
|
6070 |
-
return $this->alterColor($args, function ($base, $alter, $
|
6071 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6072 |
});
|
6073 |
}
|
6074 |
|
6075 |
-
protected static $libChangeColor = [
|
6076 |
-
'color', 'red:null', 'green:null', 'blue:null',
|
6077 |
-
'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
|
6078 |
-
];
|
6079 |
protected function libChangeColor($args)
|
6080 |
{
|
6081 |
-
return $this->alterColor($args, function ($base, $alter, $
|
|
|
|
|
|
|
|
|
6082 |
return $alter;
|
6083 |
});
|
6084 |
}
|
6085 |
|
6086 |
-
protected static $libScaleColor = [
|
6087 |
-
'color', 'red:null', 'green:null', 'blue:null',
|
6088 |
-
'hue:null', 'saturation:null', 'lightness:null', 'alpha:null'
|
6089 |
-
];
|
6090 |
protected function libScaleColor($args)
|
6091 |
{
|
6092 |
-
return $this->alterColor($args, function ($base, $scale, $
|
6093 |
-
|
6094 |
-
|
6095 |
-
// 7 - a
|
6096 |
-
switch ($i) {
|
6097 |
-
case 1:
|
6098 |
-
case 2:
|
6099 |
-
case 3:
|
6100 |
-
$max = 255;
|
6101 |
-
break;
|
6102 |
-
|
6103 |
-
case 4:
|
6104 |
-
$max = 360;
|
6105 |
-
break;
|
6106 |
-
|
6107 |
-
case 7:
|
6108 |
-
$max = 1;
|
6109 |
-
break;
|
6110 |
-
|
6111 |
-
default:
|
6112 |
-
$max = 100;
|
6113 |
}
|
6114 |
|
6115 |
$scale = $scale / 100;
|
@@ -6126,6 +8092,11 @@ class Compiler
|
|
6126 |
protected function libIeHexStr($args)
|
6127 |
{
|
6128 |
$color = $this->coerceColor($args[0]);
|
|
|
|
|
|
|
|
|
|
|
6129 |
$color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
|
6130 |
|
6131 |
return [Type::T_STRING, '', [sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3])]];
|
@@ -6136,7 +8107,11 @@ class Compiler
|
|
6136 |
{
|
6137 |
$color = $this->coerceColor($args[0]);
|
6138 |
|
6139 |
-
|
|
|
|
|
|
|
|
|
6140 |
}
|
6141 |
|
6142 |
protected static $libGreen = ['color'];
|
@@ -6144,7 +8119,11 @@ class Compiler
|
|
6144 |
{
|
6145 |
$color = $this->coerceColor($args[0]);
|
6146 |
|
6147 |
-
|
|
|
|
|
|
|
|
|
6148 |
}
|
6149 |
|
6150 |
protected static $libBlue = ['color'];
|
@@ -6152,14 +8131,18 @@ class Compiler
|
|
6152 |
{
|
6153 |
$color = $this->coerceColor($args[0]);
|
6154 |
|
6155 |
-
|
|
|
|
|
|
|
|
|
6156 |
}
|
6157 |
|
6158 |
protected static $libAlpha = ['color'];
|
6159 |
protected function libAlpha($args)
|
6160 |
{
|
6161 |
if ($color = $this->coerceColor($args[0])) {
|
6162 |
-
return isset($color[4]) ? $color[4] : 1;
|
6163 |
}
|
6164 |
|
6165 |
// this might be the IE function, so return value unchanged
|
@@ -6171,7 +8154,7 @@ class Compiler
|
|
6171 |
{
|
6172 |
$value = $args[0];
|
6173 |
|
6174 |
-
if ($value
|
6175 |
return null;
|
6176 |
}
|
6177 |
|
@@ -6179,76 +8162,127 @@ class Compiler
|
|
6179 |
}
|
6180 |
|
6181 |
// mix two colors
|
6182 |
-
protected static $libMix = [
|
|
|
|
|
|
|
6183 |
protected function libMix($args)
|
6184 |
{
|
6185 |
list($first, $second, $weight) = $args;
|
6186 |
|
6187 |
-
$first = $this->assertColor($first);
|
6188 |
-
$second = $this->assertColor($second);
|
6189 |
-
|
6190 |
-
if (! isset($weight)) {
|
6191 |
-
$weight = 0.5;
|
6192 |
-
} else {
|
6193 |
-
$weight = $this->coercePercent($weight);
|
6194 |
-
}
|
6195 |
|
6196 |
$firstAlpha = isset($first[4]) ? $first[4] : 1;
|
6197 |
$secondAlpha = isset($second[4]) ? $second[4] : 1;
|
6198 |
|
6199 |
-
$
|
6200 |
-
$
|
6201 |
|
6202 |
-
$
|
6203 |
-
$
|
|
|
6204 |
|
6205 |
$new = [Type::T_COLOR,
|
6206 |
-
$
|
6207 |
-
$
|
6208 |
-
$
|
6209 |
];
|
6210 |
|
6211 |
if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
|
6212 |
-
$new[] = $firstAlpha * $
|
6213 |
}
|
6214 |
|
6215 |
return $this->fixColor($new);
|
6216 |
}
|
6217 |
|
6218 |
-
protected static $libHsl =[
|
6219 |
['channels'],
|
|
|
6220 |
['hue', 'saturation', 'lightness'],
|
6221 |
['hue', 'saturation', 'lightness', 'alpha'] ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6222 |
protected function libHsl($args, $kwargs, $funcName = 'hsl')
|
6223 |
{
|
|
|
|
|
6224 |
if (\count($args) == 1) {
|
6225 |
if ($args[0][0] !== Type::T_LIST || \count($args[0][2]) < 3 || \count($args[0][2]) > 4) {
|
6226 |
return [Type::T_STRING, '', [$funcName . '(', $args[0], ')']];
|
6227 |
}
|
6228 |
|
6229 |
$args = $args[0][2];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6230 |
}
|
6231 |
|
6232 |
-
|
6233 |
-
|
6234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6236 |
$alpha = null;
|
6237 |
|
6238 |
if (\count($args) === 4) {
|
6239 |
$alpha = $this->compileColorPartValue($args[3], 0, 100, false);
|
6240 |
|
6241 |
-
if (
|
6242 |
return [Type::T_STRING, '',
|
6243 |
[$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ', ', $args[3], ')']];
|
6244 |
}
|
6245 |
} else {
|
6246 |
-
if (
|
6247 |
return [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ')']];
|
6248 |
}
|
6249 |
}
|
6250 |
|
6251 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
6252 |
|
6253 |
if (! \is_null($alpha)) {
|
6254 |
$color[4] = $alpha;
|
@@ -6259,7 +8293,9 @@ class Compiler
|
|
6259 |
|
6260 |
protected static $libHsla = [
|
6261 |
['channels'],
|
6262 |
-
['hue', 'saturation',
|
|
|
|
|
6263 |
protected function libHsla($args, $kwargs)
|
6264 |
{
|
6265 |
return $this->libHsl($args, $kwargs, 'hsla');
|
@@ -6268,34 +8304,173 @@ class Compiler
|
|
6268 |
protected static $libHue = ['color'];
|
6269 |
protected function libHue($args)
|
6270 |
{
|
6271 |
-
$color = $this->assertColor($args[0]);
|
6272 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
6273 |
|
6274 |
-
return new
|
6275 |
}
|
6276 |
|
6277 |
protected static $libSaturation = ['color'];
|
6278 |
protected function libSaturation($args)
|
6279 |
{
|
6280 |
-
$color = $this->assertColor($args[0]);
|
6281 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
6282 |
|
6283 |
-
return new
|
6284 |
}
|
6285 |
|
6286 |
protected static $libLightness = ['color'];
|
6287 |
protected function libLightness($args)
|
6288 |
{
|
6289 |
-
$color = $this->assertColor($args[0]);
|
6290 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
6291 |
|
6292 |
-
return new
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6293 |
}
|
|
|
6294 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6295 |
protected function adjustHsl($color, $idx, $amount)
|
6296 |
{
|
6297 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
6298 |
$hsl[$idx] += $amount;
|
|
|
|
|
|
|
|
|
|
|
|
|
6299 |
$out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
|
6300 |
|
6301 |
if (isset($color[4])) {
|
@@ -6308,8 +8483,8 @@ class Compiler
|
|
6308 |
protected static $libAdjustHue = ['color', 'degrees'];
|
6309 |
protected function libAdjustHue($args)
|
6310 |
{
|
6311 |
-
$color = $this->assertColor($args[0]);
|
6312 |
-
$degrees = $this->assertNumber($args[1]);
|
6313 |
|
6314 |
return $this->adjustHsl($color, 1, $degrees);
|
6315 |
}
|
@@ -6317,7 +8492,7 @@ class Compiler
|
|
6317 |
protected static $libLighten = ['color', 'amount'];
|
6318 |
protected function libLighten($args)
|
6319 |
{
|
6320 |
-
$color = $this->assertColor($args[0]);
|
6321 |
$amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%');
|
6322 |
|
6323 |
return $this->adjustHsl($color, 3, $amount);
|
@@ -6326,34 +8501,36 @@ class Compiler
|
|
6326 |
protected static $libDarken = ['color', 'amount'];
|
6327 |
protected function libDarken($args)
|
6328 |
{
|
6329 |
-
$color = $this->assertColor($args[0]);
|
6330 |
$amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%');
|
6331 |
|
6332 |
return $this->adjustHsl($color, 3, -$amount);
|
6333 |
}
|
6334 |
|
6335 |
-
protected static $libSaturate = [['color', 'amount'], ['
|
6336 |
protected function libSaturate($args)
|
6337 |
{
|
6338 |
$value = $args[0];
|
6339 |
|
6340 |
-
if ($
|
|
|
|
|
6341 |
return null;
|
6342 |
}
|
6343 |
|
6344 |
-
$color = $this->assertColor($
|
6345 |
-
$amount =
|
6346 |
|
6347 |
-
return $this->adjustHsl($color, 2, $amount);
|
6348 |
}
|
6349 |
|
6350 |
protected static $libDesaturate = ['color', 'amount'];
|
6351 |
protected function libDesaturate($args)
|
6352 |
{
|
6353 |
-
$color = $this->assertColor($args[0]);
|
6354 |
-
$amount =
|
6355 |
|
6356 |
-
return $this->adjustHsl($color, 2, -$amount);
|
6357 |
}
|
6358 |
|
6359 |
protected static $libGrayscale = ['color'];
|
@@ -6361,55 +8538,51 @@ class Compiler
|
|
6361 |
{
|
6362 |
$value = $args[0];
|
6363 |
|
6364 |
-
if ($value
|
6365 |
return null;
|
6366 |
}
|
6367 |
|
6368 |
-
return $this->adjustHsl($this->assertColor($value), 2, -100);
|
6369 |
}
|
6370 |
|
6371 |
protected static $libComplement = ['color'];
|
6372 |
protected function libComplement($args)
|
6373 |
{
|
6374 |
-
return $this->adjustHsl($this->assertColor($args[0]), 1, 180);
|
6375 |
}
|
6376 |
|
6377 |
-
protected static $libInvert = ['color', 'weight:
|
6378 |
protected function libInvert($args)
|
6379 |
{
|
6380 |
-
|
6381 |
|
6382 |
-
|
6383 |
-
|
6384 |
-
|
6385 |
-
$weight
|
6386 |
-
|
|
|
6387 |
|
6388 |
-
if ($value[0] === Type::T_NUMBER) {
|
6389 |
return null;
|
6390 |
}
|
6391 |
|
6392 |
-
$color = $this->assertColor($value);
|
6393 |
$inverted = $color;
|
6394 |
$inverted[1] = 255 - $inverted[1];
|
6395 |
$inverted[2] = 255 - $inverted[2];
|
6396 |
$inverted[3] = 255 - $inverted[3];
|
6397 |
|
6398 |
-
|
6399 |
-
return $this->libMix([$inverted, $color, [Type::T_NUMBER, $weight]]);
|
6400 |
-
}
|
6401 |
-
|
6402 |
-
return $inverted;
|
6403 |
}
|
6404 |
|
6405 |
// increases opacity by amount
|
6406 |
protected static $libOpacify = ['color', 'amount'];
|
6407 |
protected function libOpacify($args)
|
6408 |
{
|
6409 |
-
$color = $this->assertColor($args[0]);
|
6410 |
-
$amount = $this->
|
6411 |
|
6412 |
-
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
|
6413 |
$color[4] = min(1, max(0, $color[4]));
|
6414 |
|
6415 |
return $color;
|
@@ -6425,10 +8598,10 @@ class Compiler
|
|
6425 |
protected static $libTransparentize = ['color', 'amount'];
|
6426 |
protected function libTransparentize($args)
|
6427 |
{
|
6428 |
-
$color = $this->assertColor($args[0]);
|
6429 |
-
$amount = $this->
|
6430 |
|
6431 |
-
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
|
6432 |
$color[4] = min(1, max(0, $color[4]));
|
6433 |
|
6434 |
return $color;
|
@@ -6443,144 +8616,121 @@ class Compiler
|
|
6443 |
protected static $libUnquote = ['string'];
|
6444 |
protected function libUnquote($args)
|
6445 |
{
|
6446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6447 |
|
6448 |
-
|
6449 |
-
|
|
|
6450 |
}
|
6451 |
|
|
|
|
|
6452 |
return $str;
|
6453 |
}
|
6454 |
|
6455 |
protected static $libQuote = ['string'];
|
6456 |
protected function libQuote($args)
|
6457 |
{
|
6458 |
-
$value = $args[0];
|
6459 |
|
6460 |
-
|
6461 |
-
return $value;
|
6462 |
-
}
|
6463 |
|
6464 |
-
return
|
6465 |
}
|
6466 |
|
6467 |
protected static $libPercentage = ['number'];
|
6468 |
protected function libPercentage($args)
|
6469 |
{
|
6470 |
-
|
|
|
|
|
|
|
6471 |
}
|
6472 |
|
6473 |
protected static $libRound = ['number'];
|
6474 |
protected function libRound($args)
|
6475 |
{
|
6476 |
-
$num = $args[0];
|
6477 |
|
6478 |
-
return new
|
6479 |
}
|
6480 |
|
6481 |
protected static $libFloor = ['number'];
|
6482 |
protected function libFloor($args)
|
6483 |
{
|
6484 |
-
$num = $args[0];
|
6485 |
|
6486 |
-
return new
|
6487 |
}
|
6488 |
|
6489 |
protected static $libCeil = ['number'];
|
6490 |
protected function libCeil($args)
|
6491 |
{
|
6492 |
-
$num = $args[0];
|
6493 |
|
6494 |
-
return new
|
6495 |
}
|
6496 |
|
6497 |
protected static $libAbs = ['number'];
|
6498 |
protected function libAbs($args)
|
6499 |
{
|
6500 |
-
$num = $args[0];
|
6501 |
|
6502 |
-
return new
|
6503 |
}
|
6504 |
|
|
|
6505 |
protected function libMin($args)
|
6506 |
{
|
6507 |
-
|
6508 |
-
|
6509 |
-
|
|
|
6510 |
|
6511 |
-
foreach ($
|
6512 |
-
|
6513 |
|
6514 |
-
if (\is_null($
|
6515 |
-
|
6516 |
-
$minOriginal = $original;
|
6517 |
-
$minNormalized = $normalized;
|
6518 |
-
}
|
6519 |
-
} elseif ($normalized[1] <= $minNormalized[1]) {
|
6520 |
-
$minOriginal = $original;
|
6521 |
-
$minNormalized = $normalized;
|
6522 |
}
|
6523 |
}
|
6524 |
|
6525 |
-
|
6526 |
-
|
6527 |
-
|
6528 |
-
protected function libMax($args)
|
6529 |
-
{
|
6530 |
-
$numbers = $this->getNormalizedNumbers($args);
|
6531 |
-
$maxOriginal = null;
|
6532 |
-
$maxNormalized = null;
|
6533 |
-
|
6534 |
-
foreach ($numbers as $key => $pair) {
|
6535 |
-
list($original, $normalized) = $pair;
|
6536 |
-
|
6537 |
-
if (\is_null($normalized) or \is_null($maxNormalized)) {
|
6538 |
-
if (\is_null($maxOriginal) || $original[1] >= $maxOriginal[1]) {
|
6539 |
-
$maxOriginal = $original;
|
6540 |
-
$maxNormalized = $normalized;
|
6541 |
-
}
|
6542 |
-
} elseif ($normalized[1] >= $maxNormalized[1]) {
|
6543 |
-
$maxOriginal = $original;
|
6544 |
-
$maxNormalized = $normalized;
|
6545 |
-
}
|
6546 |
}
|
6547 |
|
6548 |
-
|
6549 |
}
|
6550 |
|
6551 |
-
|
6552 |
-
|
6553 |
-
*
|
6554 |
-
* @param array $args
|
6555 |
-
*
|
6556 |
-
* @return array
|
6557 |
-
*/
|
6558 |
-
protected function getNormalizedNumbers($args)
|
6559 |
{
|
6560 |
-
|
6561 |
-
|
6562 |
-
|
6563 |
-
|
6564 |
-
foreach ($args as $key => $item) {
|
6565 |
-
if ($item[0] !== Type::T_NUMBER) {
|
6566 |
-
$this->throwError('%s is not a number', $item[0]);
|
6567 |
-
break;
|
6568 |
-
}
|
6569 |
|
6570 |
-
|
|
|
6571 |
|
6572 |
-
if (
|
6573 |
-
$
|
6574 |
-
$originalUnit = $item->unitStr();
|
6575 |
-
} elseif ($number[1] && $unit !== $number[2] && ! empty($number[2])) {
|
6576 |
-
$this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr());
|
6577 |
-
break;
|
6578 |
}
|
|
|
6579 |
|
6580 |
-
|
|
|
6581 |
}
|
6582 |
|
6583 |
-
|
6584 |
}
|
6585 |
|
6586 |
protected static $libLength = ['list'];
|
@@ -6588,34 +8738,38 @@ class Compiler
|
|
6588 |
{
|
6589 |
$list = $this->coerceList($args[0], ',', true);
|
6590 |
|
6591 |
-
return \count($list[2]);
|
6592 |
}
|
6593 |
|
6594 |
-
|
6595 |
protected function libListSeparator($args)
|
6596 |
{
|
6597 |
-
if (\
|
6598 |
-
return '
|
6599 |
}
|
6600 |
|
6601 |
$list = $this->coerceList($args[0]);
|
6602 |
|
6603 |
-
if (\count($list[2]) <= 1) {
|
6604 |
-
return 'space';
|
6605 |
}
|
6606 |
|
6607 |
if ($list[1] === ',') {
|
6608 |
-
return 'comma';
|
|
|
|
|
|
|
|
|
6609 |
}
|
6610 |
|
6611 |
-
return 'space';
|
6612 |
}
|
6613 |
|
6614 |
protected static $libNth = ['list', 'n'];
|
6615 |
protected function libNth($args)
|
6616 |
{
|
6617 |
$list = $this->coerceList($args[0], ',', false);
|
6618 |
-
$n = $this->assertNumber($args[1]);
|
6619 |
|
6620 |
if ($n > 0) {
|
6621 |
$n--;
|
@@ -6630,7 +8784,7 @@ class Compiler
|
|
6630 |
protected function libSetNth($args)
|
6631 |
{
|
6632 |
$list = $this->coerceList($args[0]);
|
6633 |
-
$n = $this->assertNumber($args[1]);
|
6634 |
|
6635 |
if ($n > 0) {
|
6636 |
$n--;
|
@@ -6639,39 +8793,86 @@ class Compiler
|
|
6639 |
}
|
6640 |
|
6641 |
if (! isset($list[2][$n])) {
|
6642 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6643 |
|
6644 |
-
|
|
|
6645 |
}
|
6646 |
|
6647 |
-
|
6648 |
-
|
6649 |
-
return $list;
|
6650 |
}
|
6651 |
|
6652 |
-
|
6653 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6654 |
{
|
6655 |
-
$
|
6656 |
-
$key = $args[1];
|
6657 |
|
6658 |
-
|
6659 |
-
$key
|
6660 |
-
|
6661 |
-
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
6662 |
-
if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
|
6663 |
-
return $map[2][$i];
|
6664 |
-
}
|
6665 |
}
|
6666 |
}
|
6667 |
|
6668 |
-
return
|
6669 |
}
|
6670 |
|
6671 |
protected static $libMapKeys = ['map'];
|
6672 |
protected function libMapKeys($args)
|
6673 |
{
|
6674 |
-
$map = $this->assertMap($args[0]);
|
6675 |
$keys = $map[1];
|
6676 |
|
6677 |
return [Type::T_LIST, ',', $keys];
|
@@ -6680,20 +8881,33 @@ class Compiler
|
|
6680 |
protected static $libMapValues = ['map'];
|
6681 |
protected function libMapValues($args)
|
6682 |
{
|
6683 |
-
$map = $this->assertMap($args[0]);
|
6684 |
$values = $map[2];
|
6685 |
|
6686 |
return [Type::T_LIST, ',', $values];
|
6687 |
}
|
6688 |
|
6689 |
-
protected static $libMapRemove = [
|
|
|
|
|
|
|
6690 |
protected function libMapRemove($args)
|
6691 |
{
|
6692 |
-
$map = $this->assertMap($args[0]);
|
6693 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6694 |
|
6695 |
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
6696 |
-
if ($
|
6697 |
array_splice($map[1], $i, 1);
|
6698 |
array_splice($map[2], $i, 1);
|
6699 |
}
|
@@ -6702,11 +8916,38 @@ class Compiler
|
|
6702 |
return $map;
|
6703 |
}
|
6704 |
|
6705 |
-
protected static $libMapHasKey = ['map', 'key'];
|
6706 |
protected function libMapHasKey($args)
|
6707 |
{
|
6708 |
-
$map = $this->assertMap($args[0]);
|
6709 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6710 |
|
6711 |
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
6712 |
if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
|
@@ -6717,23 +8958,129 @@ class Compiler
|
|
6717 |
return false;
|
6718 |
}
|
6719 |
|
6720 |
-
protected static $libMapMerge = [
|
|
|
|
|
|
|
|
|
6721 |
protected function libMapMerge($args)
|
6722 |
{
|
6723 |
-
$map1 = $this->assertMap($args[0]);
|
6724 |
-
$map2 = $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6725 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6726 |
foreach ($map2[1] as $i2 => $key2) {
|
6727 |
-
$
|
6728 |
|
6729 |
-
|
6730 |
-
|
6731 |
-
|
6732 |
-
continue 2;
|
6733 |
-
}
|
6734 |
}
|
6735 |
|
6736 |
-
$map1[1][] = $
|
6737 |
$map1[2][] = $map2[2][$i2];
|
6738 |
}
|
6739 |
|
@@ -6743,12 +9090,18 @@ class Compiler
|
|
6743 |
protected static $libKeywords = ['args'];
|
6744 |
protected function libKeywords($args)
|
6745 |
{
|
6746 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
6747 |
|
6748 |
$keys = [];
|
6749 |
$values = [];
|
6750 |
|
6751 |
-
foreach ($
|
6752 |
$keys[] = [Type::T_KEYWORD, $name];
|
6753 |
$values[] = $arg;
|
6754 |
}
|
@@ -6763,14 +9116,25 @@ class Compiler
|
|
6763 |
$this->coerceList($list, ' ');
|
6764 |
|
6765 |
if (! empty($list['enclosing']) && $list['enclosing'] === 'bracket') {
|
6766 |
-
return true;
|
6767 |
}
|
6768 |
|
6769 |
-
return false;
|
6770 |
}
|
6771 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6772 |
protected function listSeparatorForJoin($list1, $sep)
|
6773 |
{
|
|
|
|
|
6774 |
if (! isset($sep)) {
|
6775 |
return $list1[1];
|
6776 |
}
|
@@ -6787,14 +9151,40 @@ class Compiler
|
|
6787 |
}
|
6788 |
}
|
6789 |
|
6790 |
-
protected static $libJoin = ['list1', 'list2', 'separator:
|
6791 |
protected function libJoin($args)
|
6792 |
{
|
6793 |
list($list1, $list2, $sep, $bracketed) = $args;
|
6794 |
|
6795 |
$list1 = $this->coerceList($list1, ' ', true);
|
6796 |
$list2 = $this->coerceList($list2, ' ', true);
|
6797 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6798 |
|
6799 |
if ($bracketed === static::$true) {
|
6800 |
$bracketed = true;
|
@@ -6821,11 +9211,7 @@ class Compiler
|
|
6821 |
}
|
6822 |
}
|
6823 |
|
6824 |
-
$res = [Type::T_LIST, $
|
6825 |
-
|
6826 |
-
if (isset($list1['enclosing'])) {
|
6827 |
-
$res['enlcosing'] = $list1['enclosing'];
|
6828 |
-
}
|
6829 |
|
6830 |
if ($bracketed) {
|
6831 |
$res['enclosing'] = 'bracket';
|
@@ -6834,14 +9220,35 @@ class Compiler
|
|
6834 |
return $res;
|
6835 |
}
|
6836 |
|
6837 |
-
protected static $libAppend = ['list', 'val', 'separator:
|
6838 |
protected function libAppend($args)
|
6839 |
{
|
6840 |
list($list1, $value, $sep) = $args;
|
6841 |
|
6842 |
$list1 = $this->coerceList($list1, ' ', true);
|
6843 |
-
|
6844 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6845 |
|
6846 |
if (isset($list1['enclosing'])) {
|
6847 |
$res['enclosing'] = $list1['enclosing'];
|
@@ -6850,30 +9257,39 @@ class Compiler
|
|
6850 |
return $res;
|
6851 |
}
|
6852 |
|
|
|
6853 |
protected function libZip($args)
|
6854 |
{
|
6855 |
-
|
6856 |
-
|
|
|
6857 |
}
|
6858 |
|
6859 |
$lists = [];
|
6860 |
-
$firstList = array_shift($
|
6861 |
|
6862 |
-
|
6863 |
-
|
|
|
|
|
6864 |
|
6865 |
-
|
6866 |
-
|
6867 |
-
|
6868 |
-
|
6869 |
-
|
|
|
6870 |
}
|
|
|
|
|
6871 |
}
|
6872 |
|
6873 |
-
$
|
|
|
|
|
6874 |
}
|
6875 |
|
6876 |
-
return
|
6877 |
}
|
6878 |
|
6879 |
protected static $libTypeOf = ['value'];
|
@@ -6881,6 +9297,16 @@ class Compiler
|
|
6881 |
{
|
6882 |
$value = $args[0];
|
6883 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6884 |
switch ($value[0]) {
|
6885 |
case Type::T_KEYWORD:
|
6886 |
if ($value === static::$true || $value === static::$false) {
|
@@ -6895,8 +9321,11 @@ class Compiler
|
|
6895 |
case Type::T_FUNCTION:
|
6896 |
return 'string';
|
6897 |
|
|
|
|
|
|
|
6898 |
case Type::T_LIST:
|
6899 |
-
if (isset($value[3]) && $value[3]) {
|
6900 |
return 'arglist';
|
6901 |
}
|
6902 |
|
@@ -6909,68 +9338,77 @@ class Compiler
|
|
6909 |
protected static $libUnit = ['number'];
|
6910 |
protected function libUnit($args)
|
6911 |
{
|
6912 |
-
$num = $args[0];
|
6913 |
-
|
6914 |
-
if ($num[0] === Type::T_NUMBER) {
|
6915 |
-
return [Type::T_STRING, '"', [$num->unitStr()]];
|
6916 |
-
}
|
6917 |
|
6918 |
-
return '';
|
6919 |
}
|
6920 |
|
6921 |
protected static $libUnitless = ['number'];
|
6922 |
protected function libUnitless($args)
|
6923 |
{
|
6924 |
-
$value = $args[0];
|
6925 |
|
6926 |
-
return $
|
6927 |
}
|
6928 |
|
6929 |
-
protected static $libComparable = [
|
|
|
|
|
|
|
6930 |
protected function libComparable($args)
|
6931 |
{
|
6932 |
list($number1, $number2) = $args;
|
6933 |
|
6934 |
-
if (
|
6935 |
-
!
|
|
|
6936 |
) {
|
6937 |
-
$this->
|
6938 |
-
|
6939 |
-
return null;
|
6940 |
}
|
6941 |
|
6942 |
-
|
6943 |
-
$number2 = $number2->normalize();
|
6944 |
-
|
6945 |
-
return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless();
|
6946 |
}
|
6947 |
|
6948 |
protected static $libStrIndex = ['string', 'substring'];
|
6949 |
protected function libStrIndex($args)
|
6950 |
{
|
6951 |
-
$string = $this->
|
6952 |
$stringContent = $this->compileStringContent($string);
|
6953 |
|
6954 |
-
$substring = $this->
|
6955 |
$substringContent = $this->compileStringContent($substring);
|
6956 |
|
6957 |
-
|
|
|
|
|
|
|
|
|
6958 |
|
6959 |
-
return $result === false ? static::$null : new
|
6960 |
}
|
6961 |
|
6962 |
protected static $libStrInsert = ['string', 'insert', 'index'];
|
6963 |
protected function libStrInsert($args)
|
6964 |
{
|
6965 |
-
$string = $this->
|
6966 |
$stringContent = $this->compileStringContent($string);
|
6967 |
|
6968 |
-
$insert = $this->
|
6969 |
$insertContent = $this->compileStringContent($insert);
|
6970 |
|
6971 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
6972 |
|
6973 |
-
$string[2] = [
|
|
|
|
|
|
|
|
|
6974 |
|
6975 |
return $string;
|
6976 |
}
|
@@ -6978,34 +9416,46 @@ class Compiler
|
|
6978 |
protected static $libStrLength = ['string'];
|
6979 |
protected function libStrLength($args)
|
6980 |
{
|
6981 |
-
$string = $this->
|
6982 |
$stringContent = $this->compileStringContent($string);
|
6983 |
|
6984 |
-
return new
|
6985 |
}
|
6986 |
|
6987 |
protected static $libStrSlice = ['string', 'start-at', 'end-at:-1'];
|
6988 |
protected function libStrSlice($args)
|
6989 |
{
|
6990 |
-
|
6991 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6992 |
}
|
6993 |
|
6994 |
-
$
|
6995 |
-
|
|
|
6996 |
|
6997 |
-
$
|
|
|
|
|
|
|
|
|
6998 |
|
6999 |
-
if ($
|
7000 |
-
$
|
7001 |
}
|
7002 |
|
7003 |
-
$
|
7004 |
-
$length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start : $end);
|
7005 |
|
7006 |
-
$string[2] = $length
|
7007 |
-
? [substr($stringContent, $start, $length)]
|
7008 |
-
: [substr($stringContent, $start)];
|
7009 |
|
7010 |
return $string;
|
7011 |
}
|
@@ -7013,10 +9463,10 @@ class Compiler
|
|
7013 |
protected static $libToLowerCase = ['string'];
|
7014 |
protected function libToLowerCase($args)
|
7015 |
{
|
7016 |
-
$string = $this->
|
7017 |
$stringContent = $this->compileStringContent($string);
|
7018 |
|
7019 |
-
$string[2] = [
|
7020 |
|
7021 |
return $string;
|
7022 |
}
|
@@ -7024,18 +9474,45 @@ class Compiler
|
|
7024 |
protected static $libToUpperCase = ['string'];
|
7025 |
protected function libToUpperCase($args)
|
7026 |
{
|
7027 |
-
$string = $this->
|
7028 |
$stringContent = $this->compileStringContent($string);
|
7029 |
|
7030 |
-
$string[2] = [
|
7031 |
|
7032 |
return $string;
|
7033 |
}
|
7034 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7035 |
protected static $libFeatureExists = ['feature'];
|
7036 |
protected function libFeatureExists($args)
|
7037 |
{
|
7038 |
-
$string = $this->
|
7039 |
$name = $this->compileStringContent($string);
|
7040 |
|
7041 |
return $this->toBool(
|
@@ -7046,18 +9523,18 @@ class Compiler
|
|
7046 |
protected static $libFunctionExists = ['name'];
|
7047 |
protected function libFunctionExists($args)
|
7048 |
{
|
7049 |
-
$string = $this->
|
7050 |
$name = $this->compileStringContent($string);
|
7051 |
|
7052 |
// user defined functions
|
7053 |
if ($this->has(static::$namespaces['function'] . $name)) {
|
7054 |
-
return true;
|
7055 |
}
|
7056 |
|
7057 |
$name = $this->normalizeName($name);
|
7058 |
|
7059 |
if (isset($this->userFunctions[$name])) {
|
7060 |
-
return true;
|
7061 |
}
|
7062 |
|
7063 |
// built-in functions
|
@@ -7069,30 +9546,31 @@ class Compiler
|
|
7069 |
protected static $libGlobalVariableExists = ['name'];
|
7070 |
protected function libGlobalVariableExists($args)
|
7071 |
{
|
7072 |
-
$string = $this->
|
7073 |
$name = $this->compileStringContent($string);
|
7074 |
|
7075 |
-
return $this->has($name, $this->rootEnv);
|
7076 |
}
|
7077 |
|
7078 |
protected static $libMixinExists = ['name'];
|
7079 |
protected function libMixinExists($args)
|
7080 |
{
|
7081 |
-
$string = $this->
|
7082 |
$name = $this->compileStringContent($string);
|
7083 |
|
7084 |
-
return $this->has(static::$namespaces['mixin'] . $name);
|
7085 |
}
|
7086 |
|
7087 |
protected static $libVariableExists = ['name'];
|
7088 |
protected function libVariableExists($args)
|
7089 |
{
|
7090 |
-
$string = $this->
|
7091 |
$name = $this->compileStringContent($string);
|
7092 |
|
7093 |
-
return $this->has($name);
|
7094 |
}
|
7095 |
|
|
|
7096 |
/**
|
7097 |
* Workaround IE7's content counter bug.
|
7098 |
*
|
@@ -7102,41 +9580,37 @@ class Compiler
|
|
7102 |
*/
|
7103 |
protected function libCounter($args)
|
7104 |
{
|
7105 |
-
$list = array_map([$this, 'compileValue'], $args);
|
7106 |
|
7107 |
return [Type::T_STRING, '', ['counter(' . implode(',', $list) . ')']];
|
7108 |
}
|
7109 |
|
7110 |
-
protected static $libRandom = ['limit:
|
7111 |
protected function libRandom($args)
|
7112 |
{
|
7113 |
-
if (isset($args[0])) {
|
7114 |
-
$n = $this->
|
7115 |
|
7116 |
if ($n < 1) {
|
7117 |
-
|
7118 |
-
|
7119 |
-
return null;
|
7120 |
-
}
|
7121 |
-
|
7122 |
-
if ($n - \intval($n) > 0) {
|
7123 |
-
$this->throwError("Expected \$limit to be an integer but got $n for `random`");
|
7124 |
-
|
7125 |
-
return null;
|
7126 |
}
|
7127 |
|
7128 |
-
return new
|
7129 |
}
|
7130 |
|
7131 |
-
|
|
|
7132 |
}
|
7133 |
|
|
|
7134 |
protected function libUniqueId()
|
7135 |
{
|
7136 |
static $id;
|
7137 |
|
7138 |
if (! isset($id)) {
|
7139 |
-
$id =
|
|
|
|
|
7140 |
}
|
7141 |
|
7142 |
$id += mt_rand(0, 10) + 1;
|
@@ -7144,6 +9618,12 @@ class Compiler
|
|
7144 |
return [Type::T_STRING, '', ['u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
|
7145 |
}
|
7146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
7147 |
protected function inspectFormatValue($value, $force_enclosing_display = false)
|
7148 |
{
|
7149 |
if ($value === static::$null) {
|
@@ -7152,6 +9632,10 @@ class Compiler
|
|
7152 |
|
7153 |
$stringValue = [$value];
|
7154 |
|
|
|
|
|
|
|
|
|
7155 |
if ($value[0] === Type::T_LIST) {
|
7156 |
if (end($value[2]) === static::$null) {
|
7157 |
array_pop($value[2]);
|
@@ -7159,13 +9643,16 @@ class Compiler
|
|
7159 |
$force_enclosing_display = true;
|
7160 |
}
|
7161 |
|
7162 |
-
if (
|
|
|
7163 |
($force_enclosing_display ||
|
7164 |
($value['enclosing'] === 'bracket') ||
|
7165 |
! \count($value[2]))
|
7166 |
) {
|
7167 |
-
$value['enclosing'] = 'forced_'
|
7168 |
$force_enclosing_display = true;
|
|
|
|
|
7169 |
}
|
7170 |
|
7171 |
foreach ($value[2] as $k => $listelement) {
|
@@ -7189,11 +9676,13 @@ class Compiler
|
|
7189 |
/**
|
7190 |
* Preprocess selector args
|
7191 |
*
|
7192 |
-
* @param array
|
|
|
|
|
7193 |
*
|
7194 |
-
* @return array
|
7195 |
*/
|
7196 |
-
protected function getSelectorArg($arg)
|
7197 |
{
|
7198 |
static $parser = null;
|
7199 |
|
@@ -7201,19 +9690,59 @@ class Compiler
|
|
7201 |
$parser = $this->parserFactory(__METHOD__);
|
7202 |
}
|
7203 |
|
7204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7205 |
$arg = $this->compileValue($arg);
|
7206 |
|
7207 |
$parsedSelector = [];
|
7208 |
|
7209 |
-
if ($parser->parseSelector($arg, $parsedSelector)) {
|
7210 |
$selector = $this->evalSelectors($parsedSelector);
|
7211 |
$gluedSelector = $this->glueFunctionSelectors($selector);
|
7212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7213 |
return $gluedSelector;
|
7214 |
}
|
7215 |
|
7216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7217 |
}
|
7218 |
|
7219 |
/**
|
@@ -7221,11 +9750,11 @@ class Compiler
|
|
7221 |
*
|
7222 |
* @param array $selectors
|
7223 |
*
|
7224 |
-
* @return
|
7225 |
*/
|
7226 |
protected function formatOutputSelector($selectors)
|
7227 |
{
|
7228 |
-
$selectors = $this->
|
7229 |
|
7230 |
return $selectors;
|
7231 |
}
|
@@ -7235,10 +9764,10 @@ class Compiler
|
|
7235 |
{
|
7236 |
list($super, $sub) = $args;
|
7237 |
|
7238 |
-
$super = $this->getSelectorArg($super);
|
7239 |
-
$sub = $this->getSelectorArg($sub);
|
7240 |
|
7241 |
-
return $this->isSuperSelector($super, $sub);
|
7242 |
}
|
7243 |
|
7244 |
/**
|
@@ -7247,17 +9776,35 @@ class Compiler
|
|
7247 |
* @param array $super
|
7248 |
* @param array $sub
|
7249 |
*
|
7250 |
-
* @return
|
7251 |
*/
|
7252 |
protected function isSuperSelector($super, $sub)
|
7253 |
{
|
7254 |
// one and only one selector for each arg
|
7255 |
-
if (! $super
|
7256 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7257 |
}
|
7258 |
|
7259 |
-
if (
|
7260 |
-
$
|
|
|
|
|
|
|
|
|
|
|
7261 |
}
|
7262 |
|
7263 |
$super = reset($super);
|
@@ -7310,7 +9857,7 @@ class Compiler
|
|
7310 |
* @param array $superParts
|
7311 |
* @param array $subParts
|
7312 |
*
|
7313 |
-
* @return
|
7314 |
*/
|
7315 |
protected function isSuperPart($superParts, $subParts)
|
7316 |
{
|
@@ -7339,10 +9886,13 @@ class Compiler
|
|
7339 |
$args = $args[2];
|
7340 |
|
7341 |
if (\count($args) < 1) {
|
7342 |
-
$this->
|
7343 |
}
|
7344 |
|
7345 |
-
$selectors =
|
|
|
|
|
|
|
7346 |
|
7347 |
return $this->formatOutputSelector($this->selectorAppend($selectors));
|
7348 |
}
|
@@ -7361,34 +9911,31 @@ class Compiler
|
|
7361 |
$lastSelectors = array_pop($selectors);
|
7362 |
|
7363 |
if (! $lastSelectors) {
|
7364 |
-
$this->
|
7365 |
}
|
7366 |
|
7367 |
while (\count($selectors)) {
|
7368 |
$previousSelectors = array_pop($selectors);
|
7369 |
|
7370 |
if (! $previousSelectors) {
|
7371 |
-
$this->
|
7372 |
}
|
7373 |
|
7374 |
// do the trick, happening $lastSelector to $previousSelector
|
7375 |
$appended = [];
|
7376 |
|
7377 |
-
foreach ($
|
7378 |
-
$
|
7379 |
-
|
7380 |
-
|
7381 |
-
|
7382 |
-
|
7383 |
-
|
7384 |
-
$previous[$i][$j][] = $lastSelectorPart;
|
7385 |
}
|
7386 |
}
|
7387 |
}
|
7388 |
-
}
|
7389 |
|
7390 |
-
|
7391 |
-
$appended[] = $ps;
|
7392 |
}
|
7393 |
}
|
7394 |
|
@@ -7398,17 +9945,20 @@ class Compiler
|
|
7398 |
return $lastSelectors;
|
7399 |
}
|
7400 |
|
7401 |
-
protected static $libSelectorExtend = [
|
|
|
|
|
|
|
7402 |
protected function libSelectorExtend($args)
|
7403 |
{
|
7404 |
list($selectors, $extendee, $extender) = $args;
|
7405 |
|
7406 |
-
$selectors = $this->getSelectorArg($selectors);
|
7407 |
-
$extendee = $this->getSelectorArg($extendee);
|
7408 |
-
$extender = $this->getSelectorArg($extender);
|
7409 |
|
7410 |
if (! $selectors || ! $extendee || ! $extender) {
|
7411 |
-
$this->
|
7412 |
}
|
7413 |
|
7414 |
$extended = $this->extendOrReplaceSelectors($selectors, $extendee, $extender);
|
@@ -7416,17 +9966,20 @@ class Compiler
|
|
7416 |
return $this->formatOutputSelector($extended);
|
7417 |
}
|
7418 |
|
7419 |
-
protected static $libSelectorReplace = [
|
|
|
|
|
|
|
7420 |
protected function libSelectorReplace($args)
|
7421 |
{
|
7422 |
list($selectors, $original, $replacement) = $args;
|
7423 |
|
7424 |
-
$selectors = $this->getSelectorArg($selectors);
|
7425 |
-
$original = $this->getSelectorArg($original);
|
7426 |
-
$replacement = $this->getSelectorArg($replacement);
|
7427 |
|
7428 |
if (! $selectors || ! $original || ! $replacement) {
|
7429 |
-
$this->
|
7430 |
}
|
7431 |
|
7432 |
$replaced = $this->extendOrReplaceSelectors($selectors, $original, $replacement, true);
|
@@ -7438,10 +9991,10 @@ class Compiler
|
|
7438 |
* Extend/replace in selectors
|
7439 |
* used by selector-extend and selector-replace that use the same logic
|
7440 |
*
|
7441 |
-
* @param array
|
7442 |
-
* @param array
|
7443 |
-
* @param array
|
7444 |
-
* @param
|
7445 |
*
|
7446 |
* @return array
|
7447 |
*/
|
@@ -7454,6 +10007,10 @@ class Compiler
|
|
7454 |
$this->extendsMap = [];
|
7455 |
|
7456 |
foreach ($extendee as $es) {
|
|
|
|
|
|
|
|
|
7457 |
// only use the first one
|
7458 |
$this->pushExtends(reset($es), $extender, null);
|
7459 |
}
|
@@ -7470,7 +10027,7 @@ class Compiler
|
|
7470 |
$this->matchExtends($selector, $extended);
|
7471 |
|
7472 |
// if didnt match, keep the original selector if we are in a replace operation
|
7473 |
-
if ($replace
|
7474 |
$extended[] = $selector;
|
7475 |
}
|
7476 |
}
|
@@ -7489,10 +10046,16 @@ class Compiler
|
|
7489 |
$args = $args[2];
|
7490 |
|
7491 |
if (\count($args) < 1) {
|
7492 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
7493 |
}
|
7494 |
|
7495 |
-
$selectorsMap
|
|
|
7496 |
$envs = [];
|
7497 |
|
7498 |
foreach ($selectorsMap as $selectors) {
|
@@ -7509,11 +10072,14 @@ class Compiler
|
|
7509 |
return $this->formatOutputSelector($outputSelectors);
|
7510 |
}
|
7511 |
|
7512 |
-
protected static $libSelectorParse = [
|
|
|
|
|
|
|
7513 |
protected function libSelectorParse($args)
|
7514 |
{
|
7515 |
$selectors = reset($args);
|
7516 |
-
$selectors = $this->getSelectorArg($selectors);
|
7517 |
|
7518 |
return $this->formatOutputSelector($selectors);
|
7519 |
}
|
@@ -7523,11 +10089,11 @@ class Compiler
|
|
7523 |
{
|
7524 |
list($selectors1, $selectors2) = $args;
|
7525 |
|
7526 |
-
$selectors1 = $this->getSelectorArg($selectors1);
|
7527 |
-
$selectors2 = $this->getSelectorArg($selectors2);
|
7528 |
|
7529 |
if (! $selectors1 || ! $selectors2) {
|
7530 |
-
$this->
|
7531 |
}
|
7532 |
|
7533 |
// only consider the first compound of each
|
@@ -7547,7 +10113,7 @@ class Compiler
|
|
7547 |
* @param array $compound1
|
7548 |
* @param array $compound2
|
7549 |
*
|
7550 |
-
* @return array
|
7551 |
*/
|
7552 |
protected function unifyCompoundSelectors($compound1, $compound2)
|
7553 |
{
|
@@ -7663,7 +10229,7 @@ class Compiler
|
|
7663 |
* @param array $part
|
7664 |
* @param array $compound
|
7665 |
*
|
7666 |
-
* @return array|
|
7667 |
*/
|
7668 |
protected function matchPartInCompound($part, $compound)
|
7669 |
{
|
@@ -7758,7 +10324,7 @@ class Compiler
|
|
7758 |
* @param string $tag1
|
7759 |
* @param string $tag2
|
7760 |
*
|
7761 |
-
* @return array|
|
7762 |
*/
|
7763 |
protected function checkCompatibleTags($tag1, $tag2)
|
7764 |
{
|
@@ -7781,9 +10347,9 @@ class Compiler
|
|
7781 |
/**
|
7782 |
* Find the html tag name in a selector parts list
|
7783 |
*
|
7784 |
-
* @param
|
7785 |
*
|
7786 |
-
* @return
|
7787 |
*/
|
7788 |
protected function findTagName($parts)
|
7789 |
{
|
@@ -7800,7 +10366,7 @@ class Compiler
|
|
7800 |
protected function libSimpleSelectors($args)
|
7801 |
{
|
7802 |
$selector = reset($args);
|
7803 |
-
$selector = $this->getSelectorArg($selector);
|
7804 |
|
7805 |
// remove selectors list layer, keeping the first one
|
7806 |
$selector = reset($selector);
|
@@ -7820,7 +10386,11 @@ class Compiler
|
|
7820 |
protected static $libScssphpGlob = ['pattern'];
|
7821 |
protected function libScssphpGlob($args)
|
7822 |
{
|
7823 |
-
|
|
|
|
|
|
|
|
|
7824 |
$pattern = $this->compileStringContent($string);
|
7825 |
$matches = glob($pattern);
|
7826 |
$listParts = [];
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Base\Range;
|
16 |
+
use ScssPhp\ScssPhp\Block\AtRootBlock;
|
17 |
+
use ScssPhp\ScssPhp\Block\CallableBlock;
|
18 |
+
use ScssPhp\ScssPhp\Block\DirectiveBlock;
|
19 |
+
use ScssPhp\ScssPhp\Block\EachBlock;
|
20 |
+
use ScssPhp\ScssPhp\Block\ElseBlock;
|
21 |
+
use ScssPhp\ScssPhp\Block\ElseifBlock;
|
22 |
+
use ScssPhp\ScssPhp\Block\ForBlock;
|
23 |
+
use ScssPhp\ScssPhp\Block\IfBlock;
|
24 |
+
use ScssPhp\ScssPhp\Block\MediaBlock;
|
25 |
+
use ScssPhp\ScssPhp\Block\NestedPropertyBlock;
|
26 |
+
use ScssPhp\ScssPhp\Block\WhileBlock;
|
27 |
+
use ScssPhp\ScssPhp\Compiler\CachedResult;
|
28 |
use ScssPhp\ScssPhp\Compiler\Environment;
|
29 |
use ScssPhp\ScssPhp\Exception\CompilerException;
|
30 |
+
use ScssPhp\ScssPhp\Exception\ParserException;
|
31 |
+
use ScssPhp\ScssPhp\Exception\SassException;
|
32 |
+
use ScssPhp\ScssPhp\Exception\SassScriptException;
|
33 |
+
use ScssPhp\ScssPhp\Formatter\Compressed;
|
34 |
+
use ScssPhp\ScssPhp\Formatter\Expanded;
|
35 |
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
36 |
+
use ScssPhp\ScssPhp\Logger\LoggerInterface;
|
37 |
+
use ScssPhp\ScssPhp\Logger\StreamLogger;
|
38 |
+
use ScssPhp\ScssPhp\Node\Number;
|
39 |
use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
|
40 |
+
use ScssPhp\ScssPhp\Util\Path;
|
|
|
|
|
41 |
|
42 |
/**
|
43 |
* The scss compiler and parser.
|
70 |
* SCSS compiler
|
71 |
*
|
72 |
* @author Leaf Corcoran <leafot@gmail.com>
|
73 |
+
*
|
74 |
+
* @final Extending the Compiler is deprecated
|
75 |
*/
|
76 |
class Compiler
|
77 |
{
|
78 |
+
/**
|
79 |
+
* @deprecated
|
80 |
+
*/
|
81 |
const LINE_COMMENTS = 1;
|
82 |
+
/**
|
83 |
+
* @deprecated
|
84 |
+
*/
|
85 |
const DEBUG_INFO = 2;
|
86 |
|
87 |
+
/**
|
88 |
+
* @deprecated
|
89 |
+
*/
|
90 |
const WITH_RULE = 1;
|
91 |
+
/**
|
92 |
+
* @deprecated
|
93 |
+
*/
|
94 |
const WITH_MEDIA = 2;
|
95 |
+
/**
|
96 |
+
* @deprecated
|
97 |
+
*/
|
98 |
const WITH_SUPPORTS = 4;
|
99 |
+
/**
|
100 |
+
* @deprecated
|
101 |
+
*/
|
102 |
const WITH_ALL = 7;
|
103 |
|
104 |
const SOURCE_MAP_NONE = 0;
|
106 |
const SOURCE_MAP_FILE = 2;
|
107 |
|
108 |
/**
|
109 |
+
* @var array<string, string>
|
110 |
*/
|
111 |
protected static $operatorNames = [
|
112 |
'+' => 'add',
|
122 |
|
123 |
'<=' => 'lte',
|
124 |
'>=' => 'gte',
|
|
|
125 |
];
|
126 |
|
127 |
/**
|
128 |
+
* @var array<string, string>
|
129 |
*/
|
130 |
protected static $namespaces = [
|
131 |
'special' => '%',
|
135 |
|
136 |
public static $true = [Type::T_KEYWORD, 'true'];
|
137 |
public static $false = [Type::T_KEYWORD, 'false'];
|
138 |
+
/** @deprecated */
|
139 |
public static $NaN = [Type::T_KEYWORD, 'NaN'];
|
140 |
+
/** @deprecated */
|
141 |
public static $Infinity = [Type::T_KEYWORD, 'Infinity'];
|
142 |
public static $null = [Type::T_NULL];
|
143 |
public static $nullString = [Type::T_STRING, '', []];
|
148 |
public static $emptyString = [Type::T_STRING, '"', []];
|
149 |
public static $with = [Type::T_KEYWORD, 'with'];
|
150 |
public static $without = [Type::T_KEYWORD, 'without'];
|
151 |
+
private static $emptyArgumentList = [Type::T_LIST, '', [], []];
|
152 |
|
153 |
+
/**
|
154 |
+
* @var array<int, string|callable>
|
155 |
+
*/
|
156 |
+
protected $importPaths = [];
|
157 |
+
/**
|
158 |
+
* @var array<string, Block>
|
159 |
+
*/
|
160 |
protected $importCache = [];
|
161 |
+
|
162 |
+
/**
|
163 |
+
* @var string[]
|
164 |
+
*/
|
165 |
protected $importedFiles = [];
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @var array
|
169 |
+
* @phpstan-var array<string, array{0: callable, 1: string[]|null}>
|
170 |
+
*/
|
171 |
protected $userFunctions = [];
|
172 |
+
/**
|
173 |
+
* @var array<string, mixed>
|
174 |
+
*/
|
175 |
protected $registeredVars = [];
|
176 |
+
/**
|
177 |
+
* @var array<string, bool>
|
178 |
+
*/
|
179 |
protected $registeredFeatures = [
|
180 |
'extend-selector-pseudoclass' => false,
|
181 |
'at-error' => true,
|
182 |
+
'units-level-3' => true,
|
183 |
'global-variable-shadowing' => false,
|
184 |
];
|
185 |
|
186 |
+
/**
|
187 |
+
* @var string|null
|
188 |
+
*/
|
189 |
protected $encoding = null;
|
190 |
+
/**
|
191 |
+
* @var null
|
192 |
+
* @deprecated
|
193 |
+
*/
|
194 |
protected $lineNumberStyle = null;
|
195 |
|
196 |
+
/**
|
197 |
+
* @var int|SourceMapGenerator
|
198 |
+
* @phpstan-var self::SOURCE_MAP_*|SourceMapGenerator
|
199 |
+
*/
|
200 |
protected $sourceMap = self::SOURCE_MAP_NONE;
|
201 |
+
|
202 |
+
/**
|
203 |
+
* @var array
|
204 |
+
* @phpstan-var array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string}
|
205 |
+
*/
|
206 |
protected $sourceMapOptions = [];
|
207 |
|
208 |
/**
|
209 |
+
* @var bool
|
210 |
+
*/
|
211 |
+
private $charset = true;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @var Formatter
|
215 |
*/
|
216 |
+
protected $formatter;
|
217 |
|
218 |
+
/**
|
219 |
+
* @var string
|
220 |
+
* @phpstan-var class-string<Formatter>
|
221 |
+
*/
|
222 |
+
private $configuredFormatter = Expanded::class;
|
223 |
+
|
224 |
+
/**
|
225 |
+
* @var Environment
|
226 |
+
*/
|
227 |
protected $rootEnv;
|
228 |
+
/**
|
229 |
+
* @var OutputBlock|null
|
230 |
+
*/
|
231 |
protected $rootBlock;
|
232 |
|
233 |
/**
|
234 |
* @var \ScssPhp\ScssPhp\Compiler\Environment
|
235 |
*/
|
236 |
protected $env;
|
237 |
+
/**
|
238 |
+
* @var OutputBlock|null
|
239 |
+
*/
|
240 |
protected $scope;
|
241 |
+
/**
|
242 |
+
* @var Environment|null
|
243 |
+
*/
|
244 |
protected $storeEnv;
|
245 |
+
/**
|
246 |
+
* @var bool|null
|
247 |
+
*
|
248 |
+
* @deprecated
|
249 |
+
*/
|
250 |
protected $charsetSeen;
|
251 |
+
/**
|
252 |
+
* @var array<int, string|null>
|
253 |
+
*/
|
254 |
protected $sourceNames;
|
255 |
|
256 |
+
/**
|
257 |
+
* @var Cache|null
|
258 |
+
*/
|
259 |
protected $cache;
|
260 |
|
261 |
+
/**
|
262 |
+
* @var bool
|
263 |
+
*/
|
264 |
+
protected $cacheCheckImportResolutions = false;
|
265 |
+
|
266 |
+
/**
|
267 |
+
* @var int
|
268 |
+
*/
|
269 |
protected $indentLevel;
|
270 |
+
/**
|
271 |
+
* @var array[]
|
272 |
+
*/
|
273 |
protected $extends;
|
274 |
+
/**
|
275 |
+
* @var array<string, int[]>
|
276 |
+
*/
|
277 |
protected $extendsMap;
|
278 |
+
|
279 |
+
/**
|
280 |
+
* @var array<string, int>
|
281 |
+
*/
|
282 |
+
protected $parsedFiles = [];
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @var Parser|null
|
286 |
+
*/
|
287 |
protected $parser;
|
288 |
+
/**
|
289 |
+
* @var int|null
|
290 |
+
*/
|
291 |
protected $sourceIndex;
|
292 |
+
/**
|
293 |
+
* @var int|null
|
294 |
+
*/
|
295 |
protected $sourceLine;
|
296 |
+
/**
|
297 |
+
* @var int|null
|
298 |
+
*/
|
299 |
protected $sourceColumn;
|
300 |
+
/**
|
301 |
+
* @var bool|null
|
302 |
+
*/
|
303 |
protected $shouldEvaluate;
|
304 |
+
/**
|
305 |
+
* @var null
|
306 |
+
* @deprecated
|
307 |
+
*/
|
308 |
protected $ignoreErrors;
|
309 |
+
/**
|
310 |
+
* @var bool
|
311 |
+
*/
|
312 |
protected $ignoreCallStackMessage = false;
|
313 |
|
314 |
+
/**
|
315 |
+
* @var array[]
|
316 |
+
*/
|
317 |
protected $callStack = [];
|
318 |
|
319 |
+
/**
|
320 |
+
* @var array
|
321 |
+
* @phpstan-var list<array{currentDir: string|null, path: string, filePath: string}>
|
322 |
+
*/
|
323 |
+
private $resolvedImports = [];
|
324 |
+
|
325 |
+
/**
|
326 |
+
* The directory of the currently processed file
|
327 |
+
*
|
328 |
+
* @var string|null
|
329 |
+
*/
|
330 |
+
private $currentDirectory;
|
331 |
+
|
332 |
+
/**
|
333 |
+
* The directory of the input file
|
334 |
+
*
|
335 |
+
* @var string
|
336 |
+
*/
|
337 |
+
private $rootDirectory;
|
338 |
+
|
339 |
+
/**
|
340 |
+
* @var bool
|
341 |
+
*/
|
342 |
+
private $legacyCwdImportPath = true;
|
343 |
+
|
344 |
+
/**
|
345 |
+
* @var LoggerInterface
|
346 |
+
*/
|
347 |
+
private $logger;
|
348 |
+
|
349 |
+
/**
|
350 |
+
* @var array<string, bool>
|
351 |
+
*/
|
352 |
+
private $warnedChildFunctions = [];
|
353 |
+
|
354 |
/**
|
355 |
* Constructor
|
356 |
*
|
357 |
* @param array|null $cacheOptions
|
358 |
+
* @phpstan-param array{cacheDir?: string, prefix?: string, forceRefresh?: string, checkImportResolutions?: bool}|null $cacheOptions
|
359 |
*/
|
360 |
public function __construct($cacheOptions = null)
|
361 |
{
|
|
|
362 |
$this->sourceNames = [];
|
363 |
|
364 |
if ($cacheOptions) {
|
365 |
$this->cache = new Cache($cacheOptions);
|
366 |
+
if (!empty($cacheOptions['checkImportResolutions'])) {
|
367 |
+
$this->cacheCheckImportResolutions = true;
|
368 |
+
}
|
369 |
}
|
370 |
|
371 |
+
$this->logger = new StreamLogger(fopen('php://stderr', 'w'), true);
|
372 |
}
|
373 |
|
374 |
/**
|
375 |
* Get compiler options
|
376 |
*
|
377 |
+
* @return array<string, mixed>
|
378 |
+
*
|
379 |
+
* @internal
|
380 |
*/
|
381 |
public function getCompileOptions()
|
382 |
{
|
387 |
'encoding' => $this->encoding,
|
388 |
'sourceMap' => serialize($this->sourceMap),
|
389 |
'sourceMapOptions' => $this->sourceMapOptions,
|
390 |
+
'formatter' => $this->configuredFormatter,
|
391 |
+
'legacyImportPath' => $this->legacyCwdImportPath,
|
392 |
];
|
393 |
|
394 |
return $options;
|
395 |
}
|
396 |
|
397 |
+
/**
|
398 |
+
* Sets an alternative logger.
|
399 |
+
*
|
400 |
+
* Changing the logger in the middle of the compilation is not
|
401 |
+
* supported and will result in an undefined behavior.
|
402 |
+
*
|
403 |
+
* @param LoggerInterface $logger
|
404 |
+
*
|
405 |
+
* @return void
|
406 |
+
*/
|
407 |
+
public function setLogger(LoggerInterface $logger)
|
408 |
+
{
|
409 |
+
$this->logger = $logger;
|
410 |
+
}
|
411 |
+
|
412 |
/**
|
413 |
* Set an alternative error output stream, for testing purpose only
|
414 |
*
|
415 |
* @param resource $handle
|
416 |
+
*
|
417 |
+
* @return void
|
418 |
+
*
|
419 |
+
* @deprecated Use {@see setLogger} instead
|
420 |
*/
|
421 |
public function setErrorOuput($handle)
|
422 |
{
|
423 |
+
@trigger_error('The method "setErrorOuput" is deprecated. Use "setLogger" instead.', E_USER_DEPRECATED);
|
424 |
+
|
425 |
+
$this->logger = new StreamLogger($handle);
|
426 |
}
|
427 |
|
428 |
/**
|
429 |
* Compile scss
|
430 |
*
|
431 |
+
* @param string $code
|
432 |
+
* @param string|null $path
|
|
|
|
|
433 |
*
|
434 |
* @return string
|
435 |
+
*
|
436 |
+
* @throws SassException when the source fails to compile
|
437 |
+
*
|
438 |
+
* @deprecated Use {@see compileString} instead.
|
439 |
*/
|
440 |
public function compile($code, $path = null)
|
441 |
{
|
442 |
+
@trigger_error(sprintf('The "%s" method is deprecated. Use "compileString" instead.', __METHOD__), E_USER_DEPRECATED);
|
|
|
|
|
|
|
443 |
|
444 |
+
$result = $this->compileString($code, $path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
445 |
|
446 |
+
$sourceMap = $result->getSourceMap();
|
447 |
+
|
448 |
+
if ($sourceMap !== null) {
|
449 |
+
if ($this->sourceMap instanceof SourceMapGenerator) {
|
450 |
+
$this->sourceMap->saveMap($sourceMap);
|
451 |
+
} elseif ($this->sourceMap === self::SOURCE_MAP_FILE) {
|
452 |
+
$sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions);
|
453 |
+
$sourceMapGenerator->saveMap($sourceMap);
|
454 |
}
|
455 |
}
|
456 |
|
457 |
+
return $result->getCss();
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Compile scss
|
462 |
+
*
|
463 |
+
* @param string $source
|
464 |
+
* @param string|null $path
|
465 |
+
*
|
466 |
+
* @return CompilationResult
|
467 |
+
*
|
468 |
+
* @throws SassException when the source fails to compile
|
469 |
+
*/
|
470 |
+
public function compileString($source, $path = null)
|
471 |
+
{
|
472 |
+
if ($this->cache) {
|
473 |
+
$cacheKey = ($path ? $path : '(stdin)') . ':' . md5($source);
|
474 |
+
$compileOptions = $this->getCompileOptions();
|
475 |
+
$cachedResult = $this->cache->getCache('compile', $cacheKey, $compileOptions);
|
476 |
+
|
477 |
+
if ($cachedResult instanceof CachedResult && $this->isFreshCachedResult($cachedResult)) {
|
478 |
+
return $cachedResult->getResult();
|
479 |
+
}
|
480 |
+
}
|
481 |
|
482 |
$this->indentLevel = -1;
|
483 |
$this->extends = [];
|
488 |
$this->env = null;
|
489 |
$this->scope = null;
|
490 |
$this->storeEnv = null;
|
|
|
491 |
$this->shouldEvaluate = null;
|
492 |
$this->ignoreCallStackMessage = false;
|
493 |
+
$this->parsedFiles = [];
|
494 |
+
$this->importedFiles = [];
|
495 |
+
$this->resolvedImports = [];
|
496 |
|
497 |
+
if (!\is_null($path) && is_file($path)) {
|
498 |
+
$path = realpath($path) ?: $path;
|
499 |
+
$this->currentDirectory = dirname($path);
|
500 |
+
$this->rootDirectory = $this->currentDirectory;
|
501 |
+
} else {
|
502 |
+
$this->currentDirectory = null;
|
503 |
+
$this->rootDirectory = getcwd();
|
504 |
+
}
|
505 |
|
506 |
+
try {
|
507 |
+
$this->parser = $this->parserFactory($path);
|
508 |
+
$tree = $this->parser->parse($source);
|
509 |
+
$this->parser = null;
|
510 |
|
511 |
+
$this->formatter = new $this->configuredFormatter();
|
512 |
+
$this->rootBlock = null;
|
513 |
+
$this->rootEnv = $this->pushEnv($tree);
|
514 |
|
515 |
+
$warnCallback = function ($message, $deprecation) {
|
516 |
+
$this->logger->warn($message, $deprecation);
|
517 |
+
};
|
518 |
+
$previousWarnCallback = Warn::setCallback($warnCallback);
|
519 |
|
520 |
+
try {
|
521 |
+
$this->injectVariables($this->registeredVars);
|
522 |
+
$this->compileRoot($tree);
|
523 |
+
$this->popEnv();
|
524 |
+
} finally {
|
525 |
+
Warn::setCallback($previousWarnCallback);
|
526 |
}
|
|
|
527 |
|
528 |
+
$sourceMapGenerator = null;
|
529 |
+
|
530 |
+
if ($this->sourceMap) {
|
531 |
+
if (\is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) {
|
532 |
+
$sourceMapGenerator = $this->sourceMap;
|
533 |
+
$this->sourceMap = self::SOURCE_MAP_FILE;
|
534 |
+
} elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
|
535 |
+
$sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions);
|
536 |
+
}
|
537 |
+
}
|
538 |
+
assert($this->scope !== null);
|
539 |
|
540 |
+
$out = $this->formatter->format($this->scope, $sourceMapGenerator);
|
|
|
|
|
541 |
|
542 |
+
$prefix = '';
|
|
|
|
|
|
|
543 |
|
544 |
+
if ($this->charset && strlen($out) !== Util::mbStrlen($out)) {
|
545 |
+
$prefix = '@charset "UTF-8";' . "\n";
|
546 |
+
$out = $prefix . $out;
|
547 |
+
}
|
548 |
+
|
549 |
+
$sourceMap = null;
|
550 |
+
|
551 |
+
if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) {
|
552 |
+
assert($sourceMapGenerator !== null);
|
553 |
+
$sourceMap = $sourceMapGenerator->generateJson($prefix);
|
554 |
+
$sourceMapUrl = null;
|
555 |
+
|
556 |
+
switch ($this->sourceMap) {
|
557 |
+
case self::SOURCE_MAP_INLINE:
|
558 |
+
$sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap));
|
559 |
+
break;
|
560 |
+
|
561 |
+
case self::SOURCE_MAP_FILE:
|
562 |
+
if (isset($this->sourceMapOptions['sourceMapURL'])) {
|
563 |
+
$sourceMapUrl = $this->sourceMapOptions['sourceMapURL'];
|
564 |
+
}
|
565 |
+
break;
|
566 |
+
}
|
567 |
+
|
568 |
+
if ($sourceMapUrl !== null) {
|
569 |
+
$out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl);
|
570 |
+
}
|
571 |
}
|
572 |
+
} catch (SassScriptException $e) {
|
573 |
+
throw new CompilerException($this->addLocationToMessage($e->getMessage()), 0, $e);
|
574 |
+
}
|
575 |
+
|
576 |
+
$includedFiles = [];
|
577 |
|
578 |
+
foreach ($this->resolvedImports as $resolvedImport) {
|
579 |
+
$includedFiles[$resolvedImport['filePath']] = $resolvedImport['filePath'];
|
580 |
}
|
581 |
|
582 |
+
$result = new CompilationResult($out, $sourceMap, array_values($includedFiles));
|
583 |
+
|
584 |
if ($this->cache && isset($cacheKey) && isset($compileOptions)) {
|
585 |
+
$this->cache->setCache('compile', $cacheKey, new CachedResult($result, $this->parsedFiles, $this->resolvedImports), $compileOptions);
|
586 |
+
}
|
587 |
+
|
588 |
+
// Reset state to free memory
|
589 |
+
// TODO in 2.0, reset parsedFiles as well when the getter is removed.
|
590 |
+
$this->resolvedImports = [];
|
591 |
+
$this->importedFiles = [];
|
592 |
+
|
593 |
+
return $result;
|
594 |
+
}
|
595 |
+
|
596 |
+
/**
|
597 |
+
* @param CachedResult $result
|
598 |
+
*
|
599 |
+
* @return bool
|
600 |
+
*/
|
601 |
+
private function isFreshCachedResult(CachedResult $result)
|
602 |
+
{
|
603 |
+
// check if any dependency file changed since the result was compiled
|
604 |
+
foreach ($result->getParsedFiles() as $file => $mtime) {
|
605 |
+
if (! is_file($file) || filemtime($file) !== $mtime) {
|
606 |
+
return false;
|
607 |
+
}
|
608 |
+
}
|
609 |
+
|
610 |
+
if ($this->cacheCheckImportResolutions) {
|
611 |
+
$resolvedImports = [];
|
612 |
+
|
613 |
+
foreach ($result->getResolvedImports() as $import) {
|
614 |
+
$currentDir = $import['currentDir'];
|
615 |
+
$path = $import['path'];
|
616 |
+
// store the check across all the results in memory to avoid multiple findImport() on the same path
|
617 |
+
// with same context.
|
618 |
+
// this is happening in a same hit with multiple compilations (especially with big frameworks)
|
619 |
+
if (empty($resolvedImports[$currentDir][$path])) {
|
620 |
+
$resolvedImports[$currentDir][$path] = $this->findImport($path, $currentDir);
|
621 |
+
}
|
622 |
|
623 |
+
if ($resolvedImports[$currentDir][$path] !== $import['filePath']) {
|
624 |
+
return false;
|
625 |
+
}
|
626 |
+
}
|
627 |
}
|
628 |
|
629 |
+
return true;
|
630 |
}
|
631 |
|
632 |
/**
|
633 |
* Instantiate parser
|
634 |
*
|
635 |
+
* @param string|null $path
|
636 |
*
|
637 |
* @return \ScssPhp\ScssPhp\Parser
|
638 |
*/
|
645 |
// Otherwise, the CSS will be rendered as-is. It can even be extended!
|
646 |
$cssOnly = false;
|
647 |
|
648 |
+
if ($path !== null && substr($path, -4) === '.css') {
|
649 |
$cssOnly = true;
|
650 |
}
|
651 |
|
652 |
+
$parser = new Parser($path, \count($this->sourceNames), $this->encoding, $this->cache, $cssOnly, $this->logger);
|
653 |
|
654 |
$this->sourceNames[] = $path;
|
655 |
$this->addParsedFile($path);
|
663 |
* @param array $target
|
664 |
* @param array $origin
|
665 |
*
|
666 |
+
* @return bool
|
667 |
*/
|
668 |
protected function isSelfExtend($target, $origin)
|
669 |
{
|
679 |
/**
|
680 |
* Push extends
|
681 |
*
|
682 |
+
* @param string[] $target
|
683 |
* @param array $origin
|
684 |
* @param array|null $block
|
685 |
+
*
|
686 |
+
* @return void
|
687 |
*/
|
688 |
protected function pushExtends($target, $origin, $block)
|
689 |
{
|
|
|
|
|
|
|
|
|
690 |
$i = \count($this->extends);
|
691 |
$this->extends[] = [$target, $origin, $block];
|
692 |
|
702 |
/**
|
703 |
* Make output block
|
704 |
*
|
705 |
+
* @param string|null $type
|
706 |
+
* @param string[]|null $selectors
|
707 |
*
|
708 |
* @return \ScssPhp\ScssPhp\Formatter\OutputBlock
|
709 |
*/
|
710 |
protected function makeOutputBlock($type, $selectors = null)
|
711 |
{
|
712 |
+
$out = new OutputBlock();
|
713 |
$out->type = $type;
|
714 |
$out->lines = [];
|
715 |
$out->children = [];
|
722 |
$out->sourceLine = $this->env->block->sourceLine;
|
723 |
$out->sourceColumn = $this->env->block->sourceColumn;
|
724 |
} else {
|
725 |
+
$out->sourceName = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : '(stdin)';
|
726 |
+
$out->sourceLine = $this->sourceLine;
|
727 |
+
$out->sourceColumn = $this->sourceColumn;
|
728 |
}
|
729 |
|
730 |
return $out;
|
734 |
* Compile root
|
735 |
*
|
736 |
* @param \ScssPhp\ScssPhp\Block $rootBlock
|
737 |
+
*
|
738 |
+
* @return void
|
739 |
*/
|
740 |
protected function compileRoot(Block $rootBlock)
|
741 |
{
|
742 |
$this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT);
|
743 |
|
744 |
$this->compileChildrenNoReturn($rootBlock->children, $this->scope);
|
745 |
+
assert($this->scope !== null);
|
746 |
$this->flattenSelectors($this->scope);
|
747 |
$this->missingSelectors();
|
748 |
}
|
749 |
|
750 |
/**
|
751 |
* Report missing selectors
|
752 |
+
*
|
753 |
+
* @return void
|
754 |
*/
|
755 |
protected function missingSelectors()
|
756 |
{
|
770 |
$origin = $this->collapseSelectors($origin);
|
771 |
|
772 |
$this->sourceLine = $block[Parser::SOURCE_LINE];
|
773 |
+
throw $this->error("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found.");
|
774 |
}
|
775 |
}
|
776 |
|
779 |
*
|
780 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
781 |
* @param string $parentKey
|
782 |
+
*
|
783 |
+
* @return void
|
784 |
*/
|
785 |
protected function flattenSelectors(OutputBlock $block, $parentKey = null)
|
786 |
{
|
824 |
}
|
825 |
|
826 |
if ($placeholderSelector && 0 === \count($block->selectors) && null !== $parentKey) {
|
827 |
+
assert($block->parent !== null);
|
828 |
unset($block->parent->children[$parentKey]);
|
829 |
|
830 |
return;
|
837 |
}
|
838 |
|
839 |
/**
|
840 |
+
* Glue parts of :not( or :nth-child( ... that are in general split in selectors parts
|
841 |
*
|
842 |
* @param array $parts
|
843 |
*
|
854 |
} else {
|
855 |
// a selector part finishing with a ) is the last part of a :not( or :nth-child(
|
856 |
// and need to be joined to this
|
857 |
+
if (
|
858 |
+
\count($new) && \is_string($new[\count($new) - 1]) &&
|
859 |
\strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false
|
860 |
) {
|
861 |
+
while (\count($new) > 1 && substr($new[\count($new) - 1], -1) !== '(') {
|
862 |
$part = array_pop($new) . $part;
|
863 |
}
|
864 |
$new[\count($new) - 1] .= $part;
|
874 |
/**
|
875 |
* Match extends
|
876 |
*
|
877 |
+
* @param array $selector
|
878 |
+
* @param array $out
|
879 |
+
* @param int $from
|
880 |
+
* @param bool $initial
|
881 |
+
*
|
882 |
+
* @return void
|
883 |
*/
|
884 |
protected function matchExtends($selector, &$out, $from = 0, $initial = true)
|
885 |
{
|
924 |
}
|
925 |
}
|
926 |
|
927 |
+
if (\count($nonBreakableBefore) && $k === \count($new)) {
|
928 |
$k--;
|
929 |
}
|
930 |
|
1010 |
* @param string $part
|
1011 |
* @param array $matches
|
1012 |
*
|
1013 |
+
* @return bool
|
1014 |
*/
|
1015 |
protected function isPseudoSelector($part, &$matches)
|
1016 |
{
|
1017 |
+
if (
|
1018 |
+
strpos($part, ':') === 0 &&
|
1019 |
+
preg_match(",^::?([\w-]+)\((.+)\)$,", $part, $matches)
|
1020 |
) {
|
1021 |
return true;
|
1022 |
}
|
1033 |
*
|
1034 |
* @param array $out
|
1035 |
* @param array $extended
|
1036 |
+
*
|
1037 |
+
* @return void
|
1038 |
*/
|
1039 |
protected function pushOrMergeExtentedSelector(&$out, $extended)
|
1040 |
{
|
1042 |
$single = reset($extended);
|
1043 |
$part = reset($single);
|
1044 |
|
1045 |
+
if (
|
1046 |
+
$this->isPseudoSelector($part, $matchesExtended) &&
|
1047 |
\in_array($matchesExtended[1], [ 'slotted' ])
|
1048 |
) {
|
1049 |
$prev = end($out);
|
1053 |
$single = reset($prev);
|
1054 |
$part = reset($single);
|
1055 |
|
1056 |
+
if (
|
1057 |
+
$this->isPseudoSelector($part, $matchesPrev) &&
|
1058 |
$matchesPrev[1] === $matchesExtended[1]
|
1059 |
) {
|
1060 |
$extended = explode($matchesExtended[1] . '(', $matchesExtended[0], 2);
|
1061 |
+
$extended[1] = $matchesPrev[2] . ', ' . $extended[1];
|
1062 |
$extended = implode($matchesExtended[1] . '(', $extended);
|
1063 |
$extended = [ [ $extended ]];
|
1064 |
array_pop($out);
|
1072 |
/**
|
1073 |
* Match extends single
|
1074 |
*
|
1075 |
+
* @param array $rawSingle
|
1076 |
+
* @param array $outOrigin
|
1077 |
+
* @param bool $initial
|
1078 |
*
|
1079 |
+
* @return bool
|
1080 |
*/
|
1081 |
protected function matchExtendsSingle($rawSingle, &$outOrigin, $initial = true)
|
1082 |
{
|
1118 |
}
|
1119 |
}
|
1120 |
|
1121 |
+
if (
|
1122 |
+
$initial &&
|
1123 |
$this->isPseudoSelector($part, $matches) &&
|
1124 |
! \in_array($matches[1], [ 'not' ])
|
1125 |
) {
|
1126 |
$buffer = $matches[2];
|
1127 |
$parser = $this->parserFactory(__METHOD__);
|
1128 |
|
1129 |
+
if ($parser->parseSelector($buffer, $subSelectors, false)) {
|
1130 |
foreach ($subSelectors as $ksub => $subSelector) {
|
1131 |
$subExtended = [];
|
1132 |
$this->matchExtends($subSelector, $subExtended, 0, false);
|
1141 |
|
1142 |
$subSelectorsExtended = implode(', ', $subSelectorsExtended);
|
1143 |
$singleExtended = $single;
|
1144 |
+
$singleExtended[$k] = str_replace('(' . $buffer . ')', "($subSelectorsExtended)", $part);
|
1145 |
$outOrigin[] = [ $singleExtended ];
|
1146 |
$found = true;
|
1147 |
}
|
1166 |
|
1167 |
foreach ($origin as $j => $new) {
|
1168 |
// prevent infinite loop when target extends itself
|
1169 |
+
if ($this->isSelfExtend($single, $origin) && ! $initial) {
|
1170 |
return false;
|
1171 |
}
|
1172 |
|
1173 |
$replacement = end($new);
|
1174 |
|
1175 |
// Extending a decorated tag with another tag is not possible.
|
1176 |
+
if (
|
1177 |
+
$extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag &&
|
1178 |
preg_match('/^[a-z0-9]+$/i', $replacement[0])
|
1179 |
) {
|
1180 |
unset($origin[$j]);
|
1245 |
$wasTag = false;
|
1246 |
$pseudo = [];
|
1247 |
|
1248 |
+
while (\count($other) && strpos(end($other), ':') === 0) {
|
1249 |
array_unshift($pseudo, array_pop($other));
|
1250 |
}
|
1251 |
|
1252 |
foreach ([array_reverse($base), array_reverse($other)] as $single) {
|
1253 |
+
$rang = count($single);
|
1254 |
+
|
1255 |
foreach ($single as $part) {
|
1256 |
if (preg_match('/^[\[:]/', $part)) {
|
1257 |
$out[] = $part;
|
1259 |
} elseif (preg_match('/^[\.#]/', $part)) {
|
1260 |
array_unshift($out, $part);
|
1261 |
$wasTag = false;
|
1262 |
+
} elseif (preg_match('/^[^_-]/', $part) && $rang === 1) {
|
1263 |
$tag[] = $part;
|
1264 |
$wasTag = true;
|
1265 |
} elseif ($wasTag) {
|
1266 |
$tag[\count($tag) - 1] .= $part;
|
1267 |
} else {
|
1268 |
+
array_unshift($out, $part);
|
1269 |
}
|
1270 |
+
$rang--;
|
1271 |
}
|
1272 |
}
|
1273 |
|
1286 |
* Compile media
|
1287 |
*
|
1288 |
* @param \ScssPhp\ScssPhp\Block $media
|
1289 |
+
*
|
1290 |
+
* @return void
|
1291 |
*/
|
1292 |
protected function compileMedia(Block $media)
|
1293 |
{
|
1294 |
+
assert($media instanceof MediaBlock);
|
1295 |
$this->pushEnv($media);
|
1296 |
|
1297 |
$mediaQueries = $this->compileMediaQuery($this->multiplyMedia($this->env));
|
1298 |
|
1299 |
+
if (! empty($mediaQueries)) {
|
1300 |
+
assert($this->scope !== null);
|
1301 |
$previousScope = $this->scope;
|
1302 |
$parentScope = $this->mediaParent($this->scope);
|
1303 |
|
1314 |
foreach ($media->children as $child) {
|
1315 |
$type = $child[0];
|
1316 |
|
1317 |
+
if (
|
1318 |
+
$type !== Type::T_BLOCK &&
|
1319 |
$type !== Type::T_MEDIA &&
|
1320 |
$type !== Type::T_DIRECTIVE &&
|
1321 |
$type !== Type::T_IMPORT
|
1326 |
}
|
1327 |
|
1328 |
if ($needsWrap) {
|
1329 |
+
$wrapped = new Block();
|
1330 |
$wrapped->sourceName = $media->sourceName;
|
1331 |
$wrapped->sourceIndex = $media->sourceIndex;
|
1332 |
$wrapped->sourceLine = $media->sourceLine;
|
1337 |
$wrapped->children = $media->children;
|
1338 |
|
1339 |
$media->children = [[Type::T_BLOCK, $wrapped]];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1340 |
}
|
1341 |
|
1342 |
$this->compileChildrenNoReturn($media->children, $this->scope);
|
1370 |
/**
|
1371 |
* Compile directive
|
1372 |
*
|
1373 |
+
* @param DirectiveBlock|array $directive
|
1374 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
1375 |
+
*
|
1376 |
+
* @return void
|
1377 |
*/
|
1378 |
protected function compileDirective($directive, OutputBlock $out)
|
1379 |
{
|
1380 |
if (\is_array($directive)) {
|
1381 |
+
$directiveName = $this->compileDirectiveName($directive[0]);
|
1382 |
+
$s = '@' . $directiveName;
|
1383 |
|
1384 |
if (! empty($directive[1])) {
|
1385 |
$s .= ' ' . $this->compileValue($directive[1]);
|
1386 |
}
|
1387 |
+
// sass-spec compliance on newline after directives, a bit tricky :/
|
1388 |
+
$appendNewLine = (! empty($directive[2]) || strpos($s, "\n")) ? "\n" : "";
|
1389 |
+
if (\is_array($directive[0]) && empty($directive[1])) {
|
1390 |
+
$appendNewLine = "\n";
|
1391 |
+
}
|
1392 |
|
1393 |
+
if (empty($directive[3])) {
|
1394 |
+
$this->appendRootDirective($s . ';' . $appendNewLine, $out, [Type::T_COMMENT, Type::T_DIRECTIVE]);
|
1395 |
+
} else {
|
1396 |
+
$this->appendOutputLine($out, Type::T_DIRECTIVE, $s . ';');
|
1397 |
+
}
|
1398 |
} else {
|
1399 |
+
$directive->name = $this->compileDirectiveName($directive->name);
|
1400 |
$s = '@' . $directive->name;
|
1401 |
|
1402 |
if (! empty($directive->value)) {
|
1411 |
}
|
1412 |
}
|
1413 |
|
1414 |
+
/**
|
1415 |
+
* directive names can include some interpolation
|
1416 |
+
*
|
1417 |
+
* @param string|array $directiveName
|
1418 |
+
* @return string
|
1419 |
+
* @throws CompilerException
|
1420 |
+
*/
|
1421 |
+
protected function compileDirectiveName($directiveName)
|
1422 |
+
{
|
1423 |
+
if (is_string($directiveName)) {
|
1424 |
+
return $directiveName;
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
return $this->compileValue($directiveName);
|
1428 |
+
}
|
1429 |
+
|
1430 |
/**
|
1431 |
* Compile at-root
|
1432 |
*
|
1433 |
* @param \ScssPhp\ScssPhp\Block $block
|
1434 |
+
*
|
1435 |
+
* @return void
|
1436 |
*/
|
1437 |
protected function compileAtRoot(Block $block)
|
1438 |
{
|
1439 |
+
assert($block instanceof AtRootBlock);
|
1440 |
$env = $this->pushEnv($block);
|
1441 |
$envs = $this->compactEnv($env);
|
1442 |
list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null);
|
1443 |
|
1444 |
// wrap inline selector
|
1445 |
if ($block->selector) {
|
1446 |
+
$wrapped = new Block();
|
1447 |
$wrapped->sourceName = $block->sourceName;
|
1448 |
$wrapped->sourceIndex = $block->sourceIndex;
|
1449 |
$wrapped->sourceLine = $block->sourceLine;
|
1459 |
}
|
1460 |
|
1461 |
$selfParent = $block->selfParent;
|
1462 |
+
assert($selfParent !== null, 'at-root blocks must have a selfParent set.');
|
1463 |
|
1464 |
+
if (
|
1465 |
+
! $selfParent->selectors &&
|
1466 |
+
isset($block->parent) &&
|
1467 |
isset($block->parent->selectors) && $block->parent->selectors
|
1468 |
) {
|
1469 |
$selfParent = $block->parent;
|
1471 |
|
1472 |
$this->env = $this->filterWithWithout($envs, $with, $without);
|
1473 |
|
1474 |
+
assert($this->scope !== null);
|
1475 |
$saveScope = $this->scope;
|
1476 |
$this->scope = $this->filterScopeWithWithout($saveScope, $with, $without);
|
1477 |
|
1478 |
// propagate selfParent to the children where they still can be useful
|
1479 |
$this->compileChildrenNoReturn($block->children, $this->scope, $selfParent);
|
1480 |
|
1481 |
+
assert($this->scope !== null);
|
1482 |
+
$this->completeScope($this->scope, $saveScope);
|
1483 |
$this->scope = $saveScope;
|
1484 |
$this->env = $this->extractEnv($envs);
|
1485 |
|
1487 |
}
|
1488 |
|
1489 |
/**
|
1490 |
+
* Filter at-root scope depending on with/without option
|
1491 |
*
|
1492 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1493 |
* @param array $with
|
1494 |
* @param array $without
|
1495 |
*
|
1496 |
+
* @return OutputBlock
|
1497 |
*/
|
1498 |
protected function filterScopeWithWithout($scope, $with, $without)
|
1499 |
{
|
1500 |
$filteredScopes = [];
|
1501 |
$childStash = [];
|
1502 |
|
1503 |
+
if ($scope->type === Type::T_ROOT) {
|
1504 |
return $scope;
|
1505 |
}
|
1506 |
+
assert($this->rootBlock !== null);
|
1507 |
|
1508 |
// start from the root
|
1509 |
+
while ($scope->parent && $scope->parent->type !== Type::T_ROOT) {
|
1510 |
array_unshift($childStash, $scope);
|
1511 |
$scope = $scope->parent;
|
1512 |
}
|
1567 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1568 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $previousScope
|
1569 |
*
|
1570 |
+
* @return OutputBlock
|
1571 |
*/
|
1572 |
protected function completeScope($scope, $previousScope)
|
1573 |
{
|
1574 |
+
if (! $scope->type && ! $scope->selectors && \count($scope->lines)) {
|
1575 |
$scope->selectors = $this->findScopeSelectors($previousScope, $scope->depth);
|
1576 |
}
|
1577 |
|
1588 |
* Find a selector by the depth node in the scope
|
1589 |
*
|
1590 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope
|
1591 |
+
* @param int $depth
|
1592 |
*
|
1593 |
* @return array
|
1594 |
*/
|
1612 |
/**
|
1613 |
* Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later
|
1614 |
*
|
1615 |
+
* @param array|null $withCondition
|
1616 |
*
|
1617 |
* @return array
|
1618 |
+
*
|
1619 |
+
* @phpstan-return array{array<string, bool>, array<string, bool>}
|
1620 |
*/
|
1621 |
protected function compileWith($withCondition)
|
1622 |
{
|
1625 |
$without = ['rule' => true];
|
1626 |
|
1627 |
if ($withCondition) {
|
1628 |
+
if ($withCondition[0] === Type::T_INTERPOLATE) {
|
1629 |
+
$w = $this->compileValue($withCondition);
|
1630 |
+
|
1631 |
+
$buffer = "($w)";
|
1632 |
+
$parser = $this->parserFactory(__METHOD__);
|
1633 |
+
|
1634 |
+
if ($parser->parseValue($buffer, $reParsedWith)) {
|
1635 |
+
$withCondition = $reParsedWith;
|
1636 |
+
}
|
1637 |
+
}
|
1638 |
+
|
1639 |
+
$withConfig = $this->mapGet($withCondition, static::$with);
|
1640 |
+
if ($withConfig !== null) {
|
1641 |
$without = []; // cancel the default
|
1642 |
+
$list = $this->coerceList($withConfig);
|
1643 |
|
1644 |
foreach ($list[2] as $item) {
|
1645 |
$keyword = $this->compileStringContent($this->coerceString($item));
|
1648 |
}
|
1649 |
}
|
1650 |
|
1651 |
+
$withoutConfig = $this->mapGet($withCondition, static::$without);
|
1652 |
+
if ($withoutConfig !== null) {
|
1653 |
$without = []; // cancel the default
|
1654 |
+
$list = $this->coerceList($withoutConfig);
|
1655 |
|
1656 |
foreach ($list[2] as $item) {
|
1657 |
$keyword = $this->compileStringContent($this->coerceString($item));
|
1667 |
/**
|
1668 |
* Filter env stack
|
1669 |
*
|
1670 |
+
* @param Environment[] $envs
|
1671 |
* @param array $with
|
1672 |
* @param array $without
|
1673 |
*
|
1674 |
+
* @return Environment
|
1675 |
+
*
|
1676 |
+
* @phpstan-param non-empty-array<Environment> $envs
|
1677 |
*/
|
1678 |
protected function filterWithWithout($envs, $with, $without)
|
1679 |
{
|
1701 |
* @param array $with
|
1702 |
* @param array $without
|
1703 |
*
|
1704 |
+
* @return bool
|
1705 |
*/
|
1706 |
protected function isWith($block, $with, $without)
|
1707 |
{
|
1711 |
}
|
1712 |
|
1713 |
if ($block->type === Type::T_DIRECTIVE) {
|
1714 |
+
assert($block instanceof DirectiveBlock || $block instanceof OutputBlock);
|
1715 |
if (isset($block->name)) {
|
1716 |
+
return $this->testWithWithout($this->compileDirectiveName($block->name), $with, $without);
|
1717 |
} elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) {
|
1718 |
return $this->testWithWithout($m[1], $with, $without);
|
1719 |
} else {
|
1729 |
$s = reset($s);
|
1730 |
}
|
1731 |
|
1732 |
+
if (\is_object($s) && $s instanceof Number) {
|
1733 |
return $this->testWithWithout('keyframes', $with, $without);
|
1734 |
}
|
1735 |
}
|
1747 |
* @param array $with
|
1748 |
* @param array $without
|
1749 |
*
|
1750 |
+
* @return bool
|
1751 |
* true if the block should be kept, false to reject
|
1752 |
*/
|
1753 |
protected function testWithWithout($what, $with, $without)
|
1754 |
{
|
|
|
1755 |
// if without, reject only if in the list (or 'all' is in the list)
|
1756 |
if (\count($without)) {
|
1757 |
return (isset($without[$what]) || isset($without['all'])) ? false : true;
|
1766 |
* Compile keyframe block
|
1767 |
*
|
1768 |
* @param \ScssPhp\ScssPhp\Block $block
|
1769 |
+
* @param string[] $selectors
|
1770 |
+
*
|
1771 |
+
* @return void
|
1772 |
*/
|
1773 |
protected function compileKeyframeBlock(Block $block, $selectors)
|
1774 |
{
|
1782 |
|
1783 |
$this->scope = $this->makeOutputBlock($block->type, $selectors);
|
1784 |
$this->scope->depth = 1;
|
1785 |
+
assert($this->scope->parent !== null);
|
1786 |
$this->scope->parent->children[] = $this->scope;
|
1787 |
|
1788 |
$this->compileChildrenNoReturn($block->children, $this->scope);
|
1789 |
|
1790 |
+
assert($this->scope !== null);
|
1791 |
$this->scope = $this->scope->parent;
|
1792 |
$this->env = $this->extractEnv($envs);
|
1793 |
|
1799 |
*
|
1800 |
* @param \ScssPhp\ScssPhp\Block $block
|
1801 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
1802 |
+
*
|
1803 |
+
* @return void
|
1804 |
*/
|
1805 |
protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out)
|
1806 |
{
|
1807 |
+
assert($block instanceof NestedPropertyBlock);
|
1808 |
$prefix = $this->compileValue($block->prefix) . '-';
|
1809 |
|
1810 |
$nested = $this->makeOutputBlock($block->type);
|
1823 |
break;
|
1824 |
|
1825 |
case Type::T_NESTED_PROPERTY:
|
1826 |
+
assert($child[1] instanceof NestedPropertyBlock);
|
1827 |
array_unshift($child[1]->prefix[2], $prefix);
|
1828 |
break;
|
1829 |
}
|
1836 |
* Compile nested block
|
1837 |
*
|
1838 |
* @param \ScssPhp\ScssPhp\Block $block
|
1839 |
+
* @param string[] $selectors
|
1840 |
+
*
|
1841 |
+
* @return void
|
1842 |
*/
|
1843 |
protected function compileNestedBlock(Block $block, $selectors)
|
1844 |
{
|
1845 |
$this->pushEnv($block);
|
1846 |
|
1847 |
$this->scope = $this->makeOutputBlock($block->type, $selectors);
|
1848 |
+
assert($this->scope->parent !== null);
|
1849 |
$this->scope->parent->children[] = $this->scope;
|
1850 |
|
1851 |
// wrap assign children in a block
|
1852 |
// except for @font-face
|
1853 |
+
if (!$block instanceof DirectiveBlock || $this->compileDirectiveName($block->name) !== 'font-face') {
|
1854 |
// need wrapping?
|
1855 |
$needWrapping = false;
|
1856 |
|
1862 |
}
|
1863 |
|
1864 |
if ($needWrapping) {
|
1865 |
+
$wrapped = new Block();
|
1866 |
$wrapped->sourceName = $block->sourceName;
|
1867 |
$wrapped->sourceIndex = $block->sourceIndex;
|
1868 |
$wrapped->sourceLine = $block->sourceLine;
|
1879 |
|
1880 |
$this->compileChildrenNoReturn($block->children, $this->scope);
|
1881 |
|
1882 |
+
assert($this->scope !== null);
|
1883 |
$this->scope = $this->scope->parent;
|
1884 |
|
1885 |
$this->popEnv();
|
1902 |
* @see Compiler::compileChild()
|
1903 |
*
|
1904 |
* @param \ScssPhp\ScssPhp\Block $block
|
1905 |
+
*
|
1906 |
+
* @return void
|
1907 |
*/
|
1908 |
protected function compileBlock(Block $block)
|
1909 |
{
|
1910 |
$env = $this->pushEnv($block);
|
1911 |
+
assert($block->selectors !== null);
|
1912 |
$env->selectors = $this->evalSelectors($block->selectors);
|
1913 |
|
1914 |
$out = $this->makeOutputBlock(null);
|
1915 |
|
1916 |
+
assert($this->scope !== null);
|
1917 |
+
$this->scope->children[] = $out;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1918 |
|
1919 |
if (\count($block->children)) {
|
1920 |
$out->selectors = $this->multiplySelectors($env, $block->selfParent);
|
1931 |
|
1932 |
// and revert for the following children of the same block
|
1933 |
if ($selfParentSelectors) {
|
1934 |
+
assert($block->selfParent !== null);
|
1935 |
$block->selfParent->selectors = $selfParentSelectors;
|
1936 |
}
|
1937 |
}
|
1943 |
/**
|
1944 |
* Compile the value of a comment that can have interpolation
|
1945 |
*
|
1946 |
+
* @param array $value
|
1947 |
+
* @param bool $pushEnv
|
1948 |
*
|
1949 |
+
* @return string
|
1950 |
*/
|
1951 |
protected function compileCommentValue($value, $pushEnv = false)
|
1952 |
{
|
1957 |
$this->pushEnv();
|
1958 |
}
|
1959 |
|
|
|
|
|
|
|
1960 |
try {
|
1961 |
$c = $this->compileValue($value[2]);
|
1962 |
+
} catch (SassScriptException $e) {
|
1963 |
+
$this->logger->warn('Ignoring interpolation errors in multiline comments is deprecated and will be removed in ScssPhp 2.0. ' . $this->addLocationToMessage($e->getMessage()), true);
|
1964 |
+
// ignore error in comment compilation which are only interpolation
|
1965 |
+
} catch (SassException $e) {
|
1966 |
+
$this->logger->warn('Ignoring interpolation errors in multiline comments is deprecated and will be removed in ScssPhp 2.0. ' . $e->getMessage(), true);
|
1967 |
// ignore error in comment compilation which are only interpolation
|
1968 |
}
|
1969 |
|
|
|
|
|
1970 |
if ($pushEnv) {
|
1971 |
$this->popEnv();
|
1972 |
}
|
1979 |
* Compile root level comment
|
1980 |
*
|
1981 |
* @param array $block
|
1982 |
+
*
|
1983 |
+
* @return void
|
1984 |
*/
|
1985 |
protected function compileComment($block)
|
1986 |
{
|
1987 |
$out = $this->makeOutputBlock(Type::T_COMMENT);
|
1988 |
$out->lines[] = $this->compileCommentValue($block, true);
|
1989 |
|
1990 |
+
assert($this->scope !== null);
|
1991 |
$this->scope->children[] = $out;
|
1992 |
}
|
1993 |
|
2002 |
{
|
2003 |
$this->shouldEvaluate = false;
|
2004 |
|
2005 |
+
$evaluatedSelectors = [];
|
2006 |
+
foreach ($selectors as $selector) {
|
2007 |
+
$evaluatedSelectors[] = $this->evalSelector($selector);
|
2008 |
+
}
|
2009 |
+
$selectors = $evaluatedSelectors;
|
2010 |
|
2011 |
// after evaluating interpolates, we might need a second pass
|
2012 |
if ($this->shouldEvaluate) {
|
2013 |
+
$selectors = $this->replaceSelfSelector($selectors, '&');
|
2014 |
$buffer = $this->collapseSelectors($selectors);
|
2015 |
$parser = $this->parserFactory(__METHOD__);
|
2016 |
|
2017 |
+
try {
|
2018 |
+
$isValid = $parser->parseSelector($buffer, $newSelectors, true);
|
2019 |
+
} catch (ParserException $e) {
|
2020 |
+
throw $this->error($e->getMessage());
|
2021 |
+
}
|
2022 |
+
|
2023 |
+
if ($isValid) {
|
2024 |
$selectors = array_map([$this, 'evalSelector'], $newSelectors);
|
2025 |
}
|
2026 |
}
|
2034 |
* @param array $selector
|
2035 |
*
|
2036 |
* @return array
|
2037 |
+
*
|
2038 |
+
* @phpstan-impure
|
2039 |
*/
|
2040 |
protected function evalSelector($selector)
|
2041 |
{
|
2048 |
* @param array $part
|
2049 |
*
|
2050 |
* @return array
|
2051 |
+
*
|
2052 |
+
* @phpstan-impure
|
2053 |
*/
|
2054 |
protected function evalSelectorPart($part)
|
2055 |
{
|
2057 |
if (\is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) {
|
2058 |
$p = $this->compileValue($p);
|
2059 |
|
2060 |
+
// force re-evaluation if self char or non standard char
|
2061 |
+
if (preg_match(',[^\w-],', $p)) {
|
2062 |
$this->shouldEvaluate = true;
|
2063 |
}
|
2064 |
+
} elseif (
|
2065 |
+
\is_string($p) && \strlen($p) >= 2 &&
|
2066 |
+
($p[0] === '"' || $p[0] === "'") &&
|
2067 |
+
substr($p, -1) === $p[0]
|
2068 |
) {
|
2069 |
$p = substr($p, 1, -1);
|
2070 |
}
|
2076 |
/**
|
2077 |
* Collapse selectors
|
2078 |
*
|
2079 |
+
* @param array $selectors
|
|
|
|
|
|
|
2080 |
*
|
2081 |
* @return string
|
2082 |
*/
|
2083 |
+
protected function collapseSelectors($selectors)
|
2084 |
+
{
|
2085 |
+
$parts = [];
|
2086 |
+
|
2087 |
+
foreach ($selectors as $selector) {
|
2088 |
+
$output = [];
|
2089 |
+
|
2090 |
+
foreach ($selector as $node) {
|
2091 |
+
$compound = '';
|
2092 |
+
|
2093 |
+
array_walk_recursive(
|
2094 |
+
$node,
|
2095 |
+
function ($value, $key) use (&$compound) {
|
2096 |
+
$compound .= $value;
|
2097 |
+
}
|
2098 |
+
);
|
2099 |
+
|
2100 |
+
$output[] = $compound;
|
2101 |
+
}
|
2102 |
+
|
2103 |
+
$parts[] = implode(' ', $output);
|
2104 |
+
}
|
2105 |
+
|
2106 |
+
return implode(', ', $parts);
|
2107 |
+
}
|
2108 |
+
|
2109 |
+
/**
|
2110 |
+
* Collapse selectors
|
2111 |
+
*
|
2112 |
+
* @param array $selectors
|
2113 |
+
*
|
2114 |
+
* @return array
|
2115 |
+
*/
|
2116 |
+
private function collapseSelectorsAsList($selectors)
|
2117 |
{
|
2118 |
$parts = [];
|
2119 |
|
2131 |
}
|
2132 |
);
|
2133 |
|
2134 |
+
if ($this->isImmediateRelationshipCombinator($compound)) {
|
2135 |
if (\count($output)) {
|
2136 |
$output[\count($output) - 1] .= ' ' . $compound;
|
2137 |
} else {
|
2147 |
}
|
2148 |
}
|
2149 |
|
2150 |
+
foreach ($output as &$o) {
|
2151 |
+
$o = [Type::T_STRING, '', [$o]];
|
|
|
|
|
|
|
|
|
|
|
|
|
2152 |
}
|
2153 |
|
2154 |
+
$parts[] = [Type::T_LIST, ' ', $output];
|
|
|
|
|
|
|
|
|
|
|
|
|
2155 |
}
|
2156 |
|
2157 |
+
return [Type::T_LIST, ',', $parts];
|
2158 |
}
|
2159 |
|
2160 |
/**
|
2161 |
* Parse down the selector and revert [self] to "&" before a reparsing
|
2162 |
*
|
2163 |
+
* @param array $selectors
|
2164 |
+
* @param string|null $replace
|
2165 |
*
|
2166 |
* @return array
|
2167 |
*/
|
2168 |
+
protected function replaceSelfSelector($selectors, $replace = null)
|
2169 |
{
|
2170 |
foreach ($selectors as &$part) {
|
2171 |
if (\is_array($part)) {
|
2172 |
if ($part === [Type::T_SELF]) {
|
2173 |
+
if (\is_null($replace)) {
|
2174 |
+
$replace = $this->reduce([Type::T_SELF]);
|
2175 |
+
$replace = $this->compileValue($replace);
|
2176 |
+
}
|
2177 |
+
$part = $replace;
|
2178 |
} else {
|
2179 |
+
$part = $this->replaceSelfSelector($part, $replace);
|
2180 |
}
|
2181 |
}
|
2182 |
}
|
2196 |
$joined = [];
|
2197 |
|
2198 |
foreach ($single as $part) {
|
2199 |
+
if (
|
2200 |
+
empty($joined) ||
|
2201 |
! \is_string($part) ||
|
2202 |
preg_match('/[\[.:#%]/', $part)
|
2203 |
) {
|
2270 |
*
|
2271 |
* @param array $selector
|
2272 |
*
|
2273 |
+
* @return bool
|
2274 |
*/
|
2275 |
protected function hasSelectorPlaceholder($selector)
|
2276 |
{
|
2289 |
return false;
|
2290 |
}
|
2291 |
|
2292 |
+
/**
|
2293 |
+
* @param string $name
|
2294 |
+
*
|
2295 |
+
* @return void
|
2296 |
+
*/
|
2297 |
protected function pushCallStack($name = '')
|
2298 |
{
|
2299 |
$this->callStack[] = [
|
2307 |
if (\count($this->callStack) > 25000) {
|
2308 |
// not displayed but you can var_dump it to deep debug
|
2309 |
$msg = $this->callStackMessage(true, 100);
|
2310 |
+
$msg = 'Infinite calling loop';
|
2311 |
|
2312 |
+
throw $this->error($msg);
|
2313 |
}
|
2314 |
}
|
2315 |
|
2316 |
+
/**
|
2317 |
+
* @return void
|
2318 |
+
*/
|
2319 |
protected function popCallStack()
|
2320 |
{
|
2321 |
array_pop($this->callStack);
|
2328 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2329 |
* @param string $traceName
|
2330 |
*
|
2331 |
+
* @return array|Number|null
|
2332 |
*/
|
2333 |
protected function compileChildren($stms, OutputBlock $out, $traceName = '')
|
2334 |
{
|
2350 |
}
|
2351 |
|
2352 |
/**
|
2353 |
+
* Compile children and throw exception if unexpected `@return`
|
2354 |
*
|
2355 |
* @param array $stms
|
2356 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2357 |
* @param \ScssPhp\ScssPhp\Block $selfParent
|
2358 |
* @param string $traceName
|
2359 |
*
|
2360 |
+
* @return void
|
2361 |
+
*
|
2362 |
* @throws \Exception
|
2363 |
*/
|
2364 |
protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent = null, $traceName = '')
|
2370 |
$stm[1]->selfParent = $selfParent;
|
2371 |
$ret = $this->compileChild($stm, $out);
|
2372 |
$stm[1]->selfParent = null;
|
2373 |
+
} elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) {
|
2374 |
$stm['selfParent'] = $selfParent;
|
2375 |
$ret = $this->compileChild($stm, $out);
|
2376 |
unset($stm['selfParent']);
|
2379 |
}
|
2380 |
|
2381 |
if (isset($ret)) {
|
2382 |
+
throw $this->error('@return may only be used within a function');
|
|
|
|
|
|
|
2383 |
}
|
2384 |
}
|
2385 |
|
2388 |
|
2389 |
|
2390 |
/**
|
2391 |
+
* evaluate media query : compile internal value keeping the structure unchanged
|
2392 |
*
|
2393 |
* @param array $queryList
|
2394 |
*
|
2409 |
|
2410 |
// the parser had no mean to know if media type or expression if it was an interpolation
|
2411 |
// so you need to reparse if the T_MEDIA_TYPE looks like anything else a media type
|
2412 |
+
if (
|
2413 |
+
$q[0] == Type::T_MEDIA_TYPE &&
|
2414 |
(strpos($value, '(') !== false ||
|
2415 |
strpos($value, ')') !== false ||
|
2416 |
strpos($value, ':') !== false ||
|
2431 |
$queryString = $this->compileMediaQuery([$queryList[$kql]]);
|
2432 |
$queryString = reset($queryString);
|
2433 |
|
2434 |
+
if ($queryString !== false && strpos($queryString, '@media ') === 0) {
|
2435 |
$queryString = substr($queryString, 7);
|
2436 |
$queries = [];
|
2437 |
|
2458 |
*
|
2459 |
* @param array $queryList
|
2460 |
*
|
2461 |
+
* @return string[]
|
2462 |
*/
|
2463 |
protected function compileMediaQuery($queryList)
|
2464 |
{
|
2465 |
$start = '@media ';
|
2466 |
$default = trim($start);
|
2467 |
$out = [];
|
2468 |
+
$current = '';
|
2469 |
|
2470 |
foreach ($queryList as $query) {
|
2471 |
$type = null;
|
2504 |
$out[] = $start . $current;
|
2505 |
}
|
2506 |
|
2507 |
+
$current = '';
|
2508 |
$type = null;
|
2509 |
$parts = [];
|
2510 |
}
|
2679 |
}
|
2680 |
|
2681 |
// t1 == t2, neither m1 nor m2 are "not"
|
2682 |
+
return [empty($m1) ? $m2 : $m1, $t1];
|
2683 |
}
|
2684 |
|
2685 |
/**
|
2687 |
*
|
2688 |
* @param array $rawPath
|
2689 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2690 |
+
* @param bool $once
|
2691 |
*
|
2692 |
+
* @return bool
|
2693 |
*/
|
2694 |
protected function compileImport($rawPath, OutputBlock $out, $once = false)
|
2695 |
{
|
2696 |
if ($rawPath[0] === Type::T_STRING) {
|
2697 |
$path = $this->compileStringContent($rawPath);
|
2698 |
|
2699 |
+
if (strpos($path, 'url(') !== 0 && $filePath = $this->findImport($path, $this->currentDirectory)) {
|
2700 |
+
$this->registerImport($this->currentDirectory, $path, $filePath);
|
2701 |
+
|
2702 |
+
if (! $once || ! \in_array($filePath, $this->importedFiles)) {
|
2703 |
+
$this->importFile($filePath, $out);
|
2704 |
+
$this->importedFiles[] = $filePath;
|
2705 |
}
|
2706 |
|
2707 |
return true;
|
2708 |
}
|
2709 |
|
2710 |
+
$this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out);
|
2711 |
|
2712 |
return false;
|
2713 |
}
|
2720 |
|
2721 |
foreach ($rawPath[2] as $path) {
|
2722 |
if ($path[0] !== Type::T_STRING) {
|
2723 |
+
$this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out);
|
2724 |
|
2725 |
return false;
|
2726 |
}
|
2733 |
return true;
|
2734 |
}
|
2735 |
|
2736 |
+
$this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out);
|
2737 |
|
2738 |
return false;
|
2739 |
}
|
2740 |
|
2741 |
+
/**
|
2742 |
+
* @param array $rawPath
|
2743 |
+
* @return string
|
2744 |
+
* @throws CompilerException
|
2745 |
+
*/
|
2746 |
+
protected function compileImportPath($rawPath)
|
2747 |
+
{
|
2748 |
+
$path = $this->compileValue($rawPath);
|
2749 |
+
|
2750 |
+
// case url() without quotes : suppress \r \n remaining in the path
|
2751 |
+
// if this is a real string there can not be CR or LF char
|
2752 |
+
if (strpos($path, 'url(') === 0) {
|
2753 |
+
$path = str_replace(array("\r", "\n"), array('', ' '), $path);
|
2754 |
+
} else {
|
2755 |
+
// if this is a file name in a string, spaces should be escaped
|
2756 |
+
$path = $this->reduce($rawPath);
|
2757 |
+
$path = $this->escapeImportPathString($path);
|
2758 |
+
$path = $this->compileValue($path);
|
2759 |
+
}
|
2760 |
+
|
2761 |
+
return $path;
|
2762 |
+
}
|
2763 |
+
|
2764 |
+
/**
|
2765 |
+
* @param array $path
|
2766 |
+
* @return array
|
2767 |
+
* @throws CompilerException
|
2768 |
+
*/
|
2769 |
+
protected function escapeImportPathString($path)
|
2770 |
+
{
|
2771 |
+
switch ($path[0]) {
|
2772 |
+
case Type::T_LIST:
|
2773 |
+
foreach ($path[2] as $k => $v) {
|
2774 |
+
$path[2][$k] = $this->escapeImportPathString($v);
|
2775 |
+
}
|
2776 |
+
break;
|
2777 |
+
case Type::T_STRING:
|
2778 |
+
if ($path[1]) {
|
2779 |
+
$path = $this->compileValue($path);
|
2780 |
+
$path = str_replace(' ', '\\ ', $path);
|
2781 |
+
$path = [Type::T_KEYWORD, $path];
|
2782 |
+
}
|
2783 |
+
break;
|
2784 |
+
}
|
2785 |
+
|
2786 |
+
return $path;
|
2787 |
+
}
|
2788 |
|
2789 |
/**
|
2790 |
* Append a root directive like @import or @charset as near as the possible from the source code
|
2791 |
* (keeping before comments, @import and @charset coming before in the source code)
|
2792 |
*
|
2793 |
+
* @param string $line
|
2794 |
+
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2795 |
+
* @param array $allowed
|
2796 |
+
*
|
2797 |
+
* @return void
|
2798 |
*/
|
2799 |
protected function appendRootDirective($line, $out, $allowed = [Type::T_COMMENT])
|
2800 |
{
|
2824 |
// insert the directive as a comment
|
2825 |
$child = $this->makeOutputBlock(Type::T_COMMENT);
|
2826 |
$child->lines[] = $line;
|
2827 |
+
$child->sourceName = $this->sourceNames[$this->sourceIndex] ?: '(stdin)';
|
2828 |
$child->sourceLine = $this->sourceLine;
|
2829 |
$child->sourceColumn = $this->sourceColumn;
|
2830 |
|
2842 |
*
|
2843 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2844 |
* @param string $type
|
2845 |
+
* @param string $line
|
2846 |
+
*
|
2847 |
+
* @return void
|
2848 |
*/
|
2849 |
protected function appendOutputLine(OutputBlock $out, $type, $line)
|
2850 |
{
|
2851 |
$outWrite = &$out;
|
2852 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2853 |
// check if it's a flat output or not
|
2854 |
if (\count($out->children)) {
|
2855 |
$lastChild = &$out->children[\count($out->children) - 1];
|
2856 |
|
2857 |
+
if (
|
2858 |
+
$lastChild->depth === $out->depth &&
|
2859 |
\is_null($lastChild->selectors) &&
|
2860 |
! \count($lastChild->children)
|
2861 |
) {
|
2879 |
* @param array $child
|
2880 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
2881 |
*
|
2882 |
+
* @return array|Number|null
|
2883 |
*/
|
2884 |
protected function compileChild($child, OutputBlock $out)
|
2885 |
{
|
2887 |
$this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null;
|
2888 |
$this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1;
|
2889 |
$this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1;
|
2890 |
+
} elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) {
|
2891 |
$this->sourceIndex = $child[1]->sourceIndex;
|
2892 |
$this->sourceLine = $child[1]->sourceLine;
|
2893 |
$this->sourceColumn = $child[1]->sourceColumn;
|
2894 |
} elseif (! empty($out->sourceLine) && ! empty($out->sourceName)) {
|
2895 |
$this->sourceLine = $out->sourceLine;
|
2896 |
+
$sourceIndex = array_search($out->sourceName, $this->sourceNames);
|
2897 |
$this->sourceColumn = $out->sourceColumn;
|
2898 |
|
2899 |
+
if ($sourceIndex === false) {
|
2900 |
+
$sourceIndex = null;
|
2901 |
}
|
2902 |
+
$this->sourceIndex = $sourceIndex;
|
2903 |
}
|
2904 |
|
2905 |
switch ($child[0]) {
|
2932 |
break;
|
2933 |
|
2934 |
case Type::T_CHARSET:
|
|
|
|
|
|
|
|
|
2935 |
break;
|
2936 |
|
2937 |
case Type::T_CUSTOM_PROPERTY:
|
3004 |
break;
|
3005 |
}
|
3006 |
|
3007 |
+
if ($compiledName === 'font' && $value[0] === Type::T_LIST && $value[1] === ',') {
|
3008 |
// this is the case if more than one font is given: example: "font: 400 1em/1.3 arial,helvetica"
|
3009 |
// we need to handle the first list element
|
3010 |
$shorthandValue=&$value[2][0];
|
3020 |
$divider = $this->reduce($divider, true);
|
3021 |
}
|
3022 |
|
3023 |
+
if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) {
|
3024 |
$revert = false;
|
3025 |
}
|
3026 |
}
|
3033 |
if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') {
|
3034 |
if ($maxShorthandDividers > 0) {
|
3035 |
$revert = true;
|
3036 |
+
|
3037 |
// if the list of values is too long, this has to be a shorthand,
|
3038 |
// otherwise it could be a real division
|
3039 |
+
if (\is_null($maxListElements) || \count($shorthandValue[2]) <= $maxListElements) {
|
3040 |
if ($shorthandDividerNeedsUnit) {
|
3041 |
$divider = $item[3];
|
3042 |
|
3044 |
$divider = $this->reduce($divider, true);
|
3045 |
}
|
3046 |
|
3047 |
+
if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) {
|
3048 |
$revert = false;
|
3049 |
}
|
3050 |
}
|
3095 |
case Type::T_MIXIN:
|
3096 |
case Type::T_FUNCTION:
|
3097 |
list(, $block) = $child;
|
3098 |
+
assert($block instanceof CallableBlock);
|
3099 |
// the block need to be able to go up to it's parent env to resolve vars
|
3100 |
$block->parentEnv = $this->getStoreEnv();
|
3101 |
$this->set(static::$namespaces[$block->type] . $block->name, $block, true);
|
3103 |
|
3104 |
case Type::T_EXTEND:
|
3105 |
foreach ($child[1] as $sel) {
|
3106 |
+
$replacedSel = $this->replaceSelfSelector($sel);
|
3107 |
+
|
3108 |
+
if ($replacedSel !== $sel) {
|
3109 |
+
throw $this->error('Parent selectors aren\'t allowed here.');
|
3110 |
+
}
|
3111 |
+
|
3112 |
$results = $this->evalSelectors([$sel]);
|
3113 |
|
3114 |
foreach ($results as $result) {
|
3115 |
+
if (\count($result) !== 1) {
|
3116 |
+
throw $this->error('complex selectors may not be extended.');
|
3117 |
+
}
|
3118 |
+
|
3119 |
// only use the first one
|
3120 |
+
$result = $result[0];
|
3121 |
$selectors = $out->selectors;
|
3122 |
|
3123 |
if (! $selectors && isset($child['selfParent'])) {
|
3124 |
$selectors = $this->multiplySelectors($this->env, $child['selfParent']);
|
3125 |
}
|
3126 |
+
assert($selectors !== null);
|
3127 |
+
|
3128 |
+
if (\count($result) > 1) {
|
3129 |
+
$replacement = implode(', ', $result);
|
3130 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
3131 |
+
$line = $this->sourceLine;
|
3132 |
+
|
3133 |
+
$message = <<<EOL
|
3134 |
+
on line $line of $fname:
|
3135 |
+
Compound selectors may no longer be extended.
|
3136 |
+
Consider `@extend $replacement` instead.
|
3137 |
+
See http://bit.ly/ExtendCompound for details.
|
3138 |
+
EOL;
|
3139 |
+
|
3140 |
+
$this->logger->warn($message);
|
3141 |
+
}
|
3142 |
|
3143 |
$this->pushExtends($result, $selectors, $child);
|
3144 |
}
|
3147 |
|
3148 |
case Type::T_IF:
|
3149 |
list(, $if) = $child;
|
3150 |
+
assert($if instanceof IfBlock);
|
3151 |
|
3152 |
if ($this->isTruthy($this->reduce($if->cond, true))) {
|
3153 |
return $this->compileChildren($if->children, $out);
|
3154 |
}
|
3155 |
|
3156 |
foreach ($if->cases as $case) {
|
3157 |
+
if (
|
3158 |
+
$case instanceof ElseBlock ||
|
3159 |
+
$case instanceof ElseifBlock && $this->isTruthy($this->reduce($case->cond))
|
3160 |
) {
|
3161 |
return $this->compileChildren($case->children, $out);
|
3162 |
}
|
3165 |
|
3166 |
case Type::T_EACH:
|
3167 |
list(, $each) = $child;
|
3168 |
+
assert($each instanceof EachBlock);
|
3169 |
|
3170 |
$list = $this->coerceList($this->reduce($each->list), ',', true);
|
3171 |
|
3185 |
$ret = $this->compileChildren($each->children, $out);
|
3186 |
|
3187 |
if ($ret) {
|
3188 |
+
$store = $this->env->store;
|
3189 |
+
$this->popEnv();
|
3190 |
+
$this->backPropagateEnv($store, $each->vars);
|
|
|
|
|
|
|
|
|
3191 |
|
3192 |
+
return $ret;
|
|
|
|
|
3193 |
}
|
3194 |
}
|
3195 |
$store = $this->env->store;
|
3200 |
|
3201 |
case Type::T_WHILE:
|
3202 |
list(, $while) = $child;
|
3203 |
+
assert($while instanceof WhileBlock);
|
3204 |
|
3205 |
while ($this->isTruthy($this->reduce($while->cond, true))) {
|
3206 |
$ret = $this->compileChildren($while->children, $out);
|
3207 |
|
3208 |
if ($ret) {
|
3209 |
+
return $ret;
|
|
|
|
|
|
|
|
|
|
|
|
|
3210 |
}
|
3211 |
}
|
3212 |
break;
|
3213 |
|
3214 |
case Type::T_FOR:
|
3215 |
list(, $for) = $child;
|
3216 |
+
assert($for instanceof ForBlock);
|
3217 |
|
3218 |
+
$startNumber = $this->assertNumber($this->reduce($for->start, true));
|
3219 |
+
$endNumber = $this->assertNumber($this->reduce($for->end, true));
|
3220 |
|
3221 |
+
$start = $this->assertInteger($startNumber);
|
|
|
3222 |
|
3223 |
+
$numeratorUnits = $startNumber->getNumeratorUnits();
|
3224 |
+
$denominatorUnits = $startNumber->getDenominatorUnits();
|
3225 |
|
3226 |
+
$end = $this->assertInteger($endNumber->coerce($numeratorUnits, $denominatorUnits));
|
|
|
|
|
3227 |
|
3228 |
$d = $start < $end ? 1 : -1;
|
3229 |
|
3230 |
$this->pushEnv();
|
3231 |
|
3232 |
for (;;) {
|
3233 |
+
if (
|
3234 |
+
(! $for->until && $start - $d == $end) ||
|
3235 |
($for->until && $start == $end)
|
3236 |
) {
|
3237 |
break;
|
3238 |
}
|
3239 |
|
3240 |
+
$this->set($for->var, new Number($start, $numeratorUnits, $denominatorUnits));
|
3241 |
$start += $d;
|
3242 |
|
3243 |
$ret = $this->compileChildren($for->children, $out);
|
3244 |
|
3245 |
if ($ret) {
|
3246 |
+
$store = $this->env->store;
|
3247 |
+
$this->popEnv();
|
3248 |
+
$this->backPropagateEnv($store, [$for->var]);
|
|
|
|
|
|
|
3249 |
|
3250 |
+
return $ret;
|
|
|
|
|
3251 |
}
|
3252 |
}
|
3253 |
|
3257 |
|
3258 |
break;
|
3259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
3260 |
case Type::T_RETURN:
|
3261 |
return $this->reduce($child[1], true);
|
3262 |
|
3271 |
$mixin = $this->get(static::$namespaces['mixin'] . $name, false);
|
3272 |
|
3273 |
if (! $mixin) {
|
3274 |
+
throw $this->error("Undefined mixin $name");
|
|
|
3275 |
}
|
3276 |
|
3277 |
+
assert($mixin instanceof CallableBlock);
|
3278 |
+
|
3279 |
$callingScope = $this->getStoreEnv();
|
3280 |
|
3281 |
// push scope, apply args
|
3286 |
// and assign this fake parent to childs
|
3287 |
$selfParent = null;
|
3288 |
|
3289 |
+
if (isset($child['selfParent']) && $child['selfParent'] instanceof Block && isset($child['selfParent']->selectors)) {
|
3290 |
$selfParent = $child['selfParent'];
|
3291 |
} else {
|
3292 |
$parentSelectors = $this->multiplySelectors($this->env);
|
3296 |
$parent->selectors = $parentSelectors;
|
3297 |
|
3298 |
foreach ($mixin->children as $k => $child) {
|
3299 |
+
if (isset($child[1]) && $child[1] instanceof Block) {
|
3300 |
$mixin->children[$k][1]->parent = $parent;
|
3301 |
}
|
3302 |
}
|
3330 |
if (! empty($mixin->parentEnv)) {
|
3331 |
$this->env->declarationScopeParent = $mixin->parentEnv;
|
3332 |
} else {
|
3333 |
+
throw $this->error("@mixin $name() without parentEnv");
|
3334 |
}
|
3335 |
|
3336 |
+
$this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker . ' ' . $name);
|
3337 |
|
3338 |
$this->popEnv();
|
3339 |
break;
|
3373 |
case Type::T_DEBUG:
|
3374 |
list(, $value) = $child;
|
3375 |
|
3376 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
3377 |
$line = $this->sourceLine;
|
3378 |
+
$value = $this->compileDebugValue($value);
|
3379 |
|
3380 |
+
$this->logger->debug("$fname:$line DEBUG: $value");
|
3381 |
break;
|
3382 |
|
3383 |
case Type::T_WARN:
|
3384 |
list(, $value) = $child;
|
3385 |
|
3386 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
3387 |
$line = $this->sourceLine;
|
3388 |
+
$value = $this->compileDebugValue($value);
|
3389 |
|
3390 |
+
$this->logger->warn("$value\n on line $line of $fname");
|
3391 |
break;
|
3392 |
|
3393 |
case Type::T_ERROR:
|
3394 |
list(, $value) = $child;
|
3395 |
|
3396 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
3397 |
$line = $this->sourceLine;
|
3398 |
$value = $this->compileValue($this->reduce($value, true));
|
3399 |
|
3400 |
+
throw $this->error("File $fname on line $line ERROR: $value\n");
|
|
|
|
|
|
|
|
|
|
|
3401 |
|
3402 |
default:
|
3403 |
+
throw $this->error("unknown child type: $child[0]");
|
3404 |
}
|
3405 |
+
|
3406 |
+
return null;
|
3407 |
}
|
3408 |
|
3409 |
/**
|
3410 |
* Reduce expression to string
|
3411 |
*
|
3412 |
* @param array $exp
|
3413 |
+
* @param bool $keepParens
|
3414 |
*
|
3415 |
* @return array
|
3416 |
*/
|
3417 |
+
protected function expToString($exp, $keepParens = false)
|
3418 |
{
|
3419 |
+
list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp;
|
3420 |
+
|
3421 |
+
$content = [];
|
3422 |
|
3423 |
+
if ($keepParens && $inParens) {
|
3424 |
+
$content[] = '(';
|
3425 |
+
}
|
3426 |
+
|
3427 |
+
$content[] = $this->reduce($left);
|
3428 |
|
3429 |
if ($whiteLeft) {
|
3430 |
$content[] = ' ';
|
3438 |
|
3439 |
$content[] = $this->reduce($right);
|
3440 |
|
3441 |
+
if ($keepParens && $inParens) {
|
3442 |
+
$content[] = ')';
|
3443 |
+
}
|
3444 |
+
|
3445 |
return [Type::T_STRING, '', $content];
|
3446 |
}
|
3447 |
|
3448 |
/**
|
3449 |
* Is truthy?
|
3450 |
*
|
3451 |
+
* @param array|Number $value
|
3452 |
*
|
3453 |
+
* @return bool
|
3454 |
*/
|
3455 |
+
public function isTruthy($value)
|
3456 |
{
|
3457 |
return $value !== static::$false && $value !== static::$null;
|
3458 |
}
|
3462 |
*
|
3463 |
* @param string $value
|
3464 |
*
|
3465 |
+
* @return bool
|
3466 |
*/
|
3467 |
protected function isImmediateRelationshipCombinator($value)
|
3468 |
{
|
3474 |
*
|
3475 |
* @param array $value
|
3476 |
*
|
3477 |
+
* @return bool
|
3478 |
*/
|
3479 |
protected function shouldEval($value)
|
3480 |
{
|
3496 |
/**
|
3497 |
* Reduce value
|
3498 |
*
|
3499 |
+
* @param array|Number $value
|
3500 |
+
* @param bool $inExp
|
3501 |
*
|
3502 |
+
* @return array|Number
|
3503 |
*/
|
3504 |
protected function reduce($value, $inExp = false)
|
3505 |
{
|
3506 |
+
if ($value instanceof Number) {
|
3507 |
+
return $value;
|
3508 |
}
|
3509 |
|
3510 |
switch ($value[0]) {
|
3521 |
}
|
3522 |
|
3523 |
// special case: looks like css shorthand
|
3524 |
+
if (
|
3525 |
+
$opName == 'div' && ! $inParens && ! $inExp &&
|
3526 |
+
(($right[0] !== Type::T_NUMBER && isset($right[2]) && $right[2] != '') ||
|
3527 |
($right[0] === Type::T_NUMBER && ! $right->unitless()))
|
3528 |
) {
|
3529 |
return $this->expToString($value);
|
3538 |
$ucLType = ucfirst($ltype);
|
3539 |
$ucRType = ucfirst($rtype);
|
3540 |
|
3541 |
+
$shouldEval = $inParens || $inExp;
|
3542 |
+
|
3543 |
// this tries:
|
3544 |
// 1. op[op name][left type][right type]
|
3545 |
+
// 2. op[left type][right type] (passing the op as first arg)
|
3546 |
// 3. op[op name]
|
3547 |
+
if (\is_callable([$this, $fn = "op${ucOpName}${ucLType}${ucRType}"])) {
|
3548 |
+
$out = $this->$fn($left, $right, $shouldEval);
|
3549 |
+
} elseif (\is_callable([$this, $fn = "op${ucLType}${ucRType}"])) {
|
3550 |
+
$out = $this->$fn($op, $left, $right, $shouldEval);
|
3551 |
+
} elseif (\is_callable([$this, $fn = "op${ucOpName}"])) {
|
3552 |
+
$out = $this->$fn($left, $right, $shouldEval);
|
3553 |
+
} else {
|
3554 |
+
$out = null;
|
3555 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3556 |
|
3557 |
+
if (isset($out)) {
|
3558 |
+
return $out;
|
3559 |
}
|
3560 |
|
3561 |
return $this->expToString($value);
|
3566 |
$inExp = $inExp || $this->shouldEval($exp);
|
3567 |
$exp = $this->reduce($exp);
|
3568 |
|
3569 |
+
if ($exp instanceof Number) {
|
3570 |
switch ($op) {
|
3571 |
case '+':
|
3572 |
+
return $exp;
|
3573 |
|
3574 |
case '-':
|
3575 |
+
return $exp->unaryMinus();
|
3576 |
}
|
3577 |
}
|
3578 |
|
3597 |
foreach ($value[2] as &$item) {
|
3598 |
$item = $this->reduce($item);
|
3599 |
}
|
3600 |
+
unset($item);
|
3601 |
+
|
3602 |
+
if (isset($value[3]) && \is_array($value[3])) {
|
3603 |
+
foreach ($value[3] as &$item) {
|
3604 |
+
$item = $this->reduce($item);
|
3605 |
+
}
|
3606 |
+
unset($item);
|
3607 |
+
}
|
3608 |
|
3609 |
return $value;
|
3610 |
|
3621 |
|
3622 |
case Type::T_STRING:
|
3623 |
foreach ($value[2] as &$item) {
|
3624 |
+
if (\is_array($item) || $item instanceof Number) {
|
3625 |
$item = $this->reduce($item);
|
3626 |
}
|
3627 |
}
|
3632 |
$value[1] = $this->reduce($value[1]);
|
3633 |
|
3634 |
if ($inExp) {
|
3635 |
+
return [Type::T_KEYWORD, $this->compileValue($value, false)];
|
3636 |
}
|
3637 |
|
3638 |
return $value;
|
3641 |
return $this->fncall($value[1], $value[2]);
|
3642 |
|
3643 |
case Type::T_SELF:
|
3644 |
+
$selfParent = ! empty($this->env->block->selfParent) ? $this->env->block->selfParent : null;
|
3645 |
+
$selfSelector = $this->multiplySelectors($this->env, $selfParent);
|
3646 |
+
$selfSelector = $this->collapseSelectorsAsList($selfSelector);
|
3647 |
|
3648 |
return $selfSelector;
|
3649 |
|
3655 |
/**
|
3656 |
* Function caller
|
3657 |
*
|
3658 |
+
* @param string|array $functionReference
|
3659 |
+
* @param array $argValues
|
3660 |
*
|
3661 |
+
* @return array|Number
|
3662 |
*/
|
3663 |
+
protected function fncall($functionReference, $argValues)
|
3664 |
{
|
3665 |
+
// a string means this is a static hard reference coming from the parsing
|
3666 |
+
if (is_string($functionReference)) {
|
3667 |
+
$name = $functionReference;
|
|
|
3668 |
|
3669 |
+
$functionReference = $this->getFunctionReference($name);
|
3670 |
+
if ($functionReference === static::$null || $functionReference[0] !== Type::T_FUNCTION_REFERENCE) {
|
3671 |
+
$functionReference = [Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]];
|
3672 |
+
}
|
3673 |
}
|
3674 |
|
3675 |
+
// a function type means we just want a plain css function call
|
3676 |
+
if ($functionReference[0] === Type::T_FUNCTION) {
|
3677 |
+
// for CSS functions, simply flatten the arguments into a list
|
3678 |
+
$listArgs = [];
|
3679 |
|
3680 |
+
foreach ((array) $argValues as $arg) {
|
3681 |
+
if (empty($arg[0]) || count($argValues) === 1) {
|
3682 |
+
$listArgs[] = $this->reduce($this->stringifyFncallArgs($arg[1]));
|
3683 |
+
}
|
3684 |
}
|
|
|
3685 |
|
3686 |
+
return [Type::T_FUNCTION, $functionReference[1], [Type::T_LIST, ',', $listArgs]];
|
3687 |
+
}
|
3688 |
|
3689 |
+
if ($functionReference === static::$null || $functionReference[0] !== Type::T_FUNCTION_REFERENCE) {
|
3690 |
+
return static::$defaultValue;
|
3691 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3693 |
|
3694 |
+
switch ($functionReference[1]) {
|
3695 |
+
// SCSS @function
|
3696 |
+
case 'scss':
|
3697 |
+
return $this->callScssFunction($functionReference[3], $argValues);
|
3698 |
|
3699 |
+
// native PHP functions
|
3700 |
+
case 'user':
|
3701 |
+
case 'native':
|
3702 |
+
list(,,$name, $fn, $prototype) = $functionReference;
|
3703 |
|
3704 |
+
// special cases of css valid functions min/max
|
3705 |
+
$name = strtolower($name);
|
3706 |
+
if (\in_array($name, ['min', 'max']) && count($argValues) >= 1) {
|
3707 |
+
$cssFunction = $this->cssValidArg(
|
3708 |
+
[Type::T_FUNCTION_CALL, $name, $argValues],
|
3709 |
+
['min', 'max', 'calc', 'env', 'var']
|
3710 |
+
);
|
3711 |
+
if ($cssFunction !== false) {
|
3712 |
+
return $cssFunction;
|
3713 |
+
}
|
3714 |
}
|
3715 |
+
$returnValue = $this->callNativeFunction($name, $fn, $prototype, $argValues);
|
3716 |
|
3717 |
+
if (! isset($returnValue)) {
|
3718 |
+
return $this->fncall([Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]], $argValues);
|
3719 |
}
|
3720 |
|
3721 |
+
return $returnValue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3722 |
|
3723 |
default:
|
3724 |
+
return static::$defaultValue;
|
3725 |
}
|
3726 |
}
|
3727 |
|
3728 |
/**
|
3729 |
+
* @param array|Number $arg
|
3730 |
+
* @param string[] $allowed_function
|
3731 |
+
* @param bool $inFunction
|
|
|
3732 |
*
|
3733 |
+
* @return array|Number|false
|
3734 |
*/
|
3735 |
+
protected function cssValidArg($arg, $allowed_function = [], $inFunction = false)
|
3736 |
{
|
3737 |
+
if ($arg instanceof Number) {
|
3738 |
+
return $this->stringifyFncallArgs($arg);
|
3739 |
+
}
|
3740 |
|
3741 |
+
switch ($arg[0]) {
|
3742 |
+
case Type::T_INTERPOLATE:
|
3743 |
+
return [Type::T_KEYWORD, $this->CompileValue($arg)];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3744 |
|
3745 |
+
case Type::T_FUNCTION:
|
3746 |
+
if (! \in_array($arg[1], $allowed_function)) {
|
3747 |
+
return false;
|
3748 |
+
}
|
3749 |
+
if ($arg[2][0] === Type::T_LIST) {
|
3750 |
+
foreach ($arg[2][2] as $k => $subarg) {
|
3751 |
+
$arg[2][2][$k] = $this->cssValidArg($subarg, $allowed_function, $arg[1]);
|
3752 |
+
if ($arg[2][2][$k] === false) {
|
3753 |
+
return false;
|
3754 |
+
}
|
3755 |
+
}
|
3756 |
+
}
|
3757 |
+
return $arg;
|
3758 |
+
|
3759 |
+
case Type::T_FUNCTION_CALL:
|
3760 |
+
if (! \in_array($arg[1], $allowed_function)) {
|
3761 |
+
return false;
|
3762 |
+
}
|
3763 |
+
$cssArgs = [];
|
3764 |
+
foreach ($arg[2] as $argValue) {
|
3765 |
+
if ($argValue === static::$null) {
|
3766 |
+
return false;
|
3767 |
+
}
|
3768 |
+
$cssArg = $this->cssValidArg($argValue[1], $allowed_function, $arg[1]);
|
3769 |
+
if (empty($argValue[0]) && $cssArg !== false) {
|
3770 |
+
$cssArgs[] = [$argValue[0], $cssArg];
|
3771 |
+
} else {
|
3772 |
+
return false;
|
3773 |
+
}
|
3774 |
+
}
|
3775 |
+
|
3776 |
+
return $this->fncall([Type::T_FUNCTION, $arg[1], [Type::T_LIST, ',', []]], $cssArgs);
|
3777 |
+
|
3778 |
+
case Type::T_STRING:
|
3779 |
+
case Type::T_KEYWORD:
|
3780 |
+
if (!$inFunction or !\in_array($inFunction, ['calc', 'env', 'var'])) {
|
3781 |
+
return false;
|
3782 |
+
}
|
3783 |
+
return $this->stringifyFncallArgs($arg);
|
3784 |
+
|
3785 |
+
case Type::T_LIST:
|
3786 |
+
if (!$inFunction) {
|
3787 |
+
return false;
|
3788 |
+
}
|
3789 |
+
if (empty($arg['enclosing']) and $arg[1] === '') {
|
3790 |
+
foreach ($arg[2] as $k => $subarg) {
|
3791 |
+
$arg[2][$k] = $this->cssValidArg($subarg, $allowed_function, $inFunction);
|
3792 |
+
if ($arg[2][$k] === false) {
|
3793 |
+
return false;
|
3794 |
+
}
|
3795 |
+
}
|
3796 |
+
$arg[0] = Type::T_STRING;
|
3797 |
+
return $arg;
|
3798 |
+
}
|
3799 |
+
return false;
|
3800 |
+
|
3801 |
+
case Type::T_EXPRESSION:
|
3802 |
+
if (! \in_array($arg[1], ['+', '-', '/', '*'])) {
|
3803 |
+
return false;
|
3804 |
+
}
|
3805 |
+
$arg[2] = $this->cssValidArg($arg[2], $allowed_function, $inFunction);
|
3806 |
+
$arg[3] = $this->cssValidArg($arg[3], $allowed_function, $inFunction);
|
3807 |
+
if ($arg[2] === false || $arg[3] === false) {
|
3808 |
+
return false;
|
3809 |
+
}
|
3810 |
+
return $this->expToString($arg, true);
|
3811 |
+
|
3812 |
+
case Type::T_VARIABLE:
|
3813 |
+
case Type::T_SELF:
|
3814 |
+
default:
|
3815 |
+
return false;
|
3816 |
+
}
|
3817 |
+
}
|
3818 |
+
|
3819 |
+
|
3820 |
+
/**
|
3821 |
+
* Reformat fncall arguments to proper css function output
|
3822 |
+
*
|
3823 |
+
* @param array|Number $arg
|
3824 |
+
*
|
3825 |
+
* @return array|Number
|
3826 |
+
*/
|
3827 |
+
protected function stringifyFncallArgs($arg)
|
3828 |
+
{
|
3829 |
+
if ($arg instanceof Number) {
|
3830 |
+
return $arg;
|
3831 |
+
}
|
3832 |
+
|
3833 |
+
switch ($arg[0]) {
|
3834 |
+
case Type::T_LIST:
|
3835 |
+
foreach ($arg[2] as $k => $v) {
|
3836 |
+
$arg[2][$k] = $this->stringifyFncallArgs($v);
|
3837 |
+
}
|
3838 |
+
break;
|
3839 |
+
|
3840 |
+
case Type::T_EXPRESSION:
|
3841 |
+
if ($arg[1] === '/') {
|
3842 |
+
$arg[2] = $this->stringifyFncallArgs($arg[2]);
|
3843 |
+
$arg[3] = $this->stringifyFncallArgs($arg[3]);
|
3844 |
+
$arg[5] = $arg[6] = false; // no space around /
|
3845 |
+
$arg = $this->expToString($arg);
|
3846 |
+
}
|
3847 |
+
break;
|
3848 |
+
|
3849 |
+
case Type::T_FUNCTION_CALL:
|
3850 |
+
$name = strtolower($arg[1]);
|
3851 |
+
|
3852 |
+
if (in_array($name, ['max', 'min', 'calc'])) {
|
3853 |
+
$args = $arg[2];
|
3854 |
+
$arg = $this->fncall([Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]], $args);
|
3855 |
+
}
|
3856 |
+
break;
|
3857 |
+
}
|
3858 |
+
|
3859 |
+
return $arg;
|
3860 |
+
}
|
3861 |
+
|
3862 |
+
/**
|
3863 |
+
* Find a function reference
|
3864 |
+
* @param string $name
|
3865 |
+
* @param bool $safeCopy
|
3866 |
+
* @return array
|
3867 |
+
*/
|
3868 |
+
protected function getFunctionReference($name, $safeCopy = false)
|
3869 |
+
{
|
3870 |
+
// SCSS @function
|
3871 |
+
if ($func = $this->get(static::$namespaces['function'] . $name, false)) {
|
3872 |
+
if ($safeCopy) {
|
3873 |
+
$func = clone $func;
|
3874 |
+
}
|
3875 |
+
|
3876 |
+
return [Type::T_FUNCTION_REFERENCE, 'scss', $name, $func];
|
3877 |
+
}
|
3878 |
+
|
3879 |
+
// native PHP functions
|
3880 |
+
|
3881 |
+
// try to find a native lib function
|
3882 |
+
$normalizedName = $this->normalizeName($name);
|
3883 |
+
|
3884 |
+
if (isset($this->userFunctions[$normalizedName])) {
|
3885 |
+
// see if we can find a user function
|
3886 |
+
list($f, $prototype) = $this->userFunctions[$normalizedName];
|
3887 |
+
|
3888 |
+
return [Type::T_FUNCTION_REFERENCE, 'user', $name, $f, $prototype];
|
3889 |
+
}
|
3890 |
+
|
3891 |
+
$lowercasedName = strtolower($normalizedName);
|
3892 |
+
|
3893 |
+
// Special functions overriding a CSS function are case-insensitive. We normalize them as lowercase
|
3894 |
+
// to avoid the deprecation warning about the wrong case being used.
|
3895 |
+
if ($lowercasedName === 'min' || $lowercasedName === 'max') {
|
3896 |
+
$normalizedName = $lowercasedName;
|
3897 |
+
}
|
3898 |
+
|
3899 |
+
if (($f = $this->getBuiltinFunction($normalizedName)) && \is_callable($f)) {
|
3900 |
+
/** @var string $libName */
|
3901 |
+
$libName = $f[1];
|
3902 |
+
$prototype = isset(static::$$libName) ? static::$$libName : null;
|
3903 |
+
|
3904 |
+
// All core functions have a prototype defined. Not finding the
|
3905 |
+
// prototype can mean 2 things:
|
3906 |
+
// - the function comes from a child class (deprecated just after)
|
3907 |
+
// - the function was found with a different case, which relates to calling the
|
3908 |
+
// wrong Sass function due to our camelCase usage (`fade-in()` vs `fadein()`),
|
3909 |
+
// because PHP method names are case-insensitive while property names are
|
3910 |
+
// case-sensitive.
|
3911 |
+
if ($prototype === null || strtolower($normalizedName) !== $normalizedName) {
|
3912 |
+
$r = new \ReflectionMethod($this, $libName);
|
3913 |
+
$actualLibName = $r->name;
|
3914 |
+
|
3915 |
+
if ($actualLibName !== $libName || strtolower($normalizedName) !== $normalizedName) {
|
3916 |
+
$kebabCaseName = preg_replace('~(?<=\\w)([A-Z])~', '-$1', substr($actualLibName, 3));
|
3917 |
+
assert($kebabCaseName !== null);
|
3918 |
+
$originalName = strtolower($kebabCaseName);
|
3919 |
+
$warning = "Calling built-in functions with a non-standard name is deprecated since Scssphp 1.8.0 and will not work anymore in 2.0 (they will be treated as CSS function calls instead).\nUse \"$originalName\" instead of \"$name\".";
|
3920 |
+
@trigger_error($warning, E_USER_DEPRECATED);
|
3921 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
3922 |
+
$line = $this->sourceLine;
|
3923 |
+
Warn::deprecation("$warning\n on line $line of $fname");
|
3924 |
+
|
3925 |
+
// Use the actual function definition
|
3926 |
+
$prototype = isset(static::$$actualLibName) ? static::$$actualLibName : null;
|
3927 |
+
$f[1] = $libName = $actualLibName;
|
3928 |
+
}
|
3929 |
+
}
|
3930 |
+
|
3931 |
+
if (\get_class($this) !== __CLASS__ && !isset($this->warnedChildFunctions[$libName])) {
|
3932 |
+
$r = new \ReflectionMethod($this, $libName);
|
3933 |
+
$declaringClass = $r->getDeclaringClass()->name;
|
3934 |
+
|
3935 |
+
$needsWarning = $this->warnedChildFunctions[$libName] = $declaringClass !== __CLASS__;
|
3936 |
+
|
3937 |
+
if ($needsWarning) {
|
3938 |
+
if (method_exists(__CLASS__, $libName)) {
|
3939 |
+
@trigger_error(sprintf('Overriding the "%s" core function by extending the Compiler is deprecated and will be unsupported in 2.0. Remove the "%s::%s" method.', $normalizedName, $declaringClass, $libName), E_USER_DEPRECATED);
|
3940 |
+
} else {
|
3941 |
+
@trigger_error(sprintf('Registering custom functions by extending the Compiler and using the lib* discovery mechanism is deprecated and will be removed in 2.0. Replace the "%s::%s" method with registering the "%s" function through "Compiler::registerFunction".', $declaringClass, $libName, $normalizedName), E_USER_DEPRECATED);
|
3942 |
+
}
|
3943 |
+
}
|
3944 |
+
}
|
3945 |
+
|
3946 |
+
return [Type::T_FUNCTION_REFERENCE, 'native', $name, $f, $prototype];
|
3947 |
+
}
|
3948 |
+
|
3949 |
+
return static::$null;
|
3950 |
+
}
|
3951 |
+
|
3952 |
+
|
3953 |
+
/**
|
3954 |
+
* Normalize name
|
3955 |
+
*
|
3956 |
+
* @param string $name
|
3957 |
*
|
3958 |
+
* @return string
|
3959 |
*/
|
3960 |
+
protected function normalizeName($name)
|
3961 |
{
|
3962 |
+
return str_replace('-', '_', $name);
|
3963 |
}
|
3964 |
|
3965 |
/**
|
3966 |
+
* Normalize value
|
3967 |
*
|
3968 |
+
* @internal
|
3969 |
+
*
|
3970 |
+
* @param array|Number $value
|
3971 |
*
|
3972 |
+
* @return array|Number
|
3973 |
*/
|
3974 |
+
public function normalizeValue($value)
|
3975 |
{
|
3976 |
+
$value = $this->coerceForExpression($this->reduce($value));
|
3977 |
+
|
3978 |
+
if ($value instanceof Number) {
|
3979 |
+
return $value;
|
3980 |
+
}
|
3981 |
+
|
3982 |
+
switch ($value[0]) {
|
3983 |
+
case Type::T_LIST:
|
3984 |
+
$value = $this->extractInterpolation($value);
|
3985 |
+
|
3986 |
+
if ($value[0] !== Type::T_LIST) {
|
3987 |
+
return [Type::T_KEYWORD, $this->compileValue($value)];
|
3988 |
+
}
|
3989 |
+
|
3990 |
+
foreach ($value[2] as $key => $item) {
|
3991 |
+
$value[2][$key] = $this->normalizeValue($item);
|
3992 |
+
}
|
3993 |
+
|
3994 |
+
if (! empty($value['enclosing'])) {
|
3995 |
+
unset($value['enclosing']);
|
3996 |
+
}
|
3997 |
+
|
3998 |
+
if ($value[1] === '' && count($value[2]) > 1) {
|
3999 |
+
$value[1] = ' ';
|
4000 |
+
}
|
4001 |
+
|
4002 |
+
return $value;
|
4003 |
+
|
4004 |
+
case Type::T_STRING:
|
4005 |
+
return [$value[0], '"', [$this->compileStringContent($value)]];
|
4006 |
+
|
4007 |
+
case Type::T_INTERPOLATE:
|
4008 |
+
return [Type::T_KEYWORD, $this->compileValue($value)];
|
4009 |
+
|
4010 |
+
default:
|
4011 |
+
return $value;
|
4012 |
}
|
4013 |
+
}
|
4014 |
|
4015 |
+
/**
|
4016 |
+
* Add numbers
|
4017 |
+
*
|
4018 |
+
* @param Number $left
|
4019 |
+
* @param Number $right
|
4020 |
+
*
|
4021 |
+
* @return Number
|
4022 |
+
*/
|
4023 |
+
protected function opAddNumberNumber(Number $left, Number $right)
|
4024 |
+
{
|
4025 |
+
return $left->plus($right);
|
4026 |
}
|
4027 |
|
4028 |
/**
|
4029 |
+
* Multiply numbers
|
4030 |
*
|
4031 |
+
* @param Number $left
|
4032 |
+
* @param Number $right
|
4033 |
*
|
4034 |
+
* @return Number
|
4035 |
*/
|
4036 |
+
protected function opMulNumberNumber(Number $left, Number $right)
|
4037 |
{
|
4038 |
+
return $left->times($right);
|
4039 |
+
}
|
4040 |
+
|
4041 |
+
/**
|
4042 |
+
* Subtract numbers
|
4043 |
+
*
|
4044 |
+
* @param Number $left
|
4045 |
+
* @param Number $right
|
4046 |
+
*
|
4047 |
+
* @return Number
|
4048 |
+
*/
|
4049 |
+
protected function opSubNumberNumber(Number $left, Number $right)
|
4050 |
+
{
|
4051 |
+
return $left->minus($right);
|
4052 |
+
}
|
4053 |
+
|
4054 |
+
/**
|
4055 |
+
* Divide numbers
|
4056 |
+
*
|
4057 |
+
* @param Number $left
|
4058 |
+
* @param Number $right
|
4059 |
+
*
|
4060 |
+
* @return Number
|
4061 |
+
*/
|
4062 |
+
protected function opDivNumberNumber(Number $left, Number $right)
|
4063 |
+
{
|
4064 |
+
return $left->dividedBy($right);
|
4065 |
+
}
|
4066 |
|
4067 |
+
/**
|
4068 |
+
* Mod numbers
|
4069 |
+
*
|
4070 |
+
* @param Number $left
|
4071 |
+
* @param Number $right
|
4072 |
+
*
|
4073 |
+
* @return Number
|
4074 |
+
*/
|
4075 |
+
protected function opModNumberNumber(Number $left, Number $right)
|
4076 |
+
{
|
4077 |
+
return $left->modulo($right);
|
4078 |
}
|
4079 |
|
4080 |
/**
|
4113 |
/**
|
4114 |
* Boolean and
|
4115 |
*
|
4116 |
+
* @param array|Number $left
|
4117 |
+
* @param array|Number $right
|
4118 |
+
* @param bool $shouldEval
|
4119 |
*
|
4120 |
+
* @return array|Number|null
|
4121 |
*/
|
4122 |
protected function opAnd($left, $right, $shouldEval)
|
4123 |
{
|
4141 |
/**
|
4142 |
* Boolean or
|
4143 |
*
|
4144 |
+
* @param array|Number $left
|
4145 |
+
* @param array|Number $right
|
4146 |
+
* @param bool $shouldEval
|
4147 |
*
|
4148 |
+
* @return array|Number|null
|
4149 |
*/
|
4150 |
protected function opOr($left, $right, $shouldEval)
|
4151 |
{
|
4177 |
*/
|
4178 |
protected function opColorColor($op, $left, $right)
|
4179 |
{
|
4180 |
+
if ($op !== '==' && $op !== '!=') {
|
4181 |
+
$warning = "Color arithmetic is deprecated and will be an error in future versions.\n"
|
4182 |
+
. "Consider using Sass's color functions instead.";
|
4183 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
4184 |
+
$line = $this->sourceLine;
|
4185 |
+
|
4186 |
+
Warn::deprecation("$warning\n on line $line of $fname");
|
4187 |
+
}
|
4188 |
+
|
4189 |
$out = [Type::T_COLOR];
|
4190 |
|
4191 |
foreach ([1, 2, 3] as $i) {
|
4206 |
break;
|
4207 |
|
4208 |
case '%':
|
4209 |
+
if ($rval == 0) {
|
4210 |
+
throw $this->error("color: Can't take modulo by zero");
|
4211 |
+
}
|
4212 |
+
|
4213 |
$out[] = $lval % $rval;
|
4214 |
break;
|
4215 |
|
4216 |
case '/':
|
4217 |
if ($rval == 0) {
|
4218 |
+
throw $this->error("color: Can't divide by zero");
|
|
|
4219 |
}
|
4220 |
|
4221 |
$out[] = (int) ($lval / $rval);
|
4228 |
return $this->opNeq($left, $right);
|
4229 |
|
4230 |
default:
|
4231 |
+
throw $this->error("color: unknown op $op");
|
|
|
4232 |
}
|
4233 |
}
|
4234 |
|
4246 |
*
|
4247 |
* @param string $op
|
4248 |
* @param array $left
|
4249 |
+
* @param Number $right
|
4250 |
*
|
4251 |
* @return array
|
4252 |
*/
|
4253 |
+
protected function opColorNumber($op, $left, Number $right)
|
4254 |
{
|
4255 |
+
if ($op === '==') {
|
4256 |
+
return static::$false;
|
4257 |
+
}
|
4258 |
+
|
4259 |
+
if ($op === '!=') {
|
4260 |
+
return static::$true;
|
4261 |
+
}
|
4262 |
+
|
4263 |
+
$value = $right->getDimension();
|
4264 |
|
4265 |
return $this->opColorColor(
|
4266 |
$op,
|
4273 |
* Compare number and color
|
4274 |
*
|
4275 |
* @param string $op
|
4276 |
+
* @param Number $left
|
4277 |
* @param array $right
|
4278 |
*
|
4279 |
* @return array
|
4280 |
*/
|
4281 |
+
protected function opNumberColor($op, Number $left, $right)
|
4282 |
{
|
4283 |
+
if ($op === '==') {
|
4284 |
+
return static::$false;
|
4285 |
+
}
|
4286 |
+
|
4287 |
+
if ($op === '!=') {
|
4288 |
+
return static::$true;
|
4289 |
+
}
|
4290 |
+
|
4291 |
+
$value = $left->getDimension();
|
4292 |
|
4293 |
return $this->opColorColor(
|
4294 |
$op,
|
4300 |
/**
|
4301 |
* Compare number1 == number2
|
4302 |
*
|
4303 |
+
* @param array|Number $left
|
4304 |
+
* @param array|Number $right
|
4305 |
*
|
4306 |
* @return array
|
4307 |
*/
|
4321 |
/**
|
4322 |
* Compare number1 != number2
|
4323 |
*
|
4324 |
+
* @param array|Number $left
|
4325 |
+
* @param array|Number $right
|
4326 |
*
|
4327 |
* @return array
|
4328 |
*/
|
4340 |
}
|
4341 |
|
4342 |
/**
|
4343 |
+
* Compare number1 == number2
|
4344 |
*
|
4345 |
+
* @param Number $left
|
4346 |
+
* @param Number $right
|
4347 |
*
|
4348 |
* @return array
|
4349 |
*/
|
4350 |
+
protected function opEqNumberNumber(Number $left, Number $right)
|
4351 |
{
|
4352 |
+
return $this->toBool($left->equals($right));
|
4353 |
}
|
4354 |
|
4355 |
/**
|
4356 |
+
* Compare number1 != number2
|
4357 |
*
|
4358 |
+
* @param Number $left
|
4359 |
+
* @param Number $right
|
4360 |
*
|
4361 |
* @return array
|
4362 |
*/
|
4363 |
+
protected function opNeqNumberNumber(Number $left, Number $right)
|
4364 |
{
|
4365 |
+
return $this->toBool(!$left->equals($right));
|
4366 |
}
|
4367 |
|
4368 |
/**
|
4369 |
+
* Compare number1 >= number2
|
4370 |
*
|
4371 |
+
* @param Number $left
|
4372 |
+
* @param Number $right
|
4373 |
*
|
4374 |
* @return array
|
4375 |
*/
|
4376 |
+
protected function opGteNumberNumber(Number $left, Number $right)
|
4377 |
{
|
4378 |
+
return $this->toBool($left->greaterThanOrEqual($right));
|
4379 |
}
|
4380 |
|
4381 |
/**
|
4382 |
+
* Compare number1 > number2
|
4383 |
*
|
4384 |
+
* @param Number $left
|
4385 |
+
* @param Number $right
|
4386 |
*
|
4387 |
* @return array
|
4388 |
*/
|
4389 |
+
protected function opGtNumberNumber(Number $left, Number $right)
|
4390 |
{
|
4391 |
+
return $this->toBool($left->greaterThan($right));
|
4392 |
}
|
4393 |
|
4394 |
/**
|
4395 |
+
* Compare number1 <= number2
|
4396 |
*
|
4397 |
+
* @param Number $left
|
4398 |
+
* @param Number $right
|
4399 |
*
|
4400 |
+
* @return array
|
4401 |
*/
|
4402 |
+
protected function opLteNumberNumber(Number $left, Number $right)
|
4403 |
{
|
4404 |
+
return $this->toBool($left->lessThanOrEqual($right));
|
4405 |
+
}
|
4406 |
|
4407 |
+
/**
|
4408 |
+
* Compare number1 < number2
|
4409 |
+
*
|
4410 |
+
* @param Number $left
|
4411 |
+
* @param Number $right
|
4412 |
+
*
|
4413 |
+
* @return array
|
4414 |
+
*/
|
4415 |
+
protected function opLtNumberNumber(Number $left, Number $right)
|
4416 |
+
{
|
4417 |
+
return $this->toBool($left->lessThan($right));
|
4418 |
}
|
4419 |
|
4420 |
/**
|
4422 |
*
|
4423 |
* @api
|
4424 |
*
|
4425 |
+
* @param bool $thing
|
4426 |
*
|
4427 |
* @return array
|
4428 |
*/
|
4431 |
return $thing ? static::$true : static::$false;
|
4432 |
}
|
4433 |
|
4434 |
+
/**
|
4435 |
+
* Escape non printable chars in strings output as in dart-sass
|
4436 |
+
*
|
4437 |
+
* @internal
|
4438 |
+
*
|
4439 |
+
* @param string $string
|
4440 |
+
* @param bool $inKeyword
|
4441 |
+
*
|
4442 |
+
* @return string
|
4443 |
+
*/
|
4444 |
+
public function escapeNonPrintableChars($string, $inKeyword = false)
|
4445 |
+
{
|
4446 |
+
static $replacement = [];
|
4447 |
+
if (empty($replacement[$inKeyword])) {
|
4448 |
+
for ($i = 0; $i < 32; $i++) {
|
4449 |
+
if ($i !== 9 || $inKeyword) {
|
4450 |
+
$replacement[$inKeyword][chr($i)] = '\\' . dechex($i) . ($inKeyword ? ' ' : chr(0));
|
4451 |
+
}
|
4452 |
+
}
|
4453 |
+
}
|
4454 |
+
$string = str_replace(array_keys($replacement[$inKeyword]), array_values($replacement[$inKeyword]), $string);
|
4455 |
+
// chr(0) is not a possible char from the input, so any chr(0) comes from our escaping replacement
|
4456 |
+
if (strpos($string, chr(0)) !== false) {
|
4457 |
+
if (substr($string, -1) === chr(0)) {
|
4458 |
+
$string = substr($string, 0, -1);
|
4459 |
+
}
|
4460 |
+
$string = str_replace(
|
4461 |
+
[chr(0) . '\\',chr(0) . ' '],
|
4462 |
+
[ '\\', ' '],
|
4463 |
+
$string
|
4464 |
+
);
|
4465 |
+
if (strpos($string, chr(0)) !== false) {
|
4466 |
+
$parts = explode(chr(0), $string);
|
4467 |
+
$string = array_shift($parts);
|
4468 |
+
while (count($parts)) {
|
4469 |
+
$next = array_shift($parts);
|
4470 |
+
if (strpos("0123456789abcdefABCDEF" . chr(9), $next[0]) !== false) {
|
4471 |
+
$string .= " ";
|
4472 |
+
}
|
4473 |
+
$string .= $next;
|
4474 |
+
}
|
4475 |
+
}
|
4476 |
+
}
|
4477 |
+
|
4478 |
+
return $string;
|
4479 |
+
}
|
4480 |
+
|
4481 |
/**
|
4482 |
* Compiles a primitive value into a CSS property value.
|
4483 |
*
|
4491 |
*
|
4492 |
* @api
|
4493 |
*
|
4494 |
+
* @param array|Number $value
|
4495 |
+
* @param bool $quote
|
4496 |
*
|
4497 |
+
* @return string
|
4498 |
*/
|
4499 |
+
public function compileValue($value, $quote = true)
|
4500 |
{
|
4501 |
$value = $this->reduce($value);
|
4502 |
|
4503 |
+
if ($value instanceof Number) {
|
4504 |
+
return $value->output($this);
|
4505 |
+
}
|
4506 |
+
|
4507 |
switch ($value[0]) {
|
4508 |
case Type::T_KEYWORD:
|
4509 |
+
return $this->escapeNonPrintableChars($value[1], true);
|
4510 |
|
4511 |
case Type::T_COLOR:
|
4512 |
// [1] - red component (either number for a %)
|
4530 |
}
|
4531 |
|
4532 |
if (is_numeric($alpha)) {
|
4533 |
+
$a = new Number($alpha, '');
|
4534 |
} else {
|
4535 |
$a = $alpha;
|
4536 |
}
|
4558 |
|
4559 |
return $h;
|
4560 |
|
|
|
|
|
|
|
4561 |
case Type::T_STRING:
|
4562 |
+
$content = $this->compileStringContent($value, $quote);
|
4563 |
+
|
4564 |
+
if ($value[1] && $quote) {
|
4565 |
+
$content = str_replace('\\', '\\\\', $content);
|
4566 |
+
|
4567 |
+
$content = $this->escapeNonPrintableChars($content);
|
4568 |
+
|
4569 |
+
// force double quote as string quote for the output in certain cases
|
4570 |
+
if (
|
4571 |
+
$value[1] === "'" &&
|
4572 |
+
(strpos($content, '"') === false or strpos($content, "'") !== false)
|
4573 |
+
) {
|
4574 |
+
$value[1] = '"';
|
4575 |
+
} elseif (
|
4576 |
+
$value[1] === '"' &&
|
4577 |
+
(strpos($content, '"') !== false and strpos($content, "'") === false)
|
4578 |
+
) {
|
4579 |
+
$value[1] = "'";
|
4580 |
+
}
|
4581 |
+
|
4582 |
+
$content = str_replace($value[1], '\\' . $value[1], $content);
|
4583 |
+
}
|
4584 |
+
|
4585 |
+
return $value[1] . $content . $value[1];
|
4586 |
|
4587 |
case Type::T_FUNCTION:
|
4588 |
+
$args = ! empty($value[2]) ? $this->compileValue($value[2], $quote) : '';
|
4589 |
|
4590 |
return "$value[1]($args)";
|
4591 |
|
4592 |
+
case Type::T_FUNCTION_REFERENCE:
|
4593 |
+
$name = ! empty($value[2]) ? $value[2] : '';
|
4594 |
+
|
4595 |
+
return "get-function(\"$name\")";
|
4596 |
+
|
4597 |
case Type::T_LIST:
|
4598 |
$value = $this->extractInterpolation($value);
|
4599 |
|
4600 |
if ($value[0] !== Type::T_LIST) {
|
4601 |
+
return $this->compileValue($value, $quote);
|
4602 |
}
|
4603 |
|
4604 |
list(, $delim, $items) = $value;
|
4605 |
+
$pre = $post = '';
|
4606 |
|
4607 |
if (! empty($value['enclosing'])) {
|
4608 |
switch ($value['enclosing']) {
|
4609 |
case 'parent':
|
4610 |
+
//$pre = '(';
|
4611 |
+
//$post = ')';
|
4612 |
break;
|
4613 |
case 'forced_parent':
|
4614 |
+
$pre = '(';
|
4615 |
+
$post = ')';
|
4616 |
break;
|
4617 |
case 'bracket':
|
4618 |
case 'forced_bracket':
|
4619 |
+
$pre = '[';
|
4620 |
+
$post = ']';
|
4621 |
break;
|
4622 |
}
|
4623 |
}
|
4624 |
|
4625 |
+
$separator = $delim === '/' ? ' /' : $delim;
|
4626 |
+
|
4627 |
$prefix_value = '';
|
4628 |
+
|
4629 |
if ($delim !== ' ') {
|
4630 |
$prefix_value = ' ';
|
4631 |
}
|
4632 |
|
4633 |
$filtered = [];
|
4634 |
|
4635 |
+
$same_string_quote = null;
|
4636 |
foreach ($items as $item) {
|
4637 |
+
if (\is_null($same_string_quote)) {
|
4638 |
+
$same_string_quote = false;
|
4639 |
+
if ($item[0] === Type::T_STRING) {
|
4640 |
+
$same_string_quote = $item[1];
|
4641 |
+
foreach ($items as $ii) {
|
4642 |
+
if ($ii[0] !== Type::T_STRING) {
|
4643 |
+
$same_string_quote = false;
|
4644 |
+
break;
|
4645 |
+
}
|
4646 |
+
}
|
4647 |
+
}
|
4648 |
+
}
|
4649 |
if ($item[0] === Type::T_NULL) {
|
4650 |
continue;
|
4651 |
}
|
4652 |
+
if ($same_string_quote === '"' && $item[0] === Type::T_STRING && $item[1]) {
|
4653 |
+
$item[1] = $same_string_quote;
|
4654 |
+
}
|
4655 |
+
|
4656 |
+
$compiled = $this->compileValue($item, $quote);
|
4657 |
|
|
|
4658 |
if ($prefix_value && \strlen($compiled)) {
|
4659 |
$compiled = $prefix_value . $compiled;
|
4660 |
}
|
4661 |
+
|
4662 |
$filtered[] = $compiled;
|
4663 |
}
|
4664 |
|
4665 |
+
return $pre . substr(implode($separator, $filtered), \strlen($prefix_value)) . $post;
|
4666 |
|
4667 |
case Type::T_MAP:
|
4668 |
$keys = $value[1];
|
4670 |
$filtered = [];
|
4671 |
|
4672 |
for ($i = 0, $s = \count($keys); $i < $s; $i++) {
|
4673 |
+
$filtered[$this->compileValue($keys[$i], $quote)] = $this->compileValue($values[$i], $quote);
|
4674 |
}
|
4675 |
|
4676 |
array_walk($filtered, function (&$value, $key) {
|
4690 |
$delim .= ' ';
|
4691 |
}
|
4692 |
|
4693 |
+
$left = \count($left[2]) > 0
|
4694 |
+
? $this->compileValue($left, $quote) . $delim . $whiteLeft
|
4695 |
+
: '';
|
4696 |
|
4697 |
$delim = $right[1];
|
4698 |
|
4701 |
}
|
4702 |
|
4703 |
$right = \count($right[2]) > 0 ?
|
4704 |
+
$whiteRight . $delim . $this->compileValue($right, $quote) : '';
|
4705 |
|
4706 |
+
return $left . $this->compileValue($interpolate, $quote) . $right;
|
4707 |
|
4708 |
case Type::T_INTERPOLATE:
|
4709 |
// strip quotes if it's a string
|
4710 |
$reduced = $this->reduce($value[1]);
|
4711 |
|
4712 |
+
if ($reduced instanceof Number) {
|
4713 |
+
return $this->compileValue($reduced, $quote);
|
4714 |
+
}
|
4715 |
+
|
4716 |
switch ($reduced[0]) {
|
4717 |
case Type::T_LIST:
|
4718 |
$reduced = $this->extractInterpolation($reduced);
|
4734 |
continue;
|
4735 |
}
|
4736 |
|
4737 |
+
if ($item[0] === Type::T_STRING) {
|
4738 |
+
$filtered[] = $this->compileStringContent($item, $quote);
|
4739 |
+
} elseif ($item[0] === Type::T_KEYWORD) {
|
4740 |
+
$filtered[] = $item[1];
|
|
|
|
|
4741 |
} else {
|
4742 |
+
$filtered[] = $this->compileValue($item, $quote);
|
4743 |
}
|
4744 |
}
|
4745 |
|
4747 |
break;
|
4748 |
|
4749 |
case Type::T_STRING:
|
4750 |
+
$reduced = [Type::T_STRING, '', [$this->compileStringContent($reduced)]];
|
4751 |
break;
|
4752 |
|
4753 |
case Type::T_NULL:
|
4754 |
$reduced = [Type::T_KEYWORD, ''];
|
4755 |
}
|
4756 |
|
4757 |
+
return $this->compileValue($reduced, $quote);
|
4758 |
|
4759 |
case Type::T_NULL:
|
4760 |
return 'null';
|
4763 |
return $this->compileCommentValue($value);
|
4764 |
|
4765 |
default:
|
4766 |
+
throw $this->error('unknown value type: ' . json_encode($value));
|
4767 |
+
}
|
4768 |
+
}
|
4769 |
+
|
4770 |
+
/**
|
4771 |
+
* @param array|Number $value
|
4772 |
+
*
|
4773 |
+
* @return string
|
4774 |
+
*/
|
4775 |
+
protected function compileDebugValue($value)
|
4776 |
+
{
|
4777 |
+
$value = $this->reduce($value, true);
|
4778 |
+
|
4779 |
+
if ($value instanceof Number) {
|
4780 |
+
return $this->compileValue($value);
|
4781 |
+
}
|
4782 |
+
|
4783 |
+
switch ($value[0]) {
|
4784 |
+
case Type::T_STRING:
|
4785 |
+
return $this->compileStringContent($value);
|
4786 |
+
|
4787 |
+
default:
|
4788 |
+
return $this->compileValue($value);
|
4789 |
}
|
4790 |
}
|
4791 |
|
4795 |
* @param array $list
|
4796 |
*
|
4797 |
* @return string
|
4798 |
+
*
|
4799 |
+
* @deprecated
|
4800 |
*/
|
4801 |
protected function flattenList($list)
|
4802 |
{
|
4803 |
+
@trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
|
4804 |
+
|
4805 |
return $this->compileValue($list);
|
4806 |
}
|
4807 |
|
4808 |
+
/**
|
4809 |
+
* Gets the text of a Sass string
|
4810 |
+
*
|
4811 |
+
* Calling this method on anything else than a SassString is unsupported. Use {@see assertString} first
|
4812 |
+
* to ensure that the value is indeed a string.
|
4813 |
+
*
|
4814 |
+
* @param array $value
|
4815 |
+
*
|
4816 |
+
* @return string
|
4817 |
+
*/
|
4818 |
+
public function getStringText(array $value)
|
4819 |
+
{
|
4820 |
+
if ($value[0] !== Type::T_STRING) {
|
4821 |
+
throw new \InvalidArgumentException('The argument is not a sass string. Did you forgot to use "assertString"?');
|
4822 |
+
}
|
4823 |
+
|
4824 |
+
return $this->compileStringContent($value);
|
4825 |
+
}
|
4826 |
+
|
4827 |
/**
|
4828 |
* Compile string content
|
4829 |
*
|
4830 |
* @param array $string
|
4831 |
+
* @param bool $quote
|
4832 |
*
|
4833 |
* @return string
|
4834 |
*/
|
4835 |
+
protected function compileStringContent($string, $quote = true)
|
4836 |
{
|
4837 |
$parts = [];
|
4838 |
|
4839 |
foreach ($string[2] as $part) {
|
4840 |
+
if (\is_array($part) || $part instanceof Number) {
|
4841 |
+
$parts[] = $this->compileValue($part, $quote);
|
4842 |
} else {
|
4843 |
$parts[] = $part;
|
4844 |
}
|
4902 |
$prevSelectors = $selectors;
|
4903 |
$selectors = [];
|
4904 |
|
4905 |
+
foreach ($parentSelectors as $parent) {
|
4906 |
+
foreach ($prevSelectors as $selector) {
|
4907 |
if ($selfParentSelectors) {
|
4908 |
foreach ($selfParentSelectors as $selfParent) {
|
4909 |
// if no '&' in the selector, each call will give same result, only add once
|
4923 |
|
4924 |
$selectors = array_values($selectors);
|
4925 |
|
4926 |
+
// case we are just starting a at-root : nothing to multiply but parentSelectors
|
4927 |
+
if (! $selectors && $selfParentSelectors) {
|
4928 |
+
$selectors = $selfParentSelectors;
|
4929 |
+
}
|
4930 |
+
|
4931 |
return $selectors;
|
4932 |
}
|
4933 |
|
4934 |
/**
|
4935 |
* Join selectors; looks for & to replace, or append parent before child
|
4936 |
*
|
4937 |
+
* @param array $parent
|
4938 |
+
* @param array $child
|
4939 |
+
* @param bool $stillHasSelf
|
4940 |
+
* @param array $selfParentSelectors
|
4941 |
|
4942 |
* @return array
|
4943 |
*/
|
5003 |
*/
|
5004 |
protected function multiplyMedia(Environment $env = null, $childQueries = null)
|
5005 |
{
|
5006 |
+
if (
|
5007 |
+
! isset($env) ||
|
5008 |
! empty($env->block->type) && $env->block->type !== Type::T_MEDIA
|
5009 |
) {
|
5010 |
return $childQueries;
|
5015 |
return $this->multiplyMedia($env->parent, $childQueries);
|
5016 |
}
|
5017 |
|
5018 |
+
assert($env->block instanceof MediaBlock);
|
5019 |
+
|
5020 |
$parentQueries = isset($env->block->queryList)
|
5021 |
? $env->block->queryList
|
5022 |
: [[[Type::T_MEDIA_VALUE, $env->block->value]]];
|
5052 |
/**
|
5053 |
* Convert env linked list to stack
|
5054 |
*
|
5055 |
+
* @param Environment $env
|
5056 |
*
|
5057 |
+
* @return Environment[]
|
5058 |
+
*
|
5059 |
+
* @phpstan-return non-empty-array<Environment>
|
5060 |
*/
|
5061 |
protected function compactEnv(Environment $env)
|
5062 |
{
|
5070 |
/**
|
5071 |
* Convert env stack to singly linked list
|
5072 |
*
|
5073 |
+
* @param Environment[] $envs
|
5074 |
*
|
5075 |
+
* @return Environment
|
5076 |
+
*
|
5077 |
+
* @phpstan-param non-empty-array<Environment> $envs
|
5078 |
*/
|
5079 |
protected function extractEnv($envs)
|
5080 |
{
|
5095 |
*/
|
5096 |
protected function pushEnv(Block $block = null)
|
5097 |
{
|
5098 |
+
$env = new Environment();
|
5099 |
$env->parent = $this->env;
|
5100 |
$env->parentStore = $this->storeEnv;
|
5101 |
$env->store = [];
|
5110 |
|
5111 |
/**
|
5112 |
* Pop environment
|
5113 |
+
*
|
5114 |
+
* @return void
|
5115 |
*/
|
5116 |
protected function popEnv()
|
5117 |
{
|
5122 |
/**
|
5123 |
* Propagate vars from a just poped Env (used in @each and @for)
|
5124 |
*
|
5125 |
+
* @param array $store
|
5126 |
+
* @param null|string[] $excludedVars
|
5127 |
+
*
|
5128 |
+
* @return void
|
5129 |
*/
|
5130 |
protected function backPropagateEnv($store, $excludedVars = null)
|
5131 |
{
|
5151 |
*
|
5152 |
* @param string $name
|
5153 |
* @param mixed $value
|
5154 |
+
* @param bool $shadow
|
5155 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
5156 |
* @param mixed $valueUnreduced
|
5157 |
+
*
|
5158 |
+
* @return void
|
5159 |
*/
|
5160 |
protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null)
|
5161 |
{
|
5179 |
* @param mixed $value
|
5180 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
5181 |
* @param mixed $valueUnreduced
|
5182 |
+
*
|
5183 |
+
* @return void
|
5184 |
*/
|
5185 |
protected function setExisting($name, $value, Environment $env, $valueUnreduced = null)
|
5186 |
{
|
5239 |
* @param mixed $value
|
5240 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
5241 |
* @param mixed $valueUnreduced
|
5242 |
+
*
|
5243 |
+
* @return void
|
5244 |
*/
|
5245 |
protected function setRaw($name, $value, Environment $env, $valueUnreduced = null)
|
5246 |
{
|
5254 |
/**
|
5255 |
* Get variable
|
5256 |
*
|
5257 |
+
* @internal
|
5258 |
*
|
5259 |
* @param string $name
|
5260 |
+
* @param bool $shouldThrow
|
5261 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
5262 |
+
* @param bool $unreduced
|
5263 |
*
|
5264 |
* @return mixed|null
|
5265 |
*/
|
5313 |
}
|
5314 |
|
5315 |
if ($shouldThrow) {
|
5316 |
+
throw $this->error("Undefined variable \$$name" . ($maxDepth <= 0 ? ' (infinite recursion)' : ''));
|
5317 |
}
|
5318 |
|
5319 |
// found nothing
|
5326 |
* @param string $name
|
5327 |
* @param \ScssPhp\ScssPhp\Compiler\Environment $env
|
5328 |
*
|
5329 |
+
* @return bool
|
5330 |
*/
|
5331 |
protected function has($name, Environment $env = null)
|
5332 |
{
|
5337 |
* Inject variables
|
5338 |
*
|
5339 |
* @param array $args
|
5340 |
+
*
|
5341 |
+
* @return void
|
5342 |
*/
|
5343 |
protected function injectVariables(array $args)
|
5344 |
{
|
5353 |
$name = substr($name, 1);
|
5354 |
}
|
5355 |
|
5356 |
+
if (!\is_string($strValue) || ! $parser->parseValue($strValue, $value)) {
|
5357 |
$value = $this->coerceValue($strValue);
|
5358 |
}
|
5359 |
|
5361 |
}
|
5362 |
}
|
5363 |
|
5364 |
+
/**
|
5365 |
+
* Replaces variables.
|
5366 |
+
*
|
5367 |
+
* @param array<string, mixed> $variables
|
5368 |
+
*
|
5369 |
+
* @return void
|
5370 |
+
*/
|
5371 |
+
public function replaceVariables(array $variables)
|
5372 |
+
{
|
5373 |
+
$this->registeredVars = [];
|
5374 |
+
$this->addVariables($variables);
|
5375 |
+
}
|
5376 |
+
|
5377 |
+
/**
|
5378 |
+
* Replaces variables.
|
5379 |
+
*
|
5380 |
+
* @param array<string, mixed> $variables
|
5381 |
+
*
|
5382 |
+
* @return void
|
5383 |
+
*/
|
5384 |
+
public function addVariables(array $variables)
|
5385 |
+
{
|
5386 |
+
$triggerWarning = false;
|
5387 |
+
|
5388 |
+
foreach ($variables as $name => $value) {
|
5389 |
+
if (!$value instanceof Number && !\is_array($value)) {
|
5390 |
+
$triggerWarning = true;
|
5391 |
+
}
|
5392 |
+
|
5393 |
+
$this->registeredVars[$name] = $value;
|
5394 |
+
}
|
5395 |
+
|
5396 |
+
if ($triggerWarning) {
|
5397 |
+
@trigger_error('Passing raw values to as custom variables to the Compiler is deprecated. Use "\ScssPhp\ScssPhp\ValueConverter::parseValue" or "\ScssPhp\ScssPhp\ValueConverter::fromPhp" to convert them instead.', E_USER_DEPRECATED);
|
5398 |
+
}
|
5399 |
+
}
|
5400 |
+
|
5401 |
/**
|
5402 |
* Set variables
|
5403 |
*
|
5404 |
* @api
|
5405 |
*
|
5406 |
* @param array $variables
|
5407 |
+
*
|
5408 |
+
* @return void
|
5409 |
+
*
|
5410 |
+
* @deprecated Use "addVariables" or "replaceVariables" instead.
|
5411 |
*/
|
5412 |
public function setVariables(array $variables)
|
5413 |
{
|
5414 |
+
@trigger_error('The method "setVariables" of the Compiler is deprecated. Use the "addVariables" method for the equivalent behavior or "replaceVariables" if merging with previous variables was not desired.');
|
5415 |
+
|
5416 |
+
$this->addVariables($variables);
|
5417 |
}
|
5418 |
|
5419 |
/**
|
5422 |
* @api
|
5423 |
*
|
5424 |
* @param string $name
|
5425 |
+
*
|
5426 |
+
* @return void
|
5427 |
*/
|
5428 |
public function unsetVariable($name)
|
5429 |
{
|
5445 |
/**
|
5446 |
* Adds to list of parsed files
|
5447 |
*
|
5448 |
+
* @internal
|
5449 |
*
|
5450 |
+
* @param string|null $path
|
5451 |
+
*
|
5452 |
+
* @return void
|
5453 |
*/
|
5454 |
public function addParsedFile($path)
|
5455 |
{
|
5456 |
+
if (! \is_null($path) && is_file($path)) {
|
5457 |
$this->parsedFiles[realpath($path)] = filemtime($path);
|
5458 |
}
|
5459 |
}
|
5461 |
/**
|
5462 |
* Returns list of parsed files
|
5463 |
*
|
5464 |
+
* @deprecated
|
5465 |
+
* @return array<string, int>
|
|
|
5466 |
*/
|
5467 |
public function getParsedFiles()
|
5468 |
{
|
5469 |
+
@trigger_error('The method "getParsedFiles" of the Compiler is deprecated. Use the "getIncludedFiles" method on the CompilationResult instance returned by compileString() instead. Be careful that the signature of the method is different.', E_USER_DEPRECATED);
|
5470 |
return $this->parsedFiles;
|
5471 |
}
|
5472 |
|
5476 |
* @api
|
5477 |
*
|
5478 |
* @param string|callable $path
|
5479 |
+
*
|
5480 |
+
* @return void
|
5481 |
*/
|
5482 |
public function addImportPath($path)
|
5483 |
{
|
5491 |
*
|
5492 |
* @api
|
5493 |
*
|
5494 |
+
* @param string|array<string|callable> $path
|
5495 |
+
*
|
5496 |
+
* @return void
|
5497 |
*/
|
5498 |
public function setImportPaths($path)
|
5499 |
{
|
5500 |
+
$paths = (array) $path;
|
5501 |
+
$actualImportPaths = array_filter($paths, function ($path) {
|
5502 |
+
return $path !== '';
|
5503 |
+
});
|
5504 |
+
|
5505 |
+
$this->legacyCwdImportPath = \count($actualImportPaths) !== \count($paths);
|
5506 |
+
|
5507 |
+
if ($this->legacyCwdImportPath) {
|
5508 |
+
@trigger_error('Passing an empty string in the import paths to refer to the current working directory is deprecated. If that\'s the intended behavior, the value of "getcwd()" should be used directly instead. If this was used for resolving relative imports of the input alongside "chdir" with the source directory, the path of the input file should be passed to "compileString()" instead.', E_USER_DEPRECATED);
|
5509 |
+
}
|
5510 |
+
|
5511 |
+
$this->importPaths = $actualImportPaths;
|
5512 |
}
|
5513 |
|
5514 |
/**
|
5516 |
*
|
5517 |
* @api
|
5518 |
*
|
5519 |
+
* @param int $numberPrecision
|
5520 |
+
*
|
5521 |
+
* @return void
|
5522 |
+
*
|
5523 |
+
* @deprecated The number precision is not configurable anymore. The default is enough for all browsers.
|
5524 |
*/
|
5525 |
public function setNumberPrecision($numberPrecision)
|
5526 |
{
|
5527 |
+
@trigger_error('The number precision is not configurable anymore. '
|
5528 |
+
. 'The default is enough for all browsers.', E_USER_DEPRECATED);
|
5529 |
+
}
|
5530 |
+
|
5531 |
+
/**
|
5532 |
+
* Sets the output style.
|
5533 |
+
*
|
5534 |
+
* @api
|
5535 |
+
*
|
5536 |
+
* @param string $style One of the OutputStyle constants
|
5537 |
+
*
|
5538 |
+
* @return void
|
5539 |
+
*
|
5540 |
+
* @phpstan-param OutputStyle::* $style
|
5541 |
+
*/
|
5542 |
+
public function setOutputStyle($style)
|
5543 |
+
{
|
5544 |
+
switch ($style) {
|
5545 |
+
case OutputStyle::EXPANDED:
|
5546 |
+
$this->configuredFormatter = Expanded::class;
|
5547 |
+
break;
|
5548 |
+
|
5549 |
+
case OutputStyle::COMPRESSED:
|
5550 |
+
$this->configuredFormatter = Compressed::class;
|
5551 |
+
break;
|
5552 |
+
|
5553 |
+
default:
|
5554 |
+
throw new \InvalidArgumentException(sprintf('Invalid output style "%s".', $style));
|
5555 |
+
}
|
5556 |
}
|
5557 |
|
5558 |
/**
|
5561 |
* @api
|
5562 |
*
|
5563 |
* @param string $formatterName
|
5564 |
+
*
|
5565 |
+
* @return void
|
5566 |
+
*
|
5567 |
+
* @deprecated Use {@see setOutputStyle} instead.
|
5568 |
+
*
|
5569 |
+
* @phpstan-param class-string<Formatter> $formatterName
|
5570 |
*/
|
5571 |
public function setFormatter($formatterName)
|
5572 |
{
|
5573 |
+
if (!\in_array($formatterName, [Expanded::class, Compressed::class], true)) {
|
5574 |
+
@trigger_error('Formatters other than Expanded and Compressed are deprecated.', E_USER_DEPRECATED);
|
5575 |
+
}
|
5576 |
+
@trigger_error('The method "setFormatter" is deprecated. Use "setOutputStyle" instead.', E_USER_DEPRECATED);
|
5577 |
+
|
5578 |
+
$this->configuredFormatter = $formatterName;
|
5579 |
}
|
5580 |
|
5581 |
/**
|
5584 |
* @api
|
5585 |
*
|
5586 |
* @param string $lineNumberStyle
|
5587 |
+
*
|
5588 |
+
* @return void
|
5589 |
+
*
|
5590 |
+
* @deprecated The line number output is not supported anymore. Use source maps instead.
|
5591 |
*/
|
5592 |
public function setLineNumberStyle($lineNumberStyle)
|
5593 |
{
|
5594 |
+
@trigger_error('The line number output is not supported anymore. '
|
5595 |
+
. 'Use source maps instead.', E_USER_DEPRECATED);
|
5596 |
+
}
|
5597 |
+
|
5598 |
+
/**
|
5599 |
+
* Configures the handling of non-ASCII outputs.
|
5600 |
+
*
|
5601 |
+
* If $charset is `true`, this will include a `@charset` declaration or a
|
5602 |
+
* UTF-8 [byte-order mark][] if the stylesheet contains any non-ASCII
|
5603 |
+
* characters. Otherwise, it will never include a `@charset` declaration or a
|
5604 |
+
* byte-order mark.
|
5605 |
+
*
|
5606 |
+
* [byte-order mark]: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
|
5607 |
+
*
|
5608 |
+
* @param bool $charset
|
5609 |
+
*
|
5610 |
+
* @return void
|
5611 |
+
*/
|
5612 |
+
public function setCharset($charset)
|
5613 |
+
{
|
5614 |
+
$this->charset = $charset;
|
5615 |
}
|
5616 |
|
5617 |
/**
|
5619 |
*
|
5620 |
* @api
|
5621 |
*
|
5622 |
+
* @param int $sourceMap
|
5623 |
+
*
|
5624 |
+
* @return void
|
5625 |
+
*
|
5626 |
+
* @phpstan-param self::SOURCE_MAP_* $sourceMap
|
5627 |
*/
|
5628 |
public function setSourceMap($sourceMap)
|
5629 |
{
|
5636 |
* @api
|
5637 |
*
|
5638 |
* @param array $sourceMapOptions
|
5639 |
+
*
|
5640 |
+
* @phpstan-param array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string} $sourceMapOptions
|
5641 |
+
*
|
5642 |
+
* @return void
|
5643 |
*/
|
5644 |
public function setSourceMapOptions($sourceMapOptions)
|
5645 |
{
|
5651 |
*
|
5652 |
* @api
|
5653 |
*
|
5654 |
+
* @param string $name
|
5655 |
+
* @param callable $callback
|
5656 |
+
* @param string[]|null $argumentDeclaration
|
5657 |
+
*
|
5658 |
+
* @return void
|
5659 |
*/
|
5660 |
+
public function registerFunction($name, $callback, $argumentDeclaration = null)
|
5661 |
{
|
5662 |
+
if (self::isNativeFunction($name)) {
|
5663 |
+
@trigger_error(sprintf('The "%s" function is a core sass function. Overriding it with a custom implementation through "%s" is deprecated and won\'t be supported in ScssPhp 2.0 anymore.', $name, __METHOD__), E_USER_DEPRECATED);
|
5664 |
+
}
|
5665 |
+
|
5666 |
+
if ($argumentDeclaration === null) {
|
5667 |
+
@trigger_error('Omitting the argument declaration when registering custom function is deprecated and won\'t be supported in ScssPhp 2.0 anymore.', E_USER_DEPRECATED);
|
5668 |
+
}
|
5669 |
+
|
5670 |
+
$this->userFunctions[$this->normalizeName($name)] = [$callback, $argumentDeclaration];
|
5671 |
}
|
5672 |
|
5673 |
/**
|
5676 |
* @api
|
5677 |
*
|
5678 |
* @param string $name
|
5679 |
+
*
|
5680 |
+
* @return void
|
5681 |
*/
|
5682 |
public function unregisterFunction($name)
|
5683 |
{
|
5690 |
* @api
|
5691 |
*
|
5692 |
* @param string $name
|
5693 |
+
*
|
5694 |
+
* @return void
|
5695 |
+
*
|
5696 |
+
* @deprecated Registering additional features is deprecated.
|
5697 |
*/
|
5698 |
public function addFeature($name)
|
5699 |
{
|
5700 |
+
@trigger_error('Registering additional features is deprecated.', E_USER_DEPRECATED);
|
5701 |
+
|
5702 |
$this->registeredFeatures[$name] = true;
|
5703 |
}
|
5704 |
|
5707 |
*
|
5708 |
* @param string $path
|
5709 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
|
5710 |
+
*
|
5711 |
+
* @return void
|
5712 |
*/
|
5713 |
protected function importFile($path, OutputBlock $out)
|
5714 |
{
|
5715 |
+
$this->pushCallStack('import ' . $this->getPrettyPath($path));
|
5716 |
// see if tree is cached
|
5717 |
$realPath = realpath($path);
|
5718 |
|
5719 |
+
if ($realPath === false) {
|
5720 |
+
$realPath = $path;
|
5721 |
+
}
|
5722 |
+
|
5723 |
+
if (substr($path, -5) === '.sass') {
|
5724 |
+
$this->sourceIndex = \count($this->sourceNames);
|
5725 |
+
$this->sourceNames[] = $path;
|
5726 |
+
$this->sourceLine = 1;
|
5727 |
+
$this->sourceColumn = 1;
|
5728 |
+
|
5729 |
+
throw $this->error('The Sass indented syntax is not implemented.');
|
5730 |
+
}
|
5731 |
+
|
5732 |
if (isset($this->importCache[$realPath])) {
|
5733 |
$this->handleImportLoop($realPath);
|
5734 |
|
5741 |
$this->importCache[$realPath] = $tree;
|
5742 |
}
|
5743 |
|
5744 |
+
$currentDirectory = $this->currentDirectory;
|
5745 |
+
$this->currentDirectory = dirname($path);
|
5746 |
|
|
|
5747 |
$this->compileChildrenNoReturn($tree->children, $out);
|
5748 |
+
$this->currentDirectory = $currentDirectory;
|
5749 |
$this->popCallStack();
|
5750 |
}
|
5751 |
|
5752 |
/**
|
5753 |
+
* Save the imported files with their resolving path context
|
5754 |
*
|
5755 |
+
* @param string|null $currentDirectory
|
5756 |
+
* @param string $path
|
5757 |
+
* @param string $filePath
|
5758 |
+
*
|
5759 |
+
* @return void
|
5760 |
+
*/
|
5761 |
+
private function registerImport($currentDirectory, $path, $filePath)
|
5762 |
+
{
|
5763 |
+
$this->resolvedImports[] = ['currentDir' => $currentDirectory, 'path' => $path, 'filePath' => $filePath];
|
5764 |
+
}
|
5765 |
+
|
5766 |
+
/**
|
5767 |
+
* Detects whether the import is a CSS import.
|
5768 |
+
*
|
5769 |
+
* For legacy reasons, custom importers are called for those, allowing them
|
5770 |
+
* to replace them with an actual Sass import. However this behavior is
|
5771 |
+
* deprecated. Custom importers are expected to return null when they receive
|
5772 |
+
* a CSS import.
|
5773 |
*
|
5774 |
* @param string $url
|
5775 |
*
|
5776 |
+
* @return bool
|
5777 |
*/
|
5778 |
+
public static function isCssImport($url)
|
5779 |
{
|
5780 |
+
return 1 === preg_match('~\.css$|^https?://|^//~', $url);
|
5781 |
+
}
|
5782 |
|
5783 |
+
/**
|
5784 |
+
* Return the file path for an import url if it exists
|
5785 |
+
*
|
5786 |
+
* @internal
|
5787 |
+
*
|
5788 |
+
* @param string $url
|
5789 |
+
* @param string|null $currentDir
|
5790 |
+
*
|
5791 |
+
* @return string|null
|
5792 |
+
*/
|
5793 |
+
public function findImport($url, $currentDir = null)
|
5794 |
+
{
|
5795 |
+
// Vanilla css and external requests. These are not meant to be Sass imports.
|
5796 |
+
// Callback importers are still called for BC.
|
5797 |
+
if (self::isCssImport($url)) {
|
5798 |
+
foreach ($this->importPaths as $dir) {
|
5799 |
+
if (\is_string($dir)) {
|
5800 |
+
continue;
|
5801 |
+
}
|
5802 |
|
5803 |
+
if (\is_callable($dir)) {
|
5804 |
+
// check custom callback for import path
|
5805 |
+
$file = \call_user_func($dir, $url);
|
5806 |
|
5807 |
+
if (! \is_null($file)) {
|
5808 |
+
if (\is_array($dir)) {
|
5809 |
+
$callableDescription = (\is_object($dir[0]) ? \get_class($dir[0]) : $dir[0]).'::'.$dir[1];
|
5810 |
+
} elseif ($dir instanceof \Closure) {
|
5811 |
+
$r = new \ReflectionFunction($dir);
|
5812 |
+
if (false !== strpos($r->name, '{closure}')) {
|
5813 |
+
$callableDescription = sprintf('closure{%s:%s}', $r->getFileName(), $r->getStartLine());
|
5814 |
+
} elseif ($class = $r->getClosureScopeClass()) {
|
5815 |
+
$callableDescription = $class->name.'::'.$r->name;
|
5816 |
+
} else {
|
5817 |
+
$callableDescription = $r->name;
|
5818 |
+
}
|
5819 |
+
} elseif (\is_object($dir)) {
|
5820 |
+
$callableDescription = \get_class($dir) . '::__invoke';
|
5821 |
+
} else {
|
5822 |
+
$callableDescription = 'callable'; // Fallback if we don't have a dedicated description
|
5823 |
+
}
|
5824 |
+
@trigger_error(sprintf('Returning a file to import for CSS or external references in custom importer callables is deprecated and will not be supported anymore in ScssPhp 2.0. This behavior is not compliant with the Sass specification. Update your "%s" importer.', $callableDescription), E_USER_DEPRECATED);
|
5825 |
|
5826 |
+
return $file;
|
5827 |
+
}
|
5828 |
+
}
|
5829 |
}
|
5830 |
+
return null;
|
5831 |
+
}
|
5832 |
|
5833 |
+
if (!\is_null($currentDir)) {
|
5834 |
+
$relativePath = $this->resolveImportPath($url, $currentDir);
|
5835 |
+
|
5836 |
+
if (!\is_null($relativePath)) {
|
5837 |
+
return $relativePath;
|
5838 |
}
|
5839 |
}
|
5840 |
|
5841 |
foreach ($this->importPaths as $dir) {
|
5842 |
if (\is_string($dir)) {
|
5843 |
+
$path = $this->resolveImportPath($url, $dir);
|
5844 |
+
|
5845 |
+
if (!\is_null($path)) {
|
5846 |
+
return $path;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5847 |
}
|
5848 |
} elseif (\is_callable($dir)) {
|
5849 |
// check custom callback for import path
|
5855 |
}
|
5856 |
}
|
5857 |
|
5858 |
+
if ($this->legacyCwdImportPath) {
|
5859 |
+
$path = $this->resolveImportPath($url, getcwd());
|
5860 |
+
|
5861 |
+
if (!\is_null($path)) {
|
5862 |
+
@trigger_error('Resolving imports relatively to the current working directory is deprecated. If that\'s the intended behavior, the value of "getcwd()" should be added as an import path explicitly instead. If this was used for resolving relative imports of the input alongside "chdir" with the source directory, the path of the input file should be passed to "compileString()" instead.', E_USER_DEPRECATED);
|
5863 |
+
|
5864 |
+
return $path;
|
5865 |
}
|
5866 |
}
|
5867 |
|
5868 |
+
throw $this->error("`$url` file not found for @import");
|
5869 |
}
|
5870 |
|
5871 |
/**
|
5872 |
+
* @param string $url
|
5873 |
+
* @param string $baseDir
|
|
|
5874 |
*
|
5875 |
+
* @return string|null
|
5876 |
*/
|
5877 |
+
private function resolveImportPath($url, $baseDir)
|
5878 |
{
|
5879 |
+
$path = Path::join($baseDir, $url);
|
5880 |
+
|
5881 |
+
$hasExtension = preg_match('/.s[ac]ss$/', $url);
|
5882 |
+
|
5883 |
+
if ($hasExtension) {
|
5884 |
+
return $this->checkImportPathConflicts($this->tryImportPath($path));
|
5885 |
+
}
|
5886 |
+
|
5887 |
+
$result = $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path));
|
5888 |
+
|
5889 |
+
if (!\is_null($result)) {
|
5890 |
+
return $result;
|
5891 |
+
}
|
5892 |
+
|
5893 |
+
return $this->tryImportPathAsDirectory($path);
|
5894 |
}
|
5895 |
|
5896 |
/**
|
5897 |
+
* @param string[] $paths
|
|
|
|
|
5898 |
*
|
5899 |
+
* @return string|null
|
|
|
|
|
5900 |
*/
|
5901 |
+
private function checkImportPathConflicts(array $paths)
|
5902 |
{
|
5903 |
+
if (\count($paths) === 0) {
|
5904 |
+
return null;
|
5905 |
+
}
|
5906 |
|
5907 |
+
if (\count($paths) === 1) {
|
5908 |
+
return $paths[0];
|
5909 |
+
}
|
5910 |
+
|
5911 |
+
$formattedPrettyPaths = [];
|
5912 |
+
|
5913 |
+
foreach ($paths as $path) {
|
5914 |
+
$formattedPrettyPaths[] = ' ' . $this->getPrettyPath($path);
|
5915 |
+
}
|
5916 |
+
|
5917 |
+
throw $this->error("It's not clear which file to import. Found:\n" . implode("\n", $formattedPrettyPaths));
|
5918 |
}
|
5919 |
|
5920 |
/**
|
5921 |
+
* @param string $path
|
|
|
|
|
|
|
|
|
5922 |
*
|
5923 |
+
* @return string[]
|
5924 |
*/
|
5925 |
+
private function tryImportPathWithExtensions($path)
|
5926 |
{
|
5927 |
+
$result = array_merge(
|
5928 |
+
$this->tryImportPath($path.'.sass'),
|
5929 |
+
$this->tryImportPath($path.'.scss')
|
5930 |
+
);
|
5931 |
|
5932 |
+
if ($result) {
|
5933 |
+
return $result;
|
5934 |
}
|
5935 |
|
5936 |
+
return $this->tryImportPath($path.'.css');
|
5937 |
+
}
|
|
|
5938 |
|
5939 |
+
/**
|
5940 |
+
* @param string $path
|
5941 |
+
*
|
5942 |
+
* @return string[]
|
5943 |
+
*/
|
5944 |
+
private function tryImportPath($path)
|
5945 |
+
{
|
5946 |
+
$partial = dirname($path).'/_'.basename($path);
|
5947 |
|
5948 |
+
$candidates = [];
|
5949 |
|
5950 |
+
if (is_file($partial)) {
|
5951 |
+
$candidates[] = $partial;
|
5952 |
+
}
|
5953 |
|
5954 |
+
if (is_file($path)) {
|
5955 |
+
$candidates[] = $path;
|
|
|
5956 |
}
|
5957 |
|
5958 |
+
return $candidates;
|
5959 |
}
|
5960 |
|
5961 |
/**
|
5962 |
+
* @param string $path
|
|
|
|
|
|
|
5963 |
*
|
5964 |
+
* @return string|null
|
5965 |
*/
|
5966 |
+
private function tryImportPathAsDirectory($path)
|
5967 |
{
|
5968 |
+
if (!is_dir($path)) {
|
5969 |
+
return null;
|
5970 |
+
}
|
5971 |
+
|
5972 |
+
return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path.'/index'));
|
5973 |
+
}
|
5974 |
+
|
5975 |
+
/**
|
5976 |
+
* @param string|null $path
|
5977 |
+
*
|
5978 |
+
* @return string
|
5979 |
+
*/
|
5980 |
+
private function getPrettyPath($path)
|
5981 |
+
{
|
5982 |
+
if ($path === null) {
|
5983 |
+
return '(unknown file)';
|
5984 |
+
}
|
5985 |
+
|
5986 |
+
$normalizedPath = $path;
|
5987 |
+
$normalizedRootDirectory = $this->rootDirectory.'/';
|
5988 |
+
|
5989 |
+
if (\DIRECTORY_SEPARATOR === '\\') {
|
5990 |
+
$normalizedRootDirectory = str_replace('\\', '/', $normalizedRootDirectory);
|
5991 |
+
$normalizedPath = str_replace('\\', '/', $path);
|
5992 |
+
}
|
5993 |
+
|
5994 |
+
if (0 === strpos($normalizedPath, $normalizedRootDirectory)) {
|
5995 |
+
return substr($path, \strlen($normalizedRootDirectory));
|
5996 |
+
}
|
5997 |
+
|
5998 |
+
return $path;
|
5999 |
+
}
|
6000 |
+
|
6001 |
+
/**
|
6002 |
+
* Set encoding
|
6003 |
+
*
|
6004 |
+
* @api
|
6005 |
+
*
|
6006 |
+
* @param string|null $encoding
|
6007 |
+
*
|
6008 |
+
* @return void
|
6009 |
+
*
|
6010 |
+
* @deprecated Non-compliant support for other encodings than UTF-8 is deprecated.
|
6011 |
+
*/
|
6012 |
+
public function setEncoding($encoding)
|
6013 |
+
{
|
6014 |
+
if (!$encoding || strtolower($encoding) === 'utf-8') {
|
6015 |
+
@trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
|
6016 |
+
} else {
|
6017 |
+
@trigger_error(sprintf('The "%s" method is deprecated. Parsing will only support UTF-8 in ScssPhp 2.0. The non-UTF-8 parsing of ScssPhp 1.x is not spec compliant.', __METHOD__), E_USER_DEPRECATED);
|
6018 |
+
}
|
6019 |
+
|
6020 |
+
$this->encoding = $encoding;
|
6021 |
+
}
|
6022 |
+
|
6023 |
+
/**
|
6024 |
+
* Ignore errors?
|
6025 |
+
*
|
6026 |
+
* @api
|
6027 |
+
*
|
6028 |
+
* @param bool $ignoreErrors
|
6029 |
+
*
|
6030 |
+
* @return \ScssPhp\ScssPhp\Compiler
|
6031 |
+
*
|
6032 |
+
* @deprecated Ignoring Sass errors is not longer supported.
|
6033 |
+
*/
|
6034 |
+
public function setIgnoreErrors($ignoreErrors)
|
6035 |
+
{
|
6036 |
+
@trigger_error('Ignoring Sass errors is not longer supported.', E_USER_DEPRECATED);
|
6037 |
+
|
6038 |
+
return $this;
|
6039 |
+
}
|
6040 |
+
|
6041 |
+
/**
|
6042 |
+
* Get source position
|
6043 |
+
*
|
6044 |
+
* @api
|
6045 |
+
*
|
6046 |
+
* @return array
|
6047 |
+
*
|
6048 |
+
* @deprecated
|
6049 |
+
*/
|
6050 |
+
public function getSourcePosition()
|
6051 |
+
{
|
6052 |
+
@trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
|
6053 |
+
|
6054 |
+
$sourceFile = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : '';
|
6055 |
+
|
6056 |
+
return [$sourceFile, $this->sourceLine, $this->sourceColumn];
|
6057 |
+
}
|
6058 |
+
|
6059 |
+
/**
|
6060 |
+
* Throw error (exception)
|
6061 |
+
*
|
6062 |
+
* @api
|
6063 |
+
*
|
6064 |
+
* @param string $msg Message with optional sprintf()-style vararg parameters
|
6065 |
+
*
|
6066 |
+
* @return never
|
6067 |
+
*
|
6068 |
+
* @throws \ScssPhp\ScssPhp\Exception\CompilerException
|
6069 |
+
*
|
6070 |
+
* @deprecated use "error" and throw the exception in the caller instead.
|
6071 |
+
*/
|
6072 |
+
public function throwError($msg)
|
6073 |
+
{
|
6074 |
+
@trigger_error(
|
6075 |
+
'The method "throwError" is deprecated. Use "error" and throw the exception in the caller instead',
|
6076 |
+
E_USER_DEPRECATED
|
6077 |
+
);
|
6078 |
+
|
6079 |
+
throw $this->error(...func_get_args());
|
6080 |
+
}
|
6081 |
+
|
6082 |
+
/**
|
6083 |
+
* Build an error (exception)
|
6084 |
+
*
|
6085 |
+
* @internal
|
6086 |
+
*
|
6087 |
+
* @param string $msg Message with optional sprintf()-style vararg parameters
|
6088 |
+
* @param bool|float|int|string|null ...$args
|
6089 |
+
*
|
6090 |
+
* @return CompilerException
|
6091 |
+
*/
|
6092 |
+
public function error($msg, ...$args)
|
6093 |
+
{
|
6094 |
+
if ($args) {
|
6095 |
+
$msg = sprintf($msg, ...$args);
|
6096 |
+
}
|
6097 |
+
|
6098 |
+
if (! $this->ignoreCallStackMessage) {
|
6099 |
+
$msg = $this->addLocationToMessage($msg);
|
6100 |
+
}
|
6101 |
+
|
6102 |
+
return new CompilerException($msg);
|
6103 |
+
}
|
6104 |
+
|
6105 |
+
/**
|
6106 |
+
* @param string $msg
|
6107 |
+
*
|
6108 |
+
* @return string
|
6109 |
+
*/
|
6110 |
+
private function addLocationToMessage($msg)
|
6111 |
+
{
|
6112 |
+
$line = $this->sourceLine;
|
6113 |
+
$column = $this->sourceColumn;
|
6114 |
+
|
6115 |
+
$loc = isset($this->sourceNames[$this->sourceIndex])
|
6116 |
+
? $this->getPrettyPath($this->sourceNames[$this->sourceIndex]) . " on line $line, at column $column"
|
6117 |
+
: "line: $line, column: $column";
|
6118 |
+
|
6119 |
+
$msg = "$msg: $loc";
|
6120 |
+
|
6121 |
+
$callStackMsg = $this->callStackMessage();
|
6122 |
+
|
6123 |
+
if ($callStackMsg) {
|
6124 |
+
$msg .= "\nCall Stack:\n" . $callStackMsg;
|
6125 |
+
}
|
6126 |
+
|
6127 |
+
return $msg;
|
6128 |
+
}
|
6129 |
+
|
6130 |
+
/**
|
6131 |
+
* @param string $functionName
|
6132 |
+
* @param array $ExpectedArgs
|
6133 |
+
* @param int $nbActual
|
6134 |
+
* @return CompilerException
|
6135 |
+
*
|
6136 |
+
* @deprecated
|
6137 |
+
*/
|
6138 |
+
public function errorArgsNumber($functionName, $ExpectedArgs, $nbActual)
|
6139 |
+
{
|
6140 |
+
@trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
|
6141 |
+
|
6142 |
+
$nbExpected = \count($ExpectedArgs);
|
6143 |
+
|
6144 |
+
if ($nbActual > $nbExpected) {
|
6145 |
+
return $this->error(
|
6146 |
+
'Error: Only %d arguments allowed in %s(), but %d were passed.',
|
6147 |
+
$nbExpected,
|
6148 |
+
$functionName,
|
6149 |
+
$nbActual
|
6150 |
+
);
|
6151 |
+
} else {
|
6152 |
+
$missing = [];
|
6153 |
+
|
6154 |
+
while (count($ExpectedArgs) && count($ExpectedArgs) > $nbActual) {
|
6155 |
+
array_unshift($missing, array_pop($ExpectedArgs));
|
6156 |
+
}
|
6157 |
+
|
6158 |
+
return $this->error(
|
6159 |
+
'Error: %s() argument%s %s missing.',
|
6160 |
+
$functionName,
|
6161 |
+
count($missing) > 1 ? 's' : '',
|
6162 |
+
implode(', ', $missing)
|
6163 |
+
);
|
6164 |
+
}
|
6165 |
+
}
|
6166 |
+
|
6167 |
+
/**
|
6168 |
+
* Beautify call stack for output
|
6169 |
+
*
|
6170 |
+
* @param bool $all
|
6171 |
+
* @param int|null $limit
|
6172 |
+
*
|
6173 |
+
* @return string
|
6174 |
+
*/
|
6175 |
+
protected function callStackMessage($all = false, $limit = null)
|
6176 |
+
{
|
6177 |
+
$callStackMsg = [];
|
6178 |
+
$ncall = 0;
|
6179 |
|
6180 |
if ($this->callStack) {
|
6181 |
foreach (array_reverse($this->callStack) as $call) {
|
6182 |
if ($all || (isset($call['n']) && $call['n'])) {
|
6183 |
+
$msg = '#' . $ncall++ . ' ' . $call['n'] . ' ';
|
6184 |
$msg .= (isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
|
6185 |
+
? $this->getPrettyPath($this->sourceNames[$call[Parser::SOURCE_INDEX]])
|
6186 |
: '(unknown file)');
|
6187 |
+
$msg .= ' on line ' . $call[Parser::SOURCE_LINE];
|
6188 |
|
6189 |
$callStackMsg[] = $msg;
|
6190 |
|
6203 |
*
|
6204 |
* @param string $name
|
6205 |
*
|
6206 |
+
* @return void
|
6207 |
+
*
|
6208 |
* @throws \Exception
|
6209 |
*/
|
6210 |
protected function handleImportLoop($name)
|
6216 |
|
6217 |
$file = $this->sourceNames[$env->block->sourceIndex];
|
6218 |
|
6219 |
+
if ($file === null) {
|
6220 |
+
continue;
|
6221 |
+
}
|
6222 |
+
|
6223 |
if (realpath($file) === $name) {
|
6224 |
+
throw $this->error('An @import loop has been found: %s imports %s', $file, basename($file));
|
|
|
6225 |
}
|
6226 |
}
|
6227 |
}
|
6229 |
/**
|
6230 |
* Call SCSS @function
|
6231 |
*
|
6232 |
+
* @param CallableBlock|null $func
|
6233 |
+
* @param array $argValues
|
|
|
6234 |
*
|
6235 |
+
* @return array|Number
|
6236 |
*/
|
6237 |
+
protected function callScssFunction($func, $argValues)
|
6238 |
{
|
|
|
|
|
6239 |
if (! $func) {
|
6240 |
+
return static::$defaultValue;
|
6241 |
}
|
6242 |
+
$name = $func->name;
|
6243 |
|
6244 |
$this->pushEnv();
|
6245 |
|
6249 |
}
|
6250 |
|
6251 |
// throw away lines and children
|
6252 |
+
$tmp = new OutputBlock();
|
6253 |
$tmp->lines = [];
|
6254 |
$tmp->children = [];
|
6255 |
|
6258 |
if (! empty($func->parentEnv)) {
|
6259 |
$this->env->declarationScopeParent = $func->parentEnv;
|
6260 |
} else {
|
6261 |
+
throw $this->error("@function $name() without parentEnv");
|
6262 |
}
|
6263 |
|
6264 |
+
$ret = $this->compileChildren($func->children, $tmp, $this->env->marker . ' ' . $name);
|
6265 |
|
6266 |
$this->popEnv();
|
6267 |
|
6268 |
+
return ! isset($ret) ? static::$defaultValue : $ret;
|
|
|
|
|
6269 |
}
|
6270 |
|
6271 |
/**
|
6272 |
* Call built-in and registered (PHP) functions
|
6273 |
*
|
6274 |
* @param string $name
|
6275 |
+
* @param callable $function
|
6276 |
+
* @param array $prototype
|
6277 |
* @param array $args
|
|
|
6278 |
*
|
6279 |
+
* @return array|Number|null
|
6280 |
*/
|
6281 |
+
protected function callNativeFunction($name, $function, $prototype, $args)
|
6282 |
{
|
6283 |
+
$libName = (is_array($function) ? end($function) : null);
|
6284 |
+
$sorted_kwargs = $this->sortNativeFunctionArgs($libName, $prototype, $args);
|
|
|
6285 |
|
6286 |
+
if (\is_null($sorted_kwargs)) {
|
6287 |
+
return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
6288 |
}
|
6289 |
+
@list($sorted, $kwargs) = $sorted_kwargs;
|
6290 |
|
6291 |
+
if ($name !== 'if') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6292 |
foreach ($sorted as &$val) {
|
6293 |
+
if ($val !== null) {
|
6294 |
+
$val = $this->reduce($val, true);
|
6295 |
+
}
|
6296 |
}
|
6297 |
}
|
6298 |
|
6299 |
+
$returnValue = \call_user_func($function, $sorted, $kwargs);
|
6300 |
|
6301 |
if (! isset($returnValue)) {
|
6302 |
+
return null;
|
6303 |
}
|
6304 |
|
6305 |
+
if (\is_array($returnValue) || $returnValue instanceof Number) {
|
6306 |
+
return $returnValue;
|
6307 |
+
}
|
6308 |
|
6309 |
+
@trigger_error(sprintf('Returning a PHP value from the "%s" custom function is deprecated. A sass value must be returned instead.', $name), E_USER_DEPRECATED);
|
6310 |
+
|
6311 |
+
return $this->coerceValue($returnValue);
|
6312 |
}
|
6313 |
|
6314 |
/**
|
6320 |
*/
|
6321 |
protected function getBuiltinFunction($name)
|
6322 |
{
|
6323 |
+
$libName = self::normalizeNativeFunctionName($name);
|
6324 |
+
return [$this, $libName];
|
6325 |
+
}
|
6326 |
+
|
6327 |
+
/**
|
6328 |
+
* Normalize native function name
|
6329 |
+
*
|
6330 |
+
* @internal
|
6331 |
+
*
|
6332 |
+
* @param string $name
|
6333 |
+
*
|
6334 |
+
* @return string
|
6335 |
+
*/
|
6336 |
+
public static function normalizeNativeFunctionName($name)
|
6337 |
+
{
|
6338 |
+
$name = str_replace("-", "_", $name);
|
6339 |
$libName = 'lib' . preg_replace_callback(
|
6340 |
'/_(.)/',
|
6341 |
function ($m) {
|
6343 |
},
|
6344 |
ucfirst($name)
|
6345 |
);
|
6346 |
+
return $libName;
|
6347 |
+
}
|
6348 |
|
6349 |
+
/**
|
6350 |
+
* Check if a function is a native built-in scss function, for css parsing
|
6351 |
+
*
|
6352 |
+
* @internal
|
6353 |
+
*
|
6354 |
+
* @param string $name
|
6355 |
+
*
|
6356 |
+
* @return bool
|
6357 |
+
*/
|
6358 |
+
public static function isNativeFunction($name)
|
6359 |
+
{
|
6360 |
+
return method_exists(Compiler::class, self::normalizeNativeFunctionName($name));
|
6361 |
}
|
6362 |
|
6363 |
/**
|
6364 |
* Sorts keyword arguments
|
6365 |
*
|
6366 |
* @param string $functionName
|
6367 |
+
* @param array|null $prototypes
|
6368 |
* @param array $args
|
6369 |
*
|
6370 |
+
* @return array|null
|
6371 |
*/
|
6372 |
protected function sortNativeFunctionArgs($functionName, $prototypes, $args)
|
6373 |
{
|
6377 |
$keyArgs = [];
|
6378 |
$posArgs = [];
|
6379 |
|
6380 |
+
if (\is_array($args) && \count($args) && \end($args) === static::$null) {
|
6381 |
+
array_pop($args);
|
6382 |
+
}
|
6383 |
+
|
6384 |
// separate positional and keyword arguments
|
6385 |
foreach ($args as $arg) {
|
6386 |
list($key, $value) = $arg;
|
6387 |
|
6388 |
+
if (empty($key) or empty($key[1])) {
|
|
|
|
|
6389 |
$posArgs[] = empty($arg[2]) ? $value : $arg;
|
6390 |
} else {
|
6391 |
+
$keyArgs[$key[1]] = $value;
|
6392 |
}
|
6393 |
}
|
6394 |
|
6399 |
if (\in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) {
|
6400 |
// notation 100 127 255 / 0 is in fact a simple list of 4 values
|
6401 |
foreach ($args as $k => $arg) {
|
6402 |
+
if (!isset($arg[1])) {
|
6403 |
+
continue; // This happens when using a trailing comma
|
6404 |
+
}
|
6405 |
if ($arg[1][0] === Type::T_LIST && \count($arg[1][2]) === 3) {
|
6406 |
+
$args[$k][1][2] = $this->extractSlashAlphaInColorFunction($arg[1][2]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6407 |
}
|
6408 |
}
|
6409 |
}
|
6410 |
|
6411 |
+
list($positionalArgs, $namedArgs, $names, $separator, $hasSplat) = $this->evaluateArguments($args, false);
|
6412 |
|
6413 |
if (! \is_array(reset($prototypes))) {
|
6414 |
$prototypes = [$prototypes];
|
6415 |
}
|
6416 |
|
6417 |
+
$parsedPrototypes = array_map([$this, 'parseFunctionPrototype'], $prototypes);
|
6418 |
+
assert(!empty($parsedPrototypes));
|
6419 |
+
$matchedPrototype = $this->selectFunctionPrototype($parsedPrototypes, \count($positionalArgs), $names);
|
6420 |
+
|
6421 |
+
$this->verifyPrototype($matchedPrototype, \count($positionalArgs), $names, $hasSplat);
|
6422 |
+
|
6423 |
+
$vars = $this->applyArgumentsToDeclaration($matchedPrototype, $positionalArgs, $namedArgs, $separator);
|
6424 |
+
|
6425 |
+
$finalArgs = [];
|
6426 |
$keyArgs = [];
|
6427 |
|
6428 |
+
foreach ($matchedPrototype['arguments'] as $argument) {
|
6429 |
+
list($normalizedName, $originalName, $default) = $argument;
|
|
|
6430 |
|
6431 |
+
if (isset($vars[$normalizedName])) {
|
6432 |
+
$value = $vars[$normalizedName];
|
6433 |
+
} else {
|
6434 |
+
$value = $default;
|
6435 |
+
}
|
6436 |
|
6437 |
+
// special null value as default: translate to real null here
|
6438 |
+
if ($value === [Type::T_KEYWORD, 'null']) {
|
6439 |
+
$value = null;
|
6440 |
+
}
|
6441 |
|
6442 |
+
$finalArgs[] = $value;
|
6443 |
+
$keyArgs[$originalName] = $value;
|
6444 |
+
}
|
6445 |
|
6446 |
+
if ($matchedPrototype['rest_argument'] !== null) {
|
6447 |
+
$value = $vars[$matchedPrototype['rest_argument']];
|
|
|
|
|
|
|
|
|
|
|
6448 |
|
6449 |
+
$finalArgs[] = $value;
|
6450 |
+
$keyArgs[$matchedPrototype['rest_argument']] = $value;
|
6451 |
+
}
|
6452 |
|
6453 |
+
return [$finalArgs, $keyArgs];
|
6454 |
+
}
|
6455 |
+
|
6456 |
+
/**
|
6457 |
+
* Parses a function prototype to the internal representation of arguments.
|
6458 |
+
*
|
6459 |
+
* The input is an array of strings describing each argument, as supported
|
6460 |
+
* in {@see registerFunction}. Argument names don't include the `$`.
|
6461 |
+
* The output contains the list of positional argument, with their normalized
|
6462 |
+
* name (underscores are replaced by dashes), their original name (to be used
|
6463 |
+
* in case of error reporting) and their default value. The output also contains
|
6464 |
+
* the normalized name of the rest argument, or null if the function prototype
|
6465 |
+
* is not variadic.
|
6466 |
+
*
|
6467 |
+
* @param string[] $prototype
|
6468 |
+
*
|
6469 |
+
* @return array
|
6470 |
+
* @phpstan-return array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}
|
6471 |
+
*/
|
6472 |
+
private function parseFunctionPrototype(array $prototype)
|
6473 |
+
{
|
6474 |
+
static $parser = null;
|
6475 |
+
|
6476 |
+
$arguments = [];
|
6477 |
+
$restArgument = null;
|
6478 |
|
6479 |
+
foreach ($prototype as $p) {
|
6480 |
+
if (null !== $restArgument) {
|
6481 |
+
throw new \InvalidArgumentException('The argument declaration is invalid. The rest argument must be the last one.');
|
6482 |
+
}
|
6483 |
+
|
6484 |
+
$default = null;
|
6485 |
+
$p = explode(':', $p, 2);
|
6486 |
+
$name = str_replace('_', '-', $p[0]);
|
6487 |
+
|
6488 |
+
if (isset($p[1])) {
|
6489 |
+
$defaultSource = trim($p[1]);
|
6490 |
+
|
6491 |
+
if ($defaultSource === 'null') {
|
6492 |
+
// differentiate this null from the static::$null
|
6493 |
+
$default = [Type::T_KEYWORD, 'null'];
|
6494 |
+
} else {
|
6495 |
+
if (\is_null($parser)) {
|
6496 |
+
$parser = $this->parserFactory(__METHOD__);
|
6497 |
+
}
|
6498 |
+
|
6499 |
+
$parser->parseValue($defaultSource, $default);
|
6500 |
}
|
6501 |
+
}
|
6502 |
|
6503 |
+
if (substr($name, -3) === '...') {
|
6504 |
+
$restArgument = substr($name, 0, -3);
|
6505 |
+
} else {
|
6506 |
+
$arguments[] = [$name, $p[0], $default];
|
6507 |
}
|
6508 |
+
}
|
6509 |
+
|
6510 |
+
return [
|
6511 |
+
'arguments' => $arguments,
|
6512 |
+
'rest_argument' => $restArgument,
|
6513 |
+
];
|
6514 |
+
}
|
6515 |
|
6516 |
+
/**
|
6517 |
+
* Returns the function prototype for the given positional and named arguments.
|
6518 |
+
*
|
6519 |
+
* If no exact match is found, finds the closest approximation. Note that this
|
6520 |
+
* doesn't guarantee that $positional and $names are valid for the returned
|
6521 |
+
* prototype.
|
6522 |
+
*
|
6523 |
+
* @param array[] $prototypes
|
6524 |
+
* @param int $positional
|
6525 |
+
* @param array<string, string> $names A set of names, as both keys and values
|
6526 |
+
*
|
6527 |
+
* @return array
|
6528 |
+
*
|
6529 |
+
* @phpstan-param non-empty-list<array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}> $prototypes
|
6530 |
+
* @phpstan-return array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}
|
6531 |
+
*/
|
6532 |
+
private function selectFunctionPrototype(array $prototypes, $positional, array $names)
|
6533 |
+
{
|
6534 |
+
$fuzzyMatch = null;
|
6535 |
+
$minMismatchDistance = null;
|
6536 |
|
6537 |
+
foreach ($prototypes as $prototype) {
|
6538 |
+
// Ideally, find an exact match.
|
6539 |
+
if ($this->checkPrototypeMatches($prototype, $positional, $names)) {
|
6540 |
+
return $prototype;
|
6541 |
+
}
|
6542 |
|
6543 |
+
$mismatchDistance = \count($prototype['arguments']) - $positional;
|
|
|
|
|
6544 |
|
6545 |
+
if ($minMismatchDistance !== null) {
|
6546 |
+
if (abs($mismatchDistance) > abs($minMismatchDistance)) {
|
6547 |
+
continue;
|
6548 |
}
|
6549 |
|
6550 |
+
// If two overloads have the same mismatch distance, favor the overload
|
6551 |
+
// that has more arguments.
|
6552 |
+
if (abs($mismatchDistance) === abs($minMismatchDistance) && $mismatchDistance < 0) {
|
6553 |
+
continue;
|
6554 |
}
|
6555 |
+
}
|
6556 |
|
6557 |
+
$minMismatchDistance = $mismatchDistance;
|
6558 |
+
$fuzzyMatch = $prototype;
|
6559 |
+
}
|
6560 |
|
6561 |
+
return $fuzzyMatch;
|
6562 |
+
}
|
|
|
6563 |
|
6564 |
+
/**
|
6565 |
+
* Checks whether the argument invocation matches the callable prototype.
|
6566 |
+
*
|
6567 |
+
* The rules are similar to {@see verifyPrototype}. The boolean return value
|
6568 |
+
* avoids the overhead of building and catching exceptions when the reason of
|
6569 |
+
* not matching the prototype does not need to be known.
|
6570 |
+
*
|
6571 |
+
* @param array $prototype
|
6572 |
+
* @param int $positional
|
6573 |
+
* @param array<string, string> $names
|
6574 |
+
*
|
6575 |
+
* @return bool
|
6576 |
+
*
|
6577 |
+
* @phpstan-param array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null} $prototype
|
6578 |
+
*/
|
6579 |
+
private function checkPrototypeMatches(array $prototype, $positional, array $names)
|
6580 |
+
{
|
6581 |
+
$nameUsed = 0;
|
6582 |
|
6583 |
+
foreach ($prototype['arguments'] as $i => $argument) {
|
6584 |
+
list ($name, $originalName, $default) = $argument;
|
6585 |
+
|
6586 |
+
if ($i < $positional) {
|
6587 |
+
if (isset($names[$name])) {
|
6588 |
+
return false;
|
6589 |
}
|
6590 |
+
} elseif (isset($names[$name])) {
|
6591 |
+
$nameUsed++;
|
6592 |
+
} elseif ($default === null) {
|
6593 |
+
return false;
|
6594 |
}
|
|
|
6595 |
}
|
6596 |
|
6597 |
+
if ($prototype['rest_argument'] !== null) {
|
6598 |
+
return true;
|
6599 |
}
|
6600 |
|
6601 |
+
if ($positional > \count($prototype['arguments'])) {
|
6602 |
+
return false;
|
6603 |
+
}
|
6604 |
+
|
6605 |
+
if ($nameUsed < \count($names)) {
|
6606 |
+
return false;
|
6607 |
+
}
|
6608 |
+
|
6609 |
+
return true;
|
6610 |
}
|
6611 |
|
6612 |
/**
|
6613 |
+
* Verifies that the argument invocation is valid for the callable prototype.
|
6614 |
*
|
6615 |
+
* @param array $prototype
|
6616 |
+
* @param int $positional
|
6617 |
+
* @param array<string, string> $names
|
6618 |
+
* @param bool $hasSplat
|
|
|
6619 |
*
|
6620 |
+
* @return void
|
6621 |
*
|
6622 |
+
* @throws SassScriptException
|
6623 |
+
*
|
6624 |
+
* @phpstan-param array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null} $prototype
|
6625 |
*/
|
6626 |
+
private function verifyPrototype(array $prototype, $positional, array $names, $hasSplat)
|
6627 |
{
|
6628 |
+
$nameUsed = 0;
|
6629 |
|
6630 |
+
foreach ($prototype['arguments'] as $i => $argument) {
|
6631 |
+
list ($name, $originalName, $default) = $argument;
|
|
|
6632 |
|
6633 |
+
if ($i < $positional) {
|
6634 |
+
if (isset($names[$name])) {
|
6635 |
+
throw new SassScriptException(sprintf('Argument $%s was passed both by position and by name.', $originalName));
|
6636 |
+
}
|
6637 |
+
} elseif (isset($names[$name])) {
|
6638 |
+
$nameUsed++;
|
6639 |
+
} elseif ($default === null) {
|
6640 |
+
throw new SassScriptException(sprintf('Missing argument $%s', $originalName));
|
6641 |
+
}
|
6642 |
+
}
|
6643 |
|
6644 |
+
if ($prototype['rest_argument'] !== null) {
|
6645 |
+
return;
|
6646 |
}
|
6647 |
|
6648 |
+
if ($positional > \count($prototype['arguments'])) {
|
6649 |
+
$message = sprintf(
|
6650 |
+
'Only %d %sargument%s allowed, but %d %s passed.',
|
6651 |
+
\count($prototype['arguments']),
|
6652 |
+
empty($names) ? '' : 'positional ',
|
6653 |
+
\count($prototype['arguments']) === 1 ? '' : 's',
|
6654 |
+
$positional,
|
6655 |
+
$positional === 1 ? 'was' : 'were'
|
6656 |
+
);
|
6657 |
+
if (!$hasSplat) {
|
6658 |
+
throw new SassScriptException($message);
|
6659 |
+
}
|
6660 |
|
6661 |
+
$message = $this->addLocationToMessage($message);
|
6662 |
+
$message .= "\nThis will be an error in future versions of Sass.";
|
6663 |
+
$this->logger->warn($message, true);
|
6664 |
+
}
|
6665 |
|
6666 |
+
if ($nameUsed < \count($names)) {
|
6667 |
+
$unknownNames = array_values(array_diff($names, array_column($prototype['arguments'], 0)));
|
6668 |
+
$lastName = array_pop($unknownNames);
|
6669 |
+
$message = sprintf(
|
6670 |
+
'No argument%s named $%s%s.',
|
6671 |
+
$unknownNames ? 's' : '',
|
6672 |
+
$unknownNames ? implode(', $', $unknownNames) . ' or $' : '',
|
6673 |
+
$lastName
|
6674 |
+
);
|
6675 |
+
throw new SassScriptException($message);
|
6676 |
}
|
6677 |
+
}
|
6678 |
|
6679 |
+
/**
|
6680 |
+
* Evaluates the argument from the invocation.
|
6681 |
+
*
|
6682 |
+
* This returns several things about this invocation:
|
6683 |
+
* - the list of positional arguments
|
6684 |
+
* - the map of named arguments, indexed by normalized names
|
6685 |
+
* - the set of names used in the arguments (that's an array using the normalized names as keys for O(1) access)
|
6686 |
+
* - the separator used by the list using the splat operator, if any
|
6687 |
+
* - a boolean indicator whether any splat argument (list or map) was used, to support the incomplete error reporting.
|
6688 |
+
*
|
6689 |
+
* @param array[] $args
|
6690 |
+
* @param bool $reduce Whether arguments should be reduced to their value
|
6691 |
+
*
|
6692 |
+
* @return array
|
6693 |
+
*
|
6694 |
+
* @throws SassScriptException
|
6695 |
+
*
|
6696 |
+
* @phpstan-return array{0: list<array|Number>, 1: array<string, array|Number>, 2: array<string, string>, 3: string|null, 4: bool}
|
6697 |
+
*/
|
6698 |
+
private function evaluateArguments(array $args, $reduce = true)
|
6699 |
+
{
|
6700 |
+
// this represents trailing commas
|
6701 |
+
if (\count($args) && end($args) === static::$null) {
|
6702 |
+
array_pop($args);
|
6703 |
+
}
|
6704 |
+
|
6705 |
+
$splatSeparator = null;
|
6706 |
+
$keywordArgs = [];
|
6707 |
+
$names = [];
|
6708 |
+
$positionalArgs = [];
|
6709 |
+
$hasKeywordArgument = false;
|
6710 |
+
$hasSplat = false;
|
6711 |
|
6712 |
+
foreach ($args as $arg) {
|
6713 |
+
if (!empty($arg[0])) {
|
|
|
6714 |
$hasKeywordArgument = true;
|
6715 |
|
6716 |
+
assert(\is_string($arg[0][1]));
|
6717 |
+
$name = str_replace('_', '-', $arg[0][1]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6718 |
|
6719 |
+
if (isset($keywordArgs[$name])) {
|
6720 |
+
throw new SassScriptException(sprintf('Duplicate named argument $%s.', $arg[0][1]));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6721 |
}
|
6722 |
+
|
6723 |
+
$keywordArgs[$name] = $this->maybeReduce($reduce, $arg[1]);
|
6724 |
+
$names[$name] = $name;
|
6725 |
+
} elseif (! empty($arg[2])) {
|
6726 |
+
// $arg[2] means a var followed by ... in the arg ($list... )
|
6727 |
$val = $this->reduce($arg[1], true);
|
6728 |
+
$hasSplat = true;
|
6729 |
|
6730 |
if ($val[0] === Type::T_LIST) {
|
6731 |
+
foreach ($val[2] as $item) {
|
6732 |
+
if (\is_null($splatSeparator)) {
|
6733 |
+
$splatSeparator = $val[1];
|
6734 |
+
}
|
6735 |
+
|
6736 |
+
$positionalArgs[] = $this->maybeReduce($reduce, $item);
|
6737 |
+
}
|
6738 |
+
|
6739 |
+
if (isset($val[3]) && \is_array($val[3])) {
|
6740 |
+
foreach ($val[3] as $name => $item) {
|
6741 |
+
assert(\is_string($name));
|
6742 |
+
|
6743 |
+
$normalizedName = str_replace('_', '-', $name);
|
6744 |
+
|
6745 |
+
if (isset($keywordArgs[$normalizedName])) {
|
6746 |
+
throw new SassScriptException(sprintf('Duplicate named argument $%s.', $name));
|
6747 |
}
|
6748 |
|
6749 |
+
$keywordArgs[$normalizedName] = $this->maybeReduce($reduce, $item);
|
6750 |
+
$names[$normalizedName] = $normalizedName;
|
6751 |
+
$hasKeywordArgument = true;
|
6752 |
+
}
|
6753 |
+
}
|
6754 |
+
} elseif ($val[0] === Type::T_MAP) {
|
6755 |
+
foreach ($val[1] as $i => $name) {
|
6756 |
+
$name = $this->compileStringContent($this->coerceString($name));
|
6757 |
+
$item = $val[2][$i];
|
6758 |
+
|
6759 |
+
if (! is_numeric($name)) {
|
6760 |
+
$normalizedName = str_replace('_', '-', $name);
|
6761 |
+
|
6762 |
+
if (isset($keywordArgs[$normalizedName])) {
|
6763 |
+
throw new SassScriptException(sprintf('Duplicate named argument $%s.', $name));
|
6764 |
}
|
6765 |
+
|
6766 |
+
$keywordArgs[$normalizedName] = $this->maybeReduce($reduce, $item);
|
6767 |
+
$names[$normalizedName] = $normalizedName;
|
6768 |
+
$hasKeywordArgument = true;
|
6769 |
} else {
|
6770 |
if (\is_null($splatSeparator)) {
|
6771 |
$splatSeparator = $val[1];
|
6772 |
}
|
6773 |
|
6774 |
+
$positionalArgs[] = $this->maybeReduce($reduce, $item);
|
6775 |
+
}
|
6776 |
+
}
|
6777 |
+
} elseif ($val[0] !== Type::T_NULL) { // values other than null are treated a single-element lists, while null is the empty list
|
6778 |
+
$positionalArgs[] = $this->maybeReduce($reduce, $val);
|
6779 |
+
}
|
6780 |
+
} elseif ($hasKeywordArgument) {
|
6781 |
+
throw new SassScriptException('Positional arguments must come before keyword arguments.');
|
6782 |
+
} else {
|
6783 |
+
$positionalArgs[] = $this->maybeReduce($reduce, $arg[1]);
|
6784 |
+
}
|
6785 |
+
}
|
6786 |
+
|
6787 |
+
return [$positionalArgs, $keywordArgs, $names, $splatSeparator, $hasSplat];
|
6788 |
+
}
|
6789 |
+
|
6790 |
+
/**
|
6791 |
+
* @param bool $reduce
|
6792 |
+
* @param array|Number $value
|
6793 |
+
*
|
6794 |
+
* @return array|Number
|
6795 |
+
*/
|
6796 |
+
private function maybeReduce($reduce, $value)
|
6797 |
+
{
|
6798 |
+
if ($reduce) {
|
6799 |
+
return $this->reduce($value, true);
|
6800 |
+
}
|
6801 |
+
|
6802 |
+
return $value;
|
6803 |
+
}
|
6804 |
+
|
6805 |
+
/**
|
6806 |
+
* Apply argument values per definition
|
6807 |
+
*
|
6808 |
+
* @param array[] $argDef
|
6809 |
+
* @param array|null $argValues
|
6810 |
+
* @param bool $storeInEnv
|
6811 |
+
* @param bool $reduce only used if $storeInEnv = false
|
6812 |
+
*
|
6813 |
+
* @return array<string, array|Number>
|
6814 |
+
*
|
6815 |
+
* @phpstan-param list<array{0: string, 1: array|Number|null, 2: bool}> $argDef
|
6816 |
+
*
|
6817 |
+
* @throws \Exception
|
6818 |
+
*/
|
6819 |
+
protected function applyArguments($argDef, $argValues, $storeInEnv = true, $reduce = true)
|
6820 |
+
{
|
6821 |
+
$output = [];
|
6822 |
+
|
6823 |
+
if (\is_null($argValues)) {
|
6824 |
+
$argValues = [];
|
6825 |
+
}
|
6826 |
+
|
6827 |
+
if ($storeInEnv) {
|
6828 |
+
$storeEnv = $this->getStoreEnv();
|
6829 |
+
|
6830 |
+
$env = new Environment();
|
6831 |
+
$env->store = $storeEnv->store;
|
6832 |
+
}
|
6833 |
|
6834 |
+
$prototype = ['arguments' => [], 'rest_argument' => null];
|
6835 |
+
$originalRestArgumentName = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6836 |
|
6837 |
+
foreach ($argDef as $arg) {
|
6838 |
+
list($name, $default, $isVariable) = $arg;
|
6839 |
+
$normalizedName = str_replace('_', '-', $name);
|
|
|
|
|
|
|
|
|
|
|
|
|
6840 |
|
6841 |
+
if ($isVariable) {
|
6842 |
+
$originalRestArgumentName = $name;
|
6843 |
+
$prototype['rest_argument'] = $normalizedName;
|
|
|
|
|
|
|
|
|
|
|
|
|
6844 |
} else {
|
6845 |
+
$prototype['arguments'][] = [$normalizedName, $name, !empty($default) ? $default : null];
|
6846 |
}
|
6847 |
}
|
6848 |
|
6849 |
+
list($positionalArgs, $namedArgs, $names, $splatSeparator, $hasSplat) = $this->evaluateArguments($argValues, $reduce);
|
|
|
6850 |
|
6851 |
+
$this->verifyPrototype($prototype, \count($positionalArgs), $names, $hasSplat);
|
|
|
6852 |
|
6853 |
+
$vars = $this->applyArgumentsToDeclaration($prototype, $positionalArgs, $namedArgs, $splatSeparator);
|
|
|
|
|
6854 |
|
6855 |
+
foreach ($prototype['arguments'] as $argument) {
|
6856 |
+
list($normalizedName, $name) = $argument;
|
6857 |
+
|
6858 |
+
if (!isset($vars[$normalizedName])) {
|
|
|
|
|
|
|
|
|
6859 |
continue;
|
6860 |
+
}
|
6861 |
+
|
6862 |
+
$val = $vars[$normalizedName];
|
6863 |
+
|
6864 |
+
if ($storeInEnv) {
|
6865 |
+
$this->set($name, $this->reduce($val, true), true, $env);
|
6866 |
} else {
|
6867 |
+
$output[$name] = ($reduce ? $this->reduce($val, true) : $val);
|
|
|
6868 |
}
|
6869 |
+
}
|
6870 |
+
|
6871 |
+
if ($prototype['rest_argument'] !== null) {
|
6872 |
+
assert($originalRestArgumentName !== null);
|
6873 |
+
$name = $originalRestArgumentName;
|
6874 |
+
$val = $vars[$prototype['rest_argument']];
|
6875 |
|
6876 |
if ($storeInEnv) {
|
6877 |
$this->set($name, $this->reduce($val, true), true, $env);
|
6884 |
$storeEnv->store = $env->store;
|
6885 |
}
|
6886 |
|
6887 |
+
foreach ($prototype['arguments'] as $argument) {
|
6888 |
+
list($normalizedName, $name, $default) = $argument;
|
6889 |
|
6890 |
+
if (isset($vars[$normalizedName])) {
|
6891 |
continue;
|
6892 |
}
|
6893 |
+
assert($default !== null);
|
6894 |
|
6895 |
if ($storeInEnv) {
|
6896 |
$this->set($name, $this->reduce($default, true), true);
|
6902 |
return $output;
|
6903 |
}
|
6904 |
|
6905 |
+
/**
|
6906 |
+
* Apply argument values per definition.
|
6907 |
+
*
|
6908 |
+
* This method assumes that the arguments are valid for the provided prototype.
|
6909 |
+
* The validation with {@see verifyPrototype} must have been run before calling
|
6910 |
+
* it.
|
6911 |
+
* Arguments are returned as a map from the normalized argument names to the
|
6912 |
+
* value. Additional arguments are collected in a sass argument list available
|
6913 |
+
* under the name of the rest argument in the result.
|
6914 |
+
*
|
6915 |
+
* Defaults are not applied as they are resolved in a different environment.
|
6916 |
+
*
|
6917 |
+
* @param array $prototype
|
6918 |
+
* @param array<array|Number> $positionalArgs
|
6919 |
+
* @param array<string, array|Number> $namedArgs
|
6920 |
+
* @param string|null $splatSeparator
|
6921 |
+
*
|
6922 |
+
* @return array<string, array|Number>
|
6923 |
+
*
|
6924 |
+
* @phpstan-param array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null} $prototype
|
6925 |
+
*/
|
6926 |
+
private function applyArgumentsToDeclaration(array $prototype, array $positionalArgs, array $namedArgs, $splatSeparator)
|
6927 |
+
{
|
6928 |
+
$output = [];
|
6929 |
+
$minLength = min(\count($positionalArgs), \count($prototype['arguments']));
|
6930 |
+
|
6931 |
+
for ($i = 0; $i < $minLength; $i++) {
|
6932 |
+
list($name) = $prototype['arguments'][$i];
|
6933 |
+
$val = $positionalArgs[$i];
|
6934 |
+
|
6935 |
+
$output[$name] = $val;
|
6936 |
+
}
|
6937 |
+
|
6938 |
+
$restNamed = $namedArgs;
|
6939 |
+
|
6940 |
+
for ($i = \count($positionalArgs); $i < \count($prototype['arguments']); $i++) {
|
6941 |
+
$argument = $prototype['arguments'][$i];
|
6942 |
+
list($name) = $argument;
|
6943 |
+
|
6944 |
+
if (isset($namedArgs[$name])) {
|
6945 |
+
$val = $namedArgs[$name];
|
6946 |
+
unset($restNamed[$name]);
|
6947 |
+
} else {
|
6948 |
+
continue;
|
6949 |
+
}
|
6950 |
+
|
6951 |
+
$output[$name] = $val;
|
6952 |
+
}
|
6953 |
+
|
6954 |
+
if ($prototype['rest_argument'] !== null) {
|
6955 |
+
$name = $prototype['rest_argument'];
|
6956 |
+
$rest = array_values(array_slice($positionalArgs, \count($prototype['arguments'])));
|
6957 |
+
|
6958 |
+
$val = [Type::T_LIST, \is_null($splatSeparator) ? ',' : $splatSeparator , $rest, $restNamed];
|
6959 |
+
|
6960 |
+
$output[$name] = $val;
|
6961 |
+
}
|
6962 |
+
|
6963 |
+
return $output;
|
6964 |
+
}
|
6965 |
+
|
6966 |
/**
|
6967 |
* Coerce a php value into a scss one
|
6968 |
*
|
6969 |
* @param mixed $value
|
6970 |
*
|
6971 |
+
* @return array|Number
|
6972 |
*/
|
6973 |
protected function coerceValue($value)
|
6974 |
{
|
6975 |
+
if (\is_array($value) || $value instanceof Number) {
|
6976 |
return $value;
|
6977 |
}
|
6978 |
|
6985 |
}
|
6986 |
|
6987 |
if (is_numeric($value)) {
|
6988 |
+
return new Number($value, '');
|
6989 |
}
|
6990 |
|
6991 |
if ($value === '') {
|
7003 |
}
|
7004 |
|
7005 |
/**
|
7006 |
+
* Tries to convert an item to a Sass map
|
7007 |
*
|
7008 |
+
* @param Number|array $item
|
7009 |
*
|
7010 |
+
* @return array|null
|
7011 |
*/
|
7012 |
+
private function tryMap($item)
|
7013 |
{
|
7014 |
+
if ($item instanceof Number) {
|
7015 |
+
return null;
|
7016 |
+
}
|
7017 |
+
|
7018 |
if ($item[0] === Type::T_MAP) {
|
7019 |
return $item;
|
7020 |
}
|
7021 |
|
7022 |
+
if (
|
7023 |
+
$item[0] === Type::T_LIST &&
|
7024 |
+
$item[2] === []
|
7025 |
) {
|
7026 |
return static::$emptyMap;
|
7027 |
}
|
7028 |
|
7029 |
+
return null;
|
7030 |
+
}
|
7031 |
+
|
7032 |
+
/**
|
7033 |
+
* Coerce something to map
|
7034 |
+
*
|
7035 |
+
* @param array|Number $item
|
7036 |
+
*
|
7037 |
+
* @return array|Number
|
7038 |
+
*/
|
7039 |
+
protected function coerceMap($item)
|
7040 |
+
{
|
7041 |
+
$map = $this->tryMap($item);
|
7042 |
+
|
7043 |
+
if ($map !== null) {
|
7044 |
+
return $map;
|
7045 |
+
}
|
7046 |
+
|
7047 |
+
return $item;
|
7048 |
}
|
7049 |
|
7050 |
/**
|
7051 |
* Coerce something to list
|
7052 |
*
|
7053 |
+
* @param array|Number $item
|
7054 |
+
* @param string $delim
|
7055 |
+
* @param bool $removeTrailingNull
|
7056 |
*
|
7057 |
* @return array
|
7058 |
*/
|
7059 |
protected function coerceList($item, $delim = ',', $removeTrailingNull = false)
|
7060 |
{
|
7061 |
+
if ($item instanceof Number) {
|
7062 |
+
return [Type::T_LIST, '', [$item]];
|
7063 |
+
}
|
7064 |
+
|
7065 |
+
if ($item[0] === Type::T_LIST) {
|
7066 |
// remove trailing null from the list
|
7067 |
if ($removeTrailingNull && end($item[2]) === static::$null) {
|
7068 |
array_pop($item[2]);
|
7071 |
return $item;
|
7072 |
}
|
7073 |
|
7074 |
+
if ($item[0] === Type::T_MAP) {
|
7075 |
$keys = $item[1];
|
7076 |
$values = $item[2];
|
7077 |
$list = [];
|
7080 |
$key = $keys[$i];
|
7081 |
$value = $values[$i];
|
7082 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7083 |
$list[] = [
|
7084 |
Type::T_LIST,
|
7085 |
+
' ',
|
7086 |
[$key, $value]
|
7087 |
];
|
7088 |
}
|
7089 |
|
7090 |
+
return [Type::T_LIST, $list ? ',' : '', $list];
|
7091 |
}
|
7092 |
|
7093 |
+
return [Type::T_LIST, '', [$item]];
|
7094 |
}
|
7095 |
|
7096 |
/**
|
7097 |
* Coerce color for expression
|
7098 |
*
|
7099 |
+
* @param array|Number $value
|
7100 |
*
|
7101 |
+
* @return array|Number
|
7102 |
*/
|
7103 |
protected function coerceForExpression($value)
|
7104 |
{
|
7112 |
/**
|
7113 |
* Coerce value to color
|
7114 |
*
|
7115 |
+
* @param array|Number $value
|
7116 |
+
* @param bool $inRGBFunction
|
7117 |
*
|
7118 |
* @return array|null
|
7119 |
*/
|
7120 |
protected function coerceColor($value, $inRGBFunction = false)
|
7121 |
{
|
7122 |
+
if ($value instanceof Number) {
|
7123 |
+
return null;
|
7124 |
+
}
|
7125 |
+
|
7126 |
switch ($value[0]) {
|
7127 |
case Type::T_COLOR:
|
7128 |
for ($i = 1; $i <= 3; $i++) {
|
7208 |
if ($color[3] === 255) {
|
7209 |
$color[3] = 1; // fully opaque
|
7210 |
} else {
|
7211 |
+
$color[3] = round($color[3] / 255, Number::PRECISION);
|
7212 |
}
|
7213 |
}
|
7214 |
|
7231 |
}
|
7232 |
|
7233 |
/**
|
7234 |
+
* @param int|Number $value
|
7235 |
+
* @param bool $isAlpha
|
7236 |
*
|
7237 |
+
* @return int|mixed
|
7238 |
*/
|
7239 |
protected function compileRGBAValue($value, $isAlpha = false)
|
7240 |
{
|
7246 |
}
|
7247 |
|
7248 |
/**
|
7249 |
+
* @param mixed $value
|
7250 |
+
* @param int|float $min
|
7251 |
+
* @param int|float $max
|
7252 |
+
* @param bool $isInt
|
|
|
|
|
7253 |
*
|
7254 |
+
* @return int|mixed
|
7255 |
*/
|
7256 |
+
protected function compileColorPartValue($value, $min, $max, $isInt = true)
|
7257 |
{
|
7258 |
if (! is_numeric($value)) {
|
7259 |
if (\is_array($value)) {
|
7260 |
$reduced = $this->reduce($value);
|
7261 |
|
7262 |
+
if ($reduced instanceof Number) {
|
7263 |
$value = $reduced;
|
7264 |
}
|
7265 |
}
|
7266 |
|
7267 |
+
if ($value instanceof Number) {
|
7268 |
+
if ($value->unitless()) {
|
7269 |
+
$num = $value->getDimension();
|
7270 |
+
} elseif ($value->hasUnit('%')) {
|
7271 |
+
$num = $max * $value->getDimension() / 100;
|
7272 |
+
} else {
|
7273 |
+
throw $this->error('Expected %s to have no units or "%%".', $value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7274 |
}
|
7275 |
|
7276 |
$value = $num;
|
7284 |
$value = round($value);
|
7285 |
}
|
7286 |
|
7287 |
+
$value = min($max, max($min, $value));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7288 |
|
7289 |
return $value;
|
7290 |
}
|
7295 |
/**
|
7296 |
* Coerce value to string
|
7297 |
*
|
7298 |
+
* @param array|Number $value
|
7299 |
*
|
7300 |
+
* @return array
|
7301 |
*/
|
7302 |
protected function coerceString($value)
|
7303 |
{
|
7304 |
if ($value[0] === Type::T_STRING) {
|
7305 |
+
assert(\is_array($value));
|
7306 |
+
|
7307 |
return $value;
|
7308 |
}
|
7309 |
|
7310 |
return [Type::T_STRING, '', [$this->compileValue($value)]];
|
7311 |
}
|
7312 |
|
7313 |
+
/**
|
7314 |
+
* Assert value is a string
|
7315 |
+
*
|
7316 |
+
* This method deals with internal implementation details of the value
|
7317 |
+
* representation where unquoted strings can sometimes be stored under
|
7318 |
+
* other types.
|
7319 |
+
* The returned value is always using the T_STRING type.
|
7320 |
+
*
|
7321 |
+
* @api
|
7322 |
+
*
|
7323 |
+
* @param array|Number $value
|
7324 |
+
* @param string|null $varName
|
7325 |
+
*
|
7326 |
+
* @return array
|
7327 |
+
*
|
7328 |
+
* @throws SassScriptException
|
7329 |
+
*/
|
7330 |
+
public function assertString($value, $varName = null)
|
7331 |
+
{
|
7332 |
+
// case of url(...) parsed a a function
|
7333 |
+
if ($value[0] === Type::T_FUNCTION) {
|
7334 |
+
$value = $this->coerceString($value);
|
7335 |
+
}
|
7336 |
+
|
7337 |
+
if (! \in_array($value[0], [Type::T_STRING, Type::T_KEYWORD])) {
|
7338 |
+
$value = $this->compileValue($value);
|
7339 |
+
throw SassScriptException::forArgument("$value is not a string.", $varName);
|
7340 |
+
}
|
7341 |
+
|
7342 |
+
return $this->coerceString($value);
|
7343 |
+
}
|
7344 |
+
|
7345 |
/**
|
7346 |
* Coerce value to a percentage
|
7347 |
*
|
7348 |
+
* @param array|Number $value
|
7349 |
*
|
7350 |
+
* @return int|float
|
7351 |
+
*
|
7352 |
+
* @deprecated
|
7353 |
*/
|
7354 |
protected function coercePercent($value)
|
7355 |
{
|
7356 |
+
@trigger_error(sprintf('"%s" is deprecated since 1.7.0.', __METHOD__), E_USER_DEPRECATED);
|
7357 |
+
|
7358 |
+
if ($value instanceof Number) {
|
7359 |
+
if ($value->hasUnit('%')) {
|
7360 |
+
return $value->getDimension() / 100;
|
7361 |
}
|
7362 |
|
7363 |
+
return $value->getDimension();
|
7364 |
}
|
7365 |
|
7366 |
return 0;
|
7371 |
*
|
7372 |
* @api
|
7373 |
*
|
7374 |
+
* @param array|Number $value
|
7375 |
+
* @param string|null $varName
|
7376 |
*
|
7377 |
* @return array
|
7378 |
*
|
7379 |
+
* @throws SassScriptException
|
7380 |
*/
|
7381 |
+
public function assertMap($value, $varName = null)
|
7382 |
{
|
7383 |
+
$map = $this->tryMap($value);
|
7384 |
|
7385 |
+
if ($map === null) {
|
7386 |
+
$value = $this->compileValue($value);
|
7387 |
+
|
7388 |
+
throw SassScriptException::forArgument("$value is not a map.", $varName);
|
7389 |
}
|
7390 |
|
7391 |
+
return $map;
|
7392 |
}
|
7393 |
|
7394 |
/**
|
7396 |
*
|
7397 |
* @api
|
7398 |
*
|
7399 |
+
* @param array|Number $value
|
7400 |
*
|
7401 |
* @return array
|
7402 |
*
|
7405 |
public function assertList($value)
|
7406 |
{
|
7407 |
if ($value[0] !== Type::T_LIST) {
|
7408 |
+
throw $this->error('expecting list, %s received', $value[0]);
|
7409 |
}
|
7410 |
+
assert(\is_array($value));
|
7411 |
|
7412 |
return $value;
|
7413 |
}
|
7414 |
|
7415 |
+
/**
|
7416 |
+
* Gets the keywords of an argument list.
|
7417 |
+
*
|
7418 |
+
* Keys in the returned array are normalized names (underscores are replaced with dashes)
|
7419 |
+
* without the leading `$`.
|
7420 |
+
* Calling this helper with anything that an argument list received for a rest argument
|
7421 |
+
* of the function argument declaration is not supported.
|
7422 |
+
*
|
7423 |
+
* @param array|Number $value
|
7424 |
+
*
|
7425 |
+
* @return array<string, array|Number>
|
7426 |
+
*/
|
7427 |
+
public function getArgumentListKeywords($value)
|
7428 |
+
{
|
7429 |
+
if ($value[0] !== Type::T_LIST || !isset($value[3]) || !\is_array($value[3])) {
|
7430 |
+
throw new \InvalidArgumentException('The argument is not a sass argument list.');
|
7431 |
+
}
|
7432 |
+
|
7433 |
+
return $value[3];
|
7434 |
+
}
|
7435 |
+
|
7436 |
/**
|
7437 |
* Assert value is a color
|
7438 |
*
|
7439 |
* @api
|
7440 |
*
|
7441 |
+
* @param array|Number $value
|
7442 |
+
* @param string|null $varName
|
7443 |
*
|
7444 |
* @return array
|
7445 |
*
|
7446 |
+
* @throws SassScriptException
|
7447 |
*/
|
7448 |
+
public function assertColor($value, $varName = null)
|
7449 |
{
|
7450 |
if ($color = $this->coerceColor($value)) {
|
7451 |
return $color;
|
7452 |
}
|
7453 |
|
7454 |
+
$value = $this->compileValue($value);
|
7455 |
+
|
7456 |
+
throw SassScriptException::forArgument("$value is not a color.", $varName);
|
7457 |
}
|
7458 |
|
7459 |
/**
|
7461 |
*
|
7462 |
* @api
|
7463 |
*
|
7464 |
+
* @param array|Number $value
|
7465 |
+
* @param string|null $varName
|
7466 |
*
|
7467 |
+
* @return Number
|
7468 |
*
|
7469 |
+
* @throws SassScriptException
|
7470 |
+
*/
|
7471 |
+
public function assertNumber($value, $varName = null)
|
7472 |
+
{
|
7473 |
+
if (!$value instanceof Number) {
|
7474 |
+
$value = $this->compileValue($value);
|
7475 |
+
throw SassScriptException::forArgument("$value is not a number.", $varName);
|
7476 |
+
}
|
7477 |
+
|
7478 |
+
return $value;
|
7479 |
+
}
|
7480 |
+
|
7481 |
+
/**
|
7482 |
+
* Assert value is a integer
|
7483 |
+
*
|
7484 |
+
* @api
|
7485 |
+
*
|
7486 |
+
* @param array|Number $value
|
7487 |
+
* @param string|null $varName
|
7488 |
+
*
|
7489 |
+
* @return int
|
7490 |
+
*
|
7491 |
+
* @throws SassScriptException
|
7492 |
*/
|
7493 |
+
public function assertInteger($value, $varName = null)
|
7494 |
{
|
7495 |
+
$value = $this->assertNumber($value, $varName)->getDimension();
|
7496 |
+
if (round($value - \intval($value), Number::PRECISION) > 0) {
|
7497 |
+
throw SassScriptException::forArgument("$value is not an integer.", $varName);
|
7498 |
}
|
7499 |
|
7500 |
+
return intval($value);
|
7501 |
}
|
7502 |
|
7503 |
+
/**
|
7504 |
+
* Extract the ... / alpha on the last argument of channel arg
|
7505 |
+
* in color functions
|
7506 |
+
*
|
7507 |
+
* @param array $args
|
7508 |
+
* @return array
|
7509 |
+
*/
|
7510 |
+
private function extractSlashAlphaInColorFunction($args)
|
7511 |
+
{
|
7512 |
+
$last = end($args);
|
7513 |
+
if (\count($args) === 3 && $last[0] === Type::T_EXPRESSION && $last[1] === '/') {
|
7514 |
+
array_pop($args);
|
7515 |
+
$args[] = $last[2];
|
7516 |
+
$args[] = $last[3];
|
7517 |
+
}
|
7518 |
+
return $args;
|
7519 |
+
}
|
7520 |
+
|
7521 |
+
|
7522 |
/**
|
7523 |
* Make sure a color's components don't go out of bounds
|
7524 |
*
|
7536 |
if ($c[$i] > 255) {
|
7537 |
$c[$i] = 255;
|
7538 |
}
|
7539 |
+
|
7540 |
+
if (!\is_int($c[$i])) {
|
7541 |
+
$c[$i] = round($c[$i]);
|
7542 |
+
}
|
7543 |
}
|
7544 |
|
7545 |
return $c;
|
7548 |
/**
|
7549 |
* Convert RGB to HSL
|
7550 |
*
|
7551 |
+
* @internal
|
7552 |
*
|
7553 |
+
* @param int $red
|
7554 |
+
* @param int $green
|
7555 |
+
* @param int $blue
|
7556 |
*
|
7557 |
* @return array
|
7558 |
*/
|
7577 |
$h = 60 * ($green - $blue) / $d;
|
7578 |
} elseif ($green == $max) {
|
7579 |
$h = 60 * ($blue - $red) / $d + 120;
|
7580 |
+
} else {
|
7581 |
$h = 60 * ($red - $green) / $d + 240;
|
7582 |
}
|
7583 |
}
|
7584 |
|
7585 |
+
return [Type::T_HSL, fmod($h + 360, 360), $s * 100, $l / 5.1];
|
7586 |
}
|
7587 |
|
7588 |
/**
|
7611 |
}
|
7612 |
|
7613 |
if ($h * 3 < 2) {
|
7614 |
+
return $m1 + ($m2 - $m1) * (2 / 3 - $h) * 6;
|
7615 |
}
|
7616 |
|
7617 |
return $m1;
|
7620 |
/**
|
7621 |
* Convert HSL to RGB
|
7622 |
*
|
7623 |
+
* @internal
|
7624 |
*
|
7625 |
+
* @param int|float $hue H from 0 to 360
|
7626 |
+
* @param int|float $saturation S from 0 to 100
|
7627 |
+
* @param int|float $lightness L from 0 to 100
|
7628 |
*
|
7629 |
* @return array
|
7630 |
*/
|
7641 |
$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
|
7642 |
$m1 = $l * 2 - $m2;
|
7643 |
|
7644 |
+
$r = $this->hueToRGB($m1, $m2, $h + 1 / 3) * 255;
|
7645 |
$g = $this->hueToRGB($m1, $m2, $h) * 255;
|
7646 |
+
$b = $this->hueToRGB($m1, $m2, $h - 1 / 3) * 255;
|
7647 |
|
7648 |
$out = [Type::T_COLOR, $r, $g, $b];
|
7649 |
|
7650 |
return $out;
|
7651 |
}
|
7652 |
|
7653 |
+
/**
|
7654 |
+
* Convert HWB to RGB
|
7655 |
+
* https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
7656 |
+
*
|
7657 |
+
* @api
|
7658 |
+
*
|
7659 |
+
* @param int|float $hue H from 0 to 360
|
7660 |
+
* @param int|float $whiteness W from 0 to 100
|
7661 |
+
* @param int|float $blackness B from 0 to 100
|
7662 |
+
*
|
7663 |
+
* @return array
|
7664 |
+
*/
|
7665 |
+
private function HWBtoRGB($hue, $whiteness, $blackness)
|
7666 |
+
{
|
7667 |
+
$w = min(100, max(0, $whiteness)) / 100;
|
7668 |
+
$b = min(100, max(0, $blackness)) / 100;
|
7669 |
|
7670 |
+
$sum = $w + $b;
|
7671 |
+
if ($sum > 1.0) {
|
7672 |
+
$w = $w / $sum;
|
7673 |
+
$b = $b / $sum;
|
7674 |
+
}
|
7675 |
+
$b = min(1.0 - $w, $b);
|
7676 |
+
|
7677 |
+
$rgb = $this->toRGB($hue, 100, 50);
|
7678 |
+
for($i = 1; $i < 4; $i++) {
|
7679 |
+
$rgb[$i] *= (1.0 - $w - $b);
|
7680 |
+
$rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001);
|
7681 |
+
}
|
7682 |
+
|
7683 |
+
return $rgb;
|
7684 |
+
}
|
7685 |
+
|
7686 |
+
/**
|
7687 |
+
* Convert RGB to HWB
|
7688 |
+
*
|
7689 |
+
* @api
|
7690 |
+
*
|
7691 |
+
* @param int $red
|
7692 |
+
* @param int $green
|
7693 |
+
* @param int $blue
|
7694 |
+
*
|
7695 |
+
* @return array
|
7696 |
+
*/
|
7697 |
+
private function RGBtoHWB($red, $green, $blue)
|
7698 |
{
|
7699 |
+
$min = min($red, $green, $blue);
|
7700 |
+
$max = max($red, $green, $blue);
|
7701 |
+
|
7702 |
+
$d = $max - $min;
|
7703 |
+
|
7704 |
+
if ((int) $d === 0) {
|
7705 |
+
$h = 0;
|
7706 |
+
} else {
|
7707 |
|
7708 |
+
if ($red == $max) {
|
7709 |
+
$h = 60 * ($green - $blue) / $d;
|
7710 |
+
} elseif ($green == $max) {
|
7711 |
+
$h = 60 * ($blue - $red) / $d + 120;
|
7712 |
} else {
|
7713 |
+
$h = 60 * ($red - $green) / $d + 240;
|
7714 |
}
|
7715 |
+
}
|
7716 |
+
|
7717 |
+
return [Type::T_HWB, fmod($h, 360), $min / 255 * 100, 100 - $max / 255 *100];
|
7718 |
+
}
|
7719 |
+
|
7720 |
+
|
7721 |
+
// Built in functions
|
7722 |
+
|
7723 |
+
protected static $libCall = ['function', 'args...'];
|
7724 |
+
protected function libCall($args)
|
7725 |
+
{
|
7726 |
+
$functionReference = $args[0];
|
7727 |
+
|
7728 |
+
if (in_array($functionReference[0], [Type::T_STRING, Type::T_KEYWORD])) {
|
7729 |
+
$name = $this->compileStringContent($this->coerceString($functionReference));
|
7730 |
+
$warning = "Passing a string to call() is deprecated and will be illegal\n"
|
7731 |
+
. "in Sass 4.0. Use call(function-reference($name)) instead.";
|
7732 |
+
Warn::deprecation($warning);
|
7733 |
+
$functionReference = $this->libGetFunction([$this->assertString($functionReference, 'function')]);
|
7734 |
+
}
|
7735 |
+
|
7736 |
+
if ($functionReference === static::$null) {
|
7737 |
+
return static::$null;
|
7738 |
+
}
|
7739 |
+
|
7740 |
+
if (! in_array($functionReference[0], [Type::T_FUNCTION_REFERENCE, Type::T_FUNCTION])) {
|
7741 |
+
throw $this->error('Function reference expected, got ' . $functionReference[0]);
|
7742 |
+
}
|
7743 |
|
7744 |
+
$callArgs = [
|
7745 |
+
[null, $args[1], true]
|
7746 |
+
];
|
7747 |
+
|
7748 |
+
return $this->reduce([Type::T_FUNCTION_CALL, $functionReference, $callArgs]);
|
7749 |
+
}
|
7750 |
+
|
7751 |
+
|
7752 |
+
protected static $libGetFunction = [
|
7753 |
+
['name'],
|
7754 |
+
['name', 'css']
|
7755 |
+
];
|
7756 |
+
protected function libGetFunction($args)
|
7757 |
+
{
|
7758 |
+
$name = $this->compileStringContent($this->assertString(array_shift($args), 'name'));
|
7759 |
+
$isCss = false;
|
7760 |
+
|
7761 |
+
if (count($args)) {
|
7762 |
+
$isCss = array_shift($args);
|
7763 |
+
$isCss = (($isCss === static::$true) ? true : false);
|
7764 |
}
|
7765 |
|
7766 |
+
if ($isCss) {
|
7767 |
+
return [Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]];
|
7768 |
+
}
|
7769 |
+
|
7770 |
+
return $this->getFunctionReference($name, true);
|
7771 |
}
|
7772 |
|
7773 |
protected static $libIf = ['condition', 'if-true', 'if-false:'];
|
7787 |
{
|
7788 |
list($list, $value) = $args;
|
7789 |
|
7790 |
+
if (
|
7791 |
+
$list[0] === Type::T_MAP ||
|
|
|
|
|
|
|
7792 |
$list[0] === Type::T_STRING ||
|
7793 |
$list[0] === Type::T_KEYWORD ||
|
7794 |
$list[0] === Type::T_INTERPOLATE
|
7796 |
$list = $this->coerceList($list, ' ');
|
7797 |
}
|
7798 |
|
7799 |
+
if ($list[0] !== Type::T_LIST) {
|
7800 |
+
return static::$null;
|
7801 |
+
}
|
7802 |
+
|
7803 |
+
// Numbers are represented with value objects, for which the PHP equality operator does not
|
7804 |
+
// match the Sass rules (and we cannot overload it). As they are the only type of values
|
7805 |
+
// represented with a value object for now, they require a special case.
|
7806 |
+
if ($value instanceof Number) {
|
7807 |
+
$key = 0;
|
7808 |
+
foreach ($list[2] as $item) {
|
7809 |
+
$key++;
|
7810 |
+
$itemValue = $this->normalizeValue($item);
|
7811 |
+
|
7812 |
+
if ($itemValue instanceof Number && $value->equals($itemValue)) {
|
7813 |
+
return new Number($key, '');
|
7814 |
+
}
|
7815 |
+
}
|
7816 |
return static::$null;
|
7817 |
}
|
7818 |
|
7824 |
|
7825 |
$key = array_search($this->normalizeValue($value), $values);
|
7826 |
|
7827 |
+
return false === $key ? static::$null : new Number($key + 1, '');
|
7828 |
}
|
7829 |
|
7830 |
protected static $libRgb = [
|
7833 |
['channels'],
|
7834 |
['red', 'green', 'blue'],
|
7835 |
['red', 'green', 'blue', 'alpha'] ];
|
7836 |
+
|
7837 |
+
/**
|
7838 |
+
* @param array $args
|
7839 |
+
* @param array $kwargs
|
7840 |
+
* @param string $funcName
|
7841 |
+
*
|
7842 |
+
* @return array
|
7843 |
+
*/
|
7844 |
protected function libRgb($args, $kwargs, $funcName = 'rgb')
|
7845 |
{
|
7846 |
switch (\count($args)) {
|
7854 |
$color = [Type::T_COLOR, $args[0], $args[1], $args[2]];
|
7855 |
|
7856 |
if (! $color = $this->coerceColor($color)) {
|
7857 |
+
$color = [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ')']];
|
7858 |
}
|
7859 |
|
7860 |
return $color;
|
7870 |
[$funcName . '(', $color[1], ', ', $color[2], ', ', $color[3], ', ', $alpha, ')']];
|
7871 |
}
|
7872 |
} else {
|
7873 |
+
$color = [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ')']];
|
7874 |
}
|
7875 |
break;
|
7876 |
|
7899 |
return $this->libRgb($args, $kwargs, 'rgba');
|
7900 |
}
|
7901 |
|
7902 |
+
/**
|
7903 |
+
* Helper function for adjust_color, change_color, and scale_color
|
7904 |
+
*
|
7905 |
+
* @param array<array|Number> $args
|
7906 |
+
* @param string $operation
|
7907 |
+
* @param callable $fn
|
7908 |
+
*
|
7909 |
+
* @return array
|
7910 |
+
*
|
7911 |
+
* @phpstan-param callable(float|int, float|int|null, float|int): (float|int) $fn
|
7912 |
+
*/
|
7913 |
+
protected function alterColor(array $args, $operation, $fn)
|
7914 |
{
|
7915 |
+
$color = $this->assertColor($args[0], 'color');
|
7916 |
+
|
7917 |
+
if ($args[1][2]) {
|
7918 |
+
throw new SassScriptException('Only one positional argument is allowed. All other arguments must be passed by name.');
|
7919 |
+
}
|
7920 |
+
|
7921 |
+
$kwargs = $this->getArgumentListKeywords($args[1]);
|
7922 |
+
|
7923 |
+
$scale = $operation === 'scale';
|
7924 |
+
$change = $operation === 'change';
|
7925 |
+
|
7926 |
+
/** @phpstan-var callable(string, float|int, bool=, bool=): (float|int|null) $getParam */
|
7927 |
+
$getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) {
|
7928 |
+
if (!isset($kwargs[$name])) {
|
7929 |
+
return null;
|
7930 |
+
}
|
7931 |
|
7932 |
+
$number = $this->assertNumber($kwargs[$name], $name);
|
7933 |
+
unset($kwargs[$name]);
|
|
|
7934 |
|
7935 |
+
if (!$scale && $checkPercent) {
|
7936 |
+
if (!$number->hasUnit('%')) {
|
7937 |
+
$warning = $this->error("{$name} Passing a number `$number` without unit % is deprecated.");
|
7938 |
+
$this->logger->warn($warning->getMessage(), true);
|
7939 |
}
|
7940 |
+
}
|
7941 |
+
|
7942 |
+
if ($scale || $assertPercent) {
|
7943 |
+
$number->assertUnit('%', $name);
|
7944 |
+
}
|
7945 |
|
7946 |
+
if ($scale) {
|
7947 |
+
$max = 100;
|
7948 |
}
|
7949 |
+
|
7950 |
+
return $number->valueInRange($change ? 0 : -$max, $max, $name);
|
7951 |
+
};
|
7952 |
+
|
7953 |
+
$alpha = $getParam('alpha', 1);
|
7954 |
+
$red = $getParam('red', 255);
|
7955 |
+
$green = $getParam('green', 255);
|
7956 |
+
$blue = $getParam('blue', 255);
|
7957 |
+
|
7958 |
+
if ($scale || !isset($kwargs['hue'])) {
|
7959 |
+
$hue = null;
|
7960 |
+
} else {
|
7961 |
+
$hueNumber = $this->assertNumber($kwargs['hue'], 'hue');
|
7962 |
+
unset($kwargs['hue']);
|
7963 |
+
$hue = $hueNumber->getDimension();
|
7964 |
+
}
|
7965 |
+
$saturation = $getParam('saturation', 100, true);
|
7966 |
+
$lightness = $getParam('lightness', 100, true);
|
7967 |
+
$whiteness = $getParam('whiteness', 100, false, true);
|
7968 |
+
$blackness = $getParam('blackness', 100, false, true);
|
7969 |
+
|
7970 |
+
if (!empty($kwargs)) {
|
7971 |
+
$unknownNames = array_keys($kwargs);
|
7972 |
+
$lastName = array_pop($unknownNames);
|
7973 |
+
$message = sprintf(
|
7974 |
+
'No argument%s named $%s%s.',
|
7975 |
+
$unknownNames ? 's' : '',
|
7976 |
+
$unknownNames ? implode(', $', $unknownNames) . ' or $' : '',
|
7977 |
+
$lastName
|
7978 |
+
);
|
7979 |
+
throw new SassScriptException($message);
|
7980 |
+
}
|
7981 |
+
|
7982 |
+
$hasRgb = $red !== null || $green !== null || $blue !== null;
|
7983 |
+
$hasSL = $saturation !== null || $lightness !== null;
|
7984 |
+
$hasWB = $whiteness !== null || $blackness !== null;
|
7985 |
+
|
7986 |
+
if ($hasRgb && ($hasSL || $hasWB || $hue !== null)) {
|
7987 |
+
throw new SassScriptException(sprintf('RGB parameters may not be passed along with %s parameters.', $hasWB ? 'HWB' : 'HSL'));
|
7988 |
+
}
|
7989 |
+
|
7990 |
+
if ($hasWB && $hasSL) {
|
7991 |
+
throw new SassScriptException('HSL parameters may not be passed along with HWB parameters.');
|
7992 |
}
|
7993 |
|
7994 |
+
if ($hasRgb) {
|
7995 |
+
$color[1] = round($fn($color[1], $red, 255));
|
7996 |
+
$color[2] = round($fn($color[2], $green, 255));
|
7997 |
+
$color[3] = round($fn($color[3], $blue, 255));
|
7998 |
+
} elseif ($hasWB) {
|
7999 |
+
$hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]);
|
8000 |
+
if ($hue !== null) {
|
8001 |
+
$hwb[1] = $change ? $hue : $hwb[1] + $hue;
|
8002 |
+
}
|
8003 |
+
$hwb[2] = $fn($hwb[2], $whiteness, 100);
|
8004 |
+
$hwb[3] = $fn($hwb[3], $blackness, 100);
|
8005 |
+
|
8006 |
+
$rgb = $this->HWBtoRGB($hwb[1], $hwb[2], $hwb[3]);
|
8007 |
+
|
8008 |
+
if (isset($color[4])) {
|
8009 |
+
$rgb[4] = $color[4];
|
8010 |
+
}
|
8011 |
+
|
8012 |
+
$color = $rgb;
|
8013 |
+
} elseif ($hue !== null || $hasSL) {
|
8014 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
8015 |
|
8016 |
+
if ($hue !== null) {
|
8017 |
+
$hsl[1] = $change ? $hue : $hsl[1] + $hue;
|
|
|
|
|
|
|
8018 |
}
|
8019 |
+
$hsl[2] = $fn($hsl[2], $saturation, 100);
|
8020 |
+
$hsl[3] = $fn($hsl[3], $lightness, 100);
|
8021 |
|
8022 |
$rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
|
8023 |
|
8028 |
$color = $rgb;
|
8029 |
}
|
8030 |
|
8031 |
+
if ($alpha !== null) {
|
8032 |
+
$existingAlpha = isset($color[4]) ? $color[4] : 1;
|
8033 |
+
$color[4] = $fn($existingAlpha, $alpha, 1);
|
8034 |
+
}
|
8035 |
+
|
8036 |
return $color;
|
8037 |
}
|
8038 |
|
8039 |
+
protected static $libAdjustColor = ['color', 'kwargs...'];
|
|
|
|
|
|
|
8040 |
protected function libAdjustColor($args)
|
8041 |
{
|
8042 |
+
return $this->alterColor($args, 'adjust', function ($base, $alter, $max) {
|
8043 |
+
if ($alter === null) {
|
8044 |
+
return $base;
|
8045 |
+
}
|
8046 |
+
|
8047 |
+
$new = $base + $alter;
|
8048 |
+
|
8049 |
+
if ($new < 0) {
|
8050 |
+
return 0;
|
8051 |
+
}
|
8052 |
+
|
8053 |
+
if ($new > $max) {
|
8054 |
+
return $max;
|
8055 |
+
}
|
8056 |
+
|
8057 |
+
return $new;
|
8058 |
});
|
8059 |
}
|
8060 |
|
8061 |
+
protected static $libChangeColor = ['color', 'kwargs...'];
|
|
|
|
|
|
|
8062 |
protected function libChangeColor($args)
|
8063 |
{
|
8064 |
+
return $this->alterColor($args,'change', function ($base, $alter, $max) {
|
8065 |
+
if ($alter === null) {
|
8066 |
+
return $base;
|
8067 |
+
}
|
8068 |
+
|
8069 |
return $alter;
|
8070 |
});
|
8071 |
}
|
8072 |
|
8073 |
+
protected static $libScaleColor = ['color', 'kwargs...'];
|
|
|
|
|
|
|
8074 |
protected function libScaleColor($args)
|
8075 |
{
|
8076 |
+
return $this->alterColor($args, 'scale', function ($base, $scale, $max) {
|
8077 |
+
if ($scale === null) {
|
8078 |
+
return $base;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8079 |
}
|
8080 |
|
8081 |
$scale = $scale / 100;
|
8092 |
protected function libIeHexStr($args)
|
8093 |
{
|
8094 |
$color = $this->coerceColor($args[0]);
|
8095 |
+
|
8096 |
+
if (\is_null($color)) {
|
8097 |
+
throw $this->error('Error: argument `$color` of `ie-hex-str($color)` must be a color');
|
8098 |
+
}
|
8099 |
+
|
8100 |
$color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
|
8101 |
|
8102 |
return [Type::T_STRING, '', [sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3])]];
|
8107 |
{
|
8108 |
$color = $this->coerceColor($args[0]);
|
8109 |
|
8110 |
+
if (\is_null($color)) {
|
8111 |
+
throw $this->error('Error: argument `$color` of `red($color)` must be a color');
|
8112 |
+
}
|
8113 |
+
|
8114 |
+
return new Number((int) $color[1], '');
|
8115 |
}
|
8116 |
|
8117 |
protected static $libGreen = ['color'];
|
8119 |
{
|
8120 |
$color = $this->coerceColor($args[0]);
|
8121 |
|
8122 |
+
if (\is_null($color)) {
|
8123 |
+
throw $this->error('Error: argument `$color` of `green($color)` must be a color');
|
8124 |
+
}
|
8125 |
+
|
8126 |
+
return new Number((int) $color[2], '');
|
8127 |
}
|
8128 |
|
8129 |
protected static $libBlue = ['color'];
|
8131 |
{
|
8132 |
$color = $this->coerceColor($args[0]);
|
8133 |
|
8134 |
+
if (\is_null($color)) {
|
8135 |
+
throw $this->error('Error: argument `$color` of `blue($color)` must be a color');
|
8136 |
+
}
|
8137 |
+
|
8138 |
+
return new Number((int) $color[3], '');
|
8139 |
}
|
8140 |
|
8141 |
protected static $libAlpha = ['color'];
|
8142 |
protected function libAlpha($args)
|
8143 |
{
|
8144 |
if ($color = $this->coerceColor($args[0])) {
|
8145 |
+
return new Number(isset($color[4]) ? $color[4] : 1, '');
|
8146 |
}
|
8147 |
|
8148 |
// this might be the IE function, so return value unchanged
|
8154 |
{
|
8155 |
$value = $args[0];
|
8156 |
|
8157 |
+
if ($value instanceof Number) {
|
8158 |
return null;
|
8159 |
}
|
8160 |
|
8162 |
}
|
8163 |
|
8164 |
// mix two colors
|
8165 |
+
protected static $libMix = [
|
8166 |
+
['color1', 'color2', 'weight:50%'],
|
8167 |
+
['color-1', 'color-2', 'weight:50%']
|
8168 |
+
];
|
8169 |
protected function libMix($args)
|
8170 |
{
|
8171 |
list($first, $second, $weight) = $args;
|
8172 |
|
8173 |
+
$first = $this->assertColor($first, 'color1');
|
8174 |
+
$second = $this->assertColor($second, 'color2');
|
8175 |
+
$weightScale = $this->assertNumber($weight, 'weight')->valueInRange(0, 100, 'weight') / 100;
|
|
|
|
|
|
|
|
|
|
|
8176 |
|
8177 |
$firstAlpha = isset($first[4]) ? $first[4] : 1;
|
8178 |
$secondAlpha = isset($second[4]) ? $second[4] : 1;
|
8179 |
|
8180 |
+
$normalizedWeight = $weightScale * 2 - 1;
|
8181 |
+
$alphaDistance = $firstAlpha - $secondAlpha;
|
8182 |
|
8183 |
+
$combinedWeight = $normalizedWeight * $alphaDistance == -1 ? $normalizedWeight : ($normalizedWeight + $alphaDistance) / (1 + $normalizedWeight * $alphaDistance);
|
8184 |
+
$weight1 = ($combinedWeight + 1) / 2.0;
|
8185 |
+
$weight2 = 1.0 - $weight1;
|
8186 |
|
8187 |
$new = [Type::T_COLOR,
|
8188 |
+
$weight1 * $first[1] + $weight2 * $second[1],
|
8189 |
+
$weight1 * $first[2] + $weight2 * $second[2],
|
8190 |
+
$weight1 * $first[3] + $weight2 * $second[3],
|
8191 |
];
|
8192 |
|
8193 |
if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
|
8194 |
+
$new[] = $firstAlpha * $weightScale + $secondAlpha * (1 - $weightScale);
|
8195 |
}
|
8196 |
|
8197 |
return $this->fixColor($new);
|
8198 |
}
|
8199 |
|
8200 |
+
protected static $libHsl = [
|
8201 |
['channels'],
|
8202 |
+
['hue', 'saturation'],
|
8203 |
['hue', 'saturation', 'lightness'],
|
8204 |
['hue', 'saturation', 'lightness', 'alpha'] ];
|
8205 |
+
|
8206 |
+
/**
|
8207 |
+
* @param array $args
|
8208 |
+
* @param array $kwargs
|
8209 |
+
* @param string $funcName
|
8210 |
+
*
|
8211 |
+
* @return array|null
|
8212 |
+
*/
|
8213 |
protected function libHsl($args, $kwargs, $funcName = 'hsl')
|
8214 |
{
|
8215 |
+
$args_to_check = $args;
|
8216 |
+
|
8217 |
if (\count($args) == 1) {
|
8218 |
if ($args[0][0] !== Type::T_LIST || \count($args[0][2]) < 3 || \count($args[0][2]) > 4) {
|
8219 |
return [Type::T_STRING, '', [$funcName . '(', $args[0], ')']];
|
8220 |
}
|
8221 |
|
8222 |
$args = $args[0][2];
|
8223 |
+
$args_to_check = $kwargs['channels'][2];
|
8224 |
+
}
|
8225 |
+
|
8226 |
+
if (\count($args) === 2) {
|
8227 |
+
// if var() is used as an argument, return as a css function
|
8228 |
+
foreach ($args as $arg) {
|
8229 |
+
if ($arg[0] === Type::T_FUNCTION && in_array($arg[1], ['var'])) {
|
8230 |
+
return null;
|
8231 |
+
}
|
8232 |
+
}
|
8233 |
+
|
8234 |
+
throw new SassScriptException('Missing argument $lightness.');
|
8235 |
}
|
8236 |
|
8237 |
+
foreach ($kwargs as $arg) {
|
8238 |
+
if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) {
|
8239 |
+
return null;
|
8240 |
+
}
|
8241 |
+
}
|
8242 |
+
|
8243 |
+
foreach ($args_to_check as $k => $arg) {
|
8244 |
+
if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) {
|
8245 |
+
if (count($kwargs) > 1 || ($k >= 2 && count($args) === 4)) {
|
8246 |
+
return null;
|
8247 |
+
}
|
8248 |
+
|
8249 |
+
$args[$k] = $this->stringifyFncallArgs($arg);
|
8250 |
+
}
|
8251 |
|
8252 |
+
if (
|
8253 |
+
$k >= 2 && count($args) === 4 &&
|
8254 |
+
in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) &&
|
8255 |
+
in_array($arg[1], ['calc','env'])
|
8256 |
+
) {
|
8257 |
+
return null;
|
8258 |
+
}
|
8259 |
+
}
|
8260 |
+
|
8261 |
+
$hue = $this->reduce($args[0]);
|
8262 |
+
$saturation = $this->reduce($args[1]);
|
8263 |
+
$lightness = $this->reduce($args[2]);
|
8264 |
$alpha = null;
|
8265 |
|
8266 |
if (\count($args) === 4) {
|
8267 |
$alpha = $this->compileColorPartValue($args[3], 0, 100, false);
|
8268 |
|
8269 |
+
if (!$hue instanceof Number || !$saturation instanceof Number || ! $lightness instanceof Number || ! is_numeric($alpha)) {
|
8270 |
return [Type::T_STRING, '',
|
8271 |
[$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ', ', $args[3], ')']];
|
8272 |
}
|
8273 |
} else {
|
8274 |
+
if (!$hue instanceof Number || !$saturation instanceof Number || ! $lightness instanceof Number) {
|
8275 |
return [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ')']];
|
8276 |
}
|
8277 |
}
|
8278 |
|
8279 |
+
$hueValue = fmod($hue->getDimension(), 360);
|
8280 |
+
|
8281 |
+
while ($hueValue < 0) {
|
8282 |
+
$hueValue += 360;
|
8283 |
+
}
|
8284 |
+
|
8285 |
+
$color = $this->toRGB($hueValue, max(0, min($saturation->getDimension(), 100)), max(0, min($lightness->getDimension(), 100)));
|
8286 |
|
8287 |
if (! \is_null($alpha)) {
|
8288 |
$color[4] = $alpha;
|
8293 |
|
8294 |
protected static $libHsla = [
|
8295 |
['channels'],
|
8296 |
+
['hue', 'saturation'],
|
8297 |
+
['hue', 'saturation', 'lightness'],
|
8298 |
+
['hue', 'saturation', 'lightness', 'alpha']];
|
8299 |
protected function libHsla($args, $kwargs)
|
8300 |
{
|
8301 |
return $this->libHsl($args, $kwargs, 'hsla');
|
8304 |
protected static $libHue = ['color'];
|
8305 |
protected function libHue($args)
|
8306 |
{
|
8307 |
+
$color = $this->assertColor($args[0], 'color');
|
8308 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
8309 |
|
8310 |
+
return new Number($hsl[1], 'deg');
|
8311 |
}
|
8312 |
|
8313 |
protected static $libSaturation = ['color'];
|
8314 |
protected function libSaturation($args)
|
8315 |
{
|
8316 |
+
$color = $this->assertColor($args[0], 'color');
|
8317 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
8318 |
|
8319 |
+
return new Number($hsl[2], '%');
|
8320 |
}
|
8321 |
|
8322 |
protected static $libLightness = ['color'];
|
8323 |
protected function libLightness($args)
|
8324 |
{
|
8325 |
+
$color = $this->assertColor($args[0], 'color');
|
8326 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
8327 |
|
8328 |
+
return new Number($hsl[3], '%');
|
8329 |
+
}
|
8330 |
+
|
8331 |
+
/*
|
8332 |
+
* Todo : a integrer dans le futur module color
|
8333 |
+
protected static $libHwb = [
|
8334 |
+
['channels'],
|
8335 |
+
['hue', 'whiteness', 'blackness'],
|
8336 |
+
['hue', 'whiteness', 'blackness', 'alpha'] ];
|
8337 |
+
protected function libHwb($args, $kwargs, $funcName = 'hwb')
|
8338 |
+
{
|
8339 |
+
$args_to_check = $args;
|
8340 |
+
|
8341 |
+
if (\count($args) == 1) {
|
8342 |
+
if ($args[0][0] !== Type::T_LIST) {
|
8343 |
+
throw $this->error("Missing elements \$whiteness and \$blackness");
|
8344 |
+
}
|
8345 |
+
|
8346 |
+
if (\trim($args[0][1])) {
|
8347 |
+
throw $this->error("\$channels must be a space-separated list.");
|
8348 |
+
}
|
8349 |
+
|
8350 |
+
if (! empty($args[0]['enclosing'])) {
|
8351 |
+
throw $this->error("\$channels must be an unbracketed list.");
|
8352 |
+
}
|
8353 |
+
|
8354 |
+
$args = $args[0][2];
|
8355 |
+
if (\count($args) > 3) {
|
8356 |
+
throw $this->error("hwb() : Only 3 elements are allowed but ". \count($args) . "were passed");
|
8357 |
+
}
|
8358 |
+
|
8359 |
+
$args_to_check = $this->extractSlashAlphaInColorFunction($kwargs['channels'][2]);
|
8360 |
+
if (\count($args_to_check) !== \count($kwargs['channels'][2])) {
|
8361 |
+
$args = $args_to_check;
|
8362 |
+
}
|
8363 |
+
}
|
8364 |
+
|
8365 |
+
if (\count($args_to_check) < 2) {
|
8366 |
+
throw $this->error("Missing elements \$whiteness and \$blackness");
|
8367 |
+
}
|
8368 |
+
if (\count($args_to_check) < 3) {
|
8369 |
+
throw $this->error("Missing element \$blackness");
|
8370 |
+
}
|
8371 |
+
if (\count($args_to_check) > 4) {
|
8372 |
+
throw $this->error("hwb() : Only 4 elements are allowed but ". \count($args) . "were passed");
|
8373 |
+
}
|
8374 |
+
|
8375 |
+
foreach ($kwargs as $k => $arg) {
|
8376 |
+
if (in_array($arg[0], [Type::T_FUNCTION_CALL]) && in_array($arg[1], ['min', 'max'])) {
|
8377 |
+
return null;
|
8378 |
+
}
|
8379 |
+
}
|
8380 |
+
|
8381 |
+
foreach ($args_to_check as $k => $arg) {
|
8382 |
+
if (in_array($arg[0], [Type::T_FUNCTION_CALL]) && in_array($arg[1], ['min', 'max'])) {
|
8383 |
+
if (count($kwargs) > 1 || ($k >= 2 && count($args) === 4)) {
|
8384 |
+
return null;
|
8385 |
+
}
|
8386 |
+
|
8387 |
+
$args[$k] = $this->stringifyFncallArgs($arg);
|
8388 |
+
}
|
8389 |
+
|
8390 |
+
if (
|
8391 |
+
$k >= 2 && count($args) === 4 &&
|
8392 |
+
in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) &&
|
8393 |
+
in_array($arg[1], ['calc','env'])
|
8394 |
+
) {
|
8395 |
+
return null;
|
8396 |
+
}
|
8397 |
+
}
|
8398 |
+
|
8399 |
+
$hue = $this->reduce($args[0]);
|
8400 |
+
$whiteness = $this->reduce($args[1]);
|
8401 |
+
$blackness = $this->reduce($args[2]);
|
8402 |
+
$alpha = null;
|
8403 |
+
|
8404 |
+
if (\count($args) === 4) {
|
8405 |
+
$alpha = $this->compileColorPartValue($args[3], 0, 1, false);
|
8406 |
+
|
8407 |
+
if (! \is_numeric($alpha)) {
|
8408 |
+
$val = $this->compileValue($args[3]);
|
8409 |
+
throw $this->error("\$alpha: $val is not a number");
|
8410 |
+
}
|
8411 |
+
}
|
8412 |
+
|
8413 |
+
$this->assertNumber($hue, 'hue');
|
8414 |
+
$this->assertUnit($whiteness, ['%'], 'whiteness');
|
8415 |
+
$this->assertUnit($blackness, ['%'], 'blackness');
|
8416 |
+
|
8417 |
+
$this->assertRange($whiteness, 0, 100, "0% and 100%", "whiteness");
|
8418 |
+
$this->assertRange($blackness, 0, 100, "0% and 100%", "blackness");
|
8419 |
+
|
8420 |
+
$w = $whiteness->getDimension();
|
8421 |
+
$b = $blackness->getDimension();
|
8422 |
+
|
8423 |
+
$hueValue = $hue->getDimension() % 360;
|
8424 |
+
|
8425 |
+
while ($hueValue < 0) {
|
8426 |
+
$hueValue += 360;
|
8427 |
+
}
|
8428 |
+
|
8429 |
+
$color = $this->HWBtoRGB($hueValue, $w, $b);
|
8430 |
+
|
8431 |
+
if (! \is_null($alpha)) {
|
8432 |
+
$color[4] = $alpha;
|
8433 |
+
}
|
8434 |
+
|
8435 |
+
return $color;
|
8436 |
+
}
|
8437 |
+
|
8438 |
+
protected static $libWhiteness = ['color'];
|
8439 |
+
protected function libWhiteness($args, $kwargs, $funcName = 'whiteness') {
|
8440 |
+
|
8441 |
+
$color = $this->assertColor($args[0]);
|
8442 |
+
$hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]);
|
8443 |
+
|
8444 |
+
return new Number($hwb[2], '%');
|
8445 |
+
}
|
8446 |
+
|
8447 |
+
protected static $libBlackness = ['color'];
|
8448 |
+
protected function libBlackness($args, $kwargs, $funcName = 'blackness') {
|
8449 |
+
|
8450 |
+
$color = $this->assertColor($args[0]);
|
8451 |
+
$hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]);
|
8452 |
+
|
8453 |
+
return new Number($hwb[3], '%');
|
8454 |
}
|
8455 |
+
*/
|
8456 |
|
8457 |
+
/**
|
8458 |
+
* @param array $color
|
8459 |
+
* @param int $idx
|
8460 |
+
* @param int|float $amount
|
8461 |
+
*
|
8462 |
+
* @return array
|
8463 |
+
*/
|
8464 |
protected function adjustHsl($color, $idx, $amount)
|
8465 |
{
|
8466 |
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
|
8467 |
$hsl[$idx] += $amount;
|
8468 |
+
|
8469 |
+
if ($idx !== 1) {
|
8470 |
+
// Clamp the saturation and lightness
|
8471 |
+
$hsl[$idx] = min(max(0, $hsl[$idx]), 100);
|
8472 |
+
}
|
8473 |
+
|
8474 |
$out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
|
8475 |
|
8476 |
if (isset($color[4])) {
|
8483 |
protected static $libAdjustHue = ['color', 'degrees'];
|
8484 |
protected function libAdjustHue($args)
|
8485 |
{
|
8486 |
+
$color = $this->assertColor($args[0], 'color');
|
8487 |
+
$degrees = $this->assertNumber($args[1], 'degrees')->getDimension();
|
8488 |
|
8489 |
return $this->adjustHsl($color, 1, $degrees);
|
8490 |
}
|
8492 |
protected static $libLighten = ['color', 'amount'];
|
8493 |
protected function libLighten($args)
|
8494 |
{
|
8495 |
+
$color = $this->assertColor($args[0], 'color');
|
8496 |
$amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%');
|
8497 |
|
8498 |
return $this->adjustHsl($color, 3, $amount);
|
8501 |
protected static $libDarken = ['color', 'amount'];
|
8502 |
protected function libDarken($args)
|
8503 |
{
|
8504 |
+
$color = $this->assertColor($args[0], 'color');
|
8505 |
$amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%');
|
8506 |
|
8507 |
return $this->adjustHsl($color, 3, -$amount);
|
8508 |
}
|
8509 |
|
8510 |
+
protected static $libSaturate = [['color', 'amount'], ['amount']];
|
8511 |
protected function libSaturate($args)
|
8512 |
{
|
8513 |
$value = $args[0];
|
8514 |
|
8515 |
+
if (count($args) === 1) {
|
8516 |
+
$this->assertNumber($args[0], 'amount');
|
8517 |
+
|
8518 |
return null;
|
8519 |
}
|
8520 |
|
8521 |
+
$color = $this->assertColor($args[0], 'color');
|
8522 |
+
$amount = $this->assertNumber($args[1], 'amount');
|
8523 |
|
8524 |
+
return $this->adjustHsl($color, 2, $amount->valueInRange(0, 100, 'amount'));
|
8525 |
}
|
8526 |
|
8527 |
protected static $libDesaturate = ['color', 'amount'];
|
8528 |
protected function libDesaturate($args)
|
8529 |
{
|
8530 |
+
$color = $this->assertColor($args[0], 'color');
|
8531 |
+
$amount = $this->assertNumber($args[1], 'amount');
|
8532 |
|
8533 |
+
return $this->adjustHsl($color, 2, -$amount->valueInRange(0, 100, 'amount'));
|
8534 |
}
|
8535 |
|
8536 |
protected static $libGrayscale = ['color'];
|
8538 |
{
|
8539 |
$value = $args[0];
|
8540 |
|
8541 |
+
if ($value instanceof Number) {
|
8542 |
return null;
|
8543 |
}
|
8544 |
|
8545 |
+
return $this->adjustHsl($this->assertColor($value, 'color'), 2, -100);
|
8546 |
}
|
8547 |
|
8548 |
protected static $libComplement = ['color'];
|
8549 |
protected function libComplement($args)
|
8550 |
{
|
8551 |
+
return $this->adjustHsl($this->assertColor($args[0], 'color'), 1, 180);
|
8552 |
}
|
8553 |
|
8554 |
+
protected static $libInvert = ['color', 'weight:100%'];
|
8555 |
protected function libInvert($args)
|
8556 |
{
|
8557 |
+
$value = $args[0];
|
8558 |
|
8559 |
+
$weight = $this->assertNumber($args[1], 'weight');
|
8560 |
+
|
8561 |
+
if ($value instanceof Number) {
|
8562 |
+
if ($weight->getDimension() != 100 || !$weight->hasUnit('%')) {
|
8563 |
+
throw new SassScriptException('Only one argument may be passed to the plain-CSS invert() function.');
|
8564 |
+
}
|
8565 |
|
|
|
8566 |
return null;
|
8567 |
}
|
8568 |
|
8569 |
+
$color = $this->assertColor($value, 'color');
|
8570 |
$inverted = $color;
|
8571 |
$inverted[1] = 255 - $inverted[1];
|
8572 |
$inverted[2] = 255 - $inverted[2];
|
8573 |
$inverted[3] = 255 - $inverted[3];
|
8574 |
|
8575 |
+
return $this->libMix([$inverted, $color, $weight]);
|
|
|
|
|
|
|
|
|
8576 |
}
|
8577 |
|
8578 |
// increases opacity by amount
|
8579 |
protected static $libOpacify = ['color', 'amount'];
|
8580 |
protected function libOpacify($args)
|
8581 |
{
|
8582 |
+
$color = $this->assertColor($args[0], 'color');
|
8583 |
+
$amount = $this->assertNumber($args[1], 'amount');
|
8584 |
|
8585 |
+
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRange(0, 1, 'amount');
|
8586 |
$color[4] = min(1, max(0, $color[4]));
|
8587 |
|
8588 |
return $color;
|
8598 |
protected static $libTransparentize = ['color', 'amount'];
|
8599 |
protected function libTransparentize($args)
|
8600 |
{
|
8601 |
+
$color = $this->assertColor($args[0], 'color');
|
8602 |
+
$amount = $this->assertNumber($args[1], 'amount');
|
8603 |
|
8604 |
+
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRange(0, 1, 'amount');
|
8605 |
$color[4] = min(1, max(0, $color[4]));
|
8606 |
|
8607 |
return $color;
|
8616 |
protected static $libUnquote = ['string'];
|
8617 |
protected function libUnquote($args)
|
8618 |
{
|
8619 |
+
try {
|
8620 |
+
$str = $this->assertString($args[0], 'string');
|
8621 |
+
} catch (SassScriptException $e) {
|
8622 |
+
$value = $this->compileValue($args[0]);
|
8623 |
+
$fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]);
|
8624 |
+
$line = $this->sourceLine;
|
8625 |
+
|
8626 |
+
$message = "Passing $value, a non-string value, to unquote()
|
8627 |
+
will be an error in future versions of Sass.\n on line $line of $fname";
|
8628 |
|
8629 |
+
$this->logger->warn($message, true);
|
8630 |
+
|
8631 |
+
return $args[0];
|
8632 |
}
|
8633 |
|
8634 |
+
$str[1] = '';
|
8635 |
+
|
8636 |
return $str;
|
8637 |
}
|
8638 |
|
8639 |
protected static $libQuote = ['string'];
|
8640 |
protected function libQuote($args)
|
8641 |
{
|
8642 |
+
$value = $this->assertString($args[0], 'string');
|
8643 |
|
8644 |
+
$value[1] = '"';
|
|
|
|
|
8645 |
|
8646 |
+
return $value;
|
8647 |
}
|
8648 |
|
8649 |
protected static $libPercentage = ['number'];
|
8650 |
protected function libPercentage($args)
|
8651 |
{
|
8652 |
+
$num = $this->assertNumber($args[0], 'number');
|
8653 |
+
$num->assertNoUnits('number');
|
8654 |
+
|
8655 |
+
return new Number($num->getDimension() * 100, '%');
|
8656 |
}
|
8657 |
|
8658 |
protected static $libRound = ['number'];
|
8659 |
protected function libRound($args)
|
8660 |
{
|
8661 |
+
$num = $this->assertNumber($args[0], 'number');
|
8662 |
|
8663 |
+
return new Number(round($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits());
|
8664 |
}
|
8665 |
|
8666 |
protected static $libFloor = ['number'];
|
8667 |
protected function libFloor($args)
|
8668 |
{
|
8669 |
+
$num = $this->assertNumber($args[0], 'number');
|
8670 |
|
8671 |
+
return new Number(floor($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits());
|
8672 |
}
|
8673 |
|
8674 |
protected static $libCeil = ['number'];
|
8675 |
protected function libCeil($args)
|
8676 |
{
|
8677 |
+
$num = $this->assertNumber($args[0], 'number');
|
8678 |
|
8679 |
+
return new Number(ceil($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits());
|
8680 |
}
|
8681 |
|
8682 |
protected static $libAbs = ['number'];
|
8683 |
protected function libAbs($args)
|
8684 |
{
|
8685 |
+
$num = $this->assertNumber($args[0], 'number');
|
8686 |
|
8687 |
+
return new Number(abs($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits());
|
8688 |
}
|
8689 |
|
8690 |
+
protected static $libMin = ['numbers...'];
|
8691 |
protected function libMin($args)
|
8692 |
{
|
8693 |
+
/**
|
8694 |
+
* @var Number|null
|
8695 |
+
*/
|
8696 |
+
$min = null;
|
8697 |
|
8698 |
+
foreach ($args[0][2] as $arg) {
|
8699 |
+
$number = $this->assertNumber($arg);
|
8700 |
|
8701 |
+
if (\is_null($min) || $min->greaterThan($number)) {
|
8702 |
+
$min = $number;
|
|
|
|
|
|
|
|
|
|
|
|
|
8703 |
}
|
8704 |
}
|
8705 |
|
8706 |
+
if (!\is_null($min)) {
|
8707 |
+
return $min;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8708 |
}
|
8709 |
|
8710 |
+
throw $this->error('At least one argument must be passed.');
|
8711 |
}
|
8712 |
|
8713 |
+
protected static $libMax = ['numbers...'];
|
8714 |
+
protected function libMax($args)
|
|
|
|
|
|
|
|
|
|
|
|
|
8715 |
{
|
8716 |
+
/**
|
8717 |
+
* @var Number|null
|
8718 |
+
*/
|
8719 |
+
$max = null;
|
|
|
|
|
|
|
|
|
|
|
8720 |
|
8721 |
+
foreach ($args[0][2] as $arg) {
|
8722 |
+
$number = $this->assertNumber($arg);
|
8723 |
|
8724 |
+
if (\is_null($max) || $max->lessThan($number)) {
|
8725 |
+
$max = $number;
|
|
|
|
|
|
|
|
|
8726 |
}
|
8727 |
+
}
|
8728 |
|
8729 |
+
if (!\is_null($max)) {
|
8730 |
+
return $max;
|
8731 |
}
|
8732 |
|
8733 |
+
throw $this->error('At least one argument must be passed.');
|
8734 |
}
|
8735 |
|
8736 |
protected static $libLength = ['list'];
|
8738 |
{
|
8739 |
$list = $this->coerceList($args[0], ',', true);
|
8740 |
|
8741 |
+
return new Number(\count($list[2]), '');
|
8742 |
}
|
8743 |
|
8744 |
+
protected static $libListSeparator = ['list'];
|
8745 |
protected function libListSeparator($args)
|
8746 |
{
|
8747 |
+
if (! \in_array($args[0][0], [Type::T_LIST, Type::T_MAP])) {
|
8748 |
+
return [Type::T_KEYWORD, 'space'];
|
8749 |
}
|
8750 |
|
8751 |
$list = $this->coerceList($args[0]);
|
8752 |
|
8753 |
+
if ($list[1] === '' && \count($list[2]) <= 1 && empty($list['enclosing'])) {
|
8754 |
+
return [Type::T_KEYWORD, 'space'];
|
8755 |
}
|
8756 |
|
8757 |
if ($list[1] === ',') {
|
8758 |
+
return [Type::T_KEYWORD, 'comma'];
|
8759 |
+
}
|
8760 |
+
|
8761 |
+
if ($list[1] === '/') {
|
8762 |
+
return [Type::T_KEYWORD, 'slash'];
|
8763 |
}
|
8764 |
|
8765 |
+
return [Type::T_KEYWORD, 'space'];
|
8766 |
}
|
8767 |
|
8768 |
protected static $libNth = ['list', 'n'];
|
8769 |
protected function libNth($args)
|
8770 |
{
|
8771 |
$list = $this->coerceList($args[0], ',', false);
|
8772 |
+
$n = $this->assertNumber($args[1])->getDimension();
|
8773 |
|
8774 |
if ($n > 0) {
|
8775 |
$n--;
|
8784 |
protected function libSetNth($args)
|
8785 |
{
|
8786 |
$list = $this->coerceList($args[0]);
|
8787 |
+
$n = $this->assertNumber($args[1])->getDimension();
|
8788 |
|
8789 |
if ($n > 0) {
|
8790 |
$n--;
|
8793 |
}
|
8794 |
|
8795 |
if (! isset($list[2][$n])) {
|
8796 |
+
throw $this->error('Invalid argument for "n"');
|
8797 |
+
}
|
8798 |
+
|
8799 |
+
$list[2][$n] = $args[2];
|
8800 |
+
|
8801 |
+
return $list;
|
8802 |
+
}
|
8803 |
+
|
8804 |
+
protected static $libMapGet = ['map', 'key', 'keys...'];
|
8805 |
+
protected function libMapGet($args)
|
8806 |
+
{
|
8807 |
+
$map = $this->assertMap($args[0], 'map');
|
8808 |
+
if (!isset($args[2])) {
|
8809 |
+
// BC layer for usages of the function from PHP code rather than from the Sass function
|
8810 |
+
$args[2] = self::$emptyArgumentList;
|
8811 |
+
}
|
8812 |
+
$keys = array_merge([$args[1]], $args[2][2]);
|
8813 |
+
$value = static::$null;
|
8814 |
+
|
8815 |
+
foreach ($keys as $key) {
|
8816 |
+
if (!\is_array($map) || $map[0] !== Type::T_MAP) {
|
8817 |
+
return static::$null;
|
8818 |
+
}
|
8819 |
+
|
8820 |
+
$map = $this->mapGet($map, $key);
|
8821 |
+
|
8822 |
+
if ($map === null) {
|
8823 |
+
return static::$null;
|
8824 |
+
}
|
8825 |
+
|
8826 |
+
$value = $map;
|
8827 |
+
}
|
8828 |
+
|
8829 |
+
return $value;
|
8830 |
+
}
|
8831 |
+
|
8832 |
+
/**
|
8833 |
+
* Gets the value corresponding to that key in the map
|
8834 |
+
*
|
8835 |
+
* @param array $map
|
8836 |
+
* @param Number|array $key
|
8837 |
+
*
|
8838 |
+
* @return Number|array|null
|
8839 |
+
*/
|
8840 |
+
private function mapGet(array $map, $key)
|
8841 |
+
{
|
8842 |
+
$index = $this->mapGetEntryIndex($map, $key);
|
8843 |
|
8844 |
+
if ($index !== null) {
|
8845 |
+
return $map[2][$index];
|
8846 |
}
|
8847 |
|
8848 |
+
return null;
|
|
|
|
|
8849 |
}
|
8850 |
|
8851 |
+
/**
|
8852 |
+
* Gets the index corresponding to that key in the map entries
|
8853 |
+
*
|
8854 |
+
* @param array $map
|
8855 |
+
* @param Number|array $key
|
8856 |
+
*
|
8857 |
+
* @return int|null
|
8858 |
+
*/
|
8859 |
+
private function mapGetEntryIndex(array $map, $key)
|
8860 |
{
|
8861 |
+
$key = $this->compileStringContent($this->coerceString($key));
|
|
|
8862 |
|
8863 |
+
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
8864 |
+
if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
|
8865 |
+
return $i;
|
|
|
|
|
|
|
|
|
8866 |
}
|
8867 |
}
|
8868 |
|
8869 |
+
return null;
|
8870 |
}
|
8871 |
|
8872 |
protected static $libMapKeys = ['map'];
|
8873 |
protected function libMapKeys($args)
|
8874 |
{
|
8875 |
+
$map = $this->assertMap($args[0], 'map');
|
8876 |
$keys = $map[1];
|
8877 |
|
8878 |
return [Type::T_LIST, ',', $keys];
|
8881 |
protected static $libMapValues = ['map'];
|
8882 |
protected function libMapValues($args)
|
8883 |
{
|
8884 |
+
$map = $this->assertMap($args[0], 'map');
|
8885 |
$values = $map[2];
|
8886 |
|
8887 |
return [Type::T_LIST, ',', $values];
|
8888 |
}
|
8889 |
|
8890 |
+
protected static $libMapRemove = [
|
8891 |
+
['map'],
|
8892 |
+
['map', 'key', 'keys...'],
|
8893 |
+
];
|
8894 |
protected function libMapRemove($args)
|
8895 |
{
|
8896 |
+
$map = $this->assertMap($args[0], 'map');
|
8897 |
+
|
8898 |
+
if (\count($args) === 1) {
|
8899 |
+
return $map;
|
8900 |
+
}
|
8901 |
+
|
8902 |
+
$keys = [];
|
8903 |
+
$keys[] = $this->compileStringContent($this->coerceString($args[1]));
|
8904 |
+
|
8905 |
+
foreach ($args[2][2] as $key) {
|
8906 |
+
$keys[] = $this->compileStringContent($this->coerceString($key));
|
8907 |
+
}
|
8908 |
|
8909 |
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
8910 |
+
if (in_array($this->compileStringContent($this->coerceString($map[1][$i])), $keys)) {
|
8911 |
array_splice($map[1], $i, 1);
|
8912 |
array_splice($map[2], $i, 1);
|
8913 |
}
|
8916 |
return $map;
|
8917 |
}
|
8918 |
|
8919 |
+
protected static $libMapHasKey = ['map', 'key', 'keys...'];
|
8920 |
protected function libMapHasKey($args)
|
8921 |
{
|
8922 |
+
$map = $this->assertMap($args[0], 'map');
|
8923 |
+
if (!isset($args[2])) {
|
8924 |
+
// BC layer for usages of the function from PHP code rather than from the Sass function
|
8925 |
+
$args[2] = self::$emptyArgumentList;
|
8926 |
+
}
|
8927 |
+
$keys = array_merge([$args[1]], $args[2][2]);
|
8928 |
+
$lastKey = array_pop($keys);
|
8929 |
+
|
8930 |
+
foreach ($keys as $key) {
|
8931 |
+
$value = $this->mapGet($map, $key);
|
8932 |
+
|
8933 |
+
if ($value === null || $value instanceof Number || $value[0] !== Type::T_MAP) {
|
8934 |
+
return self::$false;
|
8935 |
+
}
|
8936 |
+
|
8937 |
+
$map = $value;
|
8938 |
+
}
|
8939 |
+
|
8940 |
+
return $this->toBool($this->mapHasKey($map, $lastKey));
|
8941 |
+
}
|
8942 |
+
|
8943 |
+
/**
|
8944 |
+
* @param array|Number $keyValue
|
8945 |
+
*
|
8946 |
+
* @return bool
|
8947 |
+
*/
|
8948 |
+
private function mapHasKey(array $map, $keyValue)
|
8949 |
+
{
|
8950 |
+
$key = $this->compileStringContent($this->coerceString($keyValue));
|
8951 |
|
8952 |
for ($i = \count($map[1]) - 1; $i >= 0; $i--) {
|
8953 |
if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
|
8958 |
return false;
|
8959 |
}
|
8960 |
|
8961 |
+
protected static $libMapMerge = [
|
8962 |
+
['map1', 'map2'],
|
8963 |
+
['map-1', 'map-2'],
|
8964 |
+
['map1', 'args...']
|
8965 |
+
];
|
8966 |
protected function libMapMerge($args)
|
8967 |
{
|
8968 |
+
$map1 = $this->assertMap($args[0], 'map1');
|
8969 |
+
$map2 = $args[1];
|
8970 |
+
$keys = [];
|
8971 |
+
if ($map2[0] === Type::T_LIST && isset($map2[3]) && \is_array($map2[3])) {
|
8972 |
+
// This is an argument list for the variadic signature
|
8973 |
+
if (\count($map2[2]) === 0) {
|
8974 |
+
throw new SassScriptException('Expected $args to contain a key.');
|
8975 |
+
}
|
8976 |
+
if (\count($map2[2]) === 1) {
|
8977 |
+
throw new SassScriptException('Expected $args to contain a value.');
|
8978 |
+
}
|
8979 |
+
$keys = $map2[2];
|
8980 |
+
$map2 = array_pop($keys);
|
8981 |
+
}
|
8982 |
+
$map2 = $this->assertMap($map2, 'map2');
|
8983 |
+
|
8984 |
+
return $this->modifyMap($map1, $keys, function ($oldValue) use ($map2) {
|
8985 |
+
$nestedMap = $this->tryMap($oldValue);
|
8986 |
+
|
8987 |
+
if ($nestedMap === null) {
|
8988 |
+
return $map2;
|
8989 |
+
}
|
8990 |
+
|
8991 |
+
return $this->mergeMaps($nestedMap, $map2);
|
8992 |
+
});
|
8993 |
+
}
|
8994 |
+
|
8995 |
+
/**
|
8996 |
+
* @param array $map
|
8997 |
+
* @param array $keys
|
8998 |
+
* @param callable $modify
|
8999 |
+
* @param bool $addNesting
|
9000 |
+
*
|
9001 |
+
* @return Number|array
|
9002 |
+
*
|
9003 |
+
* @phpstan-param array<Number|array> $keys
|
9004 |
+
* @phpstan-param callable(Number|array): (Number|array) $modify
|
9005 |
+
*/
|
9006 |
+
private function modifyMap(array $map, array $keys, callable $modify, $addNesting = true)
|
9007 |
+
{
|
9008 |
+
if ($keys === []) {
|
9009 |
+
return $modify($map);
|
9010 |
+
}
|
9011 |
+
|
9012 |
+
return $this->modifyNestedMap($map, $keys, $modify, $addNesting);
|
9013 |
+
}
|
9014 |
+
|
9015 |
+
/**
|
9016 |
+
* @param array $map
|
9017 |
+
* @param array $keys
|
9018 |
+
* @param callable $modify
|
9019 |
+
* @param bool $addNesting
|
9020 |
+
*
|
9021 |
+
* @return array
|
9022 |
+
*
|
9023 |
+
* @phpstan-param non-empty-array<Number|array> $keys
|
9024 |
+
* @phpstan-param callable(Number|array): (Number|array) $modify
|
9025 |
+
*/
|
9026 |
+
private function modifyNestedMap(array $map, array $keys, callable $modify, $addNesting)
|
9027 |
+
{
|
9028 |
+
$key = array_shift($keys);
|
9029 |
+
|
9030 |
+
$nestedValueIndex = $this->mapGetEntryIndex($map, $key);
|
9031 |
+
|
9032 |
+
if ($keys === []) {
|
9033 |
+
if ($nestedValueIndex !== null) {
|
9034 |
+
$map[2][$nestedValueIndex] = $modify($map[2][$nestedValueIndex]);
|
9035 |
+
} else {
|
9036 |
+
$map[1][] = $key;
|
9037 |
+
$map[2][] = $modify(self::$null);
|
9038 |
+
}
|
9039 |
+
|
9040 |
+
return $map;
|
9041 |
+
}
|
9042 |
+
|
9043 |
+
$nestedMap = $nestedValueIndex !== null ? $this->tryMap($map[2][$nestedValueIndex]) : null;
|
9044 |
+
|
9045 |
+
if ($nestedMap === null && !$addNesting) {
|
9046 |
+
return $map;
|
9047 |
+
}
|
9048 |
+
|
9049 |
+
if ($nestedMap === null) {
|
9050 |
+
$nestedMap = self::$emptyMap;
|
9051 |
+
}
|
9052 |
+
|
9053 |
+
$newNestedMap = $this->modifyNestedMap($nestedMap, $keys, $modify, $addNesting);
|
9054 |
+
|
9055 |
+
if ($nestedValueIndex !== null) {
|
9056 |
+
$map[2][$nestedValueIndex] = $newNestedMap;
|
9057 |
+
} else {
|
9058 |
+
$map[1][] = $key;
|
9059 |
+
$map[2][] = $newNestedMap;
|
9060 |
+
}
|
9061 |
+
|
9062 |
+
return $map;
|
9063 |
+
}
|
9064 |
|
9065 |
+
/**
|
9066 |
+
* Merges 2 Sass maps together
|
9067 |
+
*
|
9068 |
+
* @param array $map1
|
9069 |
+
* @param array $map2
|
9070 |
+
*
|
9071 |
+
* @return array
|
9072 |
+
*/
|
9073 |
+
private function mergeMaps(array $map1, array $map2)
|
9074 |
+
{
|
9075 |
foreach ($map2[1] as $i2 => $key2) {
|
9076 |
+
$map1EntryIndex = $this->mapGetEntryIndex($map1, $key2);
|
9077 |
|
9078 |
+
if ($map1EntryIndex !== null) {
|
9079 |
+
$map1[2][$map1EntryIndex] = $map2[2][$i2];
|
9080 |
+
continue;
|
|
|
|
|
9081 |
}
|
9082 |
|
9083 |
+
$map1[1][] = $key2;
|
9084 |
$map1[2][] = $map2[2][$i2];
|
9085 |
}
|
9086 |
|
9090 |
protected static $libKeywords = ['args'];
|
9091 |
protected function libKeywords($args)
|
9092 |
{
|
9093 |
+
$value = $args[0];
|
9094 |
+
|
9095 |
+
if ($value[0] !== Type::T_LIST || !isset($value[3]) || !\is_array($value[3])) {
|
9096 |
+
$compiledValue = $this->compileValue($value);
|
9097 |
+
|
9098 |
+
throw SassScriptException::forArgument($compiledValue . ' is not an argument list.', 'args');
|
9099 |
+
}
|
9100 |
|
9101 |
$keys = [];
|
9102 |
$values = [];
|
9103 |
|
9104 |
+
foreach ($this->getArgumentListKeywords($value) as $name => $arg) {
|
9105 |
$keys[] = [Type::T_KEYWORD, $name];
|
9106 |
$values[] = $arg;
|
9107 |
}
|
9116 |
$this->coerceList($list, ' ');
|
9117 |
|
9118 |
if (! empty($list['enclosing']) && $list['enclosing'] === 'bracket') {
|
9119 |
+
return self::$true;
|
9120 |
}
|
9121 |
|
9122 |
+
return self::$false;
|
9123 |
}
|
9124 |
|
9125 |
+
/**
|
9126 |
+
* @param array $list1
|
9127 |
+
* @param array|Number|null $sep
|
9128 |
+
*
|
9129 |
+
* @return string
|
9130 |
+
* @throws CompilerException
|
9131 |
+
*
|
9132 |
+
* @deprecated
|
9133 |
+
*/
|
9134 |
protected function listSeparatorForJoin($list1, $sep)
|
9135 |
{
|
9136 |
+
@trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED);
|
9137 |
+
|
9138 |
if (! isset($sep)) {
|
9139 |
return $list1[1];
|
9140 |
}
|
9151 |
}
|
9152 |
}
|
9153 |
|
9154 |
+
protected static $libJoin = ['list1', 'list2', 'separator:auto', 'bracketed:auto'];
|
9155 |
protected function libJoin($args)
|
9156 |
{
|
9157 |
list($list1, $list2, $sep, $bracketed) = $args;
|
9158 |
|
9159 |
$list1 = $this->coerceList($list1, ' ', true);
|
9160 |
$list2 = $this->coerceList($list2, ' ', true);
|
9161 |
+
|
9162 |
+
switch ($this->compileStringContent($this->assertString($sep, 'separator'))) {
|
9163 |
+
case 'comma':
|
9164 |
+
$separator = ',';
|
9165 |
+
break;
|
9166 |
+
|
9167 |
+
case 'space':
|
9168 |
+
$separator = ' ';
|
9169 |
+
break;
|
9170 |
+
|
9171 |
+
case 'slash':
|
9172 |
+
$separator = '/';
|
9173 |
+
break;
|
9174 |
+
|
9175 |
+
case 'auto':
|
9176 |
+
if ($list1[1] !== '' || count($list1[2]) > 1 || !empty($list1['enclosing']) && $list1['enclosing'] !== 'parent') {
|
9177 |
+
$separator = $list1[1] ?: ' ';
|
9178 |
+
} elseif ($list2[1] !== '' || count($list2[2]) > 1 || !empty($list2['enclosing']) && $list2['enclosing'] !== 'parent') {
|
9179 |
+
$separator = $list2[1] ?: ' ';
|
9180 |
+
} else {
|
9181 |
+
$separator = ' ';
|
9182 |
+
}
|
9183 |
+
break;
|
9184 |
+
|
9185 |
+
default:
|
9186 |
+
throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator');
|
9187 |
+
}
|
9188 |
|
9189 |
if ($bracketed === static::$true) {
|
9190 |
$bracketed = true;
|
9211 |
}
|
9212 |
}
|
9213 |
|
9214 |
+
$res = [Type::T_LIST, $separator, array_merge($list1[2], $list2[2])];
|
|
|
|
|
|
|
|
|
9215 |
|
9216 |
if ($bracketed) {
|
9217 |
$res['enclosing'] = 'bracket';
|
9220 |
return $res;
|
9221 |
}
|
9222 |
|
9223 |
+
protected static $libAppend = ['list', 'val', 'separator:auto'];
|
9224 |
protected function libAppend($args)
|
9225 |
{
|
9226 |
list($list1, $value, $sep) = $args;
|
9227 |
|
9228 |
$list1 = $this->coerceList($list1, ' ', true);
|
9229 |
+
|
9230 |
+
switch ($this->compileStringContent($this->assertString($sep, 'separator'))) {
|
9231 |
+
case 'comma':
|
9232 |
+
$separator = ',';
|
9233 |
+
break;
|
9234 |
+
|
9235 |
+
case 'space':
|
9236 |
+
$separator = ' ';
|
9237 |
+
break;
|
9238 |
+
|
9239 |
+
case 'slash':
|
9240 |
+
$separator = '/';
|
9241 |
+
break;
|
9242 |
+
|
9243 |
+
case 'auto':
|
9244 |
+
$separator = $list1[1] === '' && \count($list1[2]) <= 1 && (empty($list1['enclosing']) || $list1['enclosing'] === 'parent') ? ' ' : $list1[1];
|
9245 |
+
break;
|
9246 |
+
|
9247 |
+
default:
|
9248 |
+
throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator');
|
9249 |
+
}
|
9250 |
+
|
9251 |
+
$res = [Type::T_LIST, $separator, array_merge($list1[2], [$value])];
|
9252 |
|
9253 |
if (isset($list1['enclosing'])) {
|
9254 |
$res['enclosing'] = $list1['enclosing'];
|
9257 |
return $res;
|
9258 |
}
|
9259 |
|
9260 |
+
protected static $libZip = ['lists...'];
|
9261 |
protected function libZip($args)
|
9262 |
{
|
9263 |
+
$argLists = [];
|
9264 |
+
foreach ($args[0][2] as $arg) {
|
9265 |
+
$argLists[] = $this->coerceList($arg);
|
9266 |
}
|
9267 |
|
9268 |
$lists = [];
|
9269 |
+
$firstList = array_shift($argLists);
|
9270 |
|
9271 |
+
$result = [Type::T_LIST, ',', $lists];
|
9272 |
+
if (! \is_null($firstList)) {
|
9273 |
+
foreach ($firstList[2] as $key => $item) {
|
9274 |
+
$list = [Type::T_LIST, ' ', [$item]];
|
9275 |
|
9276 |
+
foreach ($argLists as $arg) {
|
9277 |
+
if (isset($arg[2][$key])) {
|
9278 |
+
$list[2][] = $arg[2][$key];
|
9279 |
+
} else {
|
9280 |
+
break 2;
|
9281 |
+
}
|
9282 |
}
|
9283 |
+
|
9284 |
+
$lists[] = $list;
|
9285 |
}
|
9286 |
|
9287 |
+
$result[2] = $lists;
|
9288 |
+
} else {
|
9289 |
+
$result['enclosing'] = 'parent';
|
9290 |
}
|
9291 |
|
9292 |
+
return $result;
|
9293 |
}
|
9294 |
|
9295 |
protected static $libTypeOf = ['value'];
|
9297 |
{
|
9298 |
$value = $args[0];
|
9299 |
|
9300 |
+
return [Type::T_KEYWORD, $this->getTypeOf($value)];
|
9301 |
+
}
|
9302 |
+
|
9303 |
+
/**
|
9304 |
+
* @param array|Number $value
|
9305 |
+
*
|
9306 |
+
* @return string
|
9307 |
+
*/
|
9308 |
+
private function getTypeOf($value)
|
9309 |
+
{
|
9310 |
switch ($value[0]) {
|
9311 |
case Type::T_KEYWORD:
|
9312 |
if ($value === static::$true || $value === static::$false) {
|
9321 |
case Type::T_FUNCTION:
|
9322 |
return 'string';
|
9323 |
|
9324 |
+
case Type::T_FUNCTION_REFERENCE:
|
9325 |
+
return 'function';
|
9326 |
+
|
9327 |
case Type::T_LIST:
|
9328 |
+
if (isset($value[3]) && \is_array($value[3])) {
|
9329 |
return 'arglist';
|
9330 |
}
|
9331 |
|
9338 |
protected static $libUnit = ['number'];
|
9339 |
protected function libUnit($args)
|
9340 |
{
|
9341 |
+
$num = $this->assertNumber($args[0], 'number');
|
|
|
|
|
|
|
|
|
9342 |
|
9343 |
+
return [Type::T_STRING, '"', [$num->unitStr()]];
|
9344 |
}
|
9345 |
|
9346 |
protected static $libUnitless = ['number'];
|
9347 |
protected function libUnitless($args)
|
9348 |
{
|
9349 |
+
$value = $this->assertNumber($args[0], 'number');
|
9350 |
|
9351 |
+
return $this->toBool($value->unitless());
|
9352 |
}
|
9353 |
|
9354 |
+
protected static $libComparable = [
|
9355 |
+
['number1', 'number2'],
|
9356 |
+
['number-1', 'number-2']
|
9357 |
+
];
|
9358 |
protected function libComparable($args)
|
9359 |
{
|
9360 |
list($number1, $number2) = $args;
|
9361 |
|
9362 |
+
if (
|
9363 |
+
! $number1 instanceof Number ||
|
9364 |
+
! $number2 instanceof Number
|
9365 |
) {
|
9366 |
+
throw $this->error('Invalid argument(s) for "comparable"');
|
|
|
|
|
9367 |
}
|
9368 |
|
9369 |
+
return $this->toBool($number1->isComparableTo($number2));
|
|
|
|
|
|
|
9370 |
}
|
9371 |
|
9372 |
protected static $libStrIndex = ['string', 'substring'];
|
9373 |
protected function libStrIndex($args)
|
9374 |
{
|
9375 |
+
$string = $this->assertString($args[0], 'string');
|
9376 |
$stringContent = $this->compileStringContent($string);
|
9377 |
|
9378 |
+
$substring = $this->assertString($args[1], 'substring');
|
9379 |
$substringContent = $this->compileStringContent($substring);
|
9380 |
|
9381 |
+
if (! \strlen($substringContent)) {
|
9382 |
+
$result = 0;
|
9383 |
+
} else {
|
9384 |
+
$result = Util::mbStrpos($stringContent, $substringContent);
|
9385 |
+
}
|
9386 |
|
9387 |
+
return $result === false ? static::$null : new Number($result + 1, '');
|
9388 |
}
|
9389 |
|
9390 |
protected static $libStrInsert = ['string', 'insert', 'index'];
|
9391 |
protected function libStrInsert($args)
|
9392 |
{
|
9393 |
+
$string = $this->assertString($args[0], 'string');
|
9394 |
$stringContent = $this->compileStringContent($string);
|
9395 |
|
9396 |
+
$insert = $this->assertString($args[1], 'insert');
|
9397 |
$insertContent = $this->compileStringContent($insert);
|
9398 |
|
9399 |
+
$index = $this->assertInteger($args[2], 'index');
|
9400 |
+
if ($index > 0) {
|
9401 |
+
$index = $index - 1;
|
9402 |
+
}
|
9403 |
+
if ($index < 0) {
|
9404 |
+
$index = max(Util::mbStrlen($stringContent) + 1 + $index, 0);
|
9405 |
+
}
|
9406 |
|
9407 |
+
$string[2] = [
|
9408 |
+
Util::mbSubstr($stringContent, 0, $index),
|
9409 |
+
$insertContent,
|
9410 |
+
Util::mbSubstr($stringContent, $index)
|
9411 |
+
];
|
9412 |
|
9413 |
return $string;
|
9414 |
}
|
9416 |
protected static $libStrLength = ['string'];
|
9417 |
protected function libStrLength($args)
|
9418 |
{
|
9419 |
+
$string = $this->assertString($args[0], 'string');
|
9420 |
$stringContent = $this->compileStringContent($string);
|
9421 |
|
9422 |
+
return new Number(Util::mbStrlen($stringContent), '');
|
9423 |
}
|
9424 |
|
9425 |
protected static $libStrSlice = ['string', 'start-at', 'end-at:-1'];
|
9426 |
protected function libStrSlice($args)
|
9427 |
{
|
9428 |
+
$string = $this->assertString($args[0], 'string');
|
9429 |
+
$stringContent = $this->compileStringContent($string);
|
9430 |
+
|
9431 |
+
$start = $this->assertNumber($args[1], 'start-at');
|
9432 |
+
$start->assertNoUnits('start-at');
|
9433 |
+
$startInt = $this->assertInteger($start, 'start-at');
|
9434 |
+
$end = $this->assertNumber($args[2], 'end-at');
|
9435 |
+
$end->assertNoUnits('end-at');
|
9436 |
+
$endInt = $this->assertInteger($end, 'end-at');
|
9437 |
+
|
9438 |
+
if ($endInt === 0) {
|
9439 |
+
return [Type::T_STRING, $string[1], []];
|
9440 |
}
|
9441 |
|
9442 |
+
if ($startInt > 0) {
|
9443 |
+
$startInt--;
|
9444 |
+
}
|
9445 |
|
9446 |
+
if ($endInt < 0) {
|
9447 |
+
$endInt = Util::mbStrlen($stringContent) + $endInt;
|
9448 |
+
} else {
|
9449 |
+
$endInt--;
|
9450 |
+
}
|
9451 |
|
9452 |
+
if ($endInt < $startInt) {
|
9453 |
+
return [Type::T_STRING, $string[1], []];
|
9454 |
}
|
9455 |
|
9456 |
+
$length = $endInt - $startInt + 1; // The end of the slice is inclusive
|
|
|
9457 |
|
9458 |
+
$string[2] = [Util::mbSubstr($stringContent, $startInt, $length)];
|
|
|
|
|
9459 |
|
9460 |
return $string;
|
9461 |
}
|
9463 |
protected static $libToLowerCase = ['string'];
|
9464 |
protected function libToLowerCase($args)
|
9465 |
{
|
9466 |
+
$string = $this->assertString($args[0], 'string');
|
9467 |
$stringContent = $this->compileStringContent($string);
|
9468 |
|
9469 |
+
$string[2] = [$this->stringTransformAsciiOnly($stringContent, 'strtolower')];
|
9470 |
|
9471 |
return $string;
|
9472 |
}
|
9474 |
protected static $libToUpperCase = ['string'];
|
9475 |
protected function libToUpperCase($args)
|
9476 |
{
|
9477 |
+
$string = $this->assertString($args[0], 'string');
|
9478 |
$stringContent = $this->compileStringContent($string);
|
9479 |
|
9480 |
+
$string[2] = [$this->stringTransformAsciiOnly($stringContent, 'strtoupper')];
|
9481 |
|
9482 |
return $string;
|
9483 |
}
|
9484 |
|
9485 |
+
/**
|
9486 |
+
* Apply a filter on a string content, only on ascii chars
|
9487 |
+
* let extended chars untouched
|
9488 |
+
*
|
9489 |
+
* @param string $stringContent
|
9490 |
+
* @param callable $filter
|
9491 |
+
* @return string
|
9492 |
+
*/
|
9493 |
+
protected function stringTransformAsciiOnly($stringContent, $filter)
|
9494 |
+
{
|
9495 |
+
$mblength = Util::mbStrlen($stringContent);
|
9496 |
+
if ($mblength === strlen($stringContent)) {
|
9497 |
+
return $filter($stringContent);
|
9498 |
+
}
|
9499 |
+
$filteredString = "";
|
9500 |
+
for ($i = 0; $i < $mblength; $i++) {
|
9501 |
+
$char = Util::mbSubstr($stringContent, $i, 1);
|
9502 |
+
if (strlen($char) > 1) {
|
9503 |
+
$filteredString .= $char;
|
9504 |
+
} else {
|
9505 |
+
$filteredString .= $filter($char);
|
9506 |
+
}
|
9507 |
+
}
|
9508 |
+
|
9509 |
+
return $filteredString;
|
9510 |
+
}
|
9511 |
+
|
9512 |
protected static $libFeatureExists = ['feature'];
|
9513 |
protected function libFeatureExists($args)
|
9514 |
{
|
9515 |
+
$string = $this->assertString($args[0], 'feature');
|
9516 |
$name = $this->compileStringContent($string);
|
9517 |
|
9518 |
return $this->toBool(
|
9523 |
protected static $libFunctionExists = ['name'];
|
9524 |
protected function libFunctionExists($args)
|
9525 |
{
|
9526 |
+
$string = $this->assertString($args[0], 'name');
|
9527 |
$name = $this->compileStringContent($string);
|
9528 |
|
9529 |
// user defined functions
|
9530 |
if ($this->has(static::$namespaces['function'] . $name)) {
|
9531 |
+
return self::$true;
|
9532 |
}
|
9533 |
|
9534 |
$name = $this->normalizeName($name);
|
9535 |
|
9536 |
if (isset($this->userFunctions[$name])) {
|
9537 |
+
return self::$true;
|
9538 |
}
|
9539 |
|
9540 |
// built-in functions
|
9546 |
protected static $libGlobalVariableExists = ['name'];
|
9547 |
protected function libGlobalVariableExists($args)
|
9548 |
{
|
9549 |
+
$string = $this->assertString($args[0], 'name');
|
9550 |
$name = $this->compileStringContent($string);
|
9551 |
|
9552 |
+
return $this->toBool($this->has($name, $this->rootEnv));
|
9553 |
}
|
9554 |
|
9555 |
protected static $libMixinExists = ['name'];
|
9556 |
protected function libMixinExists($args)
|
9557 |
{
|
9558 |
+
$string = $this->assertString($args[0], 'name');
|
9559 |
$name = $this->compileStringContent($string);
|
9560 |
|
9561 |
+
return $this->toBool($this->has(static::$namespaces['mixin'] . $name));
|
9562 |
}
|
9563 |
|
9564 |
protected static $libVariableExists = ['name'];
|
9565 |
protected function libVariableExists($args)
|
9566 |
{
|
9567 |
+
$string = $this->assertString($args[0], 'name');
|
9568 |
$name = $this->compileStringContent($string);
|
9569 |
|
9570 |
+
return $this->toBool($this->has($name));
|
9571 |
}
|
9572 |
|
9573 |
+
protected static $libCounter = ['args...'];
|
9574 |
/**
|
9575 |
* Workaround IE7's content counter bug.
|
9576 |
*
|
9580 |
*/
|
9581 |
protected function libCounter($args)
|
9582 |
{
|
9583 |
+
$list = array_map([$this, 'compileValue'], $args[0][2]);
|
9584 |
|
9585 |
return [Type::T_STRING, '', ['counter(' . implode(',', $list) . ')']];
|
9586 |
}
|
9587 |
|
9588 |
+
protected static $libRandom = ['limit:null'];
|
9589 |
protected function libRandom($args)
|
9590 |
{
|
9591 |
+
if (isset($args[0]) && $args[0] !== static::$null) {
|
9592 |
+
$n = $this->assertInteger($args[0], 'limit');
|
9593 |
|
9594 |
if ($n < 1) {
|
9595 |
+
throw new SassScriptException("\$limit: Must be greater than 0, was $n.");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9596 |
}
|
9597 |
|
9598 |
+
return new Number(mt_rand(1, $n), '');
|
9599 |
}
|
9600 |
|
9601 |
+
$max = mt_getrandmax();
|
9602 |
+
return new Number(mt_rand(0, $max - 1) / $max, '');
|
9603 |
}
|
9604 |
|
9605 |
+
protected static $libUniqueId = [];
|
9606 |
protected function libUniqueId()
|
9607 |
{
|
9608 |
static $id;
|
9609 |
|
9610 |
if (! isset($id)) {
|
9611 |
+
$id = PHP_INT_SIZE === 4
|
9612 |
+
? mt_rand(0, pow(36, 5)) . str_pad(mt_rand(0, pow(36, 5)) % 10000000, 7, '0', STR_PAD_LEFT)
|
9613 |
+
: mt_rand(0, pow(36, 8));
|
9614 |
}
|
9615 |
|
9616 |
$id += mt_rand(0, 10) + 1;
|
9618 |
return [Type::T_STRING, '', ['u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
|
9619 |
}
|
9620 |
|
9621 |
+
/**
|
9622 |
+
* @param array|Number $value
|
9623 |
+
* @param bool $force_enclosing_display
|
9624 |
+
*
|
9625 |
+
* @return array
|
9626 |
+
*/
|
9627 |
protected function inspectFormatValue($value, $force_enclosing_display = false)
|
9628 |
{
|
9629 |
if ($value === static::$null) {
|
9632 |
|
9633 |
$stringValue = [$value];
|
9634 |
|
9635 |
+
if ($value instanceof Number) {
|
9636 |
+
return [Type::T_STRING, '', $stringValue];
|
9637 |
+
}
|
9638 |
+
|
9639 |
if ($value[0] === Type::T_LIST) {
|
9640 |
if (end($value[2]) === static::$null) {
|
9641 |
array_pop($value[2]);
|
9643 |
$force_enclosing_display = true;
|
9644 |
}
|
9645 |
|
9646 |
+
if (
|
9647 |
+
! empty($value['enclosing']) &&
|
9648 |
($force_enclosing_display ||
|
9649 |
($value['enclosing'] === 'bracket') ||
|
9650 |
! \count($value[2]))
|
9651 |
) {
|
9652 |
+
$value['enclosing'] = 'forced_' . $value['enclosing'];
|
9653 |
$force_enclosing_display = true;
|
9654 |
+
} elseif (! \count($value[2])) {
|
9655 |
+
$value['enclosing'] = 'forced_parent';
|
9656 |
}
|
9657 |
|
9658 |
foreach ($value[2] as $k => $listelement) {
|
9676 |
/**
|
9677 |
* Preprocess selector args
|
9678 |
*
|
9679 |
+
* @param array $arg
|
9680 |
+
* @param string|null $varname
|
9681 |
+
* @param bool $allowParent
|
9682 |
*
|
9683 |
+
* @return array
|
9684 |
*/
|
9685 |
+
protected function getSelectorArg($arg, $varname = null, $allowParent = false)
|
9686 |
{
|
9687 |
static $parser = null;
|
9688 |
|
9690 |
$parser = $this->parserFactory(__METHOD__);
|
9691 |
}
|
9692 |
|
9693 |
+
if (! $this->checkSelectorArgType($arg)) {
|
9694 |
+
$var_value = $this->compileValue($arg);
|
9695 |
+
throw SassScriptException::forArgument("$var_value is not a valid selector: it must be a string, a list of strings, or a list of lists of strings", $varname);
|
9696 |
+
}
|
9697 |
+
|
9698 |
+
|
9699 |
+
if ($arg[0] === Type::T_STRING) {
|
9700 |
+
$arg[1] = '';
|
9701 |
+
}
|
9702 |
$arg = $this->compileValue($arg);
|
9703 |
|
9704 |
$parsedSelector = [];
|
9705 |
|
9706 |
+
if ($parser->parseSelector($arg, $parsedSelector, true)) {
|
9707 |
$selector = $this->evalSelectors($parsedSelector);
|
9708 |
$gluedSelector = $this->glueFunctionSelectors($selector);
|
9709 |
|
9710 |
+
if (! $allowParent) {
|
9711 |
+
foreach ($gluedSelector as $selector) {
|
9712 |
+
foreach ($selector as $s) {
|
9713 |
+
if (in_array(static::$selfSelector, $s)) {
|
9714 |
+
throw SassScriptException::forArgument("Parent selectors aren't allowed here.", $varname);
|
9715 |
+
}
|
9716 |
+
}
|
9717 |
+
}
|
9718 |
+
}
|
9719 |
+
|
9720 |
return $gluedSelector;
|
9721 |
}
|
9722 |
|
9723 |
+
throw SassScriptException::forArgument("expected more input, invalid selector.", $varname);
|
9724 |
+
}
|
9725 |
+
|
9726 |
+
/**
|
9727 |
+
* Check variable type for getSelectorArg() function
|
9728 |
+
* @param array $arg
|
9729 |
+
* @param int $maxDepth
|
9730 |
+
* @return bool
|
9731 |
+
*/
|
9732 |
+
protected function checkSelectorArgType($arg, $maxDepth = 2)
|
9733 |
+
{
|
9734 |
+
if ($arg[0] === Type::T_LIST && $maxDepth > 0) {
|
9735 |
+
foreach ($arg[2] as $elt) {
|
9736 |
+
if (! $this->checkSelectorArgType($elt, $maxDepth - 1)) {
|
9737 |
+
return false;
|
9738 |
+
}
|
9739 |
+
}
|
9740 |
+
return true;
|
9741 |
+
}
|
9742 |
+
if (!in_array($arg[0], [Type::T_STRING, Type::T_KEYWORD])) {
|
9743 |
+
return false;
|
9744 |
+
}
|
9745 |
+
return true;
|
9746 |
}
|
9747 |
|
9748 |
/**
|
9750 |
*
|
9751 |
* @param array $selectors
|
9752 |
*
|
9753 |
+
* @return array
|
9754 |
*/
|
9755 |
protected function formatOutputSelector($selectors)
|
9756 |
{
|
9757 |
+
$selectors = $this->collapseSelectorsAsList($selectors);
|
9758 |
|
9759 |
return $selectors;
|
9760 |
}
|
9764 |
{
|
9765 |
list($super, $sub) = $args;
|
9766 |
|
9767 |
+
$super = $this->getSelectorArg($super, 'super');
|
9768 |
+
$sub = $this->getSelectorArg($sub, 'sub');
|
9769 |
|
9770 |
+
return $this->toBool($this->isSuperSelector($super, $sub));
|
9771 |
}
|
9772 |
|
9773 |
/**
|
9776 |
* @param array $super
|
9777 |
* @param array $sub
|
9778 |
*
|
9779 |
+
* @return bool
|
9780 |
*/
|
9781 |
protected function isSuperSelector($super, $sub)
|
9782 |
{
|
9783 |
// one and only one selector for each arg
|
9784 |
+
if (! $super) {
|
9785 |
+
throw $this->error('Invalid super selector for isSuperSelector()');
|
9786 |
+
}
|
9787 |
+
|
9788 |
+
if (! $sub) {
|
9789 |
+
throw $this->error('Invalid sub selector for isSuperSelector()');
|
9790 |
+
}
|
9791 |
+
|
9792 |
+
if (count($sub) > 1) {
|
9793 |
+
foreach ($sub as $s) {
|
9794 |
+
if (! $this->isSuperSelector($super, [$s])) {
|
9795 |
+
return false;
|
9796 |
+
}
|
9797 |
+
}
|
9798 |
+
return true;
|
9799 |
}
|
9800 |
|
9801 |
+
if (count($super) > 1) {
|
9802 |
+
foreach ($super as $s) {
|
9803 |
+
if ($this->isSuperSelector([$s], $sub)) {
|
9804 |
+
return true;
|
9805 |
+
}
|
9806 |
+
}
|
9807 |
+
return false;
|
9808 |
}
|
9809 |
|
9810 |
$super = reset($super);
|
9857 |
* @param array $superParts
|
9858 |
* @param array $subParts
|
9859 |
*
|
9860 |
+
* @return bool
|
9861 |
*/
|
9862 |
protected function isSuperPart($superParts, $subParts)
|
9863 |
{
|
9886 |
$args = $args[2];
|
9887 |
|
9888 |
if (\count($args) < 1) {
|
9889 |
+
throw $this->error('selector-append() needs at least 1 argument');
|
9890 |
}
|
9891 |
|
9892 |
+
$selectors = [];
|
9893 |
+
foreach ($args as $arg) {
|
9894 |
+
$selectors[] = $this->getSelectorArg($arg, 'selector');
|
9895 |
+
}
|
9896 |
|
9897 |
return $this->formatOutputSelector($this->selectorAppend($selectors));
|
9898 |
}
|
9911 |
$lastSelectors = array_pop($selectors);
|
9912 |
|
9913 |
if (! $lastSelectors) {
|
9914 |
+
throw $this->error('Invalid selector list in selector-append()');
|
9915 |
}
|
9916 |
|
9917 |
while (\count($selectors)) {
|
9918 |
$previousSelectors = array_pop($selectors);
|
9919 |
|
9920 |
if (! $previousSelectors) {
|
9921 |
+
throw $this->error('Invalid selector list in selector-append()');
|
9922 |
}
|
9923 |
|
9924 |
// do the trick, happening $lastSelector to $previousSelector
|
9925 |
$appended = [];
|
9926 |
|
9927 |
+
foreach ($previousSelectors as $previousSelector) {
|
9928 |
+
foreach ($lastSelectors as $lastSelector) {
|
9929 |
+
$previous = $previousSelector;
|
9930 |
+
foreach ($previousSelector as $j => $previousSelectorParts) {
|
9931 |
+
foreach ($lastSelector as $lastSelectorParts) {
|
9932 |
+
foreach ($lastSelectorParts as $lastSelectorPart) {
|
9933 |
+
$previous[$j][] = $lastSelectorPart;
|
|
|
9934 |
}
|
9935 |
}
|
9936 |
}
|
|
|
9937 |
|
9938 |
+
$appended[] = $previous;
|
|
|
9939 |
}
|
9940 |
}
|
9941 |
|
9945 |
return $lastSelectors;
|
9946 |
}
|
9947 |
|
9948 |
+
protected static $libSelectorExtend = [
|
9949 |
+
['selector', 'extendee', 'extender'],
|
9950 |
+
['selectors', 'extendee', 'extender']
|
9951 |
+
];
|
9952 |
protected function libSelectorExtend($args)
|
9953 |
{
|
9954 |
list($selectors, $extendee, $extender) = $args;
|
9955 |
|
9956 |
+
$selectors = $this->getSelectorArg($selectors, 'selector');
|
9957 |
+
$extendee = $this->getSelectorArg($extendee, 'extendee');
|
9958 |
+
$extender = $this->getSelectorArg($extender, 'extender');
|
9959 |
|
9960 |
if (! $selectors || ! $extendee || ! $extender) {
|
9961 |
+
throw $this->error('selector-extend() invalid arguments');
|
9962 |
}
|
9963 |
|
9964 |
$extended = $this->extendOrReplaceSelectors($selectors, $extendee, $extender);
|
9966 |
return $this->formatOutputSelector($extended);
|
9967 |
}
|
9968 |
|
9969 |
+
protected static $libSelectorReplace = [
|
9970 |
+
['selector', 'original', 'replacement'],
|
9971 |
+
['selectors', 'original', 'replacement']
|
9972 |
+
];
|
9973 |
protected function libSelectorReplace($args)
|
9974 |
{
|
9975 |
list($selectors, $original, $replacement) = $args;
|
9976 |
|
9977 |
+
$selectors = $this->getSelectorArg($selectors, 'selector');
|
9978 |
+
$original = $this->getSelectorArg($original, 'original');
|
9979 |
+
$replacement = $this->getSelectorArg($replacement, 'replacement');
|
9980 |
|
9981 |
if (! $selectors || ! $original || ! $replacement) {
|
9982 |
+
throw $this->error('selector-replace() invalid arguments');
|
9983 |
}
|
9984 |
|
9985 |
$replaced = $this->extendOrReplaceSelectors($selectors, $original, $replacement, true);
|
9991 |
* Extend/replace in selectors
|
9992 |
* used by selector-extend and selector-replace that use the same logic
|
9993 |
*
|
9994 |
+
* @param array $selectors
|
9995 |
+
* @param array $extendee
|
9996 |
+
* @param array $extender
|
9997 |
+
* @param bool $replace
|
9998 |
*
|
9999 |
* @return array
|
10000 |
*/
|
10007 |
$this->extendsMap = [];
|
10008 |
|
10009 |
foreach ($extendee as $es) {
|
10010 |
+
if (\count($es) !== 1) {
|
10011 |
+
throw $this->error('Can\'t extend complex selector.');
|
10012 |
+
}
|
10013 |
+
|
10014 |
// only use the first one
|
10015 |
$this->pushExtends(reset($es), $extender, null);
|
10016 |
}
|
10027 |
$this->matchExtends($selector, $extended);
|
10028 |
|
10029 |
// if didnt match, keep the original selector if we are in a replace operation
|
10030 |
+
if ($replace && \count($extended) === $n) {
|
10031 |
$extended[] = $selector;
|
10032 |
}
|
10033 |
}
|
10046 |
$args = $args[2];
|
10047 |
|
10048 |
if (\count($args) < 1) {
|
10049 |
+
throw $this->error('selector-nest() needs at least 1 argument');
|
10050 |
+
}
|
10051 |
+
|
10052 |
+
$selectorsMap = [];
|
10053 |
+
foreach ($args as $arg) {
|
10054 |
+
$selectorsMap[] = $this->getSelectorArg($arg, 'selector', true);
|
10055 |
}
|
10056 |
|
10057 |
+
assert(!empty($selectorsMap));
|
10058 |
+
|
10059 |
$envs = [];
|
10060 |
|
10061 |
foreach ($selectorsMap as $selectors) {
|
10072 |
return $this->formatOutputSelector($outputSelectors);
|
10073 |
}
|
10074 |
|
10075 |
+
protected static $libSelectorParse = [
|
10076 |
+
['selector'],
|
10077 |
+
['selectors']
|
10078 |
+
];
|
10079 |
protected function libSelectorParse($args)
|
10080 |
{
|
10081 |
$selectors = reset($args);
|
10082 |
+
$selectors = $this->getSelectorArg($selectors, 'selector');
|
10083 |
|
10084 |
return $this->formatOutputSelector($selectors);
|
10085 |
}
|
10089 |
{
|
10090 |
list($selectors1, $selectors2) = $args;
|
10091 |
|
10092 |
+
$selectors1 = $this->getSelectorArg($selectors1, 'selectors1');
|
10093 |
+
$selectors2 = $this->getSelectorArg($selectors2, 'selectors2');
|
10094 |
|
10095 |
if (! $selectors1 || ! $selectors2) {
|
10096 |
+
throw $this->error('selector-unify() invalid arguments');
|
10097 |
}
|
10098 |
|
10099 |
// only consider the first compound of each
|
10113 |
* @param array $compound1
|
10114 |
* @param array $compound2
|
10115 |
*
|
10116 |
+
* @return array
|
10117 |
*/
|
10118 |
protected function unifyCompoundSelectors($compound1, $compound2)
|
10119 |
{
|
10229 |
* @param array $part
|
10230 |
* @param array $compound
|
10231 |
*
|
10232 |
+
* @return array|false
|
10233 |
*/
|
10234 |
protected function matchPartInCompound($part, $compound)
|
10235 |
{
|
10324 |
* @param string $tag1
|
10325 |
* @param string $tag2
|
10326 |
*
|
10327 |
+
* @return array|false
|
10328 |
*/
|
10329 |
protected function checkCompatibleTags($tag1, $tag2)
|
10330 |
{
|
10347 |
/**
|
10348 |
* Find the html tag name in a selector parts list
|
10349 |
*
|
10350 |
+
* @param string[] $parts
|
10351 |
*
|
10352 |
+
* @return string
|
10353 |
*/
|
10354 |
protected function findTagName($parts)
|
10355 |
{
|
10366 |
protected function libSimpleSelectors($args)
|
10367 |
{
|
10368 |
$selector = reset($args);
|
10369 |
+
$selector = $this->getSelectorArg($selector, 'selector');
|
10370 |
|
10371 |
// remove selectors list layer, keeping the first one
|
10372 |
$selector = reset($selector);
|
10386 |
protected static $libScssphpGlob = ['pattern'];
|
10387 |
protected function libScssphpGlob($args)
|
10388 |
{
|
10389 |
+
@trigger_error(sprintf('The "scssphp-glob" function is deprecated an will be removed in ScssPhp 2.0. Register your own alternative through "%s::registerFunction', __CLASS__), E_USER_DEPRECATED);
|
10390 |
+
|
10391 |
+
$this->logger->warn('The "scssphp-glob" function is deprecated an will be removed in ScssPhp 2.0.', true);
|
10392 |
+
|
10393 |
+
$string = $this->assertString($args[0], 'pattern');
|
10394 |
$pattern = $this->compileStringContent($string);
|
10395 |
$matches = glob($pattern);
|
10396 |
$listParts = [];
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler/CachedResult.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Compiler;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\CompilationResult;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @internal
|
19 |
+
*/
|
20 |
+
class CachedResult
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* @var CompilationResult
|
24 |
+
*/
|
25 |
+
private $result;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var array<string, int>
|
29 |
+
*/
|
30 |
+
private $parsedFiles;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var array
|
34 |
+
* @phpstan-var list<array{currentDir: string|null, path: string, filePath: string}>
|
35 |
+
*/
|
36 |
+
private $resolvedImports;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param CompilationResult $result
|
40 |
+
* @param array<string, int> $parsedFiles
|
41 |
+
* @param array $resolvedImports
|
42 |
+
*
|
43 |
+
* @phpstan-param list<array{currentDir: string|null, path: string, filePath: string}> $resolvedImports
|
44 |
+
*/
|
45 |
+
public function __construct(CompilationResult $result, array $parsedFiles, array $resolvedImports)
|
46 |
+
{
|
47 |
+
$this->result = $result;
|
48 |
+
$this->parsedFiles = $parsedFiles;
|
49 |
+
$this->resolvedImports = $resolvedImports;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @return CompilationResult
|
54 |
+
*/
|
55 |
+
public function getResult()
|
56 |
+
{
|
57 |
+
return $this->result;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return array<string, int>
|
62 |
+
*/
|
63 |
+
public function getParsedFiles()
|
64 |
+
{
|
65 |
+
return $this->parsedFiles;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @return array
|
70 |
+
*
|
71 |
+
* @phpstan-return list<array{currentDir: string|null, path: string, filePath: string}>
|
72 |
+
*/
|
73 |
+
public function getResolvedImports()
|
74 |
+
{
|
75 |
+
return $this->resolvedImports;
|
76 |
+
}
|
77 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Compiler/Environment.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,19 +16,41 @@ namespace ScssPhp\ScssPhp\Compiler;
|
|
15 |
* Compiler environment
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class Environment
|
20 |
{
|
21 |
/**
|
22 |
-
* @var \ScssPhp\ScssPhp\Block
|
23 |
*/
|
24 |
public $block;
|
25 |
|
26 |
/**
|
27 |
-
* @var \ScssPhp\ScssPhp\Compiler\Environment
|
28 |
*/
|
29 |
public $parent;
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
/**
|
32 |
* @var array
|
33 |
*/
|
@@ -39,7 +62,7 @@ class Environment
|
|
39 |
public $storeUnreduced;
|
40 |
|
41 |
/**
|
42 |
-
* @var
|
43 |
*/
|
44 |
public $depth;
|
45 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Compiler environment
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class Environment
|
23 |
{
|
24 |
/**
|
25 |
+
* @var \ScssPhp\ScssPhp\Block|null
|
26 |
*/
|
27 |
public $block;
|
28 |
|
29 |
/**
|
30 |
+
* @var \ScssPhp\ScssPhp\Compiler\Environment|null
|
31 |
*/
|
32 |
public $parent;
|
33 |
|
34 |
+
/**
|
35 |
+
* @var Environment|null
|
36 |
+
*/
|
37 |
+
public $declarationScopeParent;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @var Environment|null
|
41 |
+
*/
|
42 |
+
public $parentStore;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @var array|null
|
46 |
+
*/
|
47 |
+
public $selectors;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @var string|null
|
51 |
+
*/
|
52 |
+
public $marker;
|
53 |
+
|
54 |
/**
|
55 |
* @var array
|
56 |
*/
|
62 |
public $storeUnreduced;
|
63 |
|
64 |
/**
|
65 |
+
* @var int
|
66 |
*/
|
67 |
public $depth;
|
68 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/CompilerException.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,7 +16,9 @@ namespace ScssPhp\ScssPhp\Exception;
|
|
15 |
* Compiler exception
|
16 |
*
|
17 |
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
-
class CompilerException extends \Exception
|
20 |
{
|
21 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Compiler exception
|
17 |
*
|
18 |
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
+
class CompilerException extends \Exception implements SassException
|
23 |
{
|
24 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/ParserException.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,7 +16,43 @@ namespace ScssPhp\ScssPhp\Exception;
|
|
15 |
* Parser Exception
|
16 |
*
|
17 |
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
-
class ParserException extends \Exception
|
20 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Parser Exception
|
17 |
*
|
18 |
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
+
class ParserException extends \Exception implements SassException
|
23 |
{
|
24 |
+
/**
|
25 |
+
* @var array|null
|
26 |
+
* @phpstan-var array{string, int, int}|null
|
27 |
+
*/
|
28 |
+
private $sourcePosition;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Get source position
|
32 |
+
*
|
33 |
+
* @api
|
34 |
+
*
|
35 |
+
* @return array|null
|
36 |
+
* @phpstan-return array{string, int, int}|null
|
37 |
+
*/
|
38 |
+
public function getSourcePosition()
|
39 |
+
{
|
40 |
+
return $this->sourcePosition;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Set source position
|
45 |
+
*
|
46 |
+
* @api
|
47 |
+
*
|
48 |
+
* @param array $sourcePosition
|
49 |
+
*
|
50 |
+
* @return void
|
51 |
+
*
|
52 |
+
* @phpstan-param array{string, int, int} $sourcePosition
|
53 |
+
*/
|
54 |
+
public function setSourcePosition($sourcePosition)
|
55 |
+
{
|
56 |
+
$this->sourcePosition = $sourcePosition;
|
57 |
+
}
|
58 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/RangeException.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,7 +16,9 @@ namespace ScssPhp\ScssPhp\Exception;
|
|
15 |
* Range exception
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
-
class RangeException extends \Exception
|
20 |
{
|
21 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Range exception
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
+
class RangeException extends \Exception implements SassException
|
23 |
{
|
24 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/SassException.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ScssPhp\ScssPhp\Exception;
|
4 |
+
|
5 |
+
interface SassException
|
6 |
+
{
|
7 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/SassScriptException.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ScssPhp\ScssPhp\Exception;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* An exception thrown by SassScript.
|
7 |
+
*
|
8 |
+
* This class does not implement SassException on purpose, as it should
|
9 |
+
* never be returned to the outside code. The compilation will catch it
|
10 |
+
* and replace it with a SassException reporting the location of the
|
11 |
+
* error.
|
12 |
+
*/
|
13 |
+
class SassScriptException extends \Exception
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* Creates a SassScriptException with support for an argument name.
|
17 |
+
*
|
18 |
+
* This helper ensures a consistent handling of argument names in the
|
19 |
+
* error message, without duplicating it.
|
20 |
+
*
|
21 |
+
* @param string $message
|
22 |
+
* @param string|null $name The argument name, without $
|
23 |
+
*
|
24 |
+
* @return SassScriptException
|
25 |
+
*/
|
26 |
+
public static function forArgument($message, $name = null)
|
27 |
+
{
|
28 |
+
$varDisplay = !\is_null($name) ? "\${$name}: " : '';
|
29 |
+
|
30 |
+
return new self($varDisplay . $message);
|
31 |
+
}
|
32 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Exception/ServerException.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -11,11 +12,15 @@
|
|
11 |
|
12 |
namespace ScssPhp\ScssPhp\Exception;
|
13 |
|
|
|
|
|
14 |
/**
|
15 |
* Server Exception
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
-
class ServerException extends \Exception
|
20 |
{
|
21 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
12 |
|
13 |
namespace ScssPhp\ScssPhp\Exception;
|
14 |
|
15 |
+
@trigger_error(sprintf('The "%s" class is deprecated.', ServerException::class), E_USER_DEPRECATED);
|
16 |
+
|
17 |
/**
|
18 |
* Server Exception
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
21 |
+
*
|
22 |
+
* @deprecated The Scssphp server should define its own exception instead.
|
23 |
*/
|
24 |
+
class ServerException extends \Exception implements SassException
|
25 |
{
|
26 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -18,11 +19,13 @@ use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
|
|
18 |
* Base formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
21 |
*/
|
22 |
abstract class Formatter
|
23 |
{
|
24 |
/**
|
25 |
-
* @var
|
26 |
*/
|
27 |
public $indentLevel;
|
28 |
|
@@ -57,7 +60,7 @@ abstract class Formatter
|
|
57 |
public $assignSeparator;
|
58 |
|
59 |
/**
|
60 |
-
* @var
|
61 |
*/
|
62 |
public $keepSemicolons;
|
63 |
|
@@ -67,17 +70,17 @@ abstract class Formatter
|
|
67 |
protected $currentBlock;
|
68 |
|
69 |
/**
|
70 |
-
* @var
|
71 |
*/
|
72 |
protected $currentLine;
|
73 |
|
74 |
/**
|
75 |
-
* @var
|
76 |
*/
|
77 |
protected $currentColumn;
|
78 |
|
79 |
/**
|
80 |
-
* @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator
|
81 |
*/
|
82 |
protected $sourceMapGenerator;
|
83 |
|
@@ -138,6 +141,8 @@ abstract class Formatter
|
|
138 |
* Output lines inside a block
|
139 |
*
|
140 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
|
|
|
|
141 |
*/
|
142 |
protected function blockLines(OutputBlock $block)
|
143 |
{
|
@@ -155,9 +160,13 @@ abstract class Formatter
|
|
155 |
* Output block selectors
|
156 |
*
|
157 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
|
|
|
|
158 |
*/
|
159 |
protected function blockSelectors(OutputBlock $block)
|
160 |
{
|
|
|
|
|
161 |
$inner = $this->indentStr();
|
162 |
|
163 |
$this->write($inner
|
@@ -169,6 +178,8 @@ abstract class Formatter
|
|
169 |
* Output block children
|
170 |
*
|
171 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
|
|
|
|
172 |
*/
|
173 |
protected function blockChildren(OutputBlock $block)
|
174 |
{
|
@@ -181,6 +192,8 @@ abstract class Formatter
|
|
181 |
* Output non-empty block
|
182 |
*
|
183 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
|
|
|
|
184 |
*/
|
185 |
protected function block(OutputBlock $block)
|
186 |
{
|
@@ -226,7 +239,7 @@ abstract class Formatter
|
|
226 |
*
|
227 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
228 |
*
|
229 |
-
* @return
|
230 |
*/
|
231 |
protected function testEmptyChildren($block)
|
232 |
{
|
@@ -273,9 +286,18 @@ abstract class Formatter
|
|
273 |
|
274 |
ob_start();
|
275 |
|
276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
|
278 |
$out = ob_get_clean();
|
|
|
279 |
|
280 |
return $out;
|
281 |
}
|
@@ -284,6 +306,8 @@ abstract class Formatter
|
|
284 |
* Output content
|
285 |
*
|
286 |
* @param string $str
|
|
|
|
|
287 |
*/
|
288 |
protected function write($str)
|
289 |
{
|
@@ -297,7 +321,8 @@ abstract class Formatter
|
|
297 |
* Maybe Strip semi-colon appended by property(); it's a separator, not a terminator
|
298 |
* will be striped for real before a closing, otherwise displayed unchanged starting the next write
|
299 |
*/
|
300 |
-
if (
|
|
|
301 |
$str &&
|
302 |
(strpos($str, ';') !== false) &&
|
303 |
(substr($str, -1) === ';')
|
@@ -308,22 +333,43 @@ abstract class Formatter
|
|
308 |
}
|
309 |
|
310 |
if ($this->sourceMapGenerator) {
|
311 |
-
$this->sourceMapGenerator->addMapping(
|
312 |
-
$this->currentLine,
|
313 |
-
$this->currentColumn,
|
314 |
-
$this->currentBlock->sourceLine,
|
315 |
-
//columns from parser are off by one
|
316 |
-
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
317 |
-
$this->currentBlock->sourceName
|
318 |
-
);
|
319 |
-
|
320 |
$lines = explode("\n", $str);
|
321 |
-
$lineCount = \count($lines);
|
322 |
-
$this->currentLine += $lineCount-1;
|
323 |
-
|
324 |
$lastLine = array_pop($lines);
|
325 |
|
326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
}
|
328 |
|
329 |
echo $str;
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
19 |
* Base formatter
|
20 |
*
|
21 |
* @author Leaf Corcoran <leafot@gmail.com>
|
22 |
+
*
|
23 |
+
* @internal
|
24 |
*/
|
25 |
abstract class Formatter
|
26 |
{
|
27 |
/**
|
28 |
+
* @var int
|
29 |
*/
|
30 |
public $indentLevel;
|
31 |
|
60 |
public $assignSeparator;
|
61 |
|
62 |
/**
|
63 |
+
* @var bool
|
64 |
*/
|
65 |
public $keepSemicolons;
|
66 |
|
70 |
protected $currentBlock;
|
71 |
|
72 |
/**
|
73 |
+
* @var int
|
74 |
*/
|
75 |
protected $currentLine;
|
76 |
|
77 |
/**
|
78 |
+
* @var int
|
79 |
*/
|
80 |
protected $currentColumn;
|
81 |
|
82 |
/**
|
83 |
+
* @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator|null
|
84 |
*/
|
85 |
protected $sourceMapGenerator;
|
86 |
|
141 |
* Output lines inside a block
|
142 |
*
|
143 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
144 |
+
*
|
145 |
+
* @return void
|
146 |
*/
|
147 |
protected function blockLines(OutputBlock $block)
|
148 |
{
|
160 |
* Output block selectors
|
161 |
*
|
162 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
163 |
+
*
|
164 |
+
* @return void
|
165 |
*/
|
166 |
protected function blockSelectors(OutputBlock $block)
|
167 |
{
|
168 |
+
assert(! empty($block->selectors));
|
169 |
+
|
170 |
$inner = $this->indentStr();
|
171 |
|
172 |
$this->write($inner
|
178 |
* Output block children
|
179 |
*
|
180 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
181 |
+
*
|
182 |
+
* @return void
|
183 |
*/
|
184 |
protected function blockChildren(OutputBlock $block)
|
185 |
{
|
192 |
* Output non-empty block
|
193 |
*
|
194 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
195 |
+
*
|
196 |
+
* @return void
|
197 |
*/
|
198 |
protected function block(OutputBlock $block)
|
199 |
{
|
239 |
*
|
240 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
241 |
*
|
242 |
+
* @return bool
|
243 |
*/
|
244 |
protected function testEmptyChildren($block)
|
245 |
{
|
286 |
|
287 |
ob_start();
|
288 |
|
289 |
+
try {
|
290 |
+
$this->block($block);
|
291 |
+
} catch (\Exception $e) {
|
292 |
+
ob_end_clean();
|
293 |
+
throw $e;
|
294 |
+
} catch (\Throwable $e) {
|
295 |
+
ob_end_clean();
|
296 |
+
throw $e;
|
297 |
+
}
|
298 |
|
299 |
$out = ob_get_clean();
|
300 |
+
assert($out !== false);
|
301 |
|
302 |
return $out;
|
303 |
}
|
306 |
* Output content
|
307 |
*
|
308 |
* @param string $str
|
309 |
+
*
|
310 |
+
* @return void
|
311 |
*/
|
312 |
protected function write($str)
|
313 |
{
|
321 |
* Maybe Strip semi-colon appended by property(); it's a separator, not a terminator
|
322 |
* will be striped for real before a closing, otherwise displayed unchanged starting the next write
|
323 |
*/
|
324 |
+
if (
|
325 |
+
! $this->keepSemicolons &&
|
326 |
$str &&
|
327 |
(strpos($str, ';') !== false) &&
|
328 |
(substr($str, -1) === ';')
|
333 |
}
|
334 |
|
335 |
if ($this->sourceMapGenerator) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
$lines = explode("\n", $str);
|
|
|
|
|
|
|
337 |
$lastLine = array_pop($lines);
|
338 |
|
339 |
+
foreach ($lines as $line) {
|
340 |
+
// If the written line starts is empty, adding a mapping would add it for
|
341 |
+
// a non-existent column as we are at the end of the line
|
342 |
+
if ($line !== '') {
|
343 |
+
assert($this->currentBlock->sourceLine !== null);
|
344 |
+
assert($this->currentBlock->sourceName !== null);
|
345 |
+
$this->sourceMapGenerator->addMapping(
|
346 |
+
$this->currentLine,
|
347 |
+
$this->currentColumn,
|
348 |
+
$this->currentBlock->sourceLine,
|
349 |
+
//columns from parser are off by one
|
350 |
+
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
351 |
+
$this->currentBlock->sourceName
|
352 |
+
);
|
353 |
+
}
|
354 |
+
|
355 |
+
$this->currentLine++;
|
356 |
+
$this->currentColumn = 0;
|
357 |
+
}
|
358 |
+
|
359 |
+
if ($lastLine !== '') {
|
360 |
+
assert($this->currentBlock->sourceLine !== null);
|
361 |
+
assert($this->currentBlock->sourceName !== null);
|
362 |
+
$this->sourceMapGenerator->addMapping(
|
363 |
+
$this->currentLine,
|
364 |
+
$this->currentColumn,
|
365 |
+
$this->currentBlock->sourceLine,
|
366 |
+
//columns from parser are off by one
|
367 |
+
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
368 |
+
$this->currentBlock->sourceName
|
369 |
+
);
|
370 |
+
}
|
371 |
+
|
372 |
+
$this->currentColumn += \strlen($lastLine);
|
373 |
}
|
374 |
|
375 |
echo $str;
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Compact.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -17,6 +18,10 @@ use ScssPhp\ScssPhp\Formatter;
|
|
17 |
* Compact formatter
|
18 |
*
|
19 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
|
|
|
|
20 |
*/
|
21 |
class Compact extends Formatter
|
22 |
{
|
@@ -25,6 +30,8 @@ class Compact extends Formatter
|
|
25 |
*/
|
26 |
public function __construct()
|
27 |
{
|
|
|
|
|
28 |
$this->indentLevel = 0;
|
29 |
$this->indentChar = '';
|
30 |
$this->break = '';
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
18 |
* Compact formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
21 |
+
*
|
22 |
+
* @deprecated since 1.4.0. Use the Compressed formatter instead.
|
23 |
+
*
|
24 |
+
* @internal
|
25 |
*/
|
26 |
class Compact extends Formatter
|
27 |
{
|
30 |
*/
|
31 |
public function __construct()
|
32 |
{
|
33 |
+
@trigger_error('The Compact formatter is deprecated since 1.4.0. Use the Compressed formatter instead.', E_USER_DEPRECATED);
|
34 |
+
|
35 |
$this->indentLevel = 0;
|
36 |
$this->indentChar = '';
|
37 |
$this->break = '';
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Compressed.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,12 +13,13 @@
|
|
12 |
namespace ScssPhp\ScssPhp\Formatter;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Formatter;
|
15 |
-
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
16 |
|
17 |
/**
|
18 |
* Compressed formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
21 |
*/
|
22 |
class Compressed extends Formatter
|
23 |
{
|
@@ -48,8 +50,6 @@ class Compressed extends Formatter
|
|
48 |
foreach ($block->lines as $index => $line) {
|
49 |
if (substr($line, 0, 2) === '/*' && substr($line, 2, 1) !== '!') {
|
50 |
unset($block->lines[$index]);
|
51 |
-
} elseif (substr($line, 0, 3) === '/*!') {
|
52 |
-
$block->lines[$index] = '/*' . substr($line, 3);
|
53 |
}
|
54 |
}
|
55 |
|
@@ -67,6 +67,8 @@ class Compressed extends Formatter
|
|
67 |
*/
|
68 |
protected function blockSelectors(OutputBlock $block)
|
69 |
{
|
|
|
|
|
70 |
$inner = $this->indentStr();
|
71 |
|
72 |
$this->write(
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp\Formatter;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Formatter;
|
|
|
16 |
|
17 |
/**
|
18 |
* Compressed formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
21 |
+
*
|
22 |
+
* @internal
|
23 |
*/
|
24 |
class Compressed extends Formatter
|
25 |
{
|
50 |
foreach ($block->lines as $index => $line) {
|
51 |
if (substr($line, 0, 2) === '/*' && substr($line, 2, 1) !== '!') {
|
52 |
unset($block->lines[$index]);
|
|
|
|
|
53 |
}
|
54 |
}
|
55 |
|
67 |
*/
|
68 |
protected function blockSelectors(OutputBlock $block)
|
69 |
{
|
70 |
+
assert(! empty($block->selectors));
|
71 |
+
|
72 |
$inner = $this->indentStr();
|
73 |
|
74 |
$this->write(
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Crunched.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,12 +13,15 @@
|
|
12 |
namespace ScssPhp\ScssPhp\Formatter;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Formatter;
|
15 |
-
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
16 |
|
17 |
/**
|
18 |
* Crunched formatter
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
|
|
|
|
21 |
*/
|
22 |
class Crunched extends Formatter
|
23 |
{
|
@@ -26,6 +30,8 @@ class Crunched extends Formatter
|
|
26 |
*/
|
27 |
public function __construct()
|
28 |
{
|
|
|
|
|
29 |
$this->indentLevel = 0;
|
30 |
$this->indentChar = ' ';
|
31 |
$this->break = '';
|
@@ -65,6 +71,8 @@ class Crunched extends Formatter
|
|
65 |
*/
|
66 |
protected function blockSelectors(OutputBlock $block)
|
67 |
{
|
|
|
|
|
68 |
$inner = $this->indentStr();
|
69 |
|
70 |
$this->write(
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp\Formatter;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Formatter;
|
|
|
16 |
|
17 |
/**
|
18 |
* Crunched formatter
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
21 |
+
*
|
22 |
+
* @deprecated since 1.4.0. Use the Compressed formatter instead.
|
23 |
+
*
|
24 |
+
* @internal
|
25 |
*/
|
26 |
class Crunched extends Formatter
|
27 |
{
|
30 |
*/
|
31 |
public function __construct()
|
32 |
{
|
33 |
+
@trigger_error('The Crunched formatter is deprecated since 1.4.0. Use the Compressed formatter instead.', E_USER_DEPRECATED);
|
34 |
+
|
35 |
$this->indentLevel = 0;
|
36 |
$this->indentChar = ' ';
|
37 |
$this->break = '';
|
71 |
*/
|
72 |
protected function blockSelectors(OutputBlock $block)
|
73 |
{
|
74 |
+
assert(! empty($block->selectors));
|
75 |
+
|
76 |
$inner = $this->indentStr();
|
77 |
|
78 |
$this->write(
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Debug.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,12 +13,15 @@
|
|
12 |
namespace ScssPhp\ScssPhp\Formatter;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Formatter;
|
15 |
-
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
16 |
|
17 |
/**
|
18 |
* Debug formatter
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
|
|
|
|
21 |
*/
|
22 |
class Debug extends Formatter
|
23 |
{
|
@@ -26,6 +30,8 @@ class Debug extends Formatter
|
|
26 |
*/
|
27 |
public function __construct()
|
28 |
{
|
|
|
|
|
29 |
$this->indentLevel = 0;
|
30 |
$this->indentChar = '';
|
31 |
$this->break = "\n";
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp\Formatter;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Formatter;
|
|
|
16 |
|
17 |
/**
|
18 |
* Debug formatter
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
21 |
+
*
|
22 |
+
* @deprecated since 1.4.0.
|
23 |
+
*
|
24 |
+
* @internal
|
25 |
*/
|
26 |
class Debug extends Formatter
|
27 |
{
|
30 |
*/
|
31 |
public function __construct()
|
32 |
{
|
33 |
+
@trigger_error('The Debug formatter is deprecated since 1.4.0.', E_USER_DEPRECATED);
|
34 |
+
|
35 |
$this->indentLevel = 0;
|
36 |
$this->indentChar = '';
|
37 |
$this->break = "\n";
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Expanded.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,12 +13,13 @@
|
|
12 |
namespace ScssPhp\ScssPhp\Formatter;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Formatter;
|
15 |
-
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
16 |
|
17 |
/**
|
18 |
* Expanded formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
21 |
*/
|
22 |
class Expanded extends Formatter
|
23 |
{
|
@@ -55,7 +57,9 @@ class Expanded extends Formatter
|
|
55 |
|
56 |
foreach ($block->lines as $index => $line) {
|
57 |
if (substr($line, 0, 2) === '/*') {
|
58 |
-
$
|
|
|
|
|
59 |
}
|
60 |
}
|
61 |
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp\Formatter;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Formatter;
|
|
|
16 |
|
17 |
/**
|
18 |
* Expanded formatter
|
19 |
*
|
20 |
* @author Leaf Corcoran <leafot@gmail.com>
|
21 |
+
*
|
22 |
+
* @internal
|
23 |
*/
|
24 |
class Expanded extends Formatter
|
25 |
{
|
57 |
|
58 |
foreach ($block->lines as $index => $line) {
|
59 |
if (substr($line, 0, 2) === '/*') {
|
60 |
+
$replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
|
61 |
+
assert($replacedLine !== null);
|
62 |
+
$block->lines[$index] = $replacedLine;
|
63 |
}
|
64 |
}
|
65 |
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/Nested.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -12,18 +13,21 @@
|
|
12 |
namespace ScssPhp\ScssPhp\Formatter;
|
13 |
|
14 |
use ScssPhp\ScssPhp\Formatter;
|
15 |
-
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
16 |
use ScssPhp\ScssPhp\Type;
|
17 |
|
18 |
/**
|
19 |
* Nested formatter
|
20 |
*
|
21 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
|
|
|
|
22 |
*/
|
23 |
class Nested extends Formatter
|
24 |
{
|
25 |
/**
|
26 |
-
* @var
|
27 |
*/
|
28 |
private $depth;
|
29 |
|
@@ -32,6 +36,8 @@ class Nested extends Formatter
|
|
32 |
*/
|
33 |
public function __construct()
|
34 |
{
|
|
|
|
|
35 |
$this->indentLevel = 0;
|
36 |
$this->indentChar = ' ';
|
37 |
$this->break = "\n";
|
@@ -62,7 +68,9 @@ class Nested extends Formatter
|
|
62 |
|
63 |
foreach ($block->lines as $index => $line) {
|
64 |
if (substr($line, 0, 2) === '/*') {
|
65 |
-
$
|
|
|
|
|
66 |
}
|
67 |
}
|
68 |
|
@@ -97,7 +105,8 @@ class Nested extends Formatter
|
|
97 |
array_pop($depths);
|
98 |
$this->depth--;
|
99 |
|
100 |
-
if (
|
|
|
101 |
(($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
|
102 |
) {
|
103 |
$downLevel = $this->break;
|
@@ -214,7 +223,7 @@ class Nested extends Formatter
|
|
214 |
*
|
215 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
216 |
*
|
217 |
-
* @return
|
218 |
*/
|
219 |
private function hasFlatChild($block)
|
220 |
{
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
13 |
namespace ScssPhp\ScssPhp\Formatter;
|
14 |
|
15 |
use ScssPhp\ScssPhp\Formatter;
|
|
|
16 |
use ScssPhp\ScssPhp\Type;
|
17 |
|
18 |
/**
|
19 |
* Nested formatter
|
20 |
*
|
21 |
* @author Leaf Corcoran <leafot@gmail.com>
|
22 |
+
*
|
23 |
+
* @deprecated since 1.4.0. Use the Expanded formatter instead.
|
24 |
+
*
|
25 |
+
* @internal
|
26 |
*/
|
27 |
class Nested extends Formatter
|
28 |
{
|
29 |
/**
|
30 |
+
* @var int
|
31 |
*/
|
32 |
private $depth;
|
33 |
|
36 |
*/
|
37 |
public function __construct()
|
38 |
{
|
39 |
+
@trigger_error('The Nested formatter is deprecated since 1.4.0. Use the Expanded formatter instead.', E_USER_DEPRECATED);
|
40 |
+
|
41 |
$this->indentLevel = 0;
|
42 |
$this->indentChar = ' ';
|
43 |
$this->break = "\n";
|
68 |
|
69 |
foreach ($block->lines as $index => $line) {
|
70 |
if (substr($line, 0, 2) === '/*') {
|
71 |
+
$replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
|
72 |
+
assert($replacedLine !== null);
|
73 |
+
$block->lines[$index] = $replacedLine;
|
74 |
}
|
75 |
}
|
76 |
|
105 |
array_pop($depths);
|
106 |
$this->depth--;
|
107 |
|
108 |
+
if (
|
109 |
+
! $this->depth && ($block->depth <= 1 || (! $this->indentLevel && $block->type === Type::T_COMMENT)) &&
|
110 |
(($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
|
111 |
) {
|
112 |
$downLevel = $this->break;
|
223 |
*
|
224 |
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
225 |
*
|
226 |
+
* @return bool
|
227 |
*/
|
228 |
private function hasFlatChild($block)
|
229 |
{
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Formatter/OutputBlock.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,51 +16,53 @@ namespace ScssPhp\ScssPhp\Formatter;
|
|
15 |
* Output block
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class OutputBlock
|
20 |
{
|
21 |
/**
|
22 |
-
* @var string
|
23 |
*/
|
24 |
public $type;
|
25 |
|
26 |
/**
|
27 |
-
* @var
|
28 |
*/
|
29 |
public $depth;
|
30 |
|
31 |
/**
|
32 |
-
* @var array
|
33 |
*/
|
34 |
public $selectors;
|
35 |
|
36 |
/**
|
37 |
-
* @var
|
38 |
*/
|
39 |
public $lines;
|
40 |
|
41 |
/**
|
42 |
-
* @var
|
43 |
*/
|
44 |
public $children;
|
45 |
|
46 |
/**
|
47 |
-
* @var
|
48 |
*/
|
49 |
public $parent;
|
50 |
|
51 |
/**
|
52 |
-
* @var string
|
53 |
*/
|
54 |
public $sourceName;
|
55 |
|
56 |
/**
|
57 |
-
* @var
|
58 |
*/
|
59 |
public $sourceLine;
|
60 |
|
61 |
/**
|
62 |
-
* @var
|
63 |
*/
|
64 |
public $sourceColumn;
|
65 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Output block
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class OutputBlock
|
23 |
{
|
24 |
/**
|
25 |
+
* @var string|null
|
26 |
*/
|
27 |
public $type;
|
28 |
|
29 |
/**
|
30 |
+
* @var int
|
31 |
*/
|
32 |
public $depth;
|
33 |
|
34 |
/**
|
35 |
+
* @var array|null
|
36 |
*/
|
37 |
public $selectors;
|
38 |
|
39 |
/**
|
40 |
+
* @var string[]
|
41 |
*/
|
42 |
public $lines;
|
43 |
|
44 |
/**
|
45 |
+
* @var OutputBlock[]
|
46 |
*/
|
47 |
public $children;
|
48 |
|
49 |
/**
|
50 |
+
* @var OutputBlock|null
|
51 |
*/
|
52 |
public $parent;
|
53 |
|
54 |
/**
|
55 |
+
* @var string|null
|
56 |
*/
|
57 |
public $sourceName;
|
58 |
|
59 |
/**
|
60 |
+
* @var int|null
|
61 |
*/
|
62 |
public $sourceLine;
|
63 |
|
64 |
/**
|
65 |
+
* @var int|null
|
66 |
*/
|
67 |
public $sourceColumn;
|
68 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/LoggerInterface.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Logger;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Interface implemented by loggers for warnings and debug messages.
|
17 |
+
*
|
18 |
+
* The official Sass implementation recommends that loggers report the
|
19 |
+
* messages immediately rather than waiting for the end of the
|
20 |
+
* compilation, to provide a better debugging experience when the
|
21 |
+
* compilation does not end (error or infinite loop after the warning
|
22 |
+
* for instance).
|
23 |
+
*/
|
24 |
+
interface LoggerInterface
|
25 |
+
{
|
26 |
+
/**
|
27 |
+
* Emits a warning with the given message.
|
28 |
+
*
|
29 |
+
* If $deprecation is true, it indicates that this is a deprecation
|
30 |
+
* warning. Implementations should surface all this information to
|
31 |
+
* the end user.
|
32 |
+
*
|
33 |
+
* @param string $message
|
34 |
+
* @param bool $deprecation
|
35 |
+
*
|
36 |
+
* @return void
|
37 |
+
*/
|
38 |
+
public function warn($message, $deprecation = false);
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Emits a debugging message.
|
42 |
+
*
|
43 |
+
* @param string $message
|
44 |
+
*
|
45 |
+
* @return void
|
46 |
+
*/
|
47 |
+
public function debug($message);
|
48 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/QuietLogger.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Logger;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* A logger that silently ignores all messages.
|
17 |
+
*/
|
18 |
+
class QuietLogger implements LoggerInterface
|
19 |
+
{
|
20 |
+
public function warn($message, $deprecation = false)
|
21 |
+
{
|
22 |
+
}
|
23 |
+
|
24 |
+
public function debug($message)
|
25 |
+
{
|
26 |
+
}
|
27 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Logger/StreamLogger.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp\Logger;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* A logger that prints to a PHP stream (for instance stderr)
|
17 |
+
*/
|
18 |
+
class StreamLogger implements LoggerInterface
|
19 |
+
{
|
20 |
+
private $stream;
|
21 |
+
private $closeOnDestruct;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @param resource $stream A stream resource
|
25 |
+
* @param bool $closeOnDestruct If true, takes ownership of the stream and close it on destruct to avoid leaks.
|
26 |
+
*/
|
27 |
+
public function __construct($stream, $closeOnDestruct = false)
|
28 |
+
{
|
29 |
+
$this->stream = $stream;
|
30 |
+
$this->closeOnDestruct = $closeOnDestruct;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @internal
|
35 |
+
*/
|
36 |
+
public function __destruct()
|
37 |
+
{
|
38 |
+
if ($this->closeOnDestruct) {
|
39 |
+
fclose($this->stream);
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @inheritDoc
|
45 |
+
*/
|
46 |
+
public function warn($message, $deprecation = false)
|
47 |
+
{
|
48 |
+
$prefix = ($deprecation ? 'DEPRECATION ' : '') . 'WARNING: ';
|
49 |
+
|
50 |
+
fwrite($this->stream, $prefix . $message . "\n\n");
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @inheritDoc
|
55 |
+
*/
|
56 |
+
public function debug($message)
|
57 |
+
{
|
58 |
+
fwrite($this->stream, $message . "\n");
|
59 |
+
}
|
60 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Node.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,6 +16,8 @@ namespace ScssPhp\ScssPhp;
|
|
15 |
* Base node
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
abstract class Node
|
20 |
{
|
@@ -24,17 +27,17 @@ abstract class Node
|
|
24 |
public $type;
|
25 |
|
26 |
/**
|
27 |
-
* @var
|
28 |
*/
|
29 |
public $sourceIndex;
|
30 |
|
31 |
/**
|
32 |
-
* @var
|
33 |
*/
|
34 |
public $sourceLine;
|
35 |
|
36 |
/**
|
37 |
-
* @var
|
38 |
*/
|
39 |
public $sourceColumn;
|
40 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Base node
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
abstract class Node
|
23 |
{
|
27 |
public $type;
|
28 |
|
29 |
/**
|
30 |
+
* @var int
|
31 |
*/
|
32 |
public $sourceIndex;
|
33 |
|
34 |
/**
|
35 |
+
* @var int|null
|
36 |
*/
|
37 |
public $sourceLine;
|
38 |
|
39 |
/**
|
40 |
+
* @var int|null
|
41 |
*/
|
42 |
public $sourceColumn;
|
43 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Node/Number.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -11,9 +12,13 @@
|
|
11 |
|
12 |
namespace ScssPhp\ScssPhp\Node;
|
13 |
|
|
|
14 |
use ScssPhp\ScssPhp\Compiler;
|
|
|
|
|
15 |
use ScssPhp\ScssPhp\Node;
|
16 |
use ScssPhp\ScssPhp\Type;
|
|
|
17 |
|
18 |
/**
|
19 |
* Dimension + optional units
|
@@ -25,18 +30,24 @@ use ScssPhp\ScssPhp\Type;
|
|
25 |
* }}
|
26 |
*
|
27 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
28 |
*/
|
29 |
class Number extends Node implements \ArrayAccess
|
30 |
{
|
|
|
|
|
31 |
/**
|
32 |
-
* @var
|
|
|
33 |
*/
|
34 |
-
public static $precision =
|
35 |
|
36 |
/**
|
37 |
* @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
|
38 |
*
|
39 |
* @var array
|
|
|
40 |
*/
|
41 |
protected static $unitTable = [
|
42 |
'in' => [
|
@@ -64,87 +75,81 @@ class Number extends Node implements \ArrayAccess
|
|
64 |
],
|
65 |
'dpi' => [
|
66 |
'dpi' => 1,
|
67 |
-
'dpcm' => 1/2.54,
|
68 |
-
'dppx' => 1/96,
|
69 |
],
|
70 |
];
|
71 |
|
72 |
/**
|
73 |
-
* @var
|
74 |
*/
|
75 |
-
|
76 |
|
77 |
/**
|
78 |
-
* @var
|
|
|
79 |
*/
|
80 |
-
|
81 |
|
82 |
/**
|
83 |
-
*
|
84 |
-
*
|
85 |
-
* @param mixed $dimension
|
86 |
-
* @param mixed $initialUnit
|
87 |
*/
|
88 |
-
|
89 |
-
{
|
90 |
-
$this->type = Type::T_NUMBER;
|
91 |
-
$this->dimension = $dimension;
|
92 |
-
$this->units = \is_array($initialUnit)
|
93 |
-
? $initialUnit
|
94 |
-
: ($initialUnit ? [$initialUnit => 1]
|
95 |
-
: []);
|
96 |
-
}
|
97 |
|
98 |
/**
|
99 |
-
*
|
100 |
*
|
101 |
-
* @param
|
|
|
|
|
102 |
*
|
103 |
-
* @
|
|
|
104 |
*/
|
105 |
-
public function
|
106 |
{
|
107 |
-
if ($
|
108 |
-
|
109 |
-
}
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
if (\count($units)) {
|
114 |
-
$baseUnit = array_keys($units);
|
115 |
-
$baseUnit = reset($baseUnit);
|
116 |
-
$baseUnit = $this->findBaseUnit($baseUnit);
|
117 |
-
if ($baseUnit && isset(static::$unitTable[$baseUnit])) {
|
118 |
-
foreach (static::$unitTable[$baseUnit] as $unit => $conv) {
|
119 |
-
$from = isset($this->units[$unit]) ? $this->units[$unit] : 0;
|
120 |
-
$to = isset($units[$unit]) ? $units[$unit] : 0;
|
121 |
-
$factor = pow($conv, $from - $to);
|
122 |
-
$dimension /= $factor;
|
123 |
-
}
|
124 |
-
}
|
125 |
}
|
126 |
|
127 |
-
|
|
|
|
|
128 |
}
|
129 |
|
130 |
/**
|
131 |
-
*
|
132 |
-
*
|
133 |
-
* @return \ScssPhp\ScssPhp\Node\Number
|
134 |
*/
|
135 |
-
public function
|
136 |
{
|
137 |
-
|
138 |
-
|
139 |
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
143 |
}
|
144 |
|
145 |
/**
|
146 |
-
*
|
147 |
*/
|
|
|
148 |
public function offsetExists($offset)
|
149 |
{
|
150 |
if ($offset === -3) {
|
@@ -155,7 +160,8 @@ class Number extends Node implements \ArrayAccess
|
|
155 |
return ! \is_null($this->sourceLine);
|
156 |
}
|
157 |
|
158 |
-
if (
|
|
|
159 |
$offset === 0 ||
|
160 |
$offset === 1 ||
|
161 |
$offset === 2
|
@@ -167,8 +173,9 @@ class Number extends Node implements \ArrayAccess
|
|
167 |
}
|
168 |
|
169 |
/**
|
170 |
-
*
|
171 |
*/
|
|
|
172 |
public function offsetGet($offset)
|
173 |
{
|
174 |
switch ($offset) {
|
@@ -182,114 +189,346 @@ class Number extends Node implements \ArrayAccess
|
|
182 |
return $this->sourceIndex;
|
183 |
|
184 |
case 0:
|
185 |
-
return
|
186 |
|
187 |
case 1:
|
188 |
return $this->dimension;
|
189 |
|
190 |
case 2:
|
191 |
-
return $this->
|
192 |
}
|
193 |
}
|
194 |
|
195 |
/**
|
196 |
-
*
|
197 |
*/
|
|
|
198 |
public function offsetSet($offset, $value)
|
199 |
{
|
200 |
-
|
201 |
-
$this->dimension = $value;
|
202 |
-
} elseif ($offset === 2) {
|
203 |
-
$this->units = $value;
|
204 |
-
} elseif ($offset == -1) {
|
205 |
-
$this->sourceIndex = $value;
|
206 |
-
} elseif ($offset == -2) {
|
207 |
-
$this->sourceLine = $value;
|
208 |
-
} elseif ($offset == -3) {
|
209 |
-
$this->sourceColumn = $value;
|
210 |
-
}
|
211 |
}
|
212 |
|
213 |
/**
|
214 |
-
*
|
215 |
*/
|
|
|
216 |
public function offsetUnset($offset)
|
217 |
{
|
218 |
-
|
219 |
-
$this->dimension = null;
|
220 |
-
} elseif ($offset === 2) {
|
221 |
-
$this->units = null;
|
222 |
-
} elseif ($offset === -1) {
|
223 |
-
$this->sourceIndex = null;
|
224 |
-
} elseif ($offset === -2) {
|
225 |
-
$this->sourceLine = null;
|
226 |
-
} elseif ($offset === -3) {
|
227 |
-
$this->sourceColumn = null;
|
228 |
-
}
|
229 |
}
|
230 |
|
231 |
/**
|
232 |
* Returns true if the number is unitless
|
233 |
*
|
234 |
-
* @return
|
235 |
*/
|
236 |
public function unitless()
|
237 |
{
|
238 |
-
return
|
239 |
}
|
240 |
|
241 |
/**
|
242 |
-
*
|
243 |
-
*
|
|
|
244 |
*
|
245 |
-
* @return
|
246 |
*/
|
247 |
-
public function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
{
|
249 |
if ($this->unitless()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
return false;
|
251 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
|
255 |
-
|
256 |
-
|
|
|
|
|
|
|
|
|
|
|
257 |
|
258 |
-
|
259 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
}
|
261 |
|
262 |
-
|
263 |
-
|
|
|
|
|
264 |
}
|
265 |
-
}
|
266 |
|
267 |
-
|
|
|
|
|
|
|
|
|
|
|
268 |
}
|
269 |
|
270 |
/**
|
271 |
-
*
|
272 |
*
|
273 |
-
* @return
|
274 |
*/
|
275 |
-
public function
|
276 |
{
|
277 |
-
$
|
278 |
-
|
279 |
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
}
|
|
|
|
|
|
|
285 |
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
290 |
}
|
291 |
|
292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
}
|
294 |
|
295 |
/**
|
@@ -301,37 +540,31 @@ class Number extends Node implements \ArrayAccess
|
|
301 |
*/
|
302 |
public function output(Compiler $compiler = null)
|
303 |
{
|
304 |
-
$dimension = round($this->dimension,
|
305 |
-
|
306 |
-
$units = array_filter($this->units, function ($unitSize) {
|
307 |
-
return $unitSize;
|
308 |
-
});
|
309 |
-
|
310 |
-
if (\count($units) > 1 && array_sum($units) === 0) {
|
311 |
-
$dimension = $this->dimension;
|
312 |
-
$units = [];
|
313 |
|
314 |
-
|
|
|
|
|
315 |
|
316 |
-
|
317 |
-
|
318 |
-
return $unitSize;
|
319 |
-
});
|
320 |
}
|
321 |
|
322 |
-
$
|
|
|
|
|
323 |
|
324 |
-
if ($compiler
|
325 |
-
$this->units = $units;
|
326 |
$unit = $this->unitStr();
|
|
|
|
|
327 |
} else {
|
328 |
-
|
329 |
-
$unit = key($units);
|
330 |
}
|
331 |
|
332 |
-
$dimension = number_format($dimension,
|
333 |
|
334 |
-
return
|
335 |
}
|
336 |
|
337 |
/**
|
@@ -343,48 +576,229 @@ class Number extends Node implements \ArrayAccess
|
|
343 |
}
|
344 |
|
345 |
/**
|
346 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
347 |
*
|
348 |
-
* @
|
349 |
-
* @param array $units
|
350 |
-
* @param string $baseUnit
|
351 |
*/
|
352 |
-
private function
|
353 |
{
|
354 |
-
|
355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
356 |
|
357 |
-
|
358 |
-
|
359 |
-
|
|
|
|
|
|
|
|
|
360 |
}
|
361 |
|
362 |
-
|
363 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
|
365 |
-
|
366 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
367 |
}
|
368 |
|
369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
}
|
|
|
|
|
371 |
}
|
372 |
|
373 |
/**
|
374 |
-
*
|
|
|
|
|
|
|
|
|
375 |
*
|
376 |
-
* @
|
377 |
*
|
378 |
-
* @
|
|
|
|
|
|
|
379 |
*/
|
380 |
-
private function
|
381 |
{
|
382 |
-
|
383 |
-
|
384 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
}
|
386 |
}
|
387 |
|
388 |
return null;
|
389 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
12 |
|
13 |
namespace ScssPhp\ScssPhp\Node;
|
14 |
|
15 |
+
use ScssPhp\ScssPhp\Base\Range;
|
16 |
use ScssPhp\ScssPhp\Compiler;
|
17 |
+
use ScssPhp\ScssPhp\Exception\RangeException;
|
18 |
+
use ScssPhp\ScssPhp\Exception\SassScriptException;
|
19 |
use ScssPhp\ScssPhp\Node;
|
20 |
use ScssPhp\ScssPhp\Type;
|
21 |
+
use ScssPhp\ScssPhp\Util;
|
22 |
|
23 |
/**
|
24 |
* Dimension + optional units
|
30 |
* }}
|
31 |
*
|
32 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
33 |
+
*
|
34 |
+
* @template-implements \ArrayAccess<int, mixed>
|
35 |
*/
|
36 |
class Number extends Node implements \ArrayAccess
|
37 |
{
|
38 |
+
const PRECISION = 10;
|
39 |
+
|
40 |
/**
|
41 |
+
* @var int
|
42 |
+
* @deprecated use {Number::PRECISION} instead to read the precision. Configuring it is not supported anymore.
|
43 |
*/
|
44 |
+
public static $precision = self::PRECISION;
|
45 |
|
46 |
/**
|
47 |
* @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
|
48 |
*
|
49 |
* @var array
|
50 |
+
* @phpstan-var array<string, array<string, float|int>>
|
51 |
*/
|
52 |
protected static $unitTable = [
|
53 |
'in' => [
|
75 |
],
|
76 |
'dpi' => [
|
77 |
'dpi' => 1,
|
78 |
+
'dpcm' => 1 / 2.54,
|
79 |
+
'dppx' => 1 / 96,
|
80 |
],
|
81 |
];
|
82 |
|
83 |
/**
|
84 |
+
* @var int|float
|
85 |
*/
|
86 |
+
private $dimension;
|
87 |
|
88 |
/**
|
89 |
+
* @var string[]
|
90 |
+
* @phpstan-var list<string>
|
91 |
*/
|
92 |
+
private $numeratorUnits;
|
93 |
|
94 |
/**
|
95 |
+
* @var string[]
|
96 |
+
* @phpstan-var list<string>
|
|
|
|
|
97 |
*/
|
98 |
+
private $denominatorUnits;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
/**
|
101 |
+
* Initialize number
|
102 |
*
|
103 |
+
* @param int|float $dimension
|
104 |
+
* @param string[]|string $numeratorUnits
|
105 |
+
* @param string[] $denominatorUnits
|
106 |
*
|
107 |
+
* @phpstan-param list<string>|string $numeratorUnits
|
108 |
+
* @phpstan-param list<string> $denominatorUnits
|
109 |
*/
|
110 |
+
public function __construct($dimension, $numeratorUnits, array $denominatorUnits = [])
|
111 |
{
|
112 |
+
if (is_string($numeratorUnits)) {
|
113 |
+
$numeratorUnits = $numeratorUnits ? [$numeratorUnits] : [];
|
114 |
+
} elseif (isset($numeratorUnits['numerator_units'], $numeratorUnits['denominator_units'])) {
|
115 |
+
// TODO get rid of this once `$number[2]` is not used anymore
|
116 |
+
$denominatorUnits = $numeratorUnits['denominator_units'];
|
117 |
+
$numeratorUnits = $numeratorUnits['numerator_units'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
}
|
119 |
|
120 |
+
$this->dimension = $dimension;
|
121 |
+
$this->numeratorUnits = $numeratorUnits;
|
122 |
+
$this->denominatorUnits = $denominatorUnits;
|
123 |
}
|
124 |
|
125 |
/**
|
126 |
+
* @return float|int
|
|
|
|
|
127 |
*/
|
128 |
+
public function getDimension()
|
129 |
{
|
130 |
+
return $this->dimension;
|
131 |
+
}
|
132 |
|
133 |
+
/**
|
134 |
+
* @return string[]
|
135 |
+
*/
|
136 |
+
public function getNumeratorUnits()
|
137 |
+
{
|
138 |
+
return $this->numeratorUnits;
|
139 |
+
}
|
140 |
|
141 |
+
/**
|
142 |
+
* @return string[]
|
143 |
+
*/
|
144 |
+
public function getDenominatorUnits()
|
145 |
+
{
|
146 |
+
return $this->denominatorUnits;
|
147 |
}
|
148 |
|
149 |
/**
|
150 |
+
* @return bool
|
151 |
*/
|
152 |
+
#[\ReturnTypeWillChange]
|
153 |
public function offsetExists($offset)
|
154 |
{
|
155 |
if ($offset === -3) {
|
160 |
return ! \is_null($this->sourceLine);
|
161 |
}
|
162 |
|
163 |
+
if (
|
164 |
+
$offset === -1 ||
|
165 |
$offset === 0 ||
|
166 |
$offset === 1 ||
|
167 |
$offset === 2
|
173 |
}
|
174 |
|
175 |
/**
|
176 |
+
* @return mixed
|
177 |
*/
|
178 |
+
#[\ReturnTypeWillChange]
|
179 |
public function offsetGet($offset)
|
180 |
{
|
181 |
switch ($offset) {
|
189 |
return $this->sourceIndex;
|
190 |
|
191 |
case 0:
|
192 |
+
return Type::T_NUMBER;
|
193 |
|
194 |
case 1:
|
195 |
return $this->dimension;
|
196 |
|
197 |
case 2:
|
198 |
+
return array('numerator_units' => $this->numeratorUnits, 'denominator_units' => $this->denominatorUnits);
|
199 |
}
|
200 |
}
|
201 |
|
202 |
/**
|
203 |
+
* @return void
|
204 |
*/
|
205 |
+
#[\ReturnTypeWillChange]
|
206 |
public function offsetSet($offset, $value)
|
207 |
{
|
208 |
+
throw new \BadMethodCallException('Number is immutable');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
}
|
210 |
|
211 |
/**
|
212 |
+
* @return void
|
213 |
*/
|
214 |
+
#[\ReturnTypeWillChange]
|
215 |
public function offsetUnset($offset)
|
216 |
{
|
217 |
+
throw new \BadMethodCallException('Number is immutable');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
}
|
219 |
|
220 |
/**
|
221 |
* Returns true if the number is unitless
|
222 |
*
|
223 |
+
* @return bool
|
224 |
*/
|
225 |
public function unitless()
|
226 |
{
|
227 |
+
return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0;
|
228 |
}
|
229 |
|
230 |
/**
|
231 |
+
* Checks whether the number has exactly this unit
|
232 |
+
*
|
233 |
+
* @param string $unit
|
234 |
*
|
235 |
+
* @return bool
|
236 |
*/
|
237 |
+
public function hasUnit($unit)
|
238 |
+
{
|
239 |
+
return \count($this->numeratorUnits) === 1 && \count($this->denominatorUnits) === 0 && $this->numeratorUnits[0] === $unit;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Returns unit(s) as the product of numerator units divided by the product of denominator units
|
244 |
+
*
|
245 |
+
* @return string
|
246 |
+
*/
|
247 |
+
public function unitStr()
|
248 |
{
|
249 |
if ($this->unitless()) {
|
250 |
+
return '';
|
251 |
+
}
|
252 |
+
|
253 |
+
return self::getUnitString($this->numeratorUnits, $this->denominatorUnits);
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* @param float|int $min
|
258 |
+
* @param float|int $max
|
259 |
+
* @param string|null $name
|
260 |
+
*
|
261 |
+
* @return float|int
|
262 |
+
* @throws SassScriptException
|
263 |
+
*/
|
264 |
+
public function valueInRange($min, $max, $name = null)
|
265 |
+
{
|
266 |
+
try {
|
267 |
+
return Util::checkRange('', new Range($min, $max), $this);
|
268 |
+
} catch (RangeException $e) {
|
269 |
+
throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s', $this, $min, $this->unitStr(), $max), $name);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* @param string|null $varName
|
275 |
+
*
|
276 |
+
* @return void
|
277 |
+
*/
|
278 |
+
public function assertNoUnits($varName = null)
|
279 |
+
{
|
280 |
+
if ($this->unitless()) {
|
281 |
+
return;
|
282 |
+
}
|
283 |
+
|
284 |
+
throw SassScriptException::forArgument(sprintf('Expected %s to have no units.', $this), $varName);
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* @param string $unit
|
289 |
+
* @param string|null $varName
|
290 |
+
*
|
291 |
+
* @return void
|
292 |
+
*/
|
293 |
+
public function assertUnit($unit, $varName = null)
|
294 |
+
{
|
295 |
+
if ($this->hasUnit($unit)) {
|
296 |
+
return;
|
297 |
+
}
|
298 |
+
|
299 |
+
throw SassScriptException::forArgument(sprintf('Expected %s to have unit "%s".', $this, $unit), $varName);
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* @param Number $other
|
304 |
+
*
|
305 |
+
* @return void
|
306 |
+
*/
|
307 |
+
public function assertSameUnitOrUnitless(Number $other)
|
308 |
+
{
|
309 |
+
if ($other->unitless()) {
|
310 |
+
return;
|
311 |
+
}
|
312 |
+
|
313 |
+
if ($this->numeratorUnits === $other->numeratorUnits && $this->denominatorUnits === $other->denominatorUnits) {
|
314 |
+
return;
|
315 |
+
}
|
316 |
+
|
317 |
+
throw new SassScriptException(sprintf(
|
318 |
+
'Incompatible units %s and %s.',
|
319 |
+
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
320 |
+
self::getUnitString($other->numeratorUnits, $other->denominatorUnits)
|
321 |
+
));
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Returns a copy of this number, converted to the units represented by $newNumeratorUnits and $newDenominatorUnits.
|
326 |
+
*
|
327 |
+
* This does not throw an error if this number is unitless and
|
328 |
+
* $newNumeratorUnits/$newDenominatorUnits are not empty, or vice versa. Instead,
|
329 |
+
* it treats all unitless numbers as convertible to and from all units without
|
330 |
+
* changing the value.
|
331 |
+
*
|
332 |
+
* @param string[] $newNumeratorUnits
|
333 |
+
* @param string[] $newDenominatorUnits
|
334 |
+
*
|
335 |
+
* @return Number
|
336 |
+
*
|
337 |
+
* @phpstan-param list<string> $newNumeratorUnits
|
338 |
+
* @phpstan-param list<string> $newDenominatorUnits
|
339 |
+
*
|
340 |
+
* @throws SassScriptException if this number's units are not compatible with $newNumeratorUnits and $newDenominatorUnits
|
341 |
+
*/
|
342 |
+
public function coerce(array $newNumeratorUnits, array $newDenominatorUnits)
|
343 |
+
{
|
344 |
+
return new Number($this->valueInUnits($newNumeratorUnits, $newDenominatorUnits), $newNumeratorUnits, $newDenominatorUnits);
|
345 |
+
}
|
346 |
+
|
347 |
+
/**
|
348 |
+
* @param Number $other
|
349 |
+
*
|
350 |
+
* @return bool
|
351 |
+
*/
|
352 |
+
public function isComparableTo(Number $other)
|
353 |
+
{
|
354 |
+
if ($this->unitless() || $other->unitless()) {
|
355 |
+
return true;
|
356 |
+
}
|
357 |
+
|
358 |
+
try {
|
359 |
+
$this->greaterThan($other);
|
360 |
+
return true;
|
361 |
+
} catch (SassScriptException $e) {
|
362 |
return false;
|
363 |
}
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* @param Number $other
|
368 |
+
*
|
369 |
+
* @return bool
|
370 |
+
*/
|
371 |
+
public function lessThan(Number $other)
|
372 |
+
{
|
373 |
+
return $this->coerceUnits($other, function ($num1, $num2) {
|
374 |
+
return $num1 < $num2;
|
375 |
+
});
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* @param Number $other
|
380 |
+
*
|
381 |
+
* @return bool
|
382 |
+
*/
|
383 |
+
public function lessThanOrEqual(Number $other)
|
384 |
+
{
|
385 |
+
return $this->coerceUnits($other, function ($num1, $num2) {
|
386 |
+
return $num1 <= $num2;
|
387 |
+
});
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* @param Number $other
|
392 |
+
*
|
393 |
+
* @return bool
|
394 |
+
*/
|
395 |
+
public function greaterThan(Number $other)
|
396 |
+
{
|
397 |
+
return $this->coerceUnits($other, function ($num1, $num2) {
|
398 |
+
return $num1 > $num2;
|
399 |
+
});
|
400 |
+
}
|
401 |
+
|
402 |
+
/**
|
403 |
+
* @param Number $other
|
404 |
+
*
|
405 |
+
* @return bool
|
406 |
+
*/
|
407 |
+
public function greaterThanOrEqual(Number $other)
|
408 |
+
{
|
409 |
+
return $this->coerceUnits($other, function ($num1, $num2) {
|
410 |
+
return $num1 >= $num2;
|
411 |
+
});
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* @param Number $other
|
416 |
+
*
|
417 |
+
* @return Number
|
418 |
+
*/
|
419 |
+
public function plus(Number $other)
|
420 |
+
{
|
421 |
+
return $this->coerceNumber($other, function ($num1, $num2) {
|
422 |
+
return $num1 + $num2;
|
423 |
+
});
|
424 |
+
}
|
425 |
|
426 |
+
/**
|
427 |
+
* @param Number $other
|
428 |
+
*
|
429 |
+
* @return Number
|
430 |
+
*/
|
431 |
+
public function minus(Number $other)
|
432 |
+
{
|
433 |
+
return $this->coerceNumber($other, function ($num1, $num2) {
|
434 |
+
return $num1 - $num2;
|
435 |
+
});
|
436 |
+
}
|
437 |
|
438 |
+
/**
|
439 |
+
* @return Number
|
440 |
+
*/
|
441 |
+
public function unaryMinus()
|
442 |
+
{
|
443 |
+
return new Number(-$this->dimension, $this->numeratorUnits, $this->denominatorUnits);
|
444 |
+
}
|
445 |
|
446 |
+
/**
|
447 |
+
* @param Number $other
|
448 |
+
*
|
449 |
+
* @return Number
|
450 |
+
*/
|
451 |
+
public function modulo(Number $other)
|
452 |
+
{
|
453 |
+
return $this->coerceNumber($other, function ($num1, $num2) {
|
454 |
+
if ($num2 == 0) {
|
455 |
+
return NAN;
|
456 |
}
|
457 |
|
458 |
+
$result = fmod($num1, $num2);
|
459 |
+
|
460 |
+
if ($result == 0) {
|
461 |
+
return 0;
|
462 |
}
|
|
|
463 |
|
464 |
+
if ($num2 < 0 xor $num1 < 0) {
|
465 |
+
$result += $num2;
|
466 |
+
}
|
467 |
+
|
468 |
+
return $result;
|
469 |
+
});
|
470 |
}
|
471 |
|
472 |
/**
|
473 |
+
* @param Number $other
|
474 |
*
|
475 |
+
* @return Number
|
476 |
*/
|
477 |
+
public function times(Number $other)
|
478 |
{
|
479 |
+
return $this->multiplyUnits($this->dimension * $other->dimension, $this->numeratorUnits, $this->denominatorUnits, $other->numeratorUnits, $other->denominatorUnits);
|
480 |
+
}
|
481 |
|
482 |
+
/**
|
483 |
+
* @param Number $other
|
484 |
+
*
|
485 |
+
* @return Number
|
486 |
+
*/
|
487 |
+
public function dividedBy(Number $other)
|
488 |
+
{
|
489 |
+
if ($other->dimension == 0) {
|
490 |
+
if ($this->dimension == 0) {
|
491 |
+
$value = NAN;
|
492 |
+
} elseif ($this->dimension > 0) {
|
493 |
+
$value = INF;
|
494 |
+
} else {
|
495 |
+
$value = -INF;
|
496 |
}
|
497 |
+
} else {
|
498 |
+
$value = $this->dimension / $other->dimension;
|
499 |
+
}
|
500 |
|
501 |
+
return $this->multiplyUnits($value, $this->numeratorUnits, $this->denominatorUnits, $other->denominatorUnits, $other->numeratorUnits);
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* @param Number $other
|
506 |
+
*
|
507 |
+
* @return bool
|
508 |
+
*/
|
509 |
+
public function equals(Number $other)
|
510 |
+
{
|
511 |
+
// Unitless numbers are convertable to unit numbers, but not equal, so we special-case unitless here.
|
512 |
+
if ($this->unitless() !== $other->unitless()) {
|
513 |
+
return false;
|
514 |
}
|
515 |
|
516 |
+
// In Sass, neither NaN nor Infinity are equal to themselves, while PHP defines INF==INF
|
517 |
+
if (is_nan($this->dimension) || is_nan($other->dimension) || !is_finite($this->dimension) || !is_finite($other->dimension)) {
|
518 |
+
return false;
|
519 |
+
}
|
520 |
+
|
521 |
+
if ($this->unitless()) {
|
522 |
+
return round($this->dimension, self::PRECISION) == round($other->dimension, self::PRECISION);
|
523 |
+
}
|
524 |
+
|
525 |
+
try {
|
526 |
+
return $this->coerceUnits($other, function ($num1, $num2) {
|
527 |
+
return round($num1,self::PRECISION) == round($num2, self::PRECISION);
|
528 |
+
});
|
529 |
+
} catch (SassScriptException $e) {
|
530 |
+
return false;
|
531 |
+
}
|
532 |
}
|
533 |
|
534 |
/**
|
540 |
*/
|
541 |
public function output(Compiler $compiler = null)
|
542 |
{
|
543 |
+
$dimension = round($this->dimension, self::PRECISION);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
544 |
|
545 |
+
if (is_nan($dimension)) {
|
546 |
+
return 'NaN';
|
547 |
+
}
|
548 |
|
549 |
+
if ($dimension === INF) {
|
550 |
+
return 'Infinity';
|
|
|
|
|
551 |
}
|
552 |
|
553 |
+
if ($dimension === -INF) {
|
554 |
+
return '-Infinity';
|
555 |
+
}
|
556 |
|
557 |
+
if ($compiler) {
|
|
|
558 |
$unit = $this->unitStr();
|
559 |
+
} elseif (isset($this->numeratorUnits[0])) {
|
560 |
+
$unit = $this->numeratorUnits[0];
|
561 |
} else {
|
562 |
+
$unit = '';
|
|
|
563 |
}
|
564 |
|
565 |
+
$dimension = number_format($dimension, self::PRECISION, '.', '');
|
566 |
|
567 |
+
return rtrim(rtrim($dimension, '0'), '.') . $unit;
|
568 |
}
|
569 |
|
570 |
/**
|
576 |
}
|
577 |
|
578 |
/**
|
579 |
+
* @param Number $other
|
580 |
+
* @param callable $operation
|
581 |
+
*
|
582 |
+
* @return Number
|
583 |
+
*
|
584 |
+
* @phpstan-param callable(int|float, int|float): (int|float) $operation
|
585 |
+
*/
|
586 |
+
private function coerceNumber(Number $other, $operation)
|
587 |
+
{
|
588 |
+
$result = $this->coerceUnits($other, $operation);
|
589 |
+
|
590 |
+
if (!$this->unitless()) {
|
591 |
+
return new Number($result, $this->numeratorUnits, $this->denominatorUnits);
|
592 |
+
}
|
593 |
+
|
594 |
+
return new Number($result, $other->numeratorUnits, $other->denominatorUnits);
|
595 |
+
}
|
596 |
+
|
597 |
+
/**
|
598 |
+
* @param Number $other
|
599 |
+
* @param callable $operation
|
600 |
+
*
|
601 |
+
* @return mixed
|
602 |
+
*
|
603 |
+
* @phpstan-template T
|
604 |
+
* @phpstan-param callable(int|float, int|float): T $operation
|
605 |
+
* @phpstan-return T
|
606 |
+
*/
|
607 |
+
private function coerceUnits(Number $other, $operation)
|
608 |
+
{
|
609 |
+
if (!$this->unitless()) {
|
610 |
+
$num1 = $this->dimension;
|
611 |
+
$num2 = $other->valueInUnits($this->numeratorUnits, $this->denominatorUnits);
|
612 |
+
} else {
|
613 |
+
$num1 = $this->valueInUnits($other->numeratorUnits, $other->denominatorUnits);
|
614 |
+
$num2 = $other->dimension;
|
615 |
+
}
|
616 |
+
|
617 |
+
return \call_user_func($operation, $num1, $num2);
|
618 |
+
}
|
619 |
+
|
620 |
+
/**
|
621 |
+
* @param string[] $numeratorUnits
|
622 |
+
* @param string[] $denominatorUnits
|
623 |
+
*
|
624 |
+
* @return int|float
|
625 |
+
*
|
626 |
+
* @phpstan-param list<string> $numeratorUnits
|
627 |
+
* @phpstan-param list<string> $denominatorUnits
|
628 |
*
|
629 |
+
* @throws SassScriptException if this number's units are not compatible with $numeratorUnits and $denominatorUnits
|
|
|
|
|
630 |
*/
|
631 |
+
private function valueInUnits(array $numeratorUnits, array $denominatorUnits)
|
632 |
{
|
633 |
+
if (
|
634 |
+
$this->unitless()
|
635 |
+
|| (\count($numeratorUnits) === 0 && \count($denominatorUnits) === 0)
|
636 |
+
|| ($this->numeratorUnits === $numeratorUnits && $this->denominatorUnits === $denominatorUnits)
|
637 |
+
) {
|
638 |
+
return $this->dimension;
|
639 |
+
}
|
640 |
+
|
641 |
+
$value = $this->dimension;
|
642 |
+
$oldNumerators = $this->numeratorUnits;
|
643 |
+
|
644 |
+
foreach ($numeratorUnits as $newNumerator) {
|
645 |
+
foreach ($oldNumerators as $key => $oldNumerator) {
|
646 |
+
$conversionFactor = self::getConversionFactor($newNumerator, $oldNumerator);
|
647 |
|
648 |
+
if (\is_null($conversionFactor)) {
|
649 |
+
continue;
|
650 |
+
}
|
651 |
+
|
652 |
+
$value *= $conversionFactor;
|
653 |
+
unset($oldNumerators[$key]);
|
654 |
+
continue 2;
|
655 |
}
|
656 |
|
657 |
+
throw new SassScriptException(sprintf(
|
658 |
+
'Incompatible units %s and %s.',
|
659 |
+
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
660 |
+
self::getUnitString($numeratorUnits, $denominatorUnits)
|
661 |
+
));
|
662 |
+
}
|
663 |
+
|
664 |
+
$oldDenominators = $this->denominatorUnits;
|
665 |
|
666 |
+
foreach ($denominatorUnits as $newDenominator) {
|
667 |
+
foreach ($oldDenominators as $key => $oldDenominator) {
|
668 |
+
$conversionFactor = self::getConversionFactor($newDenominator, $oldDenominator);
|
669 |
+
|
670 |
+
if (\is_null($conversionFactor)) {
|
671 |
+
continue;
|
672 |
+
}
|
673 |
+
|
674 |
+
$value /= $conversionFactor;
|
675 |
+
unset($oldDenominators[$key]);
|
676 |
+
continue 2;
|
677 |
}
|
678 |
|
679 |
+
throw new SassScriptException(sprintf(
|
680 |
+
'Incompatible units %s and %s.',
|
681 |
+
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
682 |
+
self::getUnitString($numeratorUnits, $denominatorUnits)
|
683 |
+
));
|
684 |
+
}
|
685 |
+
|
686 |
+
if (\count($oldNumerators) || \count($oldDenominators)) {
|
687 |
+
throw new SassScriptException(sprintf(
|
688 |
+
'Incompatible units %s and %s.',
|
689 |
+
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
690 |
+
self::getUnitString($numeratorUnits, $denominatorUnits)
|
691 |
+
));
|
692 |
}
|
693 |
+
|
694 |
+
return $value;
|
695 |
}
|
696 |
|
697 |
/**
|
698 |
+
* @param int|float $value
|
699 |
+
* @param string[] $numerators1
|
700 |
+
* @param string[] $denominators1
|
701 |
+
* @param string[] $numerators2
|
702 |
+
* @param string[] $denominators2
|
703 |
*
|
704 |
+
* @return Number
|
705 |
*
|
706 |
+
* @phpstan-param list<string> $numerators1
|
707 |
+
* @phpstan-param list<string> $denominators1
|
708 |
+
* @phpstan-param list<string> $numerators2
|
709 |
+
* @phpstan-param list<string> $denominators2
|
710 |
*/
|
711 |
+
private function multiplyUnits($value, array $numerators1, array $denominators1, array $numerators2, array $denominators2)
|
712 |
{
|
713 |
+
$newNumerators = array();
|
714 |
+
|
715 |
+
foreach ($numerators1 as $numerator) {
|
716 |
+
foreach ($denominators2 as $key => $denominator) {
|
717 |
+
$conversionFactor = self::getConversionFactor($numerator, $denominator);
|
718 |
+
|
719 |
+
if (\is_null($conversionFactor)) {
|
720 |
+
continue;
|
721 |
+
}
|
722 |
+
|
723 |
+
$value /= $conversionFactor;
|
724 |
+
unset($denominators2[$key]);
|
725 |
+
continue 2;
|
726 |
+
}
|
727 |
+
|
728 |
+
$newNumerators[] = $numerator;
|
729 |
+
}
|
730 |
+
|
731 |
+
foreach ($numerators2 as $numerator) {
|
732 |
+
foreach ($denominators1 as $key => $denominator) {
|
733 |
+
$conversionFactor = self::getConversionFactor($numerator, $denominator);
|
734 |
+
|
735 |
+
if (\is_null($conversionFactor)) {
|
736 |
+
continue;
|
737 |
+
}
|
738 |
+
|
739 |
+
$value /= $conversionFactor;
|
740 |
+
unset($denominators1[$key]);
|
741 |
+
continue 2;
|
742 |
+
}
|
743 |
+
|
744 |
+
$newNumerators[] = $numerator;
|
745 |
+
}
|
746 |
+
|
747 |
+
$newDenominators = array_values(array_merge($denominators1, $denominators2));
|
748 |
+
|
749 |
+
return new Number($value, $newNumerators, $newDenominators);
|
750 |
+
}
|
751 |
+
|
752 |
+
/**
|
753 |
+
* Returns the number of [unit1]s per [unit2].
|
754 |
+
*
|
755 |
+
* Equivalently, `1unit1 * conversionFactor(unit1, unit2) = 1unit2`.
|
756 |
+
*
|
757 |
+
* @param string $unit1
|
758 |
+
* @param string $unit2
|
759 |
+
*
|
760 |
+
* @return float|int|null
|
761 |
+
*/
|
762 |
+
private static function getConversionFactor($unit1, $unit2)
|
763 |
+
{
|
764 |
+
if ($unit1 === $unit2) {
|
765 |
+
return 1;
|
766 |
+
}
|
767 |
+
|
768 |
+
foreach (static::$unitTable as $unitVariants) {
|
769 |
+
if (isset($unitVariants[$unit1]) && isset($unitVariants[$unit2])) {
|
770 |
+
return $unitVariants[$unit1] / $unitVariants[$unit2];
|
771 |
}
|
772 |
}
|
773 |
|
774 |
return null;
|
775 |
}
|
776 |
+
|
777 |
+
/**
|
778 |
+
* Returns unit(s) as the product of numerator units divided by the product of denominator units
|
779 |
+
*
|
780 |
+
* @param string[] $numerators
|
781 |
+
* @param string[] $denominators
|
782 |
+
*
|
783 |
+
* @phpstan-param list<string> $numerators
|
784 |
+
* @phpstan-param list<string> $denominators
|
785 |
+
*
|
786 |
+
* @return string
|
787 |
+
*/
|
788 |
+
private static function getUnitString(array $numerators, array $denominators)
|
789 |
+
{
|
790 |
+
if (!\count($numerators)) {
|
791 |
+
if (\count($denominators) === 0) {
|
792 |
+
return 'no units';
|
793 |
+
}
|
794 |
+
|
795 |
+
if (\count($denominators) === 1) {
|
796 |
+
return $denominators[0] . '^-1';
|
797 |
+
}
|
798 |
+
|
799 |
+
return '(' . implode('*', $denominators) . ')^-1';
|
800 |
+
}
|
801 |
+
|
802 |
+
return implode('*', $numerators) . (\count($denominators) ? '/' . implode('*', $denominators) : '');
|
803 |
+
}
|
804 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/OutputStyle.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ScssPhp\ScssPhp;
|
4 |
+
|
5 |
+
final class OutputStyle
|
6 |
+
{
|
7 |
+
const EXPANDED = 'expanded';
|
8 |
+
const COMPRESSED = 'compressed';
|
9 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Parser.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -11,17 +12,29 @@
|
|
11 |
|
12 |
namespace ScssPhp\ScssPhp;
|
13 |
|
14 |
-
use ScssPhp\ScssPhp\Block;
|
15 |
-
use ScssPhp\ScssPhp\
|
16 |
-
use ScssPhp\ScssPhp\
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
use ScssPhp\ScssPhp\Exception\ParserException;
|
18 |
-
use ScssPhp\ScssPhp\
|
19 |
-
use ScssPhp\ScssPhp\
|
|
|
20 |
|
21 |
/**
|
22 |
* Parser
|
23 |
*
|
24 |
* @author Leaf Corcoran <leafot@gmail.com>
|
|
|
|
|
25 |
*/
|
26 |
class Parser
|
27 |
{
|
@@ -30,7 +43,7 @@ class Parser
|
|
30 |
const SOURCE_COLUMN = -3;
|
31 |
|
32 |
/**
|
33 |
-
* @var array
|
34 |
*/
|
35 |
protected static $precedence = [
|
36 |
'=' => 0,
|
@@ -38,7 +51,6 @@ class Parser
|
|
38 |
'and' => 2,
|
39 |
'==' => 3,
|
40 |
'!=' => 3,
|
41 |
-
'<=>' => 3,
|
42 |
'<=' => 4,
|
43 |
'>=' => 4,
|
44 |
'<' => 4,
|
@@ -50,54 +62,97 @@ class Parser
|
|
50 |
'%' => 6,
|
51 |
];
|
52 |
|
|
|
|
|
|
|
53 |
protected static $commentPattern;
|
|
|
|
|
|
|
54 |
protected static $operatorPattern;
|
|
|
|
|
|
|
55 |
protected static $whitePattern;
|
56 |
|
|
|
|
|
|
|
57 |
protected $cache;
|
58 |
|
59 |
private $sourceName;
|
60 |
private $sourceIndex;
|
|
|
|
|
|
|
61 |
private $sourcePositions;
|
62 |
-
|
|
|
|
|
|
|
|
|
63 |
private $count;
|
|
|
|
|
|
|
64 |
private $env;
|
|
|
|
|
|
|
65 |
private $inParens;
|
|
|
|
|
|
|
66 |
private $eatWhiteDefault;
|
|
|
|
|
|
|
67 |
private $discardComments;
|
68 |
private $allowVars;
|
|
|
|
|
|
|
69 |
private $buffer;
|
70 |
private $utf8;
|
|
|
|
|
|
|
71 |
private $encoding;
|
72 |
private $patternModifiers;
|
73 |
private $commentsSeen;
|
74 |
|
75 |
private $cssOnly;
|
76 |
|
|
|
|
|
|
|
|
|
|
|
77 |
/**
|
78 |
* Constructor
|
79 |
*
|
80 |
* @api
|
81 |
*
|
82 |
-
* @param string
|
83 |
-
* @param
|
84 |
-
* @param string
|
85 |
-
* @param
|
|
|
|
|
86 |
*/
|
87 |
-
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', $cache = null, $cssOnly = false)
|
88 |
{
|
89 |
$this->sourceName = $sourceName ?: '(stdin)';
|
90 |
$this->sourceIndex = $sourceIndex;
|
91 |
-
$this->charset = null;
|
92 |
$this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8';
|
93 |
$this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
|
94 |
$this->commentsSeen = [];
|
95 |
-
$this->commentsSeen = [];
|
96 |
$this->allowVars = true;
|
97 |
$this->cssOnly = $cssOnly;
|
|
|
98 |
|
99 |
if (empty(static::$operatorPattern)) {
|
100 |
-
static::$operatorPattern = '([*\/%+-]|[!=]
|
101 |
|
102 |
$commentSingle = '\/\/';
|
103 |
$commentMultiLeft = '\/\*';
|
@@ -109,9 +164,7 @@ class Parser
|
|
109 |
: '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS';
|
110 |
}
|
111 |
|
112 |
-
|
113 |
-
$this->cache = $cache;
|
114 |
-
}
|
115 |
}
|
116 |
|
117 |
/**
|
@@ -133,9 +186,32 @@ class Parser
|
|
133 |
*
|
134 |
* @param string $msg
|
135 |
*
|
136 |
-
* @
|
|
|
|
|
|
|
|
|
137 |
*/
|
138 |
public function throwParseError($msg = 'parse error')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
{
|
140 |
list($line, $column) = $this->getSourcePosition($this->count);
|
141 |
|
@@ -143,15 +219,21 @@ class Parser
|
|
143 |
? "line: $line, column: $column"
|
144 |
: "$this->sourceName on line $line, at column $column";
|
145 |
|
146 |
-
if ($this->peek(
|
147 |
$this->restoreEncoding();
|
148 |
|
149 |
-
|
|
|
|
|
|
|
150 |
}
|
151 |
|
152 |
$this->restoreEncoding();
|
153 |
|
154 |
-
|
|
|
|
|
|
|
155 |
}
|
156 |
|
157 |
/**
|
@@ -161,17 +243,16 @@ class Parser
|
|
161 |
*
|
162 |
* @param string $buffer
|
163 |
*
|
164 |
-
* @return
|
165 |
*/
|
166 |
public function parse($buffer)
|
167 |
{
|
168 |
if ($this->cache) {
|
169 |
-
$cacheKey = $this->sourceName .
|
170 |
$parseOptions = [
|
171 |
-
'charset' => $this->charset,
|
172 |
'utf8' => $this->utf8,
|
173 |
];
|
174 |
-
$v = $this->cache->getCache(
|
175 |
|
176 |
if (! \is_null($v)) {
|
177 |
return $v;
|
@@ -202,21 +283,18 @@ class Parser
|
|
202 |
}
|
203 |
|
204 |
if ($this->count !== \strlen($this->buffer)) {
|
205 |
-
$this->
|
206 |
}
|
207 |
|
208 |
if (! empty($this->env->parent)) {
|
209 |
-
$this->
|
210 |
-
}
|
211 |
-
|
212 |
-
if ($this->charset) {
|
213 |
-
array_unshift($this->env->children, $this->charset);
|
214 |
}
|
215 |
|
216 |
$this->restoreEncoding();
|
|
|
217 |
|
218 |
if ($this->cache) {
|
219 |
-
$this->cache->setCache(
|
220 |
}
|
221 |
|
222 |
return $this->env;
|
@@ -230,7 +308,7 @@ class Parser
|
|
230 |
* @param string $buffer
|
231 |
* @param string|array $out
|
232 |
*
|
233 |
-
* @return
|
234 |
*/
|
235 |
public function parseValue($buffer, &$out)
|
236 |
{
|
@@ -241,6 +319,7 @@ class Parser
|
|
241 |
$this->buffer = (string) $buffer;
|
242 |
|
243 |
$this->saveEncoding();
|
|
|
244 |
|
245 |
$list = $this->valueList($out);
|
246 |
|
@@ -256,10 +335,11 @@ class Parser
|
|
256 |
*
|
257 |
* @param string $buffer
|
258 |
* @param string|array $out
|
|
|
259 |
*
|
260 |
-
* @return
|
261 |
*/
|
262 |
-
public function parseSelector($buffer, &$out)
|
263 |
{
|
264 |
$this->count = 0;
|
265 |
$this->env = null;
|
@@ -268,11 +348,21 @@ class Parser
|
|
268 |
$this->buffer = (string) $buffer;
|
269 |
|
270 |
$this->saveEncoding();
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
$selector = $this->selectors($out);
|
273 |
|
274 |
$this->restoreEncoding();
|
275 |
|
|
|
|
|
|
|
|
|
276 |
return $selector;
|
277 |
}
|
278 |
|
@@ -281,10 +371,10 @@ class Parser
|
|
281 |
*
|
282 |
* @api
|
283 |
*
|
284 |
-
* @param string
|
285 |
-
* @param
|
286 |
*
|
287 |
-
* @return
|
288 |
*/
|
289 |
public function parseMediaQueryList($buffer, &$out)
|
290 |
{
|
@@ -295,6 +385,7 @@ class Parser
|
|
295 |
$this->buffer = (string) $buffer;
|
296 |
|
297 |
$this->saveEncoding();
|
|
|
298 |
|
299 |
$isMediaQuery = $this->mediaQueryList($out);
|
300 |
|
@@ -340,7 +431,7 @@ class Parser
|
|
340 |
* position into $s. Then if a chain fails, use $this->seek($s) to
|
341 |
* go back where we started.
|
342 |
*
|
343 |
-
* @return
|
344 |
*/
|
345 |
protected function parseChunk()
|
346 |
{
|
@@ -348,19 +439,19 @@ class Parser
|
|
348 |
|
349 |
// the directives
|
350 |
if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
|
351 |
-
if (
|
|
|
352 |
($this->selectors($selector) || true) &&
|
353 |
($this->map($with) || true) &&
|
354 |
-
(($this->matchChar('(')
|
355 |
-
|
356 |
-
|
357 |
$this->matchChar('{', false)
|
358 |
) {
|
359 |
-
|
360 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
361 |
-
}
|
362 |
|
363 |
-
$atRoot =
|
|
|
364 |
$atRoot->selector = $selector;
|
365 |
$atRoot->with = $with;
|
366 |
|
@@ -369,8 +460,13 @@ class Parser
|
|
369 |
|
370 |
$this->seek($s);
|
371 |
|
372 |
-
if (
|
373 |
-
$
|
|
|
|
|
|
|
|
|
|
|
374 |
$media->queryList = $mediaQueryList[2];
|
375 |
|
376 |
return true;
|
@@ -378,16 +474,16 @@ class Parser
|
|
378 |
|
379 |
$this->seek($s);
|
380 |
|
381 |
-
if (
|
|
|
382 |
$this->keyword($mixinName) &&
|
383 |
($this->argumentDef($args) || true) &&
|
384 |
$this->matchChar('{', false)
|
385 |
) {
|
386 |
-
|
387 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
388 |
-
}
|
389 |
|
390 |
-
$mixin =
|
|
|
391 |
$mixin->name = $mixinName;
|
392 |
$mixin->args = $args;
|
393 |
|
@@ -396,20 +492,19 @@ class Parser
|
|
396 |
|
397 |
$this->seek($s);
|
398 |
|
399 |
-
if (
|
400 |
-
$this->
|
401 |
-
|
|
|
402 |
($this->argValues($argValues) || true) &&
|
403 |
$this->matchChar(')') || true) &&
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
) {
|
410 |
-
|
411 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
412 |
-
}
|
413 |
|
414 |
$child = [
|
415 |
Type::T_INCLUDE,
|
@@ -420,7 +515,8 @@ class Parser
|
|
420 |
];
|
421 |
|
422 |
if (! empty($hasBlock)) {
|
423 |
-
$include =
|
|
|
424 |
$include->child = $child;
|
425 |
} else {
|
426 |
$this->append($child, $s);
|
@@ -431,13 +527,16 @@ class Parser
|
|
431 |
|
432 |
$this->seek($s);
|
433 |
|
434 |
-
if (
|
|
|
435 |
$this->valueList($importPath) &&
|
436 |
$this->end()
|
437 |
) {
|
438 |
-
|
439 |
-
|
440 |
-
|
|
|
|
|
441 |
|
442 |
$this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s);
|
443 |
|
@@ -446,10 +545,18 @@ class Parser
|
|
446 |
|
447 |
$this->seek($s);
|
448 |
|
449 |
-
if (
|
|
|
450 |
$this->valueList($importPath) &&
|
|
|
451 |
$this->end()
|
452 |
) {
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
$this->append([Type::T_IMPORT, $importPath], $s);
|
454 |
|
455 |
return true;
|
@@ -457,12 +564,15 @@ class Parser
|
|
457 |
|
458 |
$this->seek($s);
|
459 |
|
460 |
-
if (
|
|
|
461 |
$this->url($importPath) &&
|
462 |
$this->end()
|
463 |
) {
|
464 |
if ($this->cssOnly) {
|
465 |
-
$this->
|
|
|
|
|
466 |
}
|
467 |
|
468 |
$this->append([Type::T_IMPORT, $importPath], $s);
|
@@ -472,13 +582,12 @@ class Parser
|
|
472 |
|
473 |
$this->seek($s);
|
474 |
|
475 |
-
if (
|
|
|
476 |
$this->selectors($selectors) &&
|
477 |
$this->end()
|
478 |
) {
|
479 |
-
|
480 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
481 |
-
}
|
482 |
|
483 |
// check for '!flag'
|
484 |
$optional = $this->stripOptionalFlag($selectors);
|
@@ -489,16 +598,16 @@ class Parser
|
|
489 |
|
490 |
$this->seek($s);
|
491 |
|
492 |
-
if (
|
|
|
493 |
$this->keyword($fnName) &&
|
494 |
$this->argumentDef($args) &&
|
495 |
$this->matchChar('{', false)
|
496 |
) {
|
497 |
-
|
498 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
499 |
-
}
|
500 |
|
501 |
-
$func =
|
|
|
502 |
$func->name = $fnName;
|
503 |
$func->args = $args;
|
504 |
|
@@ -507,34 +616,12 @@ class Parser
|
|
507 |
|
508 |
$this->seek($s);
|
509 |
|
510 |
-
if (
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
$this->
|
516 |
-
|
517 |
-
return true;
|
518 |
-
}
|
519 |
-
|
520 |
-
$this->seek($s);
|
521 |
-
|
522 |
-
if ($this->literal('@continue', 9) && $this->end()) {
|
523 |
-
if ($this->cssOnly) {
|
524 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
525 |
-
}
|
526 |
-
|
527 |
-
$this->append([Type::T_CONTINUE], $s);
|
528 |
-
|
529 |
-
return true;
|
530 |
-
}
|
531 |
-
|
532 |
-
$this->seek($s);
|
533 |
-
|
534 |
-
if ($this->literal('@return', 7) && ($this->valueList($retVal) || true) && $this->end()) {
|
535 |
-
if ($this->cssOnly) {
|
536 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
537 |
-
}
|
538 |
|
539 |
$this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
|
540 |
|
@@ -543,17 +630,17 @@ class Parser
|
|
543 |
|
544 |
$this->seek($s);
|
545 |
|
546 |
-
if (
|
|
|
547 |
$this->genericList($varNames, 'variable', ',', false) &&
|
548 |
$this->literal('in', 2) &&
|
549 |
$this->valueList($list) &&
|
550 |
$this->matchChar('{', false)
|
551 |
) {
|
552 |
-
|
553 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
554 |
-
}
|
555 |
|
556 |
-
$each =
|
|
|
557 |
|
558 |
foreach ($varNames[2] as $varName) {
|
559 |
$each->vars[] = $varName[1];
|
@@ -566,15 +653,24 @@ class Parser
|
|
566 |
|
567 |
$this->seek($s);
|
568 |
|
569 |
-
if (
|
|
|
570 |
$this->expression($cond) &&
|
571 |
$this->matchChar('{', false)
|
572 |
) {
|
573 |
-
|
574 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
575 |
}
|
576 |
|
577 |
-
$while =
|
|
|
578 |
$while->cond = $cond;
|
579 |
|
580 |
return true;
|
@@ -582,7 +678,8 @@ class Parser
|
|
582 |
|
583 |
$this->seek($s);
|
584 |
|
585 |
-
if (
|
|
|
586 |
$this->variable($varName) &&
|
587 |
$this->literal('from', 4) &&
|
588 |
$this->expression($start) &&
|
@@ -591,11 +688,10 @@ class Parser
|
|
591 |
$this->expression($end) &&
|
592 |
$this->matchChar('{', false)
|
593 |
) {
|
594 |
-
|
595 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
596 |
-
}
|
597 |
|
598 |
-
$for =
|
|
|
599 |
$for->var = $varName[1];
|
600 |
$for->start = $start;
|
601 |
$for->end = $end;
|
@@ -606,17 +702,21 @@ class Parser
|
|
606 |
|
607 |
$this->seek($s);
|
608 |
|
609 |
-
if (
|
610 |
-
|
611 |
-
|
612 |
-
|
|
|
613 |
|
614 |
-
$if =
|
|
|
615 |
|
616 |
-
while (
|
617 |
-
|
618 |
-
|
619 |
-
|
|
|
|
|
620 |
$cond = reset($cond[2]);
|
621 |
}
|
622 |
|
@@ -628,13 +728,11 @@ class Parser
|
|
628 |
|
629 |
$this->seek($s);
|
630 |
|
631 |
-
if (
|
632 |
-
$this->
|
633 |
-
$this->
|
634 |
) {
|
635 |
-
|
636 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
637 |
-
}
|
638 |
|
639 |
$this->append([Type::T_DEBUG, $value], $s);
|
640 |
|
@@ -643,13 +741,11 @@ class Parser
|
|
643 |
|
644 |
$this->seek($s);
|
645 |
|
646 |
-
if (
|
647 |
-
$this->
|
648 |
-
$this->
|
649 |
) {
|
650 |
-
|
651 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
652 |
-
}
|
653 |
|
654 |
$this->append([Type::T_WARN, $value], $s);
|
655 |
|
@@ -658,13 +754,11 @@ class Parser
|
|
658 |
|
659 |
$this->seek($s);
|
660 |
|
661 |
-
if (
|
662 |
-
$this->
|
663 |
-
$this->
|
664 |
) {
|
665 |
-
|
666 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
667 |
-
}
|
668 |
|
669 |
$this->append([Type::T_ERROR, $value], $s);
|
670 |
|
@@ -673,16 +767,15 @@ class Parser
|
|
673 |
|
674 |
$this->seek($s);
|
675 |
|
676 |
-
if (
|
|
|
677 |
($this->end() ||
|
678 |
$this->matchChar('(') &&
|
679 |
$this->argValues($argContent) &&
|
680 |
$this->matchChar(')') &&
|
681 |
$this->end())
|
682 |
) {
|
683 |
-
|
684 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
685 |
-
}
|
686 |
|
687 |
$this->append([Type::T_MIXIN_CONTENT, isset($argContent) ? $argContent : null], $s);
|
688 |
|
@@ -695,17 +788,21 @@ class Parser
|
|
695 |
|
696 |
if (isset($last) && $last[0] === Type::T_IF) {
|
697 |
list(, $if) = $last;
|
|
|
698 |
|
699 |
if ($this->literal('@else', 5)) {
|
700 |
if ($this->matchChar('{', false)) {
|
701 |
-
$else =
|
702 |
-
} elseif (
|
703 |
-
$
|
|
|
|
|
|
|
704 |
$else->cond = $cond;
|
705 |
}
|
706 |
|
707 |
if (isset($else)) {
|
708 |
-
$else
|
709 |
$if->cases[] = $else;
|
710 |
|
711 |
return true;
|
@@ -716,32 +813,23 @@ class Parser
|
|
716 |
}
|
717 |
|
718 |
// only retain the first @charset directive encountered
|
719 |
-
if (
|
|
|
720 |
$this->valueList($charset) &&
|
721 |
$this->end()
|
722 |
) {
|
723 |
-
if (! isset($this->charset)) {
|
724 |
-
$statement = [Type::T_CHARSET, $charset];
|
725 |
-
|
726 |
-
list($line, $column) = $this->getSourcePosition($s);
|
727 |
-
|
728 |
-
$statement[static::SOURCE_LINE] = $line;
|
729 |
-
$statement[static::SOURCE_COLUMN] = $column;
|
730 |
-
$statement[static::SOURCE_INDEX] = $this->sourceIndex;
|
731 |
-
|
732 |
-
$this->charset = $statement;
|
733 |
-
}
|
734 |
-
|
735 |
return true;
|
736 |
}
|
737 |
|
738 |
$this->seek($s);
|
739 |
|
740 |
-
if (
|
741 |
-
|
742 |
-
($
|
|
|
743 |
) {
|
744 |
-
$directive =
|
|
|
745 |
$directive->name = 'supports';
|
746 |
$directive->value = $supportQuery;
|
747 |
|
@@ -751,18 +839,26 @@ class Parser
|
|
751 |
$this->seek($s);
|
752 |
|
753 |
// doesn't match built in directive, do generic one
|
754 |
-
if (
|
755 |
-
$this->
|
|
|
756 |
$this->directiveValue($dirValue, '{')
|
757 |
) {
|
|
|
|
|
|
|
|
|
|
|
758 |
if ($dirName === 'media') {
|
759 |
-
$directive =
|
760 |
} else {
|
761 |
-
$directive =
|
762 |
$directive->name = $dirName;
|
763 |
}
|
|
|
764 |
|
765 |
if (isset($dirValue)) {
|
|
|
766 |
$directive->value = $dirValue;
|
767 |
}
|
768 |
|
@@ -772,12 +868,38 @@ class Parser
|
|
772 |
$this->seek($s);
|
773 |
|
774 |
// maybe it's a generic blockless directive
|
775 |
-
if (
|
776 |
-
$this->
|
777 |
-
$this->
|
778 |
-
$this->
|
|
|
779 |
) {
|
780 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
781 |
|
782 |
return true;
|
783 |
}
|
@@ -787,15 +909,19 @@ class Parser
|
|
787 |
return false;
|
788 |
}
|
789 |
|
|
|
|
|
|
|
|
|
|
|
790 |
// custom properties : right part is static
|
791 |
-
if (($this->customProperty($name)
|
792 |
-
$this->matchChar(':', false)
|
793 |
-
) {
|
794 |
$start = $this->count;
|
795 |
|
796 |
// but can be complex and finish with ; or }
|
797 |
foreach ([';','}'] as $ending) {
|
798 |
-
if (
|
|
|
799 |
$this->end()
|
800 |
) {
|
801 |
$end = $this->count;
|
@@ -810,7 +936,8 @@ class Parser
|
|
810 |
if ($p && $p < $end) {
|
811 |
$this->seek($start);
|
812 |
|
813 |
-
if (
|
|
|
814 |
$this->end() &&
|
815 |
$this->count > $end
|
816 |
) {
|
@@ -834,7 +961,8 @@ class Parser
|
|
834 |
|
835 |
// property shortcut
|
836 |
// captures most properties before having to parse a selector
|
837 |
-
if (
|
|
|
838 |
$this->literal(': ', 2) &&
|
839 |
$this->valueList($value) &&
|
840 |
$this->end()
|
@@ -848,14 +976,13 @@ class Parser
|
|
848 |
$this->seek($s);
|
849 |
|
850 |
// variable assigns
|
851 |
-
if (
|
|
|
852 |
$this->matchChar(':') &&
|
853 |
$this->valueList($value) &&
|
854 |
$this->end()
|
855 |
) {
|
856 |
-
|
857 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
858 |
-
}
|
859 |
|
860 |
// check for '!flag'
|
861 |
$assignmentFlags = $this->stripAssignmentFlags($value);
|
@@ -866,18 +993,12 @@ class Parser
|
|
866 |
|
867 |
$this->seek($s);
|
868 |
|
869 |
-
// misc
|
870 |
-
if ($this->literal('-->', 3)) {
|
871 |
-
return true;
|
872 |
-
}
|
873 |
-
|
874 |
// opening css block
|
875 |
-
if (
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
}
|
881 |
|
882 |
$this->pushBlock($selectors, $s);
|
883 |
|
@@ -892,12 +1013,15 @@ class Parser
|
|
892 |
$this->seek($s);
|
893 |
|
894 |
// property assign, or nested assign
|
895 |
-
if (
|
|
|
|
|
|
|
896 |
$foundSomething = false;
|
897 |
|
898 |
if ($this->valueList($value)) {
|
899 |
if (empty($this->env->parent)) {
|
900 |
-
$this->
|
901 |
}
|
902 |
|
903 |
$this->append([Type::T_ASSIGN, $name, $value], $s);
|
@@ -905,11 +1029,10 @@ class Parser
|
|
905 |
}
|
906 |
|
907 |
if ($this->matchChar('{', false)) {
|
908 |
-
|
909 |
-
$this->throwParseError("SCSS syntax not allowed in CSS file");
|
910 |
-
}
|
911 |
|
912 |
-
$propBlock =
|
|
|
913 |
$propBlock->prefix = $name;
|
914 |
$propBlock->hasValue = $foundSomething;
|
915 |
|
@@ -930,17 +1053,20 @@ class Parser
|
|
930 |
$block = $this->popBlock();
|
931 |
|
932 |
if (! isset($block->type) || $block->type !== Type::T_IF) {
|
|
|
|
|
933 |
if ($this->env->parent) {
|
934 |
$this->append(null); // collect comments before next statement if needed
|
935 |
}
|
936 |
}
|
937 |
|
938 |
-
if (
|
939 |
$include = $block->child;
|
|
|
940 |
unset($block->child);
|
941 |
$include[3] = $block;
|
942 |
$this->append($include, $s);
|
943 |
-
} elseif (
|
944 |
$type = isset($block->type) ? $block->type : Type::T_BLOCK;
|
945 |
$this->append([$type, $block], $s);
|
946 |
}
|
@@ -948,6 +1074,7 @@ class Parser
|
|
948 |
// collect comments just after the block closing if needed
|
949 |
if ($this->eatWhiteDefault) {
|
950 |
$this->whitespace();
|
|
|
951 |
|
952 |
if ($this->env->comments) {
|
953 |
$this->append(null);
|
@@ -958,9 +1085,7 @@ class Parser
|
|
958 |
}
|
959 |
|
960 |
// extra stuff
|
961 |
-
if ($this->matchChar(';')
|
962 |
-
$this->literal('<!--', 4)
|
963 |
-
) {
|
964 |
return true;
|
965 |
}
|
966 |
|
@@ -970,21 +1095,35 @@ class Parser
|
|
970 |
/**
|
971 |
* Push block onto parse tree
|
972 |
*
|
973 |
-
* @param array
|
974 |
-
* @param
|
975 |
*
|
976 |
-
* @return
|
977 |
*/
|
978 |
protected function pushBlock($selectors, $pos = 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
979 |
{
|
980 |
list($line, $column) = $this->getSourcePosition($pos);
|
981 |
|
982 |
-
$b = new Block;
|
983 |
$b->sourceName = $this->sourceName;
|
984 |
$b->sourceLine = $line;
|
985 |
$b->sourceColumn = $column;
|
986 |
$b->sourceIndex = $this->sourceIndex;
|
987 |
-
$b->selectors = $selectors;
|
988 |
$b->comments = [];
|
989 |
$b->parent = $this->env;
|
990 |
|
@@ -1004,22 +1143,23 @@ class Parser
|
|
1004 |
// collect comments at the beginning of a block if needed
|
1005 |
if ($this->eatWhiteDefault) {
|
1006 |
$this->whitespace();
|
|
|
1007 |
|
1008 |
if ($this->env->comments) {
|
1009 |
$this->append(null);
|
1010 |
}
|
1011 |
}
|
1012 |
-
|
1013 |
-
return $b;
|
1014 |
}
|
1015 |
|
1016 |
/**
|
1017 |
* Push special (named) block onto parse tree
|
1018 |
*
|
|
|
|
|
1019 |
* @param string $type
|
1020 |
-
* @param
|
1021 |
*
|
1022 |
-
* @return
|
1023 |
*/
|
1024 |
protected function pushSpecialBlock($type, $pos)
|
1025 |
{
|
@@ -1032,12 +1172,13 @@ class Parser
|
|
1032 |
/**
|
1033 |
* Pop scope and return last block
|
1034 |
*
|
1035 |
-
* @return
|
1036 |
*
|
1037 |
* @throws \Exception
|
1038 |
*/
|
1039 |
protected function popBlock()
|
1040 |
{
|
|
|
1041 |
|
1042 |
// collect comments ending just before of a block closing
|
1043 |
if ($this->env->comments) {
|
@@ -1048,7 +1189,7 @@ class Parser
|
|
1048 |
$block = $this->env;
|
1049 |
|
1050 |
if (empty($block->parent)) {
|
1051 |
-
$this->
|
1052 |
}
|
1053 |
|
1054 |
if ($block->type == Type::T_AT_ROOT) {
|
@@ -1066,11 +1207,11 @@ class Parser
|
|
1066 |
/**
|
1067 |
* Peek input stream
|
1068 |
*
|
1069 |
-
* @param string
|
1070 |
-
* @param array
|
1071 |
-
* @param
|
1072 |
*
|
1073 |
-
* @return
|
1074 |
*/
|
1075 |
protected function peek($regex, &$out, $from = null)
|
1076 |
{
|
@@ -1079,7 +1220,7 @@ class Parser
|
|
1079 |
}
|
1080 |
|
1081 |
$r = '/' . $regex . '/' . $this->patternModifiers;
|
1082 |
-
$result = preg_match($r, $this->buffer, $out,
|
1083 |
|
1084 |
return $result;
|
1085 |
}
|
@@ -1087,22 +1228,236 @@ class Parser
|
|
1087 |
/**
|
1088 |
* Seek to position in input stream (or return current position in input stream)
|
1089 |
*
|
1090 |
-
* @param
|
|
|
|
|
1091 |
*/
|
1092 |
protected function seek($where)
|
1093 |
{
|
1094 |
$this->count = $where;
|
1095 |
}
|
1096 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1097 |
/**
|
1098 |
* Match string looking for either ending delim, escape, or string interpolation
|
1099 |
*
|
1100 |
* {@internal This is a workaround for preg_match's 250K string match limit. }}
|
1101 |
*
|
1102 |
* @param array $m Matches (passed by reference)
|
1103 |
-
* @param string $delim
|
|
|
|
|
1104 |
*
|
1105 |
-
* @
|
1106 |
*/
|
1107 |
protected function matchString(&$m, $delim)
|
1108 |
{
|
@@ -1111,7 +1466,7 @@ class Parser
|
|
1111 |
$end = \strlen($this->buffer);
|
1112 |
|
1113 |
// look for either ending delim, escape, or string interpolation
|
1114 |
-
foreach (['#{', '\\', $delim] as $lookahead) {
|
1115 |
$pos = strpos($this->buffer, $lookahead, $this->count);
|
1116 |
|
1117 |
if ($pos !== false && $pos < $end) {
|
@@ -1138,17 +1493,19 @@ class Parser
|
|
1138 |
/**
|
1139 |
* Try to match something on head of buffer
|
1140 |
*
|
1141 |
-
* @param string
|
1142 |
-
* @param array
|
1143 |
-
* @param
|
1144 |
*
|
1145 |
-
* @return
|
|
|
|
|
1146 |
*/
|
1147 |
protected function match($regex, &$out, $eatWhitespace = null)
|
1148 |
{
|
1149 |
$r = '/' . $regex . '/' . $this->patternModifiers;
|
1150 |
|
1151 |
-
if (! preg_match($r, $this->buffer, $out,
|
1152 |
return false;
|
1153 |
}
|
1154 |
|
@@ -1168,10 +1525,12 @@ class Parser
|
|
1168 |
/**
|
1169 |
* Match a single string
|
1170 |
*
|
1171 |
-
* @param string
|
1172 |
-
* @param
|
|
|
|
|
1173 |
*
|
1174 |
-
* @
|
1175 |
*/
|
1176 |
protected function matchChar($char, $eatWhitespace = null)
|
1177 |
{
|
@@ -1195,11 +1554,13 @@ class Parser
|
|
1195 |
/**
|
1196 |
* Match literal string
|
1197 |
*
|
1198 |
-
* @param string
|
1199 |
-
* @param
|
1200 |
-
* @param
|
|
|
|
|
1201 |
*
|
1202 |
-
* @
|
1203 |
*/
|
1204 |
protected function literal($what, $len, $eatWhitespace = null)
|
1205 |
{
|
@@ -1223,13 +1584,15 @@ class Parser
|
|
1223 |
/**
|
1224 |
* Match some whitespace
|
1225 |
*
|
1226 |
-
* @return
|
|
|
|
|
1227 |
*/
|
1228 |
protected function whitespace()
|
1229 |
{
|
1230 |
$gotWhite = false;
|
1231 |
|
1232 |
-
while (preg_match(static::$whitePattern, $this->buffer, $m,
|
1233 |
if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
|
1234 |
// comment that are kept in the output CSS
|
1235 |
$comment = [];
|
@@ -1248,7 +1611,7 @@ class Parser
|
|
1248 |
if ($this->interpolation($out)) {
|
1249 |
// keep right spaces in the following string part
|
1250 |
if ($out[3]) {
|
1251 |
-
while ($this->buffer[$this->count-1] !== '}') {
|
1252 |
$this->count--;
|
1253 |
}
|
1254 |
|
@@ -1257,6 +1620,11 @@ class Parser
|
|
1257 |
|
1258 |
$comment[] = [Type::T_COMMENT, substr($this->buffer, $p, $this->count - $p), $out];
|
1259 |
} else {
|
|
|
|
|
|
|
|
|
|
|
1260 |
$comment[] = substr($this->buffer, $this->count, 2);
|
1261 |
|
1262 |
$this->count += 2;
|
@@ -1270,18 +1638,29 @@ class Parser
|
|
1270 |
|
1271 |
if (! $comment) {
|
1272 |
// single part static comment
|
1273 |
-
$
|
1274 |
} else {
|
1275 |
$comment[] = $c;
|
1276 |
$staticComment = substr($this->buffer, $startCommentCount, $endCommentCount - $startCommentCount);
|
1277 |
-
$
|
1278 |
}
|
1279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1280 |
$this->commentsSeen[$startCommentCount] = true;
|
1281 |
$this->count = $endCommentCount;
|
1282 |
} else {
|
1283 |
// comment that are ignored and not kept in the output css
|
1284 |
$this->count += \strlen($m[0]);
|
|
|
|
|
|
|
|
|
1285 |
}
|
1286 |
|
1287 |
$gotWhite = true;
|
@@ -1294,29 +1673,14 @@ class Parser
|
|
1294 |
* Append comment to current block
|
1295 |
*
|
1296 |
* @param array $comment
|
|
|
|
|
1297 |
*/
|
1298 |
protected function appendComment($comment)
|
1299 |
{
|
1300 |
-
|
1301 |
-
if ($comment[0] === Type::T_COMMENT) {
|
1302 |
-
if (\is_string($comment[1])) {
|
1303 |
-
$comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
|
1304 |
-
}
|
1305 |
-
|
1306 |
-
if (isset($comment[2]) and \is_array($comment[2]) and $comment[2][0] === Type::T_STRING) {
|
1307 |
-
foreach ($comment[2][2] as $k => $v) {
|
1308 |
-
if (\is_string($v)) {
|
1309 |
-
$p = strpos($v, "\n");
|
1310 |
-
|
1311 |
-
if ($p !== false) {
|
1312 |
-
$comment[2][2][$k] = substr($v, 0, $p + 1)
|
1313 |
-
. preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], substr($v, $p+1));
|
1314 |
-
}
|
1315 |
-
}
|
1316 |
-
}
|
1317 |
-
}
|
1318 |
-
}
|
1319 |
|
|
|
1320 |
$this->env->comments[] = $comment;
|
1321 |
}
|
1322 |
}
|
@@ -1324,12 +1688,18 @@ class Parser
|
|
1324 |
/**
|
1325 |
* Append statement to current block
|
1326 |
*
|
1327 |
-
* @param array
|
1328 |
-
* @param
|
|
|
|
|
1329 |
*/
|
1330 |
protected function append($statement, $pos = null)
|
1331 |
{
|
|
|
|
|
1332 |
if (! \is_null($statement)) {
|
|
|
|
|
1333 |
if (! \is_null($pos)) {
|
1334 |
list($line, $column) = $this->getSourcePosition($pos);
|
1335 |
|
@@ -1356,11 +1726,15 @@ class Parser
|
|
1356 |
*/
|
1357 |
protected function last()
|
1358 |
{
|
|
|
|
|
1359 |
$i = \count($this->env->children) - 1;
|
1360 |
|
1361 |
if (isset($this->env->children[$i])) {
|
1362 |
return $this->env->children[$i];
|
1363 |
}
|
|
|
|
|
1364 |
}
|
1365 |
|
1366 |
/**
|
@@ -1368,7 +1742,7 @@ class Parser
|
|
1368 |
*
|
1369 |
* @param array $out
|
1370 |
*
|
1371 |
-
* @return
|
1372 |
*/
|
1373 |
protected function mediaQueryList(&$out)
|
1374 |
{
|
@@ -1380,14 +1754,16 @@ class Parser
|
|
1380 |
*
|
1381 |
* @param array $out
|
1382 |
*
|
1383 |
-
* @return
|
1384 |
*/
|
1385 |
protected function mediaQuery(&$out)
|
1386 |
{
|
1387 |
$expressions = null;
|
1388 |
$parts = [];
|
1389 |
|
1390 |
-
if (
|
|
|
|
|
1391 |
$this->mixedKeyword($mediaType)
|
1392 |
) {
|
1393 |
$prop = [Type::T_MEDIA_TYPE];
|
@@ -1432,7 +1808,7 @@ class Parser
|
|
1432 |
*
|
1433 |
* @param array $out
|
1434 |
*
|
1435 |
-
* @return
|
1436 |
*/
|
1437 |
protected function supportsQuery(&$out)
|
1438 |
{
|
@@ -1443,7 +1819,8 @@ class Parser
|
|
1443 |
|
1444 |
$not = false;
|
1445 |
|
1446 |
-
if (
|
|
|
1447 |
$this->matchChar('(') &&
|
1448 |
($this->expression($property)) &&
|
1449 |
$this->literal(': ', 2) &&
|
@@ -1462,7 +1839,8 @@ class Parser
|
|
1462 |
$this->seek($s);
|
1463 |
}
|
1464 |
|
1465 |
-
if (
|
|
|
1466 |
$this->supportsQuery($subQuery) &&
|
1467 |
$this->matchChar(')')
|
1468 |
) {
|
@@ -1472,7 +1850,8 @@ class Parser
|
|
1472 |
$this->seek($s);
|
1473 |
}
|
1474 |
|
1475 |
-
if (
|
|
|
1476 |
$this->supportsQuery($subQuery)
|
1477 |
) {
|
1478 |
$parts[] = [Type::T_STRING, '', [[Type::T_KEYWORD, 'not '], $subQuery]];
|
@@ -1481,7 +1860,8 @@ class Parser
|
|
1481 |
$this->seek($s);
|
1482 |
}
|
1483 |
|
1484 |
-
if (
|
|
|
1485 |
$this->selector($selector) &&
|
1486 |
$this->matchChar(')')
|
1487 |
) {
|
@@ -1518,8 +1898,10 @@ class Parser
|
|
1518 |
$this->seek($s);
|
1519 |
}
|
1520 |
|
1521 |
-
if (
|
1522 |
-
$this->
|
|
|
|
|
1523 |
array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
|
1524 |
|
1525 |
$parts = [$expressions];
|
@@ -1528,8 +1910,10 @@ class Parser
|
|
1528 |
$this->seek($s);
|
1529 |
}
|
1530 |
|
1531 |
-
if (
|
1532 |
-
$this->
|
|
|
|
|
1533 |
array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
|
1534 |
|
1535 |
$parts = [$expressions];
|
@@ -1557,16 +1941,18 @@ class Parser
|
|
1557 |
*
|
1558 |
* @param array $out
|
1559 |
*
|
1560 |
-
* @return
|
1561 |
*/
|
1562 |
protected function mediaExpression(&$out)
|
1563 |
{
|
1564 |
$s = $this->count;
|
1565 |
$value = null;
|
1566 |
|
1567 |
-
if (
|
|
|
1568 |
$this->expression($feature) &&
|
1569 |
-
($this->matchChar(':') &&
|
|
|
1570 |
$this->matchChar(')')
|
1571 |
) {
|
1572 |
$out = [Type::T_MEDIA_EXPRESSION, $feature];
|
@@ -1588,16 +1974,23 @@ class Parser
|
|
1588 |
*
|
1589 |
* @param array $out
|
1590 |
*
|
1591 |
-
* @return
|
1592 |
*/
|
1593 |
protected function argValues(&$out)
|
1594 |
{
|
|
|
|
|
|
|
1595 |
if ($this->genericList($list, 'argValue', ',', false)) {
|
1596 |
$out = $list[2];
|
1597 |
|
|
|
|
|
1598 |
return true;
|
1599 |
}
|
1600 |
|
|
|
|
|
1601 |
return false;
|
1602 |
}
|
1603 |
|
@@ -1606,7 +1999,7 @@ class Parser
|
|
1606 |
*
|
1607 |
* @param array $out
|
1608 |
*
|
1609 |
-
* @return
|
1610 |
*/
|
1611 |
protected function argValue(&$out)
|
1612 |
{
|
@@ -1620,7 +2013,7 @@ class Parser
|
|
1620 |
$keyword = null;
|
1621 |
}
|
1622 |
|
1623 |
-
if ($this->genericList($value, 'expression')) {
|
1624 |
$out = [$keyword, $value, false];
|
1625 |
$s = $this->count;
|
1626 |
|
@@ -1636,13 +2029,62 @@ class Parser
|
|
1636 |
return false;
|
1637 |
}
|
1638 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1639 |
/**
|
1640 |
* Parse directive value list that considers $vars as keyword
|
1641 |
*
|
1642 |
-
* @param array
|
1643 |
-
* @param
|
1644 |
*
|
1645 |
-
* @return
|
|
|
|
|
1646 |
*/
|
1647 |
protected function directiveValue(&$out, $endChar = false)
|
1648 |
{
|
@@ -1660,8 +2102,13 @@ class Parser
|
|
1660 |
|
1661 |
$this->seek($s);
|
1662 |
|
1663 |
-
if ($endChar
|
1664 |
-
if ($this->matchChar($endChar, false)) {
|
|
|
|
|
|
|
|
|
|
|
1665 |
return true;
|
1666 |
}
|
1667 |
}
|
@@ -1698,7 +2145,7 @@ class Parser
|
|
1698 |
*
|
1699 |
* @param array $out
|
1700 |
*
|
1701 |
-
* @return
|
1702 |
*/
|
1703 |
protected function valueList(&$out)
|
1704 |
{
|
@@ -1710,12 +2157,52 @@ class Parser
|
|
1710 |
return $res;
|
1711 |
}
|
1712 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1713 |
/**
|
1714 |
* Parse space separated value list
|
1715 |
*
|
1716 |
* @param array $out
|
1717 |
*
|
1718 |
-
* @return
|
1719 |
*/
|
1720 |
protected function spaceList(&$out)
|
1721 |
{
|
@@ -1725,17 +2212,18 @@ class Parser
|
|
1725 |
/**
|
1726 |
* Parse generic list
|
1727 |
*
|
1728 |
-
* @param array
|
1729 |
-
* @param
|
1730 |
-
* @param string
|
1731 |
-
* @param
|
1732 |
*
|
1733 |
-
* @return
|
1734 |
*/
|
1735 |
protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
|
1736 |
{
|
1737 |
$s = $this->count;
|
1738 |
$items = [];
|
|
|
1739 |
$value = null;
|
1740 |
|
1741 |
while ($this->$parseItem($value)) {
|
@@ -1748,6 +2236,64 @@ class Parser
|
|
1748 |
}
|
1749 |
|
1750 |
$trailing_delim = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1751 |
}
|
1752 |
}
|
1753 |
|
@@ -1773,11 +2319,13 @@ class Parser
|
|
1773 |
/**
|
1774 |
* Parse expression
|
1775 |
*
|
1776 |
-
* @param array
|
1777 |
-
* @param
|
1778 |
-
* @param
|
1779 |
*
|
1780 |
-
* @return
|
|
|
|
|
1781 |
*/
|
1782 |
protected function expression(&$out, $listOnly = false, $lookForExp = true)
|
1783 |
{
|
@@ -1787,7 +2335,7 @@ class Parser
|
|
1787 |
$allowedTypes = ($listOnly ? [Type::T_LIST] : [Type::T_LIST, Type::T_MAP]);
|
1788 |
|
1789 |
if ($this->matchChar('(')) {
|
1790 |
-
if ($this->enclosedExpression($lhs, $s,
|
1791 |
if ($lookForExp) {
|
1792 |
$out = $this->expHelper($lhs, 0);
|
1793 |
} else {
|
@@ -1803,7 +2351,7 @@ class Parser
|
|
1803 |
}
|
1804 |
|
1805 |
if (\in_array(Type::T_LIST, $allowedTypes) && $this->matchChar('[')) {
|
1806 |
-
if ($this->enclosedExpression($lhs, $s,
|
1807 |
if ($lookForExp) {
|
1808 |
$out = $this->expHelper($lhs, 0);
|
1809 |
} else {
|
@@ -1838,24 +2386,26 @@ class Parser
|
|
1838 |
/**
|
1839 |
* Parse expression specifically checking for lists in parenthesis or brackets
|
1840 |
*
|
1841 |
-
* @param array
|
1842 |
-
* @param
|
1843 |
-
* @param string
|
1844 |
-
* @param
|
|
|
|
|
1845 |
*
|
1846 |
-
* @
|
1847 |
*/
|
1848 |
-
protected function enclosedExpression(&$out, $s, $closingParen =
|
1849 |
{
|
1850 |
if ($this->matchChar($closingParen) && \in_array(Type::T_LIST, $allowedTypes)) {
|
1851 |
$out = [Type::T_LIST, '', []];
|
1852 |
|
1853 |
switch ($closingParen) {
|
1854 |
-
case
|
1855 |
$out['enclosing'] = 'parent'; // parenthesis list
|
1856 |
break;
|
1857 |
|
1858 |
-
case
|
1859 |
$out['enclosing'] = 'bracket'; // bracketed list
|
1860 |
break;
|
1861 |
}
|
@@ -1863,8 +2413,10 @@ class Parser
|
|
1863 |
return true;
|
1864 |
}
|
1865 |
|
1866 |
-
if (
|
1867 |
-
|
|
|
|
|
1868 |
\in_array(Type::T_LIST, $allowedTypes)
|
1869 |
) {
|
1870 |
if ($out[0] !== Type::T_LIST || ! empty($out['enclosing'])) {
|
@@ -1872,11 +2424,11 @@ class Parser
|
|
1872 |
}
|
1873 |
|
1874 |
switch ($closingParen) {
|
1875 |
-
case
|
1876 |
$out['enclosing'] = 'parent'; // parenthesis list
|
1877 |
break;
|
1878 |
|
1879 |
-
case
|
1880 |
$out['enclosing'] = 'bracket'; // bracketed list
|
1881 |
break;
|
1882 |
}
|
@@ -1896,8 +2448,8 @@ class Parser
|
|
1896 |
/**
|
1897 |
* Parse left-hand side of subexpression
|
1898 |
*
|
1899 |
-
* @param array
|
1900 |
-
* @param
|
1901 |
*
|
1902 |
* @return array
|
1903 |
*/
|
@@ -1928,12 +2480,15 @@ class Parser
|
|
1928 |
break;
|
1929 |
}
|
1930 |
|
1931 |
-
|
1932 |
-
|
1933 |
-
$rhs = $this->expHelper($rhs, static::$precedence[$next[1]]);
|
1934 |
}
|
1935 |
|
|
|
|
|
|
|
1936 |
$lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
|
|
|
1937 |
$ss = $this->count;
|
1938 |
$whiteBefore = isset($this->buffer[$this->count - 1]) &&
|
1939 |
ctype_space($this->buffer[$this->count - 1]);
|
@@ -1949,7 +2504,7 @@ class Parser
|
|
1949 |
*
|
1950 |
* @param array $out
|
1951 |
*
|
1952 |
-
* @return
|
1953 |
*/
|
1954 |
protected function value(&$out)
|
1955 |
{
|
@@ -1960,7 +2515,10 @@ class Parser
|
|
1960 |
$s = $this->count;
|
1961 |
$char = $this->buffer[$this->count];
|
1962 |
|
1963 |
-
if (
|
|
|
|
|
|
|
1964 |
$len = strspn(
|
1965 |
$this->buffer,
|
1966 |
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=',
|
@@ -1979,7 +2537,10 @@ class Parser
|
|
1979 |
|
1980 |
$this->seek($s);
|
1981 |
|
1982 |
-
if (
|
|
|
|
|
|
|
1983 |
$content = 'url(' . $m[1];
|
1984 |
|
1985 |
if ($this->matchChar(')')) {
|
@@ -1994,7 +2555,10 @@ class Parser
|
|
1994 |
|
1995 |
// not
|
1996 |
if ($char === 'n' && $this->literal('not', 3, false)) {
|
1997 |
-
if (
|
|
|
|
|
|
|
1998 |
$out = [Type::T_UNARY, 'not', $inner, $this->inParens];
|
1999 |
|
2000 |
return true;
|
@@ -2049,7 +2613,10 @@ class Parser
|
|
2049 |
return true;
|
2050 |
}
|
2051 |
|
2052 |
-
if (
|
|
|
|
|
|
|
2053 |
$out = [Type::T_UNARY, '-', $inner, $this->inParens];
|
2054 |
|
2055 |
return true;
|
@@ -2077,7 +2644,7 @@ class Parser
|
|
2077 |
$this->count++;
|
2078 |
|
2079 |
if ($this->keyword($keyword)) {
|
2080 |
-
$out = [Type::T_KEYWORD,
|
2081 |
|
2082 |
return true;
|
2083 |
}
|
@@ -2108,10 +2675,17 @@ class Parser
|
|
2108 |
}
|
2109 |
|
2110 |
// unicode range with wildcards
|
2111 |
-
if (
|
2112 |
-
$
|
|
|
|
|
|
|
|
|
|
|
2113 |
|
2114 |
-
|
|
|
|
|
2115 |
}
|
2116 |
|
2117 |
if ($this->keyword($keyword, false)) {
|
@@ -2138,7 +2712,7 @@ class Parser
|
|
2138 |
*
|
2139 |
* @param array $out
|
2140 |
*
|
2141 |
-
* @return
|
2142 |
*/
|
2143 |
protected function parenValue(&$out)
|
2144 |
{
|
@@ -2155,7 +2729,10 @@ class Parser
|
|
2155 |
|
2156 |
$this->inParens = true;
|
2157 |
|
2158 |
-
if (
|
|
|
|
|
|
|
2159 |
$out = $exp;
|
2160 |
$this->inParens = $inParens;
|
2161 |
|
@@ -2174,13 +2751,14 @@ class Parser
|
|
2174 |
*
|
2175 |
* @param array $out
|
2176 |
*
|
2177 |
-
* @return
|
2178 |
*/
|
2179 |
protected function progid(&$out)
|
2180 |
{
|
2181 |
$s = $this->count;
|
2182 |
|
2183 |
-
if (
|
|
|
2184 |
$this->openString('(', $fn) &&
|
2185 |
$this->matchChar('(')
|
2186 |
) {
|
@@ -2206,7 +2784,7 @@ class Parser
|
|
2206 |
* @param string $name
|
2207 |
* @param array $func
|
2208 |
*
|
2209 |
-
* @return
|
2210 |
*/
|
2211 |
protected function func($name, &$func)
|
2212 |
{
|
@@ -2222,7 +2800,10 @@ class Parser
|
|
2222 |
if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) {
|
2223 |
$ss = $this->count;
|
2224 |
|
2225 |
-
if (
|
|
|
|
|
|
|
2226 |
$func = [Type::T_FUNCTION_CALL, $name, $args];
|
2227 |
|
2228 |
return true;
|
@@ -2231,7 +2812,8 @@ class Parser
|
|
2231 |
$this->seek($ss);
|
2232 |
}
|
2233 |
|
2234 |
-
if (
|
|
|
2235 |
$this->matchChar(')')
|
2236 |
) {
|
2237 |
$args = [];
|
@@ -2256,7 +2838,7 @@ class Parser
|
|
2256 |
*
|
2257 |
* @param array $out
|
2258 |
*
|
2259 |
-
* @return
|
2260 |
*/
|
2261 |
protected function argumentList(&$out)
|
2262 |
{
|
@@ -2266,7 +2848,10 @@ class Parser
|
|
2266 |
$args = [];
|
2267 |
|
2268 |
while ($this->keyword($var)) {
|
2269 |
-
if (
|
|
|
|
|
|
|
2270 |
$args[] = [Type::T_STRING, '', [$var . '=']];
|
2271 |
$arg = $exp;
|
2272 |
} else {
|
@@ -2298,7 +2883,7 @@ class Parser
|
|
2298 |
*
|
2299 |
* @param array $out
|
2300 |
*
|
2301 |
-
* @return
|
2302 |
*/
|
2303 |
protected function argumentDef(&$out)
|
2304 |
{
|
@@ -2312,7 +2897,10 @@ class Parser
|
|
2312 |
|
2313 |
$ss = $this->count;
|
2314 |
|
2315 |
-
if (
|
|
|
|
|
|
|
2316 |
$arg[1] = $defaultVal;
|
2317 |
} else {
|
2318 |
$this->seek($ss);
|
@@ -2324,7 +2912,7 @@ class Parser
|
|
2324 |
$sss = $this->count;
|
2325 |
|
2326 |
if (! $this->matchChar(')')) {
|
2327 |
-
$this->
|
2328 |
}
|
2329 |
|
2330 |
$arg[2] = true;
|
@@ -2357,7 +2945,7 @@ class Parser
|
|
2357 |
*
|
2358 |
* @param array $out
|
2359 |
*
|
2360 |
-
* @return
|
2361 |
*/
|
2362 |
protected function map(&$out)
|
2363 |
{
|
@@ -2370,8 +2958,10 @@ class Parser
|
|
2370 |
$keys = [];
|
2371 |
$values = [];
|
2372 |
|
2373 |
-
while (
|
2374 |
-
$this->genericList($
|
|
|
|
|
2375 |
) {
|
2376 |
$keys[] = $key;
|
2377 |
$values[] = $value;
|
@@ -2397,13 +2987,13 @@ class Parser
|
|
2397 |
*
|
2398 |
* @param array $out
|
2399 |
*
|
2400 |
-
* @return
|
2401 |
*/
|
2402 |
protected function color(&$out)
|
2403 |
{
|
2404 |
$s = $this->count;
|
2405 |
|
2406 |
-
if ($this->match('(#([0-9a-f]+))', $m)) {
|
2407 |
if (\in_array(\strlen($m[2]), [3,4,6,8])) {
|
2408 |
$out = [Type::T_KEYWORD, $m[0]];
|
2409 |
|
@@ -2423,7 +3013,7 @@ class Parser
|
|
2423 |
*
|
2424 |
* @param array $unit
|
2425 |
*
|
2426 |
-
* @return
|
2427 |
*/
|
2428 |
protected function unit(&$unit)
|
2429 |
{
|
@@ -2448,10 +3038,11 @@ class Parser
|
|
2448 |
* Parse string
|
2449 |
*
|
2450 |
* @param array $out
|
|
|
2451 |
*
|
2452 |
-
* @return
|
2453 |
*/
|
2454 |
-
protected function string(&$out)
|
2455 |
{
|
2456 |
$s = $this->count;
|
2457 |
|
@@ -2483,21 +3074,27 @@ class Parser
|
|
2483 |
$this->count += \strlen($m[2]);
|
2484 |
$content[] = '#{'; // ignore it
|
2485 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2486 |
} elseif ($m[2] === '\\') {
|
2487 |
-
if (
|
2488 |
-
$
|
2489 |
-
} elseif ($this->matchChar("'", false)) {
|
2490 |
-
$content[] = $m[2] . "'";
|
2491 |
-
} elseif ($this->literal("\\", 1, false)) {
|
2492 |
-
$content[] = $m[2] . "\\";
|
2493 |
-
} elseif ($this->literal("\r\n", 2, false) ||
|
2494 |
$this->matchChar("\r", false) ||
|
2495 |
$this->matchChar("\n", false) ||
|
2496 |
$this->matchChar("\f", false)
|
2497 |
) {
|
2498 |
// this is a continuation escaping, to be ignored
|
|
|
|
|
2499 |
} else {
|
2500 |
-
$
|
2501 |
}
|
2502 |
} else {
|
2503 |
$this->count -= \strlen($delim);
|
@@ -2508,18 +3105,8 @@ class Parser
|
|
2508 |
$this->eatWhiteDefault = $oldWhite;
|
2509 |
|
2510 |
if ($this->literal($delim, \strlen($delim))) {
|
2511 |
-
if ($hasInterpolation) {
|
2512 |
$delim = '"';
|
2513 |
-
|
2514 |
-
foreach ($content as &$string) {
|
2515 |
-
if ($string === "\\\\") {
|
2516 |
-
$string = "\\";
|
2517 |
-
} elseif ($string === "\\'") {
|
2518 |
-
$string = "'";
|
2519 |
-
} elseif ($string === '\\"') {
|
2520 |
-
$string = '"';
|
2521 |
-
}
|
2522 |
-
}
|
2523 |
}
|
2524 |
|
2525 |
$out = [Type::T_STRING, $delim, $content];
|
@@ -2532,13 +3119,63 @@ class Parser
|
|
2532 |
return false;
|
2533 |
}
|
2534 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2535 |
/**
|
2536 |
* Parse keyword or interpolation
|
2537 |
*
|
2538 |
-
* @param array
|
2539 |
-
* @param
|
2540 |
*
|
2541 |
-
* @return
|
2542 |
*/
|
2543 |
protected function mixedKeyword(&$out, $restricted = false)
|
2544 |
{
|
@@ -2579,26 +3216,28 @@ class Parser
|
|
2579 |
/**
|
2580 |
* Parse an unbounded string stopped by $end
|
2581 |
*
|
2582 |
-
* @param string
|
2583 |
-
* @param array
|
2584 |
-
* @param string
|
2585 |
-
* @param string
|
2586 |
-
* @param
|
|
|
2587 |
*
|
2588 |
-
* @return
|
2589 |
*/
|
2590 |
-
protected function openString($end, &$out, $
|
2591 |
{
|
2592 |
$oldWhite = $this->eatWhiteDefault;
|
2593 |
$this->eatWhiteDefault = false;
|
2594 |
|
2595 |
-
if ($
|
2596 |
-
$
|
2597 |
}
|
2598 |
|
2599 |
-
$patt = '(
|
|
|
2600 |
. $this->pregQuote($end) . '|'
|
2601 |
-
. (($
|
2602 |
. static::$commentPattern . ')';
|
2603 |
|
2604 |
$nestingLevel = 0;
|
@@ -2609,24 +3248,24 @@ class Parser
|
|
2609 |
if (isset($m[1]) && $m[1] !== '') {
|
2610 |
$content[] = $m[1];
|
2611 |
|
2612 |
-
if ($
|
2613 |
-
$nestingLevel += substr_count($m[1], $
|
2614 |
}
|
2615 |
}
|
2616 |
|
2617 |
$tok = $m[2];
|
2618 |
|
2619 |
-
$this->count-= \strlen($tok);
|
2620 |
|
2621 |
if ($tok === $end && ! $nestingLevel) {
|
2622 |
break;
|
2623 |
}
|
2624 |
|
2625 |
-
if ($tok === $
|
2626 |
$nestingLevel--;
|
2627 |
}
|
2628 |
|
2629 |
-
if (($tok === "'" || $tok === '"') && $this->string($str)) {
|
2630 |
$content[] = $str;
|
2631 |
continue;
|
2632 |
}
|
@@ -2637,7 +3276,7 @@ class Parser
|
|
2637 |
}
|
2638 |
|
2639 |
$content[] = $tok;
|
2640 |
-
$this->count+= \strlen($tok);
|
2641 |
}
|
2642 |
|
2643 |
$this->eatWhiteDefault = $oldWhite;
|
@@ -2647,7 +3286,7 @@ class Parser
|
|
2647 |
}
|
2648 |
|
2649 |
// trim the end
|
2650 |
-
if ($
|
2651 |
$content[\count($content) - 1] = rtrim(end($content));
|
2652 |
}
|
2653 |
|
@@ -2660,9 +3299,9 @@ class Parser
|
|
2660 |
* Parser interpolation
|
2661 |
*
|
2662 |
* @param string|array $out
|
2663 |
-
* @param
|
2664 |
*
|
2665 |
-
* @return
|
2666 |
*/
|
2667 |
protected function interpolation(&$out, $lookWhite = true)
|
2668 |
{
|
@@ -2673,13 +3312,20 @@ class Parser
|
|
2673 |
|
2674 |
$s = $this->count;
|
2675 |
|
2676 |
-
if (
|
|
|
|
|
|
|
|
|
2677 |
if ($value === [Type::T_SELF]) {
|
2678 |
$out = $value;
|
2679 |
} else {
|
2680 |
if ($lookWhite) {
|
2681 |
$left = ($s > 0 && preg_match('/\s/', $this->buffer[$s - 1])) ? ' ' : '';
|
2682 |
-
$right =
|
|
|
|
|
|
|
2683 |
} else {
|
2684 |
$left = $right = false;
|
2685 |
}
|
@@ -2710,7 +3356,7 @@ class Parser
|
|
2710 |
*
|
2711 |
* @param array $out
|
2712 |
*
|
2713 |
-
* @return
|
2714 |
*/
|
2715 |
protected function propertyName(&$out)
|
2716 |
{
|
@@ -2746,7 +3392,7 @@ class Parser
|
|
2746 |
}
|
2747 |
|
2748 |
// match comment hack
|
2749 |
-
if (preg_match(static::$whitePattern, $this->buffer, $m,
|
2750 |
if (! empty($m[0])) {
|
2751 |
$parts[] = $m[0];
|
2752 |
$this->count += \strlen($m[0]);
|
@@ -2765,7 +3411,7 @@ class Parser
|
|
2765 |
*
|
2766 |
* @param array $out
|
2767 |
*
|
2768 |
-
* @return
|
2769 |
*/
|
2770 |
protected function customProperty(&$out)
|
2771 |
{
|
@@ -2786,6 +3432,11 @@ class Parser
|
|
2786 |
continue;
|
2787 |
}
|
2788 |
|
|
|
|
|
|
|
|
|
|
|
2789 |
if ($this->variable($var)) {
|
2790 |
$parts[] = $var;
|
2791 |
continue;
|
@@ -2817,10 +3468,10 @@ class Parser
|
|
2817 |
/**
|
2818 |
* Parse comma separated selector list
|
2819 |
*
|
2820 |
-
* @param array
|
2821 |
-
* @param
|
2822 |
*
|
2823 |
-
* @return
|
2824 |
*/
|
2825 |
protected function selectors(&$out, $subSelector = false)
|
2826 |
{
|
@@ -2853,20 +3504,24 @@ class Parser
|
|
2853 |
/**
|
2854 |
* Parse whitespace separated selector list
|
2855 |
*
|
2856 |
-
* @param array
|
2857 |
-
* @param
|
2858 |
*
|
2859 |
-
* @return
|
2860 |
*/
|
2861 |
protected function selector(&$out, $subSelector = false)
|
2862 |
{
|
2863 |
$selector = [];
|
2864 |
|
|
|
|
|
|
|
2865 |
for (;;) {
|
2866 |
$s = $this->count;
|
2867 |
|
2868 |
if ($this->match('[>+~]+', $m, true)) {
|
2869 |
-
if (
|
|
|
2870 |
$m[0] === '+' && $this->match("(\d+|n\b)", $counter)
|
2871 |
) {
|
2872 |
$this->seek($s);
|
@@ -2878,18 +3533,15 @@ class Parser
|
|
2878 |
|
2879 |
if ($this->selectorSingle($part, $subSelector)) {
|
2880 |
$selector[] = $part;
|
2881 |
-
$this->
|
2882 |
-
continue;
|
2883 |
-
}
|
2884 |
-
|
2885 |
-
if ($this->match('\/[^\/]+\/', $m, true)) {
|
2886 |
-
$selector[] = [$m[0]];
|
2887 |
continue;
|
2888 |
}
|
2889 |
|
2890 |
break;
|
2891 |
}
|
2892 |
|
|
|
|
|
2893 |
if (! $selector) {
|
2894 |
return false;
|
2895 |
}
|
@@ -2899,6 +3551,56 @@ class Parser
|
|
2899 |
return true;
|
2900 |
}
|
2901 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2902 |
/**
|
2903 |
* Parse the parts that make up a selector
|
2904 |
*
|
@@ -2906,10 +3608,10 @@ class Parser
|
|
2906 |
* div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
|
2907 |
* }}
|
2908 |
*
|
2909 |
-
* @param array
|
2910 |
-
* @param
|
2911 |
*
|
2912 |
-
* @return
|
2913 |
*/
|
2914 |
protected function selectorSingle(&$out, $subSelector = false)
|
2915 |
{
|
@@ -2945,6 +3647,7 @@ class Parser
|
|
2945 |
case '&':
|
2946 |
$parts[] = Compiler::$selfSelector;
|
2947 |
$this->count++;
|
|
|
2948 |
continue 2;
|
2949 |
|
2950 |
case '.':
|
@@ -2958,9 +3661,14 @@ class Parser
|
|
2958 |
continue 2;
|
2959 |
}
|
2960 |
|
2961 |
-
|
2962 |
-
|
2963 |
-
|
|
|
|
|
|
|
|
|
|
|
2964 |
}
|
2965 |
|
2966 |
if ($char === '%') {
|
@@ -2969,6 +3677,7 @@ class Parser
|
|
2969 |
if ($this->placeholder($placeholder)) {
|
2970 |
$parts[] = '%';
|
2971 |
$parts[] = $placeholder;
|
|
|
2972 |
continue;
|
2973 |
}
|
2974 |
|
@@ -2978,6 +3687,7 @@ class Parser
|
|
2978 |
if ($char === '#') {
|
2979 |
if ($this->interpolation($inter)) {
|
2980 |
$parts[] = $inter;
|
|
|
2981 |
continue;
|
2982 |
}
|
2983 |
|
@@ -3005,13 +3715,19 @@ class Parser
|
|
3005 |
|
3006 |
$ss = $this->count;
|
3007 |
|
3008 |
-
if (
|
3009 |
-
$nameParts === ['
|
|
|
|
|
|
|
3010 |
$nameParts === ['slotted'] ||
|
3011 |
-
$nameParts === ['nth-child'] ||
|
3012 |
-
$nameParts === ['nth-
|
|
|
|
|
3013 |
) {
|
3014 |
-
if (
|
|
|
3015 |
($this->selectors($subs, reset($nameParts)) || true) &&
|
3016 |
$this->matchChar(')')
|
3017 |
) {
|
@@ -3037,21 +3753,20 @@ class Parser
|
|
3037 |
} else {
|
3038 |
$this->seek($ss);
|
3039 |
}
|
3040 |
-
}
|
3041 |
-
|
3042 |
-
|
3043 |
-
|
3044 |
-
|
3045 |
-
|
3046 |
-
|
3047 |
-
if (! empty($str)) {
|
3048 |
-
$parts[] = $str;
|
3049 |
-
}
|
3050 |
|
3051 |
-
|
3052 |
-
|
3053 |
-
$this->seek($ss);
|
3054 |
}
|
|
|
|
|
|
|
|
|
3055 |
}
|
3056 |
|
3057 |
continue;
|
@@ -3072,7 +3787,8 @@ class Parser
|
|
3072 |
$this->seek($s);
|
3073 |
|
3074 |
// attribute selector
|
3075 |
-
if (
|
|
|
3076 |
$this->matchChar('[') &&
|
3077 |
($this->openString(']', $str, '[') || true) &&
|
3078 |
$this->matchChar(']')
|
@@ -3095,7 +3811,7 @@ class Parser
|
|
3095 |
continue;
|
3096 |
}
|
3097 |
|
3098 |
-
if ($this->restrictedKeyword($name)) {
|
3099 |
$parts[] = $name;
|
3100 |
continue;
|
3101 |
}
|
@@ -3119,13 +3835,16 @@ class Parser
|
|
3119 |
*
|
3120 |
* @param array $out
|
3121 |
*
|
3122 |
-
* @return
|
3123 |
*/
|
3124 |
protected function variable(&$out)
|
3125 |
{
|
3126 |
$s = $this->count;
|
3127 |
|
3128 |
-
if (
|
|
|
|
|
|
|
3129 |
if ($this->allowVars) {
|
3130 |
$out = [Type::T_VARIABLE, $name];
|
3131 |
} else {
|
@@ -3143,22 +3862,64 @@ class Parser
|
|
3143 |
/**
|
3144 |
* Parse a keyword
|
3145 |
*
|
3146 |
-
* @param string
|
3147 |
-
* @param
|
|
|
3148 |
*
|
3149 |
-
* @return
|
3150 |
*/
|
3151 |
-
protected function keyword(&$word, $eatWhitespace = null)
|
3152 |
{
|
3153 |
-
|
|
|
3154 |
$this->utf8
|
3155 |
-
? '(([\pL\w\x{00A0}-\x{10FFFF}_\-\*!"\']
|
3156 |
-
: '(([\w_\-\*!"\']
|
3157 |
$m,
|
3158 |
-
|
3159 |
-
)
|
|
|
|
|
3160 |
$word = $m[1];
|
3161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3162 |
return true;
|
3163 |
}
|
3164 |
|
@@ -3168,16 +3929,17 @@ class Parser
|
|
3168 |
/**
|
3169 |
* Parse a keyword that should not start with a number
|
3170 |
*
|
3171 |
-
* @param string
|
3172 |
-
* @param
|
|
|
3173 |
*
|
3174 |
-
* @return
|
3175 |
*/
|
3176 |
-
protected function restrictedKeyword(&$word, $eatWhitespace = null)
|
3177 |
{
|
3178 |
$s = $this->count;
|
3179 |
|
3180 |
-
if ($this->keyword($word, $eatWhitespace) && (\ord($word[0]) > 57 || \ord($word[0]) < 48)) {
|
3181 |
return true;
|
3182 |
}
|
3183 |
|
@@ -3191,16 +3953,18 @@ class Parser
|
|
3191 |
*
|
3192 |
* @param string|array $placeholder
|
3193 |
*
|
3194 |
-
* @return
|
3195 |
*/
|
3196 |
protected function placeholder(&$placeholder)
|
3197 |
{
|
3198 |
-
|
3199 |
$this->utf8
|
3200 |
? '([\pL\w\-_]+)'
|
3201 |
: '([\w\-_]+)',
|
3202 |
$m
|
3203 |
-
)
|
|
|
|
|
3204 |
$placeholder = $m[1];
|
3205 |
|
3206 |
return true;
|
@@ -3218,14 +3982,32 @@ class Parser
|
|
3218 |
*
|
3219 |
* @param array $out
|
3220 |
*
|
3221 |
-
* @return
|
3222 |
*/
|
3223 |
protected function url(&$out)
|
3224 |
{
|
3225 |
-
if ($this->
|
3226 |
-
$
|
3227 |
|
3228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3229 |
}
|
3230 |
|
3231 |
return false;
|
@@ -3233,12 +4015,13 @@ class Parser
|
|
3233 |
|
3234 |
/**
|
3235 |
* Consume an end of statement delimiter
|
|
|
3236 |
*
|
3237 |
-
* @return
|
3238 |
*/
|
3239 |
-
protected function end()
|
3240 |
{
|
3241 |
-
if ($this->matchChar(';')) {
|
3242 |
return true;
|
3243 |
}
|
3244 |
|
@@ -3255,7 +4038,7 @@ class Parser
|
|
3255 |
*
|
3256 |
* @param array $value
|
3257 |
*
|
3258 |
-
* @return
|
3259 |
*/
|
3260 |
protected function stripAssignmentFlags(&$value)
|
3261 |
{
|
@@ -3282,7 +4065,7 @@ class Parser
|
|
3282 |
*
|
3283 |
* @param array $selectors
|
3284 |
*
|
3285 |
-
* @return
|
3286 |
*/
|
3287 |
protected function stripOptionalFlag(&$selectors)
|
3288 |
{
|
@@ -3331,6 +4114,8 @@ class Parser
|
|
3331 |
* Extract line numbers from buffer
|
3332 |
*
|
3333 |
* @param string $buffer
|
|
|
|
|
3334 |
*/
|
3335 |
private function extractLineNumbers($buffer)
|
3336 |
{
|
@@ -3352,9 +4137,10 @@ class Parser
|
|
3352 |
/**
|
3353 |
* Get source line number and column (given character position in the buffer)
|
3354 |
*
|
3355 |
-
* @param
|
3356 |
*
|
3357 |
* @return array
|
|
|
3358 |
*/
|
3359 |
private function getSourcePosition($pos)
|
3360 |
{
|
@@ -3381,11 +4167,20 @@ class Parser
|
|
3381 |
}
|
3382 |
|
3383 |
/**
|
3384 |
-
* Save internal encoding
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3385 |
*/
|
3386 |
private function saveEncoding()
|
3387 |
{
|
3388 |
-
if (\extension_loaded('mbstring')) {
|
3389 |
$this->encoding = mb_internal_encoding();
|
3390 |
|
3391 |
mb_internal_encoding('iso-8859-1');
|
@@ -3394,6 +4189,8 @@ class Parser
|
|
3394 |
|
3395 |
/**
|
3396 |
* Restore internal encoding
|
|
|
|
|
3397 |
*/
|
3398 |
private function restoreEncoding()
|
3399 |
{
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
12 |
|
13 |
namespace ScssPhp\ScssPhp;
|
14 |
|
15 |
+
use ScssPhp\ScssPhp\Block\AtRootBlock;
|
16 |
+
use ScssPhp\ScssPhp\Block\CallableBlock;
|
17 |
+
use ScssPhp\ScssPhp\Block\ContentBlock;
|
18 |
+
use ScssPhp\ScssPhp\Block\DirectiveBlock;
|
19 |
+
use ScssPhp\ScssPhp\Block\EachBlock;
|
20 |
+
use ScssPhp\ScssPhp\Block\ElseBlock;
|
21 |
+
use ScssPhp\ScssPhp\Block\ElseifBlock;
|
22 |
+
use ScssPhp\ScssPhp\Block\ForBlock;
|
23 |
+
use ScssPhp\ScssPhp\Block\IfBlock;
|
24 |
+
use ScssPhp\ScssPhp\Block\MediaBlock;
|
25 |
+
use ScssPhp\ScssPhp\Block\NestedPropertyBlock;
|
26 |
+
use ScssPhp\ScssPhp\Block\WhileBlock;
|
27 |
use ScssPhp\ScssPhp\Exception\ParserException;
|
28 |
+
use ScssPhp\ScssPhp\Logger\LoggerInterface;
|
29 |
+
use ScssPhp\ScssPhp\Logger\QuietLogger;
|
30 |
+
use ScssPhp\ScssPhp\Node\Number;
|
31 |
|
32 |
/**
|
33 |
* Parser
|
34 |
*
|
35 |
* @author Leaf Corcoran <leafot@gmail.com>
|
36 |
+
*
|
37 |
+
* @internal
|
38 |
*/
|
39 |
class Parser
|
40 |
{
|
43 |
const SOURCE_COLUMN = -3;
|
44 |
|
45 |
/**
|
46 |
+
* @var array<string, int>
|
47 |
*/
|
48 |
protected static $precedence = [
|
49 |
'=' => 0,
|
51 |
'and' => 2,
|
52 |
'==' => 3,
|
53 |
'!=' => 3,
|
|
|
54 |
'<=' => 4,
|
55 |
'>=' => 4,
|
56 |
'<' => 4,
|
62 |
'%' => 6,
|
63 |
];
|
64 |
|
65 |
+
/**
|
66 |
+
* @var string
|
67 |
+
*/
|
68 |
protected static $commentPattern;
|
69 |
+
/**
|
70 |
+
* @var string
|
71 |
+
*/
|
72 |
protected static $operatorPattern;
|
73 |
+
/**
|
74 |
+
* @var string
|
75 |
+
*/
|
76 |
protected static $whitePattern;
|
77 |
|
78 |
+
/**
|
79 |
+
* @var Cache|null
|
80 |
+
*/
|
81 |
protected $cache;
|
82 |
|
83 |
private $sourceName;
|
84 |
private $sourceIndex;
|
85 |
+
/**
|
86 |
+
* @var array<int, int>
|
87 |
+
*/
|
88 |
private $sourcePositions;
|
89 |
+
/**
|
90 |
+
* The current offset in the buffer
|
91 |
+
*
|
92 |
+
* @var int
|
93 |
+
*/
|
94 |
private $count;
|
95 |
+
/**
|
96 |
+
* @var Block|null
|
97 |
+
*/
|
98 |
private $env;
|
99 |
+
/**
|
100 |
+
* @var bool
|
101 |
+
*/
|
102 |
private $inParens;
|
103 |
+
/**
|
104 |
+
* @var bool
|
105 |
+
*/
|
106 |
private $eatWhiteDefault;
|
107 |
+
/**
|
108 |
+
* @var bool
|
109 |
+
*/
|
110 |
private $discardComments;
|
111 |
private $allowVars;
|
112 |
+
/**
|
113 |
+
* @var string
|
114 |
+
*/
|
115 |
private $buffer;
|
116 |
private $utf8;
|
117 |
+
/**
|
118 |
+
* @var string|null
|
119 |
+
*/
|
120 |
private $encoding;
|
121 |
private $patternModifiers;
|
122 |
private $commentsSeen;
|
123 |
|
124 |
private $cssOnly;
|
125 |
|
126 |
+
/**
|
127 |
+
* @var LoggerInterface
|
128 |
+
*/
|
129 |
+
private $logger;
|
130 |
+
|
131 |
/**
|
132 |
* Constructor
|
133 |
*
|
134 |
* @api
|
135 |
*
|
136 |
+
* @param string|null $sourceName
|
137 |
+
* @param int $sourceIndex
|
138 |
+
* @param string|null $encoding
|
139 |
+
* @param Cache|null $cache
|
140 |
+
* @param bool $cssOnly
|
141 |
+
* @param LoggerInterface|null $logger
|
142 |
*/
|
143 |
+
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', Cache $cache = null, $cssOnly = false, LoggerInterface $logger = null)
|
144 |
{
|
145 |
$this->sourceName = $sourceName ?: '(stdin)';
|
146 |
$this->sourceIndex = $sourceIndex;
|
|
|
147 |
$this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8';
|
148 |
$this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
|
149 |
$this->commentsSeen = [];
|
|
|
150 |
$this->allowVars = true;
|
151 |
$this->cssOnly = $cssOnly;
|
152 |
+
$this->logger = $logger ?: new QuietLogger();
|
153 |
|
154 |
if (empty(static::$operatorPattern)) {
|
155 |
+
static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=?|and|or)';
|
156 |
|
157 |
$commentSingle = '\/\/';
|
158 |
$commentMultiLeft = '\/\*';
|
164 |
: '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS';
|
165 |
}
|
166 |
|
167 |
+
$this->cache = $cache;
|
|
|
|
|
168 |
}
|
169 |
|
170 |
/**
|
186 |
*
|
187 |
* @param string $msg
|
188 |
*
|
189 |
+
* @phpstan-return never-return
|
190 |
+
*
|
191 |
+
* @throws ParserException
|
192 |
+
*
|
193 |
+
* @deprecated use "parseError" and throw the exception in the caller instead.
|
194 |
*/
|
195 |
public function throwParseError($msg = 'parse error')
|
196 |
+
{
|
197 |
+
@trigger_error(
|
198 |
+
'The method "throwParseError" is deprecated. Use "parseError" and throw the exception in the caller instead',
|
199 |
+
E_USER_DEPRECATED
|
200 |
+
);
|
201 |
+
|
202 |
+
throw $this->parseError($msg);
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Creates a parser error
|
207 |
+
*
|
208 |
+
* @api
|
209 |
+
*
|
210 |
+
* @param string $msg
|
211 |
+
*
|
212 |
+
* @return ParserException
|
213 |
+
*/
|
214 |
+
public function parseError($msg = 'parse error')
|
215 |
{
|
216 |
list($line, $column) = $this->getSourcePosition($this->count);
|
217 |
|
219 |
? "line: $line, column: $column"
|
220 |
: "$this->sourceName on line $line, at column $column";
|
221 |
|
222 |
+
if ($this->peek('(.*?)(\n|$)', $m, $this->count)) {
|
223 |
$this->restoreEncoding();
|
224 |
|
225 |
+
$e = new ParserException("$msg: failed at `$m[1]` $loc");
|
226 |
+
$e->setSourcePosition([$this->sourceName, $line, $column]);
|
227 |
+
|
228 |
+
return $e;
|
229 |
}
|
230 |
|
231 |
$this->restoreEncoding();
|
232 |
|
233 |
+
$e = new ParserException("$msg: $loc");
|
234 |
+
$e->setSourcePosition([$this->sourceName, $line, $column]);
|
235 |
+
|
236 |
+
return $e;
|
237 |
}
|
238 |
|
239 |
/**
|
243 |
*
|
244 |
* @param string $buffer
|
245 |
*
|
246 |
+
* @return Block
|
247 |
*/
|
248 |
public function parse($buffer)
|
249 |
{
|
250 |
if ($this->cache) {
|
251 |
+
$cacheKey = $this->sourceName . ':' . md5($buffer);
|
252 |
$parseOptions = [
|
|
|
253 |
'utf8' => $this->utf8,
|
254 |
];
|
255 |
+
$v = $this->cache->getCache('parse', $cacheKey, $parseOptions);
|
256 |
|
257 |
if (! \is_null($v)) {
|
258 |
return $v;
|
283 |
}
|
284 |
|
285 |
if ($this->count !== \strlen($this->buffer)) {
|
286 |
+
throw $this->parseError();
|
287 |
}
|
288 |
|
289 |
if (! empty($this->env->parent)) {
|
290 |
+
throw $this->parseError('unclosed block');
|
|
|
|
|
|
|
|
|
291 |
}
|
292 |
|
293 |
$this->restoreEncoding();
|
294 |
+
assert($this->env !== null);
|
295 |
|
296 |
if ($this->cache) {
|
297 |
+
$this->cache->setCache('parse', $cacheKey, $this->env, $parseOptions);
|
298 |
}
|
299 |
|
300 |
return $this->env;
|
308 |
* @param string $buffer
|
309 |
* @param string|array $out
|
310 |
*
|
311 |
+
* @return bool
|
312 |
*/
|
313 |
public function parseValue($buffer, &$out)
|
314 |
{
|
319 |
$this->buffer = (string) $buffer;
|
320 |
|
321 |
$this->saveEncoding();
|
322 |
+
$this->extractLineNumbers($this->buffer);
|
323 |
|
324 |
$list = $this->valueList($out);
|
325 |
|
335 |
*
|
336 |
* @param string $buffer
|
337 |
* @param string|array $out
|
338 |
+
* @param bool $shouldValidate
|
339 |
*
|
340 |
+
* @return bool
|
341 |
*/
|
342 |
+
public function parseSelector($buffer, &$out, $shouldValidate = true)
|
343 |
{
|
344 |
$this->count = 0;
|
345 |
$this->env = null;
|
348 |
$this->buffer = (string) $buffer;
|
349 |
|
350 |
$this->saveEncoding();
|
351 |
+
$this->extractLineNumbers($this->buffer);
|
352 |
+
|
353 |
+
// discard space/comments at the start
|
354 |
+
$this->discardComments = true;
|
355 |
+
$this->whitespace();
|
356 |
+
$this->discardComments = false;
|
357 |
|
358 |
$selector = $this->selectors($out);
|
359 |
|
360 |
$this->restoreEncoding();
|
361 |
|
362 |
+
if ($shouldValidate && $this->count !== strlen($buffer)) {
|
363 |
+
throw $this->parseError("`" . substr($buffer, $this->count) . "` is not a valid Selector in `$buffer`");
|
364 |
+
}
|
365 |
+
|
366 |
return $selector;
|
367 |
}
|
368 |
|
371 |
*
|
372 |
* @api
|
373 |
*
|
374 |
+
* @param string $buffer
|
375 |
+
* @param array $out
|
376 |
*
|
377 |
+
* @return bool
|
378 |
*/
|
379 |
public function parseMediaQueryList($buffer, &$out)
|
380 |
{
|
385 |
$this->buffer = (string) $buffer;
|
386 |
|
387 |
$this->saveEncoding();
|
388 |
+
$this->extractLineNumbers($this->buffer);
|
389 |
|
390 |
$isMediaQuery = $this->mediaQueryList($out);
|
391 |
|
431 |
* position into $s. Then if a chain fails, use $this->seek($s) to
|
432 |
* go back where we started.
|
433 |
*
|
434 |
+
* @return bool
|
435 |
*/
|
436 |
protected function parseChunk()
|
437 |
{
|
439 |
|
440 |
// the directives
|
441 |
if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
|
442 |
+
if (
|
443 |
+
$this->literal('@at-root', 8) &&
|
444 |
($this->selectors($selector) || true) &&
|
445 |
($this->map($with) || true) &&
|
446 |
+
(($this->matchChar('(') &&
|
447 |
+
$this->interpolation($with) &&
|
448 |
+
$this->matchChar(')')) || true) &&
|
449 |
$this->matchChar('{', false)
|
450 |
) {
|
451 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
452 |
|
453 |
+
$atRoot = new AtRootBlock();
|
454 |
+
$this->registerPushedBlock($atRoot, $s);
|
455 |
$atRoot->selector = $selector;
|
456 |
$atRoot->with = $with;
|
457 |
|
460 |
|
461 |
$this->seek($s);
|
462 |
|
463 |
+
if (
|
464 |
+
$this->literal('@media', 6) &&
|
465 |
+
$this->mediaQueryList($mediaQueryList) &&
|
466 |
+
$this->matchChar('{', false)
|
467 |
+
) {
|
468 |
+
$media = new MediaBlock();
|
469 |
+
$this->registerPushedBlock($media, $s);
|
470 |
$media->queryList = $mediaQueryList[2];
|
471 |
|
472 |
return true;
|
474 |
|
475 |
$this->seek($s);
|
476 |
|
477 |
+
if (
|
478 |
+
$this->literal('@mixin', 6) &&
|
479 |
$this->keyword($mixinName) &&
|
480 |
($this->argumentDef($args) || true) &&
|
481 |
$this->matchChar('{', false)
|
482 |
) {
|
483 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
484 |
|
485 |
+
$mixin = new CallableBlock(Type::T_MIXIN);
|
486 |
+
$this->registerPushedBlock($mixin, $s);
|
487 |
$mixin->name = $mixinName;
|
488 |
$mixin->args = $args;
|
489 |
|
492 |
|
493 |
$this->seek($s);
|
494 |
|
495 |
+
if (
|
496 |
+
($this->literal('@include', 8) &&
|
497 |
+
$this->keyword($mixinName) &&
|
498 |
+
($this->matchChar('(') &&
|
499 |
($this->argValues($argValues) || true) &&
|
500 |
$this->matchChar(')') || true) &&
|
501 |
+
($this->end()) ||
|
502 |
+
($this->literal('using', 5) &&
|
503 |
+
$this->argumentDef($argUsing) &&
|
504 |
+
($this->end() || $this->matchChar('{') && $hasBlock = true)) ||
|
505 |
+
$this->matchChar('{') && $hasBlock = true)
|
506 |
) {
|
507 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
508 |
|
509 |
$child = [
|
510 |
Type::T_INCLUDE,
|
515 |
];
|
516 |
|
517 |
if (! empty($hasBlock)) {
|
518 |
+
$include = new ContentBlock();
|
519 |
+
$this->registerPushedBlock($include, $s);
|
520 |
$include->child = $child;
|
521 |
} else {
|
522 |
$this->append($child, $s);
|
527 |
|
528 |
$this->seek($s);
|
529 |
|
530 |
+
if (
|
531 |
+
$this->literal('@scssphp-import-once', 20) &&
|
532 |
$this->valueList($importPath) &&
|
533 |
$this->end()
|
534 |
) {
|
535 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
536 |
+
|
537 |
+
list($line, $column) = $this->getSourcePosition($s);
|
538 |
+
$file = $this->sourceName;
|
539 |
+
$this->logger->warn("The \"@scssphp-import-once\" directive is deprecated and will be removed in ScssPhp 2.0, in \"$file\", line $line, column $column.", true);
|
540 |
|
541 |
$this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s);
|
542 |
|
545 |
|
546 |
$this->seek($s);
|
547 |
|
548 |
+
if (
|
549 |
+
$this->literal('@import', 7) &&
|
550 |
$this->valueList($importPath) &&
|
551 |
+
$importPath[0] !== Type::T_FUNCTION_CALL &&
|
552 |
$this->end()
|
553 |
) {
|
554 |
+
if ($this->cssOnly) {
|
555 |
+
$this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s);
|
556 |
+
$this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]);
|
557 |
+
return true;
|
558 |
+
}
|
559 |
+
|
560 |
$this->append([Type::T_IMPORT, $importPath], $s);
|
561 |
|
562 |
return true;
|
564 |
|
565 |
$this->seek($s);
|
566 |
|
567 |
+
if (
|
568 |
+
$this->literal('@import', 7) &&
|
569 |
$this->url($importPath) &&
|
570 |
$this->end()
|
571 |
) {
|
572 |
if ($this->cssOnly) {
|
573 |
+
$this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s);
|
574 |
+
$this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]);
|
575 |
+
return true;
|
576 |
}
|
577 |
|
578 |
$this->append([Type::T_IMPORT, $importPath], $s);
|
582 |
|
583 |
$this->seek($s);
|
584 |
|
585 |
+
if (
|
586 |
+
$this->literal('@extend', 7) &&
|
587 |
$this->selectors($selectors) &&
|
588 |
$this->end()
|
589 |
) {
|
590 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
591 |
|
592 |
// check for '!flag'
|
593 |
$optional = $this->stripOptionalFlag($selectors);
|
598 |
|
599 |
$this->seek($s);
|
600 |
|
601 |
+
if (
|
602 |
+
$this->literal('@function', 9) &&
|
603 |
$this->keyword($fnName) &&
|
604 |
$this->argumentDef($args) &&
|
605 |
$this->matchChar('{', false)
|
606 |
) {
|
607 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
608 |
|
609 |
+
$func = new CallableBlock(Type::T_FUNCTION);
|
610 |
+
$this->registerPushedBlock($func, $s);
|
611 |
$func->name = $fnName;
|
612 |
$func->args = $args;
|
613 |
|
616 |
|
617 |
$this->seek($s);
|
618 |
|
619 |
+
if (
|
620 |
+
$this->literal('@return', 7) &&
|
621 |
+
($this->valueList($retVal) || true) &&
|
622 |
+
$this->end()
|
623 |
+
) {
|
624 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
625 |
|
626 |
$this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
|
627 |
|
630 |
|
631 |
$this->seek($s);
|
632 |
|
633 |
+
if (
|
634 |
+
$this->literal('@each', 5) &&
|
635 |
$this->genericList($varNames, 'variable', ',', false) &&
|
636 |
$this->literal('in', 2) &&
|
637 |
$this->valueList($list) &&
|
638 |
$this->matchChar('{', false)
|
639 |
) {
|
640 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
641 |
|
642 |
+
$each = new EachBlock();
|
643 |
+
$this->registerPushedBlock($each, $s);
|
644 |
|
645 |
foreach ($varNames[2] as $varName) {
|
646 |
$each->vars[] = $varName[1];
|
653 |
|
654 |
$this->seek($s);
|
655 |
|
656 |
+
if (
|
657 |
+
$this->literal('@while', 6) &&
|
658 |
$this->expression($cond) &&
|
659 |
$this->matchChar('{', false)
|
660 |
) {
|
661 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
662 |
+
|
663 |
+
while (
|
664 |
+
$cond[0] === Type::T_LIST &&
|
665 |
+
! empty($cond['enclosing']) &&
|
666 |
+
$cond['enclosing'] === 'parent' &&
|
667 |
+
\count($cond[2]) == 1
|
668 |
+
) {
|
669 |
+
$cond = reset($cond[2]);
|
670 |
}
|
671 |
|
672 |
+
$while = new WhileBlock();
|
673 |
+
$this->registerPushedBlock($while, $s);
|
674 |
$while->cond = $cond;
|
675 |
|
676 |
return true;
|
678 |
|
679 |
$this->seek($s);
|
680 |
|
681 |
+
if (
|
682 |
+
$this->literal('@for', 4) &&
|
683 |
$this->variable($varName) &&
|
684 |
$this->literal('from', 4) &&
|
685 |
$this->expression($start) &&
|
688 |
$this->expression($end) &&
|
689 |
$this->matchChar('{', false)
|
690 |
) {
|
691 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
692 |
|
693 |
+
$for = new ForBlock();
|
694 |
+
$this->registerPushedBlock($for, $s);
|
695 |
$for->var = $varName[1];
|
696 |
$for->start = $start;
|
697 |
$for->end = $end;
|
702 |
|
703 |
$this->seek($s);
|
704 |
|
705 |
+
if (
|
706 |
+
$this->literal('@if', 3) &&
|
707 |
+
$this->functionCallArgumentsList($cond, false, '{', false)
|
708 |
+
) {
|
709 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
710 |
|
711 |
+
$if = new IfBlock();
|
712 |
+
$this->registerPushedBlock($if, $s);
|
713 |
|
714 |
+
while (
|
715 |
+
$cond[0] === Type::T_LIST &&
|
716 |
+
! empty($cond['enclosing']) &&
|
717 |
+
$cond['enclosing'] === 'parent' &&
|
718 |
+
\count($cond[2]) == 1
|
719 |
+
) {
|
720 |
$cond = reset($cond[2]);
|
721 |
}
|
722 |
|
728 |
|
729 |
$this->seek($s);
|
730 |
|
731 |
+
if (
|
732 |
+
$this->literal('@debug', 6) &&
|
733 |
+
$this->functionCallArgumentsList($value, false)
|
734 |
) {
|
735 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
736 |
|
737 |
$this->append([Type::T_DEBUG, $value], $s);
|
738 |
|
741 |
|
742 |
$this->seek($s);
|
743 |
|
744 |
+
if (
|
745 |
+
$this->literal('@warn', 5) &&
|
746 |
+
$this->functionCallArgumentsList($value, false)
|
747 |
) {
|
748 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
749 |
|
750 |
$this->append([Type::T_WARN, $value], $s);
|
751 |
|
754 |
|
755 |
$this->seek($s);
|
756 |
|
757 |
+
if (
|
758 |
+
$this->literal('@error', 6) &&
|
759 |
+
$this->functionCallArgumentsList($value, false)
|
760 |
) {
|
761 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
762 |
|
763 |
$this->append([Type::T_ERROR, $value], $s);
|
764 |
|
767 |
|
768 |
$this->seek($s);
|
769 |
|
770 |
+
if (
|
771 |
+
$this->literal('@content', 8) &&
|
772 |
($this->end() ||
|
773 |
$this->matchChar('(') &&
|
774 |
$this->argValues($argContent) &&
|
775 |
$this->matchChar(')') &&
|
776 |
$this->end())
|
777 |
) {
|
778 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
779 |
|
780 |
$this->append([Type::T_MIXIN_CONTENT, isset($argContent) ? $argContent : null], $s);
|
781 |
|
788 |
|
789 |
if (isset($last) && $last[0] === Type::T_IF) {
|
790 |
list(, $if) = $last;
|
791 |
+
assert($if instanceof IfBlock);
|
792 |
|
793 |
if ($this->literal('@else', 5)) {
|
794 |
if ($this->matchChar('{', false)) {
|
795 |
+
$else = new ElseBlock();
|
796 |
+
} elseif (
|
797 |
+
$this->literal('if', 2) &&
|
798 |
+
$this->functionCallArgumentsList($cond, false, '{', false)
|
799 |
+
) {
|
800 |
+
$else = new ElseifBlock();
|
801 |
$else->cond = $cond;
|
802 |
}
|
803 |
|
804 |
if (isset($else)) {
|
805 |
+
$this->registerPushedBlock($else, $s);
|
806 |
$if->cases[] = $else;
|
807 |
|
808 |
return true;
|
813 |
}
|
814 |
|
815 |
// only retain the first @charset directive encountered
|
816 |
+
if (
|
817 |
+
$this->literal('@charset', 8) &&
|
818 |
$this->valueList($charset) &&
|
819 |
$this->end()
|
820 |
) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
821 |
return true;
|
822 |
}
|
823 |
|
824 |
$this->seek($s);
|
825 |
|
826 |
+
if (
|
827 |
+
$this->literal('@supports', 9) &&
|
828 |
+
($t1 = $this->supportsQuery($supportQuery)) &&
|
829 |
+
($t2 = $this->matchChar('{', false))
|
830 |
) {
|
831 |
+
$directive = new DirectiveBlock();
|
832 |
+
$this->registerPushedBlock($directive, $s);
|
833 |
$directive->name = 'supports';
|
834 |
$directive->value = $supportQuery;
|
835 |
|
839 |
$this->seek($s);
|
840 |
|
841 |
// doesn't match built in directive, do generic one
|
842 |
+
if (
|
843 |
+
$this->matchChar('@', false) &&
|
844 |
+
$this->mixedKeyword($dirName) &&
|
845 |
$this->directiveValue($dirValue, '{')
|
846 |
) {
|
847 |
+
if (count($dirName) === 1 && is_string(reset($dirName))) {
|
848 |
+
$dirName = reset($dirName);
|
849 |
+
} else {
|
850 |
+
$dirName = [Type::T_STRING, '', $dirName];
|
851 |
+
}
|
852 |
if ($dirName === 'media') {
|
853 |
+
$directive = new MediaBlock();
|
854 |
} else {
|
855 |
+
$directive = new DirectiveBlock();
|
856 |
$directive->name = $dirName;
|
857 |
}
|
858 |
+
$this->registerPushedBlock($directive, $s);
|
859 |
|
860 |
if (isset($dirValue)) {
|
861 |
+
! $this->cssOnly || ($dirValue = $this->assertPlainCssValid($dirValue));
|
862 |
$directive->value = $dirValue;
|
863 |
}
|
864 |
|
868 |
$this->seek($s);
|
869 |
|
870 |
// maybe it's a generic blockless directive
|
871 |
+
if (
|
872 |
+
$this->matchChar('@', false) &&
|
873 |
+
$this->mixedKeyword($dirName) &&
|
874 |
+
! $this->isKnownGenericDirective($dirName) &&
|
875 |
+
($this->end(false) || ($this->directiveValue($dirValue, '') && $this->end(false)))
|
876 |
) {
|
877 |
+
if (\count($dirName) === 1 && \is_string(\reset($dirName))) {
|
878 |
+
$dirName = \reset($dirName);
|
879 |
+
} else {
|
880 |
+
$dirName = [Type::T_STRING, '', $dirName];
|
881 |
+
}
|
882 |
+
if (
|
883 |
+
! empty($this->env->parent) &&
|
884 |
+
$this->env->type &&
|
885 |
+
! \in_array($this->env->type, [Type::T_DIRECTIVE, Type::T_MEDIA])
|
886 |
+
) {
|
887 |
+
$plain = \trim(\substr($this->buffer, $s, $this->count - $s));
|
888 |
+
throw $this->parseError(
|
889 |
+
"Unknown directive `{$plain}` not allowed in `" . $this->env->type . "` block"
|
890 |
+
);
|
891 |
+
}
|
892 |
+
// blockless directives with a blank line after keeps their blank lines after
|
893 |
+
// sass-spec compliance purpose
|
894 |
+
$s = $this->count;
|
895 |
+
$hasBlankLine = false;
|
896 |
+
if ($this->match('\s*?\n\s*\n', $out, false)) {
|
897 |
+
$hasBlankLine = true;
|
898 |
+
$this->seek($s);
|
899 |
+
}
|
900 |
+
$isNotRoot = ! empty($this->env->parent);
|
901 |
+
$this->append([Type::T_DIRECTIVE, [$dirName, $dirValue, $hasBlankLine, $isNotRoot]], $s);
|
902 |
+
$this->whitespace();
|
903 |
|
904 |
return true;
|
905 |
}
|
909 |
return false;
|
910 |
}
|
911 |
|
912 |
+
$inCssSelector = null;
|
913 |
+
if ($this->cssOnly) {
|
914 |
+
$inCssSelector = (! empty($this->env->parent) &&
|
915 |
+
! in_array($this->env->type, [Type::T_DIRECTIVE, Type::T_MEDIA]));
|
916 |
+
}
|
917 |
// custom properties : right part is static
|
918 |
+
if (($this->customProperty($name) ) && $this->matchChar(':', false)) {
|
|
|
|
|
919 |
$start = $this->count;
|
920 |
|
921 |
// but can be complex and finish with ; or }
|
922 |
foreach ([';','}'] as $ending) {
|
923 |
+
if (
|
924 |
+
$this->openString($ending, $stringValue, '(', ')', false) &&
|
925 |
$this->end()
|
926 |
) {
|
927 |
$end = $this->count;
|
936 |
if ($p && $p < $end) {
|
937 |
$this->seek($start);
|
938 |
|
939 |
+
if (
|
940 |
+
$this->openString($ending, $stringValue, $nestingPair[0], $nestingPair[1], false) &&
|
941 |
$this->end() &&
|
942 |
$this->count > $end
|
943 |
) {
|
961 |
|
962 |
// property shortcut
|
963 |
// captures most properties before having to parse a selector
|
964 |
+
if (
|
965 |
+
$this->keyword($name, false) &&
|
966 |
$this->literal(': ', 2) &&
|
967 |
$this->valueList($value) &&
|
968 |
$this->end()
|
976 |
$this->seek($s);
|
977 |
|
978 |
// variable assigns
|
979 |
+
if (
|
980 |
+
$this->variable($name) &&
|
981 |
$this->matchChar(':') &&
|
982 |
$this->valueList($value) &&
|
983 |
$this->end()
|
984 |
) {
|
985 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
|
|
|
|
986 |
|
987 |
// check for '!flag'
|
988 |
$assignmentFlags = $this->stripAssignmentFlags($value);
|
993 |
|
994 |
$this->seek($s);
|
995 |
|
|
|
|
|
|
|
|
|
|
|
996 |
// opening css block
|
997 |
+
if (
|
998 |
+
$this->selectors($selectors) &&
|
999 |
+
$this->matchChar('{', false)
|
1000 |
+
) {
|
1001 |
+
! $this->cssOnly || ! $inCssSelector || $this->assertPlainCssValid(false);
|
|
|
1002 |
|
1003 |
$this->pushBlock($selectors, $s);
|
1004 |
|
1013 |
$this->seek($s);
|
1014 |
|
1015 |
// property assign, or nested assign
|
1016 |
+
if (
|
1017 |
+
$this->propertyName($name) &&
|
1018 |
+
$this->matchChar(':')
|
1019 |
+
) {
|
1020 |
$foundSomething = false;
|
1021 |
|
1022 |
if ($this->valueList($value)) {
|
1023 |
if (empty($this->env->parent)) {
|
1024 |
+
throw $this->parseError('expected "{"');
|
1025 |
}
|
1026 |
|
1027 |
$this->append([Type::T_ASSIGN, $name, $value], $s);
|
1029 |
}
|
1030 |
|
1031 |
if ($this->matchChar('{', false)) {
|
1032 |
+
! $this->cssOnly || $this->assertPlainCssValid(false);
|
|
|
|
|
1033 |
|
1034 |
+
$propBlock = new NestedPropertyBlock();
|
1035 |
+
$this->registerPushedBlock($propBlock, $s);
|
1036 |
$propBlock->prefix = $name;
|
1037 |
$propBlock->hasValue = $foundSomething;
|
1038 |
|
1053 |
$block = $this->popBlock();
|
1054 |
|
1055 |
if (! isset($block->type) || $block->type !== Type::T_IF) {
|
1056 |
+
assert($this->env !== null);
|
1057 |
+
|
1058 |
if ($this->env->parent) {
|
1059 |
$this->append(null); // collect comments before next statement if needed
|
1060 |
}
|
1061 |
}
|
1062 |
|
1063 |
+
if ($block instanceof ContentBlock) {
|
1064 |
$include = $block->child;
|
1065 |
+
assert(\is_array($include));
|
1066 |
unset($block->child);
|
1067 |
$include[3] = $block;
|
1068 |
$this->append($include, $s);
|
1069 |
+
} elseif (!$block instanceof ElseBlock && !$block instanceof ElseifBlock) {
|
1070 |
$type = isset($block->type) ? $block->type : Type::T_BLOCK;
|
1071 |
$this->append([$type, $block], $s);
|
1072 |
}
|
1074 |
// collect comments just after the block closing if needed
|
1075 |
if ($this->eatWhiteDefault) {
|
1076 |
$this->whitespace();
|
1077 |
+
assert($this->env !== null);
|
1078 |
|
1079 |
if ($this->env->comments) {
|
1080 |
$this->append(null);
|
1085 |
}
|
1086 |
|
1087 |
// extra stuff
|
1088 |
+
if ($this->matchChar(';')) {
|
|
|
|
|
1089 |
return true;
|
1090 |
}
|
1091 |
|
1095 |
/**
|
1096 |
* Push block onto parse tree
|
1097 |
*
|
1098 |
+
* @param array|null $selectors
|
1099 |
+
* @param int $pos
|
1100 |
*
|
1101 |
+
* @return Block
|
1102 |
*/
|
1103 |
protected function pushBlock($selectors, $pos = 0)
|
1104 |
+
{
|
1105 |
+
$b = new Block();
|
1106 |
+
$b->selectors = $selectors;
|
1107 |
+
|
1108 |
+
$this->registerPushedBlock($b, $pos);
|
1109 |
+
|
1110 |
+
return $b;
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
/**
|
1114 |
+
* @param Block $b
|
1115 |
+
* @param int $pos
|
1116 |
+
*
|
1117 |
+
* @return void
|
1118 |
+
*/
|
1119 |
+
private function registerPushedBlock(Block $b, $pos)
|
1120 |
{
|
1121 |
list($line, $column) = $this->getSourcePosition($pos);
|
1122 |
|
|
|
1123 |
$b->sourceName = $this->sourceName;
|
1124 |
$b->sourceLine = $line;
|
1125 |
$b->sourceColumn = $column;
|
1126 |
$b->sourceIndex = $this->sourceIndex;
|
|
|
1127 |
$b->comments = [];
|
1128 |
$b->parent = $this->env;
|
1129 |
|
1143 |
// collect comments at the beginning of a block if needed
|
1144 |
if ($this->eatWhiteDefault) {
|
1145 |
$this->whitespace();
|
1146 |
+
assert($this->env !== null);
|
1147 |
|
1148 |
if ($this->env->comments) {
|
1149 |
$this->append(null);
|
1150 |
}
|
1151 |
}
|
|
|
|
|
1152 |
}
|
1153 |
|
1154 |
/**
|
1155 |
* Push special (named) block onto parse tree
|
1156 |
*
|
1157 |
+
* @deprecated
|
1158 |
+
*
|
1159 |
* @param string $type
|
1160 |
+
* @param int $pos
|
1161 |
*
|
1162 |
+
* @return Block
|
1163 |
*/
|
1164 |
protected function pushSpecialBlock($type, $pos)
|
1165 |
{
|
1172 |
/**
|
1173 |
* Pop scope and return last block
|
1174 |
*
|
1175 |
+
* @return Block
|
1176 |
*
|
1177 |
* @throws \Exception
|
1178 |
*/
|
1179 |
protected function popBlock()
|
1180 |
{
|
1181 |
+
assert($this->env !== null);
|
1182 |
|
1183 |
// collect comments ending just before of a block closing
|
1184 |
if ($this->env->comments) {
|
1189 |
$block = $this->env;
|
1190 |
|
1191 |
if (empty($block->parent)) {
|
1192 |
+
throw $this->parseError('unexpected }');
|
1193 |
}
|
1194 |
|
1195 |
if ($block->type == Type::T_AT_ROOT) {
|
1207 |
/**
|
1208 |
* Peek input stream
|
1209 |
*
|
1210 |
+
* @param string $regex
|
1211 |
+
* @param array $out
|
1212 |
+
* @param int $from
|
1213 |
*
|
1214 |
+
* @return int
|
1215 |
*/
|
1216 |
protected function peek($regex, &$out, $from = null)
|
1217 |
{
|
1220 |
}
|
1221 |
|
1222 |
$r = '/' . $regex . '/' . $this->patternModifiers;
|
1223 |
+
$result = preg_match($r, $this->buffer, $out, 0, $from);
|
1224 |
|
1225 |
return $result;
|
1226 |
}
|
1228 |
/**
|
1229 |
* Seek to position in input stream (or return current position in input stream)
|
1230 |
*
|
1231 |
+
* @param int $where
|
1232 |
+
*
|
1233 |
+
* @return void
|
1234 |
*/
|
1235 |
protected function seek($where)
|
1236 |
{
|
1237 |
$this->count = $where;
|
1238 |
}
|
1239 |
|
1240 |
+
/**
|
1241 |
+
* Assert a parsed part is plain CSS Valid
|
1242 |
+
*
|
1243 |
+
* @param array|false $parsed
|
1244 |
+
* @param int $startPos
|
1245 |
+
*
|
1246 |
+
* @return array
|
1247 |
+
*
|
1248 |
+
* @throws ParserException
|
1249 |
+
*/
|
1250 |
+
protected function assertPlainCssValid($parsed, $startPos = null)
|
1251 |
+
{
|
1252 |
+
$type = '';
|
1253 |
+
if ($parsed) {
|
1254 |
+
$type = $parsed[0];
|
1255 |
+
$parsed = $this->isPlainCssValidElement($parsed);
|
1256 |
+
}
|
1257 |
+
if (! $parsed) {
|
1258 |
+
if (! \is_null($startPos)) {
|
1259 |
+
$plain = rtrim(substr($this->buffer, $startPos, $this->count - $startPos));
|
1260 |
+
$message = "Error : `{$plain}` isn't allowed in plain CSS";
|
1261 |
+
} else {
|
1262 |
+
$message = 'Error: SCSS syntax not allowed in CSS file';
|
1263 |
+
}
|
1264 |
+
if ($type) {
|
1265 |
+
$message .= " ($type)";
|
1266 |
+
}
|
1267 |
+
throw $this->parseError($message);
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
return $parsed;
|
1271 |
+
}
|
1272 |
+
|
1273 |
+
/**
|
1274 |
+
* Check a parsed element is plain CSS Valid
|
1275 |
+
*
|
1276 |
+
* @param array $parsed
|
1277 |
+
* @param bool $allowExpression
|
1278 |
+
*
|
1279 |
+
* @return array|false
|
1280 |
+
*/
|
1281 |
+
protected function isPlainCssValidElement($parsed, $allowExpression = false)
|
1282 |
+
{
|
1283 |
+
// keep string as is
|
1284 |
+
if (is_string($parsed)) {
|
1285 |
+
return $parsed;
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
if (
|
1289 |
+
\in_array($parsed[0], [Type::T_FUNCTION, Type::T_FUNCTION_CALL]) &&
|
1290 |
+
!\in_array($parsed[1], [
|
1291 |
+
'alpha',
|
1292 |
+
'attr',
|
1293 |
+
'calc',
|
1294 |
+
'cubic-bezier',
|
1295 |
+
'env',
|
1296 |
+
'grayscale',
|
1297 |
+
'hsl',
|
1298 |
+
'hsla',
|
1299 |
+
'hwb',
|
1300 |
+
'invert',
|
1301 |
+
'linear-gradient',
|
1302 |
+
'min',
|
1303 |
+
'max',
|
1304 |
+
'radial-gradient',
|
1305 |
+
'repeating-linear-gradient',
|
1306 |
+
'repeating-radial-gradient',
|
1307 |
+
'rgb',
|
1308 |
+
'rgba',
|
1309 |
+
'rotate',
|
1310 |
+
'saturate',
|
1311 |
+
'var',
|
1312 |
+
]) &&
|
1313 |
+
Compiler::isNativeFunction($parsed[1])
|
1314 |
+
) {
|
1315 |
+
return false;
|
1316 |
+
}
|
1317 |
+
|
1318 |
+
switch ($parsed[0]) {
|
1319 |
+
case Type::T_BLOCK:
|
1320 |
+
case Type::T_KEYWORD:
|
1321 |
+
case Type::T_NULL:
|
1322 |
+
case Type::T_NUMBER:
|
1323 |
+
case Type::T_MEDIA:
|
1324 |
+
return $parsed;
|
1325 |
+
|
1326 |
+
case Type::T_COMMENT:
|
1327 |
+
if (isset($parsed[2])) {
|
1328 |
+
return false;
|
1329 |
+
}
|
1330 |
+
return $parsed;
|
1331 |
+
|
1332 |
+
case Type::T_DIRECTIVE:
|
1333 |
+
if (\is_array($parsed[1])) {
|
1334 |
+
$parsed[1][1] = $this->isPlainCssValidElement($parsed[1][1]);
|
1335 |
+
if (! $parsed[1][1]) {
|
1336 |
+
return false;
|
1337 |
+
}
|
1338 |
+
}
|
1339 |
+
|
1340 |
+
return $parsed;
|
1341 |
+
|
1342 |
+
case Type::T_IMPORT:
|
1343 |
+
if ($parsed[1][0] === Type::T_LIST) {
|
1344 |
+
return false;
|
1345 |
+
}
|
1346 |
+
$parsed[1] = $this->isPlainCssValidElement($parsed[1]);
|
1347 |
+
if ($parsed[1] === false) {
|
1348 |
+
return false;
|
1349 |
+
}
|
1350 |
+
return $parsed;
|
1351 |
+
|
1352 |
+
case Type::T_STRING:
|
1353 |
+
foreach ($parsed[2] as $k => $substr) {
|
1354 |
+
if (\is_array($substr)) {
|
1355 |
+
$parsed[2][$k] = $this->isPlainCssValidElement($substr);
|
1356 |
+
if (! $parsed[2][$k]) {
|
1357 |
+
return false;
|
1358 |
+
}
|
1359 |
+
}
|
1360 |
+
}
|
1361 |
+
return $parsed;
|
1362 |
+
|
1363 |
+
case Type::T_LIST:
|
1364 |
+
if (!empty($parsed['enclosing'])) {
|
1365 |
+
return false;
|
1366 |
+
}
|
1367 |
+
foreach ($parsed[2] as $k => $listElement) {
|
1368 |
+
$parsed[2][$k] = $this->isPlainCssValidElement($listElement);
|
1369 |
+
if (! $parsed[2][$k]) {
|
1370 |
+
return false;
|
1371 |
+
}
|
1372 |
+
}
|
1373 |
+
return $parsed;
|
1374 |
+
|
1375 |
+
case Type::T_ASSIGN:
|
1376 |
+
foreach ([1, 2, 3] as $k) {
|
1377 |
+
if (! empty($parsed[$k])) {
|
1378 |
+
$parsed[$k] = $this->isPlainCssValidElement($parsed[$k]);
|
1379 |
+
if (! $parsed[$k]) {
|
1380 |
+
return false;
|
1381 |
+
}
|
1382 |
+
}
|
1383 |
+
}
|
1384 |
+
return $parsed;
|
1385 |
+
|
1386 |
+
case Type::T_EXPRESSION:
|
1387 |
+
list( ,$op, $lhs, $rhs, $inParens, $whiteBefore, $whiteAfter) = $parsed;
|
1388 |
+
if (! $allowExpression && ! \in_array($op, ['and', 'or', '/'])) {
|
1389 |
+
return false;
|
1390 |
+
}
|
1391 |
+
$lhs = $this->isPlainCssValidElement($lhs, true);
|
1392 |
+
if (! $lhs) {
|
1393 |
+
return false;
|
1394 |
+
}
|
1395 |
+
$rhs = $this->isPlainCssValidElement($rhs, true);
|
1396 |
+
if (! $rhs) {
|
1397 |
+
return false;
|
1398 |
+
}
|
1399 |
+
|
1400 |
+
return [
|
1401 |
+
Type::T_STRING,
|
1402 |
+
'', [
|
1403 |
+
$this->inParens ? '(' : '',
|
1404 |
+
$lhs,
|
1405 |
+
($whiteBefore ? ' ' : '') . $op . ($whiteAfter ? ' ' : ''),
|
1406 |
+
$rhs,
|
1407 |
+
$this->inParens ? ')' : ''
|
1408 |
+
]
|
1409 |
+
];
|
1410 |
+
|
1411 |
+
case Type::T_CUSTOM_PROPERTY:
|
1412 |
+
case Type::T_UNARY:
|
1413 |
+
$parsed[2] = $this->isPlainCssValidElement($parsed[2]);
|
1414 |
+
if (! $parsed[2]) {
|
1415 |
+
return false;
|
1416 |
+
}
|
1417 |
+
return $parsed;
|
1418 |
+
|
1419 |
+
case Type::T_FUNCTION:
|
1420 |
+
$argsList = $parsed[2];
|
1421 |
+
foreach ($argsList[2] as $argElement) {
|
1422 |
+
if (! $this->isPlainCssValidElement($argElement)) {
|
1423 |
+
return false;
|
1424 |
+
}
|
1425 |
+
}
|
1426 |
+
return $parsed;
|
1427 |
+
|
1428 |
+
case Type::T_FUNCTION_CALL:
|
1429 |
+
$parsed[0] = Type::T_FUNCTION;
|
1430 |
+
$argsList = [Type::T_LIST, ',', []];
|
1431 |
+
foreach ($parsed[2] as $arg) {
|
1432 |
+
if ($arg[0] || ! empty($arg[2])) {
|
1433 |
+
// no named arguments possible in a css function call
|
1434 |
+
// nor ... argument
|
1435 |
+
return false;
|
1436 |
+
}
|
1437 |
+
$arg = $this->isPlainCssValidElement($arg[1], $parsed[1] === 'calc');
|
1438 |
+
if (! $arg) {
|
1439 |
+
return false;
|
1440 |
+
}
|
1441 |
+
$argsList[2][] = $arg;
|
1442 |
+
}
|
1443 |
+
$parsed[2] = $argsList;
|
1444 |
+
return $parsed;
|
1445 |
+
}
|
1446 |
+
|
1447 |
+
return false;
|
1448 |
+
}
|
1449 |
+
|
1450 |
/**
|
1451 |
* Match string looking for either ending delim, escape, or string interpolation
|
1452 |
*
|
1453 |
* {@internal This is a workaround for preg_match's 250K string match limit. }}
|
1454 |
*
|
1455 |
* @param array $m Matches (passed by reference)
|
1456 |
+
* @param string $delim Delimiter
|
1457 |
+
*
|
1458 |
+
* @return bool True if match; false otherwise
|
1459 |
*
|
1460 |
+
* @phpstan-impure
|
1461 |
*/
|
1462 |
protected function matchString(&$m, $delim)
|
1463 |
{
|
1466 |
$end = \strlen($this->buffer);
|
1467 |
|
1468 |
// look for either ending delim, escape, or string interpolation
|
1469 |
+
foreach (['#{', '\\', "\r", $delim] as $lookahead) {
|
1470 |
$pos = strpos($this->buffer, $lookahead, $this->count);
|
1471 |
|
1472 |
if ($pos !== false && $pos < $end) {
|
1493 |
/**
|
1494 |
* Try to match something on head of buffer
|
1495 |
*
|
1496 |
+
* @param string $regex
|
1497 |
+
* @param array $out
|
1498 |
+
* @param bool $eatWhitespace
|
1499 |
*
|
1500 |
+
* @return bool
|
1501 |
+
*
|
1502 |
+
* @phpstan-impure
|
1503 |
*/
|
1504 |
protected function match($regex, &$out, $eatWhitespace = null)
|
1505 |
{
|
1506 |
$r = '/' . $regex . '/' . $this->patternModifiers;
|
1507 |
|
1508 |
+
if (! preg_match($r, $this->buffer, $out, 0, $this->count)) {
|
1509 |
return false;
|
1510 |
}
|
1511 |
|
1525 |
/**
|
1526 |
* Match a single string
|
1527 |
*
|
1528 |
+
* @param string $char
|
1529 |
+
* @param bool $eatWhitespace
|
1530 |
+
*
|
1531 |
+
* @return bool
|
1532 |
*
|
1533 |
+
* @phpstan-impure
|
1534 |
*/
|
1535 |
protected function matchChar($char, $eatWhitespace = null)
|
1536 |
{
|
1554 |
/**
|
1555 |
* Match literal string
|
1556 |
*
|
1557 |
+
* @param string $what
|
1558 |
+
* @param int $len
|
1559 |
+
* @param bool $eatWhitespace
|
1560 |
+
*
|
1561 |
+
* @return bool
|
1562 |
*
|
1563 |
+
* @phpstan-impure
|
1564 |
*/
|
1565 |
protected function literal($what, $len, $eatWhitespace = null)
|
1566 |
{
|
1584 |
/**
|
1585 |
* Match some whitespace
|
1586 |
*
|
1587 |
+
* @return bool
|
1588 |
+
*
|
1589 |
+
* @phpstan-impure
|
1590 |
*/
|
1591 |
protected function whitespace()
|
1592 |
{
|
1593 |
$gotWhite = false;
|
1594 |
|
1595 |
+
while (preg_match(static::$whitePattern, $this->buffer, $m, 0, $this->count)) {
|
1596 |
if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
|
1597 |
// comment that are kept in the output CSS
|
1598 |
$comment = [];
|
1611 |
if ($this->interpolation($out)) {
|
1612 |
// keep right spaces in the following string part
|
1613 |
if ($out[3]) {
|
1614 |
+
while ($this->buffer[$this->count - 1] !== '}') {
|
1615 |
$this->count--;
|
1616 |
}
|
1617 |
|
1620 |
|
1621 |
$comment[] = [Type::T_COMMENT, substr($this->buffer, $p, $this->count - $p), $out];
|
1622 |
} else {
|
1623 |
+
list($line, $column) = $this->getSourcePosition($this->count);
|
1624 |
+
$file = $this->sourceName;
|
1625 |
+
if (!$this->discardComments) {
|
1626 |
+
$this->logger->warn("Unterminated interpolations in multiline comments are deprecated and will be removed in ScssPhp 2.0, in \"$file\", line $line, column $column.", true);
|
1627 |
+
}
|
1628 |
$comment[] = substr($this->buffer, $this->count, 2);
|
1629 |
|
1630 |
$this->count += 2;
|
1638 |
|
1639 |
if (! $comment) {
|
1640 |
// single part static comment
|
1641 |
+
$commentStatement = [Type::T_COMMENT, $c];
|
1642 |
} else {
|
1643 |
$comment[] = $c;
|
1644 |
$staticComment = substr($this->buffer, $startCommentCount, $endCommentCount - $startCommentCount);
|
1645 |
+
$commentStatement = [Type::T_COMMENT, $staticComment, [Type::T_STRING, '', $comment]];
|
1646 |
}
|
1647 |
|
1648 |
+
list($line, $column) = $this->getSourcePosition($startCommentCount);
|
1649 |
+
$commentStatement[self::SOURCE_LINE] = $line;
|
1650 |
+
$commentStatement[self::SOURCE_COLUMN] = $column;
|
1651 |
+
$commentStatement[self::SOURCE_INDEX] = $this->sourceIndex;
|
1652 |
+
|
1653 |
+
$this->appendComment($commentStatement);
|
1654 |
+
|
1655 |
$this->commentsSeen[$startCommentCount] = true;
|
1656 |
$this->count = $endCommentCount;
|
1657 |
} else {
|
1658 |
// comment that are ignored and not kept in the output css
|
1659 |
$this->count += \strlen($m[0]);
|
1660 |
+
// silent comments are not allowed in plain CSS files
|
1661 |
+
! $this->cssOnly
|
1662 |
+
|| ! \strlen(trim($m[0]))
|
1663 |
+
|| $this->assertPlainCssValid(false, $this->count - \strlen($m[0]));
|
1664 |
}
|
1665 |
|
1666 |
$gotWhite = true;
|
1673 |
* Append comment to current block
|
1674 |
*
|
1675 |
* @param array $comment
|
1676 |
+
*
|
1677 |
+
* @return void
|
1678 |
*/
|
1679 |
protected function appendComment($comment)
|
1680 |
{
|
1681 |
+
assert($this->env !== null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1682 |
|
1683 |
+
if (! $this->discardComments) {
|
1684 |
$this->env->comments[] = $comment;
|
1685 |
}
|
1686 |
}
|
1688 |
/**
|
1689 |
* Append statement to current block
|
1690 |
*
|
1691 |
+
* @param array|null $statement
|
1692 |
+
* @param int $pos
|
1693 |
+
*
|
1694 |
+
* @return void
|
1695 |
*/
|
1696 |
protected function append($statement, $pos = null)
|
1697 |
{
|
1698 |
+
assert($this->env !== null);
|
1699 |
+
|
1700 |
if (! \is_null($statement)) {
|
1701 |
+
! $this->cssOnly || ($statement = $this->assertPlainCssValid($statement, $pos));
|
1702 |
+
|
1703 |
if (! \is_null($pos)) {
|
1704 |
list($line, $column) = $this->getSourcePosition($pos);
|
1705 |
|
1726 |
*/
|
1727 |
protected function last()
|
1728 |
{
|
1729 |
+
assert($this->env !== null);
|
1730 |
+
|
1731 |
$i = \count($this->env->children) - 1;
|
1732 |
|
1733 |
if (isset($this->env->children[$i])) {
|
1734 |
return $this->env->children[$i];
|
1735 |
}
|
1736 |
+
|
1737 |
+
return null;
|
1738 |
}
|
1739 |
|
1740 |
/**
|
1742 |
*
|
1743 |
* @param array $out
|
1744 |
*
|
1745 |
+
* @return bool
|
1746 |
*/
|
1747 |
protected function mediaQueryList(&$out)
|
1748 |
{
|
1754 |
*
|
1755 |
* @param array $out
|
1756 |
*
|
1757 |
+
* @return bool
|
1758 |
*/
|
1759 |
protected function mediaQuery(&$out)
|
1760 |
{
|
1761 |
$expressions = null;
|
1762 |
$parts = [];
|
1763 |
|
1764 |
+
if (
|
1765 |
+
($this->literal('only', 4) && ($only = true) ||
|
1766 |
+
$this->literal('not', 3) && ($not = true) || true) &&
|
1767 |
$this->mixedKeyword($mediaType)
|
1768 |
) {
|
1769 |
$prop = [Type::T_MEDIA_TYPE];
|
1808 |
*
|
1809 |
* @param array $out
|
1810 |
*
|
1811 |
+
* @return bool
|
1812 |
*/
|
1813 |
protected function supportsQuery(&$out)
|
1814 |
{
|
1819 |
|
1820 |
$not = false;
|
1821 |
|
1822 |
+
if (
|
1823 |
+
($this->literal('not', 3) && ($not = true) || true) &&
|
1824 |
$this->matchChar('(') &&
|
1825 |
($this->expression($property)) &&
|
1826 |
$this->literal(': ', 2) &&
|
1839 |
$this->seek($s);
|
1840 |
}
|
1841 |
|
1842 |
+
if (
|
1843 |
+
$this->matchChar('(') &&
|
1844 |
$this->supportsQuery($subQuery) &&
|
1845 |
$this->matchChar(')')
|
1846 |
) {
|
1850 |
$this->seek($s);
|
1851 |
}
|
1852 |
|
1853 |
+
if (
|
1854 |
+
$this->literal('not', 3) &&
|
1855 |
$this->supportsQuery($subQuery)
|
1856 |
) {
|
1857 |
$parts[] = [Type::T_STRING, '', [[Type::T_KEYWORD, 'not '], $subQuery]];
|
1860 |
$this->seek($s);
|
1861 |
}
|
1862 |
|
1863 |
+
if (
|
1864 |
+
$this->literal('selector(', 9) &&
|
1865 |
$this->selector($selector) &&
|
1866 |
$this->matchChar(')')
|
1867 |
) {
|
1898 |
$this->seek($s);
|
1899 |
}
|
1900 |
|
1901 |
+
if (
|
1902 |
+
$this->literal('and', 3) &&
|
1903 |
+
$this->genericList($expressions, 'supportsQuery', ' and', false)
|
1904 |
+
) {
|
1905 |
array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
|
1906 |
|
1907 |
$parts = [$expressions];
|
1910 |
$this->seek($s);
|
1911 |
}
|
1912 |
|
1913 |
+
if (
|
1914 |
+
$this->literal('or', 2) &&
|
1915 |
+
$this->genericList($expressions, 'supportsQuery', ' or', false)
|
1916 |
+
) {
|
1917 |
array_unshift($expressions[2], [Type::T_STRING, '', $parts]);
|
1918 |
|
1919 |
$parts = [$expressions];
|
1941 |
*
|
1942 |
* @param array $out
|
1943 |
*
|
1944 |
+
* @return bool
|
1945 |
*/
|
1946 |
protected function mediaExpression(&$out)
|
1947 |
{
|
1948 |
$s = $this->count;
|
1949 |
$value = null;
|
1950 |
|
1951 |
+
if (
|
1952 |
+
$this->matchChar('(') &&
|
1953 |
$this->expression($feature) &&
|
1954 |
+
($this->matchChar(':') &&
|
1955 |
+
$this->expression($value) || true) &&
|
1956 |
$this->matchChar(')')
|
1957 |
) {
|
1958 |
$out = [Type::T_MEDIA_EXPRESSION, $feature];
|
1974 |
*
|
1975 |
* @param array $out
|
1976 |
*
|
1977 |
+
* @return bool
|
1978 |
*/
|
1979 |
protected function argValues(&$out)
|
1980 |
{
|
1981 |
+
$discardComments = $this->discardComments;
|
1982 |
+
$this->discardComments = true;
|
1983 |
+
|
1984 |
if ($this->genericList($list, 'argValue', ',', false)) {
|
1985 |
$out = $list[2];
|
1986 |
|
1987 |
+
$this->discardComments = $discardComments;
|
1988 |
+
|
1989 |
return true;
|
1990 |
}
|
1991 |
|
1992 |
+
$this->discardComments = $discardComments;
|
1993 |
+
|
1994 |
return false;
|
1995 |
}
|
1996 |
|
1999 |
*
|
2000 |
* @param array $out
|
2001 |
*
|
2002 |
+
* @return bool
|
2003 |
*/
|
2004 |
protected function argValue(&$out)
|
2005 |
{
|
2013 |
$keyword = null;
|
2014 |
}
|
2015 |
|
2016 |
+
if ($this->genericList($value, 'expression', '', true)) {
|
2017 |
$out = [$keyword, $value, false];
|
2018 |
$s = $this->count;
|
2019 |
|
2029 |
return false;
|
2030 |
}
|
2031 |
|
2032 |
+
/**
|
2033 |
+
* Check if a generic directive is known to be able to allow almost any syntax or not
|
2034 |
+
* @param mixed $directiveName
|
2035 |
+
* @return bool
|
2036 |
+
*/
|
2037 |
+
protected function isKnownGenericDirective($directiveName)
|
2038 |
+
{
|
2039 |
+
if (\is_array($directiveName) && \is_string(reset($directiveName))) {
|
2040 |
+
$directiveName = reset($directiveName);
|
2041 |
+
}
|
2042 |
+
if (! \is_string($directiveName)) {
|
2043 |
+
return false;
|
2044 |
+
}
|
2045 |
+
if (
|
2046 |
+
\in_array($directiveName, [
|
2047 |
+
'at-root',
|
2048 |
+
'media',
|
2049 |
+
'mixin',
|
2050 |
+
'include',
|
2051 |
+
'scssphp-import-once',
|
2052 |
+
'import',
|
2053 |
+
'extend',
|
2054 |
+
'function',
|
2055 |
+
'break',
|
2056 |
+
'continue',
|
2057 |
+
'return',
|
2058 |
+
'each',
|
2059 |
+
'while',
|
2060 |
+
'for',
|
2061 |
+
'if',
|
2062 |
+
'debug',
|
2063 |
+
'warn',
|
2064 |
+
'error',
|
2065 |
+
'content',
|
2066 |
+
'else',
|
2067 |
+
'charset',
|
2068 |
+
'supports',
|
2069 |
+
// Todo
|
2070 |
+
'use',
|
2071 |
+
'forward',
|
2072 |
+
])
|
2073 |
+
) {
|
2074 |
+
return true;
|
2075 |
+
}
|
2076 |
+
return false;
|
2077 |
+
}
|
2078 |
+
|
2079 |
/**
|
2080 |
* Parse directive value list that considers $vars as keyword
|
2081 |
*
|
2082 |
+
* @param array $out
|
2083 |
+
* @param string|false $endChar
|
2084 |
*
|
2085 |
+
* @return bool
|
2086 |
+
*
|
2087 |
+
* @phpstan-impure
|
2088 |
*/
|
2089 |
protected function directiveValue(&$out, $endChar = false)
|
2090 |
{
|
2102 |
|
2103 |
$this->seek($s);
|
2104 |
|
2105 |
+
if (\is_string($endChar) && $this->openString($endChar ? $endChar : ';', $out, null, null, true, ";}{")) {
|
2106 |
+
if ($endChar && $this->matchChar($endChar, false)) {
|
2107 |
+
return true;
|
2108 |
+
}
|
2109 |
+
$ss = $this->count;
|
2110 |
+
if (!$endChar && $this->end()) {
|
2111 |
+
$this->seek($ss);
|
2112 |
return true;
|
2113 |
}
|
2114 |
}
|
2145 |
*
|
2146 |
* @param array $out
|
2147 |
*
|
2148 |
+
* @return bool
|
2149 |
*/
|
2150 |
protected function valueList(&$out)
|
2151 |
{
|
2157 |
return $res;
|
2158 |
}
|
2159 |
|
2160 |
+
/**
|
2161 |
+
* Parse a function call, where externals () are part of the call
|
2162 |
+
* and not of the value list
|
2163 |
+
*
|
2164 |
+
* @param array $out
|
2165 |
+
* @param bool $mandatoryEnclos
|
2166 |
+
* @param null|string $charAfter
|
2167 |
+
* @param null|bool $eatWhiteSp
|
2168 |
+
*
|
2169 |
+
* @return bool
|
2170 |
+
*/
|
2171 |
+
protected function functionCallArgumentsList(&$out, $mandatoryEnclos = true, $charAfter = null, $eatWhiteSp = null)
|
2172 |
+
{
|
2173 |
+
$s = $this->count;
|
2174 |
+
|
2175 |
+
if (
|
2176 |
+
$this->matchChar('(') &&
|
2177 |
+
$this->valueList($out) &&
|
2178 |
+
$this->matchChar(')') &&
|
2179 |
+
($charAfter ? $this->matchChar($charAfter, $eatWhiteSp) : $this->end())
|
2180 |
+
) {
|
2181 |
+
return true;
|
2182 |
+
}
|
2183 |
+
|
2184 |
+
if (! $mandatoryEnclos) {
|
2185 |
+
$this->seek($s);
|
2186 |
+
|
2187 |
+
if (
|
2188 |
+
$this->valueList($out) &&
|
2189 |
+
($charAfter ? $this->matchChar($charAfter, $eatWhiteSp) : $this->end())
|
2190 |
+
) {
|
2191 |
+
return true;
|
2192 |
+
}
|
2193 |
+
}
|
2194 |
+
|
2195 |
+
$this->seek($s);
|
2196 |
+
|
2197 |
+
return false;
|
2198 |
+
}
|
2199 |
+
|
2200 |
/**
|
2201 |
* Parse space separated value list
|
2202 |
*
|
2203 |
* @param array $out
|
2204 |
*
|
2205 |
+
* @return bool
|
2206 |
*/
|
2207 |
protected function spaceList(&$out)
|
2208 |
{
|
2212 |
/**
|
2213 |
* Parse generic list
|
2214 |
*
|
2215 |
+
* @param array $out
|
2216 |
+
* @param string $parseItem The name of the method used to parse items
|
2217 |
+
* @param string $delim
|
2218 |
+
* @param bool $flatten
|
2219 |
*
|
2220 |
+
* @return bool
|
2221 |
*/
|
2222 |
protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
|
2223 |
{
|
2224 |
$s = $this->count;
|
2225 |
$items = [];
|
2226 |
+
/** @var array|Number|null $value */
|
2227 |
$value = null;
|
2228 |
|
2229 |
while ($this->$parseItem($value)) {
|
2236 |
}
|
2237 |
|
2238 |
$trailing_delim = true;
|
2239 |
+
} else {
|
2240 |
+
assert(\is_array($value) || $value instanceof Number);
|
2241 |
+
// if no delim watch that a keyword didn't eat the single/double quote
|
2242 |
+
// from the following starting string
|
2243 |
+
if ($value[0] === Type::T_KEYWORD) {
|
2244 |
+
assert(\is_array($value));
|
2245 |
+
/** @var string $word */
|
2246 |
+
$word = $value[1];
|
2247 |
+
|
2248 |
+
$last_char = substr($word, -1);
|
2249 |
+
|
2250 |
+
if (
|
2251 |
+
strlen($word) > 1 &&
|
2252 |
+
in_array($last_char, [ "'", '"']) &&
|
2253 |
+
substr($word, -2, 1) !== '\\'
|
2254 |
+
) {
|
2255 |
+
// if there is a non escaped opening quote in the keyword, this seems unlikely a mistake
|
2256 |
+
$word = str_replace('\\' . $last_char, '\\\\', $word);
|
2257 |
+
if (strpos($word, $last_char) < strlen($word) - 1) {
|
2258 |
+
continue;
|
2259 |
+
}
|
2260 |
+
|
2261 |
+
$currentCount = $this->count;
|
2262 |
+
|
2263 |
+
// let's try to rewind to previous char and try a parse
|
2264 |
+
$this->count--;
|
2265 |
+
// in case the keyword also eat spaces
|
2266 |
+
while (substr($this->buffer, $this->count, 1) !== $last_char) {
|
2267 |
+
$this->count--;
|
2268 |
+
}
|
2269 |
+
|
2270 |
+
/** @var array|Number|null $nextValue */
|
2271 |
+
$nextValue = null;
|
2272 |
+
if ($this->$parseItem($nextValue)) {
|
2273 |
+
assert(\is_array($nextValue) || $nextValue instanceof Number);
|
2274 |
+
if ($nextValue[0] === Type::T_KEYWORD && $nextValue[1] === $last_char) {
|
2275 |
+
// bad try, forget it
|
2276 |
+
$this->seek($currentCount);
|
2277 |
+
continue;
|
2278 |
+
}
|
2279 |
+
if ($nextValue[0] !== Type::T_STRING) {
|
2280 |
+
// bad try, forget it
|
2281 |
+
$this->seek($currentCount);
|
2282 |
+
continue;
|
2283 |
+
}
|
2284 |
+
|
2285 |
+
// OK it was a good idea
|
2286 |
+
$value[1] = substr($value[1], 0, -1);
|
2287 |
+
array_pop($items);
|
2288 |
+
$items[] = $value;
|
2289 |
+
$items[] = $nextValue;
|
2290 |
+
} else {
|
2291 |
+
// bad try, forget it
|
2292 |
+
$this->seek($currentCount);
|
2293 |
+
continue;
|
2294 |
+
}
|
2295 |
+
}
|
2296 |
+
}
|
2297 |
}
|
2298 |
}
|
2299 |
|
2319 |
/**
|
2320 |
* Parse expression
|
2321 |
*
|
2322 |
+
* @param array $out
|
2323 |
+
* @param bool $listOnly
|
2324 |
+
* @param bool $lookForExp
|
2325 |
*
|
2326 |
+
* @return bool
|
2327 |
+
*
|
2328 |
+
* @phpstan-impure
|
2329 |
*/
|
2330 |
protected function expression(&$out, $listOnly = false, $lookForExp = true)
|
2331 |
{
|
2335 |
$allowedTypes = ($listOnly ? [Type::T_LIST] : [Type::T_LIST, Type::T_MAP]);
|
2336 |
|
2337 |
if ($this->matchChar('(')) {
|
2338 |
+
if ($this->enclosedExpression($lhs, $s, ')', $allowedTypes)) {
|
2339 |
if ($lookForExp) {
|
2340 |
$out = $this->expHelper($lhs, 0);
|
2341 |
} else {
|
2351 |
}
|
2352 |
|
2353 |
if (\in_array(Type::T_LIST, $allowedTypes) && $this->matchChar('[')) {
|
2354 |
+
if ($this->enclosedExpression($lhs, $s, ']', [Type::T_LIST])) {
|
2355 |
if ($lookForExp) {
|
2356 |
$out = $this->expHelper($lhs, 0);
|
2357 |
} else {
|
2386 |
/**
|
2387 |
* Parse expression specifically checking for lists in parenthesis or brackets
|
2388 |
*
|
2389 |
+
* @param array $out
|
2390 |
+
* @param int $s
|
2391 |
+
* @param string $closingParen
|
2392 |
+
* @param string[] $allowedTypes
|
2393 |
+
*
|
2394 |
+
* @return bool
|
2395 |
*
|
2396 |
+
* @phpstan-param array<Type::*> $allowedTypes
|
2397 |
*/
|
2398 |
+
protected function enclosedExpression(&$out, $s, $closingParen = ')', $allowedTypes = [Type::T_LIST, Type::T_MAP])
|
2399 |
{
|
2400 |
if ($this->matchChar($closingParen) && \in_array(Type::T_LIST, $allowedTypes)) {
|
2401 |
$out = [Type::T_LIST, '', []];
|
2402 |
|
2403 |
switch ($closingParen) {
|
2404 |
+
case ')':
|
2405 |
$out['enclosing'] = 'parent'; // parenthesis list
|
2406 |
break;
|
2407 |
|
2408 |
+
case ']':
|
2409 |
$out['enclosing'] = 'bracket'; // bracketed list
|
2410 |
break;
|
2411 |
}
|
2413 |
return true;
|
2414 |
}
|
2415 |
|
2416 |
+
if (
|
2417 |
+
$this->valueList($out) &&
|
2418 |
+
$this->matchChar($closingParen) && ! ($closingParen === ')' &&
|
2419 |
+
\in_array($out[0], [Type::T_EXPRESSION, Type::T_UNARY])) &&
|
2420 |
\in_array(Type::T_LIST, $allowedTypes)
|
2421 |
) {
|
2422 |
if ($out[0] !== Type::T_LIST || ! empty($out['enclosing'])) {
|
2424 |
}
|
2425 |
|
2426 |
switch ($closingParen) {
|
2427 |
+
case ')':
|
2428 |
$out['enclosing'] = 'parent'; // parenthesis list
|
2429 |
break;
|
2430 |
|
2431 |
+
case ']':
|
2432 |
$out['enclosing'] = 'bracket'; // bracketed list
|
2433 |
break;
|
2434 |
}
|
2448 |
/**
|
2449 |
* Parse left-hand side of subexpression
|
2450 |
*
|
2451 |
+
* @param array $lhs
|
2452 |
+
* @param int $minP
|
2453 |
*
|
2454 |
* @return array
|
2455 |
*/
|
2480 |
break;
|
2481 |
}
|
2482 |
|
2483 |
+
if ($op === '-' && ! $whiteAfter && $rhs[0] === Type::T_KEYWORD) {
|
2484 |
+
break;
|
|
|
2485 |
}
|
2486 |
|
2487 |
+
// consume higher-precedence operators on the right-hand side
|
2488 |
+
$rhs = $this->expHelper($rhs, static::$precedence[$op] + 1);
|
2489 |
+
|
2490 |
$lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
|
2491 |
+
|
2492 |
$ss = $this->count;
|
2493 |
$whiteBefore = isset($this->buffer[$this->count - 1]) &&
|
2494 |
ctype_space($this->buffer[$this->count - 1]);
|
2504 |
*
|
2505 |
* @param array $out
|
2506 |
*
|
2507 |
+
* @return bool
|
2508 |
*/
|
2509 |
protected function value(&$out)
|
2510 |
{
|
2515 |
$s = $this->count;
|
2516 |
$char = $this->buffer[$this->count];
|
2517 |
|
2518 |
+
if (
|
2519 |
+
$this->literal('url(', 4) &&
|
2520 |
+
$this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)
|
2521 |
+
) {
|
2522 |
$len = strspn(
|
2523 |
$this->buffer,
|
2524 |
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=',
|
2537 |
|
2538 |
$this->seek($s);
|
2539 |
|
2540 |
+
if (
|
2541 |
+
$this->literal('url(', 4, false) &&
|
2542 |
+
$this->match('\s*(\/\/[^\s\)]+)\s*', $m)
|
2543 |
+
) {
|
2544 |
$content = 'url(' . $m[1];
|
2545 |
|
2546 |
if ($this->matchChar(')')) {
|
2555 |
|
2556 |
// not
|
2557 |
if ($char === 'n' && $this->literal('not', 3, false)) {
|
2558 |
+
if (
|
2559 |
+
$this->whitespace() &&
|
2560 |
+
$this->value($inner)
|
2561 |
+
) {
|
2562 |
$out = [Type::T_UNARY, 'not', $inner, $this->inParens];
|
2563 |
|
2564 |
return true;
|
2613 |
return true;
|
2614 |
}
|
2615 |
|
2616 |
+
if (
|
2617 |
+
$this->keyword($inner) &&
|
2618 |
+
! $this->func($inner, $out)
|
2619 |
+
) {
|
2620 |
$out = [Type::T_UNARY, '-', $inner, $this->inParens];
|
2621 |
|
2622 |
return true;
|
2644 |
$this->count++;
|
2645 |
|
2646 |
if ($this->keyword($keyword)) {
|
2647 |
+
$out = [Type::T_KEYWORD, '#' . $keyword];
|
2648 |
|
2649 |
return true;
|
2650 |
}
|
2675 |
}
|
2676 |
|
2677 |
// unicode range with wildcards
|
2678 |
+
if (
|
2679 |
+
$this->literal('U+', 2) &&
|
2680 |
+
$this->match('\?+|([0-9A-F]+(\?+|(-[0-9A-F]+))?)', $m, false)
|
2681 |
+
) {
|
2682 |
+
$unicode = explode('-', $m[0]);
|
2683 |
+
if (strlen(reset($unicode)) <= 6 && strlen(end($unicode)) <= 6) {
|
2684 |
+
$out = [Type::T_KEYWORD, 'U+' . $m[0]];
|
2685 |
|
2686 |
+
return true;
|
2687 |
+
}
|
2688 |
+
$this->count -= strlen($m[0]) + 2;
|
2689 |
}
|
2690 |
|
2691 |
if ($this->keyword($keyword, false)) {
|
2712 |
*
|
2713 |
* @param array $out
|
2714 |
*
|
2715 |
+
* @return bool
|
2716 |
*/
|
2717 |
protected function parenValue(&$out)
|
2718 |
{
|
2729 |
|
2730 |
$this->inParens = true;
|
2731 |
|
2732 |
+
if (
|
2733 |
+
$this->expression($exp) &&
|
2734 |
+
$this->matchChar(')')
|
2735 |
+
) {
|
2736 |
$out = $exp;
|
2737 |
$this->inParens = $inParens;
|
2738 |
|
2751 |
*
|
2752 |
* @param array $out
|
2753 |
*
|
2754 |
+
* @return bool
|
2755 |
*/
|
2756 |
protected function progid(&$out)
|
2757 |
{
|
2758 |
$s = $this->count;
|
2759 |
|
2760 |
+
if (
|
2761 |
+
$this->literal('progid:', 7, false) &&
|
2762 |
$this->openString('(', $fn) &&
|
2763 |
$this->matchChar('(')
|
2764 |
) {
|
2784 |
* @param string $name
|
2785 |
* @param array $func
|
2786 |
*
|
2787 |
+
* @return bool
|
2788 |
*/
|
2789 |
protected function func($name, &$func)
|
2790 |
{
|
2800 |
if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) {
|
2801 |
$ss = $this->count;
|
2802 |
|
2803 |
+
if (
|
2804 |
+
$this->argValues($args) &&
|
2805 |
+
$this->matchChar(')')
|
2806 |
+
) {
|
2807 |
$func = [Type::T_FUNCTION_CALL, $name, $args];
|
2808 |
|
2809 |
return true;
|
2812 |
$this->seek($ss);
|
2813 |
}
|
2814 |
|
2815 |
+
if (
|
2816 |
+
($this->openString(')', $str, '(') || true) &&
|
2817 |
$this->matchChar(')')
|
2818 |
) {
|
2819 |
$args = [];
|
2838 |
*
|
2839 |
* @param array $out
|
2840 |
*
|
2841 |
+
* @return bool
|
2842 |
*/
|
2843 |
protected function argumentList(&$out)
|
2844 |
{
|
2848 |
$args = [];
|
2849 |
|
2850 |
while ($this->keyword($var)) {
|
2851 |
+
if (
|
2852 |
+
$this->matchChar('=') &&
|
2853 |
+
$this->expression($exp)
|
2854 |
+
) {
|
2855 |
$args[] = [Type::T_STRING, '', [$var . '=']];
|
2856 |
$arg = $exp;
|
2857 |
} else {
|
2883 |
*
|
2884 |
* @param array $out
|
2885 |
*
|
2886 |
+
* @return bool
|
2887 |
*/
|
2888 |
protected function argumentDef(&$out)
|
2889 |
{
|
2897 |
|
2898 |
$ss = $this->count;
|
2899 |
|
2900 |
+
if (
|
2901 |
+
$this->matchChar(':') &&
|
2902 |
+
$this->genericList($defaultVal, 'expression', '', true)
|
2903 |
+
) {
|
2904 |
$arg[1] = $defaultVal;
|
2905 |
} else {
|
2906 |
$this->seek($ss);
|
2912 |
$sss = $this->count;
|
2913 |
|
2914 |
if (! $this->matchChar(')')) {
|
2915 |
+
throw $this->parseError('... has to be after the final argument');
|
2916 |
}
|
2917 |
|
2918 |
$arg[2] = true;
|
2945 |
*
|
2946 |
* @param array $out
|
2947 |
*
|
2948 |
+
* @return bool
|
2949 |
*/
|
2950 |
protected function map(&$out)
|
2951 |
{
|
2958 |
$keys = [];
|
2959 |
$values = [];
|
2960 |
|
2961 |
+
while (
|
2962 |
+
$this->genericList($key, 'expression', '', true) &&
|
2963 |
+
$this->matchChar(':') &&
|
2964 |
+
$this->genericList($value, 'expression', '', true)
|
2965 |
) {
|
2966 |
$keys[] = $key;
|
2967 |
$values[] = $value;
|
2987 |
*
|
2988 |
* @param array $out
|
2989 |
*
|
2990 |
+
* @return bool
|
2991 |
*/
|
2992 |
protected function color(&$out)
|
2993 |
{
|
2994 |
$s = $this->count;
|
2995 |
|
2996 |
+
if ($this->match('(#([0-9a-f]+)\b)', $m)) {
|
2997 |
if (\in_array(\strlen($m[2]), [3,4,6,8])) {
|
2998 |
$out = [Type::T_KEYWORD, $m[0]];
|
2999 |
|
3013 |
*
|
3014 |
* @param array $unit
|
3015 |
*
|
3016 |
+
* @return bool
|
3017 |
*/
|
3018 |
protected function unit(&$unit)
|
3019 |
{
|
3038 |
* Parse string
|
3039 |
*
|
3040 |
* @param array $out
|
3041 |
+
* @param bool $keepDelimWithInterpolation
|
3042 |
*
|
3043 |
+
* @return bool
|
3044 |
*/
|
3045 |
+
protected function string(&$out, $keepDelimWithInterpolation = false)
|
3046 |
{
|
3047 |
$s = $this->count;
|
3048 |
|
3074 |
$this->count += \strlen($m[2]);
|
3075 |
$content[] = '#{'; // ignore it
|
3076 |
}
|
3077 |
+
} elseif ($m[2] === "\r") {
|
3078 |
+
$content[] = chr(10);
|
3079 |
+
// TODO : warning
|
3080 |
+
# DEPRECATION WARNING on line x, column y of zzz:
|
3081 |
+
# Unescaped multiline strings are deprecated and will be removed in a future version of Sass.
|
3082 |
+
# To include a newline in a string, use "\a" or "\a " as in CSS.
|
3083 |
+
if ($this->matchChar("\n", false)) {
|
3084 |
+
$content[] = ' ';
|
3085 |
+
}
|
3086 |
} elseif ($m[2] === '\\') {
|
3087 |
+
if (
|
3088 |
+
$this->literal("\r\n", 2, false) ||
|
|
|
|
|
|
|
|
|
|
|
3089 |
$this->matchChar("\r", false) ||
|
3090 |
$this->matchChar("\n", false) ||
|
3091 |
$this->matchChar("\f", false)
|
3092 |
) {
|
3093 |
// this is a continuation escaping, to be ignored
|
3094 |
+
} elseif ($this->matchEscapeCharacter($c)) {
|
3095 |
+
$content[] = $c;
|
3096 |
} else {
|
3097 |
+
throw $this->parseError('Unterminated escape sequence');
|
3098 |
}
|
3099 |
} else {
|
3100 |
$this->count -= \strlen($delim);
|
3105 |
$this->eatWhiteDefault = $oldWhite;
|
3106 |
|
3107 |
if ($this->literal($delim, \strlen($delim))) {
|
3108 |
+
if ($hasInterpolation && ! $keepDelimWithInterpolation) {
|
3109 |
$delim = '"';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3110 |
}
|
3111 |
|
3112 |
$out = [Type::T_STRING, $delim, $content];
|
3119 |
return false;
|
3120 |
}
|
3121 |
|
3122 |
+
/**
|
3123 |
+
* @param string $out
|
3124 |
+
* @param bool $inKeywords
|
3125 |
+
*
|
3126 |
+
* @return bool
|
3127 |
+
*/
|
3128 |
+
protected function matchEscapeCharacter(&$out, $inKeywords = false)
|
3129 |
+
{
|
3130 |
+
$s = $this->count;
|
3131 |
+
if ($this->match('[a-f0-9]', $m, false)) {
|
3132 |
+
$hex = $m[0];
|
3133 |
+
|
3134 |
+
for ($i = 5; $i--;) {
|
3135 |
+
if ($this->match('[a-f0-9]', $m, false)) {
|
3136 |
+
$hex .= $m[0];
|
3137 |
+
} else {
|
3138 |
+
break;
|
3139 |
+
}
|
3140 |
+
}
|
3141 |
+
|
3142 |
+
// CSS allows Unicode escape sequences to be followed by a delimiter space
|
3143 |
+
// (necessary in some cases for shorter sequences to disambiguate their end)
|
3144 |
+
$this->matchChar(' ', false);
|
3145 |
+
|
3146 |
+
$value = hexdec($hex);
|
3147 |
+
|
3148 |
+
if (!$inKeywords && ($value == 0 || ($value >= 0xD800 && $value <= 0xDFFF) || $value >= 0x10FFFF)) {
|
3149 |
+
$out = "\xEF\xBF\xBD"; // "\u{FFFD}" but with a syntax supported on PHP 5
|
3150 |
+
} elseif ($value < 0x20) {
|
3151 |
+
$out = Util::mbChr($value);
|
3152 |
+
} else {
|
3153 |
+
$out = Util::mbChr($value);
|
3154 |
+
}
|
3155 |
+
|
3156 |
+
return true;
|
3157 |
+
}
|
3158 |
+
|
3159 |
+
if ($this->match('.', $m, false)) {
|
3160 |
+
if ($inKeywords && in_array($m[0], ["'",'"','@','&',' ','\\',':','/','%'])) {
|
3161 |
+
$this->seek($s);
|
3162 |
+
return false;
|
3163 |
+
}
|
3164 |
+
$out = $m[0];
|
3165 |
+
|
3166 |
+
return true;
|
3167 |
+
}
|
3168 |
+
|
3169 |
+
return false;
|
3170 |
+
}
|
3171 |
+
|
3172 |
/**
|
3173 |
* Parse keyword or interpolation
|
3174 |
*
|
3175 |
+
* @param array $out
|
3176 |
+
* @param bool $restricted
|
3177 |
*
|
3178 |
+
* @return bool
|
3179 |
*/
|
3180 |
protected function mixedKeyword(&$out, $restricted = false)
|
3181 |
{
|
3216 |
/**
|
3217 |
* Parse an unbounded string stopped by $end
|
3218 |
*
|
3219 |
+
* @param string $end
|
3220 |
+
* @param array $out
|
3221 |
+
* @param string $nestOpen
|
3222 |
+
* @param string $nestClose
|
3223 |
+
* @param bool $rtrim
|
3224 |
+
* @param string $disallow
|
3225 |
*
|
3226 |
+
* @return bool
|
3227 |
*/
|
3228 |
+
protected function openString($end, &$out, $nestOpen = null, $nestClose = null, $rtrim = true, $disallow = null)
|
3229 |
{
|
3230 |
$oldWhite = $this->eatWhiteDefault;
|
3231 |
$this->eatWhiteDefault = false;
|
3232 |
|
3233 |
+
if ($nestOpen && ! $nestClose) {
|
3234 |
+
$nestClose = $end;
|
3235 |
}
|
3236 |
|
3237 |
+
$patt = ($disallow ? '[^' . $this->pregQuote($disallow) . ']' : '.');
|
3238 |
+
$patt = '(' . $patt . '*?)([\'"]|#\{|'
|
3239 |
. $this->pregQuote($end) . '|'
|
3240 |
+
. (($nestClose && $nestClose !== $end) ? $this->pregQuote($nestClose) . '|' : '')
|
3241 |
. static::$commentPattern . ')';
|
3242 |
|
3243 |
$nestingLevel = 0;
|
3248 |
if (isset($m[1]) && $m[1] !== '') {
|
3249 |
$content[] = $m[1];
|
3250 |
|
3251 |
+
if ($nestOpen) {
|
3252 |
+
$nestingLevel += substr_count($m[1], $nestOpen);
|
3253 |
}
|
3254 |
}
|
3255 |
|
3256 |
$tok = $m[2];
|
3257 |
|
3258 |
+
$this->count -= \strlen($tok);
|
3259 |
|
3260 |
if ($tok === $end && ! $nestingLevel) {
|
3261 |
break;
|
3262 |
}
|
3263 |
|
3264 |
+
if ($tok === $nestClose) {
|
3265 |
$nestingLevel--;
|
3266 |
}
|
3267 |
|
3268 |
+
if (($tok === "'" || $tok === '"') && $this->string($str, true)) {
|
3269 |
$content[] = $str;
|
3270 |
continue;
|
3271 |
}
|
3276 |
}
|
3277 |
|
3278 |
$content[] = $tok;
|
3279 |
+
$this->count += \strlen($tok);
|
3280 |
}
|
3281 |
|
3282 |
$this->eatWhiteDefault = $oldWhite;
|
3286 |
}
|
3287 |
|
3288 |
// trim the end
|
3289 |
+
if ($rtrim && \is_string(end($content))) {
|
3290 |
$content[\count($content) - 1] = rtrim(end($content));
|
3291 |
}
|
3292 |
|
3299 |
* Parser interpolation
|
3300 |
*
|
3301 |
* @param string|array $out
|
3302 |
+
* @param bool $lookWhite save information about whitespace before and after
|
3303 |
*
|
3304 |
+
* @return bool
|
3305 |
*/
|
3306 |
protected function interpolation(&$out, $lookWhite = true)
|
3307 |
{
|
3312 |
|
3313 |
$s = $this->count;
|
3314 |
|
3315 |
+
if (
|
3316 |
+
$this->literal('#{', 2) &&
|
3317 |
+
$this->valueList($value) &&
|
3318 |
+
$this->matchChar('}', false)
|
3319 |
+
) {
|
3320 |
if ($value === [Type::T_SELF]) {
|
3321 |
$out = $value;
|
3322 |
} else {
|
3323 |
if ($lookWhite) {
|
3324 |
$left = ($s > 0 && preg_match('/\s/', $this->buffer[$s - 1])) ? ' ' : '';
|
3325 |
+
$right = (
|
3326 |
+
! empty($this->buffer[$this->count]) &&
|
3327 |
+
preg_match('/\s/', $this->buffer[$this->count])
|
3328 |
+
) ? ' ' : '';
|
3329 |
} else {
|
3330 |
$left = $right = false;
|
3331 |
}
|
3356 |
*
|
3357 |
* @param array $out
|
3358 |
*
|
3359 |
+
* @return bool
|
3360 |
*/
|
3361 |
protected function propertyName(&$out)
|
3362 |
{
|
3392 |
}
|
3393 |
|
3394 |
// match comment hack
|
3395 |
+
if (preg_match(static::$whitePattern, $this->buffer, $m, 0, $this->count)) {
|
3396 |
if (! empty($m[0])) {
|
3397 |
$parts[] = $m[0];
|
3398 |
$this->count += \strlen($m[0]);
|
3411 |
*
|
3412 |
* @param array $out
|
3413 |
*
|
3414 |
+
* @return bool
|
3415 |
*/
|
3416 |
protected function customProperty(&$out)
|
3417 |
{
|
3432 |
continue;
|
3433 |
}
|
3434 |
|
3435 |
+
if ($this->matchChar('&', false)) {
|
3436 |
+
$parts[] = [Type::T_SELF];
|
3437 |
+
continue;
|
3438 |
+
}
|
3439 |
+
|
3440 |
if ($this->variable($var)) {
|
3441 |
$parts[] = $var;
|
3442 |
continue;
|
3468 |
/**
|
3469 |
* Parse comma separated selector list
|
3470 |
*
|
3471 |
+
* @param array $out
|
3472 |
+
* @param string|bool $subSelector
|
3473 |
*
|
3474 |
+
* @return bool
|
3475 |
*/
|
3476 |
protected function selectors(&$out, $subSelector = false)
|
3477 |
{
|
3504 |
/**
|
3505 |
* Parse whitespace separated selector list
|
3506 |
*
|
3507 |
+
* @param array $out
|
3508 |
+
* @param string|bool $subSelector
|
3509 |
*
|
3510 |
+
* @return bool
|
3511 |
*/
|
3512 |
protected function selector(&$out, $subSelector = false)
|
3513 |
{
|
3514 |
$selector = [];
|
3515 |
|
3516 |
+
$discardComments = $this->discardComments;
|
3517 |
+
$this->discardComments = true;
|
3518 |
+
|
3519 |
for (;;) {
|
3520 |
$s = $this->count;
|
3521 |
|
3522 |
if ($this->match('[>+~]+', $m, true)) {
|
3523 |
+
if (
|
3524 |
+
$subSelector && \is_string($subSelector) && strpos($subSelector, 'nth-') === 0 &&
|
3525 |
$m[0] === '+' && $this->match("(\d+|n\b)", $counter)
|
3526 |
) {
|
3527 |
$this->seek($s);
|
3533 |
|
3534 |
if ($this->selectorSingle($part, $subSelector)) {
|
3535 |
$selector[] = $part;
|
3536 |
+
$this->whitespace();
|
|
|
|
|
|
|
|
|
|
|
3537 |
continue;
|
3538 |
}
|
3539 |
|
3540 |
break;
|
3541 |
}
|
3542 |
|
3543 |
+
$this->discardComments = $discardComments;
|
3544 |
+
|
3545 |
if (! $selector) {
|
3546 |
return false;
|
3547 |
}
|
3551 |
return true;
|
3552 |
}
|
3553 |
|
3554 |
+
/**
|
3555 |
+
* parsing escaped chars in selectors:
|
3556 |
+
* - escaped single chars are kept escaped in the selector but in a normalized form
|
3557 |
+
* (if not in 0-9a-f range as this would be ambigous)
|
3558 |
+
* - other escaped sequences (multibyte chars or 0-9a-f) are kept in their initial escaped form,
|
3559 |
+
* normalized to lowercase
|
3560 |
+
*
|
3561 |
+
* TODO: this is a fallback solution. Ideally escaped chars in selectors should be encoded as the genuine chars,
|
3562 |
+
* and escaping added when printing in the Compiler, where/if it's mandatory
|
3563 |
+
* - but this require a better formal selector representation instead of the array we have now
|
3564 |
+
*
|
3565 |
+
* @param string $out
|
3566 |
+
* @param bool $keepEscapedNumber
|
3567 |
+
*
|
3568 |
+
* @return bool
|
3569 |
+
*/
|
3570 |
+
protected function matchEscapeCharacterInSelector(&$out, $keepEscapedNumber = false)
|
3571 |
+
{
|
3572 |
+
$s_escape = $this->count;
|
3573 |
+
if ($this->match('\\\\', $m)) {
|
3574 |
+
$out = '\\' . $m[0];
|
3575 |
+
return true;
|
3576 |
+
}
|
3577 |
+
|
3578 |
+
if ($this->matchEscapeCharacter($escapedout, true)) {
|
3579 |
+
if (strlen($escapedout) === 1) {
|
3580 |
+
if (!preg_match(",\w,", $escapedout)) {
|
3581 |
+
$out = '\\' . $escapedout;
|
3582 |
+
return true;
|
3583 |
+
} elseif (! $keepEscapedNumber || ! \is_numeric($escapedout)) {
|
3584 |
+
$out = $escapedout;
|
3585 |
+
return true;
|
3586 |
+
}
|
3587 |
+
}
|
3588 |
+
$escape_sequence = rtrim(substr($this->buffer, $s_escape, $this->count - $s_escape));
|
3589 |
+
if (strlen($escape_sequence) < 6) {
|
3590 |
+
$escape_sequence .= ' ';
|
3591 |
+
}
|
3592 |
+
$out = '\\' . strtolower($escape_sequence);
|
3593 |
+
return true;
|
3594 |
+
}
|
3595 |
+
if ($this->match('\\S', $m)) {
|
3596 |
+
$out = '\\' . $m[0];
|
3597 |
+
return true;
|
3598 |
+
}
|
3599 |
+
|
3600 |
+
|
3601 |
+
return false;
|
3602 |
+
}
|
3603 |
+
|
3604 |
/**
|
3605 |
* Parse the parts that make up a selector
|
3606 |
*
|
3608 |
* div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
|
3609 |
* }}
|
3610 |
*
|
3611 |
+
* @param array $out
|
3612 |
+
* @param string|bool $subSelector
|
3613 |
*
|
3614 |
+
* @return bool
|
3615 |
*/
|
3616 |
protected function selectorSingle(&$out, $subSelector = false)
|
3617 |
{
|
3647 |
case '&':
|
3648 |
$parts[] = Compiler::$selfSelector;
|
3649 |
$this->count++;
|
3650 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
3651 |
continue 2;
|
3652 |
|
3653 |
case '.':
|
3661 |
continue 2;
|
3662 |
}
|
3663 |
|
3664 |
+
// handling of escaping in selectors : get the escaped char
|
3665 |
+
if ($char === '\\') {
|
3666 |
+
$this->count++;
|
3667 |
+
if ($this->matchEscapeCharacterInSelector($escaped, true)) {
|
3668 |
+
$parts[] = $escaped;
|
3669 |
+
continue;
|
3670 |
+
}
|
3671 |
+
$this->count--;
|
3672 |
}
|
3673 |
|
3674 |
if ($char === '%') {
|
3677 |
if ($this->placeholder($placeholder)) {
|
3678 |
$parts[] = '%';
|
3679 |
$parts[] = $placeholder;
|
3680 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
3681 |
continue;
|
3682 |
}
|
3683 |
|
3687 |
if ($char === '#') {
|
3688 |
if ($this->interpolation($inter)) {
|
3689 |
$parts[] = $inter;
|
3690 |
+
! $this->cssOnly || $this->assertPlainCssValid(false, $s);
|
3691 |
continue;
|
3692 |
}
|
3693 |
|
3715 |
|
3716 |
$ss = $this->count;
|
3717 |
|
3718 |
+
if (
|
3719 |
+
$nameParts === ['not'] ||
|
3720 |
+
$nameParts === ['is'] ||
|
3721 |
+
$nameParts === ['has'] ||
|
3722 |
+
$nameParts === ['where'] ||
|
3723 |
$nameParts === ['slotted'] ||
|
3724 |
+
$nameParts === ['nth-child'] ||
|
3725 |
+
$nameParts === ['nth-last-child'] ||
|
3726 |
+
$nameParts === ['nth-of-type'] ||
|
3727 |
+
$nameParts === ['nth-last-of-type']
|
3728 |
) {
|
3729 |
+
if (
|
3730 |
+
$this->matchChar('(', true) &&
|
3731 |
($this->selectors($subs, reset($nameParts)) || true) &&
|
3732 |
$this->matchChar(')')
|
3733 |
) {
|
3753 |
} else {
|
3754 |
$this->seek($ss);
|
3755 |
}
|
3756 |
+
} elseif (
|
3757 |
+
$this->matchChar('(', true) &&
|
3758 |
+
($this->openString(')', $str, '(') || true) &&
|
3759 |
+
$this->matchChar(')')
|
3760 |
+
) {
|
3761 |
+
$parts[] = '(';
|
|
|
|
|
|
|
|
|
3762 |
|
3763 |
+
if (! empty($str)) {
|
3764 |
+
$parts[] = $str;
|
|
|
3765 |
}
|
3766 |
+
|
3767 |
+
$parts[] = ')';
|
3768 |
+
} else {
|
3769 |
+
$this->seek($ss);
|
3770 |
}
|
3771 |
|
3772 |
continue;
|
3787 |
$this->seek($s);
|
3788 |
|
3789 |
// attribute selector
|
3790 |
+
if (
|
3791 |
+
$char === '[' &&
|
3792 |
$this->matchChar('[') &&
|
3793 |
($this->openString(']', $str, '[') || true) &&
|
3794 |
$this->matchChar(']')
|
3811 |
continue;
|
3812 |
}
|
3813 |
|
3814 |
+
if ($this->restrictedKeyword($name, false, true)) {
|
3815 |
$parts[] = $name;
|
3816 |
continue;
|
3817 |
}
|
3835 |
*
|
3836 |
* @param array $out
|
3837 |
*
|
3838 |
+
* @return bool
|
3839 |
*/
|
3840 |
protected function variable(&$out)
|
3841 |
{
|
3842 |
$s = $this->count;
|
3843 |
|
3844 |
+
if (
|
3845 |
+
$this->matchChar('$', false) &&
|
3846 |
+
$this->keyword($name)
|
3847 |
+
) {
|
3848 |
if ($this->allowVars) {
|
3849 |
$out = [Type::T_VARIABLE, $name];
|
3850 |
} else {
|
3862 |
/**
|
3863 |
* Parse a keyword
|
3864 |
*
|
3865 |
+
* @param string $word
|
3866 |
+
* @param bool $eatWhitespace
|
3867 |
+
* @param bool $inSelector
|
3868 |
*
|
3869 |
+
* @return bool
|
3870 |
*/
|
3871 |
+
protected function keyword(&$word, $eatWhitespace = null, $inSelector = false)
|
3872 |
{
|
3873 |
+
$s = $this->count;
|
3874 |
+
$match = $this->match(
|
3875 |
$this->utf8
|
3876 |
+
? '(([\pL\w\x{00A0}-\x{10FFFF}_\-\*!"\']|\\\\[a-f0-9]{6} ?|\\\\[a-f0-9]{1,5}(?![a-f0-9]) ?|[\\\\].)([\pL\w\x{00A0}-\x{10FFFF}\-_"\']|\\\\[a-f0-9]{6} ?|\\\\[a-f0-9]{1,5}(?![a-f0-9]) ?|[\\\\].)*)'
|
3877 |
+
: '(([\w_\-\*!"\']|\\\\[a-f0-9]{6} ?|\\\\[a-f0-9]{1,5}(?![a-f0-9]) ?|[\\\\].)([\w\-_"\']|\\\\[a-f0-9]{6} ?|\\\\[a-f0-9]{1,5}(?![a-f0-9]) ?|[\\\\].)*)',
|
3878 |
$m,
|
3879 |
+
false
|
3880 |
+
);
|
3881 |
+
|
3882 |
+
if ($match) {
|
3883 |
$word = $m[1];
|
3884 |
|
3885 |
+
// handling of escaping in keyword : get the escaped char
|
3886 |
+
if (strpos($word, '\\') !== false) {
|
3887 |
+
$send = $this->count;
|
3888 |
+
$escapedWord = [];
|
3889 |
+
$this->seek($s);
|
3890 |
+
$previousEscape = false;
|
3891 |
+
while ($this->count < $send) {
|
3892 |
+
$char = $this->buffer[$this->count];
|
3893 |
+
$this->count++;
|
3894 |
+
if (
|
3895 |
+
$this->count < $send
|
3896 |
+
&& $char === '\\'
|
3897 |
+
&& !$previousEscape
|
3898 |
+
&& (
|
3899 |
+
$inSelector ?
|
3900 |
+
$this->matchEscapeCharacterInSelector($out)
|
3901 |
+
:
|
3902 |
+
$this->matchEscapeCharacter($out, true)
|
3903 |
+
)
|
3904 |
+
) {
|
3905 |
+
$escapedWord[] = $out;
|
3906 |
+
} else {
|
3907 |
+
if ($previousEscape) {
|
3908 |
+
$previousEscape = false;
|
3909 |
+
} elseif ($char === '\\') {
|
3910 |
+
$previousEscape = true;
|
3911 |
+
}
|
3912 |
+
$escapedWord[] = $char;
|
3913 |
+
}
|
3914 |
+
}
|
3915 |
+
|
3916 |
+
$word = implode('', $escapedWord);
|
3917 |
+
}
|
3918 |
+
|
3919 |
+
if (is_null($eatWhitespace) ? $this->eatWhiteDefault : $eatWhitespace) {
|
3920 |
+
$this->whitespace();
|
3921 |
+
}
|
3922 |
+
|
3923 |
return true;
|
3924 |
}
|
3925 |
|
3929 |
/**
|
3930 |
* Parse a keyword that should not start with a number
|
3931 |
*
|
3932 |
+
* @param string $word
|
3933 |
+
* @param bool $eatWhitespace
|
3934 |
+
* @param bool $inSelector
|
3935 |
*
|
3936 |
+
* @return bool
|
3937 |
*/
|
3938 |
+
protected function restrictedKeyword(&$word, $eatWhitespace = null, $inSelector = false)
|
3939 |
{
|
3940 |
$s = $this->count;
|
3941 |
|
3942 |
+
if ($this->keyword($word, $eatWhitespace, $inSelector) && (\ord($word[0]) > 57 || \ord($word[0]) < 48)) {
|
3943 |
return true;
|
3944 |
}
|
3945 |
|
3953 |
*
|
3954 |
* @param string|array $placeholder
|
3955 |
*
|
3956 |
+
* @return bool
|
3957 |
*/
|
3958 |
protected function placeholder(&$placeholder)
|
3959 |
{
|
3960 |
+
$match = $this->match(
|
3961 |
$this->utf8
|
3962 |
? '([\pL\w\-_]+)'
|
3963 |
: '([\w\-_]+)',
|
3964 |
$m
|
3965 |
+
);
|
3966 |
+
|
3967 |
+
if ($match) {
|
3968 |
$placeholder = $m[1];
|
3969 |
|
3970 |
return true;
|
3982 |
*
|
3983 |
* @param array $out
|
3984 |
*
|
3985 |
+
* @return bool
|
3986 |
*/
|
3987 |
protected function url(&$out)
|
3988 |
{
|
3989 |
+
if ($this->literal('url(', 4)) {
|
3990 |
+
$s = $this->count;
|
3991 |
|
3992 |
+
if (
|
3993 |
+
($this->string($out) || $this->spaceList($out)) &&
|
3994 |
+
$this->matchChar(')')
|
3995 |
+
) {
|
3996 |
+
$out = [Type::T_STRING, '', ['url(', $out, ')']];
|
3997 |
+
|
3998 |
+
return true;
|
3999 |
+
}
|
4000 |
+
|
4001 |
+
$this->seek($s);
|
4002 |
+
|
4003 |
+
if (
|
4004 |
+
$this->openString(')', $out) &&
|
4005 |
+
$this->matchChar(')')
|
4006 |
+
) {
|
4007 |
+
$out = [Type::T_STRING, '', ['url(', $out, ')']];
|
4008 |
+
|
4009 |
+
return true;
|
4010 |
+
}
|
4011 |
}
|
4012 |
|
4013 |
return false;
|
4015 |
|
4016 |
/**
|
4017 |
* Consume an end of statement delimiter
|
4018 |
+
* @param bool $eatWhitespace
|
4019 |
*
|
4020 |
+
* @return bool
|
4021 |
*/
|
4022 |
+
protected function end($eatWhitespace = null)
|
4023 |
{
|
4024 |
+
if ($this->matchChar(';', $eatWhitespace)) {
|
4025 |
return true;
|
4026 |
}
|
4027 |
|
4038 |
*
|
4039 |
* @param array $value
|
4040 |
*
|
4041 |
+
* @return string[]
|
4042 |
*/
|
4043 |
protected function stripAssignmentFlags(&$value)
|
4044 |
{
|
4065 |
*
|
4066 |
* @param array $selectors
|
4067 |
*
|
4068 |
+
* @return bool
|
4069 |
*/
|
4070 |
protected function stripOptionalFlag(&$selectors)
|
4071 |
{
|
4114 |
* Extract line numbers from buffer
|
4115 |
*
|
4116 |
* @param string $buffer
|
4117 |
+
*
|
4118 |
+
* @return void
|
4119 |
*/
|
4120 |
private function extractLineNumbers($buffer)
|
4121 |
{
|
4137 |
/**
|
4138 |
* Get source line number and column (given character position in the buffer)
|
4139 |
*
|
4140 |
+
* @param int $pos
|
4141 |
*
|
4142 |
* @return array
|
4143 |
+
* @phpstan-return array{int, int}
|
4144 |
*/
|
4145 |
private function getSourcePosition($pos)
|
4146 |
{
|
4167 |
}
|
4168 |
|
4169 |
/**
|
4170 |
+
* Save internal encoding of mbstring
|
4171 |
+
*
|
4172 |
+
* When mbstring.func_overload is used to replace the standard PHP string functions,
|
4173 |
+
* this method configures the internal encoding to a single-byte one so that the
|
4174 |
+
* behavior matches the normal behavior of PHP string functions while using the parser.
|
4175 |
+
* The existing internal encoding is saved and will be restored when calling {@see restoreEncoding}.
|
4176 |
+
*
|
4177 |
+
* If mbstring.func_overload is not used (or does not override string functions), this method is a no-op.
|
4178 |
+
*
|
4179 |
+
* @return void
|
4180 |
*/
|
4181 |
private function saveEncoding()
|
4182 |
{
|
4183 |
+
if (\PHP_VERSION_ID < 80000 && \extension_loaded('mbstring') && (2 & (int) ini_get('mbstring.func_overload')) > 0) {
|
4184 |
$this->encoding = mb_internal_encoding();
|
4185 |
|
4186 |
mb_internal_encoding('iso-8859-1');
|
4189 |
|
4190 |
/**
|
4191 |
* Restore internal encoding
|
4192 |
+
*
|
4193 |
+
* @return void
|
4194 |
*/
|
4195 |
private function restoreEncoding()
|
4196 |
{
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/Base64.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -15,11 +16,13 @@ namespace ScssPhp\ScssPhp\SourceMap;
|
|
15 |
* Base 64 Encode/Decode
|
16 |
*
|
17 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
18 |
*/
|
19 |
class Base64
|
20 |
{
|
21 |
/**
|
22 |
-
* @var array
|
23 |
*/
|
24 |
private static $encodingMap = [
|
25 |
0 => 'A',
|
@@ -89,7 +92,7 @@ class Base64
|
|
89 |
];
|
90 |
|
91 |
/**
|
92 |
-
* @var array
|
93 |
*/
|
94 |
private static $decodingMap = [
|
95 |
'A' => 0,
|
@@ -161,7 +164,7 @@ class Base64
|
|
161 |
/**
|
162 |
* Convert to base64
|
163 |
*
|
164 |
-
* @param
|
165 |
*
|
166 |
* @return string
|
167 |
*/
|
@@ -175,7 +178,7 @@ class Base64
|
|
175 |
*
|
176 |
* @param string $value
|
177 |
*
|
178 |
-
* @return
|
179 |
*/
|
180 |
public static function decode($value)
|
181 |
{
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
16 |
* Base 64 Encode/Decode
|
17 |
*
|
18 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
19 |
+
*
|
20 |
+
* @internal
|
21 |
*/
|
22 |
class Base64
|
23 |
{
|
24 |
/**
|
25 |
+
* @var array<int, string>
|
26 |
*/
|
27 |
private static $encodingMap = [
|
28 |
0 => 'A',
|
92 |
];
|
93 |
|
94 |
/**
|
95 |
+
* @var array<string|int, int>
|
96 |
*/
|
97 |
private static $decodingMap = [
|
98 |
'A' => 0,
|
164 |
/**
|
165 |
* Convert to base64
|
166 |
*
|
167 |
+
* @param int $value
|
168 |
*
|
169 |
* @return string
|
170 |
*/
|
178 |
*
|
179 |
* @param string $value
|
180 |
*
|
181 |
+
* @return int
|
182 |
*/
|
183 |
public static function decode($value)
|
184 |
{
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/Base64VLQ.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -11,8 +12,6 @@
|
|
11 |
|
12 |
namespace ScssPhp\ScssPhp\SourceMap;
|
13 |
|
14 |
-
use ScssPhp\ScssPhp\SourceMap\Base64;
|
15 |
-
|
16 |
/**
|
17 |
* Base 64 VLQ
|
18 |
*
|
@@ -35,6 +34,8 @@ use ScssPhp\ScssPhp\SourceMap\Base64;
|
|
35 |
*
|
36 |
* @author John Lenz <johnlenz@google.com>
|
37 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
38 |
*/
|
39 |
class Base64VLQ
|
40 |
{
|
@@ -50,7 +51,7 @@ class Base64VLQ
|
|
50 |
/**
|
51 |
* Returns the VLQ encoded value.
|
52 |
*
|
53 |
-
* @param
|
54 |
*
|
55 |
* @return string
|
56 |
*/
|
@@ -61,7 +62,9 @@ class Base64VLQ
|
|
61 |
|
62 |
do {
|
63 |
$digit = $vlq & self::VLQ_BASE_MASK;
|
64 |
-
|
|
|
|
|
65 |
|
66 |
if ($vlq > 0) {
|
67 |
$digit |= self::VLQ_CONTINUATION_BIT;
|
@@ -77,9 +80,9 @@ class Base64VLQ
|
|
77 |
* Decodes VLQValue.
|
78 |
*
|
79 |
* @param string $str
|
80 |
-
* @param
|
81 |
*
|
82 |
-
* @return
|
83 |
*/
|
84 |
public static function decode($str, &$index)
|
85 |
{
|
@@ -104,9 +107,9 @@ class Base64VLQ
|
|
104 |
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
|
105 |
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
|
106 |
*
|
107 |
-
* @param
|
108 |
*
|
109 |
-
* @return
|
110 |
*/
|
111 |
private static function toVLQSigned($value)
|
112 |
{
|
@@ -123,14 +126,16 @@ class Base64VLQ
|
|
123 |
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
|
124 |
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
|
125 |
*
|
126 |
-
* @param
|
127 |
*
|
128 |
-
* @return
|
129 |
*/
|
130 |
private static function fromVLQSigned($value)
|
131 |
{
|
132 |
$negate = ($value & 1) === 1;
|
133 |
-
|
|
|
|
|
134 |
|
135 |
if (! $negate) {
|
136 |
return $value;
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
12 |
|
13 |
namespace ScssPhp\ScssPhp\SourceMap;
|
14 |
|
|
|
|
|
15 |
/**
|
16 |
* Base 64 VLQ
|
17 |
*
|
34 |
*
|
35 |
* @author John Lenz <johnlenz@google.com>
|
36 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
37 |
+
*
|
38 |
+
* @internal
|
39 |
*/
|
40 |
class Base64VLQ
|
41 |
{
|
51 |
/**
|
52 |
* Returns the VLQ encoded value.
|
53 |
*
|
54 |
+
* @param int $value
|
55 |
*
|
56 |
* @return string
|
57 |
*/
|
62 |
|
63 |
do {
|
64 |
$digit = $vlq & self::VLQ_BASE_MASK;
|
65 |
+
|
66 |
+
//$vlq >>>= self::VLQ_BASE_SHIFT; // unsigned right shift
|
67 |
+
$vlq = (($vlq >> 1) & PHP_INT_MAX) >> (self::VLQ_BASE_SHIFT - 1);
|
68 |
|
69 |
if ($vlq > 0) {
|
70 |
$digit |= self::VLQ_CONTINUATION_BIT;
|
80 |
* Decodes VLQValue.
|
81 |
*
|
82 |
* @param string $str
|
83 |
+
* @param int $index
|
84 |
*
|
85 |
+
* @return int
|
86 |
*/
|
87 |
public static function decode($str, &$index)
|
88 |
{
|
107 |
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
|
108 |
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
|
109 |
*
|
110 |
+
* @param int $value
|
111 |
*
|
112 |
+
* @return int
|
113 |
*/
|
114 |
private static function toVLQSigned($value)
|
115 |
{
|
126 |
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
|
127 |
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
|
128 |
*
|
129 |
+
* @param int $value
|
130 |
*
|
131 |
+
* @return int
|
132 |
*/
|
133 |
private static function fromVLQSigned($value)
|
134 |
{
|
135 |
$negate = ($value & 1) === 1;
|
136 |
+
|
137 |
+
//$value >>>= 1; // unsigned right shift
|
138 |
+
$value = ($value >> 1) & PHP_INT_MAX;
|
139 |
|
140 |
if (! $negate) {
|
141 |
return $value;
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/SourceMap/SourceMapGenerator.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -20,6 +21,8 @@ use ScssPhp\ScssPhp\Exception\CompilerException;
|
|
20 |
*
|
21 |
* @author Josh Schmidt <oyejorge@gmail.com>
|
22 |
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
|
|
|
|
|
23 |
*/
|
24 |
class SourceMapGenerator
|
25 |
{
|
@@ -32,6 +35,7 @@ class SourceMapGenerator
|
|
32 |
* Array of default options
|
33 |
*
|
34 |
* @var array
|
|
|
35 |
*/
|
36 |
protected $defaultOptions = [
|
37 |
// an optional source root, useful for relocating source files
|
@@ -69,6 +73,7 @@ class SourceMapGenerator
|
|
69 |
* Array of mappings
|
70 |
*
|
71 |
* @var array
|
|
|
72 |
*/
|
73 |
protected $mappings = [];
|
74 |
|
@@ -82,30 +87,40 @@ class SourceMapGenerator
|
|
82 |
/**
|
83 |
* File to content map
|
84 |
*
|
85 |
-
* @var array
|
86 |
*/
|
87 |
protected $sources = [];
|
|
|
|
|
|
|
|
|
88 |
protected $sourceKeys = [];
|
89 |
|
90 |
/**
|
91 |
* @var array
|
|
|
92 |
*/
|
93 |
private $options;
|
94 |
|
|
|
|
|
|
|
95 |
public function __construct(array $options = [])
|
96 |
{
|
97 |
-
$this->options =
|
98 |
$this->encoder = new Base64VLQ();
|
99 |
}
|
100 |
|
101 |
/**
|
102 |
* Adds a mapping
|
103 |
*
|
104 |
-
* @param
|
105 |
-
* @param
|
106 |
-
* @param
|
107 |
-
* @param
|
108 |
-
* @param string
|
|
|
|
|
109 |
*/
|
110 |
public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
|
111 |
{
|
@@ -125,13 +140,15 @@ class SourceMapGenerator
|
|
125 |
*
|
126 |
* @param string $content The content to write
|
127 |
*
|
128 |
-
* @return string
|
129 |
*
|
130 |
* @throws \ScssPhp\ScssPhp\Exception\CompilerException If the file could not be saved
|
|
|
131 |
*/
|
132 |
public function saveMap($content)
|
133 |
{
|
134 |
$file = $this->options['sourceMapWriteTo'];
|
|
|
135 |
$dir = \dirname($file);
|
136 |
|
137 |
// directory does not exist
|
@@ -153,14 +170,16 @@ class SourceMapGenerator
|
|
153 |
/**
|
154 |
* Generates the JSON source map
|
155 |
*
|
|
|
|
|
156 |
* @return string
|
157 |
*
|
158 |
* @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
|
159 |
*/
|
160 |
-
public function generateJson()
|
161 |
{
|
162 |
$sourceMap = [];
|
163 |
-
$mappings = $this->generateMappings();
|
164 |
|
165 |
// File version (always the first entry in the object) and must be a positive integer.
|
166 |
$sourceMap['version'] = self::VERSION;
|
@@ -183,7 +202,7 @@ class SourceMapGenerator
|
|
183 |
// A list of original sources used by the 'mappings' entry.
|
184 |
$sourceMap['sources'] = [];
|
185 |
|
186 |
-
foreach ($this->sources as $
|
187 |
$sourceMap['sources'][] = $this->normalizeFilename($sourceFilename);
|
188 |
}
|
189 |
|
@@ -205,13 +224,21 @@ class SourceMapGenerator
|
|
205 |
unset($sourceMap['sourceRoot']);
|
206 |
}
|
207 |
|
208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
}
|
210 |
|
211 |
/**
|
212 |
* Returns the sources contents
|
213 |
*
|
214 |
-
* @return
|
215 |
*/
|
216 |
protected function getSourcesContent()
|
217 |
{
|
@@ -231,14 +258,21 @@ class SourceMapGenerator
|
|
231 |
/**
|
232 |
* Generates the mappings string
|
233 |
*
|
|
|
|
|
234 |
* @return string
|
235 |
*/
|
236 |
-
public function generateMappings()
|
237 |
{
|
238 |
if (! \count($this->mappings)) {
|
239 |
return '';
|
240 |
}
|
241 |
|
|
|
|
|
|
|
|
|
|
|
242 |
$this->sourceKeys = array_flip(array_keys($this->sources));
|
243 |
|
244 |
// group mappings by generated line number.
|
@@ -253,6 +287,12 @@ class SourceMapGenerator
|
|
253 |
$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
|
254 |
|
255 |
foreach ($groupedMap as $lineNumber => $lineMap) {
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
while (++$lastGeneratedLine < $lineNumber) {
|
257 |
$groupedMapEncoded[] = ';';
|
258 |
}
|
@@ -261,8 +301,10 @@ class SourceMapGenerator
|
|
261 |
$lastGeneratedColumn = 0;
|
262 |
|
263 |
foreach ($lineMap as $m) {
|
264 |
-
$
|
265 |
-
|
|
|
|
|
266 |
|
267 |
// find the index
|
268 |
if ($m['source_file']) {
|
@@ -293,7 +335,7 @@ class SourceMapGenerator
|
|
293 |
*
|
294 |
* @param string $filename
|
295 |
*
|
296 |
-
* @return
|
297 |
*/
|
298 |
protected function findFileIndex($filename)
|
299 |
{
|
@@ -329,8 +371,8 @@ class SourceMapGenerator
|
|
329 |
/**
|
330 |
* Fix windows paths
|
331 |
*
|
332 |
-
* @param string
|
333 |
-
* @param
|
334 |
*
|
335 |
* @return string
|
336 |
*/
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
21 |
*
|
22 |
* @author Josh Schmidt <oyejorge@gmail.com>
|
23 |
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
|
24 |
+
*
|
25 |
+
* @internal
|
26 |
*/
|
27 |
class SourceMapGenerator
|
28 |
{
|
35 |
* Array of default options
|
36 |
*
|
37 |
* @var array
|
38 |
+
* @phpstan-var array{sourceRoot: string, sourceMapFilename: string|null, sourceMapURL: string|null, sourceMapWriteTo: string|null, outputSourceFiles: bool, sourceMapRootpath: string, sourceMapBasepath: string}
|
39 |
*/
|
40 |
protected $defaultOptions = [
|
41 |
// an optional source root, useful for relocating source files
|
73 |
* Array of mappings
|
74 |
*
|
75 |
* @var array
|
76 |
+
* @phpstan-var list<array{generated_line: int, generated_column: int, original_line: int, original_column: int, source_file: string}>
|
77 |
*/
|
78 |
protected $mappings = [];
|
79 |
|
87 |
/**
|
88 |
* File to content map
|
89 |
*
|
90 |
+
* @var array<string, string>
|
91 |
*/
|
92 |
protected $sources = [];
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @var array<string, int>
|
96 |
+
*/
|
97 |
protected $sourceKeys = [];
|
98 |
|
99 |
/**
|
100 |
* @var array
|
101 |
+
* @phpstan-var array{sourceRoot: string, sourceMapFilename: string|null, sourceMapURL: string|null, sourceMapWriteTo: string|null, outputSourceFiles: bool, sourceMapRootpath: string, sourceMapBasepath: string}
|
102 |
*/
|
103 |
private $options;
|
104 |
|
105 |
+
/**
|
106 |
+
* @phpstan-param array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string} $options
|
107 |
+
*/
|
108 |
public function __construct(array $options = [])
|
109 |
{
|
110 |
+
$this->options = array_replace($this->defaultOptions, $options);
|
111 |
$this->encoder = new Base64VLQ();
|
112 |
}
|
113 |
|
114 |
/**
|
115 |
* Adds a mapping
|
116 |
*
|
117 |
+
* @param int $generatedLine The line number in generated file
|
118 |
+
* @param int $generatedColumn The column number in generated file
|
119 |
+
* @param int $originalLine The line number in original file
|
120 |
+
* @param int $originalColumn The column number in original file
|
121 |
+
* @param string $sourceFile The original source file
|
122 |
+
*
|
123 |
+
* @return void
|
124 |
*/
|
125 |
public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
|
126 |
{
|
140 |
*
|
141 |
* @param string $content The content to write
|
142 |
*
|
143 |
+
* @return string|null
|
144 |
*
|
145 |
* @throws \ScssPhp\ScssPhp\Exception\CompilerException If the file could not be saved
|
146 |
+
* @deprecated
|
147 |
*/
|
148 |
public function saveMap($content)
|
149 |
{
|
150 |
$file = $this->options['sourceMapWriteTo'];
|
151 |
+
assert($file !== null);
|
152 |
$dir = \dirname($file);
|
153 |
|
154 |
// directory does not exist
|
170 |
/**
|
171 |
* Generates the JSON source map
|
172 |
*
|
173 |
+
* @param string $prefix A prefix added in the output file, which needs to shift mappings
|
174 |
+
*
|
175 |
* @return string
|
176 |
*
|
177 |
* @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
|
178 |
*/
|
179 |
+
public function generateJson($prefix = '')
|
180 |
{
|
181 |
$sourceMap = [];
|
182 |
+
$mappings = $this->generateMappings($prefix);
|
183 |
|
184 |
// File version (always the first entry in the object) and must be a positive integer.
|
185 |
$sourceMap['version'] = self::VERSION;
|
202 |
// A list of original sources used by the 'mappings' entry.
|
203 |
$sourceMap['sources'] = [];
|
204 |
|
205 |
+
foreach ($this->sources as $sourceFilename) {
|
206 |
$sourceMap['sources'][] = $this->normalizeFilename($sourceFilename);
|
207 |
}
|
208 |
|
224 |
unset($sourceMap['sourceRoot']);
|
225 |
}
|
226 |
|
227 |
+
$jsonSourceMap = json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
|
228 |
+
|
229 |
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
230 |
+
throw new \RuntimeException(json_last_error_msg());
|
231 |
+
}
|
232 |
+
|
233 |
+
assert($jsonSourceMap !== false);
|
234 |
+
|
235 |
+
return $jsonSourceMap;
|
236 |
}
|
237 |
|
238 |
/**
|
239 |
* Returns the sources contents
|
240 |
*
|
241 |
+
* @return string[]|null
|
242 |
*/
|
243 |
protected function getSourcesContent()
|
244 |
{
|
258 |
/**
|
259 |
* Generates the mappings string
|
260 |
*
|
261 |
+
* @param string $prefix A prefix added in the output file, which needs to shift mappings
|
262 |
+
*
|
263 |
* @return string
|
264 |
*/
|
265 |
+
public function generateMappings($prefix = '')
|
266 |
{
|
267 |
if (! \count($this->mappings)) {
|
268 |
return '';
|
269 |
}
|
270 |
|
271 |
+
$prefixLines = substr_count($prefix, "\n");
|
272 |
+
$lastPrefixNewLine = strrpos($prefix, "\n");
|
273 |
+
$lastPrefixLineStart = false === $lastPrefixNewLine ? 0 : $lastPrefixNewLine + 1;
|
274 |
+
$prefixColumn = strlen($prefix) - $lastPrefixLineStart;
|
275 |
+
|
276 |
$this->sourceKeys = array_flip(array_keys($this->sources));
|
277 |
|
278 |
// group mappings by generated line number.
|
287 |
$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
|
288 |
|
289 |
foreach ($groupedMap as $lineNumber => $lineMap) {
|
290 |
+
if ($lineNumber > 1) {
|
291 |
+
// The prefix only impacts the column for the first line of the original output
|
292 |
+
$prefixColumn = 0;
|
293 |
+
}
|
294 |
+
$lineNumber += $prefixLines;
|
295 |
+
|
296 |
while (++$lastGeneratedLine < $lineNumber) {
|
297 |
$groupedMapEncoded[] = ';';
|
298 |
}
|
301 |
$lastGeneratedColumn = 0;
|
302 |
|
303 |
foreach ($lineMap as $m) {
|
304 |
+
$generatedColumn = $m['generated_column'] + $prefixColumn;
|
305 |
+
|
306 |
+
$mapEncoded = $this->encoder->encode($generatedColumn - $lastGeneratedColumn);
|
307 |
+
$lastGeneratedColumn = $generatedColumn;
|
308 |
|
309 |
// find the index
|
310 |
if ($m['source_file']) {
|
335 |
*
|
336 |
* @param string $filename
|
337 |
*
|
338 |
+
* @return int|false
|
339 |
*/
|
340 |
protected function findFileIndex($filename)
|
341 |
{
|
371 |
/**
|
372 |
* Fix windows paths
|
373 |
*
|
374 |
+
* @param string $path
|
375 |
+
* @param bool $addEndSlash
|
376 |
*
|
377 |
* @return string
|
378 |
*/
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Type.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -18,53 +19,190 @@ namespace ScssPhp\ScssPhp;
|
|
18 |
*/
|
19 |
class Type
|
20 |
{
|
|
|
|
|
|
|
21 |
const T_ASSIGN = 'assign';
|
|
|
|
|
|
|
22 |
const T_AT_ROOT = 'at-root';
|
|
|
|
|
|
|
23 |
const T_BLOCK = 'block';
|
|
|
|
|
|
|
|
|
24 |
const T_BREAK = 'break';
|
|
|
|
|
|
|
25 |
const T_CHARSET = 'charset';
|
26 |
const T_COLOR = 'color';
|
|
|
|
|
|
|
27 |
const T_COMMENT = 'comment';
|
|
|
|
|
|
|
|
|
28 |
const T_CONTINUE = 'continue';
|
|
|
|
|
|
|
|
|
29 |
const T_CONTROL = 'control';
|
|
|
|
|
|
|
30 |
const T_CUSTOM_PROPERTY = 'custom';
|
|
|
|
|
|
|
31 |
const T_DEBUG = 'debug';
|
|
|
|
|
|
|
32 |
const T_DIRECTIVE = 'directive';
|
|
|
|
|
|
|
33 |
const T_EACH = 'each';
|
|
|
|
|
|
|
34 |
const T_ELSE = 'else';
|
|
|
|
|
|
|
35 |
const T_ELSEIF = 'elseif';
|
|
|
|
|
|
|
36 |
const T_ERROR = 'error';
|
|
|
|
|
|
|
37 |
const T_EXPRESSION = 'exp';
|
|
|
|
|
|
|
38 |
const T_EXTEND = 'extend';
|
|
|
|
|
|
|
39 |
const T_FOR = 'for';
|
40 |
const T_FUNCTION = 'function';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
const T_FUNCTION_CALL = 'fncall';
|
|
|
|
|
|
|
42 |
const T_HSL = 'hsl';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
const T_IF = 'if';
|
|
|
|
|
|
|
44 |
const T_IMPORT = 'import';
|
|
|
|
|
|
|
45 |
const T_INCLUDE = 'include';
|
|
|
|
|
|
|
46 |
const T_INTERPOLATE = 'interpolate';
|
|
|
|
|
|
|
47 |
const T_INTERPOLATED = 'interpolated';
|
|
|
|
|
|
|
48 |
const T_KEYWORD = 'keyword';
|
49 |
const T_LIST = 'list';
|
50 |
const T_MAP = 'map';
|
|
|
|
|
|
|
51 |
const T_MEDIA = 'media';
|
|
|
|
|
|
|
52 |
const T_MEDIA_EXPRESSION = 'mediaExp';
|
|
|
|
|
|
|
53 |
const T_MEDIA_TYPE = 'mediaType';
|
|
|
|
|
|
|
54 |
const T_MEDIA_VALUE = 'mediaValue';
|
|
|
|
|
|
|
55 |
const T_MIXIN = 'mixin';
|
|
|
|
|
|
|
56 |
const T_MIXIN_CONTENT = 'mixin_content';
|
|
|
|
|
|
|
57 |
const T_NESTED_PROPERTY = 'nestedprop';
|
|
|
|
|
|
|
58 |
const T_NOT = 'not';
|
59 |
const T_NULL = 'null';
|
60 |
const T_NUMBER = 'number';
|
|
|
|
|
|
|
61 |
const T_RETURN = 'return';
|
|
|
|
|
|
|
62 |
const T_ROOT = 'root';
|
|
|
|
|
|
|
63 |
const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
|
|
|
|
|
|
|
64 |
const T_SELF = 'self';
|
65 |
const T_STRING = 'string';
|
|
|
|
|
|
|
66 |
const T_UNARY = 'unary';
|
|
|
|
|
|
|
67 |
const T_VARIABLE = 'var';
|
|
|
|
|
|
|
68 |
const T_WARN = 'warn';
|
|
|
|
|
|
|
69 |
const T_WHILE = 'while';
|
70 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
19 |
*/
|
20 |
class Type
|
21 |
{
|
22 |
+
/**
|
23 |
+
* @internal
|
24 |
+
*/
|
25 |
const T_ASSIGN = 'assign';
|
26 |
+
/**
|
27 |
+
* @internal
|
28 |
+
*/
|
29 |
const T_AT_ROOT = 'at-root';
|
30 |
+
/**
|
31 |
+
* @internal
|
32 |
+
*/
|
33 |
const T_BLOCK = 'block';
|
34 |
+
/**
|
35 |
+
* @deprecated
|
36 |
+
* @internal
|
37 |
+
*/
|
38 |
const T_BREAK = 'break';
|
39 |
+
/**
|
40 |
+
* @internal
|
41 |
+
*/
|
42 |
const T_CHARSET = 'charset';
|
43 |
const T_COLOR = 'color';
|
44 |
+
/**
|
45 |
+
* @internal
|
46 |
+
*/
|
47 |
const T_COMMENT = 'comment';
|
48 |
+
/**
|
49 |
+
* @deprecated
|
50 |
+
* @internal
|
51 |
+
*/
|
52 |
const T_CONTINUE = 'continue';
|
53 |
+
/**
|
54 |
+
* @deprecated
|
55 |
+
* @internal
|
56 |
+
*/
|
57 |
const T_CONTROL = 'control';
|
58 |
+
/**
|
59 |
+
* @internal
|
60 |
+
*/
|
61 |
const T_CUSTOM_PROPERTY = 'custom';
|
62 |
+
/**
|
63 |
+
* @internal
|
64 |
+
*/
|
65 |
const T_DEBUG = 'debug';
|
66 |
+
/**
|
67 |
+
* @internal
|
68 |
+
*/
|
69 |
const T_DIRECTIVE = 'directive';
|
70 |
+
/**
|
71 |
+
* @internal
|
72 |
+
*/
|
73 |
const T_EACH = 'each';
|
74 |
+
/**
|
75 |
+
* @internal
|
76 |
+
*/
|
77 |
const T_ELSE = 'else';
|
78 |
+
/**
|
79 |
+
* @internal
|
80 |
+
*/
|
81 |
const T_ELSEIF = 'elseif';
|
82 |
+
/**
|
83 |
+
* @internal
|
84 |
+
*/
|
85 |
const T_ERROR = 'error';
|
86 |
+
/**
|
87 |
+
* @internal
|
88 |
+
*/
|
89 |
const T_EXPRESSION = 'exp';
|
90 |
+
/**
|
91 |
+
* @internal
|
92 |
+
*/
|
93 |
const T_EXTEND = 'extend';
|
94 |
+
/**
|
95 |
+
* @internal
|
96 |
+
*/
|
97 |
const T_FOR = 'for';
|
98 |
const T_FUNCTION = 'function';
|
99 |
+
/**
|
100 |
+
* @internal
|
101 |
+
*/
|
102 |
+
const T_FUNCTION_REFERENCE = 'function-reference';
|
103 |
+
/**
|
104 |
+
* @internal
|
105 |
+
*/
|
106 |
const T_FUNCTION_CALL = 'fncall';
|
107 |
+
/**
|
108 |
+
* @internal
|
109 |
+
*/
|
110 |
const T_HSL = 'hsl';
|
111 |
+
/**
|
112 |
+
* @internal
|
113 |
+
*/
|
114 |
+
const T_HWB = 'hwb';
|
115 |
+
/**
|
116 |
+
* @internal
|
117 |
+
*/
|
118 |
const T_IF = 'if';
|
119 |
+
/**
|
120 |
+
* @internal
|
121 |
+
*/
|
122 |
const T_IMPORT = 'import';
|
123 |
+
/**
|
124 |
+
* @internal
|
125 |
+
*/
|
126 |
const T_INCLUDE = 'include';
|
127 |
+
/**
|
128 |
+
* @internal
|
129 |
+
*/
|
130 |
const T_INTERPOLATE = 'interpolate';
|
131 |
+
/**
|
132 |
+
* @internal
|
133 |
+
*/
|
134 |
const T_INTERPOLATED = 'interpolated';
|
135 |
+
/**
|
136 |
+
* @internal
|
137 |
+
*/
|
138 |
const T_KEYWORD = 'keyword';
|
139 |
const T_LIST = 'list';
|
140 |
const T_MAP = 'map';
|
141 |
+
/**
|
142 |
+
* @internal
|
143 |
+
*/
|
144 |
const T_MEDIA = 'media';
|
145 |
+
/**
|
146 |
+
* @internal
|
147 |
+
*/
|
148 |
const T_MEDIA_EXPRESSION = 'mediaExp';
|
149 |
+
/**
|
150 |
+
* @internal
|
151 |
+
*/
|
152 |
const T_MEDIA_TYPE = 'mediaType';
|
153 |
+
/**
|
154 |
+
* @internal
|
155 |
+
*/
|
156 |
const T_MEDIA_VALUE = 'mediaValue';
|
157 |
+
/**
|
158 |
+
* @internal
|
159 |
+
*/
|
160 |
const T_MIXIN = 'mixin';
|
161 |
+
/**
|
162 |
+
* @internal
|
163 |
+
*/
|
164 |
const T_MIXIN_CONTENT = 'mixin_content';
|
165 |
+
/**
|
166 |
+
* @internal
|
167 |
+
*/
|
168 |
const T_NESTED_PROPERTY = 'nestedprop';
|
169 |
+
/**
|
170 |
+
* @internal
|
171 |
+
*/
|
172 |
const T_NOT = 'not';
|
173 |
const T_NULL = 'null';
|
174 |
const T_NUMBER = 'number';
|
175 |
+
/**
|
176 |
+
* @internal
|
177 |
+
*/
|
178 |
const T_RETURN = 'return';
|
179 |
+
/**
|
180 |
+
* @internal
|
181 |
+
*/
|
182 |
const T_ROOT = 'root';
|
183 |
+
/**
|
184 |
+
* @internal
|
185 |
+
*/
|
186 |
const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
|
187 |
+
/**
|
188 |
+
* @internal
|
189 |
+
*/
|
190 |
const T_SELF = 'self';
|
191 |
const T_STRING = 'string';
|
192 |
+
/**
|
193 |
+
* @internal
|
194 |
+
*/
|
195 |
const T_UNARY = 'unary';
|
196 |
+
/**
|
197 |
+
* @internal
|
198 |
+
*/
|
199 |
const T_VARIABLE = 'var';
|
200 |
+
/**
|
201 |
+
* @internal
|
202 |
+
*/
|
203 |
const T_WARN = 'warn';
|
204 |
+
/**
|
205 |
+
* @internal
|
206 |
+
*/
|
207 |
const T_WHILE = 'while';
|
208 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Util.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -13,11 +14,14 @@ namespace ScssPhp\ScssPhp;
|
|
13 |
|
14 |
use ScssPhp\ScssPhp\Base\Range;
|
15 |
use ScssPhp\ScssPhp\Exception\RangeException;
|
|
|
16 |
|
17 |
/**
|
18 |
-
*
|
19 |
*
|
20 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
|
|
|
|
21 |
*/
|
22 |
class Util
|
23 |
{
|
@@ -25,10 +29,10 @@ class Util
|
|
25 |
* Asserts that `value` falls within `range` (inclusive), leaving
|
26 |
* room for slight floating-point errors.
|
27 |
*
|
28 |
-
* @param string
|
29 |
-
* @param
|
30 |
-
* @param array
|
31 |
-
* @param string
|
32 |
*
|
33 |
* @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin.
|
34 |
*
|
@@ -39,6 +43,10 @@ class Util
|
|
39 |
$val = $value[1];
|
40 |
$grace = new Range(-0.00001, 0.00001);
|
41 |
|
|
|
|
|
|
|
|
|
42 |
if ($range->includes($val)) {
|
43 |
return $val;
|
44 |
}
|
@@ -67,4 +75,110 @@ class Util
|
|
67 |
|
68 |
return strtr(rawurlencode($string), $revert);
|
69 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
14 |
|
15 |
use ScssPhp\ScssPhp\Base\Range;
|
16 |
use ScssPhp\ScssPhp\Exception\RangeException;
|
17 |
+
use ScssPhp\ScssPhp\Node\Number;
|
18 |
|
19 |
/**
|
20 |
+
* Utility functions
|
21 |
*
|
22 |
* @author Anthon Pang <anthon.pang@gmail.com>
|
23 |
+
*
|
24 |
+
* @internal
|
25 |
*/
|
26 |
class Util
|
27 |
{
|
29 |
* Asserts that `value` falls within `range` (inclusive), leaving
|
30 |
* room for slight floating-point errors.
|
31 |
*
|
32 |
+
* @param string $name The name of the value. Used in the error message.
|
33 |
+
* @param Range $range Range of values.
|
34 |
+
* @param array|Number $value The value to check.
|
35 |
+
* @param string $unit The unit of the value. Used in error reporting.
|
36 |
*
|
37 |
* @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin.
|
38 |
*
|
43 |
$val = $value[1];
|
44 |
$grace = new Range(-0.00001, 0.00001);
|
45 |
|
46 |
+
if (! \is_numeric($val)) {
|
47 |
+
throw new RangeException("$name {$val} is not a number.");
|
48 |
+
}
|
49 |
+
|
50 |
if ($range->includes($val)) {
|
51 |
return $val;
|
52 |
}
|
75 |
|
76 |
return strtr(rawurlencode($string), $revert);
|
77 |
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* mb_chr() wrapper
|
81 |
+
*
|
82 |
+
* @param int $code
|
83 |
+
*
|
84 |
+
* @return string
|
85 |
+
*/
|
86 |
+
public static function mbChr($code)
|
87 |
+
{
|
88 |
+
// Use the native implementation if available, but not on PHP 7.2 as mb_chr(0) is buggy there
|
89 |
+
if (\PHP_VERSION_ID > 70300 && \function_exists('mb_chr')) {
|
90 |
+
return mb_chr($code, 'UTF-8');
|
91 |
+
}
|
92 |
+
|
93 |
+
if (0x80 > $code %= 0x200000) {
|
94 |
+
$s = \chr($code);
|
95 |
+
} elseif (0x800 > $code) {
|
96 |
+
$s = \chr(0xC0 | $code >> 6) . \chr(0x80 | $code & 0x3F);
|
97 |
+
} elseif (0x10000 > $code) {
|
98 |
+
$s = \chr(0xE0 | $code >> 12) . \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
|
99 |
+
} else {
|
100 |
+
$s = \chr(0xF0 | $code >> 18) . \chr(0x80 | $code >> 12 & 0x3F)
|
101 |
+
. \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
|
102 |
+
}
|
103 |
+
|
104 |
+
return $s;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* mb_strlen() wrapper
|
109 |
+
*
|
110 |
+
* @param string $string
|
111 |
+
* @return int
|
112 |
+
*/
|
113 |
+
public static function mbStrlen($string)
|
114 |
+
{
|
115 |
+
// Use the native implementation if available.
|
116 |
+
if (\function_exists('mb_strlen')) {
|
117 |
+
return mb_strlen($string, 'UTF-8');
|
118 |
+
}
|
119 |
+
|
120 |
+
if (\function_exists('iconv_strlen')) {
|
121 |
+
return (int) @iconv_strlen($string, 'UTF-8');
|
122 |
+
}
|
123 |
+
|
124 |
+
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* mb_substr() wrapper
|
129 |
+
* @param string $string
|
130 |
+
* @param int $start
|
131 |
+
* @param null|int $length
|
132 |
+
* @return string
|
133 |
+
*/
|
134 |
+
public static function mbSubstr($string, $start, $length = null)
|
135 |
+
{
|
136 |
+
// Use the native implementation if available.
|
137 |
+
if (\function_exists('mb_substr')) {
|
138 |
+
return mb_substr($string, $start, $length, 'UTF-8');
|
139 |
+
}
|
140 |
+
|
141 |
+
if (\function_exists('iconv_substr')) {
|
142 |
+
if ($start < 0) {
|
143 |
+
$start = static::mbStrlen($string) + $start;
|
144 |
+
if ($start < 0) {
|
145 |
+
$start = 0;
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
if (null === $length) {
|
150 |
+
$length = 2147483647;
|
151 |
+
} elseif ($length < 0) {
|
152 |
+
$length = static::mbStrlen($string) + $length - $start;
|
153 |
+
if ($length < 0) {
|
154 |
+
return '';
|
155 |
+
}
|
156 |
+
}
|
157 |
+
|
158 |
+
return (string)iconv_substr($string, $start, $length, 'UTF-8');
|
159 |
+
}
|
160 |
+
|
161 |
+
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* mb_strpos wrapper
|
166 |
+
* @param string $haystack
|
167 |
+
* @param string $needle
|
168 |
+
* @param int $offset
|
169 |
+
*
|
170 |
+
* @return int|false
|
171 |
+
*/
|
172 |
+
public static function mbStrpos($haystack, $needle, $offset = 0)
|
173 |
+
{
|
174 |
+
if (\function_exists('mb_strpos')) {
|
175 |
+
return mb_strpos($haystack, $needle, $offset, 'UTF-8');
|
176 |
+
}
|
177 |
+
|
178 |
+
if (\function_exists('iconv_strpos')) {
|
179 |
+
return iconv_strpos($haystack, $needle, $offset, 'UTF-8');
|
180 |
+
}
|
181 |
+
|
182 |
+
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
183 |
+
}
|
184 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Util/Path.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ScssPhp\ScssPhp\Util;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* @internal
|
7 |
+
*/
|
8 |
+
class Path
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* @param string $path
|
12 |
+
*
|
13 |
+
* @return bool
|
14 |
+
*/
|
15 |
+
public static function isAbsolute($path)
|
16 |
+
{
|
17 |
+
if ($path === '') {
|
18 |
+
return false;
|
19 |
+
}
|
20 |
+
|
21 |
+
if ($path[0] === '/') {
|
22 |
+
return true;
|
23 |
+
}
|
24 |
+
|
25 |
+
if (\DIRECTORY_SEPARATOR !== '\\') {
|
26 |
+
return false;
|
27 |
+
}
|
28 |
+
|
29 |
+
if ($path[0] === '\\') {
|
30 |
+
return true;
|
31 |
+
}
|
32 |
+
|
33 |
+
if (\strlen($path) < 3) {
|
34 |
+
return false;
|
35 |
+
}
|
36 |
+
|
37 |
+
if ($path[1] !== ':') {
|
38 |
+
return false;
|
39 |
+
}
|
40 |
+
|
41 |
+
if ($path[2] !== '/' && $path[2] !== '\\') {
|
42 |
+
return false;
|
43 |
+
}
|
44 |
+
|
45 |
+
if (!preg_match('/^[A-Za-z]$/', $path[0])) {
|
46 |
+
return false;
|
47 |
+
}
|
48 |
+
|
49 |
+
return true;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @param string $part1
|
54 |
+
* @param string $part2
|
55 |
+
*
|
56 |
+
* @return string
|
57 |
+
*/
|
58 |
+
public static function join($part1, $part2)
|
59 |
+
{
|
60 |
+
if ($part1 === '' || self::isAbsolute($part2)) {
|
61 |
+
return $part2;
|
62 |
+
}
|
63 |
+
|
64 |
+
if ($part2 === '') {
|
65 |
+
return $part1;
|
66 |
+
}
|
67 |
+
|
68 |
+
$last = $part1[\strlen($part1) - 1];
|
69 |
+
$separator = \DIRECTORY_SEPARATOR;
|
70 |
+
|
71 |
+
if ($last === '/' || $last === \DIRECTORY_SEPARATOR) {
|
72 |
+
$separator = '';
|
73 |
+
}
|
74 |
+
|
75 |
+
return $part1 . $separator . $part2;
|
76 |
+
}
|
77 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/ValueConverter.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp;
|
14 |
+
|
15 |
+
use ScssPhp\ScssPhp\Node\Number;
|
16 |
+
|
17 |
+
final class ValueConverter
|
18 |
+
{
|
19 |
+
// Prevent instantiating it
|
20 |
+
private function __construct()
|
21 |
+
{
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Parses a value from a Scss source string.
|
26 |
+
*
|
27 |
+
* The returned value is guaranteed to be supported by the
|
28 |
+
* Compiler methods for registering custom variables. No other
|
29 |
+
* guarantee about it is provided. It should be considered
|
30 |
+
* opaque values by the caller.
|
31 |
+
*
|
32 |
+
* @param string $source
|
33 |
+
*
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
public static function parseValue($source)
|
37 |
+
{
|
38 |
+
$parser = new Parser(__CLASS__);
|
39 |
+
|
40 |
+
if (!$parser->parseValue($source, $value)) {
|
41 |
+
throw new \InvalidArgumentException(sprintf('Invalid value source "%s".', $source));
|
42 |
+
}
|
43 |
+
|
44 |
+
return $value;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Converts a PHP value to a Sass value
|
49 |
+
*
|
50 |
+
* The returned value is guaranteed to be supported by the
|
51 |
+
* Compiler methods for registering custom variables. No other
|
52 |
+
* guarantee about it is provided. It should be considered
|
53 |
+
* opaque values by the caller.
|
54 |
+
*
|
55 |
+
* @param mixed $value
|
56 |
+
*
|
57 |
+
* @return mixed
|
58 |
+
*/
|
59 |
+
public static function fromPhp($value)
|
60 |
+
{
|
61 |
+
if ($value instanceof Number) {
|
62 |
+
return $value;
|
63 |
+
}
|
64 |
+
|
65 |
+
if (is_array($value) && isset($value[0]) && \in_array($value[0], [Type::T_NULL, Type::T_COLOR, Type::T_KEYWORD, Type::T_LIST, Type::T_MAP, Type::T_STRING])) {
|
66 |
+
return $value;
|
67 |
+
}
|
68 |
+
|
69 |
+
if ($value === null) {
|
70 |
+
return Compiler::$null;
|
71 |
+
}
|
72 |
+
|
73 |
+
if ($value === true) {
|
74 |
+
return Compiler::$true;
|
75 |
+
}
|
76 |
+
|
77 |
+
if ($value === false) {
|
78 |
+
return Compiler::$false;
|
79 |
+
}
|
80 |
+
|
81 |
+
if ($value === '') {
|
82 |
+
return Compiler::$emptyString;
|
83 |
+
}
|
84 |
+
|
85 |
+
if (\is_int($value) || \is_float($value)) {
|
86 |
+
return new Number($value, '');
|
87 |
+
}
|
88 |
+
|
89 |
+
if (\is_string($value)) {
|
90 |
+
return [Type::T_STRING, '"', [$value]];
|
91 |
+
}
|
92 |
+
|
93 |
+
throw new \InvalidArgumentException(sprintf('Cannot convert the value of type "%s" to a Sass value.', gettype($value)));
|
94 |
+
}
|
95 |
+
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Version.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* SCSSPHP
|
4 |
*
|
@@ -18,5 +19,5 @@ namespace ScssPhp\ScssPhp;
|
|
18 |
*/
|
19 |
class Version
|
20 |
{
|
21 |
-
const VERSION = '1.
|
22 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* SCSSPHP
|
5 |
*
|
19 |
*/
|
20 |
class Version
|
21 |
{
|
22 |
+
const VERSION = '1.10.2';
|
23 |
}
|
v4.0.0/libs/scssphp/vendor/scssphp/scssphp/src/Warn.php
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* SCSSPHP
|
5 |
+
*
|
6 |
+
* @copyright 2012-2020 Leaf Corcoran
|
7 |
+
*
|
8 |
+
* @license http://opensource.org/licenses/MIT MIT
|
9 |
+
*
|
10 |
+
* @link http://scssphp.github.io/scssphp
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace ScssPhp\ScssPhp;
|
14 |
+
|
15 |
+
final class Warn
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var callable|null
|
19 |
+
* @phpstan-var (callable(string, bool): void)|null
|
20 |
+
*/
|
21 |
+
private static $callback;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Prints a warning message associated with the current `@import` or function call.
|
25 |
+
*
|
26 |
+
* This may only be called within a custom function or importer callback.
|
27 |
+
*
|
28 |
+
* @param string $message
|
29 |
+
*
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
public static function warning($message)
|
33 |
+
{
|
34 |
+
self::reportWarning($message, false);
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Prints a deprecation warning message associated with the current `@import` or function call.
|
39 |
+
*
|
40 |
+
* This may only be called within a custom function or importer callback.
|
41 |
+
*
|
42 |
+
* @param string $message
|
43 |
+
*
|
44 |
+
* @return void
|
45 |
+
*/
|
46 |
+
public static function deprecation($message)
|
47 |
+
{
|
48 |
+
self::reportWarning($message, true);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param callable|null $callback
|
53 |
+
*
|
54 |
+
* @return callable|null The previous warn callback
|
55 |
+
*
|
56 |
+
* @phpstan-param (callable(string, bool): void)|null $callback
|
57 |
+
*
|
58 |
+
* @phpstan-return (callable(string, bool): void)|null
|
59 |
+
*
|
60 |
+
* @internal
|
61 |
+
*/
|
62 |
+
public static function setCallback(callable $callback = null)
|
63 |
+
{
|
64 |
+
$previousCallback = self::$callback;
|
65 |
+
self::$callback = $callback;
|
66 |
+
|
67 |
+
return $previousCallback;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @param string $message
|
72 |
+
* @param bool $deprecation
|
73 |
+
*
|
74 |
+
* @return void
|
75 |
+
*/
|
76 |
+
private static function reportWarning($message, $deprecation)
|
77 |
+
{
|
78 |
+
if (self::$callback === null) {
|
79 |
+
throw new \BadMethodCallException('The warning Reporter may only be called within a custom function or importer callback.');
|
80 |
+
}
|
81 |
+
|
82 |
+
\call_user_func(self::$callback, $message, $deprecation);
|
83 |
+
}
|
84 |
+
}
|
v4.0.0/templates/rmp-editor.php
CHANGED
@@ -47,7 +47,11 @@ $theme_manager = Theme_Manager::get_instance();
|
|
47 |
$editor = Editor::get_instance();
|
48 |
$menu_id = get_the_ID();
|
49 |
$options = $option_manager->get_options( $menu_id );
|
50 |
-
|
|
|
|
|
|
|
|
|
51 |
?>
|
52 |
<!DOCTYPE html>
|
53 |
<html <?php language_attributes(); ?>>
|
@@ -71,9 +75,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
71 |
<li id="rmp-tab-item-mobile-menu" class="rmp-tab-item" aria-owns="tab-mobile-menu">
|
72 |
<span class="rmp-tab-item-icon">
|
73 |
<?php
|
74 |
-
$svg_mobile =
|
75 |
-
if (
|
76 |
-
echo wp_kses( $svg_mobile
|
77 |
}
|
78 |
?>
|
79 |
</span>
|
@@ -83,9 +87,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
83 |
<li id="rmp-tab-item-desktop-menu" class="rmp-tab-item" aria-owns="tab-desktop-menu">
|
84 |
<span class="rmp-tab-item-icon">
|
85 |
<?php
|
86 |
-
$svg_desktop =
|
87 |
-
if (
|
88 |
-
echo wp_kses( $svg_desktop
|
89 |
}
|
90 |
?>
|
91 |
</span>
|
@@ -98,9 +102,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
98 |
<li id="rmp-tab-item-dropdowns" class="rmp-tab-item" aria-owns="tab-menu-styling">
|
99 |
<span class="rmp-tab-item-icon">
|
100 |
<?php
|
101 |
-
$svg_dropdowns =
|
102 |
-
if (
|
103 |
-
echo wp_kses( $svg_dropdowns
|
104 |
}
|
105 |
?>
|
106 |
</span>
|
@@ -110,9 +114,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
110 |
<li id="rmp-tab-item-header-bar" class="rmp-tab-item" aria-owns="tab-header-bar">
|
111 |
<span class="rmp-tab-item-icon">
|
112 |
<?php
|
113 |
-
$svg_header =
|
114 |
-
if (
|
115 |
-
echo wp_kses( $svg_header
|
116 |
}
|
117 |
?>
|
118 |
</span>
|
@@ -125,9 +129,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
125 |
<li id="rmp-tab-item-themes" class="rmp-tab-item" aria-owns="tab-themes">
|
126 |
<span class="rmp-tab-item-icon">
|
127 |
<?php
|
128 |
-
$svg_advanced =
|
129 |
-
if (
|
130 |
-
echo wp_kses( $svg_advanced
|
131 |
}
|
132 |
?>
|
133 |
</span>
|
@@ -137,9 +141,9 @@ $options = $option_manager->get_options( $menu_id );
|
|
137 |
<li id="rmp-tab-item-settings" class="rmp-tab-item" aria-owns="tab-settings">
|
138 |
<span class="rmp-tab-item-icon">
|
139 |
<?php
|
140 |
-
$svg_general =
|
141 |
-
if (
|
142 |
-
echo wp_kses( $svg_general
|
143 |
}
|
144 |
?>
|
145 |
</span>
|
@@ -416,7 +420,7 @@ $options = $option_manager->get_options( $menu_id );
|
|
416 |
'item_class' => 'is-child-item',
|
417 |
'aria_owns' => 'tab-container',
|
418 |
'item_header' => array(
|
419 |
-
'item_svg_icon' =>
|
420 |
'item_title' => esc_html__( 'Container', 'responsive-menu' ),
|
421 |
),
|
422 |
)
|
@@ -427,7 +431,7 @@ $options = $option_manager->get_options( $menu_id );
|
|
427 |
'item_class' => 'is-child-item',
|
428 |
'aria_owns' => 'tab-toggle-button',
|
429 |
'item_header' => array(
|
430 |
-
'item_svg_icon' =>
|
431 |
'item_title' => esc_html__( 'Toggle button', 'responsive-menu' ),
|
432 |
),
|
433 |
)
|
@@ -444,7 +448,7 @@ $options = $option_manager->get_options( $menu_id );
|
|
444 |
'item_class' => 'is-child-item rmp-tab-item-general-settings',
|
445 |
'aria_owns' => 'tab-general-settings',
|
446 |
'item_header' => array(
|
447 |
-
'item_svg_icon' =>
|
448 |
'item_title' => esc_html__( 'General Settings', 'responsive-menu' ),
|
449 |
),
|
450 |
)
|
@@ -455,7 +459,7 @@ $options = $option_manager->get_options( $menu_id );
|
|
455 |
'item_class' => 'is-child-item rmp-tab-item-advanced-settings',
|
456 |
'aria_owns' => 'tab-advanced-settings',
|
457 |
'item_header' => array(
|
458 |
-
'item_svg_icon' =>
|
459 |
'item_title' => esc_html__( 'Advanced Settings', 'responsive-menu' ),
|
460 |
),
|
461 |
)
|
47 |
$editor = Editor::get_instance();
|
48 |
$menu_id = get_the_ID();
|
49 |
$options = $option_manager->get_options( $menu_id );
|
50 |
+
global $wp_filesystem;
|
51 |
+
if ( empty( $wp_filesystem ) ) {
|
52 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
53 |
+
}
|
54 |
+
WP_Filesystem();
|
55 |
?>
|
56 |
<!DOCTYPE html>
|
57 |
<html <?php language_attributes(); ?>>
|
75 |
<li id="rmp-tab-item-mobile-menu" class="rmp-tab-item" aria-owns="tab-mobile-menu">
|
76 |
<span class="rmp-tab-item-icon">
|
77 |
<?php
|
78 |
+
$svg_mobile = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/mobile.svg' );
|
79 |
+
if ( $svg_mobile ) {
|
80 |
+
echo wp_kses( $svg_mobile, rmp_allow_svg_html_tags() );
|
81 |
}
|
82 |
?>
|
83 |
</span>
|
87 |
<li id="rmp-tab-item-desktop-menu" class="rmp-tab-item" aria-owns="tab-desktop-menu">
|
88 |
<span class="rmp-tab-item-icon">
|
89 |
<?php
|
90 |
+
$svg_desktop = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/desktop.svg' );
|
91 |
+
if ( $svg_desktop ) {
|
92 |
+
echo wp_kses( $svg_desktop, rmp_allow_svg_html_tags() );
|
93 |
}
|
94 |
?>
|
95 |
</span>
|
102 |
<li id="rmp-tab-item-dropdowns" class="rmp-tab-item" aria-owns="tab-menu-styling">
|
103 |
<span class="rmp-tab-item-icon">
|
104 |
<?php
|
105 |
+
$svg_dropdowns = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/dropdowns.svg' );
|
106 |
+
if ( $svg_dropdowns ) {
|
107 |
+
echo wp_kses( $svg_dropdowns, rmp_allow_svg_html_tags() );
|
108 |
}
|
109 |
?>
|
110 |
</span>
|
114 |
<li id="rmp-tab-item-header-bar" class="rmp-tab-item" aria-owns="tab-header-bar">
|
115 |
<span class="rmp-tab-item-icon">
|
116 |
<?php
|
117 |
+
$svg_header = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/header.svg' );
|
118 |
+
if ( $svg_header ) {
|
119 |
+
echo wp_kses( $svg_header, rmp_allow_svg_html_tags() );
|
120 |
}
|
121 |
?>
|
122 |
</span>
|
129 |
<li id="rmp-tab-item-themes" class="rmp-tab-item" aria-owns="tab-themes">
|
130 |
<span class="rmp-tab-item-icon">
|
131 |
<?php
|
132 |
+
$svg_advanced = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/advanced.svg' );
|
133 |
+
if ( $svg_advanced ) {
|
134 |
+
echo wp_kses( $svg_advanced, rmp_allow_svg_html_tags() );
|
135 |
}
|
136 |
?>
|
137 |
</span>
|
141 |
<li id="rmp-tab-item-settings" class="rmp-tab-item" aria-owns="tab-settings">
|
142 |
<span class="rmp-tab-item-icon">
|
143 |
<?php
|
144 |
+
$svg_general = $wp_filesystem->get_contents( RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/general.svg' );
|
145 |
+
if ( $svg_general ) {
|
146 |
+
echo wp_kses( $svg_general, rmp_allow_svg_html_tags() );
|
147 |
}
|
148 |
?>
|
149 |
</span>
|
420 |
'item_class' => 'is-child-item',
|
421 |
'aria_owns' => 'tab-container',
|
422 |
'item_header' => array(
|
423 |
+
'item_svg_icon' => RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/container.svg',
|
424 |
'item_title' => esc_html__( 'Container', 'responsive-menu' ),
|
425 |
),
|
426 |
)
|
431 |
'item_class' => 'is-child-item',
|
432 |
'aria_owns' => 'tab-toggle-button',
|
433 |
'item_header' => array(
|
434 |
+
'item_svg_icon' => RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/toggle.svg',
|
435 |
'item_title' => esc_html__( 'Toggle button', 'responsive-menu' ),
|
436 |
),
|
437 |
)
|
448 |
'item_class' => 'is-child-item rmp-tab-item-general-settings',
|
449 |
'aria_owns' => 'tab-general-settings',
|
450 |
'item_header' => array(
|
451 |
+
'item_svg_icon' => RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/general.svg',
|
452 |
'item_title' => esc_html__( 'General Settings', 'responsive-menu' ),
|
453 |
),
|
454 |
)
|
459 |
'item_class' => 'is-child-item rmp-tab-item-advanced-settings',
|
460 |
'aria_owns' => 'tab-advanced-settings',
|
461 |
'item_header' => array(
|
462 |
+
'item_svg_icon' => RMP_PLUGIN_PATH_V4 . '/assets/admin/icons/svg/advanced.svg',
|
463 |
'item_title' => esc_html__( 'Advanced Settings', 'responsive-menu' ),
|
464 |
),
|
465 |
)
|