mirror of
https://github.com/YunoHost-Apps/dolibarr_ynh.git
synced 2024-09-03 18:35:53 +02:00
383 lines
No EOL
15 KiB
PHP
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
|
|
*/
|
|
}
|
|
} |