1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/dolibarr_ynh.git synced 2024-09-03 18:35:53 +02:00
dolibarr_ynh/sources/dolibarr/htdocs/includes/restler/UI/Emmet.php
Laurent Peuch e6008fc691 init
2015-09-28 22:09:38 +02:00

383 lines
No EOL
15 KiB
PHP

<?php
namespace Luracast\Restler\UI;
use Luracast\Restler\UI\Tags as T;
use Luracast\Restler\Util;
class Emmet
{
const DELIMITERS = '.#*>+^[=" ]{$@-#}';
/**
* Create the needed tag hierarchy from emmet string
*
* @param string $string
*
* @param array|string $data
*
* @return array|T
*/
public static function make($string, $data = null)
{
if (!strlen($string))
return array();
$implicitTag =
function () use (& $tag) {
if (empty($tag->tag)) {
switch ($tag->parent->tag) {
case 'ul':
case 'ol':
$tag->tag = 'li';
break;
case 'em':
$tag->tag = 'span';
break;
case 'table':
case 'tbody':
case 'thead':
case 'tfoot':
$tag->tag = 'tr';
break;
case 'tr':
$tag->tag = 'td';
break;
case 'select':
case 'optgroup':
$tag->tag = 'option';
break;
default:
$tag->tag = 'div';
}
}
};
$parseText =
function (
$text, $round, $total, $data, $delimiter = null
)
use (
& $tokens, & $tag
) {
$digits = 0;
if ($delimiter == null)
$delimiter = array(
'.' => true,
'#' => true,
'*' => true,
'>' => true,
'+' => true,
'^' => true,
'[' => true,
']' => true,
'=' => true,
);
while (!empty($tokens) &&
!isset($delimiter[$t = array_shift($tokens)])) {
while ('$' === $t) {
$digits++;
$t = array_shift($tokens);
}
if ($digits) {
$negative = false;
$offset = 0;
if ('@' == $t) {
if ('-' == ($t = array_shift($tokens))) {
$negative = true;
if (is_numeric(reset($tokens))) {
$offset = array_shift($tokens);
}
} elseif (is_numeric($t)) {
$offset = $t;
} else {
array_unshift($tokens, $t);
}
} elseif ('#' == ($h = array_shift($tokens))) {
if (!empty($t)) {
$data = Util::nestedValue($data, $t);
if (is_null($data)) {
return null;
}
}
if (is_numeric($data)) {
$text .= sprintf("%0{$digits}d", (int)$data);
} elseif (is_string($data)) {
$text .= $data;
}
$digits = 0;
continue;
} else {
array_unshift($tokens, $t, $h);
}
if ($negative) {
$n = $total + 1 - $round + $offset;
} else {
$n = $round + $offset;
}
$text .= sprintf("%0{$digits}d", $n);
$digits = 0;
} else {
$text .= $t;
}
}
if (isset($t))
array_unshift($tokens, $t);
return $text;
};
$parseAttributes =
function (Callable $self, $round, $total, $data)
use (& $tokens, & $tag, $parseText) {
$a = $parseText(
'', $round, $total, $data
);
if (is_null($a))
return;
if ('=' == ($v = array_shift($tokens))) {
//value
if ('"' == ($v = array_shift($tokens))) {
$text = '';
$tag->$a($parseText(
$text, $round, $total, $data,
array('"' => true)
));
} else {
array_unshift($tokens, $v);
$text = '';
$tag->$a($parseText(
$text, $round, $total, $data,
array(' ' => true, ']' => true)
));
}
if (' ' == ($v = array_shift($tokens))) {
$self($self, $round, $total, $data);
}
} elseif (']' == $v) {
//end
$tag->$a('');
return;
} elseif (' ' == $v) {
$tag->$a('');
$self($self, $round, $total, $data);
}
};
$tokens = static::tokenize($string);
$tag = new T(array_shift($tokens));
$parent = $root = new T;
$parse =
function (
Callable $self, $round = 1, $total = 1
)
use (
& $tokens, & $parent, & $tag, & $data,
$parseAttributes, $implicitTag, $parseText
) {
$offsetTokens = null;
$parent[] = $tag;
$isInChild = false;
while ($tokens) {
switch (array_shift($tokens)) {
//class
case '.':
$offsetTokens = array_values($tokens);
array_unshift($offsetTokens, '.');
$implicitTag();
$e = array_filter(explode(' ', $tag->class));
$e[] = $parseText('', $round, $total, $data);
$tag->class(implode(' ', array_unique($e)));
break;
//id
case '#':
$offsetTokens = array_values($tokens);
array_unshift($offsetTokens, '#');
$implicitTag();
$tag->id(
$parseText(
array_shift($tokens), $round, $total, $data
)
);
break;
//attributes
case '[':
$offsetTokens = array_values($tokens);
array_unshift($offsetTokens, '[');
$implicitTag();
$parseAttributes(
$parseAttributes, $round, $total, $data
);
break;
//child
case '{':
$text = '';
$tag[] = $parseText(
$text, $round, $total, $data, array('}' => true)
);
break;
case '>':
$isInChild = true;
$offsetTokens = null;
if ('{' == ($t = array_shift($tokens))) {
array_unshift($tokens, $t);
$child = new T();
$tag[] = $child;
$parent = $tag;
$tag = $child;
} elseif ('[' == $t) {
array_unshift($tokens, $t);
} else {
$child = new T($t);
$tag[] = $child;
$parent = $tag;
$tag = $child;
}
break;
//sibling
case '+':
$offsetTokens = null;
if (!$isInChild && $round != $total) {
$tokens = array();
break;
}
if ('{' == ($t = array_shift($tokens))) {
$tag = $tag->parent;
array_unshift($tokens, $t);
break;
} elseif ('[' == $t) {
array_unshift($tokens, $t);
} else {
$child = new T($t);
$tag = $tag->parent;
$tag[] = $child;
$tag = $child;
}
break;
//sibling of parent
case '^':
if ($round != $total) {
$tokens = array();
break;
}
$tag = $tag->parent;
if ($tag->parent)
$tag = $tag->parent;
while ('^' == ($t = array_shift($tokens))) {
if ($tag->parent)
$tag = $tag->parent;
}
$child = new T($t);
$tag[] = $child;
$tag = $child;
break;
//clone
case '*':
$times = array_shift($tokens);
$removeCount = 2;
$delimiter = array(
'.' => true,
'#' => true,
'*' => true,
'>' => true,
'+' => true,
'^' => true,
'[' => true,
']' => true,
'=' => true,
);
if (!is_numeric($times)) {
if (is_string($times)) {
if (!isset($delimiter[$times])) {
$data = Util::nestedValue($data, $times)
? : $data;
} else {
array_unshift($tokens, $times);
$removeCount = 1;
}
}
$indexed = array_values($data);
$times = is_array($data) && $indexed == $data
? count($data) : 0;
}
$source = $tag;
if (!empty($offsetTokens)) {
if (false !== strpos($source->class, ' ')) {
$class = explode(' ', $source->class);
array_pop($class);
$class = implode(' ', $class);
} else {
$class = null;
}
$tag->class($class);
$star = array_search('*', $offsetTokens);
array_splice($offsetTokens, $star, $removeCount);
$remainingTokens = $offsetTokens;
} else {
$remainingTokens = $tokens;
}
$source->parent = null;
$sourceData = $data;
$currentParent = $parent;
for ($i = 1; $i <= $times; $i++) {
$tag = clone $source;
$parent = $currentParent;
$data = is_array($sourceData)
&& isset($sourceData[$i - 1])
? $sourceData[$i - 1]
: @(string)$sourceData;
$tokens = array_values($remainingTokens);
$self($self, $i, $times);
}
$round = 1;
$offsetTokens = null;
$tag = $source;
$tokens = array(); //$remainingTokens;
break;
}
}
};
$parse($parse);
return count($root) == 1 ? $root[0] : $root;
}
public static function tokenize($string)
{
$r = array();
$f = strtok($string, static::DELIMITERS);
$pos = 0;
do {
$start = $pos;
$pos = strpos($string, $f, $start);
$tokens = array();
for ($i = $start; $i < $pos; $i++) {
$token = $string{$i};
if (('#' == $token || '.' == $token) &&
(!empty($tokens) || $i == 0)
) {
$r[] = '';
}
$r[] = $tokens[] = $token;
}
$pos += strlen($f);
$r[] = $f;
} while (false != ($f = strtok(static::DELIMITERS)));
for ($i = $pos; $i < strlen($string); $i++) {
$token = $string{$i};
$r[] = $tokens[] = $token;
}
return $r;
/* sample output produced by ".row*3>.col*3"
[0] => div
[1] => .
[2] => row
[3] => *
[4] => 3
[5] => >
[6] => div
[7] => .
[8] => col
[9] => *
[10] => 4
*/
}
}