Version Description
Download this release
Release Info
Developer | oncletom |
Plugin | WP-LESS |
Version | 1.0 |
Comparing to | |
See all releases |
Version 1.0
- bootstrap.php +25 -0
- lib/Configuration.class.php +25 -0
- lib/Exception.class.php +12 -0
- lib/Plugin.class.php +101 -0
- lib/Stylesheet.class.php +203 -0
- lib/vendor/lessphp/LICENSE +180 -0
- lib/vendor/lessphp/README +66 -0
- lib/vendor/lessphp/docs/docs.html +342 -0
- lib/vendor/lessphp/docs/style.css +207 -0
- lib/vendor/lessphp/lessc.inc.php +1238 -0
- lib/vendor/lessphp/plessc +112 -0
- lib/vendor/plugin-toolkit/BaseConfiguration.class.php +255 -0
- lib/vendor/plugin-toolkit/BasePlugin.class.php +102 -0
- readme.txt +79 -0
- screenshot-1.png +0 -0
bootstrap.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin Name: WP LESS
|
4 |
+
Description: LESS extends CSS with variables, mixins, operations and nested rules. This plugin magically parse all your <code>*.less</code> files queued with <code>wp_enqueue_style</code> in WordPress.
|
5 |
+
Author: Oncle Tom
|
6 |
+
Version: 1.0
|
7 |
+
Author URI: http://case.oncle-tom.net/
|
8 |
+
Plugin URI: http://wordpress.org/extend/plugins/wp-less/
|
9 |
+
|
10 |
+
This plugin is released under version 3 of the GPL:
|
11 |
+
http://www.opensource.org/licenses/gpl-3.0.html
|
12 |
+
*/
|
13 |
+
|
14 |
+
require dirname(__FILE__).'/lib/Plugin.class.php';
|
15 |
+
$WPLessPlugin = WPPluginToolkitPlugin::create('WPLess', __FILE__);
|
16 |
+
|
17 |
+
if (!is_admin())
|
18 |
+
{
|
19 |
+
do_action('wp-less_init', $WPLessPlugin);
|
20 |
+
add_action('wp_print_styles', array($WPLessPlugin, 'processStylesheets'));
|
21 |
+
}
|
22 |
+
else
|
23 |
+
{
|
24 |
+
do_action('wp-less_init_admin', $WPLessPlugin);
|
25 |
+
}
|
lib/Configuration.class.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class WPLessConfiguration extends WPPluginToolkitConfiguration
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* Refers to the name of the plugin
|
7 |
+
*/
|
8 |
+
const UNIX_NAME = 'wp-less';
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Refers to the version of the plugin
|
12 |
+
*/
|
13 |
+
const VERSION = '1.0';
|
14 |
+
|
15 |
+
|
16 |
+
protected function configure()
|
17 |
+
{
|
18 |
+
$this->configureOptions();
|
19 |
+
}
|
20 |
+
|
21 |
+
protected function configureOptions()
|
22 |
+
{
|
23 |
+
|
24 |
+
}
|
25 |
+
}
|
lib/Exception.class.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Basic Exception
|
4 |
+
*
|
5 |
+
* @author oncletom
|
6 |
+
* @package wp-less
|
7 |
+
* @subpackage lib
|
8 |
+
*/
|
9 |
+
class WPLessException extends Exception
|
10 |
+
{
|
11 |
+
|
12 |
+
}
|
lib/Plugin.class.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if (!class_exists('BasePlugin'))
|
3 |
+
{
|
4 |
+
require dirname(__FILE__).'/vendor/plugin-toolkit/BasePlugin.class.php';
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WP LESS Plugin class
|
9 |
+
*
|
10 |
+
* @author oncletom
|
11 |
+
* @package wp-less
|
12 |
+
* @subpackage lib
|
13 |
+
*/
|
14 |
+
class WPLessPlugin extends WPPluginToolkitPlugin
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @static
|
18 |
+
* @var Pattern used to match stylesheet files to process them as pure CSS
|
19 |
+
*/
|
20 |
+
public static $match_pattern = '/\.less$/U';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Process all stylesheets to compile just in time
|
24 |
+
*
|
25 |
+
* @author oncletom
|
26 |
+
* @since 1.0
|
27 |
+
* @version 1.0
|
28 |
+
*/
|
29 |
+
public function processStylesheets()
|
30 |
+
{
|
31 |
+
$styles = $this->getQueuedStylesToProcess();
|
32 |
+
$wp_styles = $this->getStyles();
|
33 |
+
$upload_dir = $this->configuration->getUploadDir();
|
34 |
+
|
35 |
+
if (empty($styles))
|
36 |
+
{
|
37 |
+
return;
|
38 |
+
}
|
39 |
+
|
40 |
+
if (!wp_mkdir_p($upload_dir))
|
41 |
+
{
|
42 |
+
throw new WPLessException(sprintf('The upload dir folder (`%s`) is not writable from %s.', $upload_dir, get_class($this)));
|
43 |
+
}
|
44 |
+
|
45 |
+
WPLessStylesheet::$upload_dir = $this->configuration->getUploadDir();
|
46 |
+
WPLessStylesheet::$upload_uri = $this->configuration->getUploadUrl();
|
47 |
+
|
48 |
+
foreach ($styles as $style_id)
|
49 |
+
{
|
50 |
+
$stylesheet = new WPLessStylesheet($wp_styles->registered[$style_id]);
|
51 |
+
|
52 |
+
if ($stylesheet->hasToCompile())
|
53 |
+
{
|
54 |
+
$stylesheet->save();
|
55 |
+
}
|
56 |
+
|
57 |
+
$wp_styles->registered[$style_id]->src = $stylesheet->getTargetUri();
|
58 |
+
}
|
59 |
+
|
60 |
+
do_action('wp-less_plugin_process_stylesheets', $styles);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Find any style to process
|
65 |
+
*
|
66 |
+
* @author oncletom
|
67 |
+
* @since 1.0
|
68 |
+
* @version 1.0
|
69 |
+
* @return array styles to process
|
70 |
+
*/
|
71 |
+
protected function getQueuedStylesToProcess()
|
72 |
+
{
|
73 |
+
$wp_styles = $this->getStyles();
|
74 |
+
$to_process = array();
|
75 |
+
|
76 |
+
foreach ($wp_styles->queue as $style_id)
|
77 |
+
{
|
78 |
+
if (preg_match(self::$match_pattern, $wp_styles->registered[$style_id]->src))
|
79 |
+
{
|
80 |
+
$to_process[] = $style_id;
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
return apply_filters('wp-less_get_queued_styles_to_process', $to_process);
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Returns WordPress Styles manager
|
89 |
+
*
|
90 |
+
* @author oncletom
|
91 |
+
* @uses WP_Styles
|
92 |
+
* @since 1.0
|
93 |
+
* @version 1.0
|
94 |
+
* @return WP_Styles styles instance
|
95 |
+
*/
|
96 |
+
public function getStyles()
|
97 |
+
{
|
98 |
+
global $wp_styles;
|
99 |
+
return $wp_styles;
|
100 |
+
}
|
101 |
+
}
|
lib/Stylesheet.class.php
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
require dirname(__FILE__).'/vendor/lessphp/lessc.inc.php';
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Stylesheet management
|
6 |
+
*
|
7 |
+
* @author oncletom
|
8 |
+
* @package wp-less
|
9 |
+
* @subpackage lib
|
10 |
+
*/
|
11 |
+
class WPLessStylesheet
|
12 |
+
{
|
13 |
+
protected $compiler,
|
14 |
+
$stylesheet;
|
15 |
+
|
16 |
+
protected $source_path,
|
17 |
+
$source_uri,
|
18 |
+
$target_path,
|
19 |
+
$target_uri;
|
20 |
+
|
21 |
+
public static $upload_dir,
|
22 |
+
$upload_uri;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Constructs the object, paths and all
|
26 |
+
*
|
27 |
+
* @author oncletom
|
28 |
+
* @since 1.0
|
29 |
+
* @version 1.0
|
30 |
+
* @throws WPLessException if something is not properly configured
|
31 |
+
* @param _WP_Dependency $stylesheet
|
32 |
+
*/
|
33 |
+
public function __construct(_WP_Dependency $stylesheet)
|
34 |
+
{
|
35 |
+
$this->stylesheet = $stylesheet;
|
36 |
+
|
37 |
+
if (!self::$upload_dir || !self::$upload_uri)
|
38 |
+
{
|
39 |
+
throw new WPLessException('You must configure `upload_dir` and `upload_uri` static attributes before constructing this object.');
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->configurePath();
|
43 |
+
do_action('wp-less_stylesheet_construct', $this);
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Returns the computed path for a given dependency
|
48 |
+
*
|
49 |
+
* @author oncletom
|
50 |
+
* @since 1.0
|
51 |
+
* @version 1.0
|
52 |
+
* @return string
|
53 |
+
*/
|
54 |
+
public function computeTargetPath()
|
55 |
+
{
|
56 |
+
$target_path = preg_replace('#^'.get_theme_root_uri().'#U', '', $this->stylesheet->src);
|
57 |
+
$target_path = preg_replace('/.less$/U', '', $target_path);
|
58 |
+
|
59 |
+
$target_path .= '.css';
|
60 |
+
|
61 |
+
return apply_filters('wp-less_stylesheet_compute_target_path', $target_path);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Configure paths for the stylesheet
|
66 |
+
* Since this moment, everything is configured to be usable
|
67 |
+
*
|
68 |
+
* @protected
|
69 |
+
* @author oncletom
|
70 |
+
* @since 1.0
|
71 |
+
* @version 1.0
|
72 |
+
*/
|
73 |
+
protected function configurePath()
|
74 |
+
{
|
75 |
+
$target_file = $this->computeTargetPath();
|
76 |
+
|
77 |
+
$this->source_path = WP_CONTENT_DIR.preg_replace('#^'.WP_CONTENT_URL.'#U', '', $this->stylesheet->src);
|
78 |
+
$this->source_uri = $this->stylesheet->src;
|
79 |
+
$this->target_path = self::$upload_dir.$target_file;
|
80 |
+
$this->target_uri = self::$upload_uri.$target_file;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Returns source content (CSS to parse)
|
85 |
+
*
|
86 |
+
* @author oncletom
|
87 |
+
* @since 1.0
|
88 |
+
* @version 1.0
|
89 |
+
* @return string
|
90 |
+
*/
|
91 |
+
public function getSourceContent()
|
92 |
+
{
|
93 |
+
return apply_filters('wp-less_stylesheet_source_content', file_get_contents($this->source_path));
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Returns source path
|
98 |
+
*
|
99 |
+
* @author oncletom
|
100 |
+
* @since 1.0
|
101 |
+
* @version 1.0
|
102 |
+
* @return string
|
103 |
+
*/
|
104 |
+
public function getSourcePath()
|
105 |
+
{
|
106 |
+
return $this->source_path;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Returns source URI
|
111 |
+
*
|
112 |
+
* @author oncletom
|
113 |
+
* @since 1.0
|
114 |
+
* @version 1.0
|
115 |
+
* @return string
|
116 |
+
*/
|
117 |
+
public function getSourceUri()
|
118 |
+
{
|
119 |
+
return $this->source_uri;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Returns parsed CSS
|
124 |
+
*
|
125 |
+
* @author oncletom
|
126 |
+
* @since 1.0
|
127 |
+
* @version 1.0
|
128 |
+
* @return string
|
129 |
+
*/
|
130 |
+
public function getTargetContent()
|
131 |
+
{
|
132 |
+
if (!$this->compiler)
|
133 |
+
{
|
134 |
+
$this->compiler = new lessc($this->getSourcePath());
|
135 |
+
}
|
136 |
+
|
137 |
+
return apply_filters('wp-less_stylesheet_target_content', $this->compiler->parse());
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Returns target path
|
142 |
+
*
|
143 |
+
* @author oncletom
|
144 |
+
* @since 1.0
|
145 |
+
* @version 1.0
|
146 |
+
* @return string
|
147 |
+
*/
|
148 |
+
public function getTargetPath()
|
149 |
+
{
|
150 |
+
return $this->target_path;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Returns target URI
|
155 |
+
*
|
156 |
+
* @author oncletom
|
157 |
+
* @since 1.0
|
158 |
+
* @version 1.0
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
public function getTargetUri()
|
162 |
+
{
|
163 |
+
return $this->target_uri;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Tells if compilation is needed
|
168 |
+
*
|
169 |
+
* @author oncletom
|
170 |
+
* @since 1.0
|
171 |
+
* @version 1.0
|
172 |
+
* @return boolean
|
173 |
+
*/
|
174 |
+
public function hasToCompile()
|
175 |
+
{
|
176 |
+
return !file_exists($this->getTargetPath()) || filemtime($this->getSourcePath()) > filemtime($this->getTargetPath());
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Save the current stylesheet as a parsed css file
|
181 |
+
*
|
182 |
+
* @author oncletom
|
183 |
+
* @since 1.0
|
184 |
+
* @version 1.0
|
185 |
+
* @throws Exception in case of parsing went bad
|
186 |
+
*/
|
187 |
+
public function save()
|
188 |
+
{
|
189 |
+
wp_mkdir_p(dirname($this->getTargetPath()));
|
190 |
+
|
191 |
+
try
|
192 |
+
{
|
193 |
+
do_action('wp-less_stylesheet_save_pre', $this);
|
194 |
+
lessc::ccompile($this->getSourcePath(), $this->getTargetPath());
|
195 |
+
chmod($this->getTargetPath(), 0666);
|
196 |
+
do_action('wp-less_stylesheet_save_post', $this);
|
197 |
+
}
|
198 |
+
catch(Exception $e)
|
199 |
+
{
|
200 |
+
wp_die($e->getMessage());
|
201 |
+
}
|
202 |
+
}
|
203 |
+
}
|
lib/vendor/lessphp/LICENSE
ADDED
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
Apache License
|
3 |
+
Version 2.0, January 2004
|
4 |
+
http://www.apache.org/licenses/
|
5 |
+
|
6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
7 |
+
|
8 |
+
1. Definitions.
|
9 |
+
|
10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
12 |
+
|
13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
14 |
+
the copyright owner that is granting the License.
|
15 |
+
|
16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
17 |
+
other entities that control, are controlled by, or are under common
|
18 |
+
control with that entity. For the purposes of this definition,
|
19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
20 |
+
direction or management of such entity, whether by contract or
|
21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
23 |
+
|
24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
25 |
+
exercising permissions granted by this License.
|
26 |
+
|
27 |
+
"Source" form shall mean the preferred form for making modifications,
|
28 |
+
including but not limited to software source code, documentation
|
29 |
+
source, and configuration files.
|
30 |
+
|
31 |
+
"Object" form shall mean any form resulting from mechanical
|
32 |
+
transformation or translation of a Source form, including but
|
33 |
+
not limited to compiled object code, generated documentation,
|
34 |
+
and conversions to other media types.
|
35 |
+
|
36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
37 |
+
Object form, made available under the License, as indicated by a
|
38 |
+
copyright notice that is included in or attached to the work
|
39 |
+
(an example is provided in the Appendix below).
|
40 |
+
|
41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
42 |
+
form, that is based on (or derived from) the Work and for which the
|
43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
45 |
+
of this License, Derivative Works shall not include works that remain
|
46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
47 |
+
the Work and Derivative Works thereof.
|
48 |
+
|
49 |
+
"Contribution" shall mean any work of authorship, including
|
50 |
+
the original version of the Work and any modifications or additions
|
51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
55 |
+
means any form of electronic, verbal, or written communication sent
|
56 |
+
to the Licensor or its representatives, including but not limited to
|
57 |
+
communication on electronic mailing lists, source code control systems,
|
58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
60 |
+
excluding communication that is conspicuously marked or otherwise
|
61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
62 |
+
|
63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
65 |
+
subsequently incorporated within the Work.
|
66 |
+
|
67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
72 |
+
Work and such Derivative Works in Source or Object form.
|
73 |
+
|
74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
77 |
+
(except as stated in this section) patent license to make, have made,
|
78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
79 |
+
where such license applies only to those patent claims licensable
|
80 |
+
by such Contributor that are necessarily infringed by their
|
81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
83 |
+
institute patent litigation against any entity (including a
|
84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
85 |
+
or a Contribution incorporated within the Work constitutes direct
|
86 |
+
or contributory patent infringement, then any patent licenses
|
87 |
+
granted to You under this License for that Work shall terminate
|
88 |
+
as of the date such litigation is filed.
|
89 |
+
|
90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
91 |
+
Work or Derivative Works thereof in any medium, with or without
|
92 |
+
modifications, and in Source or Object form, provided that You
|
93 |
+
meet the following conditions:
|
94 |
+
|
95 |
+
(a) You must give any other recipients of the Work or
|
96 |
+
Derivative Works a copy of this License; and
|
97 |
+
|
98 |
+
(b) You must cause any modified files to carry prominent notices
|
99 |
+
stating that You changed the files; and
|
100 |
+
|
101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
102 |
+
that You distribute, all copyright, patent, trademark, and
|
103 |
+
attribution notices from the Source form of the Work,
|
104 |
+
excluding those notices that do not pertain to any part of
|
105 |
+
the Derivative Works; and
|
106 |
+
|
107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
108 |
+
distribution, then any Derivative Works that You distribute must
|
109 |
+
include a readable copy of the attribution notices contained
|
110 |
+
within such NOTICE file, excluding those notices that do not
|
111 |
+
pertain to any part of the Derivative Works, in at least one
|
112 |
+
of the following places: within a NOTICE text file distributed
|
113 |
+
as part of the Derivative Works; within the Source form or
|
114 |
+
documentation, if provided along with the Derivative Works; or,
|
115 |
+
within a display generated by the Derivative Works, if and
|
116 |
+
wherever such third-party notices normally appear. The contents
|
117 |
+
of the NOTICE file are for informational purposes only and
|
118 |
+
do not modify the License. You may add Your own attribution
|
119 |
+
notices within Derivative Works that You distribute, alongside
|
120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
121 |
+
that such additional attribution notices cannot be construed
|
122 |
+
as modifying the License.
|
123 |
+
|
124 |
+
You may add Your own copyright statement to Your modifications and
|
125 |
+
may provide additional or different license terms and conditions
|
126 |
+
for use, reproduction, or distribution of Your modifications, or
|
127 |
+
for any such Derivative Works as a whole, provided Your use,
|
128 |
+
reproduction, and distribution of the Work otherwise complies with
|
129 |
+
the conditions stated in this License.
|
130 |
+
|
131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
133 |
+
by You to the Licensor shall be under the terms and conditions of
|
134 |
+
this License, without any additional terms or conditions.
|
135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
136 |
+
the terms of any separate license agreement you may have executed
|
137 |
+
with Licensor regarding such Contributions.
|
138 |
+
|
139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
141 |
+
except as required for reasonable and customary use in describing the
|
142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
143 |
+
|
144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
145 |
+
agreed to in writing, Licensor provides the Work (and each
|
146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
148 |
+
implied, including, without limitation, any warranties or conditions
|
149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
151 |
+
appropriateness of using or redistributing the Work and assume any
|
152 |
+
risks associated with Your exercise of permissions under this License.
|
153 |
+
|
154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
155 |
+
whether in tort (including negligence), contract, or otherwise,
|
156 |
+
unless required by applicable law (such as deliberate and grossly
|
157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
158 |
+
liable to You for damages, including any direct, indirect, special,
|
159 |
+
incidental, or consequential damages of any character arising as a
|
160 |
+
result of this License or out of the use or inability to use the
|
161 |
+
Work (including but not limited to damages for loss of goodwill,
|
162 |
+
work stoppage, computer failure or malfunction, or any and all
|
163 |
+
other commercial damages or losses), even if such Contributor
|
164 |
+
has been advised of the possibility of such damages.
|
165 |
+
|
166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
169 |
+
or other liability obligations and/or rights consistent with this
|
170 |
+
License. However, in accepting such obligations, You may act only
|
171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
172 |
+
of any other Contributor, and only if You agree to indemnify,
|
173 |
+
defend, and hold each Contributor harmless for any liability
|
174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
175 |
+
of your accepting any such warranty or additional liability.
|
176 |
+
|
177 |
+
END OF TERMS AND CONDITIONS
|
178 |
+
|
179 |
+
Copyright 2009 Leaf Corcoran
|
180 |
+
|
lib/vendor/lessphp/README
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
lessphp v0.1.5
|
2 |
+
http://leafo.net/lessphp
|
3 |
+
========================================
|
4 |
+
|
5 |
+
lessphp is a compiler for LESS written in php.
|
6 |
+
|
7 |
+
|
8 |
+
How to use in your php project
|
9 |
+
========================================
|
10 |
+
|
11 |
+
Copy less.inc.php to your include directory and include it into your project.
|
12 |
+
|
13 |
+
There are a few ways to interface with the compiler. The easiest is to have it
|
14 |
+
compile a LESS file when the page is requested. The static function
|
15 |
+
less::ccompile, checked compile, will compile the input LESS file only when it
|
16 |
+
is newer than the output file.
|
17 |
+
|
18 |
+
try {
|
19 |
+
lessc::ccompile('input.less', 'output.css');
|
20 |
+
catch (exception $ex) {
|
21 |
+
exit($ex->getMessage());
|
22 |
+
}
|
23 |
+
|
24 |
+
Note that all failures with lessc are reported through exceptions.
|
25 |
+
If you need more control you can make your own instance of lessc.
|
26 |
+
|
27 |
+
$input = 'mystyle.less';
|
28 |
+
|
29 |
+
$lc = new lessc($input);
|
30 |
+
|
31 |
+
try {
|
32 |
+
file_put_contents('mystyle.css', $lc->parse());
|
33 |
+
} catch (exception $ex) { ... }
|
34 |
+
|
35 |
+
In addition to loading from file, you can also parse from a string like so:
|
36 |
+
|
37 |
+
$lc = new lessc();
|
38 |
+
$lesscode = 'body { ... }';
|
39 |
+
$out = $lc->parse($lesscode);
|
40 |
+
|
41 |
+
|
42 |
+
|
43 |
+
How to use from the command line
|
44 |
+
========================================
|
45 |
+
|
46 |
+
An additional script has been included to use the compiler from the command
|
47 |
+
line. In the simplest invocation, you specify an input file and the compiled
|
48 |
+
css is written to standard out:
|
49 |
+
|
50 |
+
~> plessc input.less > output.css
|
51 |
+
|
52 |
+
Using the -r flag, you can specify LESS code directly as an argument or, if
|
53 |
+
the argument is left off, from standard in:
|
54 |
+
|
55 |
+
~> plessc -r "my less code here"
|
56 |
+
|
57 |
+
Finally, by using the -w flag you can watch a specified input file and have it
|
58 |
+
compile as needed to the output file
|
59 |
+
|
60 |
+
~> plessc -w input-file output-file
|
61 |
+
|
62 |
+
Errors from watch mode are written to standard out.
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
|
lib/vendor/lessphp/docs/docs.html
ADDED
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2 |
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3 |
+
|
4 |
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5 |
+
<head>
|
6 |
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
7 |
+
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
|
8 |
+
<link rel="alternate" type="application/rss+xml" title="lessphp changelog feed" href="http://leafo.net/lessphp/feed/" />
|
9 |
+
<title>Documentation - lessphp</title>
|
10 |
+
</head>
|
11 |
+
<body>
|
12 |
+
|
13 |
+
<h1>Documentation - lessphp</h1>
|
14 |
+
<div class="content">
|
15 |
+
|
16 |
+
<ul id="nav">
|
17 |
+
<li><a href="#start">Getting Started</a></li>
|
18 |
+
<li><a href="#language">The Language</a>
|
19 |
+
<ul><li><a href="#vars">Abstract Properties (Variables)</a></li></ul>
|
20 |
+
<ul><li><a href="#pvalues">Property Values & Expressions</a></li></ul>
|
21 |
+
<ul><li><a href="#nested">Nested Blocks</a></li></ul>
|
22 |
+
<ul><li><a href="#mixins">Mixins & Namespace Access</a></li></ul>
|
23 |
+
<ul><li><a href="#ablocks">Abstract Blocks</a></li></ul>
|
24 |
+
<ul><li><a href="#args">Mixin Arguments</a></li></ul>
|
25 |
+
<ul><li><a href="#import">Import Statement</a></li></ul>
|
26 |
+
<ul><li><a href="#strings">String Mixins</a></li></ul>
|
27 |
+
<ul><li><a href="#misc">Miscellaneous</a></li></ul>
|
28 |
+
</li>
|
29 |
+
<li><a href="#interface">The Interface</a></li>
|
30 |
+
</ul>
|
31 |
+
|
32 |
+
<p class="important">This documentation is specific to the
|
33 |
+
<a href="http://leafo.net/lessphp/">php version of LESS, <strong>lessphp</strong></a>. <strong>lessphp</strong> is
|
34 |
+
a superset of LESS classic. Everything you can do in LESS classic will work
|
35 |
+
in lessphp but there are additional features unique to the php version.
|
36 |
+
</p>
|
37 |
+
|
38 |
+
<a name="start"></a>
|
39 |
+
<h2>Getting Started</h2>
|
40 |
+
<p>Download the latest version of <strong>lessphp</strong> <a href="http://leafo.net/lessphp/">here</a>.</p>
|
41 |
+
|
42 |
+
</div>
|
43 |
+
|
44 |
+
<a name="language"></a>
|
45 |
+
<h1>The Language</h1>
|
46 |
+
<div class="content">
|
47 |
+
<br />
|
48 |
+
<p><strong>lessphp</strong> is a data description language built on top of CSS. The two major components of the language are
|
49 |
+
blocks and property-value pairs. A block is a scope for a collection of property-value pairs.</p>
|
50 |
+
|
51 |
+
<p>Blocks and properties have special characteristics depending on how they are named.</p>
|
52 |
+
|
53 |
+
<p>It is important to realize that a block's state does not change over time. When a block is defined, all of its properties are constant. The best way to demonstrate this is to look at the following LESS snippet:</p>
|
54 |
+
|
55 |
+
<pre class="code">
|
56 |
+
body {
|
57 |
+
color: @val;
|
58 |
+
@val: blue;
|
59 |
+
}</pre>
|
60 |
+
|
61 |
+
<p>Because the state of the block is not changing over time, but constant after its creation,
|
62 |
+
the color property is printed with the value <code>blue</code>.
|
63 |
+
|
64 |
+
<a name="vars"></a>
|
65 |
+
<h2>Abstract Properties (Variables)</h2>
|
66 |
+
<p>Abstract properties are defined with a name starting with <code>@</code>. These types of properties are special in two ways: first, they are not included in the output of the compiler. Second, they can be easily accessed in the values of other properties just by writing their name. If a property is referenced but can not be found, a blank string is returned.</p>
|
67 |
+
<p>As a LESS programmer, it means that you can define a collection of hidden values that can be referenced in other locations.</p>
|
68 |
+
|
69 |
+
<pre class="code">
|
70 |
+
@mycolor: #fff;
|
71 |
+
body {
|
72 |
+
color: @mycolor;
|
73 |
+
}
|
74 |
+
pre {
|
75 |
+
color: @mycolor;
|
76 |
+
}</pre>
|
77 |
+
|
78 |
+
<p>Also take note that you can define abstract properties in the global scope.</p>
|
79 |
+
|
80 |
+
<a name="pvalues"></a>
|
81 |
+
<h2>Property Values & Expressions</h2>
|
82 |
+
<p>All properties have at least one value. The value is a list of expressions separated by spaces or commas.
|
83 |
+
An expression is any CSS value with optional mathematical operators applied to it. The operators only function
|
84 |
+
with number and color value types.<p>
|
85 |
+
|
86 |
+
<p>Operations on units will keep the unit of the rightmost value,
|
87 |
+
unless it is unit-less, then then leftmost unit will be kept for the result. See the following examples below.<p>
|
88 |
+
|
89 |
+
<pre class="code">body {
|
90 |
+
color: #001 + #abc; // evaulates to #aabbdd:
|
91 |
+
width: 3430px + 22; // evaluates to 3452px;
|
92 |
+
margin: (1.5em / 2) + 2px; // evaluates to 2.85px;
|
93 |
+
margin: 20 + 2px; // evaluates to 22px;
|
94 |
+
}</pre>
|
95 |
+
|
96 |
+
<p>It is important to notice that in the example above the output will print the margin property twice.
|
97 |
+
A single property name can hold more than one value; older values are not overwritten.</p>
|
98 |
+
|
99 |
+
<a name="nested"></a>
|
100 |
+
<h2>Nested Blocks</h2>
|
101 |
+
<p>Blocks can be nested inside of each other in order to achieve the same effect as listing out the
|
102 |
+
path of identifiers in CSS. This can help to increase organization of your LESS code and reduce
|
103 |
+
the amount of repeated tag names you have to type.</p>
|
104 |
+
|
105 |
+
<pre class="code">body {
|
106 |
+
a {
|
107 |
+
color: green
|
108 |
+
:hover {
|
109 |
+
color: blue;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
}</pre>
|
113 |
+
|
114 |
+
|
115 |
+
<a name="mixins"></a>
|
116 |
+
<h2>Mixins & Namespace Access</h2>
|
117 |
+
|
118 |
+
<p>Any block can be "mixed" into the current block. This means that all properties and blocks
|
119 |
+
in the target block are brought into the current block. It is possible to achieve the same effect
|
120 |
+
with just CSS and HTML, but there are some additional features discussed below that make it worthwhile
|
121 |
+
to utilize LESS-style mixing.</p>
|
122 |
+
<p>The syntax is as follows:<p>
|
123 |
+
|
124 |
+
<pre class="code">
|
125 |
+
.myclass {
|
126 |
+
@fonts: Helvetica, Arial;
|
127 |
+
margin: 1.0em;
|
128 |
+
line-spacing: 150%;
|
129 |
+
|
130 |
+
a {
|
131 |
+
background-color: black;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
pre {
|
136 |
+
.myclass;
|
137 |
+
font-family: @fonts; // uses the mixed in variable
|
138 |
+
}
|
139 |
+
|
140 |
+
div.notice {
|
141 |
+
.myclass;
|
142 |
+
}</pre>
|
143 |
+
|
144 |
+
<p>If you want to mix in a specific block within another block you can use the <code>></code> namespace operator.
|
145 |
+
Additionally you can pull out specific values of properties from blocks using the <code>[ ]</code> operator.
|
146 |
+
|
147 |
+
<pre class="code">// using .myclass from above
|
148 |
+
li {
|
149 |
+
.myclass > a; // just mix in properties from 'a' tag in .myclass
|
150 |
+
fonts: .myclass[@fonts];
|
151 |
+
padding: .myclass['margin'];
|
152 |
+
}</pre>
|
153 |
+
|
154 |
+
|
155 |
+
<a name="ablocks"></a>
|
156 |
+
<h2>Abstract Blocks</h2>
|
157 |
+
|
158 |
+
<p>Abstract blocks are like any other blocks, but their names start with a <code>@</code>. Like abstract variables, they are not
|
159 |
+
included in the compiler's output. This allows you to do utilize mixins without adding any unused blocks to the output.
|
160 |
+
You can also use an abstract class to define a package of invisible, but extractable blocks and properties.<p>
|
161 |
+
|
162 |
+
<pre class="code">@mypackage {
|
163 |
+
.coolColors {
|
164 |
+
color: pink;
|
165 |
+
background-color: green;
|
166 |
+
}
|
167 |
+
|
168 |
+
.hotColors {
|
169 |
+
color: red;
|
170 |
+
background-color: orange;
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
p {
|
175 |
+
@mypackage > .coolColors;
|
176 |
+
}
|
177 |
+
|
178 |
+
div {
|
179 |
+
@mypackage; // inserts both classes into this block
|
180 |
+
}</pre>
|
181 |
+
|
182 |
+
<p>It is possible to give an abstract block the same name as an abstract property; their names will not collide.
|
183 |
+
Block names and property names exist in different spaces.</p>
|
184 |
+
|
185 |
+
|
186 |
+
<a name="args"></a>
|
187 |
+
<h2>Mixin Arguments</h2>
|
188 |
+
<p>All blocks have the option of taking argument lists, and the arguments can have default values.</p>
|
189 |
+
|
190 |
+
<pre class="code">.myclass(@width: 200px;@radius) {
|
191 |
+
border-radius: @radius;
|
192 |
+
width: @width
|
193 |
+
}
|
194 |
+
|
195 |
+
@color(@color:red) { color: @color; } // this is valid
|
196 |
+
|
197 |
+
.first {
|
198 |
+
.myclass(300px; 2em);
|
199 |
+
@color(blue):
|
200 |
+
}
|
201 |
+
|
202 |
+
.second {
|
203 |
+
.myclass(;4px); // blank argument takes default value
|
204 |
+
}</pre>
|
205 |
+
|
206 |
+
<a name="import"></a>
|
207 |
+
<h2>Import Statement</h2>
|
208 |
+
<p>If you have multiple LESS files, you can combine them into a single CSS file during compilation using the
|
209 |
+
<code>@import</code> directive. LESS import uses the same syntax as CSS import. If it can find the file specified
|
210 |
+
then it will pull it into the compiler in place of the statement. If the file can't be found, the statement is
|
211 |
+
printed to the output. The following are all valid:</p>
|
212 |
+
|
213 |
+
<pre class="code">@import "file";
|
214 |
+
@import 'file.less';
|
215 |
+
@import url("file");
|
216 |
+
@import url('file');
|
217 |
+
@import url(file); </pre>
|
218 |
+
|
219 |
+
<p>Note that if it fails to find a file it will append <code>.less</code> to the filename and try again.
|
220 |
+
This means <code>@import 'somefile'</code> and <code>@import 'somefile.less'</code> will
|
221 |
+
both import the file <code>somefile.less</code>.
|
222 |
+
|
223 |
+
|
224 |
+
<a name="strings"></a>
|
225 |
+
<h2>String Mixins</h2>
|
226 |
+
<p>It is possible to access the value of an abstract property from a string using <code>{ }</code> operators.</p>
|
227 |
+
|
228 |
+
<pre class="code">@image_folder: darktheme;
|
229 |
+
.header {
|
230 |
+
background-image: url(/images/{@image_folder}/header.png);
|
231 |
+
}</pre>
|
232 |
+
|
233 |
+
<p>The <code>{ }</code> syntax will also work in any string value type, which is any text
|
234 |
+
wrapped in single or double quotes</p>
|
235 |
+
|
236 |
+
|
237 |
+
<a name="misc"></a>
|
238 |
+
<h2>Miscellaneous</h2>
|
239 |
+
<p>As mentioned before, all properties hold constant data. This includes abstract values. While it may be convenient to
|
240 |
+
think of them as variables, they don't have the ability to vary. For convenience, some tricks were implemented to
|
241 |
+
make self referencing properties evaluate in order as if they had changing state. Consider the following statement:</p>
|
242 |
+
|
243 |
+
<pre class="code">
|
244 |
+
@what: 1;
|
245 |
+
body {
|
246 |
+
@what: @what + 1;
|
247 |
+
@what: @what + 2;
|
248 |
+
.class {
|
249 |
+
@what: @what + 1;
|
250 |
+
width: @what;
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
#something {
|
255 |
+
@what: 200;
|
256 |
+
body > .class;
|
257 |
+
}</pre>
|
258 |
+
|
259 |
+
<p>In the output, <code>body .class</code> has width set to 5, and <code>#something</code> has width set to 201. It appears from that result that
|
260 |
+
the property <code>@what</code> is being incremented. But, as mentioned above, the name <code>@what</code> stores a series of unchanging values.
|
261 |
+
The values use delayed evaluation, so each value holds an equation.</p>
|
262 |
+
|
263 |
+
<p>What this means is that the values of the properties don't change while the block is parsed, only additional property-value pairs are added
|
264 |
+
to the block's definition.
|
265 |
+
When the block's is compiled into CSS, the equations and property references are solved using the data in the scope. Technically, it is
|
266 |
+
ambiguous what value to use for referencing a variable, because the single name can store multiple values.<p>
|
267 |
+
|
268 |
+
<p>The approach taken in <strong>lessphp</strong> is to use the most recent value. This gives the appearance that the block is parsed line by line.
|
269 |
+
In order to prevent infinite loops when a variable references itself, a single value in the set of all values for a given name can only be used once in
|
270 |
+
the chain of dereferencing.</p>
|
271 |
+
|
272 |
+
|
273 |
+
|
274 |
+
|
275 |
+
<pre class="code bnf" style="display: none"><b>property-value</b> <u>:=</u> <b>inner-list</b> , <b>property-value</b> <u>|</u> <b>inner-list</b>
|
276 |
+
<b>inner-list</b> <u>:=</u> <b>expression</b> <b>inner-list</b> <u>|</u> <b>expression</b>
|
277 |
+
<b>expression</b> <u>:=</u> <b>simple-value</b> <b>operator</b> <b>expression</b> <u>|</u> <b>simple-value</b>
|
278 |
+
<b>operator</b> <u>:=</u> + <u>|</u> - <u>|</u> * <u>|</u> /
|
279 |
+
<b>simple-value</b> <u>:=</u> <b>keyword</b> <u>|</u> <b>string</b> <u>|</u> <b>unit</b> <u>|</u>
|
280 |
+
</pre>
|
281 |
+
|
282 |
+
</div>
|
283 |
+
|
284 |
+
<a name="interface"></a>
|
285 |
+
<h1>The PHP Interface</h1><br />
|
286 |
+
<div class="content">
|
287 |
+
<p>There are a few ways to interface with the compiler. The easiest is to have it
|
288 |
+
compile a LESS file when the page is requested. The static function
|
289 |
+
<code>less::ccompile</code>, checked compile, will compile the inputed LESS file only when it
|
290 |
+
is newer than the output file.<p>
|
291 |
+
|
292 |
+
<pre class="code">
|
293 |
+
<span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
|
294 |
+
|
295 |
+
<span class="Statement">try</span> <span class="Delimiter">{</span>
|
296 |
+
lessc<span class="Operator">::</span>ccompile<span class="Delimiter">(</span>'<span class="String">input.less</span>', '<span class="String">out.css</span>'<span class="Delimiter">)</span>;
|
297 |
+
<span class="Delimiter">}</span> <span class="Statement">catch</span> <span class="Delimiter">(</span><span class="Function">exception</span> <span class="Operator">$</span><span class="Identifier">ex</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
|
298 |
+
<span class="Statement">exit</span><span class="Delimiter">(</span>'<span class="String">lessc fatal error:<br /></span>'<span class="Operator">.</span><span class="Operator">$</span><span class="Identifier">ex</span><span class="Type">-></span>getMessage<span class="Delimiter">())</span>;
|
299 |
+
<span class="Delimiter">}</span>
|
300 |
+
</pre>
|
301 |
+
|
302 |
+
<br />
|
303 |
+
<p>Note that all failures with lessc are reported through exceptions.
|
304 |
+
If you need more control then you can make your own instance of <code>lessc</code>.</p>
|
305 |
+
<pre class="code">
|
306 |
+
<span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
|
307 |
+
|
308 |
+
<span class="Operator">$</span><span class="Identifier">less</span> <span class="Operator">=</span> <span class="PreProc">new</span> lessc<span class="Delimiter">(</span>'<span class="String">path/to/style.less</span>'<span class="Delimiter">)</span>;
|
309 |
+
<span class="Function">file_put_contents</span><span class="Delimiter">(</span>'<span class="String">path/to/style.css</span>', <span class="Operator">$</span><span class="Identifier">less</span><span class="Type">-></span>parse<span class="Delimiter">())</span>;
|
310 |
+
</pre>
|
311 |
+
|
312 |
+
<br />
|
313 |
+
<p>In addition to loading from a file, you can also parse from a string like so:</p>
|
314 |
+
<pre class="code">
|
315 |
+
<span class="PreProc">require</span> '<span class="String">lessc.inc.php</span>';
|
316 |
+
|
317 |
+
<span class="Operator">$</span><span class="Identifier">less</span> <span class="Operator">=</span> <span class="PreProc">new</span> lessc<span class="Delimiter">()</span>;
|
318 |
+
<span class="Operator">$</span><span class="Identifier">style</span> <span class="Operator">=</span> '<span class="String"><style type="text/css"></span>'<span class="Operator">.</span>
|
319 |
+
<span class="Operator">$</span><span class="Identifier">less</span><span class="Type">-></span>parse<span class="Delimiter">(</span>'<span class="String">.block { padding: 3 + 4px }</span>'<span class="Delimiter">)</span><span class="Operator">.</span>
|
320 |
+
'<span class="String"></style></span>';
|
321 |
+
</pre>
|
322 |
+
|
323 |
+
|
324 |
+
<h2>Import Directoy</h2>
|
325 |
+
<p>When using the <code>@import</code> directive, the compiler searches for files
|
326 |
+
in the <code>$importDir</code> public property of <code>lessc</code>. If the compiler is loaded with a filename
|
327 |
+
(either in the constructor or using <code>ccompile</code>) it will extract the directory and set that as the
|
328 |
+
import directory</p>
|
329 |
+
|
330 |
+
|
331 |
+
|
332 |
+
<br />
|
333 |
+
<br />
|
334 |
+
<hr />
|
335 |
+
<p class="foot">
|
336 |
+
<a href="http://leafo.net/lessphp/">http://leafo.net/lessphp</a> - Last updated August 6th 2009</p>
|
337 |
+
|
338 |
+
</div>
|
339 |
+
|
340 |
+
</body>
|
341 |
+
</html>
|
342 |
+
|
lib/vendor/lessphp/docs/style.css
ADDED
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
body {
|
3 |
+
margin: 0;
|
4 |
+
padding: 0;
|
5 |
+
font: 14px verdana, sans-serif;
|
6 |
+
}
|
7 |
+
|
8 |
+
#content, .content {
|
9 |
+
width: 966px;
|
10 |
+
margin-bottom: 1.0em;
|
11 |
+
}
|
12 |
+
|
13 |
+
h1,h2 {
|
14 |
+
padding: 0.5em;
|
15 |
+
font-family: calibri, helvetica, sans-serif;
|
16 |
+
margin: 0;
|
17 |
+
}
|
18 |
+
|
19 |
+
h1 {
|
20 |
+
background: #E7F3FB;
|
21 |
+
color: #182F51;
|
22 |
+
}
|
23 |
+
|
24 |
+
h2 {
|
25 |
+
color: #3AA6EE;
|
26 |
+
color: #1C97E8;
|
27 |
+
}
|
28 |
+
|
29 |
+
|
30 |
+
p {
|
31 |
+
margin: 0px;
|
32 |
+
padding: 0px 1.0em 0.5em 1.0em;
|
33 |
+
}
|
34 |
+
|
35 |
+
a:link, a:visited {
|
36 |
+
color: #4570B8;
|
37 |
+
border-bottom: 1px solid #4570B8;
|
38 |
+
text-decoration: none;
|
39 |
+
}
|
40 |
+
|
41 |
+
a:hover {
|
42 |
+
background-color: #E7F3FB;
|
43 |
+
}
|
44 |
+
|
45 |
+
ul {
|
46 |
+
margin: 0px;
|
47 |
+
}
|
48 |
+
|
49 |
+
ul#nav {
|
50 |
+
margin: 1.0em 0.0em;
|
51 |
+
}
|
52 |
+
|
53 |
+
hr {
|
54 |
+
border: 0;
|
55 |
+
height: 1px;
|
56 |
+
background-color: #afafaf;
|
57 |
+
margin: 0px 1em;
|
58 |
+
}
|
59 |
+
|
60 |
+
.foot {
|
61 |
+
color: #afafaf;
|
62 |
+
font-size: 80%;
|
63 |
+
text-align: center;
|
64 |
+
margin: 1.0em 0em;
|
65 |
+
}
|
66 |
+
|
67 |
+
.foot a {
|
68 |
+
text-decoration: none;
|
69 |
+
color: #777777;
|
70 |
+
border-bottom: 1px solid #777777;
|
71 |
+
}
|
72 |
+
|
73 |
+
.foot a:hover {
|
74 |
+
color: #000000;
|
75 |
+
background: none;
|
76 |
+
}
|
77 |
+
|
78 |
+
.demo {
|
79 |
+
border: 1px solid #777777;
|
80 |
+
margin: 1.0em;
|
81 |
+
}
|
82 |
+
|
83 |
+
table.demo {
|
84 |
+
width: 938px;
|
85 |
+
}
|
86 |
+
|
87 |
+
div.demo {
|
88 |
+
margin-bottom: 0;
|
89 |
+
padding: 0.5em 0em;
|
90 |
+
}
|
91 |
+
|
92 |
+
.demo .input, .demo .output {
|
93 |
+
width: 50%;
|
94 |
+
padding: 0.5em;
|
95 |
+
}
|
96 |
+
|
97 |
+
.demo .output {
|
98 |
+
background: #f4f4f4;
|
99 |
+
border-left: 1px dashed #777;
|
100 |
+
}
|
101 |
+
|
102 |
+
.demo textarea {
|
103 |
+
width: 99%;
|
104 |
+
}
|
105 |
+
|
106 |
+
.demo pre {
|
107 |
+
overflow-x: auto;
|
108 |
+
white-space: pre-wrap;
|
109 |
+
white-space: -moz-pre-wrap !important;
|
110 |
+
white-space: -pre-wrap;
|
111 |
+
white-space: -o-pre-wrap;
|
112 |
+
word-wrap: break-word;
|
113 |
+
}
|
114 |
+
|
115 |
+
.demo .buttons {
|
116 |
+
text-align: right;
|
117 |
+
}
|
118 |
+
|
119 |
+
.examples {
|
120 |
+
display: none;
|
121 |
+
}
|
122 |
+
|
123 |
+
.comments {
|
124 |
+
margin: 0.0em 1.0em;
|
125 |
+
padding: 0.5em;
|
126 |
+
border: 1px solid #777777;
|
127 |
+
}
|
128 |
+
|
129 |
+
#twitter {
|
130 |
+
border: 0;
|
131 |
+
text-decoration: none;
|
132 |
+
display: block;
|
133 |
+
position: absolute;
|
134 |
+
top:4px;
|
135 |
+
left:776px;
|
136 |
+
}
|
137 |
+
|
138 |
+
#twitter:hover {
|
139 |
+
background: none;
|
140 |
+
}
|
141 |
+
|
142 |
+
#twitter img {
|
143 |
+
border: 0;
|
144 |
+
}
|
145 |
+
|
146 |
+
p.important {
|
147 |
+
background-color: #FBE7E7;
|
148 |
+
background-color: #FFD6D7;
|
149 |
+
background-color: #FFDEE2;
|
150 |
+
padding: 1em 0.5em;
|
151 |
+
margin: 0.5em 0.0em 0.5em 0.5em;
|
152 |
+
}
|
153 |
+
|
154 |
+
|
155 |
+
.info {
|
156 |
+
background: #ECFBE7;
|
157 |
+
background: #E0FAD7;
|
158 |
+
-moz-border-radius: 8px;
|
159 |
+
-webkit-border-radius: 8px;
|
160 |
+
padding: 1em;
|
161 |
+
margin-left: 0.5em;
|
162 |
+
float: right;
|
163 |
+
width: 200px;
|
164 |
+
}
|
165 |
+
|
166 |
+
.info h3 {
|
167 |
+
padding: 0.5em;
|
168 |
+
margin: 0px;
|
169 |
+
font-size: 18px;
|
170 |
+
font-family: calibri, helvetica, sans-serif;
|
171 |
+
}
|
172 |
+
|
173 |
+
.info a:hover {
|
174 |
+
background: none;
|
175 |
+
}
|
176 |
+
|
177 |
+
|
178 |
+
/* vim stuff */
|
179 |
+
.Comment { color: #7c7c7c; }
|
180 |
+
.Conditional { color: #6699cc; }
|
181 |
+
.Statement { color: #6699cc; }
|
182 |
+
.String { color: #a8ff60; }
|
183 |
+
.Operator { color: #ffffff; }
|
184 |
+
.Identifier { color: #c6c5fe; }
|
185 |
+
.Function { color: #ffd2a7; }
|
186 |
+
.Type { color: #ffffb6; }
|
187 |
+
pre.code { font-family: monospace;
|
188 |
+
color: #f6f3e8; background-color: #000000;
|
189 |
+
background-color: #101C2D /*#090E16*/;
|
190 |
+
background-color: #101010;
|
191 |
+
padding: 1.0em 4.0em;
|
192 |
+
margin: 1.0em;
|
193 |
+
}
|
194 |
+
pre.code b {
|
195 |
+
color: #96cbfe;
|
196 |
+
}
|
197 |
+
|
198 |
+
pre.code u {
|
199 |
+
color: #a8ff60;
|
200 |
+
text-decoration: none;
|
201 |
+
}
|
202 |
+
|
203 |
+
|
204 |
+
.Delimiter { color: #00a0a0; }
|
205 |
+
.PreProc { color: #96cbfe; }
|
206 |
+
|
207 |
+
|
lib/vendor/lessphp/lessc.inc.php
ADDED
@@ -0,0 +1,1238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* less.inc.php
|
5 |
+
* v0.1.5
|
6 |
+
*
|
7 |
+
* less css compiler
|
8 |
+
* adapted from http://lesscss.org/docs.html
|
9 |
+
*
|
10 |
+
* leaf corcoran <leafo.net>
|
11 |
+
*/
|
12 |
+
|
13 |
+
|
14 |
+
// future todo: define type names as constants
|
15 |
+
|
16 |
+
// todo: potential problem with parse tree search order:
|
17 |
+
//
|
18 |
+
// #default['color'] is an accessor, but if color is searched
|
19 |
+
// first then #def is matched as a color and it returns true and the head is
|
20 |
+
// moved to ault['color']; That is then seen as a second value in the list
|
21 |
+
//
|
22 |
+
// solution, enforce at least a space, } or {, or ; after match
|
23 |
+
// i can consume the spaces but not the symbols
|
24 |
+
// need more time to think about this, maybe leaving order requirement is good
|
25 |
+
//
|
26 |
+
|
27 |
+
class lessc
|
28 |
+
{
|
29 |
+
private $buffer;
|
30 |
+
private $out;
|
31 |
+
private $env = array(); // environment stack
|
32 |
+
private $count = 0; // temporary advance
|
33 |
+
private $level = 0;
|
34 |
+
private $line = 1; // the current line
|
35 |
+
private $precedence = array(
|
36 |
+
'+' => '0',
|
37 |
+
'-' => '0',
|
38 |
+
'*' => '1',
|
39 |
+
'/' => '1',
|
40 |
+
);
|
41 |
+
|
42 |
+
// delayed types
|
43 |
+
private $dtypes = array('expression', 'variable');
|
44 |
+
|
45 |
+
// the default set of units
|
46 |
+
private $units = array(
|
47 |
+
'px', '%', 'in', 'cm', 'mm', 'em', 'ex', 'pt', 'pc', 's');
|
48 |
+
|
49 |
+
public $importDisabled = false;
|
50 |
+
public $importDir = '';
|
51 |
+
|
52 |
+
public function __construct($fname = null)
|
53 |
+
{
|
54 |
+
if ($fname) $this->load($fname);
|
55 |
+
|
56 |
+
$this->matchString =
|
57 |
+
'('.implode('|',array_map(array($this, 'preg_quote'), array_keys($this->precedence))).')';
|
58 |
+
}
|
59 |
+
|
60 |
+
// load a css from file
|
61 |
+
public function load($fname)
|
62 |
+
{
|
63 |
+
if (!is_file($fname)) {
|
64 |
+
throw new Exception('load error: failed to find '.$fname);
|
65 |
+
}
|
66 |
+
$pi = pathinfo($fname);
|
67 |
+
|
68 |
+
$this->file = $fname;
|
69 |
+
$this->importDir = $pi['dirname'].'/';
|
70 |
+
$this->buffer = file_get_contents($fname);
|
71 |
+
}
|
72 |
+
|
73 |
+
public function parse($text = null)
|
74 |
+
{
|
75 |
+
if ($text) $this->buffer = $text;
|
76 |
+
$this->reset();
|
77 |
+
|
78 |
+
$this->push(); // set up global scope
|
79 |
+
$this->set('__tags', array('')); // equivalent to 1 in tag multiplication
|
80 |
+
|
81 |
+
$this->buffer = $this->removeComments($this->buffer);
|
82 |
+
|
83 |
+
// trim whitespace on head
|
84 |
+
if (preg_match('/^\s+/', $this->buffer, $m)) {
|
85 |
+
$this->line += substr_count($m[0], "\n");
|
86 |
+
$this->buffer = ltrim($this->buffer);
|
87 |
+
}
|
88 |
+
|
89 |
+
while (false !== ($dat = $this->readChunk())) {
|
90 |
+
if (is_string($dat)) $this->out .= $dat;
|
91 |
+
}
|
92 |
+
|
93 |
+
if ($count = count($this->env) > 1) {
|
94 |
+
throw new
|
95 |
+
exception('Failed to parse '.(count($this->env) - 1).
|
96 |
+
' unclosed block'.($count > 1 ? 's' : ''));
|
97 |
+
}
|
98 |
+
|
99 |
+
// print_r($this->env);
|
100 |
+
return $this->out;
|
101 |
+
}
|
102 |
+
|
103 |
+
|
104 |
+
// read a chunk off the head of the buffer
|
105 |
+
// chunks are separated by ; (in most cases)
|
106 |
+
private function readChunk()
|
107 |
+
{
|
108 |
+
if ($this->buffer == '') return false;
|
109 |
+
|
110 |
+
// todo: media directive
|
111 |
+
// media screen {
|
112 |
+
// blocks
|
113 |
+
// }
|
114 |
+
|
115 |
+
// a property
|
116 |
+
try {
|
117 |
+
$this->keyword($name)->literal(':')->propertyValue($value)->end()->advance();
|
118 |
+
$this->append($name, $value);
|
119 |
+
|
120 |
+
// we can print it right away if we are in global scope (makes no sense, but w/e)
|
121 |
+
if ($this->level > 1)
|
122 |
+
return true;
|
123 |
+
else
|
124 |
+
return $this->compileProperty($name,
|
125 |
+
array($this->getVal($name)))."\n";
|
126 |
+
} catch (exception $ex) {
|
127 |
+
$this->undo();
|
128 |
+
}
|
129 |
+
|
130 |
+
// entering a block
|
131 |
+
try {
|
132 |
+
$this->tags($tags);
|
133 |
+
|
134 |
+
// it can only be a function if there is one tag
|
135 |
+
if (count($tags) == 1) {
|
136 |
+
try {
|
137 |
+
$save = $this->count;
|
138 |
+
$this->argumentDef($args);
|
139 |
+
} catch (exception $ex) {
|
140 |
+
$this->count = $save;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
$this->literal('{')->advance();
|
145 |
+
$this->push();
|
146 |
+
|
147 |
+
// move @ tags out of variable namespace!
|
148 |
+
foreach($tags as &$tag) {
|
149 |
+
if ($tag{0} == "@") $tag[0] = "%";
|
150 |
+
}
|
151 |
+
|
152 |
+
$this->set('__tags', $tags);
|
153 |
+
if (isset($args)) $this->set('__args', $args);
|
154 |
+
|
155 |
+
return true;
|
156 |
+
} catch (exception $ex) {
|
157 |
+
$this->undo();
|
158 |
+
}
|
159 |
+
|
160 |
+
// leaving a block
|
161 |
+
try {
|
162 |
+
$this->literal('}')->advance();
|
163 |
+
|
164 |
+
$tags = $this->multiplyTags();
|
165 |
+
|
166 |
+
$env = end($this->env);
|
167 |
+
$ctags = $env['__tags'];
|
168 |
+
unset($env['__tags']);
|
169 |
+
|
170 |
+
// insert the default arguments
|
171 |
+
if (isset($env['__args'])) {
|
172 |
+
foreach ($env['__args'] as $arg) {
|
173 |
+
if (isset($arg[1])) {
|
174 |
+
$this->prepend('@'.$arg[0], $arg[1]);
|
175 |
+
}
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
if (!empty($tags))
|
180 |
+
$out = $this->compileBlock($tags, $env);
|
181 |
+
|
182 |
+
$this->pop();
|
183 |
+
|
184 |
+
// make the block(s) available in the new current scope
|
185 |
+
foreach ($ctags as $t)
|
186 |
+
$this->set($t, $env);
|
187 |
+
|
188 |
+
return isset($out) ? $out : true;
|
189 |
+
} catch (exception $ex) {
|
190 |
+
$this->undo();
|
191 |
+
}
|
192 |
+
|
193 |
+
// look for import
|
194 |
+
try {
|
195 |
+
$this->import($url, $media)->advance();
|
196 |
+
if ($this->importDisabled) return "/* import is disabled */\n";
|
197 |
+
|
198 |
+
$full = $this->importDir.$url;
|
199 |
+
|
200 |
+
if (file_exists($file = $full) || file_exists($file = $full.'.less')) {
|
201 |
+
$this->buffer =
|
202 |
+
$this->removeComments(file_get_contents($file).";\n".$this->buffer);
|
203 |
+
return true;
|
204 |
+
}
|
205 |
+
|
206 |
+
return '@import url("'.$url.'")'.($media ? ' '.$media : '').";\n";
|
207 |
+
} catch (exception $ex) {
|
208 |
+
$this->undo();
|
209 |
+
}
|
210 |
+
|
211 |
+
// setting a variable
|
212 |
+
try {
|
213 |
+
$this->variable($name)->literal(':')->propertyValue($value)->end()->advance();
|
214 |
+
$this->append('@'.$name, $value);
|
215 |
+
return true;
|
216 |
+
} catch (exception $ex) {
|
217 |
+
$this->undo();
|
218 |
+
}
|
219 |
+
|
220 |
+
|
221 |
+
// look for a namespace/function to expand
|
222 |
+
// todo: this catches a lot of invalid syntax because tag
|
223 |
+
// consumer is liberal. This causes errors to be hidden
|
224 |
+
try {
|
225 |
+
$this->tags($tags, true, '>');
|
226 |
+
|
227 |
+
// move @ tags out of variable namespace
|
228 |
+
foreach($tags as &$tag) {
|
229 |
+
if ($tag{0} == "@") $tag[0] = "%";
|
230 |
+
}
|
231 |
+
|
232 |
+
// look for arguments
|
233 |
+
$save = $this->count;
|
234 |
+
try {
|
235 |
+
$this->argumentValues($argv);
|
236 |
+
} catch (exception $ex) { $this->count = $save; }
|
237 |
+
|
238 |
+
$this->end()->advance();
|
239 |
+
|
240 |
+
// find the final environment
|
241 |
+
$env = $this->get(array_shift($tags));
|
242 |
+
|
243 |
+
while ($sub = array_shift($tags)) {
|
244 |
+
if (isset($env[$sub])) // todo add a type check for environment
|
245 |
+
$env = $env[$sub];
|
246 |
+
else {
|
247 |
+
$env = null;
|
248 |
+
break;
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
if ($env == null) return true;
|
253 |
+
|
254 |
+
// if we have arguments then insert them
|
255 |
+
if (!empty($env['__args'])) {
|
256 |
+
foreach($env['__args'] as $arg) {
|
257 |
+
$name = $arg[0];
|
258 |
+
$value = is_array($argv) ? array_shift($argv) : null;
|
259 |
+
// copy default value if there isn't one supplied
|
260 |
+
if ($value == null && isset($arg[1]))
|
261 |
+
$value = $arg[1];
|
262 |
+
|
263 |
+
// if ($value == null) continue; // don't define so it can search up
|
264 |
+
|
265 |
+
// create new entry if var doesn't exist in scope
|
266 |
+
if (isset($env['@'.$name])) {
|
267 |
+
array_unshift($env['@'.$name], $value);
|
268 |
+
} else {
|
269 |
+
// new element
|
270 |
+
$env['@'.$name] = array($value);
|
271 |
+
}
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
// set all properties
|
276 |
+
ob_start();
|
277 |
+
foreach ($env as $name => $value) {
|
278 |
+
// if it is a block then render it
|
279 |
+
if (!isset($value[0])) {
|
280 |
+
$rtags = $this->multiplyTags(array($name));
|
281 |
+
echo $this->compileBlock($rtags, $value);
|
282 |
+
}
|
283 |
+
|
284 |
+
// copy everything except metadata
|
285 |
+
if (!preg_match('/^__/', $name)) {
|
286 |
+
// don't overwrite previous value
|
287 |
+
if ($this->get($name)) {
|
288 |
+
while ($tval = array_shift($value))
|
289 |
+
$this->append($name, $tval);
|
290 |
+
} else
|
291 |
+
$this->set($name, $value); // fixme: this should be append?
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
return ob_get_clean();
|
296 |
+
} catch (exception $ex) { $this->undo(); }
|
297 |
+
|
298 |
+
// ignore spare ;
|
299 |
+
try {
|
300 |
+
$this->literal(';')->advance();
|
301 |
+
return true;
|
302 |
+
} catch (exception $ex) { $this->undo(); }
|
303 |
+
|
304 |
+
// something failed
|
305 |
+
// print_r($this->env);
|
306 |
+
$this->match("(.*?)(\n|$)", $m);
|
307 |
+
throw new exception('Failed to parse line '.$this->line."\nOffending line: ".$m[1]);
|
308 |
+
}
|
309 |
+
|
310 |
+
|
311 |
+
/**
|
312 |
+
* consume functions
|
313 |
+
*
|
314 |
+
* they return instance of class so they can be chained
|
315 |
+
* any return vals are put into referenced arguments
|
316 |
+
*/
|
317 |
+
|
318 |
+
// look for an import statement on the head of the buffer
|
319 |
+
private function import(&$url, &$media)
|
320 |
+
{
|
321 |
+
$this->literal('@import');
|
322 |
+
$save = $this->count;
|
323 |
+
try {
|
324 |
+
// todo: merge this with the keyword url('')
|
325 |
+
$this->literal('url(')->string($url)->literal(')');
|
326 |
+
} catch (exception $ex) {
|
327 |
+
$this->count = $save;
|
328 |
+
$this->string($url);
|
329 |
+
}
|
330 |
+
|
331 |
+
$this->to(';', $media);
|
332 |
+
|
333 |
+
return $this;
|
334 |
+
}
|
335 |
+
|
336 |
+
private function string(&$string, &$d = null)
|
337 |
+
{
|
338 |
+
try {
|
339 |
+
$this->literal('"', true);
|
340 |
+
$delim = '"';
|
341 |
+
} catch (exception $ex) {
|
342 |
+
$this->literal("'", true);
|
343 |
+
$delim = "'";
|
344 |
+
}
|
345 |
+
|
346 |
+
$this->to($delim, $string);
|
347 |
+
|
348 |
+
if (!isset($d)) $d = $delim;
|
349 |
+
|
350 |
+
return $this;
|
351 |
+
}
|
352 |
+
|
353 |
+
private function end()
|
354 |
+
{
|
355 |
+
try {
|
356 |
+
$this->literal(';');
|
357 |
+
} catch (exception $ex) {
|
358 |
+
// there is an end of block next, then no problem
|
359 |
+
if ($this->buffer{$this->count} != '}')
|
360 |
+
throw new exception('parse error: failed to find end');
|
361 |
+
}
|
362 |
+
|
363 |
+
return $this;
|
364 |
+
}
|
365 |
+
|
366 |
+
// gets a list of property values separated by ; between ( and )
|
367 |
+
private function argumentValues(&$args, $delim = ';')
|
368 |
+
{
|
369 |
+
$this->literal('(');
|
370 |
+
|
371 |
+
$values = array();
|
372 |
+
while (true){
|
373 |
+
try {
|
374 |
+
$this->propertyValue($values[])->literal(';');
|
375 |
+
} catch (exception $ex) { break; }
|
376 |
+
}
|
377 |
+
|
378 |
+
$this->literal(')');
|
379 |
+
$args = $values;
|
380 |
+
|
381 |
+
return $this;
|
382 |
+
}
|
383 |
+
|
384 |
+
// consume agument definition, variable names with optional value
|
385 |
+
private function argumentDef(&$args, $delim = ';')
|
386 |
+
{
|
387 |
+
$this->literal('(');
|
388 |
+
|
389 |
+
$values = array();
|
390 |
+
while (true) {
|
391 |
+
try {
|
392 |
+
$arg = array();
|
393 |
+
$this->variable($arg[]);
|
394 |
+
// look for a default value
|
395 |
+
try {
|
396 |
+
$this->literal(':')->propertyValue($value);
|
397 |
+
$arg[] = $value;
|
398 |
+
} catch (exception $ax) { }
|
399 |
+
|
400 |
+
$values[] = $arg;
|
401 |
+
$this->literal($delim);
|
402 |
+
} catch (exception $ex) {
|
403 |
+
break;
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
$this->literal(')');
|
408 |
+
$args = $values;
|
409 |
+
|
410 |
+
return $this;
|
411 |
+
}
|
412 |
+
|
413 |
+
|
414 |
+
// get a list of tags separated by commas
|
415 |
+
private function tags(&$tags, $simple = false, $delim = ',')
|
416 |
+
{
|
417 |
+
$tags = array();
|
418 |
+
while (1) {
|
419 |
+
$this->tag($tmp, $simple);
|
420 |
+
$tags[] = trim($tmp);
|
421 |
+
|
422 |
+
try { $this->literal($delim); }
|
423 |
+
catch (Exception $ex) { break; }
|
424 |
+
}
|
425 |
+
|
426 |
+
return $this;
|
427 |
+
}
|
428 |
+
|
429 |
+
// match a single tag, aka the block identifier
|
430 |
+
// $simple only match simple tags, no funny selectors allowed
|
431 |
+
// this accepts spaces so it can mis comments...
|
432 |
+
private function tag(&$tag, $simple = false)
|
433 |
+
{
|
434 |
+
if ($simple)
|
435 |
+
$chars = '^,:;{}\][>\(\)';
|
436 |
+
else
|
437 |
+
$chars = '^,;{}\(\)';
|
438 |
+
|
439 |
+
// can't start with a number
|
440 |
+
if (!$this->match('(['.$chars.'0-9]['.$chars.']*)', $m))
|
441 |
+
throw new exception('parse error: failed to parse tag');
|
442 |
+
|
443 |
+
$tag = trim($m[1]);
|
444 |
+
|
445 |
+
return $this;
|
446 |
+
}
|
447 |
+
|
448 |
+
// consume $what and following whitespace from the head of buffer
|
449 |
+
private function literal($what)
|
450 |
+
{
|
451 |
+
// if $what is one char we can speed things up
|
452 |
+
// fixme: throws a notice here when going over the len of the buffer
|
453 |
+
if ((strlen($what) == 1 && $what != $this->buffer{$this->count}) ||
|
454 |
+
!$this->match($this->preg_quote($what), $m))
|
455 |
+
{
|
456 |
+
throw new
|
457 |
+
Exception('parse error: failed to prase literal '.$what);
|
458 |
+
}
|
459 |
+
return $this;
|
460 |
+
}
|
461 |
+
|
462 |
+
// consume list of values for property
|
463 |
+
private function propertyValue(&$value)
|
464 |
+
{
|
465 |
+
$out = array();
|
466 |
+
|
467 |
+
while (1) {
|
468 |
+
try {
|
469 |
+
$this->expressionList($out[]);
|
470 |
+
$this->literal(','); }
|
471 |
+
catch (exception $ex) { break; }
|
472 |
+
}
|
473 |
+
|
474 |
+
if (!empty($out)) {
|
475 |
+
$out = array_map(array($this, 'compressValues'), $out);
|
476 |
+
$value = $this->compressValues($out, ', ');
|
477 |
+
}
|
478 |
+
|
479 |
+
return $this;
|
480 |
+
}
|
481 |
+
|
482 |
+
// evaluate a list of expressions separated by spaces
|
483 |
+
private function expressionList(&$vals)
|
484 |
+
{
|
485 |
+
$vals = array();
|
486 |
+
$this->expression($vals[]); // there should be at least one
|
487 |
+
|
488 |
+
while (1) {
|
489 |
+
try { $this->expression($tmp); }
|
490 |
+
catch (Exception $ex) { break; }
|
491 |
+
|
492 |
+
$vals[] = $tmp;
|
493 |
+
}
|
494 |
+
|
495 |
+
return $this;
|
496 |
+
}
|
497 |
+
|
498 |
+
// evaluate a group of values separated by operators
|
499 |
+
private function expression(&$result)
|
500 |
+
{
|
501 |
+
try {
|
502 |
+
$this->literal('(')->expression($exp)->literal(')');
|
503 |
+
$lhs = $exp;
|
504 |
+
} catch (exception $ex) {
|
505 |
+
$this->value($lhs);
|
506 |
+
}
|
507 |
+
$result = $this->expHelper($lhs, 0);
|
508 |
+
return $this;
|
509 |
+
}
|
510 |
+
|
511 |
+
|
512 |
+
// used to recursively love infix equation with proper operator order
|
513 |
+
private function expHelper($lhs, $minP)
|
514 |
+
{
|
515 |
+
// while there is an operator and the precedence is greater or equal to min
|
516 |
+
while ($this->match($this->matchString, $m) && $this->precedence[$m[1]] >= $minP) {
|
517 |
+
// check for subexp
|
518 |
+
try {
|
519 |
+
$this->literal('(')->expression($exp)->literal(')');
|
520 |
+
$rhs = $exp;
|
521 |
+
} catch (exception $ex) {
|
522 |
+
$this->value($rhs);
|
523 |
+
}
|
524 |
+
|
525 |
+
// find out if next up needs rhs
|
526 |
+
if ($this->peek($this->matchString, $mi) && $this->precedence[$mi[1]] > $minP) {
|
527 |
+
$rhs = $this->expHelper($rhs, $this->precedence[$mi[1]]);
|
528 |
+
}
|
529 |
+
|
530 |
+
// todo: find a way to precalculate non-delayed types
|
531 |
+
if (in_array($rhs[0], $this->dtypes) || in_array($lhs[0], $this->dtypes))
|
532 |
+
$lhs = array('expression', $m[1], $lhs, $rhs);
|
533 |
+
else
|
534 |
+
$lhs = $this->evaluate($m[1], $lhs, $rhs);
|
535 |
+
}
|
536 |
+
return $lhs;
|
537 |
+
}
|
538 |
+
|
539 |
+
// consume a css value:
|
540 |
+
// a keyword
|
541 |
+
// a variable (includes accessor);
|
542 |
+
// a color
|
543 |
+
// a unit (em, px, pt, %, mm), can also have no unit 4px + 3;
|
544 |
+
// a string
|
545 |
+
private function value(&$val)
|
546 |
+
{
|
547 |
+
try {
|
548 |
+
return $this->unit($val);
|
549 |
+
} catch (exception $ex) { /* $this->undo(); */ }
|
550 |
+
|
551 |
+
// look for accessor
|
552 |
+
// must be done before color
|
553 |
+
try {
|
554 |
+
$save = $this->count; // todo: replace with counter stack
|
555 |
+
$this->accessor($a);
|
556 |
+
$tmp = $this->get($a[0]); // get env
|
557 |
+
$val = end($tmp[$a[1]]); // get latest var
|
558 |
+
|
559 |
+
return $this;
|
560 |
+
} catch (exception $ex) { $this->count = $save; /* $this->undo(); */ }
|
561 |
+
|
562 |
+
try {
|
563 |
+
return $this->color($val);
|
564 |
+
} catch (exception $ex) { /* $this->undo(); */ }
|
565 |
+
|
566 |
+
try {
|
567 |
+
$save = $this->count;
|
568 |
+
$this->func($f);
|
569 |
+
|
570 |
+
$val = array('string', $f);
|
571 |
+
|
572 |
+
return $this;
|
573 |
+
} catch (exception $ex) { $this->count = $save; }
|
574 |
+
|
575 |
+
// a string
|
576 |
+
try {
|
577 |
+
$save = $this->count;
|
578 |
+
$this->string($tmp, $d);
|
579 |
+
$val = array('string', $d.$tmp.$d);
|
580 |
+
return $this;
|
581 |
+
} catch (exception $ex) { $this->count = $save; }
|
582 |
+
|
583 |
+
try {
|
584 |
+
$this->keyword($k);
|
585 |
+
$val = array('keyword', $k);
|
586 |
+
return $this;
|
587 |
+
} catch (exception $ex) { /* $this->undo(); */ }
|
588 |
+
|
589 |
+
|
590 |
+
|
591 |
+
// try to get a variable
|
592 |
+
try {
|
593 |
+
$this->variable($name);
|
594 |
+
$val = array('variable', '@'.$name);
|
595 |
+
|
596 |
+
return $this;
|
597 |
+
} catch (exception $ex) { /* $this->undo(); */ }
|
598 |
+
|
599 |
+
throw new exception('parse error: failed to find value');
|
600 |
+
}
|
601 |
+
|
602 |
+
// $units the allowed units
|
603 |
+
// number is always allowed (is this okay?)
|
604 |
+
private function unit(&$unit, $units = null)
|
605 |
+
{
|
606 |
+
if (!$units) $units = $this->units;
|
607 |
+
|
608 |
+
if (!$this->match('(-?[0-9]*(\.)?[0-9]+)('.implode('|', $units).')?', $m)) {
|
609 |
+
throw new exception('parse error: failed to consume unit');
|
610 |
+
}
|
611 |
+
|
612 |
+
// throw on a default unit
|
613 |
+
if (!isset($m[3])) $m[3] = 'number';
|
614 |
+
|
615 |
+
$unit = array($m[3], $m[1]);
|
616 |
+
return $this;
|
617 |
+
}
|
618 |
+
|
619 |
+
// todo: hue saturation lightness support (hsl)
|
620 |
+
// need a function to convert hsl to rgb
|
621 |
+
private function color(&$out)
|
622 |
+
{
|
623 |
+
$color = array('color');
|
624 |
+
if($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
|
625 |
+
if (isset($m[3])) {
|
626 |
+
$num = $m[3];
|
627 |
+
$width = 16;
|
628 |
+
} else {
|
629 |
+
$num = $m[2];
|
630 |
+
$width = 256;
|
631 |
+
}
|
632 |
+
|
633 |
+
$num = hexdec($num);
|
634 |
+
foreach(array(3,2,1) as $i) {
|
635 |
+
$t = $num % $width;
|
636 |
+
$num /= $width;
|
637 |
+
|
638 |
+
// todo: this is retarded
|
639 |
+
$color[$i] = $t * (256/$width) + $t * floor(16/$width);
|
640 |
+
}
|
641 |
+
|
642 |
+
} else {
|
643 |
+
$save = $this->count;
|
644 |
+
try {
|
645 |
+
$this->literal('rgb');
|
646 |
+
|
647 |
+
try {
|
648 |
+
$this->literal('a');
|
649 |
+
$count = 4;
|
650 |
+
} catch (exception $ex) {
|
651 |
+
$count = 3;
|
652 |
+
}
|
653 |
+
|
654 |
+
$this->literal('(');
|
655 |
+
|
656 |
+
// grab the numbers and format
|
657 |
+
foreach (range(1, $count) as $i) {
|
658 |
+
$this->unit($color[], array('%'));
|
659 |
+
if ($i != $count) $this->literal(',');
|
660 |
+
|
661 |
+
if ($color[$i][0] == '%')
|
662 |
+
$color[$i] = 255 * ($color[$i][1] / 100);
|
663 |
+
else
|
664 |
+
$color[$i] = $color[$i][1];
|
665 |
+
}
|
666 |
+
|
667 |
+
$this->literal(')');
|
668 |
+
|
669 |
+
$color = $this->fixColor($color);
|
670 |
+
} catch (exception $ex) {
|
671 |
+
$this->count = $save;
|
672 |
+
|
673 |
+
throw new exception('failed to find color');
|
674 |
+
}
|
675 |
+
}
|
676 |
+
|
677 |
+
$out = $color; // don't put things on out unless everything works out
|
678 |
+
return $this;
|
679 |
+
}
|
680 |
+
|
681 |
+
private function variable(&$var)
|
682 |
+
{
|
683 |
+
$this->literal('@')->keyword($var);
|
684 |
+
return $this;
|
685 |
+
}
|
686 |
+
|
687 |
+
private function accessor(&$var)
|
688 |
+
{
|
689 |
+
$this->tag($scope, true)->literal('[');
|
690 |
+
|
691 |
+
// see if it is a variable
|
692 |
+
try {
|
693 |
+
$this->variable($name);
|
694 |
+
$name = '@'.$name;
|
695 |
+
} catch (exception $ex) {
|
696 |
+
// try to see if it is a property
|
697 |
+
try {
|
698 |
+
$this->literal("'")->keyword($name)->literal("'");
|
699 |
+
} catch (exception $ex) {
|
700 |
+
throw new exception('parse error: failed to parse accessor');
|
701 |
+
}
|
702 |
+
}
|
703 |
+
|
704 |
+
$this->literal(']');
|
705 |
+
|
706 |
+
$var = array($scope, $name);
|
707 |
+
|
708 |
+
return $this;
|
709 |
+
}
|
710 |
+
|
711 |
+
// read a css function off the head of the buffer
|
712 |
+
private function func(&$func)
|
713 |
+
{
|
714 |
+
$this->keyword($fname)->literal('(')->to(')', $args);
|
715 |
+
|
716 |
+
$func = $fname.'('.$args.')';
|
717 |
+
return $this;
|
718 |
+
}
|
719 |
+
|
720 |
+
// read a keyword off the head of the buffer
|
721 |
+
private function keyword(&$word)
|
722 |
+
{
|
723 |
+
if (!$this->match('([\w_\-!"][\w\-_"]*)', $m)) {
|
724 |
+
throw new Exception('parse error: failed to find keyword');
|
725 |
+
}
|
726 |
+
|
727 |
+
$word = $m[1];
|
728 |
+
return $this;
|
729 |
+
}
|
730 |
+
|
731 |
+
// this ignores comments because it doesn't grab by token
|
732 |
+
private function to($what, &$out)
|
733 |
+
{
|
734 |
+
if (!$this->match('(.*?)'.$this->preg_quote($what), $m))
|
735 |
+
throw new exception('parse error: failed to consume to '.$what);
|
736 |
+
|
737 |
+
$out = $m[1];
|
738 |
+
|
739 |
+
return $this;
|
740 |
+
}
|
741 |
+
|
742 |
+
|
743 |
+
/**
|
744 |
+
* compile functions turn data into css code
|
745 |
+
*/
|
746 |
+
private function compileBlock($rtags, $env)
|
747 |
+
{
|
748 |
+
// don't render functions
|
749 |
+
foreach ($rtags as $i => $tag) {
|
750 |
+
if (preg_match('/( |^)%/', $tag))
|
751 |
+
unset($rtags[$i]);
|
752 |
+
}
|
753 |
+
if (empty($rtags)) return '';
|
754 |
+
|
755 |
+
$props = 0;
|
756 |
+
// print all the properties
|
757 |
+
ob_start();
|
758 |
+
foreach ($env as $name => $value) {
|
759 |
+
// todo: change this, poor hack
|
760 |
+
// make a better name storage system!!! (value types are fine)
|
761 |
+
// but.. don't render special properties (blocks, vars, metadata)
|
762 |
+
if (isset($value[0]) && $name{0} != '@' && $name != '__args') {
|
763 |
+
echo $this->compileProperty($name, $value, 1)."\n";
|
764 |
+
$props++;
|
765 |
+
}
|
766 |
+
}
|
767 |
+
$list = ob_get_clean();
|
768 |
+
|
769 |
+
if ($props == 0) return true;
|
770 |
+
|
771 |
+
// do some formatting
|
772 |
+
if ($props == 1) $list = ' '.trim($list).' ';
|
773 |
+
return implode(", ", $rtags).' {'.($props > 1 ? "\n" : '').
|
774 |
+
$list."}\n";
|
775 |
+
}
|
776 |
+
|
777 |
+
private function compileProperty($name, $value, $level = 0)
|
778 |
+
{
|
779 |
+
// compile all repeated properties
|
780 |
+
foreach ($value as $v)
|
781 |
+
$props[] = str_repeat(' ', $level).
|
782 |
+
$name.':'.$this->compileValue($v).';';
|
783 |
+
|
784 |
+
return implode("\n", $props);
|
785 |
+
}
|
786 |
+
|
787 |
+
|
788 |
+
// todo replace render color
|
789 |
+
private function compileValue($value)
|
790 |
+
{
|
791 |
+
switch ($value[0]) {
|
792 |
+
case 'list':
|
793 |
+
return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
|
794 |
+
case 'expression':
|
795 |
+
return $this->compileValue($this->evaluate($value[1], $value[2], $value[3]));
|
796 |
+
|
797 |
+
case 'variable':
|
798 |
+
$tmp = $this->compileValue(
|
799 |
+
$this->getVal($value[1],
|
800 |
+
$this->pushName($value[1]))
|
801 |
+
);
|
802 |
+
$this->popName();
|
803 |
+
|
804 |
+
return $tmp;
|
805 |
+
|
806 |
+
case 'string':
|
807 |
+
// search for values inside the string
|
808 |
+
$replace = array();
|
809 |
+
if (preg_match_all('/{(@[\w-_][0-9\w-_]*)}/', $value[1], $m)) {
|
810 |
+
foreach($m[1] as $name) {
|
811 |
+
if (!isset($replace[$name]))
|
812 |
+
$replace[$name] = $this->compileValue(array('variable', $name));
|
813 |
+
}
|
814 |
+
}
|
815 |
+
foreach ($replace as $var=>$val)
|
816 |
+
$value[1] = str_replace('{'.$var.'}', $val, $value[1]);
|
817 |
+
|
818 |
+
return $value[1];
|
819 |
+
|
820 |
+
case 'color':
|
821 |
+
return $this->compileColor($value);
|
822 |
+
|
823 |
+
case 'keyword':
|
824 |
+
return $value[1];
|
825 |
+
|
826 |
+
case 'number':
|
827 |
+
return $value[1];
|
828 |
+
|
829 |
+
default: // assumed to be a unit
|
830 |
+
return $value[1].$value[0];
|
831 |
+
}
|
832 |
+
}
|
833 |
+
|
834 |
+
|
835 |
+
private function compileColor($c)
|
836 |
+
{
|
837 |
+
if (count($c) == 5) { // rgba
|
838 |
+
return 'rgba('.$c[1].','.$c[2].','.$c[3].','.$c[4].')';
|
839 |
+
}
|
840 |
+
|
841 |
+
$out = '#';
|
842 |
+
foreach (range(1,3) as $i)
|
843 |
+
$out .= ($c[$i] < 16 ? '0' : '').dechex($c[$i]);
|
844 |
+
return $out;
|
845 |
+
}
|
846 |
+
|
847 |
+
|
848 |
+
/**
|
849 |
+
* arithmetic evaluator and operators
|
850 |
+
*/
|
851 |
+
|
852 |
+
// evalue an operator
|
853 |
+
// this is a messy function, probably a better way to do it
|
854 |
+
private function evaluate($op, $lft, $rgt)
|
855 |
+
{
|
856 |
+
$pushed = 0;
|
857 |
+
// figure out what expressions and variables are equal to
|
858 |
+
while (in_array($lft[0], $this->dtypes))
|
859 |
+
{
|
860 |
+
if ($lft[0] == 'expression')
|
861 |
+
$lft = $this->evaluate($lft[1], $lft[2], $lft[3]);
|
862 |
+
else if ($lft[0] == 'variable') {
|
863 |
+
$lft = $this->getVal($lft[1], $this->pushName($lft[1]), array('number', 0));
|
864 |
+
$pushed++;
|
865 |
+
}
|
866 |
+
|
867 |
+
}
|
868 |
+
while ($pushed != 0) { $this->popName(); $pushed--; }
|
869 |
+
|
870 |
+
while (in_array($rgt[0], $this->dtypes))
|
871 |
+
{
|
872 |
+
if ($rgt[0] == 'expression')
|
873 |
+
$rgt = $this->evaluate($rgt[1], $rgt[2], $rgt[3]);
|
874 |
+
else if ($rgt[0] == 'variable') {
|
875 |
+
$rgt = $this->getVal($rgt[1], $this->pushName($rgt[1]), array('number', 0));
|
876 |
+
$pushed++;
|
877 |
+
}
|
878 |
+
}
|
879 |
+
while ($pushed != 0) { $this->popName(); $pushed--; }
|
880 |
+
|
881 |
+
if ($lft [0] == 'color' && $rgt[0] == 'color') {
|
882 |
+
return $this->op_color_color($op, $lft, $rgt);
|
883 |
+
}
|
884 |
+
|
885 |
+
if ($lft[0] == 'color') {
|
886 |
+
return $this->op_color_number($op, $lft, $rgt);
|
887 |
+
}
|
888 |
+
|
889 |
+
if ($rgt[0] == 'color') {
|
890 |
+
return $this->op_number_color($op, $lft, $rgt);
|
891 |
+
}
|
892 |
+
|
893 |
+
// default number number
|
894 |
+
return $this->op_number_number($op, $lft, $rgt);
|
895 |
+
}
|
896 |
+
|
897 |
+
private function op_number_number($op, $lft, $rgt)
|
898 |
+
{
|
899 |
+
if ($rgt[0] == '%') $rgt[1] /= 100;
|
900 |
+
|
901 |
+
// figure out the type
|
902 |
+
if ($rgt[0] == 'number' || $rgt[0] == '%') $type = $lft[0];
|
903 |
+
else $type = $rgt[0];
|
904 |
+
|
905 |
+
$num = array($type);
|
906 |
+
|
907 |
+
switch($op) {
|
908 |
+
case '+':
|
909 |
+
$num[] = $lft[1] + $rgt[1];
|
910 |
+
break;
|
911 |
+
case '*':
|
912 |
+
$num[] = $lft[1] * $rgt[1];
|
913 |
+
break;
|
914 |
+
case '-':
|
915 |
+
$num[] = $lft[1] - $rgt[1];
|
916 |
+
break;
|
917 |
+
case '/';
|
918 |
+
if ($rgt[1] == 0) throw new exception("parse error: can't divide by zero");
|
919 |
+
$num[] = $lft[1] / $rgt[1];
|
920 |
+
break;
|
921 |
+
default:
|
922 |
+
throw new exception('parse error: number op number failed on op '.$op);
|
923 |
+
}
|
924 |
+
|
925 |
+
return $num;
|
926 |
+
}
|
927 |
+
|
928 |
+
private function op_number_color($op, $lft, $rgt)
|
929 |
+
{
|
930 |
+
if ($op == '+' || $op = '*') {
|
931 |
+
return $this->op_color_number($op, $rgt, $lft);
|
932 |
+
}
|
933 |
+
}
|
934 |
+
|
935 |
+
private function op_color_number($op, $lft, $rgt)
|
936 |
+
{
|
937 |
+
if ($rgt[0] == '%') $rgt[1] /= 100;
|
938 |
+
|
939 |
+
return $this->op_color_color($op, $lft,
|
940 |
+
array('color', $rgt[1], $rgt[1], $rgt[1]));
|
941 |
+
}
|
942 |
+
|
943 |
+
private function op_color_color($op, $lft, $rgt)
|
944 |
+
{
|
945 |
+
$newc = array('color');
|
946 |
+
|
947 |
+
switch ($op) {
|
948 |
+
case '+':
|
949 |
+
$newc[] = $lft[1] + $rgt[1];
|
950 |
+
$newc[] = $lft[2] + $rgt[2];
|
951 |
+
$newc[] = $lft[3] + $rgt[3];
|
952 |
+
break;
|
953 |
+
case '*':
|
954 |
+
$newc[] = $lft[1] * $rgt[1];
|
955 |
+
$newc[] = $lft[2] * $rgt[2];
|
956 |
+
$newc[] = $lft[3] * $rgt[3];
|
957 |
+
break;
|
958 |
+
case '-':
|
959 |
+
$newc[] = $lft[1] - $rgt[1];
|
960 |
+
$newc[] = $lft[2] - $rgt[2];
|
961 |
+
$newc[] = $lft[3] - $rgt[3];
|
962 |
+
break;
|
963 |
+
case '/';
|
964 |
+
if ($rgt[1] == 0 || $rgt[2] == 0 || $rgt[3] == 0)
|
965 |
+
throw new exception("parse error: can't divide by zero");
|
966 |
+
$newc[] = $lft[1] / $rgt[1];
|
967 |
+
$newc[] = $lft[2] / $rgt[2];
|
968 |
+
$newc[] = $lft[3] / $rgt[3];
|
969 |
+
break;
|
970 |
+
default:
|
971 |
+
throw new exception('parse error: color op number failed on op '.$op);
|
972 |
+
}
|
973 |
+
return $this->fixColor($newc);
|
974 |
+
}
|
975 |
+
|
976 |
+
|
977 |
+
/**
|
978 |
+
* functions for controlling the environment
|
979 |
+
*/
|
980 |
+
|
981 |
+
// get something out of the env
|
982 |
+
// search from the head of the stack down
|
983 |
+
// $env what environment to search in
|
984 |
+
private function get($name, $env = null)
|
985 |
+
{
|
986 |
+
if (empty($env)) $env = $this->env;
|
987 |
+
|
988 |
+
for ($i = count($env) - 1; $i >= 0; $i--)
|
989 |
+
if (isset($env[$i][$name])) return $env[$i][$name];
|
990 |
+
|
991 |
+
return null;
|
992 |
+
}
|
993 |
+
|
994 |
+
|
995 |
+
// get the most recent value of a variable
|
996 |
+
// return default if it isn't found
|
997 |
+
// $skip is number of vars to skip
|
998 |
+
// todo: rename to getVar ?
|
999 |
+
private function getVal($name, $skip = 0, $default = array('keyword', ''))
|
1000 |
+
{
|
1001 |
+
$val = $this->get($name);
|
1002 |
+
if ($val == null) return $default;
|
1003 |
+
|
1004 |
+
$tmp = $this->env;
|
1005 |
+
while (!isset($tmp[count($tmp) - 1][$name])) array_pop($tmp);
|
1006 |
+
while ($skip > 0) {
|
1007 |
+
$skip--;
|
1008 |
+
|
1009 |
+
if (!empty($val)) {
|
1010 |
+
array_pop($val);
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
if (empty($val)) {
|
1014 |
+
array_pop($tmp);
|
1015 |
+
$val = $this->get($name, $tmp);
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
if (empty($val)) return $default;
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
return end($val);
|
1022 |
+
}
|
1023 |
+
|
1024 |
+
// set something in the current env
|
1025 |
+
private function set($name, $value)
|
1026 |
+
{
|
1027 |
+
$this->env[count($this->env) - 1][$name] = $value;
|
1028 |
+
}
|
1029 |
+
|
1030 |
+
// append to array in the current env
|
1031 |
+
private function append($name, $value)
|
1032 |
+
{
|
1033 |
+
$this->env[count($this->env) - 1][$name][] = $value;
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
// put on the front of the value
|
1037 |
+
private function prepend($name, $value)
|
1038 |
+
{
|
1039 |
+
if (isset($this->env[count($this->env) - 1][$name]))
|
1040 |
+
array_unshift($this->env[count($this->env) - 1][$name], $value);
|
1041 |
+
else $this->append($name, $value);
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
// push a new environment stack
|
1045 |
+
private function push()
|
1046 |
+
{
|
1047 |
+
$this->level++;
|
1048 |
+
$this->env[] = array();
|
1049 |
+
}
|
1050 |
+
|
1051 |
+
// pop environment off the stack
|
1052 |
+
private function pop()
|
1053 |
+
{
|
1054 |
+
if ($this->level == 1)
|
1055 |
+
throw new exception('parse error: unexpected end of block');
|
1056 |
+
|
1057 |
+
$this->level--;
|
1058 |
+
return array_pop($this->env);
|
1059 |
+
}
|
1060 |
+
|
1061 |
+
/**
|
1062 |
+
* misc functions
|
1063 |
+
*/
|
1064 |
+
|
1065 |
+
// functions for manipulating the expand stack
|
1066 |
+
private $expandStack = array();
|
1067 |
+
|
1068 |
+
// push name on expand stack and return its count
|
1069 |
+
// before being pushed
|
1070 |
+
private function pushName($name) {
|
1071 |
+
$count = array_count_values($this->expandStack);
|
1072 |
+
$count = isset($count[$name]) ? $count[$name] : 0;
|
1073 |
+
|
1074 |
+
$this->expandStack[] = $name;
|
1075 |
+
return $count;
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
// pop name of expand stack and return it
|
1079 |
+
private function popName() {
|
1080 |
+
return array_pop($this->expandStack);
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
|
1084 |
+
// remove comments from $text
|
1085 |
+
// todo: make it work for all functions, not just url
|
1086 |
+
private function removeComments($text)
|
1087 |
+
{
|
1088 |
+
$out = '';
|
1089 |
+
|
1090 |
+
while (!empty($text) &&
|
1091 |
+
preg_match('/^(.*?)("|\'|\/\/|\/\*|url\(|$)/is', $text, $m))
|
1092 |
+
{
|
1093 |
+
if (!trim($text)) break;
|
1094 |
+
|
1095 |
+
$out .= $m[1];
|
1096 |
+
$text = substr($text, strlen($m[0]));
|
1097 |
+
|
1098 |
+
switch ($m[2]) {
|
1099 |
+
case 'url(':
|
1100 |
+
preg_match('/^(.*?)(\)|$)/is', $text, $inner);
|
1101 |
+
$text = substr($text, strlen($inner[0]));
|
1102 |
+
$out .= $m[2].$inner[1].$inner[2];
|
1103 |
+
break;
|
1104 |
+
case '//':
|
1105 |
+
preg_match("/^(.*?)(\n|$)/is", $text, $inner);
|
1106 |
+
// give back the newline
|
1107 |
+
$text = substr($text, strlen($inner[0]) - 1);
|
1108 |
+
break;
|
1109 |
+
case '/*';
|
1110 |
+
preg_match("/^(.*?)(\*\/|$)/is", $text, $inner);
|
1111 |
+
$text = substr($text, strlen($inner[0]));
|
1112 |
+
break;
|
1113 |
+
case '"':
|
1114 |
+
case "'":
|
1115 |
+
preg_match("/^(.*?)(".$m[2]."|$)/is", $text, $inner);
|
1116 |
+
$text = substr($text, strlen($inner[0]));
|
1117 |
+
$out .= $m[2].$inner[1].$inner[2];
|
1118 |
+
break;
|
1119 |
+
}
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
$this->count = 0;
|
1123 |
+
return $out;
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
|
1127 |
+
// match text from the head while skipping $count characters
|
1128 |
+
// advances the temp counter if it succeeds
|
1129 |
+
private function match($regex, &$out, $eatWhitespace = true)
|
1130 |
+
{
|
1131 |
+
// if ($this->count > 100) echo '-- '.$this->count."\n";
|
1132 |
+
$r = '/^.{'.$this->count.'}'.$regex.($eatWhitespace ? '\s*' : '').'/is';
|
1133 |
+
if (preg_match($r, $this->buffer, $out)) {
|
1134 |
+
$this->count = strlen($out[0]);
|
1135 |
+
return true;
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
return false;
|
1139 |
+
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
private function peek($regex, &$out = null)
|
1143 |
+
{
|
1144 |
+
return preg_match('/^.{'.$this->count.'}'.$regex.'/is', $this->buffer, $out);
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
|
1148 |
+
// compress a list of values into a single type
|
1149 |
+
// if the list contains one thing, then return that thing
|
1150 |
+
private function compressValues($values, $delim = ' ')
|
1151 |
+
{
|
1152 |
+
if (count($values) == 1) return $values[0];
|
1153 |
+
return array('list', $delim, $values);
|
1154 |
+
}
|
1155 |
+
|
1156 |
+
// make sure a color's components don't go out of bounds
|
1157 |
+
private function fixColor($c)
|
1158 |
+
{
|
1159 |
+
for ($i = 1; $i < 4; $i++) {
|
1160 |
+
if ($c[$i] < 0) $c[$i] = 0;
|
1161 |
+
if ($c[$i] > 255) $c[$i] = 255;
|
1162 |
+
$c[$i] = floor($c[$i]);
|
1163 |
+
}
|
1164 |
+
return $c;
|
1165 |
+
}
|
1166 |
+
|
1167 |
+
private function preg_quote($what)
|
1168 |
+
{
|
1169 |
+
// I don't know why it doesn't include it by default
|
1170 |
+
return preg_quote($what, '/');
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
// reset all internal state to default
|
1174 |
+
private function reset()
|
1175 |
+
{
|
1176 |
+
$this->out = '';
|
1177 |
+
$this->env = array();
|
1178 |
+
$this->line = 1;
|
1179 |
+
$this->count = 0;
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
// advance the buffer n places
|
1183 |
+
private function advance()
|
1184 |
+
{
|
1185 |
+
// this is probably slow
|
1186 |
+
$tmp = substr($this->buffer, 0, $this->count);
|
1187 |
+
$this->line += substr_count($tmp, "\n");
|
1188 |
+
|
1189 |
+
$this->buffer = substr($this->buffer, $this->count);
|
1190 |
+
$this->count = 0;
|
1191 |
+
}
|
1192 |
+
|
1193 |
+
// reset the temporary advance
|
1194 |
+
private function undo()
|
1195 |
+
{
|
1196 |
+
$this->count = 0;
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
// find the cartesian product of all tags in stack
|
1200 |
+
private function multiplyTags($tags = array(' '), $d = null)
|
1201 |
+
{
|
1202 |
+
if ($d === null) $d = count($this->env) - 1;
|
1203 |
+
|
1204 |
+
$parents = $d == 0 ? $this->env[$d]['__tags']
|
1205 |
+
: $this->multiplyTags($this->env[$d]['__tags'], $d - 1);
|
1206 |
+
|
1207 |
+
$rtags = array();
|
1208 |
+
foreach ($parents as $p) {
|
1209 |
+
foreach ($tags as $t) {
|
1210 |
+
if ($t{0} == '@') continue; // skip functions
|
1211 |
+
$rtags[] = trim($p.($t{0} == ':' ? '' : ' ').$t);
|
1212 |
+
}
|
1213 |
+
}
|
1214 |
+
|
1215 |
+
return $rtags;
|
1216 |
+
}
|
1217 |
+
|
1218 |
+
|
1219 |
+
/**
|
1220 |
+
* static utility functions
|
1221 |
+
*/
|
1222 |
+
|
1223 |
+
// compile to $in to $out if $in is newer than $out
|
1224 |
+
// returns true when it compiles, false otherwise
|
1225 |
+
public static function ccompile($in, $out)
|
1226 |
+
{
|
1227 |
+
if (!is_file($out) || filemtime($in) > filemtime($out)) {
|
1228 |
+
$less = new lessc($in);
|
1229 |
+
file_put_contents($out, $less->parse());
|
1230 |
+
return true;
|
1231 |
+
}
|
1232 |
+
|
1233 |
+
return false;
|
1234 |
+
}
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
|
1238 |
+
?>
|
lib/vendor/lessphp/plessc
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/php -q
|
2 |
+
<?php
|
3 |
+
//
|
4 |
+
// command line utility to compile less to stdout
|
5 |
+
// leaf corcoran <leafo.net>
|
6 |
+
|
7 |
+
require './lessc.inc.php';
|
8 |
+
|
9 |
+
$colors = array (
|
10 |
+
'color' => array(
|
11 |
+
'black' => 30,
|
12 |
+
'red' => 31,
|
13 |
+
'green' => 32,
|
14 |
+
'brown' => 33,
|
15 |
+
'blue' => 34,
|
16 |
+
'purple' => 35,
|
17 |
+
'cyan' => 36,
|
18 |
+
'grey' => 37,
|
19 |
+
'yellow' => 33
|
20 |
+
),
|
21 |
+
'style' => array(
|
22 |
+
'normal' => 0,
|
23 |
+
'bold' => 1,
|
24 |
+
'light' => 1,
|
25 |
+
'underscore' => 4,
|
26 |
+
'underline' => 4,
|
27 |
+
'blink' => 5,
|
28 |
+
'inverse' => 6,
|
29 |
+
'hidden' => 8,
|
30 |
+
'concealed' => 8
|
31 |
+
),
|
32 |
+
'background' => array(
|
33 |
+
'black' => 40,
|
34 |
+
'red' => 41,
|
35 |
+
'green' => 42,
|
36 |
+
'brown' => 43,
|
37 |
+
'yellow' => 43,
|
38 |
+
'blue' => 44,
|
39 |
+
'purple' => 45,
|
40 |
+
'cyan' => 46,
|
41 |
+
'grey' => 47
|
42 |
+
)
|
43 |
+
);
|
44 |
+
|
45 |
+
array_shift($argv);
|
46 |
+
|
47 |
+
// read less code from argument or from std in if there is no argument
|
48 |
+
if (false !== ($loc = array_search("-r", $argv))) {
|
49 |
+
unset($argv[$loc]);
|
50 |
+
$c = array_shift($argv);
|
51 |
+
if (!$c) {
|
52 |
+
while (!feof(STDIN)) {
|
53 |
+
$c .= fread(STDIN, 8192);
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
} else if (false !== ($loc = array_search("-w", $argv))) {
|
58 |
+
unset($argv[$loc]);
|
59 |
+
|
60 |
+
// see if we are sending notifications
|
61 |
+
$notif = array_search("-n", $argv);
|
62 |
+
if ($notif) unset($argv[$notif]);
|
63 |
+
|
64 |
+
if (!is_file($in = array_shift($argv)) ||
|
65 |
+
null == $out = array_shift($argv))
|
66 |
+
{
|
67 |
+
exit("\033[31m\033[1mFatal Error:\033[0m plessc -w infile outfile\n");
|
68 |
+
}
|
69 |
+
|
70 |
+
$fail_time = 0;
|
71 |
+
echo "Watching ".$in.($notif ? ' with notifications' : '').", press Ctrl + c to exit.\n";
|
72 |
+
while (1) {
|
73 |
+
if (!is_file($out) || (filemtime($in) > filemtime($out) && $fail_time != filemtime($in))) {
|
74 |
+
// try to compile it
|
75 |
+
try {
|
76 |
+
$l = new lessc($in);
|
77 |
+
$c = $l->parse();
|
78 |
+
echo "Writing updated file: ".$out."\n";
|
79 |
+
if (!file_put_contents($out, $c))
|
80 |
+
exit('Fatal Error: Failed to write file '.$out."\n");
|
81 |
+
} catch (exception $ex) {
|
82 |
+
echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
|
83 |
+
$fail_time = filemtime($in);
|
84 |
+
|
85 |
+
if ($notif) {
|
86 |
+
`notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
}
|
90 |
+
sleep(1);
|
91 |
+
}
|
92 |
+
|
93 |
+
} else {
|
94 |
+
if (!is_file($fname = array_shift($argv)))
|
95 |
+
exit('Fatal Error: failed to find file: '.$fname."\n");
|
96 |
+
$c = file_get_contents($fname);
|
97 |
+
|
98 |
+
$pi = pathinfo($fname);
|
99 |
+
$importDir = $pi['dirname'].'/';
|
100 |
+
}
|
101 |
+
|
102 |
+
error_reporting(E_ALL);
|
103 |
+
|
104 |
+
$l = new lessc();
|
105 |
+
if ($importDir) $l->importDir = $importDir;
|
106 |
+
try {
|
107 |
+
echo $l->parse($c);
|
108 |
+
} catch (exception $ex) {
|
109 |
+
echo "Fatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n";
|
110 |
+
}
|
111 |
+
|
112 |
+
?>
|
lib/vendor/plugin-toolkit/BaseConfiguration.class.php
ADDED
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
abstract class WPPluginToolkitConfiguration
|
4 |
+
{
|
5 |
+
const UNIX_NAME = null;
|
6 |
+
const I18N_DIR = 'i18n';
|
7 |
+
|
8 |
+
protected static $upload_dir,
|
9 |
+
$upload_url;
|
10 |
+
|
11 |
+
protected $base_class_name,
|
12 |
+
$base_dirname,
|
13 |
+
$base_filename,
|
14 |
+
$dirname,
|
15 |
+
$filename,
|
16 |
+
$i18n_path,
|
17 |
+
$i18n_path_from_plugins,
|
18 |
+
$options,
|
19 |
+
$plugin_path,
|
20 |
+
$unix_name;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Launch the configure process
|
24 |
+
* It is generally totally specific to each plugin.
|
25 |
+
*
|
26 |
+
* @author oncletom
|
27 |
+
* @protected
|
28 |
+
*/
|
29 |
+
protected function configure()
|
30 |
+
{
|
31 |
+
$this->configureOptions();
|
32 |
+
do_action($this->unix_name.'_configuration_configure', $this);
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Let the plugin configure its own options
|
37 |
+
*
|
38 |
+
* @author oncletom
|
39 |
+
* @abstract
|
40 |
+
* @protected
|
41 |
+
*/
|
42 |
+
abstract protected function configureOptions();
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Base constructor for a plugin configuration
|
46 |
+
*
|
47 |
+
* @author oncletom
|
48 |
+
* @since 1.0
|
49 |
+
* @version 1.0
|
50 |
+
* @param string $baseClassName
|
51 |
+
* @param string $baseFileName
|
52 |
+
*/
|
53 |
+
public function __construct($baseClassName, $baseFileName)
|
54 |
+
{
|
55 |
+
$unix_name_pattern = $baseClassName.'Configuration::UNIX_NAME';
|
56 |
+
$this->unix_name = constant($unix_name_pattern);
|
57 |
+
|
58 |
+
if (is_null($this->unix_name))
|
59 |
+
{
|
60 |
+
throw new Exception(sprintf('%s has not been configured for %sConfiguration.', $unix_name_pattern, $baseClassName));
|
61 |
+
}
|
62 |
+
|
63 |
+
$this->base_class_name = $baseClassName;
|
64 |
+
$this->setupPath($baseFileName, constant($unix_name_pattern));
|
65 |
+
$this->setupPathGlobal();
|
66 |
+
//$this->options = new $baseClassName.'OptionCollection';
|
67 |
+
|
68 |
+
$this->configure();
|
69 |
+
do_action($this->unix_name.'_configuration_construct', $this);
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Returns resolved plugin full path location
|
74 |
+
*
|
75 |
+
* @author oncletom
|
76 |
+
* @since 1.0
|
77 |
+
* @version 1.0
|
78 |
+
* @return string
|
79 |
+
*/
|
80 |
+
public function getDirname()
|
81 |
+
{
|
82 |
+
return $this->dirname;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Returns resolved plugin full path filename
|
87 |
+
*
|
88 |
+
* @author oncletom
|
89 |
+
* @since 1.0
|
90 |
+
* @version 1.0
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
public function getFilename()
|
94 |
+
{
|
95 |
+
return $this->filename;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Returns plugin prefix for classes
|
100 |
+
*
|
101 |
+
* @author oncletom
|
102 |
+
* @since 1.0
|
103 |
+
* @version 1.0
|
104 |
+
* @return string
|
105 |
+
*/
|
106 |
+
public function getPrefix()
|
107 |
+
{
|
108 |
+
return $this->base_class_name;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Returns resolved plugin path location, from plugin path
|
113 |
+
*
|
114 |
+
* In theory, it's the same as Unix path but in fact, if the plugin is renamed it can helps
|
115 |
+
* Not very used yet, though.
|
116 |
+
*
|
117 |
+
* @author oncletom
|
118 |
+
* @since 1.0
|
119 |
+
* @version 1.0
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
public function getPluginPath()
|
123 |
+
{
|
124 |
+
return $this->plugin_path;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Returns unix name of the plugin
|
129 |
+
*
|
130 |
+
* @author oncletom
|
131 |
+
* @since 1.0
|
132 |
+
* @version 1.0
|
133 |
+
* @return string
|
134 |
+
*/
|
135 |
+
public function getUnixName()
|
136 |
+
{
|
137 |
+
return $this->unix_name;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Returns the upload dir for this configuration class (common to all instances)
|
142 |
+
*
|
143 |
+
* @author oncletom
|
144 |
+
* @since 1.0
|
145 |
+
* @version 1.0
|
146 |
+
* @return string
|
147 |
+
*/
|
148 |
+
public function getUploadDir()
|
149 |
+
{
|
150 |
+
return self::$upload_dir;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Returns the upload URL for this configuration class (common to all instances)
|
155 |
+
*
|
156 |
+
* @author oncletom
|
157 |
+
* @since 1.0
|
158 |
+
* @version 1.0
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
public function getUploadUrl()
|
162 |
+
{
|
163 |
+
return self::$upload_url;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Build paths for various access
|
168 |
+
*
|
169 |
+
* @author oncletom
|
170 |
+
* @protected
|
171 |
+
* @since 1.0
|
172 |
+
* @version 1.0
|
173 |
+
* @param string $baseFileName
|
174 |
+
* @param string $unix_name
|
175 |
+
*/
|
176 |
+
protected function setupPath($baseFileName, $unix_name)
|
177 |
+
{
|
178 |
+
$this->base_filename = $baseFileName;
|
179 |
+
$this->base_dirname = dirname($baseFileName);
|
180 |
+
|
181 |
+
/*
|
182 |
+
* Plugin & i18n path
|
183 |
+
*/
|
184 |
+
if (function_exists('is_link') && is_link(WP_PLUGIN_DIR.'/'.$unix_name))
|
185 |
+
{
|
186 |
+
$this->filename = WP_PLUGIN_DIR.'/'.$unix_name.'/'.basename($this->base_filename);
|
187 |
+
$this->i18n_path = PLUGINDIR.'/'.$unix_name.'/i18n';
|
188 |
+
$this->i18n_path_from_plugins = $unix_name.'/i18n';
|
189 |
+
}
|
190 |
+
else
|
191 |
+
{
|
192 |
+
$this->filename = $this->base_filename;
|
193 |
+
$this->i18n_path = PLUGINDIR.'/'.dirname(plugin_basename($this->filename)).'/i18n';
|
194 |
+
$this->i18n_path_from_plugins = dirname(plugin_basename($this->filename)).'/i18n';
|
195 |
+
}
|
196 |
+
|
197 |
+
$this->dirname = dirname($this->filename);
|
198 |
+
$this->plugin_path = preg_replace('#(.+)([^/]+/[^/]+)$#sU', "$2", $this->filename);
|
199 |
+
do_action($this->unix_name.'_configuration_setup_path', $this);
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Resolves global upload path as WP does not provide any clean and independant solution for that
|
204 |
+
*
|
205 |
+
* It's barely based on the logic of `wp_upload_dir` function.
|
206 |
+
*
|
207 |
+
* @author oncletom
|
208 |
+
* @since 1.0
|
209 |
+
* @version 1.0
|
210 |
+
* @return boolean
|
211 |
+
*/
|
212 |
+
protected function setupPathGlobal()
|
213 |
+
{
|
214 |
+
if (isset(self::$upload_url))
|
215 |
+
{
|
216 |
+
return false;
|
217 |
+
}
|
218 |
+
|
219 |
+
$siteurl = get_option('siteurl');
|
220 |
+
$upload_path = trim(get_option('upload_path'));
|
221 |
+
$subdir = '/'.$this->unix_name;
|
222 |
+
|
223 |
+
if (defined('UPLOADS'))
|
224 |
+
{
|
225 |
+
$dir = ABSPATH.UPLOADS;
|
226 |
+
$url = trailingslashit($siteurl).UPLOADS;
|
227 |
+
}
|
228 |
+
else
|
229 |
+
{
|
230 |
+
$dir = $upload_path ? $upload_path : WP_CONTENT_DIR.'/uploads';
|
231 |
+
$dir = path_join(ABSPATH, $dir);
|
232 |
+
|
233 |
+
if (!$url = get_option( 'upload_url_path'))
|
234 |
+
{
|
235 |
+
$url = empty($upload_path) or ($upload_path == $dir)
|
236 |
+
? WP_CONTENT_URL . '/uploads'
|
237 |
+
: trailingslashit($siteurl).$upload_path;
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
$uploads = apply_filters('upload_dir', array(
|
242 |
+
'path' => $dir,
|
243 |
+
'url' => $url,
|
244 |
+
'subdir' => $subdir,
|
245 |
+
'basedir' => $dir.$subdir,
|
246 |
+
'baseurl' => $url.$subdir,
|
247 |
+
'error' => false,
|
248 |
+
));
|
249 |
+
|
250 |
+
self::$upload_dir = $uploads['basedir'];
|
251 |
+
self::$upload_url = $uploads['baseurl'];
|
252 |
+
|
253 |
+
return $uploads['error'] ? false : true;
|
254 |
+
}
|
255 |
+
}
|
lib/vendor/plugin-toolkit/BasePlugin.class.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Base plugin class to extend
|
4 |
+
*
|
5 |
+
* @author oncletom
|
6 |
+
* @package plugin-toolkit
|
7 |
+
*/
|
8 |
+
abstract class WPPluginToolkitPlugin
|
9 |
+
{
|
10 |
+
protected $configuration;
|
11 |
+
protected static $autoload_configured = false;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Plugin constructor
|
15 |
+
*
|
16 |
+
* @author oncletom
|
17 |
+
* @since 1.0
|
18 |
+
* @version 1.0
|
19 |
+
* @param WPPluginToolkitConfiguration $configuration
|
20 |
+
*/
|
21 |
+
public function __construct(WPPluginToolkitConfiguration $configuration)
|
22 |
+
{
|
23 |
+
$this->configuration = $configuration;
|
24 |
+
|
25 |
+
if (!self::$autoload_configured)
|
26 |
+
{
|
27 |
+
spl_autoload_register(array($this, 'configureAutoload'));
|
28 |
+
}
|
29 |
+
|
30 |
+
do_action($this->configuration->getUnixName().'_plugin_construct', $this);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Autoloads classes for this plugin
|
35 |
+
*
|
36 |
+
* @author oncletom
|
37 |
+
* @return boolean
|
38 |
+
* @param string $className
|
39 |
+
* @version 1.0
|
40 |
+
* @since 1.0
|
41 |
+
*/
|
42 |
+
public function configureAutoload($className)
|
43 |
+
{
|
44 |
+
$prefix = $this->configuration->getPrefix();
|
45 |
+
|
46 |
+
if (!preg_match('/^'.$prefix.'/U', $className))
|
47 |
+
{
|
48 |
+
return;
|
49 |
+
}
|
50 |
+
|
51 |
+
$libdir = $this->configuration->getDirname().'/lib';
|
52 |
+
$path = preg_replace('/([A-Z]{1})/U', "/$1", str_replace($prefix, '', $className)).'.class.php';
|
53 |
+
|
54 |
+
if (file_exists($libdir.$path))
|
55 |
+
{
|
56 |
+
require $libdir.$path;
|
57 |
+
}
|
58 |
+
|
59 |
+
return false;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* WordPress plugin builder
|
64 |
+
*
|
65 |
+
* @author oncletom
|
66 |
+
* @static
|
67 |
+
* @final
|
68 |
+
* @since 1.0
|
69 |
+
* @version 1.0
|
70 |
+
* @param string $baseClassName
|
71 |
+
* @param string $baseFileName
|
72 |
+
* @return $baseClassName+Plugin instance
|
73 |
+
*/
|
74 |
+
public final static function create($baseClassName, $baseFileName)
|
75 |
+
{
|
76 |
+
require_once dirname(__FILE__).'/BaseConfiguration.class.php';
|
77 |
+
require_once dirname($baseFileName).'/lib/Configuration.class.php';
|
78 |
+
|
79 |
+
$class = $baseClassName.'Plugin';
|
80 |
+
$configuration = $baseClassName.'Configuration';
|
81 |
+
|
82 |
+
list($class, $configuration) = apply_filters('plugin-toolkit_create', array($class, $configuration));
|
83 |
+
|
84 |
+
$object = new $class(new $configuration($baseClassName, $baseFileName));
|
85 |
+
do_action('plugin-toolkit_create', $object);
|
86 |
+
|
87 |
+
return $object;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Returns the current configuration
|
92 |
+
*
|
93 |
+
* @author oncletom
|
94 |
+
* @since 1.0
|
95 |
+
* @version 1.0
|
96 |
+
* @return WPPluginToolkitConfiguration instance
|
97 |
+
*/
|
98 |
+
public function getConfiguration()
|
99 |
+
{
|
100 |
+
return $this->configuration;
|
101 |
+
}
|
102 |
+
}
|
readme.txt
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== WP-LESS ===
|
2 |
+
Contributors: oncletom
|
3 |
+
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=752034
|
4 |
+
Tags: dev, theme, themes, toolkit, plugin-toolkit, less, lesscss, lessc, lessphp, productivity, style, stylesheet, api
|
5 |
+
Requires at least: 2.8
|
6 |
+
Tested up to: 2.8.x
|
7 |
+
Stable tag: 1.0
|
8 |
+
|
9 |
+
Implementation of LESS (Leaner CSS) in order to make themes development easier.
|
10 |
+
|
11 |
+
|
12 |
+
== Description ==
|
13 |
+
[LESS](http://lesscss.org) is a templating language based on top of CSS. It provides numerous enhancements to speed up development and make its maintenance easier.
|
14 |
+
|
15 |
+
You can count on:
|
16 |
+
|
17 |
+
* Variables
|
18 |
+
* Mixins (inheritance of rules)
|
19 |
+
* Nested Rules (write less, do more)
|
20 |
+
* Accessors (inherit a value from a specific rule)
|
21 |
+
* Functions (logic operations for dynamic results)
|
22 |
+
|
23 |
+
The plugin lets you concentrate on what you need: coding CSS. Everything else is handled automatically, from cache management to user delivery.
|
24 |
+
Seriously.
|
25 |
+
|
26 |
+
The sole requirement is to use WordPress API and LESS convention: the `.less` extension.
|
27 |
+
|
28 |
+
**Minimal Requirements**: PHP 5.1.2 and WordPress 2.8.
|
29 |
+
**Relies on**: [LESSPHP 0.1.5](http://leafo.net/lessphp/), [plugin-toolkit](http://wordpress.org/extend/plugins/plugin-toolkit/).
|
30 |
+
|
31 |
+
|
32 |
+
== Installation ==
|
33 |
+
|
34 |
+
= Automatic =
|
35 |
+
1. Search for the plugin name (`WP-LESS`)
|
36 |
+
1. Click on the install button
|
37 |
+
1. Activate it
|
38 |
+
|
39 |
+
= Manual =
|
40 |
+
1. Download the latest stable archive of the plugin
|
41 |
+
1. Unzip it in your plugin folder (by default, `wp-content/plugins`)
|
42 |
+
1. Activate it through your WordPress plugins administration page
|
43 |
+
|
44 |
+
== Changelog ==
|
45 |
+
= Version 1.0 =
|
46 |
+
|
47 |
+
* implemented the forthcoming `plugin-toolkit` to speed up plugin development
|
48 |
+
* bundled lessphp 0.1.5
|
49 |
+
* implemented API to let you control the plugin the way you want
|
50 |
+
* just in time compilation with static file caching
|
51 |
+
|
52 |
+
|
53 |
+
== Frequently Asked Questions ==
|
54 |
+
= How do I transform a LESS file into CSS? =
|
55 |
+
Consider this bit of code to automatically enqueue your stylesheet from your theme (or plugin):
|
56 |
+
`wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.css', array('blueprint'), '', 'screen, projection');`
|
57 |
+
|
58 |
+
To make it process by WP-LESS, switch to this way:
|
59 |
+
`wp_enqueue_style('mytheme', get_bloginfo('template_directory').'/style.less', array('blueprint'), '', 'screen, projection');`
|
60 |
+
|
61 |
+
You understood well: you just need to change the extension of the file.
|
62 |
+
|
63 |
+
= And if I don't use the wp_enqueue_style method? =
|
64 |
+
For the moment, it's the unique way to handle this.
|
65 |
+
Helpers will be provided soon to include LESS files in your templates in a fluent way.
|
66 |
+
|
67 |
+
= What if a *.less file contains only pure CSS? =
|
68 |
+
Nothing special. The LESS parser is fully compliant with CSS syntax.
|
69 |
+
It means nothing will be broken so don't worry.
|
70 |
+
|
71 |
+
= I'm a themer and I don't want to ask my users to activate this plugin =
|
72 |
+
It's a very good moto. Anyway, at the moment I don't provide any helper for this purpose.
|
73 |
+
It is planned for later.
|
74 |
+
|
75 |
+
If you are familiar with OOP programmation, you can make your way out of this. The source code is fully self-documented.
|
76 |
+
|
77 |
+
== Screenshots ==
|
78 |
+
|
79 |
+
1. Sample of LESS to CSS conversion.
|
screenshot-1.png
ADDED
Binary file
|