565 lines
14 KiB
PHP
565 lines
14 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
/**
|
|
* The js class file of zin lib.
|
|
*
|
|
* @copyright Copyright 2024 青岛易软天创网络科技有限公司(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;
|
|
|
|
require_once dirname(__DIR__) . DS . 'utils' . DS . 'json.func.php';
|
|
require_once __DIR__ . DS . 'node.class.php';
|
|
require_once __DIR__ . DS . 'directive.class.php';
|
|
require_once __DIR__ . DS . 'zin.func.php';
|
|
|
|
use zin\jsContext;
|
|
use zin\jsCallback;
|
|
use zin\jQuery;
|
|
use zin\node;
|
|
|
|
/**
|
|
* Class for generating js code.
|
|
* 用于生成 JS 代码。
|
|
*
|
|
* @access public
|
|
*/
|
|
class js implements \JsonSerializable, iDirective
|
|
{
|
|
public bool $notRenderInGlobal = true;
|
|
|
|
/**
|
|
* The parent node.
|
|
* 父节点。
|
|
*
|
|
* @access public
|
|
* @var object
|
|
*/
|
|
public $parent = null;
|
|
|
|
/**
|
|
* The js code lines.
|
|
* JS 代码行。
|
|
*
|
|
* @access protected
|
|
* @var array
|
|
*/
|
|
protected array $jsLines = array();
|
|
|
|
/**
|
|
* The construct function.
|
|
* 构造函数。
|
|
*
|
|
* @param null|string|js|array ...$codes Codes.
|
|
*/
|
|
public function __construct(null|string|js|array ...$codes)
|
|
{
|
|
$this->appendLines(...$codes);
|
|
}
|
|
|
|
/**
|
|
* The call magic function, used to call other methods on the object by condition through the xxxIf method.
|
|
* 魔术方法,用于通过 xxxIf 的方式根据条件调用对象上的其他方法。
|
|
*
|
|
* @access public
|
|
* @param string $name Method name.
|
|
* @param array $arguments Arguments.
|
|
* @return self
|
|
*/
|
|
public function __call($name, $arguments)
|
|
{
|
|
if(str_ends_with($name, 'If'))
|
|
{
|
|
$methodName = substr($name, 0, -2);
|
|
if(method_exists($this, $methodName))
|
|
{
|
|
$condition = array_shift($arguments);
|
|
$this->beginIf($condition);
|
|
$this->$methodName(...$arguments);
|
|
return $this->endIf();
|
|
}
|
|
}
|
|
trigger_error("Call to undefined method " . __CLASS__ . "::{$name}()", E_USER_ERROR);
|
|
}
|
|
|
|
/**
|
|
* The magic function, used to convert the object to a string.
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->toJS();
|
|
}
|
|
|
|
public function __debugInfo(): array
|
|
{
|
|
return array(
|
|
'jsLines' => $this->jsLines
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Call a function.
|
|
* 调用一个函数。
|
|
*
|
|
* @access public
|
|
* @param string $func Function name.
|
|
* @param mixed ...$args Arguments.
|
|
* @return self
|
|
*/
|
|
public function call(string $func, mixed ...$args): self
|
|
{
|
|
$argCodes = array();
|
|
foreach($args as $arg)
|
|
{
|
|
$argCodes[] = ($arg instanceof js) ? $arg->toJS() : static::value($arg);
|
|
}
|
|
|
|
return $this->appendLine($func . '(' . implode(',', $argCodes) . ')');
|
|
}
|
|
|
|
/**
|
|
* Append JS codes.
|
|
* 追加要执行的 JS 代码。
|
|
*
|
|
* @access public
|
|
* @param mixed ...$codes Codes.
|
|
* @return self
|
|
*/
|
|
public function do(null|string|js|array ...$codes): self
|
|
{
|
|
return $this->appendLines(...$codes);
|
|
}
|
|
|
|
/**
|
|
* Declare JS variables.
|
|
* 声明 JS 变量。
|
|
*
|
|
* @access public
|
|
* @param string $name Variable name.
|
|
* @param mixed $value Variable value.
|
|
* @return self
|
|
*/
|
|
public function let(string $name, mixed $value): self
|
|
{
|
|
return $this->appendLine('let', $name, '=', static::value($value));
|
|
}
|
|
|
|
/**
|
|
* Declare JS constants.
|
|
* 声明 JS 常量。
|
|
*
|
|
* @access public
|
|
* @param string $name Constant name.
|
|
* @param mixed $value Constant value.
|
|
* @return self
|
|
*/
|
|
public function const(string $name, mixed $value): self
|
|
{
|
|
return $this->appendLine('const', $name, '=', static::value($value));
|
|
}
|
|
|
|
/**
|
|
* Declare JS variables globally.
|
|
* 声明 JS 全局变量。
|
|
*
|
|
* @access public
|
|
* @param string $name Variable name.
|
|
* @param mixed $value Variable value.
|
|
* @return self
|
|
*/
|
|
public function globalVar(string $name, mixed $value): self
|
|
{
|
|
if(!str_starts_with($name, 'window.')) $name = 'window.' . $name;
|
|
return $this->appendLine($name, '=', static::value($value));
|
|
}
|
|
|
|
public function var(string|array $nameOrVars, mixed $value = null): self
|
|
{
|
|
if(is_array($nameOrVars))
|
|
{
|
|
foreach($nameOrVars as $name => $val) $this->var($name, $val);
|
|
return $this;
|
|
}
|
|
|
|
$name = $nameOrVars;
|
|
if(str_starts_with($name, '+')) return $this->let(substr($name, 1), $value);
|
|
if(str_starts_with($name, 'window.')) return $this->globalVar($name, $value);
|
|
return $this->const($name, $value);
|
|
}
|
|
|
|
/**
|
|
* Declare JS scope with keyword "with".
|
|
* 声明 JS 作用域,使用 with 关键字。
|
|
*
|
|
* @access public
|
|
* @param string $name Variable name.
|
|
* @param mixed $codes Scoped codes.
|
|
* @return self
|
|
*/
|
|
public function with(string $name, null|string|js|array ...$codes): self
|
|
{
|
|
$this->appendLine('with(', $name, '){');
|
|
$this->appendLines(...$codes);
|
|
return $this->appendLine('}');
|
|
}
|
|
|
|
/**
|
|
* Begin declaring "if" statements.
|
|
* 开始声明 "if" 语句。
|
|
*
|
|
* @access public
|
|
* @param mixed $conditions Conditions.
|
|
* @return self
|
|
*/
|
|
public function beginIf(string ...$conditions): self
|
|
{
|
|
$conditions = implode(' && ', $conditions);
|
|
return $this->appendLine("if($conditions){");
|
|
}
|
|
|
|
/**
|
|
* Declare "else if" statements.
|
|
* 声明 "else if" 语句。
|
|
*
|
|
* @access public
|
|
* @param mixed $conditions Conditions.
|
|
* @return self
|
|
*/
|
|
public function elseIf(string ...$conditions): self
|
|
{
|
|
$conditions = implode(' && ', $conditions);
|
|
return $this->appendLine("}else if($conditions){");
|
|
}
|
|
|
|
/**
|
|
* Declare "else" statements.
|
|
* 声明 "else" 语句。
|
|
*
|
|
* @access public
|
|
* @return self
|
|
*/
|
|
public function else(): self
|
|
{
|
|
return $this->appendLine('}else{');
|
|
}
|
|
|
|
/**
|
|
* Declare "if" statements end.
|
|
* 声明 "if" 结束的括号。
|
|
*
|
|
* @access public
|
|
* @return self
|
|
*/
|
|
public function endIf(): self
|
|
{
|
|
return $this->appendLine('}');
|
|
}
|
|
|
|
/**
|
|
* Declare independent scopes with IIFE.
|
|
* 使用立即执行函数声明独立的作用域。
|
|
*
|
|
* @access public
|
|
* @return self
|
|
*/
|
|
public function scopeBegin(): self
|
|
{
|
|
return $this->appendLine(';(function(){');
|
|
}
|
|
|
|
/**
|
|
* Declare independent scopes end.
|
|
* 声明独立的作用域的结束部分。
|
|
*
|
|
* @access public
|
|
* @return self
|
|
*/
|
|
public function scopeEnd(): self
|
|
{
|
|
return $this->appendLine('}());');
|
|
}
|
|
|
|
/**
|
|
* Append a line of JS code.
|
|
* 追加一行 JS 代码。
|
|
*
|
|
* @access public
|
|
* @param string ...$codes Codes.
|
|
* @return self
|
|
*/
|
|
public function appendLine(string ...$codes): self
|
|
{
|
|
$line = trim(implode(' ', $codes));
|
|
if(empty($line)) return $this;
|
|
|
|
if(!str_ends_with(';', $line)) $line .= ';';
|
|
$this->jsLines[] = $line;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Append lines of JS code.
|
|
* 追加多行 JS 代码。
|
|
*
|
|
* @access public
|
|
* @param null|string|js|array ...$lines Lines.
|
|
* @return self
|
|
*/
|
|
public function appendLines(null|string|js|array ...$lines): self
|
|
{
|
|
foreach($lines as $line)
|
|
{
|
|
if(is_null($line)) continue;
|
|
if(is_array($line))
|
|
{
|
|
$this->appendLines(...$line);
|
|
continue;
|
|
}
|
|
if($line instanceof js)
|
|
{
|
|
$line = $line->toJS();
|
|
}
|
|
$this->appendLine($line);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Append JS codes.
|
|
* 追加要执行的 JS 代码。
|
|
*
|
|
* @access public
|
|
* @param string ...$codes Codes.
|
|
* @return self
|
|
*/
|
|
public function appendCode(string ...$codes): self
|
|
{
|
|
foreach($codes as $code)
|
|
{
|
|
if(empty($code)) continue;
|
|
$this->jsLines[] = $code;
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Convert js code to string.
|
|
* 将 JS 导出为代码字符串。
|
|
*
|
|
* @access public
|
|
* @param string $joiner Joiner.
|
|
* @return string
|
|
*/
|
|
public function toJS(string $joiner = "\n"): string
|
|
{
|
|
return implode($joiner, $this->jsLines);
|
|
}
|
|
|
|
/**
|
|
* Convert js code to string with IIFE scope.
|
|
* 将 JS 导出为代码字符串,并使用立即执行函数作用域。
|
|
*
|
|
* @access public
|
|
* @param string $joiner Joiner.
|
|
* @return string
|
|
*/
|
|
public function toScopeJS(string $joiner = "\n"): string
|
|
{
|
|
return $this->scope($this->toJS($joiner));
|
|
}
|
|
|
|
/**
|
|
* Apply JS code to zin node.
|
|
* 将 JS 代码应用到指定的 zin 部件中。
|
|
*
|
|
* @access public
|
|
* @param node $node zin node object.
|
|
* @param string $blockName zin node block name.
|
|
*/
|
|
public function apply(node $node, string $blockName): void
|
|
{
|
|
$node->addToBlock($blockName, h::js($this->toJS()));
|
|
}
|
|
|
|
/**
|
|
* Serialized to JSON.
|
|
* 序列化为 JSON。
|
|
*
|
|
* @access public
|
|
* @return mixed
|
|
*/
|
|
#[\ReturnTypeWillChange]
|
|
public function jsonSerialize(): mixed
|
|
{
|
|
$js = trim($this->toJS());
|
|
if(str_ends_with(';', $js)) $js = substr($js, 0, -1);
|
|
return js::raw($js);
|
|
}
|
|
|
|
public static function raw(string ...$codes)
|
|
{
|
|
$js = implode('<RAWJS_LINE>', $codes);
|
|
$js = str_replace(array("\n", '"'), array('<RAWJS_LINE>', '<RAWJS_QUOTE>'), $js);
|
|
return "RAWJS<$js>RAWJS";
|
|
}
|
|
|
|
public static function decodeRaw(string $str): string
|
|
{
|
|
if(!str_contains($str, 'RAWJS')) return $str;
|
|
return str_replace(array('<RAWJS_LINE>', '<RAWJS_QUOTE>', '"RAWJS<', '>RAWJS"'), array("\n", '"', '', ''), $str);
|
|
}
|
|
|
|
/**
|
|
* Wrap js code with IIFE scope.
|
|
* 使用立即执行函数作用域包装 JS 代码。
|
|
*
|
|
* @access public
|
|
* @param string ...$codes Codes.
|
|
* @return string
|
|
*/
|
|
public static function scope(null|string|js|array ...$codes): string
|
|
{
|
|
$js = new js(...$codes);
|
|
return ';(function(){' . $js->toJS() . '}());';
|
|
}
|
|
|
|
/**
|
|
* Encode php value to JS code and decode html special chars.
|
|
* 将 PHP 值编码为 JS 代码,并解码 HTML 实体字符。
|
|
*
|
|
* @access public
|
|
* @param mixed $data PHP value.
|
|
* @return string
|
|
*/
|
|
public static function value(mixed $data): string
|
|
{
|
|
if($data instanceof js) return $data->toJS();
|
|
$js = \zin\utils\jsonEncode($data, JSON_UNESCAPED_UNICODE);
|
|
if(empty($js) && (is_array($data) || is_object($data))) return '[]';
|
|
|
|
return static::decodeRaw($js);
|
|
}
|
|
|
|
/**
|
|
* Create js var definition.
|
|
* 创建 JS 变量定义。
|
|
*
|
|
* @access public
|
|
* @param string $name Variable name.
|
|
* @param mixed $value Variable value.
|
|
* @return string
|
|
*/
|
|
public static function defineVar(string $name, mixed $value): string
|
|
{
|
|
$js = new js();
|
|
$js->var($name, $value);
|
|
return $js->toJS();
|
|
}
|
|
|
|
public static function defineJSCall(string $func, array $args): string
|
|
{
|
|
$js = new js();
|
|
$js->call($func, ...$args);
|
|
return $js->toJS();
|
|
}
|
|
|
|
/**
|
|
* Create js context object.
|
|
* 创建给定 JS 值的上下文操作辅助对象。
|
|
*
|
|
* @access public
|
|
* @param mixed $value Value.
|
|
* @param null|string|bool $name Name.
|
|
* @param null|string|js|array ...$codes Codes.
|
|
* @return jsContext
|
|
*/
|
|
public static function context(mixed $value, null|string|bool $name = null, null|string|js|array ...$codes): jsContext
|
|
{
|
|
return new jsContext($value, $name, ...$codes);
|
|
}
|
|
|
|
/**
|
|
* Create zui context object.
|
|
* 创建 ZUI 上下文操作辅助对象。
|
|
*
|
|
* @access public
|
|
* @param null|string $name Name.
|
|
* @return jsContext
|
|
*/
|
|
public static function zui(?string $name = null): jsContext
|
|
{
|
|
if(is_null($name)) return static::context('zui');
|
|
|
|
return static::context("zui.{$name}");
|
|
}
|
|
|
|
/**
|
|
* Create jquery context object.
|
|
* 创建 jQuery 上下文操作辅助对象。
|
|
*
|
|
* @access public
|
|
* @param string $selector Selector.
|
|
* @param null|string $name Name.
|
|
* @param null|string|js|array ...$codes Codes.
|
|
* @return jquery
|
|
*/
|
|
public static function jquery(string $selector, ?string $name = null, null|string|js|array ...$codes): jquery
|
|
{
|
|
return new jquery($selector, $name, ...$codes);
|
|
}
|
|
|
|
/**
|
|
* Create js callback object.
|
|
* 创建 js 回调函数代码生成对象。
|
|
*
|
|
* @access public
|
|
* @param string ...$args Function argument name list.
|
|
* @return jsCallback
|
|
* @static
|
|
*/
|
|
public static function callback(string ...$args): jsCallback
|
|
{
|
|
return new jsCallback(...$args);
|
|
}
|
|
|
|
/**
|
|
* Create window variables context object.
|
|
* 创建 window 变量上下文操作辅助对象。
|
|
*
|
|
* @access public
|
|
* @param string $name Name.
|
|
* @param mixed ...$args Arguments.
|
|
*/
|
|
public static function __callStatic(string $name, array $args)
|
|
{
|
|
$context = static::context("window.{$name}");
|
|
if(empty($args)) return $context;
|
|
return $context->call(...$args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create js object.
|
|
* 创建 JS 对象。
|
|
*
|
|
* @access public
|
|
* @param null|string|js|array ...$codes Codes.
|
|
* @return js
|
|
*/
|
|
function js(null|string|js|array ...$codes): js
|
|
{
|
|
return new js(...$codes);
|
|
}
|
|
|
|
function jsRaw(string ...$codes): string
|
|
{
|
|
return js::raw(...$codes);
|
|
}
|