WP-SCSS - Version 1.1.9

Version Description

  • Added filter to set variables via PHP @ohlle
  • Added option to minify CSS output @mndewitt
Download this release

Release Info

Developer connectthink
Plugin Icon wp plugin WP-SCSS
Version 1.1.9
Comparing to
See all releases

Code changes from version 1.1.0 to 1.1.9

Files changed (5) hide show
  1. class/class-wp-scss.php +45 -36
  2. options.php +4 -2
  3. readme.txt +40 -12
  4. scssphp/scss.inc.php +367 -138
  5. wp-scss.php +96 -66
class/class-wp-scss.php CHANGED
@@ -1,22 +1,22 @@
1
- <?php
2
 
3
  class Wp_Scss {
4
  /**
5
  * Compiling preferences properites
6
- *
7
  * @var string
8
  * @access public
9
  */
10
  public $scss_dir, $css_dir, $compile_method, $scssc, $compile_errors;
11
-
12
 
13
  /**
14
  * Set values for Wp_Scss::properties
15
- *
16
  * @param string scss_dir - path to source directory for scss files
17
  * @param string css_dir - path to output directory for css files
18
  * @param string method - type of compile (compressed, expanded, etc)
19
- *
20
  * @var object scssc - instantiate the compiling object.
21
  *
22
  * @var array compile_errors - catches errors from compile
@@ -29,35 +29,35 @@ class Wp_Scss {
29
  global $scssc;
30
  $scssc = new scssc();
31
  $scssc->setFormatter($compile_method);
32
- $scssc->setImportPaths($scss_dir);
33
 
34
  $this->compile_errors = array();
35
  }
36
 
37
- /**
38
  * METHOD COMPILE
39
  * Loops through scss directory and compilers files that end
40
  * with .scss and do not have '_' in front.
41
  *
42
- * @function compiler - passes input content through scssphp,
43
  * puts compiled css into cache file
44
  *
45
  * @var array input_files - array of .scss files with no '_' in front
46
  * @var array sdir_arr - an array of all the files in the scss directory
47
- *
48
- * @return nothing - Puts successfully compiled css into apporpriate location
49
  * Puts error in 'compile_errors' property
50
  * @access public
51
  */
52
  public function compile() {
53
  global $scssc, $cache;
54
  $cache = WPSCSS_PLUGIN_DIR . '/cache/';
55
-
56
  //Compiler - Takes scss $in and writes compiled css to $out file
57
  // catches errors and puts them the object's compiled_errors property
58
- function compiler($in, $out, $instance) {
59
- global $scssc, $cache;
60
-
61
  if (is_writable($cache)) {
62
  try {
63
  $css = $scssc->compile(file_get_contents($in));
@@ -71,34 +71,34 @@ class Wp_Scss {
71
  }
72
  } else {
73
  $errors = array (
74
- 'file' => "/wp-plugins/wp-scss/cache/",
75
  'message' => "File Permission Error, permission denied. Please make the cache directory writable."
76
  );
77
  array_push($instance->compile_errors, $errors);
78
  }
79
  }
80
 
81
- $input_files = array();
82
  // Loop through directory and get .scss file that do not start with '_'
83
  foreach(new DirectoryIterator($this->scss_dir) as $file) {
84
  if (substr($file, 0, 1) != "_" && pathinfo($file->getFilename(), PATHINFO_EXTENSION) == 'scss') {
85
  array_push($input_files, $file->getFilename());
86
  }
87
  }
88
-
89
  // For each input file, find matching css file and compile
90
  foreach ($input_files as $scss_file) {
91
  $input = $this->scss_dir.$scss_file;
92
  $outputName = preg_replace("/\.[^$]*/",".css", $scss_file);
93
  $output = $this->css_dir.$outputName;
94
-
95
  compiler($input, $output, $this);
96
  }
97
 
98
  if (count($this->compile_errors) < 1) {
99
  if ( is_writable($this->css_dir) ) {
100
  foreach (new DirectoryIterator($cache) as $cache_file) {
101
- if ( pathinfo($cache_file->getFilename(), PATHINFO_EXTENSION) == 'css') {
102
  file_put_contents($this->css_dir.$cache_file, file_get_contents($cache.$cache_file));
103
  unlink($cache.$cache_file->getFilename()); // Delete file on successful write
104
  }
@@ -111,10 +111,10 @@ class Wp_Scss {
111
  array_push($this->compile_errors, $errors);
112
  }
113
  }
114
- }
115
 
116
 
117
- /**
118
  * METHOD NEEDS_COMPILING
119
  * Gets the most recently modified file in the scss directory
120
  * and compares that do the most recently modified css file.
@@ -126,60 +126,64 @@ class Wp_Scss {
126
  *
127
  * @var array sdir_arr - scss directory files
128
  * @var array cdir_arr - css directory files
129
- *
130
  * @var string latest_scss - file mod time of the most recent file change
131
  * @var string latest_css - file mod time of the most recent file change
132
- *
133
  * @return bool - true if compiling is needed
134
  */
135
  public function needs_compiling() {
 
 
 
 
136
  $latest_scss = 0;
137
  $latest_css = 0;
138
 
139
- foreach ( new DirectoryIterator($this->scss_dir) as $sfile ) {
140
  if (pathinfo($sfile->getFilename(), PATHINFO_EXTENSION) == 'scss') {
141
  $file_time = $sfile->getMTime();
142
 
143
  if ( (int) $file_time > $latest_scss) {
144
- $latest_scss = $file_time;
145
  }
146
  }
147
  }
148
 
149
- foreach ( new DirectoryIterator($this->css_dir) as $cfile ) {
150
  if (pathinfo($cfile->getFilename(), PATHINFO_EXTENSION) == 'css') {
151
  $file_time = $cfile->getMTime();
152
-
153
  if ( (int) $file_time > $latest_css) {
154
- $latest_css = $file_time;
155
  }
156
  }
157
  }
158
-
159
  if ($latest_scss > $latest_css) {
160
  return true;
161
  } else {
162
- return false;
163
  }
164
  }
165
 
166
- /**
167
  * METHOD ENQUEUE STYLES
168
  * Enqueues all styles in the css directory.
169
  *
170
  * @param $css_folder - directory from theme root. We need this passed in separately
171
- * so it can be used in a url, not path
172
  */
173
  public function enqueue_files($css_folder) {
174
-
175
  foreach( new DirectoryIterator($this->css_dir) as $stylesheet ) {
176
  if ( pathinfo($stylesheet->getFilename(), PATHINFO_EXTENSION) == 'css' ) {
177
  $name = $stylesheet->getBasename('.css') . '-style';
178
  $uri = get_stylesheet_directory_uri().$css_folder.$stylesheet->getFilename();
179
  $ver = $stylesheet->getMTime();
180
 
181
-
182
- wp_register_style(
183
  $name,
184
  $uri,
185
  array(),
@@ -191,4 +195,9 @@ class Wp_Scss {
191
  }
192
  }
193
 
194
- } // End Wp_Scss Class
 
 
 
 
 
1
+ <?php
2
 
3
  class Wp_Scss {
4
  /**
5
  * Compiling preferences properites
6
+ *
7
  * @var string
8
  * @access public
9
  */
10
  public $scss_dir, $css_dir, $compile_method, $scssc, $compile_errors;
11
+
12
 
13
  /**
14
  * Set values for Wp_Scss::properties
15
+ *
16
  * @param string scss_dir - path to source directory for scss files
17
  * @param string css_dir - path to output directory for css files
18
  * @param string method - type of compile (compressed, expanded, etc)
19
+ *
20
  * @var object scssc - instantiate the compiling object.
21
  *
22
  * @var array compile_errors - catches errors from compile
29
  global $scssc;
30
  $scssc = new scssc();
31
  $scssc->setFormatter($compile_method);
32
+ $scssc->setImportPaths($scss_dir);
33
 
34
  $this->compile_errors = array();
35
  }
36
 
37
+ /**
38
  * METHOD COMPILE
39
  * Loops through scss directory and compilers files that end
40
  * with .scss and do not have '_' in front.
41
  *
42
+ * @function compiler - passes input content through scssphp,
43
  * puts compiled css into cache file
44
  *
45
  * @var array input_files - array of .scss files with no '_' in front
46
  * @var array sdir_arr - an array of all the files in the scss directory
47
+ *
48
+ * @return nothing - Puts successfully compiled css into apporpriate location
49
  * Puts error in 'compile_errors' property
50
  * @access public
51
  */
52
  public function compile() {
53
  global $scssc, $cache;
54
  $cache = WPSCSS_PLUGIN_DIR . '/cache/';
55
+
56
  //Compiler - Takes scss $in and writes compiled css to $out file
57
  // catches errors and puts them the object's compiled_errors property
58
+ function compiler($in, $out, $instance) {
59
+ global $scssc, $cache;
60
+
61
  if (is_writable($cache)) {
62
  try {
63
  $css = $scssc->compile(file_get_contents($in));
71
  }
72
  } else {
73
  $errors = array (
74
+ 'file' => $cache,
75
  'message' => "File Permission Error, permission denied. Please make the cache directory writable."
76
  );
77
  array_push($instance->compile_errors, $errors);
78
  }
79
  }
80
 
81
+ $input_files = array();
82
  // Loop through directory and get .scss file that do not start with '_'
83
  foreach(new DirectoryIterator($this->scss_dir) as $file) {
84
  if (substr($file, 0, 1) != "_" && pathinfo($file->getFilename(), PATHINFO_EXTENSION) == 'scss') {
85
  array_push($input_files, $file->getFilename());
86
  }
87
  }
88
+
89
  // For each input file, find matching css file and compile
90
  foreach ($input_files as $scss_file) {
91
  $input = $this->scss_dir.$scss_file;
92
  $outputName = preg_replace("/\.[^$]*/",".css", $scss_file);
93
  $output = $this->css_dir.$outputName;
94
+
95
  compiler($input, $output, $this);
96
  }
97
 
98
  if (count($this->compile_errors) < 1) {
99
  if ( is_writable($this->css_dir) ) {
100
  foreach (new DirectoryIterator($cache) as $cache_file) {
101
+ if ( pathinfo($cache_file->getFilename(), PATHINFO_EXTENSION) == 'css') {
102
  file_put_contents($this->css_dir.$cache_file, file_get_contents($cache.$cache_file));
103
  unlink($cache.$cache_file->getFilename()); // Delete file on successful write
104
  }
111
  array_push($this->compile_errors, $errors);
112
  }
113
  }
114
+ }
115
 
116
 
117
+ /**
118
  * METHOD NEEDS_COMPILING
119
  * Gets the most recently modified file in the scss directory
120
  * and compares that do the most recently modified css file.
126
  *
127
  * @var array sdir_arr - scss directory files
128
  * @var array cdir_arr - css directory files
129
+ *
130
  * @var string latest_scss - file mod time of the most recent file change
131
  * @var string latest_css - file mod time of the most recent file change
132
+ *
133
  * @return bool - true if compiling is needed
134
  */
135
  public function needs_compiling() {
136
+ if (defined('WP_SCSS_ALWAYS_RECOMPILE') && WP_SCSS_ALWAYS_RECOMPILE) {
137
+ return true;
138
+ }
139
+
140
  $latest_scss = 0;
141
  $latest_css = 0;
142
 
143
+ foreach ( new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->scss_dir)) as $sfile ) {
144
  if (pathinfo($sfile->getFilename(), PATHINFO_EXTENSION) == 'scss') {
145
  $file_time = $sfile->getMTime();
146
 
147
  if ( (int) $file_time > $latest_scss) {
148
+ $latest_scss = $file_time;
149
  }
150
  }
151
  }
152
 
153
+ foreach ( new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->css_dir)) as $cfile ) {
154
  if (pathinfo($cfile->getFilename(), PATHINFO_EXTENSION) == 'css') {
155
  $file_time = $cfile->getMTime();
156
+
157
  if ( (int) $file_time > $latest_css) {
158
+ $latest_css = $file_time;
159
  }
160
  }
161
  }
162
+
163
  if ($latest_scss > $latest_css) {
164
  return true;
165
  } else {
166
+ return false;
167
  }
168
  }
169
 
170
+ /**
171
  * METHOD ENQUEUE STYLES
172
  * Enqueues all styles in the css directory.
173
  *
174
  * @param $css_folder - directory from theme root. We need this passed in separately
175
+ * so it can be used in a url, not path
176
  */
177
  public function enqueue_files($css_folder) {
178
+
179
  foreach( new DirectoryIterator($this->css_dir) as $stylesheet ) {
180
  if ( pathinfo($stylesheet->getFilename(), PATHINFO_EXTENSION) == 'css' ) {
181
  $name = $stylesheet->getBasename('.css') . '-style';
182
  $uri = get_stylesheet_directory_uri().$css_folder.$stylesheet->getFilename();
183
  $ver = $stylesheet->getMTime();
184
 
185
+
186
+ wp_register_style(
187
  $name,
188
  $uri,
189
  array(),
195
  }
196
  }
197
 
198
+ public function set_variables(array $variables) {
199
+ global $scssc;
200
+ $scssc->setVariables($variables);
201
+ }
202
+
203
+ } // End Wp_Scss Class
options.php CHANGED
@@ -208,7 +208,8 @@ class Wp_Scss_Settings
208
  $html = '<select id="compiling_options" name="wpscss_options[compiling_options]">';
209
  $html .= '<option value="scss_formatter"' . selected( $this->options['compiling_options'], 'scss_formatter', false) . '>Expanded</option>';
210
  $html .= '<option value="scss_formatter_nested"' . selected( $this->options['compiling_options'], 'scss_formatter_nested', false) . '>Nested</option>';
211
- $html .= '<option value="scss_formatter_compressed"' . selected( $this->options['compiling_options'], 'scss_formatter_compressed', false) . '>Compressed</option>';
 
212
  $html .= '</select>';
213
 
214
  echo $html;
@@ -218,6 +219,7 @@ class Wp_Scss_Settings
218
 
219
  $html = '<select id="errors" name="wpscss_options[errors]">';
220
  $html .= '<option value="show"' . selected( $this->options['errors'], 'show', false) . '>Show in Header</option>';
 
221
  $html .= '<option value="log"' . selected( $this->options['errors'], 'hide', false) . '>Print to Log</option>';
222
  $html .= '</select>';
223
 
@@ -230,7 +232,7 @@ class Wp_Scss_Settings
230
  function enqueue_callback() {
231
  $this->options = get_option( 'wpscss_options' );
232
 
233
- $html = '<input type="checkbox" id="enqueue" name="wpscss_options[enqueue]" value="1"' . checked( 1, $this->options['enqueue'], false ) . '/>';
234
  $html .= '<label for="enqueue"></label>';
235
 
236
  echo $html;
208
  $html = '<select id="compiling_options" name="wpscss_options[compiling_options]">';
209
  $html .= '<option value="scss_formatter"' . selected( $this->options['compiling_options'], 'scss_formatter', false) . '>Expanded</option>';
210
  $html .= '<option value="scss_formatter_nested"' . selected( $this->options['compiling_options'], 'scss_formatter_nested', false) . '>Nested</option>';
211
+ $html .= '<option value="scss_formatter_compressed"' . selected( $this->options['compiling_options'], 'scss_formatter_compressed', false) . '>Compressed</option>';
212
+ $html .= '<option value="scss_formatter_minified"' . selected( $this->options['compiling_options'], 'scss_formatter_minified', false) . '>Minified</option>';
213
  $html .= '</select>';
214
 
215
  echo $html;
219
 
220
  $html = '<select id="errors" name="wpscss_options[errors]">';
221
  $html .= '<option value="show"' . selected( $this->options['errors'], 'show', false) . '>Show in Header</option>';
222
+ $html .= '<option value="show-logged-in"' . selected( $this->options['errors'], 'show-logged-in', false) . '>Show to Logged In Users</option>';
223
  $html .= '<option value="log"' . selected( $this->options['errors'], 'hide', false) . '>Print to Log</option>';
224
  $html .= '</select>';
225
 
232
  function enqueue_callback() {
233
  $this->options = get_option( 'wpscss_options' );
234
 
235
+ $html = '<input type="checkbox" id="enqueue" name="wpscss_options[enqueue]" value="1"' . checked( 1, isset($this->options['enqueue']) ? $this->options['enqueue'] : 0, false ) . '/>';
236
  $html .= '<label for="enqueue"></label>';
237
 
238
  echo $html;
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: connectthink
3
  Tags: sass, scss, css
4
  Plugin URI: https://github.com/ConnectThink/WP-SCSS
5
  Requires at least: 3.0.1
6
- Tested up to: 3.8
7
- Stable tag: 1.1.0
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/copyleft/gpl.html
10
 
@@ -14,7 +14,7 @@ Compiles .scss files to .css and enqueues them.
14
 
15
  Compiles .scss files on your wordpress install using [lefo's scssphp](https://github.com/leafo/scssphp). Includes settings page for configuring directories, error reporting, compiling options, and auto enqueuing.
16
 
17
- The plugin only compiles when changes have been made to the scss files. Compiles are made to the matching css file, so disabling this plugin will not take down your stylesheets. In the instance where a matching css file does not exist yet, the plugin will create the appropriate css file in the css directory.
18
 
19
  [Get detailed instructions on github](https://github.com/ConnectThink/WP-SCSS)
20
 
@@ -26,30 +26,34 @@ The plugin only compiles when changes have been made to the scss files. Compiles
26
 
27
  == Frequently Asked Questions ==
28
 
 
 
 
 
29
  = How do I @import subfiles =
30
 
31
  You can import other scss files into parent files and compile them into a single css file. To do this, use @import as normal in your scss file. All imported file names *must* start with an underscore. Otherwise they will be compiled into their own css file.
32
 
33
  When importing in your scss file, you can leave off the underscore.
34
-
35
  > `@import 'subfile';`
36
-
37
 
38
- = Does this plugin support Compass? =
 
39
  Currently there isn't a way to fully support [compass](https://github.com/chriseppstein/compass) with a php compiler. If you want limited support, you can manually import the compass framework. You'll need both the _compass.scss and compass directory.
40
-
41
-
42
  > `compass / frameworks / compass / stylesheets /
43
 
44
  > `@import 'compass';`
45
 
46
 
47
- Alternatively, you can include [Bourbon](https://github.com/thoughtbot/bourbon) in a similar fashion.
48
 
49
  = Can I use .sass syntax with this Plugin? =
50
  This plugin will only work with .scss format.
51
 
52
- = It's not updating my css, what's happening? =
53
  Do you have errors printing to the front end? If not, check your log file in your scss directory. The css will not be updated if there are errors in your sass file(s).
54
 
55
  Make sure your directories are properly defined in the settings. Paths are defined from the root of the theme.
@@ -58,10 +62,34 @@ Make sure your directories are properly defined in the settings. Paths are defin
58
  If you are having issues with the plugin, create an issue on [github](https://github.com/ConnectThink/WP-SCSS), and we'll do our best to help.
59
 
60
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- = 1.1 =
63
  * Added error handling for file permissions issues
64
  * Changed error log to .log for auto updating errors
65
 
66
- = 1.0 =
67
  * Initial Build
3
  Tags: sass, scss, css
4
  Plugin URI: https://github.com/ConnectThink/WP-SCSS
5
  Requires at least: 3.0.1
6
+ Tested up to: 4.3
7
+ Stable tag: 1.1.9
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/copyleft/gpl.html
10
 
14
 
15
  Compiles .scss files on your wordpress install using [lefo's scssphp](https://github.com/leafo/scssphp). Includes settings page for configuring directories, error reporting, compiling options, and auto enqueuing.
16
 
17
+ The plugin only compiles when changes have been made to the scss files. Compiles are made to the matching css file, so disabling this plugin will not take down your stylesheets. In the instance where a matching css file does not exist yet, the plugin will create the appropriate css file in the css directory.
18
 
19
  [Get detailed instructions on github](https://github.com/ConnectThink/WP-SCSS)
20
 
26
 
27
  == Frequently Asked Questions ==
28
 
29
+ = Can I use a child theme? =
30
+
31
+ Yes, absolutely. Make sure you define your directories relative to your child theme and that your child theme is active. Otherwise you'll see an error regarding missing directories.
32
+
33
  = How do I @import subfiles =
34
 
35
  You can import other scss files into parent files and compile them into a single css file. To do this, use @import as normal in your scss file. All imported file names *must* start with an underscore. Otherwise they will be compiled into their own css file.
36
 
37
  When importing in your scss file, you can leave off the underscore.
38
+
39
  > `@import 'subfile';`
 
40
 
41
+
42
+ = Does this plugin support Compass? =
43
  Currently there isn't a way to fully support [compass](https://github.com/chriseppstein/compass) with a php compiler. If you want limited support, you can manually import the compass framework. You'll need both the _compass.scss and compass directory.
44
+
45
+
46
  > `compass / frameworks / compass / stylesheets /
47
 
48
  > `@import 'compass';`
49
 
50
 
51
+ Alternatively, you can include [Bourbon](https://github.com/thoughtbot/bourbon) in a similar fashion.
52
 
53
  = Can I use .sass syntax with this Plugin? =
54
  This plugin will only work with .scss format.
55
 
56
+ = It's not updating my css, what's happening? =
57
  Do you have errors printing to the front end? If not, check your log file in your scss directory. The css will not be updated if there are errors in your sass file(s).
58
 
59
  Make sure your directories are properly defined in the settings. Paths are defined from the root of the theme.
62
  If you are having issues with the plugin, create an issue on [github](https://github.com/ConnectThink/WP-SCSS), and we'll do our best to help.
63
 
64
  == Changelog ==
65
+ = 1.1.9 =
66
+ * Added filter to set variables via PHP [@ohlle](https://github.com/ohlle)
67
+ * Added option to minify CSS output [@mndewitt](https://github.com/mndewitt)
68
+
69
+ = 1.1.8 =
70
+ Various improvements from pull requests by [@jbrains](https://github.com/jbrains) and [@brainfork](https://github.com/brainfork)
71
+
72
+ = 1.1.7 =
73
+ * Update scssphp to 0.0.12 - pull from #16 [@GabrielGil](https://github.com/GabrielGil)
74
+
75
+ = 1.1.6 =
76
+ * Upgraded scss.inc.php to version 0.0.10; via pull request from [kirkhoff](https://github.com/kirkhoff)
77
+
78
+ = 1.1.5 =
79
+ * Added option to only show errors to logged in users; via pull request from [tolnem](https://github.com/tolnem)
80
+
81
+ = 1.1.4 =
82
+ * Add suport for subfolders in scss directory
83
+
84
+ = 1.1.3 =
85
+ * Hotfix for a accidental character
86
+
87
+ = 1.1.2 =
88
+ * Added support for moved wp-content directories
89
 
90
+ = 1.1.1 =
91
  * Added error handling for file permissions issues
92
  * Changed error log to .log for auto updating errors
93
 
94
+ = 1.0.0 =
95
  * Initial Build
scssphp/scss.inc.php CHANGED
@@ -14,9 +14,9 @@
14
  * The scss compiler and parser.
15
  *
16
  * Converting SCSS to CSS is a three stage process. The incoming file is parsed
17
- * by `scssc_parser` into a syntax tree, then it is compiled into another tree
18
  * representing the CSS structure by `scssc`. The CSS tree is fed into a
19
- * formatter, like `scssc_formatter` which then outputs CSS as a string.
20
  *
21
  * During the first compile, all values are *reduced*, which means that their
22
  * types are brought to the lowest form before being dump as strings. This
@@ -31,9 +31,9 @@
31
  * evaluation context, such as all available mixins and variables at any given
32
  * time.
33
  *
34
- * The `scssc_parser` class is only concerned with parsing its input.
35
  *
36
- * The `scssc_formatter` takes a CSS tree, and dumps it to a formatted string,
37
  * handling things like indentation.
38
  */
39
 
@@ -43,7 +43,7 @@
43
  * @author Leaf Corcoran <leafot@gmail.com>
44
  */
45
  class scssc {
46
- static public $VERSION = "v0.0.8";
47
 
48
  static protected $operatorNames = array(
49
  '+' => "add",
@@ -67,7 +67,6 @@ class scssc {
67
  "function" => "^",
68
  );
69
 
70
- static protected $numberPrecision = 5;
71
  static protected $unitTable = array(
72
  "in" => array(
73
  "in" => 1,
@@ -90,32 +89,48 @@ class scssc {
90
  protected $importCache = array();
91
 
92
  protected $userFunctions = array();
 
 
 
93
 
94
  protected $formatter = "scss_formatter_nested";
95
 
96
- function compile($code, $name=null) {
97
- $this->indentLevel = -1;
 
 
 
 
 
 
 
 
 
98
  $this->commentsSeen = array();
99
- $this->extends = array();
100
- $this->extendsMap = array();
 
 
 
101
 
102
  $locale = setlocale(LC_NUMERIC, 0);
103
  setlocale(LC_NUMERIC, "C");
104
 
105
- $this->parsedFiles = array();
106
  $this->parser = new scss_parser($name);
 
107
  $tree = $this->parser->parse($code);
108
 
109
  $this->formatter = new $this->formatter();
110
 
111
- $this->env = null;
112
- $this->scope = null;
113
-
114
  $this->compileRoot($tree);
 
115
 
116
  $out = $this->formatter->format($this->scope);
117
 
118
  setlocale(LC_NUMERIC, $locale);
 
119
  return $out;
120
  }
121
 
@@ -186,6 +201,13 @@ class scssc {
186
  $rem = array_diff($single, $target);
187
 
188
  foreach ($origin as $j => $new) {
 
 
 
 
 
 
 
189
  $origin[$j][count($origin[$j]) - 1] = $this->combineSelectorSingle(end($new), $rem);
190
  }
191
 
@@ -298,47 +320,50 @@ class scssc {
298
  }
299
  }
300
 
301
- protected function compileRoot($rootBlock) {
302
- $this->pushEnv($rootBlock);
303
- $this->scope = $this->makeOutputBlock("root");
304
 
305
  $this->compileChildren($rootBlock->children, $this->scope);
306
  $this->flattenSelectors($this->scope);
307
-
308
- $this->popEnv();
309
  }
310
 
311
  protected function compileMedia($media) {
312
  $this->pushEnv($media);
313
- $parentScope = $this->mediaParent($this->scope);
314
 
315
- $this->scope = $this->makeOutputBlock("media", array(
316
- $this->compileMediaQuery($this->multiplyMedia($this->env)))
317
- );
318
 
319
- $parentScope->children[] = $this->scope;
320
 
321
- // top level properties in a media cause it to be wrapped
322
- $needsWrap = false;
323
- foreach ($media->children as $child) {
324
- $type = $child[0];
325
- if ($type !== 'block' && $type !== 'media' && $type !== 'directive') {
326
- $needsWrap = true;
327
- break;
 
 
 
 
 
 
 
328
  }
329
- }
330
 
331
- if ($needsWrap) {
332
- $wrapped = (object)array(
333
- "selectors" => array(),
334
- "children" => $media->children
335
- );
336
- $media->children = array(array("block", $wrapped));
337
- }
338
 
339
- $this->compileChildren($media->children, $this->scope);
 
 
 
340
 
341
- $this->scope = $this->scope->parent;
342
  $this->popEnv();
343
  }
344
 
@@ -484,7 +509,7 @@ class scssc {
484
  protected function compileChildren($stms, $out) {
485
  foreach ($stms as $stm) {
486
  $ret = $this->compileChild($stm, $out);
487
- if (!is_null($ret)) return $ret;
488
  }
489
  }
490
 
@@ -492,11 +517,19 @@ class scssc {
492
  $out = "@media";
493
  $first = true;
494
  foreach ($queryList as $query){
 
495
  $parts = array();
496
  foreach ($query as $q) {
497
  switch ($q[0]) {
498
  case "mediaType":
499
- $parts[] = implode(" ", array_map(array($this, "compileValue"), array_slice($q, 1)));
 
 
 
 
 
 
 
500
  break;
501
  case "mediaExp":
502
  if (isset($q[2])) {
@@ -507,6 +540,9 @@ class scssc {
507
  break;
508
  }
509
  }
 
 
 
510
  if (!empty($parts)) {
511
  if ($first) {
512
  $first = false;
@@ -520,6 +556,50 @@ class scssc {
520
  return $out;
521
  }
522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  // returns true if the value was something that could be imported
524
  protected function compileImport($rawPath, $out) {
525
  if ($rawPath[0] == "string") {
@@ -710,12 +790,12 @@ class scssc {
710
  $this->env->depth--;
711
  }
712
 
713
- if (!is_null($content)) {
714
  $content->scope = $callingScope;
715
  $this->setRaw(self::$namespaces["special"] . "content", $content);
716
  }
717
 
718
- if (!is_null($mixin->args)) {
719
  $this->applyArguments($mixin->args, $argValues);
720
  }
721
 
@@ -728,7 +808,7 @@ class scssc {
728
  break;
729
  case "mixin_content":
730
  $content = $this->get(self::$namespaces["special"] . "content");
731
- if (is_null($content)) {
732
  $this->throwError("Expected @content inside of mixin");
733
  }
734
 
@@ -862,7 +942,7 @@ class scssc {
862
  $out = $this->$fn($left, $right, $shouldEval);
863
  }
864
 
865
- if (!is_null($out)) {
866
  if ($unitChange && $out[0] == "number") {
867
  $out = $this->coerceUnit($out, $targetUnit);
868
  }
@@ -938,7 +1018,7 @@ class scssc {
938
  $ret = $this->compileChildren($func->children, $tmp);
939
  $this->popEnv();
940
 
941
- return is_null($ret) ? self::$defaultValue : $ret;
942
  }
943
 
944
  // built in function
@@ -1132,7 +1212,7 @@ class scssc {
1132
  return $this->toBool($left[1] < $right[1]);
1133
  }
1134
 
1135
- protected function toBool($thing) {
1136
  return $thing ? self::$true : self::$false;
1137
  }
1138
 
@@ -1180,7 +1260,7 @@ class scssc {
1180
 
1181
  return $h;
1182
  case "number":
1183
- return round($value[1], self::$numberPrecision) . $value[2];
1184
  case "string":
1185
  return $value[1] . $this->compileStringContent($value) . $value[1];
1186
  case "function":
@@ -1315,7 +1395,7 @@ class scssc {
1315
  }
1316
 
1317
  protected function multiplyMedia($env, $childQueries = null) {
1318
- if (is_null($env) ||
1319
  !empty($env->block->type) && $env->block->type != "media")
1320
  {
1321
  return $childQueries;
@@ -1345,11 +1425,11 @@ class scssc {
1345
 
1346
  // convert something to list
1347
  protected function coerceList($item, $delim = ",") {
1348
- if (!is_null($item) && $item[0] == "list") {
1349
  return $item;
1350
  }
1351
 
1352
- return array("list", $delim, is_null($item) ? array(): array($item));
1353
  }
1354
 
1355
  protected function applyArguments($argDef, $argValues) {
@@ -1451,28 +1531,26 @@ class scssc {
1451
  }
1452
  }
1453
 
1454
- // todo: this is bugged?
1455
  protected function setExisting($name, $value, $env = null) {
1456
- if (is_null($env)) $env = $this->getStoreEnv();
1457
 
1458
- if (isset($env->store[$name])) {
1459
  $env->store[$name] = $value;
1460
- } elseif (!is_null($env->parent)) {
1461
- $this->setExisting($name, $value, $env->parent);
1462
  } else {
1463
- $this->env->store[$name] = $value;
1464
  }
1465
  }
1466
 
1467
  protected function setRaw($name, $value) {
1468
- $this->env->store[$name] = $value;
 
1469
  }
1470
 
1471
- protected function get($name, $defaultValue = null, $env = null) {
1472
  $name = $this->normalizeName($name);
1473
 
1474
- if (is_null($env)) $env = $this->getStoreEnv();
1475
- if (is_null($defaultValue)) $defaultValue = self::$defaultValue;
1476
 
1477
  if (isset($env->store[$name])) {
1478
  return $env->store[$name];
@@ -1483,6 +1561,54 @@ class scssc {
1483
  return $defaultValue; // found nothing
1484
  }
1485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1486
  protected function popEnv() {
1487
  $env = $this->env;
1488
  $this->env = $this->env->parent;
@@ -1501,6 +1627,10 @@ class scssc {
1501
  $this->importPaths = (array)$path;
1502
  }
1503
 
 
 
 
 
1504
  public function setFormatter($formatterName) {
1505
  $this->formatter = $formatterName;
1506
  }
@@ -1534,7 +1664,7 @@ class scssc {
1534
  }
1535
 
1536
  // results the file path for an import url if it exists
1537
- protected function findImport($url) {
1538
  $urls = array();
1539
 
1540
  // for "normal" scss imports (ignore vanilla css and external requests)
@@ -1578,9 +1708,8 @@ class scssc {
1578
  $name = $this->normalizeName($name);
1579
  $libName = "lib_".$name;
1580
  $f = array($this, $libName);
1581
- $prototype = isset(self::$$libName) ? self::$$libName : null;
1582
-
1583
  if (is_callable($f)) {
 
1584
  $sorted = $this->sortArgs($prototype, $args);
1585
  foreach ($sorted as &$val) {
1586
  $val = $this->reduce($val, true);
@@ -1629,7 +1758,7 @@ class scssc {
1629
  }
1630
  }
1631
 
1632
- if (is_null($prototype)) return $posArgs;
1633
 
1634
  $finalArgs = array();
1635
  foreach ($prototype as $i => $names) {
@@ -1669,10 +1798,10 @@ class scssc {
1669
  case "keyword":
1670
  $name = $value[1];
1671
  if (isset(self::$cssColors[$name])) {
1672
- @list($r, $g, $b, $a) = explode(',', self::$cssColors[$name]);
1673
- return isset($a)
1674
- ? array('color', (int) $r, (int) $g, (int) $b, (int) $a)
1675
- : array('color', (int) $r, (int) $g, (int) $b);
1676
  }
1677
  return null;
1678
  }
@@ -1690,18 +1819,18 @@ class scssc {
1690
  return null;
1691
  }
1692
 
1693
- protected function assertList($value) {
1694
  if ($value[0] != "list")
1695
  $this->throwError("expecting list");
1696
  return $value;
1697
  }
1698
 
1699
- protected function assertColor($value) {
1700
  if ($color = $this->coerceColor($value)) return $color;
1701
  $this->throwError("expecting color");
1702
  }
1703
 
1704
- protected function assertNumber($value) {
1705
  if ($value[0] != "number")
1706
  $this->throwError("expecting number");
1707
  return $value[1];
@@ -1727,36 +1856,34 @@ class scssc {
1727
  return $c;
1728
  }
1729
 
1730
- function toHSL($red, $green, $blue) {
1731
- $r = $red / 255;
1732
- $g = $green / 255;
1733
- $b = $blue / 255;
1734
 
1735
- $min = min($r, $g, $b);
1736
- $max = max($r, $g, $b);
1737
- $d = $max - $min;
1738
- $l = ($min + $max) / 2;
1739
 
1740
  if ($min == $max) {
1741
  $s = $h = 0;
1742
  } else {
1743
- if ($l < 0.5)
1744
- $s = $d / (2 * $l);
 
 
1745
  else
1746
- $s = $d / (2 - 2 * $l);
1747
 
1748
- if ($r == $max)
1749
- $h = 60 * ($g - $b) / $d;
1750
- elseif ($g == $max)
1751
- $h = 60 * ($b - $r) / $d + 120;
1752
- elseif ($b == $max)
1753
- $h = 60 * ($r - $g) / $d + 240;
1754
  }
1755
 
1756
- return array('hsl', fmod($h, 360), $s * 100, $l * 100);
1757
  }
1758
 
1759
- function hueToRGB($m1, $m2, $h) {
1760
  if ($h < 0)
1761
  $h += 1;
1762
  elseif ($h > 1)
@@ -1775,7 +1902,7 @@ class scssc {
1775
  }
1776
 
1777
  // H from 0 to 360, S and L from 0 to 100
1778
- function toRGB($hue, $saturation, $lightness) {
1779
  if ($hue < 0) {
1780
  $hue += 360;
1781
  }
@@ -1800,7 +1927,7 @@ class scssc {
1800
  protected static $lib_if = array("condition", "if-true", "if-false");
1801
  protected function lib_if($args) {
1802
  list($cond,$t, $f) = $args;
1803
- if ($cond == self::$false) return $f;
1804
  return $t;
1805
  }
1806
 
@@ -1829,7 +1956,7 @@ class scssc {
1829
  "green", "blue", "alpha");
1830
  protected function lib_rgba($args) {
1831
  if ($color = $this->coerceColor($args[0])) {
1832
- $num = is_null($args[1]) ? $args[3] : $args[1];
1833
  $alpha = $this->assertNumber($num);
1834
  $color[4] = $alpha;
1835
  return $color;
@@ -1844,7 +1971,7 @@ class scssc {
1844
  $color = $this->assertColor($args[0]);
1845
 
1846
  foreach (array(1,2,3,7) as $i) {
1847
- if (!is_null($args[$i])) {
1848
  $val = $this->assertNumber($args[$i]);
1849
  $ii = $i == 7 ? 4 : $i; // alpha
1850
  $color[$ii] =
@@ -1852,10 +1979,10 @@ class scssc {
1852
  }
1853
  }
1854
 
1855
- if (!is_null($args[4]) || !is_null($args[5]) || !is_null($args[6])) {
1856
  $hsl = $this->toHSL($color[1], $color[2], $color[3]);
1857
  foreach (array(4,5,6) as $i) {
1858
- if (!is_null($args[$i])) {
1859
  $val = $this->assertNumber($args[$i]);
1860
  $hsl[$i - 3] = $this->$fn($hsl[$i - 3], $val, $i);
1861
  }
@@ -1973,7 +2100,7 @@ class scssc {
1973
  $first = $this->assertColor($first);
1974
  $second = $this->assertColor($second);
1975
 
1976
- if (is_null($weight)) {
1977
  $weight = 0.5;
1978
  } else {
1979
  $weight = $this->coercePercent($weight);
@@ -2248,7 +2375,7 @@ class scssc {
2248
  }
2249
 
2250
  protected function listSeparatorForJoin($list1, $sep) {
2251
- if (is_null($sep)) return $list1[1];
2252
  switch ($this->compileValue($sep)) {
2253
  case "comma":
2254
  return ",";
@@ -2355,7 +2482,7 @@ class scssc {
2355
  return array('string', '', array('counter(' . implode(',', $list) . ')'));
2356
  }
2357
 
2358
- protected function throwError($msg = null) {
2359
  if (func_num_args() > 1) {
2360
  $msg = call_user_func_array("sprintf", func_get_args());
2361
  }
@@ -2560,7 +2687,13 @@ class scss_parser {
2560
  static protected $commentMultiLeft = "/*";
2561
  static protected $commentMultiRight = "*/";
2562
 
2563
- function __construct($sourceName = null, $rootParser = true) {
 
 
 
 
 
 
2564
  $this->sourceName = $sourceName;
2565
  $this->rootParser = $rootParser;
2566
 
@@ -2580,27 +2713,38 @@ class scss_parser {
2580
  $operators)).')';
2581
  }
2582
 
2583
- function parse($buffer) {
2584
- $this->count = 0;
2585
- $this->env = null;
2586
- $this->inParens = false;
2587
- $this->pushBlock(null); // root block
 
 
 
 
 
 
 
2588
  $this->eatWhiteDefault = true;
2589
- $this->insertComments = true;
2590
-
2591
- $this->buffer = $buffer;
2592
 
 
2593
  $this->whitespace();
2594
- while (false !== $this->parseChunk());
2595
 
2596
- if ($this->count != strlen($this->buffer))
 
 
 
2597
  $this->throwParseError();
 
2598
 
2599
  if (!empty($this->env->parent)) {
2600
  $this->throwParseError("unclosed block");
2601
  }
2602
 
2603
- $this->env->isRoot = true;
 
2604
  return $this->env;
2605
  }
2606
 
@@ -2802,7 +2946,7 @@ class scss_parser {
2802
  }
2803
 
2804
  $last = $this->last();
2805
- if (!is_null($last) && $last[0] == "if") {
2806
  list(, $if) = $last;
2807
  if ($this->literal("@else")) {
2808
  if ($this->literal("{")) {
@@ -2956,7 +3100,7 @@ class scss_parser {
2956
  }
2957
 
2958
  protected function literal($what, $eatWhitespace = null) {
2959
- if (is_null($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
2960
 
2961
  // shortcut on single letter
2962
  if (!isset($what[1]) && isset($this->buffer[$this->count])) {
@@ -3104,13 +3248,21 @@ class scss_parser {
3104
  return false;
3105
  }
3106
 
3107
-
3108
- protected function valueList(&$out) {
3109
- return $this->genericList($out, "spaceList", ",");
 
 
 
 
 
 
 
3110
  }
3111
 
3112
- protected function spaceList(&$out) {
3113
- return $this->genericList($out, "expression");
 
3114
  }
3115
 
3116
  protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
@@ -3885,7 +4037,7 @@ class scss_parser {
3885
  }
3886
 
3887
  public function throwParseError($msg = "parse error", $count = null) {
3888
- $count = is_null($count) ? $this->count : $count;
3889
 
3890
  $line = $this->getLineNo($count);
3891
 
@@ -3920,7 +4072,7 @@ class scss_parser {
3920
  $token = null;
3921
 
3922
  $end = strpos($this->buffer, "\n", $this->count);
3923
- if ($end === false) {
3924
  $end = strlen($this->buffer);
3925
  }
3926
 
@@ -3950,7 +4102,7 @@ class scss_parser {
3950
 
3951
  // try to match something on head of buffer
3952
  protected function match($regex, &$out, $eatWhitespace = null) {
3953
- if (is_null($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
3954
 
3955
  $r = '/'.$regex.'/Ais';
3956
  if (preg_match($r, $this->buffer, $out, null, $this->count)) {
@@ -3978,7 +4130,7 @@ class scss_parser {
3978
  }
3979
 
3980
  protected function peek($regex, &$out, $from=null) {
3981
- if (is_null($from)) $from = $this->count;
3982
 
3983
  $r = '/'.$regex.'/Ais';
3984
  $result = preg_match($r, $this->buffer, $out, null, $from);
@@ -4186,6 +4338,32 @@ class scss_formatter_compressed extends scss_formatter {
4186
  }
4187
  }
4188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4189
  /**
4190
  * SCSS server
4191
  *
@@ -4283,6 +4461,26 @@ class scss_server {
4283
  return false;
4284
  }
4285
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4286
  /**
4287
  * Compile .scss file
4288
  *
@@ -4297,7 +4495,7 @@ class scss_server {
4297
  $elapsed = round((microtime(true) - $start), 4);
4298
 
4299
  $v = scssc::$VERSION;
4300
- $t = date('r');
4301
  $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
4302
 
4303
  file_put_contents($out, $css);
@@ -4308,29 +4506,60 @@ class scss_server {
4308
 
4309
  /**
4310
  * Compile requested scss and serve css. Outputs HTTP response.
 
 
4311
  */
4312
- public function serve() {
 
 
 
 
4313
  if ($input = $this->findInput()) {
4314
- $output = $this->cacheName($input);
4315
- header('Content-type: text/css');
4316
 
4317
  if ($this->needsCompile($input, $output)) {
4318
  try {
4319
- echo $this->compile($input, $output);
 
 
 
 
 
 
 
 
 
4320
  } catch (Exception $e) {
4321
- header('HTTP/1.1 500 Internal Server Error');
 
 
4322
  echo 'Parse error: ' . $e->getMessage() . "\n";
4323
  }
4324
- } else {
4325
- header('X-SCSS-Cache: true');
4326
- echo file_get_contents($output);
4327
  }
4328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4329
  return;
4330
  }
4331
 
4332
- header('HTTP/1.0 404 Not Found');
4333
- header('Content-type: text');
 
4334
  $v = scssc::$VERSION;
4335
  echo "/* INPUT NOT FOUND scss $v */\n";
4336
  }
@@ -4345,14 +4574,14 @@ class scss_server {
4345
  public function __construct($dir, $cacheDir=null, $scss=null) {
4346
  $this->dir = $dir;
4347
 
4348
- if (is_null($cacheDir)) {
4349
  $cacheDir = $this->join($dir, 'scss_cache');
4350
  }
4351
 
4352
  $this->cacheDir = $cacheDir;
4353
  if (!is_dir($this->cacheDir)) mkdir($this->cacheDir, 0755, true);
4354
 
4355
- if (is_null($scss)) {
4356
  $scss = new scssc();
4357
  $scss->setImportPaths($this->dir);
4358
  }
14
  * The scss compiler and parser.
15
  *
16
  * Converting SCSS to CSS is a three stage process. The incoming file is parsed
17
+ * by `scss_parser` into a syntax tree, then it is compiled into another tree
18
  * representing the CSS structure by `scssc`. The CSS tree is fed into a
19
+ * formatter, like `scss_formatter` which then outputs CSS as a string.
20
  *
21
  * During the first compile, all values are *reduced*, which means that their
22
  * types are brought to the lowest form before being dump as strings. This
31
  * evaluation context, such as all available mixins and variables at any given
32
  * time.
33
  *
34
+ * The `scss_parser` class is only concerned with parsing its input.
35
  *
36
+ * The `scss_formatter` takes a CSS tree, and dumps it to a formatted string,
37
  * handling things like indentation.
38
  */
39
 
43
  * @author Leaf Corcoran <leafot@gmail.com>
44
  */
45
  class scssc {
46
+ static public $VERSION = 'v0.0.12';
47
 
48
  static protected $operatorNames = array(
49
  '+' => "add",
67
  "function" => "^",
68
  );
69
 
 
70
  static protected $unitTable = array(
71
  "in" => array(
72
  "in" => 1,
89
  protected $importCache = array();
90
 
91
  protected $userFunctions = array();
92
+ protected $registeredVars = array();
93
+
94
+ protected $numberPrecision = 5;
95
 
96
  protected $formatter = "scss_formatter_nested";
97
 
98
+ /**
99
+ * Compile scss
100
+ *
101
+ * @param string $code
102
+ * @param string $name
103
+ *
104
+ * @return string
105
+ */
106
+ public function compile($code, $name = null)
107
+ {
108
+ $this->indentLevel = -1;
109
  $this->commentsSeen = array();
110
+ $this->extends = array();
111
+ $this->extendsMap = array();
112
+ $this->parsedFiles = array();
113
+ $this->env = null;
114
+ $this->scope = null;
115
 
116
  $locale = setlocale(LC_NUMERIC, 0);
117
  setlocale(LC_NUMERIC, "C");
118
 
 
119
  $this->parser = new scss_parser($name);
120
+
121
  $tree = $this->parser->parse($code);
122
 
123
  $this->formatter = new $this->formatter();
124
 
125
+ $this->pushEnv($tree);
126
+ $this->injectVariables($this->registeredVars);
 
127
  $this->compileRoot($tree);
128
+ $this->popEnv();
129
 
130
  $out = $this->formatter->format($this->scope);
131
 
132
  setlocale(LC_NUMERIC, $locale);
133
+
134
  return $out;
135
  }
136
 
201
  $rem = array_diff($single, $target);
202
 
203
  foreach ($origin as $j => $new) {
204
+ // prevent infinite loop when target extends itself
205
+ foreach ($new as $new_selector) {
206
+ if (!array_diff($single, $new_selector)) {
207
+ continue 2;
208
+ }
209
+ }
210
+
211
  $origin[$j][count($origin[$j]) - 1] = $this->combineSelectorSingle(end($new), $rem);
212
  }
213
 
320
  }
321
  }
322
 
323
+ protected function compileRoot($rootBlock)
324
+ {
325
+ $this->scope = $this->makeOutputBlock('root');
326
 
327
  $this->compileChildren($rootBlock->children, $this->scope);
328
  $this->flattenSelectors($this->scope);
 
 
329
  }
330
 
331
  protected function compileMedia($media) {
332
  $this->pushEnv($media);
 
333
 
334
+ $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env));
 
 
335
 
336
+ if (!empty($mediaQuery)) {
337
 
338
+ $this->scope = $this->makeOutputBlock("media", array($mediaQuery));
339
+
340
+ $parentScope = $this->mediaParent($this->scope);
341
+
342
+ $parentScope->children[] = $this->scope;
343
+
344
+ // top level properties in a media cause it to be wrapped
345
+ $needsWrap = false;
346
+ foreach ($media->children as $child) {
347
+ $type = $child[0];
348
+ if ($type !== 'block' && $type !== 'media' && $type !== 'directive') {
349
+ $needsWrap = true;
350
+ break;
351
+ }
352
  }
 
353
 
354
+ if ($needsWrap) {
355
+ $wrapped = (object)array(
356
+ "selectors" => array(),
357
+ "children" => $media->children
358
+ );
359
+ $media->children = array(array("block", $wrapped));
360
+ }
361
 
362
+ $this->compileChildren($media->children, $this->scope);
363
+
364
+ $this->scope = $this->scope->parent;
365
+ }
366
 
 
367
  $this->popEnv();
368
  }
369
 
509
  protected function compileChildren($stms, $out) {
510
  foreach ($stms as $stm) {
511
  $ret = $this->compileChild($stm, $out);
512
+ if (isset($ret)) return $ret;
513
  }
514
  }
515
 
517
  $out = "@media";
518
  $first = true;
519
  foreach ($queryList as $query){
520
+ $type = null;
521
  $parts = array();
522
  foreach ($query as $q) {
523
  switch ($q[0]) {
524
  case "mediaType":
525
+ if ($type) {
526
+ $type = $this->mergeMediaTypes($type, array_map(array($this, "compileValue"), array_slice($q, 1)));
527
+ if (empty($type)) { // merge failed
528
+ return null;
529
+ }
530
+ } else {
531
+ $type = array_map(array($this, "compileValue"), array_slice($q, 1));
532
+ }
533
  break;
534
  case "mediaExp":
535
  if (isset($q[2])) {
540
  break;
541
  }
542
  }
543
+ if ($type) {
544
+ array_unshift($parts, implode(' ', array_filter($type)));
545
+ }
546
  if (!empty($parts)) {
547
  if ($first) {
548
  $first = false;
556
  return $out;
557
  }
558
 
559
+ protected function mergeMediaTypes($type1, $type2) {
560
+ if (empty($type1)) {
561
+ return $type2;
562
+ }
563
+ if (empty($type2)) {
564
+ return $type1;
565
+ }
566
+ $m1 = '';
567
+ $t1 = '';
568
+ if (count($type1) > 1) {
569
+ $m1= strtolower($type1[0]);
570
+ $t1= strtolower($type1[1]);
571
+ } else {
572
+ $t1 = strtolower($type1[0]);
573
+ }
574
+ $m2 = '';
575
+ $t2 = '';
576
+ if (count($type2) > 1) {
577
+ $m2 = strtolower($type2[0]);
578
+ $t2 = strtolower($type2[1]);
579
+ } else {
580
+ $t2 = strtolower($type2[0]);
581
+ }
582
+ if (($m1 == 'not') ^ ($m2 == 'not')) {
583
+ if ($t1 == $t2) {
584
+ return null;
585
+ }
586
+ return array(
587
+ $m1 == 'not' ? $m2 : $m1,
588
+ $m1 == 'not' ? $t2 : $t1
589
+ );
590
+ } elseif ($m1 == 'not' && $m2 == 'not') {
591
+ # CSS has no way of representing "neither screen nor print"
592
+ if ($t1 != $t2) {
593
+ return null;
594
+ }
595
+ return array('not', $t1);
596
+ } elseif ($t1 != $t2) {
597
+ return null;
598
+ } else { // t1 == t2, neither m1 nor m2 are "not"
599
+ return array(empty($m1)? $m2 : $m1, $t1);
600
+ }
601
+ }
602
+
603
  // returns true if the value was something that could be imported
604
  protected function compileImport($rawPath, $out) {
605
  if ($rawPath[0] == "string") {
790
  $this->env->depth--;
791
  }
792
 
793
+ if (isset($content)) {
794
  $content->scope = $callingScope;
795
  $this->setRaw(self::$namespaces["special"] . "content", $content);
796
  }
797
 
798
+ if (isset($mixin->args)) {
799
  $this->applyArguments($mixin->args, $argValues);
800
  }
801
 
808
  break;
809
  case "mixin_content":
810
  $content = $this->get(self::$namespaces["special"] . "content");
811
+ if (!isset($content)) {
812
  $this->throwError("Expected @content inside of mixin");
813
  }
814
 
942
  $out = $this->$fn($left, $right, $shouldEval);
943
  }
944
 
945
+ if (isset($out)) {
946
  if ($unitChange && $out[0] == "number") {
947
  $out = $this->coerceUnit($out, $targetUnit);
948
  }
1018
  $ret = $this->compileChildren($func->children, $tmp);
1019
  $this->popEnv();
1020
 
1021
+ return !isset($ret) ? self::$defaultValue : $ret;
1022
  }
1023
 
1024
  // built in function
1212
  return $this->toBool($left[1] < $right[1]);
1213
  }
1214
 
1215
+ public function toBool($thing) {
1216
  return $thing ? self::$true : self::$false;
1217
  }
1218
 
1260
 
1261
  return $h;
1262
  case "number":
1263
+ return round($value[1], $this->numberPrecision) . $value[2];
1264
  case "string":
1265
  return $value[1] . $this->compileStringContent($value) . $value[1];
1266
  case "function":
1395
  }
1396
 
1397
  protected function multiplyMedia($env, $childQueries = null) {
1398
+ if (!isset($env) ||
1399
  !empty($env->block->type) && $env->block->type != "media")
1400
  {
1401
  return $childQueries;
1425
 
1426
  // convert something to list
1427
  protected function coerceList($item, $delim = ",") {
1428
+ if (isset($item) && $item[0] == "list") {
1429
  return $item;
1430
  }
1431
 
1432
+ return array("list", $delim, !isset($item) ? array(): array($item));
1433
  }
1434
 
1435
  protected function applyArguments($argDef, $argValues) {
1531
  }
1532
  }
1533
 
 
1534
  protected function setExisting($name, $value, $env = null) {
1535
+ if (!isset($env)) $env = $this->getStoreEnv();
1536
 
1537
+ if (isset($env->store[$name]) || !isset($env->parent)) {
1538
  $env->store[$name] = $value;
 
 
1539
  } else {
1540
+ $this->setExisting($name, $value, $env->parent);
1541
  }
1542
  }
1543
 
1544
  protected function setRaw($name, $value) {
1545
+ $env = $this->getStoreEnv();
1546
+ $env->store[$name] = $value;
1547
  }
1548
 
1549
+ public function get($name, $defaultValue = null, $env = null) {
1550
  $name = $this->normalizeName($name);
1551
 
1552
+ if (!isset($env)) $env = $this->getStoreEnv();
1553
+ if (!isset($defaultValue)) $defaultValue = self::$defaultValue;
1554
 
1555
  if (isset($env->store[$name])) {
1556
  return $env->store[$name];
1561
  return $defaultValue; // found nothing
1562
  }
1563
 
1564
+ protected function injectVariables(array $args)
1565
+ {
1566
+ if (empty($args)) {
1567
+ return;
1568
+ }
1569
+
1570
+ $parser = new scss_parser(__METHOD__, false);
1571
+
1572
+ foreach ($args as $name => $strValue) {
1573
+ if ($name[0] === '$') {
1574
+ $name = substr($name, 1);
1575
+ }
1576
+
1577
+ $parser->env = null;
1578
+ $parser->count = 0;
1579
+ $parser->buffer = (string) $strValue;
1580
+ $parser->inParens = false;
1581
+ $parser->eatWhiteDefault = true;
1582
+ $parser->insertComments = true;
1583
+
1584
+ if ( ! $parser->valueList($value)) {
1585
+ throw new Exception("failed to parse passed in variable $name: $strValue");
1586
+ }
1587
+
1588
+ $this->set($name, $value);
1589
+ }
1590
+ }
1591
+
1592
+ /**
1593
+ * Set variables
1594
+ *
1595
+ * @param array $variables
1596
+ */
1597
+ public function setVariables(array $variables)
1598
+ {
1599
+ $this->registeredVars = array_merge($this->registeredVars, $variables);
1600
+ }
1601
+
1602
+ /**
1603
+ * Unset variable
1604
+ *
1605
+ * @param string $name
1606
+ */
1607
+ public function unsetVariable($name)
1608
+ {
1609
+ unset($this->registeredVars[$name]);
1610
+ }
1611
+
1612
  protected function popEnv() {
1613
  $env = $this->env;
1614
  $this->env = $this->env->parent;
1627
  $this->importPaths = (array)$path;
1628
  }
1629
 
1630
+ public function setNumberPrecision($numberPrecision) {
1631
+ $this->numberPrecision = $numberPrecision;
1632
+ }
1633
+
1634
  public function setFormatter($formatterName) {
1635
  $this->formatter = $formatterName;
1636
  }
1664
  }
1665
 
1666
  // results the file path for an import url if it exists
1667
+ public function findImport($url) {
1668
  $urls = array();
1669
 
1670
  // for "normal" scss imports (ignore vanilla css and external requests)
1708
  $name = $this->normalizeName($name);
1709
  $libName = "lib_".$name;
1710
  $f = array($this, $libName);
 
 
1711
  if (is_callable($f)) {
1712
+ $prototype = isset(self::$$libName) ? self::$$libName : null;
1713
  $sorted = $this->sortArgs($prototype, $args);
1714
  foreach ($sorted as &$val) {
1715
  $val = $this->reduce($val, true);
1758
  }
1759
  }
1760
 
1761
+ if (!isset($prototype)) return $posArgs;
1762
 
1763
  $finalArgs = array();
1764
  foreach ($prototype as $i => $names) {
1798
  case "keyword":
1799
  $name = $value[1];
1800
  if (isset(self::$cssColors[$name])) {
1801
+ $rgba = explode(',', self::$cssColors[$name]);
1802
+ return isset($rgba[3])
1803
+ ? array('color', (int) $rgba[0], (int) $rgba[1], (int) $rgba[2], (int) $rgba[3])
1804
+ : array('color', (int) $rgba[0], (int) $rgba[1], (int) $rgba[2]);
1805
  }
1806
  return null;
1807
  }
1819
  return null;
1820
  }
1821
 
1822
+ public function assertList($value) {
1823
  if ($value[0] != "list")
1824
  $this->throwError("expecting list");
1825
  return $value;
1826
  }
1827
 
1828
+ public function assertColor($value) {
1829
  if ($color = $this->coerceColor($value)) return $color;
1830
  $this->throwError("expecting color");
1831
  }
1832
 
1833
+ public function assertNumber($value) {
1834
  if ($value[0] != "number")
1835
  $this->throwError("expecting number");
1836
  return $value[1];
1856
  return $c;
1857
  }
1858
 
1859
+ public function toHSL($red, $green, $blue) {
1860
+ $min = min($red, $green, $blue);
1861
+ $max = max($red, $green, $blue);
 
1862
 
1863
+ $l = $min + $max;
 
 
 
1864
 
1865
  if ($min == $max) {
1866
  $s = $h = 0;
1867
  } else {
1868
+ $d = $max - $min;
1869
+
1870
+ if ($l < 255)
1871
+ $s = $d / $l;
1872
  else
1873
+ $s = $d / (510 - $l);
1874
 
1875
+ if ($red == $max)
1876
+ $h = 60 * ($green - $blue) / $d;
1877
+ elseif ($green == $max)
1878
+ $h = 60 * ($blue - $red) / $d + 120;
1879
+ elseif ($blue == $max)
1880
+ $h = 60 * ($red - $green) / $d + 240;
1881
  }
1882
 
1883
+ return array('hsl', fmod($h, 360), $s * 100, $l / 5.1);
1884
  }
1885
 
1886
+ public function hueToRGB($m1, $m2, $h) {
1887
  if ($h < 0)
1888
  $h += 1;
1889
  elseif ($h > 1)
1902
  }
1903
 
1904
  // H from 0 to 360, S and L from 0 to 100
1905
+ public function toRGB($hue, $saturation, $lightness) {
1906
  if ($hue < 0) {
1907
  $hue += 360;
1908
  }
1927
  protected static $lib_if = array("condition", "if-true", "if-false");
1928
  protected function lib_if($args) {
1929
  list($cond,$t, $f) = $args;
1930
+ if (!$this->isTruthy($cond)) return $f;
1931
  return $t;
1932
  }
1933
 
1956
  "green", "blue", "alpha");
1957
  protected function lib_rgba($args) {
1958
  if ($color = $this->coerceColor($args[0])) {
1959
+ $num = !isset($args[1]) ? $args[3] : $args[1];
1960
  $alpha = $this->assertNumber($num);
1961
  $color[4] = $alpha;
1962
  return $color;
1971
  $color = $this->assertColor($args[0]);
1972
 
1973
  foreach (array(1,2,3,7) as $i) {
1974
+ if (isset($args[$i])) {
1975
  $val = $this->assertNumber($args[$i]);
1976
  $ii = $i == 7 ? 4 : $i; // alpha
1977
  $color[$ii] =
1979
  }
1980
  }
1981
 
1982
+ if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
1983
  $hsl = $this->toHSL($color[1], $color[2], $color[3]);
1984
  foreach (array(4,5,6) as $i) {
1985
+ if (isset($args[$i])) {
1986
  $val = $this->assertNumber($args[$i]);
1987
  $hsl[$i - 3] = $this->$fn($hsl[$i - 3], $val, $i);
1988
  }
2100
  $first = $this->assertColor($first);
2101
  $second = $this->assertColor($second);
2102
 
2103
+ if (!isset($weight)) {
2104
  $weight = 0.5;
2105
  } else {
2106
  $weight = $this->coercePercent($weight);
2375
  }
2376
 
2377
  protected function listSeparatorForJoin($list1, $sep) {
2378
+ if (!isset($sep)) return $list1[1];
2379
  switch ($this->compileValue($sep)) {
2380
  case "comma":
2381
  return ",";
2482
  return array('string', '', array('counter(' . implode(',', $list) . ')'));
2483
  }
2484
 
2485
+ public function throwError($msg = null) {
2486
  if (func_num_args() > 1) {
2487
  $msg = call_user_func_array("sprintf", func_get_args());
2488
  }
2687
  static protected $commentMultiLeft = "/*";
2688
  static protected $commentMultiRight = "*/";
2689
 
2690
+ /**
2691
+ * Constructor
2692
+ *
2693
+ * @param string $sourceName
2694
+ * @param boolean $rootParser
2695
+ */
2696
+ public function __construct($sourceName = null, $rootParser = true) {
2697
  $this->sourceName = $sourceName;
2698
  $this->rootParser = $rootParser;
2699
 
2713
  $operators)).')';
2714
  }
2715
 
2716
+ /**
2717
+ * Parser buffer
2718
+ *
2719
+ * @param string $buffer;
2720
+ *
2721
+ * @return \StdClass
2722
+ */
2723
+ public function parse($buffer)
2724
+ {
2725
+ $this->count = 0;
2726
+ $this->env = null;
2727
+ $this->inParens = false;
2728
  $this->eatWhiteDefault = true;
2729
+ $this->insertComments = true;
2730
+ $this->buffer = $buffer;
 
2731
 
2732
+ $this->pushBlock(null); // root block
2733
  $this->whitespace();
 
2734
 
2735
+ while (false !== $this->parseChunk())
2736
+ ;
2737
+
2738
+ if ($this->count != strlen($this->buffer)) {
2739
  $this->throwParseError();
2740
+ }
2741
 
2742
  if (!empty($this->env->parent)) {
2743
  $this->throwParseError("unclosed block");
2744
  }
2745
 
2746
+ $this->env->isRoot = true;
2747
+
2748
  return $this->env;
2749
  }
2750
 
2946
  }
2947
 
2948
  $last = $this->last();
2949
+ if (isset($last) && $last[0] == "if") {
2950
  list(, $if) = $last;
2951
  if ($this->literal("@else")) {
2952
  if ($this->literal("{")) {
3100
  }
3101
 
3102
  protected function literal($what, $eatWhitespace = null) {
3103
+ if (!isset($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
3104
 
3105
  // shortcut on single letter
3106
  if (!isset($what[1]) && isset($this->buffer[$this->count])) {
3248
  return false;
3249
  }
3250
 
3251
+ /**
3252
+ * Parse list
3253
+ *
3254
+ * @param string $out
3255
+ *
3256
+ * @return boolean
3257
+ */
3258
+ public function valueList(&$out)
3259
+ {
3260
+ return $this->genericList($out, 'spaceList', ',');
3261
  }
3262
 
3263
+ protected function spaceList(&$out)
3264
+ {
3265
+ return $this->genericList($out, 'expression');
3266
  }
3267
 
3268
  protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
4037
  }
4038
 
4039
  public function throwParseError($msg = "parse error", $count = null) {
4040
+ $count = !isset($count) ? $this->count : $count;
4041
 
4042
  $line = $this->getLineNo($count);
4043
 
4072
  $token = null;
4073
 
4074
  $end = strpos($this->buffer, "\n", $this->count);
4075
+ if ($end === false || $this->buffer[$end - 1] == '\\' || $this->buffer[$end - 2] == '\\' && $this->buffer[$end - 1] == "\r") {
4076
  $end = strlen($this->buffer);
4077
  }
4078
 
4102
 
4103
  // try to match something on head of buffer
4104
  protected function match($regex, &$out, $eatWhitespace = null) {
4105
+ if (!isset($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
4106
 
4107
  $r = '/'.$regex.'/Ais';
4108
  if (preg_match($r, $this->buffer, $out, null, $this->count)) {
4130
  }
4131
 
4132
  protected function peek($regex, &$out, $from=null) {
4133
+ if (!isset($from)) $from = $this->count;
4134
 
4135
  $r = '/'.$regex.'/Ais';
4136
  $result = preg_match($r, $this->buffer, $out, null, $from);
4338
  }
4339
  }
4340
 
4341
+ /**
4342
+ * SCSS minified formatter
4343
+ *
4344
+ * @author Mike DeWitt
4345
+ */
4346
+ class scss_formatter_minified extends scss_formatter {
4347
+ public $open = "{";
4348
+ public $tagSeparator = ",";
4349
+ public $assignSeparator = ":";
4350
+ public $break = "";
4351
+
4352
+ public function indentStr($n = 0) {
4353
+ return "";
4354
+ }
4355
+
4356
+ public function format($block) {
4357
+ ob_start();
4358
+ $this->block($block);
4359
+ $out = ob_get_clean();
4360
+
4361
+ $out = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $out);
4362
+
4363
+ return $out;
4364
+ }
4365
+ }
4366
+
4367
  /**
4368
  * SCSS server
4369
  *
4461
  return false;
4462
  }
4463
 
4464
+ /**
4465
+ * Get If-Modified-Since header from client request
4466
+ *
4467
+ * @return string
4468
+ */
4469
+ protected function getModifiedSinceHeader()
4470
+ {
4471
+ $modifiedSince = '';
4472
+
4473
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
4474
+ $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
4475
+
4476
+ if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) {
4477
+ $modifiedSince = substr($modifiedSince, 0, $semicolonPos);
4478
+ }
4479
+ }
4480
+
4481
+ return $modifiedSince;
4482
+ }
4483
+
4484
  /**
4485
  * Compile .scss file
4486
  *
4495
  $elapsed = round((microtime(true) - $start), 4);
4496
 
4497
  $v = scssc::$VERSION;
4498
+ $t = @date('r');
4499
  $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
4500
 
4501
  file_put_contents($out, $css);
4506
 
4507
  /**
4508
  * Compile requested scss and serve css. Outputs HTTP response.
4509
+ *
4510
+ * @param string $salt Prefix a string to the filename for creating the cache name hash
4511
  */
4512
+ public function serve($salt = '') {
4513
+ $protocol = isset($_SERVER['SERVER_PROTOCOL'])
4514
+ ? $_SERVER['SERVER_PROTOCOL']
4515
+ : 'HTTP/1.0';
4516
+
4517
  if ($input = $this->findInput()) {
4518
+ $output = $this->cacheName($salt . $input);
 
4519
 
4520
  if ($this->needsCompile($input, $output)) {
4521
  try {
4522
+ $css = $this->compile($input, $output);
4523
+
4524
+ $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT';
4525
+
4526
+ header('Last-Modified: ' . $lastModified);
4527
+ header('Content-type: text/css');
4528
+
4529
+ echo $css;
4530
+
4531
+ return;
4532
  } catch (Exception $e) {
4533
+ header($protocol . ' 500 Internal Server Error');
4534
+ header('Content-type: text/plain');
4535
+
4536
  echo 'Parse error: ' . $e->getMessage() . "\n";
4537
  }
 
 
 
4538
  }
4539
 
4540
+ header('X-SCSS-Cache: true');
4541
+ header('Content-type: text/css');
4542
+
4543
+ $modifiedSince = $this->getModifiedSinceHeader();
4544
+ $mtime = filemtime($output);
4545
+
4546
+ if (@strtotime($modifiedSince) === $mtime) {
4547
+ header($protocol . ' 304 Not Modified');
4548
+
4549
+ return;
4550
+ }
4551
+
4552
+ $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
4553
+ header('Last-Modified: ' . $lastModified);
4554
+
4555
+ echo file_get_contents($output);
4556
+
4557
  return;
4558
  }
4559
 
4560
+ header($protocol . ' 404 Not Found');
4561
+ header('Content-type: text/plain');
4562
+
4563
  $v = scssc::$VERSION;
4564
  echo "/* INPUT NOT FOUND scss $v */\n";
4565
  }
4574
  public function __construct($dir, $cacheDir=null, $scss=null) {
4575
  $this->dir = $dir;
4576
 
4577
+ if (!isset($cacheDir)) {
4578
  $cacheDir = $this->join($dir, 'scss_cache');
4579
  }
4580
 
4581
  $this->cacheDir = $cacheDir;
4582
  if (!is_dir($this->cacheDir)) mkdir($this->cacheDir, 0755, true);
4583
 
4584
+ if (!isset($scss)) {
4585
  $scss = new scssc();
4586
  $scss->setImportPaths($this->dir);
4587
  }
wp-scss.php CHANGED
@@ -1,9 +1,9 @@
1
- <?php
2
  /**
3
  * Plugin Name: WP-SCSS
4
  * Plugin URI: https://github.com/ConnectThink/WP-SCSS
5
  * Description: Compiles scss files live on wordpress.
6
- * Version: 1.0.0
7
  * Author: Connect Think
8
  * Author URI: http://connectthink.com
9
  * License: GPLv3
@@ -30,7 +30,7 @@
30
 
31
  // Plugin Paths
32
  if (!defined('WPSCSS_THEME_DIR'))
33
- define('WPSCSS_THEME_DIR', ABSPATH . 'wp-content/themes/' . get_stylesheet());
34
 
35
  if (!defined('WPSCSS_PLUGIN_NAME'))
36
  define('WPSCSS_PLUGIN_NAME', trim(dirname(plugin_basename(__FILE__)), '/'));
@@ -46,7 +46,7 @@ if (!defined('WPSCSS_VERSION_KEY'))
46
  define('WPSCSS_VERSION_KEY', 'wpscss_version');
47
 
48
  if (!defined('WPSCSS_VERSION_NUM'))
49
- define('WPSCSS_VERSION_NUM', '1.0.0');
50
 
51
  // Add version to options table
52
  add_option(WPSCSS_VERSION_KEY, WPSCSS_VERSION_NUM);
@@ -54,8 +54,8 @@ add_option(WPSCSS_VERSION_KEY, WPSCSS_VERSION_NUM);
54
 
55
  /*
56
  * 2. REQUIRE DEPENDANCIES
57
- *
58
- * scssphp - scss compiler
59
  * class-wp-scss
60
  * options.php - settings for plugin page
61
  */
@@ -67,9 +67,9 @@ include_once WPSCSS_PLUGIN_DIR . '/options.php'; // Options page class
67
 
68
  /**
69
  * 3. REGISTER SETTINGS
70
- *
71
  * Instantiate Options Page
72
- * Create link on plugin page to settings page
73
  */
74
 
75
  if( is_admin() ) {
@@ -93,9 +93,9 @@ function wpscss_plugin_action_links($links, $file) {
93
  }
94
 
95
 
96
- /**
97
  * 4. PLUGIN SETTINGS
98
- *
99
  * Pull settings from options table
100
  * Scrub empty fields or directories that don't exists
101
  * Assign settings via settings array to pass to object
@@ -105,7 +105,7 @@ $wpscss_options = get_option( 'wpscss_options' );
105
  $scss_dir_setting = $wpscss_options['scss_dir'];
106
  $css_dir_setting = $wpscss_options['css_dir'];
107
 
108
- // Checks if directories are empty
109
  if( $scss_dir_setting == false || $css_dir_setting == false ) {
110
  function wpscss_settings_error() {
111
  echo '<div class="error">
@@ -130,9 +130,9 @@ if( $scss_dir_setting == false || $css_dir_setting == false ) {
130
  $wpscss_settings = array(
131
  'scss_dir' => WPSCSS_THEME_DIR . $scss_dir_setting,
132
  'css_dir' => WPSCSS_THEME_DIR . $css_dir_setting,
133
- 'compiling' => $wpscss_options['compiling_options'],
134
  'errors' => $wpscss_options['errors'],
135
- 'enqueue' => $wpscss_options['enqueue']
136
  );
137
 
138
 
@@ -149,82 +149,112 @@ $wpscss_compiler = new Wp_Scss(
149
  $wpscss_settings['compiling']
150
  );
151
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
- if ( $wpscss_compiler->needs_compiling() ) {
 
 
 
 
 
 
 
 
154
  $wpscss_compiler->compile();
155
  }
156
 
157
-
158
  /**
159
  * 6. HANDLE COMPILING ERRORS
160
  *
161
- * First block handles print errors to front end.
162
  * This adds a small style block the header to help errors get noticed
163
  *
164
- * Second block handles print errors to log file.
165
- * After the file gets over 1MB it does a purge and deletes the first
166
- * half of entries in the file.
167
  */
168
  $log_file = $wpscss_compiler->scss_dir.'error_log.log';
169
 
170
- if ( !is_admin() && $wpscss_settings['errors'] === 'show' && count($wpscss_compiler->compile_errors) > 0) {
171
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  echo '<div class="scss_errors"><pre>';
173
  echo '<h6 style="margin: 15px 0;">Sass Compiling Error</h6>';
174
-
175
- foreach( $wpscss_compiler->compile_errors as $error) {
176
  echo '<p class="sass_error">';
177
- echo '<strong>'. $error['file'] .'</strong> <br/><em>"'. $error['message'] .'"</em>';
178
  echo '<p class="sass_error">';
179
  }
180
 
181
  echo '</pre></div>';
182
 
183
- function wpscss_error_styles() {
184
- echo
185
- '<style>
186
- .scss_errors {
187
- position: fixed;
188
- top: 0px;
189
- z-index: 99999;
190
- width: 100%;
191
- }
192
- .scss_errors pre {
193
- background: #f5f5f5;
194
- border-left: 5px solid #DD3D36;
195
- box-shadow: 0 2px 3px rgba(51,51,51, .4);
196
- color: #666;
197
- font-family: monospace;
198
- font-size: 14px;
199
- margin: 20px 0;
200
- overflow: auto;
201
- padding: 20px;
202
- white-space: pre;
203
- white-space: pre-wrap;
204
- word-wrap: break-word;
205
- }
206
- </style>';
207
- }
208
  add_action('wp_print_styles', 'wpscss_error_styles');
209
-
210
- } else { // Hide errors and print them to a log file.
211
- foreach ($wpscss_compiler->compile_errors as $error) {
212
- $error_string = date('m/d/y g:i:s', time()) .': ';
213
- $error_string .= $error['file'] .' - '. $error['message'] . PHP_EOL;
214
- file_put_contents($log_file, $error_string, FILE_APPEND);
215
- $error_string = "";
216
- }
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  // Clean out log file if it get's too large
220
- if ( file_exists($log_file) ) {
221
- if ( filesize($log_file) > 1000000) {
222
- $log_contents = file_get_contents($log_file);
223
- $log_arr = explode("\n", $log_contents);
224
- $new_contents_arr = array_slice($log_arr, count($log_arr)/2);
225
- $new_contents = implode(PHP_EOL, $new_contents_arr) . 'LOG FILE CLEANED ' . date('n/j/y g:i:s', time());
226
- file_put_contents($log_file, $new_contents);
227
- }
 
 
228
  }
229
 
230
 
@@ -235,7 +265,7 @@ if ( file_exists($log_file) ) {
235
  if ( $wpscss_settings['enqueue'] == '1' ) {
236
  function wpscss_enqueue_styles() {
237
  global $wpscss_compiler, $wpscss_options;
238
- $wpscss_compiler->enqueue_files($wpscss_options['css_dir']);
239
  }
240
  add_action('wp_enqueue_scripts', 'wpscss_enqueue_styles', 50);
241
  }
1
+ <?php
2
  /**
3
  * Plugin Name: WP-SCSS
4
  * Plugin URI: https://github.com/ConnectThink/WP-SCSS
5
  * Description: Compiles scss files live on wordpress.
6
+ * Version: 1.1.9
7
  * Author: Connect Think
8
  * Author URI: http://connectthink.com
9
  * License: GPLv3
30
 
31
  // Plugin Paths
32
  if (!defined('WPSCSS_THEME_DIR'))
33
+ define('WPSCSS_THEME_DIR', get_stylesheet_directory());
34
 
35
  if (!defined('WPSCSS_PLUGIN_NAME'))
36
  define('WPSCSS_PLUGIN_NAME', trim(dirname(plugin_basename(__FILE__)), '/'));
46
  define('WPSCSS_VERSION_KEY', 'wpscss_version');
47
 
48
  if (!defined('WPSCSS_VERSION_NUM'))
49
+ define('WPSCSS_VERSION_NUM', '1.1.9');
50
 
51
  // Add version to options table
52
  add_option(WPSCSS_VERSION_KEY, WPSCSS_VERSION_NUM);
54
 
55
  /*
56
  * 2. REQUIRE DEPENDANCIES
57
+ *
58
+ * scssphp - scss compiler
59
  * class-wp-scss
60
  * options.php - settings for plugin page
61
  */
67
 
68
  /**
69
  * 3. REGISTER SETTINGS
70
+ *
71
  * Instantiate Options Page
72
+ * Create link on plugin page to settings page
73
  */
74
 
75
  if( is_admin() ) {
93
  }
94
 
95
 
96
+ /**
97
  * 4. PLUGIN SETTINGS
98
+ *
99
  * Pull settings from options table
100
  * Scrub empty fields or directories that don't exists
101
  * Assign settings via settings array to pass to object
105
  $scss_dir_setting = $wpscss_options['scss_dir'];
106
  $css_dir_setting = $wpscss_options['css_dir'];
107
 
108
+ // Checks if directories are empty
109
  if( $scss_dir_setting == false || $css_dir_setting == false ) {
110
  function wpscss_settings_error() {
111
  echo '<div class="error">
130
  $wpscss_settings = array(
131
  'scss_dir' => WPSCSS_THEME_DIR . $scss_dir_setting,
132
  'css_dir' => WPSCSS_THEME_DIR . $css_dir_setting,
133
+ 'compiling' => $wpscss_options['compiling_options'],
134
  'errors' => $wpscss_options['errors'],
135
+ 'enqueue' => isset($wpscss_options['enqueue']) ? $wpscss_options['enqueue'] : 0
136
  );
137
 
138
 
149
  $wpscss_settings['compiling']
150
  );
151
 
152
+ //wp_scss_needs_compiling() needs to be run as wp_head-action to make it possible
153
+ //for themes to set variables and decide if the style needs compiling
154
+ function wp_scss_needs_compiling() {
155
+ global $wpscss_compiler;
156
+ $needs_compiling = apply_filters('wp_scss_needs_compiling', $wpscss_compiler->needs_compiling());
157
+ if ( $needs_compiling ) {
158
+ wp_scss_compile();
159
+ wpscss_handle_errors();
160
+ }
161
+ }
162
+
163
+ add_action('wp_head', 'wp_scss_needs_compiling');
164
 
165
+ function wp_scss_compile() {
166
+ global $wpscss_compiler;
167
+ $variables = apply_filters('wp_scss_variables', array());
168
+ foreach ($variables as $variable_key => $variable_value) {
169
+ if (strlen(trim($variable_value)) == 0) {
170
+ unset($variables[$variable_key]);
171
+ }
172
+ }
173
+ $wpscss_compiler->set_variables($variables);
174
  $wpscss_compiler->compile();
175
  }
176
 
 
177
  /**
178
  * 6. HANDLE COMPILING ERRORS
179
  *
180
+ * First block handles print errors to front end.
181
  * This adds a small style block the header to help errors get noticed
182
  *
183
+ * Second block handles print errors to log file.
184
+ * After the file gets over 1MB it does a purge and deletes the first
185
+ * half of entries in the file.
186
  */
187
  $log_file = $wpscss_compiler->scss_dir.'error_log.log';
188
 
189
+ function wpscss_error_styles() {
190
+ echo
191
+ '<style>
192
+ .scss_errors {
193
+ position: fixed;
194
+ top: 0px;
195
+ z-index: 99999;
196
+ width: 100%;
197
+ }
198
+ .scss_errors pre {
199
+ background: #f5f5f5;
200
+ border-left: 5px solid #DD3D36;
201
+ box-shadow: 0 2px 3px rgba(51,51,51, .4);
202
+ color: #666;
203
+ font-family: monospace;
204
+ font-size: 14px;
205
+ margin: 20px 0;
206
+ overflow: auto;
207
+ padding: 20px;
208
+ white-space: pre;
209
+ white-space: pre-wrap;
210
+ word-wrap: break-word;
211
+ }
212
+ </style>';
213
+ }
214
+
215
+ function wpscss_settings_show_errors($errors) {
216
  echo '<div class="scss_errors"><pre>';
217
  echo '<h6 style="margin: 15px 0;">Sass Compiling Error</h6>';
218
+
219
+ foreach( $errors as $error) {
220
  echo '<p class="sass_error">';
221
+ echo '<strong>'. $error['file'] .'</strong> <br/><em>"'. $error['message'] .'"</em>';
222
  echo '<p class="sass_error">';
223
  }
224
 
225
  echo '</pre></div>';
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  add_action('wp_print_styles', 'wpscss_error_styles');
 
 
 
 
 
 
 
 
228
  }
229
 
230
+ function wpscss_handle_errors() {
231
+ global $wpscss_settings, $log_file, $wpscss_compiler;
232
+ // Show to logged in users: All the methods for checking user login are set up later in the WP flow, so this only checks that there is a cookie
233
+ if ( !is_admin() && $wpscss_settings['errors'] === 'show-logged-in' && !empty($_COOKIE[LOGGED_IN_COOKIE]) && count($wpscss_compiler->compile_errors) > 0) {
234
+ wpscss_settings_show_errors($wpscss_compiler->compile_errors);
235
+ // Show in the header to anyone
236
+ } else if ( !is_admin() && $wpscss_settings['errors'] === 'show' && count($wpscss_compiler->compile_errors) > 0) {
237
+ wpscss_settings_show_errors($wpscss_compiler->compile_errors);
238
+ } else { // Hide errors and print them to a log file.
239
+ foreach ($wpscss_compiler->compile_errors as $error) {
240
+ $error_string = date('m/d/y g:i:s', time()) .': ';
241
+ $error_string .= $error['file'] .' - '. $error['message'] . PHP_EOL;
242
+ file_put_contents($log_file, $error_string, FILE_APPEND);
243
+ $error_string = "";
244
+ }
245
+ }
246
+
247
  // Clean out log file if it get's too large
248
+ if ( file_exists($log_file) ) {
249
+ if ( filesize($log_file) > 1000000) {
250
+ $log_contents = file_get_contents($log_file);
251
+ $log_arr = explode("\n", $log_contents);
252
+ $new_contents_arr = array_slice($log_arr, count($log_arr)/2);
253
+ $new_contents = implode(PHP_EOL, $new_contents_arr) . 'LOG FILE CLEANED ' . date('n/j/y g:i:s', time());
254
+ file_put_contents($log_file, $new_contents);
255
+ }
256
+ }
257
+
258
  }
259
 
260
 
265
  if ( $wpscss_settings['enqueue'] == '1' ) {
266
  function wpscss_enqueue_styles() {
267
  global $wpscss_compiler, $wpscss_options;
268
+ $wpscss_compiler->enqueue_files($wpscss_options['css_dir']);
269
  }
270
  add_action('wp_enqueue_scripts', 'wpscss_enqueue_styles', 50);
271
  }