396 lines
11 KiB
PHP
396 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
/**
|
|
* The dom widget class file of zin of ZenTaoPMS.
|
|
*
|
|
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
|
* @author Hao Sun <sunhao@easycorp.ltd>
|
|
* @package zin
|
|
* @version $Id
|
|
* @link https://www.zentao.net
|
|
*/
|
|
|
|
namespace zin;
|
|
|
|
use stdClass;
|
|
|
|
require_once dirname(__DIR__) . DS . 'utils' . DS . 'deep.func.php';
|
|
require_once __DIR__ . DS . 'selector.func.php';
|
|
|
|
class dom
|
|
{
|
|
/**
|
|
* @var wg
|
|
*/
|
|
public $wg;
|
|
|
|
public $children = array();
|
|
|
|
public $selectors = null;
|
|
|
|
public $renderInner = false;
|
|
|
|
public $renderType;
|
|
|
|
public $dataGetters = null;
|
|
|
|
public $dataCommands;
|
|
|
|
public $buildList = null;
|
|
|
|
public $buildListInner = false;
|
|
|
|
/**
|
|
* Construct the dom object.
|
|
*
|
|
* @param wg $wg
|
|
* @param array $children
|
|
* @param array|string|object $selectors
|
|
* @access public
|
|
*/
|
|
public function __construct($wg, $children, $selectors = null, $renderType = null, $dataCommands = null)
|
|
{
|
|
$this->wg = $wg;
|
|
$this->renderType = $renderType;
|
|
|
|
$this->add($children);
|
|
$this->addSelectors($selectors);
|
|
$this->addDataCommands($dataCommands);
|
|
}
|
|
|
|
public function __debugInfo()
|
|
{
|
|
return array(
|
|
'gid' => $this->wg->gid,
|
|
'type' => $this->wg->type(),
|
|
'count' => count($this->children),
|
|
'renderInner' => $this->renderInner,
|
|
'renderType' => $this->renderType,
|
|
'dataCommands' => $this->dataCommands,
|
|
'selectors' => stringifyWgSelectors($this->selectors)
|
|
);
|
|
}
|
|
|
|
public function add($children)
|
|
{
|
|
if(empty($children)) return;
|
|
|
|
if(!is_array($children)) $children = [$children];
|
|
foreach($children as $child)
|
|
{
|
|
if(is_array($child)) $this->add($child);
|
|
else $this->children[] = $child;
|
|
}
|
|
}
|
|
|
|
public function addDataCommands($commands)
|
|
{
|
|
if(empty($commands)) return;
|
|
|
|
if(is_string($commands))
|
|
{
|
|
$commandList = explode(',', $commands);
|
|
$commands = array();
|
|
foreach($commandList as $command)
|
|
{
|
|
$parts = explode(':', $command, 2);
|
|
$commands[$parts[0]] = count($parts) > 1 ? $parts[1] : $parts[0];
|
|
}
|
|
}
|
|
|
|
if($this->dataCommands === null) $this->dataCommands = array();
|
|
$index = 0;
|
|
foreach($commands as $key => $command)
|
|
{
|
|
$this->dataCommands[$index == $key ? $command : $key] = $command;
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
public function addSelectors($selectors)
|
|
{
|
|
if(empty($selectors)) return;
|
|
|
|
if($this->selectors === null) $this->selectors = array();
|
|
$selectors = parseWgSelectors($selectors);
|
|
foreach($selectors as $selector)
|
|
{
|
|
if(isset($selector->command) && !empty($selector->command)) $this->addDataCommands([$selector->tag => $selector->command]);
|
|
else $this->selectors[] = $selector;
|
|
}
|
|
}
|
|
|
|
public function isMatch($selector)
|
|
{
|
|
return $this->wg->isMatch($selector);
|
|
}
|
|
|
|
/**
|
|
* Build the children dom list.
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function build()
|
|
{
|
|
if($this->buildList !== null && $this->buildListInner === $this->renderInner) return $this->buildList;
|
|
|
|
if(empty($this->selectors) && !empty($this->dataCommands))
|
|
{
|
|
$this->buildList = array();
|
|
return $this->buildList;
|
|
}
|
|
|
|
$list = array();
|
|
$children = $this->renderInner ? $this->wg->children() : $this->children;
|
|
|
|
if(empty($children)) return $list;
|
|
|
|
foreach($children as $child) $list[] = ($child instanceof wg) ? $child->buildDom() : $child;
|
|
|
|
if(!empty($this->selectors)) $list = static::filter($list, $this->selectors);
|
|
|
|
$this->buildList = $list;
|
|
$this->buildListInner = $this->renderInner;
|
|
return $list;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
if($this->renderType === 'json') return $this->renderJson();
|
|
if($this->renderType === 'list') return $this->renderList();
|
|
return $this->renderHtml();
|
|
}
|
|
|
|
/**
|
|
* Render dom to json object.
|
|
*
|
|
* @access public
|
|
* @return object
|
|
*/
|
|
public function renderJson(): object
|
|
{
|
|
$list = $this->build();
|
|
$output = new stdClass();
|
|
foreach($list as $name => $item)
|
|
{
|
|
$output->$name = static::renderItemToJson($item);
|
|
}
|
|
|
|
if(!empty($this->dataCommands))
|
|
{
|
|
$data = array();
|
|
foreach($this->dataCommands as $name => $command)
|
|
{
|
|
$data[$name] = data($command);
|
|
}
|
|
$output->data = $data;
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Render dom to html string.
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public function renderHtml(): string
|
|
{
|
|
$list = $this->build();
|
|
if(empty($list)) return '';
|
|
|
|
$output = array();
|
|
foreach($list as $item)
|
|
{
|
|
$result = static::renderItemToHtml($item);
|
|
if(!is_string($result)) $result = json_encode($result);
|
|
$output[] = $result;
|
|
}
|
|
return implode('', $output);
|
|
}
|
|
|
|
/**
|
|
* Render dom to list by given selector.
|
|
*
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function renderList(): array
|
|
{
|
|
$list = $this->build();
|
|
$output = array();
|
|
foreach($list as $name => $item)
|
|
{
|
|
if(is_array($item) && count($item) === 1) $item = $item[0];
|
|
$renderType = $item instanceof dom ? $item->renderType : 'html';
|
|
if(empty($renderType)) $renderType = 'html';
|
|
$output[] = array('name' => $name, 'data' => static::renderDomItem($item, $renderType), 'type' => $renderType);
|
|
}
|
|
|
|
if(!empty($this->dataCommands))
|
|
{
|
|
foreach($this->dataCommands as $name => $command)
|
|
{
|
|
$output[] = array('name' => $name, 'data' => data($command), 'type' => 'command');
|
|
}
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
public static function renderDomItem($item, $defaultType = 'html')
|
|
{
|
|
if($item instanceof dom)
|
|
{
|
|
$renderType = $item->renderType;
|
|
if(empty($renderType)) $renderType = $defaultType;
|
|
if($renderType === 'json') return dom::renderItemToJson($item);
|
|
return dom::renderItemToHtml($item->build());
|
|
}
|
|
|
|
$renderType = $defaultType;
|
|
if($renderType === 'json') return static::renderItemToJson($item);
|
|
return static::renderItemToHtml($item);
|
|
}
|
|
|
|
public static function renderItemToJson($item)
|
|
{
|
|
if($item === null || is_bool($item)) return null;
|
|
|
|
if(is_array($item))
|
|
{
|
|
$output = array();
|
|
foreach($item as $subItem) $output[] = static::renderItemToJson($subItem);
|
|
return $output;
|
|
}
|
|
|
|
if($item instanceof dom)
|
|
{
|
|
$json = $item->wg->toJSON();
|
|
if(!empty($item->dataGetters))
|
|
{
|
|
$output = array();
|
|
$props = explode(',', $item->dataGetters);
|
|
foreach($props as $prop)
|
|
{
|
|
$prop = trim($prop);
|
|
if(empty($prop)) continue;
|
|
|
|
$parts = explode(':', $prop, 2);
|
|
$name = $parts[0];
|
|
$namePath = count($parts) > 1 ? $parts[1] : $parts[0];
|
|
$output[$name] = \zin\utils\deepGet($json, $namePath);
|
|
}
|
|
return $output;
|
|
}
|
|
return $json;
|
|
}
|
|
if($item instanceof wg) return dom::renderDomItem($item, 'json');
|
|
if(is_string($item)) return $item;
|
|
|
|
if(is_object($item))
|
|
{
|
|
if(isDirective($item, 'html')) return $item->data;
|
|
if(isDirective($item, 'text')) return htmlspecialchars($item->data);
|
|
if(isset($item->html)) return $item->html;
|
|
if(isset($item->text)) return htmlspecialchars($item->text);
|
|
if(method_exists($item, 'render')) return $item->render();
|
|
}
|
|
|
|
return strval($item);
|
|
}
|
|
|
|
public static function renderItemToHtml($item)
|
|
{
|
|
if($item === null || is_bool($item)) return '';
|
|
|
|
if(is_array($item))
|
|
{
|
|
$output = array();
|
|
foreach($item as $subItem) $output[] = static::renderItemToHtml($subItem);
|
|
return implode('', $output);
|
|
}
|
|
|
|
if($item instanceof dom) return dom::renderItemToHtml($item->build());
|
|
if($item instanceof wg) return $item->render();
|
|
if(is_string($item)) return $item;
|
|
|
|
if(is_object($item))
|
|
{
|
|
if(isDirective($item, 'html')) return $item->data;
|
|
if(isDirective($item, 'text')) return htmlspecialchars($item->data);
|
|
if(isset($item->html)) return $item->html;
|
|
if(isset($item->text)) return htmlspecialchars($item->text);
|
|
if(method_exists($item, 'render')) return $item->render();
|
|
}
|
|
|
|
return strval($item);
|
|
}
|
|
|
|
/**
|
|
* Filter the dom list with selector.
|
|
*
|
|
* @param array $list
|
|
* @param object $selector
|
|
* @param array $filteredList
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public static function filterList(&$list, $selector, &$filteredList)
|
|
{
|
|
if(empty($list) || empty($selector)) return [];
|
|
|
|
$results = array();
|
|
foreach($list as $item)
|
|
{
|
|
if(!($item instanceof dom) || in_array($item->wg->gid, $filteredList)) continue;
|
|
|
|
if($item->wg->isMatch($selector))
|
|
{
|
|
$item->selector = $selector;
|
|
$item->renderInner = isset($selector->inner) ? $selector->inner : false;
|
|
$item->renderType = isset($selector->type) ? $selector->type : null;
|
|
$item->dataGetters = isset($selector->data) ? $selector->data : null;
|
|
|
|
$filteredList[] = $item->wg->gid;
|
|
$results[] = $item;
|
|
}
|
|
else
|
|
{
|
|
$children = $item->build();
|
|
if(!empty($children))
|
|
{
|
|
$subResults = static::filterList($children, $selector, $filteredList);
|
|
foreach($subResults as $subItem) $results[] = $subItem;
|
|
}
|
|
}
|
|
if($selector->first && !empty($results)) break;
|
|
}
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Filter the dom list with selectors.
|
|
*
|
|
* @param array $domList
|
|
* @param array $selectors
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public static function filter(&$domList, $selectors)
|
|
{
|
|
if(empty($selectors)) return $domList;
|
|
|
|
$list = array();
|
|
$filteredList = array();
|
|
foreach($selectors as $selector)
|
|
{
|
|
$results = static::filterList($domList, $selector, $filteredList);
|
|
$list[$selector->name] = $results;
|
|
}
|
|
|
|
return $list;
|
|
}
|
|
}
|