wproot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/wproot +7 -0
- data/lib/wproot.rb +11 -0
- data/lib/wproot/cli.rb +35 -0
- data/lib/wproot/compass.rb +15 -0
- data/lib/wproot/haml.rb +15 -0
- data/lib/wproot/version.rb +3 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/wproot_spec.rb +59 -0
- data/vendor/HamlPHP.php +7 -0
- data/vendor/HamlPHP/.gitignore +6 -0
- data/vendor/HamlPHP/MIT-LICENSE +22 -0
- data/vendor/HamlPHP/README.mkd +39 -0
- data/vendor/HamlPHP/src/HamlPHP/CommentNode.php +92 -0
- data/vendor/HamlPHP/src/HamlPHP/Compiler.php +109 -0
- data/vendor/HamlPHP/src/HamlPHP/ContentEvaluator/ContentEvaluator.php +16 -0
- data/vendor/HamlPHP/src/HamlPHP/ContentEvaluator/DefaultContentEvaluator.php +22 -0
- data/vendor/HamlPHP/src/HamlPHP/DoctypeNode.php +47 -0
- data/vendor/HamlPHP/src/HamlPHP/Element.php +618 -0
- data/vendor/HamlPHP/src/HamlPHP/ElementNode.php +222 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/CssFilter.php +31 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/Filter.php +18 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/FilterContainer.php +34 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/JavascriptFilter.php +29 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/PhpFilter.php +24 -0
- data/vendor/HamlPHP/src/HamlPHP/Filter/PlainFilter.php +46 -0
- data/vendor/HamlPHP/src/HamlPHP/FilterNode.php +29 -0
- data/vendor/HamlPHP/src/HamlPHP/HamlNode.php +75 -0
- data/vendor/HamlPHP/src/HamlPHP/HamlPHP.php +191 -0
- data/vendor/HamlPHP/src/HamlPHP/Helpers.php +136 -0
- data/vendor/HamlPHP/src/HamlPHP/Interpolation.php +71 -0
- data/vendor/HamlPHP/src/HamlPHP/NodeFactory.php +80 -0
- data/vendor/HamlPHP/src/HamlPHP/RootNode.php +133 -0
- data/vendor/HamlPHP/src/HamlPHP/Storage/DontEvaluateStorage.php +68 -0
- data/vendor/HamlPHP/src/HamlPHP/Storage/FileStorage.php +74 -0
- data/vendor/HamlPHP/src/HamlPHP/Storage/Storage.php +24 -0
- data/vendor/HamlPHP/src/HamlPHP/TagNode.php +125 -0
- data/vendor/HamlPHP/src/HamlPHP/Util/BaseException.php +340 -0
- data/vendor/HamlPHP/src/HamlPHP/Util/BaseObject.php +94 -0
- data/vendor/HamlPHP/src/HamlPHP/Util/StringScanner.php +989 -0
- data/wproot.gemspec +21 -0
- metadata +126 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
require_once dirname(__FILE__) . '/../Helpers.php';
|
4
|
+
|
5
|
+
interface ContentEvaluator
|
6
|
+
{
|
7
|
+
/**
|
8
|
+
* Evaluates a string. Content contains HTML and PHP code.
|
9
|
+
*
|
10
|
+
* @param $content A string
|
11
|
+
* @param $contentVariables an array of variables
|
12
|
+
* @param $id An identifier of the content. Could be a filename.
|
13
|
+
* @return string
|
14
|
+
*/
|
15
|
+
public function evaluate($content, array $contentVariables = array(), $id = null);
|
16
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
require_once 'ContentEvaluator.php';
|
4
|
+
|
5
|
+
class DefaultContentEvaluator implements ContentEvaluator
|
6
|
+
{
|
7
|
+
public function evaluate($content, array $contentVariables = array(), $id = null)
|
8
|
+
{
|
9
|
+
$tempFileName = tempnam("/tmp", "foo");
|
10
|
+
$fp = fopen($tempFileName, "w");
|
11
|
+
fwrite($fp, $content);
|
12
|
+
|
13
|
+
ob_start();
|
14
|
+
extract($contentVariables);
|
15
|
+
require $tempFileName;
|
16
|
+
$result = ob_get_clean();
|
17
|
+
|
18
|
+
fclose($fp);
|
19
|
+
unlink($tempFileName);
|
20
|
+
return $result;
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
require_once 'Interpolation.php';
|
4
|
+
|
5
|
+
class DoctypeNode extends HamlNode
|
6
|
+
{
|
7
|
+
const XHTML10_T = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
|
8
|
+
const XHTML10_S = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
|
9
|
+
const XHTML10_F = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
|
10
|
+
const HTML5 = '<!DOCTYPE html>';
|
11
|
+
|
12
|
+
private $_type;
|
13
|
+
|
14
|
+
public function __construct($line)
|
15
|
+
{
|
16
|
+
parent::__construct($line);
|
17
|
+
$parts = explode(' ', trim($line));
|
18
|
+
$this->_type = isset($parts[1]) ? $parts[1] : '!!!';
|
19
|
+
}
|
20
|
+
|
21
|
+
public function render()
|
22
|
+
{
|
23
|
+
$interpolation = new Interpolation($this->renderDoctype() . "\n");
|
24
|
+
return $interpolation->render();
|
25
|
+
}
|
26
|
+
|
27
|
+
private function renderDoctype()
|
28
|
+
{
|
29
|
+
if ($this->_type === '!!!') {
|
30
|
+
return DoctypeNode::XHTML10_T;
|
31
|
+
}
|
32
|
+
|
33
|
+
if ($this->_type === 'Strict') {
|
34
|
+
return DoctypeNode::XHTML10_S;
|
35
|
+
}
|
36
|
+
|
37
|
+
if ($this->_type === 'Frameset') {
|
38
|
+
return DoctypeNode::XHTML10_F;
|
39
|
+
}
|
40
|
+
|
41
|
+
if ($this->_type === '5') {
|
42
|
+
return DoctypeNode::HTML5;
|
43
|
+
}
|
44
|
+
|
45
|
+
return '';
|
46
|
+
}
|
47
|
+
}
|
@@ -0,0 +1,618 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
require_once 'HamlPHP.php';
|
4
|
+
require_once 'Util/StringScanner.php';
|
5
|
+
|
6
|
+
class SyntaxErrorException extends Exception
|
7
|
+
{}
|
8
|
+
|
9
|
+
class Element
|
10
|
+
{
|
11
|
+
//const HAML_REGEXP = '/^(?P<tag>%\w+)?(?P<id>#\w*)?(?P<classes>\.[\w\.\-]*)*(?P<attributes>\((?P<html_attrs>.+)\)|\{(?P<hash_attrs>.*)\})?(?P<php>=)?(?P<inline>[^\w\.#\{].*)?$/';
|
12
|
+
|
13
|
+
|
14
|
+
const ELEMENT = '%';
|
15
|
+
const ID = '#';
|
16
|
+
const KLASS = '.';
|
17
|
+
|
18
|
+
private $_haml = null;
|
19
|
+
private $_tag = null;
|
20
|
+
private $_id = null;
|
21
|
+
private $_classes = null;
|
22
|
+
private $_inlineContent = null;
|
23
|
+
private $_php = false;
|
24
|
+
private $_attributes = null;
|
25
|
+
private $_scanner;
|
26
|
+
private $_nukeOuterWhitespace;
|
27
|
+
private $_nukeInnerWhitespace;
|
28
|
+
|
29
|
+
private $_selfClosing = false;
|
30
|
+
private $_preserveWhitespace = false;
|
31
|
+
private $_useAttsHelper;
|
32
|
+
|
33
|
+
/**
|
34
|
+
* @var Compiler
|
35
|
+
*/
|
36
|
+
private $_compiler;
|
37
|
+
|
38
|
+
public function __construct($line, Compiler $compiler)
|
39
|
+
{
|
40
|
+
$this->_haml = $line;
|
41
|
+
$this->_scanner = new StringScanner($this->_haml);
|
42
|
+
$this->_compiler = $compiler;
|
43
|
+
$this->parseHaml();
|
44
|
+
$this->_compiler = null;
|
45
|
+
}
|
46
|
+
|
47
|
+
private function parseHaml()
|
48
|
+
{
|
49
|
+
$scanner = $this->_scanner;
|
50
|
+
|
51
|
+
if (! $scanner->check('/(?:%(?P<tag>[\\-:\\w]+))?(?P<attrs>[\\#\\.\\-:\\w]*)?(?P<rest>.*)/'))
|
52
|
+
throw new SyntaxErrorException("Invalid tag: \"${$this->haml}\".");
|
53
|
+
|
54
|
+
if (preg_match('/[\.#](\.|#|\z)/', $scanner['attrs']))
|
55
|
+
throw new SyntaxErrorException("Illegal element: classes and ids must have values.");
|
56
|
+
|
57
|
+
if (!empty($scanner['tag']))
|
58
|
+
$this->_tag = $scanner['tag'];
|
59
|
+
else
|
60
|
+
$this->_tag = 'div';
|
61
|
+
|
62
|
+
$classAndIdAtts = $this->_parseClassAndId($scanner['attrs']);
|
63
|
+
|
64
|
+
$scanner->string = $scanner['rest'];
|
65
|
+
|
66
|
+
$hashAtts = $htmlAtts = $objRef = false;
|
67
|
+
$merge_order = array(
|
68
|
+
$classAndIdAtts
|
69
|
+
);
|
70
|
+
|
71
|
+
while ($scanner->rest)
|
72
|
+
{
|
73
|
+
switch ($scanner->rest[0])
|
74
|
+
{
|
75
|
+
case '{':
|
76
|
+
if ($hashAtts)
|
77
|
+
break;
|
78
|
+
$hashAtts = $this->_parseHashAttrs($scanner);
|
79
|
+
$merge_order[] = $hashAtts;
|
80
|
+
break;
|
81
|
+
case '(':
|
82
|
+
if ($htmlAtts)
|
83
|
+
break;
|
84
|
+
$htmlAtts = $this->_parseHtmlAttrs($scanner);
|
85
|
+
$merge_order[] = $htmlAtts;
|
86
|
+
break;
|
87
|
+
case '[':
|
88
|
+
if ($objRef)
|
89
|
+
break;
|
90
|
+
$objRef = $this->_parseObjRef($scanner);
|
91
|
+
$merge_order[] = $objRef;
|
92
|
+
break;
|
93
|
+
default:
|
94
|
+
break 2;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
$this->_attributes = $this->_mergeAttributes($merge_order);
|
99
|
+
|
100
|
+
if (! $this->_attributes)
|
101
|
+
$this->_attributes = array();
|
102
|
+
|
103
|
+
$this->_useAttsHelper = $this->_containsPhpAttribute($this->_attributes);
|
104
|
+
|
105
|
+
if ($scanner->rest)
|
106
|
+
{
|
107
|
+
$scanner->scan('/(<>|><|[><])?([=\/\~&!])?(.*)?/');
|
108
|
+
$nuke_whitespace = trim($scanner[1]);
|
109
|
+
$action = $scanner[2];
|
110
|
+
$value = $scanner[3];
|
111
|
+
|
112
|
+
$this->_nukeOuterWhitespace = (mb_strpos($nuke_whitespace, '>') !== false);
|
113
|
+
$this->_nukeInnerWhitespace = (mb_strpos($nuke_whitespace, '<') !== false);
|
114
|
+
|
115
|
+
$escape_html = ($action == '&' || ($action != '!' && HamlPHP::$Config['escape_html']));
|
116
|
+
|
117
|
+
switch ($action)
|
118
|
+
{
|
119
|
+
case '/':
|
120
|
+
$this->_selfClosing = true;
|
121
|
+
$value = trim($value);
|
122
|
+
if(!empty($value))
|
123
|
+
throw new SyntaxErrorException("Self closing tags can't have content.");
|
124
|
+
break;
|
125
|
+
|
126
|
+
case '~': // whitespace preservation
|
127
|
+
throw new Exception("Whitespace preservation (~) is not implemented yet.");
|
128
|
+
$parse = $this->_preserveWhitespace = true;
|
129
|
+
break;
|
130
|
+
|
131
|
+
case '=': //
|
132
|
+
$this->_php = true;
|
133
|
+
$this->_inlineContent = $this->_interpolate(mb_substr($value, 1));
|
134
|
+
break;
|
135
|
+
|
136
|
+
case '&': // escape html
|
137
|
+
case '!': // unescape html
|
138
|
+
throw new Exception("Escape (&) and unescape (!) html features are not implemented yet.");
|
139
|
+
if ($value[0] == '=' || $value[0] == '~')
|
140
|
+
{
|
141
|
+
$parse = true;
|
142
|
+
$this->_preserveWhitespace = ($value[0] == '~');
|
143
|
+
if ($value[1] == '=')
|
144
|
+
{
|
145
|
+
$value = $this->_interpolate(mb_substr($value, 2, - 1), $escape_html);
|
146
|
+
$escape_html = false;
|
147
|
+
}
|
148
|
+
else
|
149
|
+
$value = mb_substr($value, 1, - 1);
|
150
|
+
}
|
151
|
+
else
|
152
|
+
$this->_interpolate($value, $escape_html);
|
153
|
+
|
154
|
+
break;
|
155
|
+
|
156
|
+
default:
|
157
|
+
$value = trim($value);
|
158
|
+
if(!empty($value))
|
159
|
+
$this->_inlineContent = $this->_interpolate($value, $escape_html);
|
160
|
+
}
|
161
|
+
|
162
|
+
}
|
163
|
+
|
164
|
+
/*
|
165
|
+
raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
|
166
|
+
raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
|
167
|
+
raise SyntaxError.new("Self-closing tags can't have content.", last_line - 1) if self_closing && !value.empty?
|
168
|
+
|
169
|
+
if block_opened? && !value.empty? && !is_ruby_multiline?(value)
|
170
|
+
raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index)
|
171
|
+
end
|
172
|
+
|
173
|
+
self_closing ||= !!(!block_opened? && value.empty? && @options[:autoclose].any? {|t| t === tag_name})
|
174
|
+
value = nil if value.empty? && (block_opened? || self_closing)
|
175
|
+
value = handle_ruby_multiline(value) if parse
|
176
|
+
*/
|
177
|
+
}
|
178
|
+
|
179
|
+
private function _containsPhpAttribute(array $att_arr)
|
180
|
+
{
|
181
|
+
foreach ($att_arr as $att)
|
182
|
+
{
|
183
|
+
if (isset($att['t']) && ($att['t'] == 'php' || $att['t'] == 'function'))
|
184
|
+
return true;
|
185
|
+
}
|
186
|
+
|
187
|
+
return false;
|
188
|
+
}
|
189
|
+
|
190
|
+
private function _mergeAttributes($att_arrays)
|
191
|
+
{
|
192
|
+
$merged = array();
|
193
|
+
|
194
|
+
$count = count($att_arrays);
|
195
|
+
|
196
|
+
if ($count == 1)
|
197
|
+
return $att_arrays[0];
|
198
|
+
|
199
|
+
for ($i = 0; $i < $count; $i ++)
|
200
|
+
{
|
201
|
+
foreach ($att_arrays[$i] as $k => $v)
|
202
|
+
{
|
203
|
+
if(is_array($v))
|
204
|
+
$att = $v;
|
205
|
+
else
|
206
|
+
$att = array($v);
|
207
|
+
|
208
|
+
for ($j = $i + 1; $j < $count; $j ++)
|
209
|
+
{
|
210
|
+
if (isset($att_arrays[$j][$k]))
|
211
|
+
{
|
212
|
+
if(is_array($att_arrays[$j][$k]) && is_array($v))
|
213
|
+
$att = array_merge($att, $att_arrays[$j][$k]);
|
214
|
+
else
|
215
|
+
$att[] = $att_arrays[$j][$k];
|
216
|
+
|
217
|
+
unset($att_arrays[$j][$k]);
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
if (count($att) == 1 && !is_array($v))
|
222
|
+
$merged[$k] = $v;
|
223
|
+
else
|
224
|
+
$merged[$k] = $att;
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
return $merged;
|
229
|
+
}
|
230
|
+
|
231
|
+
private function _parseObjRef($scanner)
|
232
|
+
{
|
233
|
+
$scanner->scan('/\\[*\s*/');
|
234
|
+
|
235
|
+
$obj = $scanner->scan('/[^,\\]]+/');
|
236
|
+
$scanner->scan('/\s*,\s*/');
|
237
|
+
$prefix = $scanner->scan('/[^\\]]*/');
|
238
|
+
if (! $prefix)
|
239
|
+
$prefix = '';
|
240
|
+
$end = trim($scanner->scan('/\s*\\]/'));
|
241
|
+
|
242
|
+
if ($end != ']')
|
243
|
+
throw new SyntaxErrorException("Invalid object reference.");
|
244
|
+
|
245
|
+
return array(
|
246
|
+
'id' => array(array(
|
247
|
+
't' => 'php' , 'v' => "id_for($obj, '$prefix')"
|
248
|
+
)),
|
249
|
+
'class' => array(array(
|
250
|
+
't' => 'php' , 'v' => "class_for($obj, '$prefix')"
|
251
|
+
))
|
252
|
+
);
|
253
|
+
}
|
254
|
+
|
255
|
+
private function _parseClassAndId($list)
|
256
|
+
{
|
257
|
+
$list = new StringScanner($list);
|
258
|
+
$attributes = array();
|
259
|
+
|
260
|
+
while ($list->scan('/([#.])([-:_a-zA-Z0-9]+)/'))
|
261
|
+
{
|
262
|
+
$type = $list[1];
|
263
|
+
$prop = $list[2];
|
264
|
+
|
265
|
+
switch ($type)
|
266
|
+
{
|
267
|
+
case '.':
|
268
|
+
$attributes['class'][] = array('t' => 'str', 'v' => $prop);
|
269
|
+
break;
|
270
|
+
case '#':
|
271
|
+
$attributes['id'][] = array('t' => 'str', 'v' => $prop);
|
272
|
+
break;
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
return $attributes;
|
277
|
+
}
|
278
|
+
|
279
|
+
private function _parseHtmlAttrs(StringScanner $scanner)
|
280
|
+
{
|
281
|
+
$atts = array(
|
282
|
+
'class' => array(),
|
283
|
+
'id' => array()
|
284
|
+
);
|
285
|
+
|
286
|
+
$scanner->scan('/\(\s*/');
|
287
|
+
while (! $scanner->scan('/\\s*\\)/'))
|
288
|
+
{
|
289
|
+
$scanner->scan('/\s*/');
|
290
|
+
|
291
|
+
if (! $name = $scanner->scan('/[-:\w]+/'))
|
292
|
+
throw new SyntaxErrorException("Invalid attribute list: {$scanner->string}");
|
293
|
+
|
294
|
+
$scanner->scan('/\s*/');
|
295
|
+
|
296
|
+
// If a equal sign doesn't follow, the value is true
|
297
|
+
if (! $scanner->scan('/=/'))
|
298
|
+
$atts[$name] = array(
|
299
|
+
't' => 'static' , 'v' => true
|
300
|
+
);
|
301
|
+
else
|
302
|
+
{
|
303
|
+
$scanner->scan('/\s*/');
|
304
|
+
|
305
|
+
// if we don't find a quote, it's a php value
|
306
|
+
if (! ($quote = $scanner->scan('/["\\\']/')))
|
307
|
+
{
|
308
|
+
if (! $var = $scanner->scan('/\$\w+/')) // in this mode only variables are accepted
|
309
|
+
throw new SyntaxErrorException("Invalid attribute list: {$scanner->string}");
|
310
|
+
|
311
|
+
if($name == 'class' || $name == 'id')
|
312
|
+
$atts[$name][] = array(
|
313
|
+
't' => 'php' , 'v' => $var
|
314
|
+
);
|
315
|
+
else
|
316
|
+
$atts[$name] = array(
|
317
|
+
't' => 'php' , 'v' => $var
|
318
|
+
);
|
319
|
+
}
|
320
|
+
elseif ($scanner->scan('/true|false/i'))
|
321
|
+
{
|
322
|
+
if ($scanner[0] == 'true')
|
323
|
+
$atts[$name] = array(
|
324
|
+
't' => 'static' , 'v' => true
|
325
|
+
);
|
326
|
+
else
|
327
|
+
$atts[$name] = array(
|
328
|
+
't' => 'static' , 'v' => false
|
329
|
+
);
|
330
|
+
}
|
331
|
+
else
|
332
|
+
{
|
333
|
+
// we've found a quote, let's scan until the ending quote
|
334
|
+
$scanner->scan("/([^\\\\]*?)$quote/");
|
335
|
+
$content = $scanner[1];
|
336
|
+
|
337
|
+
if($name == 'class' || $name == 'id')
|
338
|
+
$atts[$name][] = array(
|
339
|
+
't' => 'str' , 'v' => $content
|
340
|
+
);
|
341
|
+
else
|
342
|
+
$atts[$name] = array(
|
343
|
+
't' => 'str' , 'v' => $quote . $content . $quote
|
344
|
+
);
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
$scanner->scan('/\s*/');
|
349
|
+
if ($scanner->eos)
|
350
|
+
{
|
351
|
+
$next_line = ' ' . trim($this->_compiler->getNextLine());
|
352
|
+
$scanner->concat($next_line);
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
if(count($atts['id']) == 0)
|
357
|
+
unset($atts['id']);
|
358
|
+
|
359
|
+
if(count($atts['class']) == 0)
|
360
|
+
unset($atts['class']);
|
361
|
+
|
362
|
+
return $atts;
|
363
|
+
}
|
364
|
+
|
365
|
+
/**
|
366
|
+
* @param StringScanner $scanner
|
367
|
+
* @return array an array with the parsed elements
|
368
|
+
*/
|
369
|
+
private function _parseHashAttrs(StringScanner $scanner)
|
370
|
+
{
|
371
|
+
$atts = array(
|
372
|
+
'class' => array(),
|
373
|
+
'id' => array()
|
374
|
+
);
|
375
|
+
$litRe = '/(["\\\']).*?\\1(?=[\\s|=])|:?[\\-\\w:]*/';
|
376
|
+
|
377
|
+
$scanner->scan('/\\s*\\{?\\s*/');
|
378
|
+
|
379
|
+
while (! $scanner->scan('/}/'))
|
380
|
+
{
|
381
|
+
$scanner->scan('/\\s*/');
|
382
|
+
|
383
|
+
$name = trim($scanner->scan($litRe), ':"\'');
|
384
|
+
if (! $name)
|
385
|
+
throw new SyntaxErrorException("Invalid attribute list. Expecting an attribute name");
|
386
|
+
|
387
|
+
if (! $scanner->scan('/\s*=>\s*/'))
|
388
|
+
{
|
389
|
+
// it's an attribute function
|
390
|
+
if(!($scanner->rest[0] == '('))
|
391
|
+
throw new SyntaxErrorException("Invalid attribute list. Either missing attribute function parameters or attribute value");
|
392
|
+
|
393
|
+
list ($balanced, $value, $rest, $count) = $this->balance($scanner, '(', ')', 0);
|
394
|
+
|
395
|
+
if(!$balanced)
|
396
|
+
throw new SyntaxErrorException("Unbalanced brackets in attribute function");
|
397
|
+
|
398
|
+
$value = $name.$value;
|
399
|
+
$name = 'fn_'.count($atts);
|
400
|
+
$atts[$name] = array(
|
401
|
+
't' => 'function' , 'v' => $value
|
402
|
+
);
|
403
|
+
$scanner->scan('/\\s*,\\s*/');
|
404
|
+
continue;
|
405
|
+
}
|
406
|
+
|
407
|
+
switch ($scanner->rest[0])
|
408
|
+
{
|
409
|
+
case '"':
|
410
|
+
case "'":
|
411
|
+
$quote = $scanner->scan('/["\']/');
|
412
|
+
$value = $scanner->scan("/(.*?[^\\\\])$quote/");
|
413
|
+
|
414
|
+
if($name == 'class' || $name == 'id')
|
415
|
+
$atts[$name][] = array(
|
416
|
+
't' => 'str' , 'v' => $scanner[1]
|
417
|
+
);
|
418
|
+
else
|
419
|
+
{
|
420
|
+
$value = $this->_interpolate($quote . $value);
|
421
|
+
$atts[$name] = array(
|
422
|
+
't' => 'str' , 'v' => $value
|
423
|
+
);
|
424
|
+
}
|
425
|
+
break;
|
426
|
+
|
427
|
+
case '[':
|
428
|
+
$value = $scanner->scanUntil('/\]/');
|
429
|
+
$items = mb_substr($value, 1, - 1);
|
430
|
+
|
431
|
+
if($name == 'class' || $name == 'id')
|
432
|
+
$atts[$name][] = array(
|
433
|
+
't' => 'php' , 'v' => "array($items)"
|
434
|
+
);
|
435
|
+
else
|
436
|
+
$atts[$name] = array(
|
437
|
+
't' => 'php' , 'v' => "array($items)"
|
438
|
+
);
|
439
|
+
break;
|
440
|
+
|
441
|
+
case '{':
|
442
|
+
list ($balanced, $value, $rest, $count) = $this->balance($scanner, '{', '}', 0);
|
443
|
+
$value = mb_substr($value, 1, - 1);
|
444
|
+
|
445
|
+
if($name == 'class' || $name == 'id')
|
446
|
+
$atts[$name][] = array(
|
447
|
+
't' => 'php' , 'v' => $value
|
448
|
+
);
|
449
|
+
else
|
450
|
+
$atts[$name] = array(
|
451
|
+
't' => 'php' , 'v' => $value
|
452
|
+
);
|
453
|
+
break;
|
454
|
+
|
455
|
+
default:
|
456
|
+
if ($scanner->scan('/true|false/i'))
|
457
|
+
{
|
458
|
+
if ($scanner[0] == 'true')
|
459
|
+
$atts[$name] = array(
|
460
|
+
't' => 'static' , 'v' => true
|
461
|
+
);
|
462
|
+
else
|
463
|
+
$atts[$name] = array(
|
464
|
+
't' => 'static' , 'v' => false
|
465
|
+
);
|
466
|
+
}
|
467
|
+
else
|
468
|
+
{
|
469
|
+
$value = trim($scanner->scanUntil('/.(?=,)|.(?=\\})/'));
|
470
|
+
|
471
|
+
if($name == 'class' || $name == 'id')
|
472
|
+
$atts[$name][] = array(
|
473
|
+
't' => 'php' , 'v' => $value
|
474
|
+
);
|
475
|
+
else
|
476
|
+
$atts[$name] = array(
|
477
|
+
't' => 'php' , 'v' => $value
|
478
|
+
);
|
479
|
+
}
|
480
|
+
}
|
481
|
+
|
482
|
+
$scanner->scan('/\s*(,)\s*/');
|
483
|
+
if ($scanner->eos)
|
484
|
+
{
|
485
|
+
$next_line = ' ' . trim($this->_compiler->getNextLine());
|
486
|
+
$scanner->concat($next_line);
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
if(count($atts['id']) == 0)
|
491
|
+
unset($atts['id']);
|
492
|
+
|
493
|
+
if(count($atts['class']) == 0)
|
494
|
+
unset($atts['class']);
|
495
|
+
|
496
|
+
return $atts;
|
497
|
+
}
|
498
|
+
|
499
|
+
/**
|
500
|
+
* Checks if the string has a balanced amount of $start_char and $finish_char pairs
|
501
|
+
* If you pass a scanner, the pointer of the scanner will be altered!
|
502
|
+
*
|
503
|
+
* @param $scanner_or_line The line or a scanner to balance
|
504
|
+
* @param $start_char The balancing start char
|
505
|
+
* @param $finish_char The balancing end char
|
506
|
+
* @param $count [Optional] The current balance count
|
507
|
+
* @return array [$balanced, $balanced_str, $rest, $count]
|
508
|
+
*/
|
509
|
+
private function balance($scanner_or_line, $start_char, $finish_char, $count = 0)
|
510
|
+
{
|
511
|
+
$str = '';
|
512
|
+
$regexp = "/(.*?)[\\$start_char\\$finish_char]/";
|
513
|
+
|
514
|
+
if (! ($scanner_or_line instanceof StringScanner))
|
515
|
+
$scanner = new StringScanner($scanner_or_line);
|
516
|
+
else
|
517
|
+
$scanner = $scanner_or_line;
|
518
|
+
|
519
|
+
while ($scanner->scan($regexp))
|
520
|
+
{
|
521
|
+
$str .= $scanner->matched;
|
522
|
+
if (mb_substr($scanner->matched, - 1, 1) == $start_char)
|
523
|
+
$count ++;
|
524
|
+
if (mb_substr($scanner->matched, - 1, 1) == $finish_char)
|
525
|
+
$count --;
|
526
|
+
|
527
|
+
if ($count == 0)
|
528
|
+
return array(
|
529
|
+
true , trim($str) , $scanner->rest , $count
|
530
|
+
);
|
531
|
+
}
|
532
|
+
|
533
|
+
return array(
|
534
|
+
false , trim($str) , $scanner->rest , $count
|
535
|
+
);
|
536
|
+
}
|
537
|
+
|
538
|
+
private function _interpolate($str)
|
539
|
+
{
|
540
|
+
if (! preg_match('/(?<!\\\\)\\#\\{/', $str))
|
541
|
+
return $str;
|
542
|
+
|
543
|
+
if ($str[0] != '"' && $str[0] != "'")
|
544
|
+
{
|
545
|
+
$int = new Interpolation($str);
|
546
|
+
return $int->render();
|
547
|
+
}
|
548
|
+
|
549
|
+
$nStr = '';
|
550
|
+
$s = new StringScanner($str);
|
551
|
+
|
552
|
+
$quote = $s->scan('/["\']/');
|
553
|
+
|
554
|
+
while (! $s->eos)
|
555
|
+
{
|
556
|
+
$part = $s->scan('/([^\\\\]*)\\#\\{/');
|
557
|
+
|
558
|
+
if (! $part)
|
559
|
+
break;
|
560
|
+
|
561
|
+
$nStr = $s[1] . $quote . '.(';
|
562
|
+
|
563
|
+
$part = $s->scan('/([^\\\\]*)\\}/');
|
564
|
+
|
565
|
+
if (! $part)
|
566
|
+
throw new SyntaxErrorException("Unclosed interpolation");
|
567
|
+
|
568
|
+
$nStr .= $s[1] . ').' . $quote;
|
569
|
+
}
|
570
|
+
|
571
|
+
if ($s->rest == $quote)
|
572
|
+
return $quote . trim($nStr, ". {$quote}");
|
573
|
+
|
574
|
+
return $quote . $nStr . $s->rest;
|
575
|
+
}
|
576
|
+
|
577
|
+
public function getTag()
|
578
|
+
{
|
579
|
+
return $this->_tag;
|
580
|
+
}
|
581
|
+
|
582
|
+
public function getId()
|
583
|
+
{
|
584
|
+
if(isset($this->_attributes['id']))
|
585
|
+
return $this->_attributes['id'];
|
586
|
+
|
587
|
+
return null;
|
588
|
+
}
|
589
|
+
|
590
|
+
public function isPhpVariable()
|
591
|
+
{
|
592
|
+
return $this->_php;
|
593
|
+
}
|
594
|
+
|
595
|
+
public function getClasses()
|
596
|
+
{
|
597
|
+
if(isset($this->_attributes['class']))
|
598
|
+
return $this->_attributes['class'];
|
599
|
+
|
600
|
+
}
|
601
|
+
|
602
|
+
public function getAttributes()
|
603
|
+
{
|
604
|
+
return $this->_attributes;
|
605
|
+
}
|
606
|
+
|
607
|
+
public function getInlineContent()
|
608
|
+
{
|
609
|
+
return $this->_inlineContent;
|
610
|
+
}
|
611
|
+
|
612
|
+
public function useAttsHelper()
|
613
|
+
{
|
614
|
+
return $this->_useAttsHelper;
|
615
|
+
}
|
616
|
+
}
|
617
|
+
|
618
|
+
?>
|