TemplateRender.php 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. <?php
  2. namespace App\Http\Api;
  3. use App\Http\Controllers\CorpusController;
  4. use App\Models\BookTitle as BookSeries;
  5. use App\Models\Channel;
  6. use App\Models\DhammaTerm;
  7. use App\Models\Discussion;
  8. use App\Models\PageNumber;
  9. use App\Models\PaliText;
  10. use App\Services\ArticleService;
  11. use App\Tools\Tools;
  12. use Illuminate\Support\Facades\Cache;
  13. use Illuminate\Support\Facades\Log;
  14. use Illuminate\Support\Str;
  15. class TemplateRender
  16. {
  17. protected $param = [];
  18. protected $mode = 'read';
  19. protected $channel_id = [];
  20. protected $debug = [];
  21. protected $format = 'react';
  22. protected $studioId = null;
  23. protected $lang = 'en';
  24. protected $langFamily = 'en';
  25. protected $glossaryKey = 'glossary';
  26. protected $channelInfo = [];
  27. protected $options = [
  28. 'mode' => 'read',
  29. 'channelType' => 'translation',
  30. 'contentType' => 'markdown',
  31. 'format' => 'react',
  32. 'debug' => [],
  33. 'studioId' => null,
  34. 'lang' => 'zh-Hans',
  35. 'footnote' => false,
  36. 'paragraph' => false,
  37. 'origin' => true,
  38. 'translation' => true,
  39. ];
  40. /**
  41. * Create a new command instance.
  42. * string $mode 'read' | 'edit'
  43. * string $format 'react' | 'text' | 'tex' | 'unity'
  44. *
  45. * @return void
  46. */
  47. public function __construct(array $param, $channelInfo, string $mode, string $format = 'react', ?string $studioId = null, $debug = [], $lang = 'zh-Hans')
  48. {
  49. $this->param = $param;
  50. foreach ($channelInfo as $channel) {
  51. if ($channel && isset($channel->uid)) {
  52. $this->channel_id[] = $channel->uid;
  53. } else {
  54. Log::error('template render init error invalid channel');
  55. }
  56. }
  57. $this->channelInfo = $channelInfo;
  58. $this->mode = $mode;
  59. $this->format = $format;
  60. $this->studioId = $studioId;
  61. $this->debug = $debug;
  62. $this->glossaryKey = 'glossary';
  63. if (count($this->channel_id) > 0) {
  64. $channelId = $this->channel_id[0];
  65. if (Str::isUuid($channelId)) {
  66. $lang = Channel::where('uid', $channelId)->value('lang');
  67. }
  68. }
  69. if (! empty($lang)) {
  70. $this->lang = $lang;
  71. $this->langFamily = explode('-', $lang)[0];
  72. }
  73. }
  74. public function options($options = [])
  75. {
  76. foreach ($options as $key => $value) {
  77. $this->options[$key] = $value;
  78. }
  79. }
  80. public function glossaryKey()
  81. {
  82. return $this->glossaryKey;
  83. }
  84. /**
  85. * TODO 设置默认语言。在渲染某些内容的时候需要语言信息
  86. */
  87. public function setLang($lang)
  88. {
  89. $this->lang = $lang;
  90. $this->langFamily = explode('-', $lang)[0];
  91. }
  92. private function info($message, $debug)
  93. {
  94. if (in_array($debug, $this->debug)) {
  95. Log::info($message);
  96. }
  97. }
  98. private function error($message, $debug)
  99. {
  100. if (in_array($debug, $this->debug)) {
  101. Log::error($message);
  102. }
  103. }
  104. public function render($tpl_name)
  105. {
  106. switch ($tpl_name) {
  107. case 'term':
  108. // 术语
  109. $result = $this->render_term();
  110. break;
  111. case 'note':
  112. $result = $this->render_note();
  113. break;
  114. case 'sent':
  115. $result = $this->render_sent();
  116. break;
  117. case 'quote':
  118. $result = $this->render_quote();
  119. break;
  120. case 'ql':
  121. $result = $this->render_quote_link();
  122. break;
  123. case 'exercise':
  124. $result = $this->render_exercise();
  125. break;
  126. case 'article':
  127. $result = $this->render_article();
  128. break;
  129. case 'nissaya':
  130. $result = $this->render_nissaya();
  131. break;
  132. case 'mermaid':
  133. $result = $this->render_mermaid();
  134. break;
  135. case 'qa':
  136. $result = $this->render_qa();
  137. break;
  138. case 'v':
  139. $result = $this->render_video();
  140. break;
  141. case 'g':
  142. $result = $this->render_grammar_lookup();
  143. break;
  144. case 'ref':
  145. $result = $this->render_ref();
  146. break;
  147. case 'dict-pref':
  148. $result = $this->render_dict_pref();
  149. break;
  150. case 'ai':
  151. $result = $this->render_ai();
  152. break;
  153. case 'para':
  154. $result = $this->render_para();
  155. break;
  156. case 'category':
  157. $result = $this->render_category();
  158. break;
  159. case 'quality':
  160. // 质量标签模版:{{quality|pending}}
  161. $result = $this->render_quality();
  162. break;
  163. default:
  164. if (mb_substr($tpl_name, 0, 4, 'UTF-8') === 'Tpl:') {
  165. $result = $this->render_tpl($tpl_name);
  166. } else {
  167. $result = [
  168. 'props' => base64_encode(\json_encode([])),
  169. 'html' => '',
  170. 'tag' => 'span',
  171. 'tpl' => 'unknown',
  172. ];
  173. }
  174. break;
  175. }
  176. return $result;
  177. }
  178. public function render_tpl($name)
  179. {
  180. $article = app(ArticleService::class)->getRawByTitle($name);
  181. $content = $article->content;
  182. if (count($this->param) > 0) {
  183. $m = new \Mustache_Engine([
  184. 'entity_flags' => ENT_QUOTES,
  185. 'escape' => function ($value) {
  186. return $value;
  187. },
  188. ]);
  189. $content = $m->render($content, $this->param);
  190. }
  191. $output = [];
  192. switch ($this->format) {
  193. case 'react':
  194. $output = [
  195. 'props' => base64_encode(\json_encode(['content' => $content])),
  196. 'html' => $content,
  197. 'tag' => 'span',
  198. 'tpl' => 'tpl',
  199. ];
  200. break;
  201. default:
  202. $output = $content;
  203. break;
  204. }
  205. return $output;
  206. }
  207. public function render_para()
  208. {
  209. $props = [];
  210. $props['id'] = $this->get_param($this->param, 'id', 1);
  211. $props['title'] = $this->get_param($this->param, 'title', 2);
  212. $props['style'] = $this->get_param($this->param, 'style', 3);
  213. if (! empty($this->channel_id)) {
  214. $props['channel'] = implode('_', $this->channel_id);
  215. }
  216. $output = [];
  217. switch ($this->format) {
  218. case 'react':
  219. $output = [
  220. 'props' => base64_encode(\json_encode($props)),
  221. 'html' => $props['title'],
  222. 'tag' => 'span',
  223. 'tpl' => 'para',
  224. ];
  225. break;
  226. default:
  227. $output = $props['title'];
  228. break;
  229. }
  230. return $output;
  231. }
  232. public function render_category()
  233. {
  234. $props = [];
  235. $props['name'] = $this->get_param($this->param, 'name', 1);
  236. $output = [];
  237. switch ($this->format) {
  238. case 'react':
  239. $output = [
  240. 'props' => base64_encode(\json_encode($props)),
  241. 'html' => $props['name'],
  242. 'tag' => 'span',
  243. 'tpl' => 'para',
  244. ];
  245. break;
  246. default:
  247. $output = $props['name'];
  248. break;
  249. }
  250. return $output;
  251. }
  252. /**
  253. * 渲染质量标签模版:{{quality|pending}}
  254. *
  255. * 合法的取值只有四个:featured | standard | draft | pending
  256. * 非法取值统一回退为 pending,避免渲染出未知样式。
  257. */
  258. public function render_quality()
  259. {
  260. // 默认值为 pending,同时支持命名参数 {{quality|value=featured}}
  261. $value = $this->get_param($this->param, 'value', 1, 'pending');
  262. // 校验取值是否合法,不合法则回退到默认值
  263. $allowed = ['featured', 'standard', 'draft', 'pending'];
  264. if (! in_array($value, $allowed, true)) {
  265. $value = 'pending';
  266. }
  267. $props = ['value' => $value];
  268. $output = [];
  269. switch ($this->format) {
  270. case 'react':
  271. // 前端通过 props 解析,使用 antd Tag 按取值渲染不同颜色
  272. $output = [
  273. 'props' => base64_encode(\json_encode($props)),
  274. 'html' => $value,
  275. 'tag' => 'span',
  276. 'tpl' => 'quality',
  277. ];
  278. break;
  279. case 'html':
  280. // 静态 html 输出,class 包含固定前缀 tag-quality 和具体取值
  281. $output = "<span class=\"tag-quality {$value}\">{$value}</span>";
  282. break;
  283. default:
  284. // text / tex / simple 等纯文本格式直接输出取值
  285. $output = $value;
  286. break;
  287. }
  288. return $output;
  289. }
  290. public function getTermProps($word, $tag = null, $channel = null)
  291. {
  292. if ($channel && ! empty($channel)) {
  293. $channelId = $channel;
  294. } else {
  295. if (count($this->channel_id) > 0) {
  296. $channelId = $this->channel_id[0];
  297. } else {
  298. $channelId = null;
  299. }
  300. }
  301. if (count($this->channelInfo) === 0) {
  302. if (! empty($channel)) {
  303. $channelInfo = Channel::where('uid', $channel)->first();
  304. if (! $channelInfo) {
  305. unset($channelInfo);
  306. }
  307. }
  308. if (! isset($channelInfo)) {
  309. Log::warning('channel is null');
  310. $output = [
  311. 'word' => $word,
  312. 'innerHtml' => '',
  313. ];
  314. return $output;
  315. }
  316. } else {
  317. $channelInfo = $this->channelInfo[0];
  318. }
  319. if (Str::isUuid($channelId)) {
  320. $lang = Channel::where('uid', $channelId)->value('lang');
  321. if (! empty($lang)) {
  322. $langFamily = explode('-', $lang)[0];
  323. } else {
  324. $langFamily = 'zh';
  325. }
  326. $this->info("term:{$word} 先查属于这个channel 的", 'term');
  327. $this->info('channel id'.$channelId, 'term');
  328. $table = DhammaTerm::where('word', $word)
  329. ->where('channal', $channelId);
  330. if ($tag && ! empty($tag)) {
  331. $table = $table->where('tag', $tag);
  332. }
  333. $tplParam = $table->orderBy('updated_at', 'desc')
  334. ->first();
  335. $studioId = $channelInfo->owner_uid;
  336. } else {
  337. $tplParam = false;
  338. $lang = '';
  339. $langFamily = '';
  340. $studioId = $this->studioId;
  341. }
  342. if (! $tplParam) {
  343. if (Str::isUuid($studioId)) {
  344. /**
  345. * 没有,再查这个studio的
  346. * 按照语言过滤
  347. * 完全匹配的优先
  348. * 语族匹配也行
  349. */
  350. $this->info('没有-再查这个studio的', 'term');
  351. $table = DhammaTerm::where('word', $word);
  352. if (! empty($tag)) {
  353. $table = $table->where('tag', $tag);
  354. }
  355. $termsInStudio = $table->where('owner', $channelInfo->owner_uid)
  356. ->orderBy('updated_at', 'desc')
  357. ->get();
  358. if (count($termsInStudio) > 0) {
  359. $list = [];
  360. foreach ($termsInStudio as $key => $term) {
  361. if (empty($term->channal)) {
  362. if ($term->language === $lang) {
  363. $list[$term->guid] = 2;
  364. } elseif (strpos($term->language, $langFamily) !== false) {
  365. $list[$term->guid] = 1;
  366. }
  367. }
  368. }
  369. if (count($list) > 0) {
  370. arsort($list);
  371. foreach ($list as $key => $one) {
  372. foreach ($termsInStudio as $term) {
  373. if ($term->guid === $key) {
  374. $tplParam = $term;
  375. break;
  376. }
  377. }
  378. break;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. if (! $tplParam) {
  385. $this->info('没有,再查社区', 'term');
  386. $community_channel = ChannelApi::getSysChannel('_community_term_zh-hans_');
  387. $table = DhammaTerm::where('word', $word);
  388. if (! empty($tag)) {
  389. $table = $table->where('tag', $tag);
  390. }
  391. $tplParam = $table->where('channal', $community_channel)
  392. ->first();
  393. if ($tplParam) {
  394. $isCommunity = true;
  395. } else {
  396. $this->info('查社区没有', 'term');
  397. }
  398. }
  399. $output = [
  400. 'word' => $word,
  401. 'parentChannelId' => $channelId,
  402. 'parentStudioId' => $channelInfo ? $channelInfo->owner_uid : null,
  403. ];
  404. $innerString = $output['word'];
  405. if ($tplParam) {
  406. $output['id'] = $tplParam->guid;
  407. $output['meaning'] = $tplParam->meaning;
  408. $output['channel'] = $tplParam->channal;
  409. if (! empty($tplParam->note)) {
  410. $mdRender = new MdRender(['format' => $this->format]);
  411. $output['note'] = $mdRender->convert($tplParam->note, $this->channel_id);
  412. }
  413. if (isset($isCommunity)) {
  414. $output['isCommunity'] = true;
  415. }
  416. $innerString = "{$output['meaning']}({$output['word']})";
  417. if (! empty($tplParam->other_meaning)) {
  418. $output['meaning2'] = $tplParam->other_meaning;
  419. }
  420. }
  421. $output['innerHtml'] = $innerString;
  422. return $output;
  423. }
  424. private function render_term()
  425. {
  426. $word = $this->get_param($this->param, 'word', 1);
  427. if (str_contains($word, '@')) {
  428. [$wordHead, $tag] = explode('@', $word);
  429. }
  430. if (str_contains($word, '#')) {
  431. [$wordHead, $display] = explode('#', $word);
  432. }
  433. $props = $this->getTermProps($wordHead ?? $word, $tag ?? '');
  434. if (isset($display)) {
  435. $props['meaning'] = $display;
  436. }
  437. $output = $props['word'];
  438. switch ($this->format) {
  439. case 'react':
  440. $output = [
  441. 'props' => base64_encode(\json_encode($props)),
  442. 'html' => $props['innerHtml'],
  443. 'tag' => 'span',
  444. 'tpl' => 'term',
  445. ];
  446. break;
  447. case 'unity':
  448. $output = [
  449. 'props' => base64_encode(\json_encode($props)),
  450. 'tpl' => 'term',
  451. ];
  452. break;
  453. case 'html':
  454. $no = isset($props['id']) ? '' : 'term_invalid';
  455. $id = isset($props['id']) ? $props['id'] : '';
  456. $output = '<span ';
  457. $output .= "class='term-ref {$no}' ";
  458. $output .= "data-id='{$id}' ";
  459. $output .= "data-term='{$props['word']}' ";
  460. $output .= '>';
  461. $output .= $props['meaning'] ?? $props['word'];
  462. $output .= '</span>';
  463. break;
  464. case 'markdown':
  465. if (isset($props['meaning'])) {
  466. $key = 'term-'.$props['word'];
  467. if (isset($GLOBALS[$key]) && $GLOBALS[$key] === 1) {
  468. $GLOBALS[$key]++;
  469. $output = $props['meaning'];
  470. } else {
  471. $GLOBALS[$key] = 1;
  472. $output = $props['meaning'].'('.$props['word'].')';
  473. }
  474. } else {
  475. $output = $props['word'];
  476. }
  477. // 如果有内容且第一次出现,显示为脚注
  478. if (! empty($props['note']) && $GLOBALS[$key] === 1) {
  479. if (isset($GLOBALS['note_sn'])) {
  480. $GLOBALS['note_sn']++;
  481. } else {
  482. $GLOBALS['note_sn'] = 1;
  483. $GLOBALS['note'] = [];
  484. }
  485. $content = $props['note'];
  486. $output .= '[^'.$GLOBALS['note_sn'].']';
  487. $GLOBALS['note'][] = [
  488. 'sn' => $GLOBALS['note_sn'],
  489. 'trigger' => '',
  490. 'content' => $content,
  491. ];
  492. }
  493. break;
  494. default:
  495. $output = $props['meaning'] ?? $props['word'];
  496. break;
  497. }
  498. return $output;
  499. }
  500. private function render_note()
  501. {
  502. $note = $this->get_param($this->param, 'text', 1);
  503. $trigger = $this->get_param($this->param, 'trigger', 2, '');
  504. $props = ['note' => $note];
  505. $innerString = '';
  506. if (! empty($trigger)) {
  507. $props['trigger'] = $trigger;
  508. $innerString = $props['trigger'];
  509. }
  510. if ($this->format === 'unity') {
  511. $props['note'] = MdRender::render(
  512. $props['note'],
  513. $this->channel_id,
  514. null,
  515. 'read',
  516. 'translation',
  517. 'markdown',
  518. 'unity'
  519. );
  520. }
  521. $output = $note;
  522. switch ($this->format) {
  523. case 'react':
  524. $output = [
  525. 'props' => base64_encode(\json_encode($props)),
  526. 'html' => $innerString,
  527. 'tag' => 'span',
  528. 'tpl' => 'note',
  529. ];
  530. break;
  531. case 'unity':
  532. $output = [
  533. 'props' => base64_encode(\json_encode($props)),
  534. 'tpl' => 'note',
  535. ];
  536. break;
  537. case 'html':
  538. if (isset($GLOBALS['note_sn'])) {
  539. $GLOBALS['note_sn']++;
  540. } else {
  541. $GLOBALS['note_sn'] = 1;
  542. $GLOBALS['note'] = [];
  543. }
  544. $noteContent = MdRender::render(
  545. $props['note'],
  546. $this->channel_id,
  547. null,
  548. 'read',
  549. 'translation',
  550. 'markdown',
  551. 'html'
  552. );
  553. $GLOBALS['note'][] = [
  554. 'sn' => $GLOBALS['note_sn'],
  555. 'trigger' => $trigger,
  556. 'content' => $noteContent,
  557. ];
  558. $link = "<a href='#footnote-".$GLOBALS['note_sn']."' name='note-".$GLOBALS['note_sn']."'>";
  559. if (empty($trigger)) {
  560. $output = $link.'<sup>['.$GLOBALS['note_sn'].']</sup></a>';
  561. } else {
  562. $output = $link.$trigger.'</a>';
  563. }
  564. $output = "<label for=\"sn-{$GLOBALS['note_sn']}\"
  565. class=\"margin-toggle sidenote-number\" >{$trigger}</label>
  566. <input type=\"checkbox\" id=\"sn-{$GLOBALS['note_sn']}\"
  567. class=\"margin-toggle\"/>
  568. <span class=\"sidenote\">{$noteContent}</span>";
  569. break;
  570. case 'text':
  571. $output = $trigger;
  572. break;
  573. case 'tex':
  574. $output = $trigger;
  575. break;
  576. case 'simple':
  577. $output = '';
  578. break;
  579. case 'markdown':
  580. if (isset($GLOBALS['note_sn'])) {
  581. $GLOBALS['note_sn']++;
  582. } else {
  583. $GLOBALS['note_sn'] = 1;
  584. $GLOBALS['note'] = [];
  585. }
  586. $content = MdRender::render(
  587. $props['note'],
  588. $this->channel_id,
  589. null,
  590. 'read',
  591. 'translation',
  592. 'markdown',
  593. 'markdown'
  594. );
  595. $output = '[^'.$GLOBALS['note_sn'].']';
  596. $GLOBALS['note'][] = [
  597. 'sn' => $GLOBALS['note_sn'],
  598. 'trigger' => $trigger,
  599. 'content' => $content,
  600. ];
  601. // $output = '<footnote id="'.$GLOBALS['note_sn'].'">'.$content.'</footnote>';
  602. break;
  603. default:
  604. $output = '';
  605. break;
  606. }
  607. return $output;
  608. }
  609. private function render_nissaya()
  610. {
  611. $pali = $this->get_param($this->param, 'pali', 1);
  612. $meaning = $this->get_param($this->param, 'meaning', 2);
  613. $innerString = '';
  614. $props = [
  615. 'pali' => $pali,
  616. 'meaning' => explode('=', $meaning),
  617. 'lang' => $this->lang,
  618. ];
  619. switch ($this->format) {
  620. case 'react':
  621. $output = [
  622. 'props' => base64_encode(\json_encode($props)),
  623. 'html' => $innerString,
  624. 'tag' => 'span',
  625. 'tpl' => 'nissaya',
  626. ];
  627. break;
  628. case 'unity':
  629. $output = [
  630. 'props' => base64_encode(\json_encode($props)),
  631. 'tpl' => 'nissaya',
  632. ];
  633. break;
  634. case 'html':
  635. if ($this->lang === 'my') {
  636. $output = "<span lang='pi-Mymr'>{$pali}</span>၊ ";
  637. } else {
  638. $output = "<span lang='pi-Latn'>{$pali}</span> ";
  639. }
  640. $output .= "<span lang='{$this->lang}'>{$meaning}</span>";
  641. break;
  642. case 'prompt':
  643. $output = Tools::MyToRm($pali).':'.end($props['meaning']);
  644. break;
  645. default:
  646. $output = $pali.'၊'.$meaning;
  647. break;
  648. }
  649. return $output;
  650. }
  651. private function render_exercise()
  652. {
  653. $id = $this->get_param($this->param, 'id', 1);
  654. $title = $this->get_param($this->param, 'title', 1);
  655. $props = [
  656. 'id' => $id,
  657. 'title' => $title,
  658. 'channel' => $this->channel_id[0],
  659. ];
  660. switch ($this->format) {
  661. case 'react':
  662. $output = [
  663. 'props' => base64_encode(\json_encode($props)),
  664. 'html' => '',
  665. 'tag' => 'span',
  666. 'tpl' => 'exercise',
  667. ];
  668. break;
  669. case 'unity':
  670. $output = [
  671. 'props' => base64_encode(\json_encode($props)),
  672. 'tpl' => 'exercise',
  673. ];
  674. break;
  675. case 'text':
  676. $output = $title;
  677. break;
  678. case 'tex':
  679. $output = $title;
  680. break;
  681. case 'simple':
  682. $output = $title;
  683. break;
  684. default:
  685. $output = '';
  686. break;
  687. }
  688. return $output;
  689. }
  690. private function render_article()
  691. {
  692. $type = $this->get_param($this->param, 'type', 1);
  693. $id = $this->get_param($this->param, 'id', 2);
  694. $title = $this->get_param($this->param, 'title', 3);
  695. $channel = $this->get_param($this->param, 'channel', 4);
  696. $style = $this->get_param($this->param, 'style', 5);
  697. $book = $this->get_param($this->param, 'book', 6);
  698. $paragraphs = $this->get_param($this->param, 'paragraphs', 7);
  699. $anthology = $this->get_param($this->param, 'anthology', 8);
  700. if ($type === 'chapter' && empty($id)) {
  701. $book = (int) $book;
  702. $paragraphs = (int) $paragraphs;
  703. $id = "{$book}-{$paragraphs}";
  704. }
  705. $props = [
  706. 'type' => $type,
  707. 'id' => $id,
  708. 'style' => $style,
  709. ];
  710. if (! empty($channel)) {
  711. $props['channel'] = $channel;
  712. }
  713. if (! empty($title)) {
  714. $props['title'] = $title;
  715. }
  716. if (! empty($book)) {
  717. $props['book'] = $book;
  718. }
  719. if (! empty($paragraphs)) {
  720. $props['paragraphs'] = $paragraphs;
  721. }
  722. if (! empty($anthology)) {
  723. $props['anthology'] = $anthology;
  724. }
  725. if (is_array($this->channel_id)) {
  726. $props['parentChannels'] = $this->channel_id;
  727. }
  728. switch ($this->format) {
  729. case 'react':
  730. $output = [
  731. 'props' => base64_encode(\json_encode($props)),
  732. 'html' => '',
  733. 'text' => $title,
  734. 'tag' => 'span',
  735. 'tpl' => 'article',
  736. ];
  737. break;
  738. case 'unity':
  739. $output = [
  740. 'props' => base64_encode(\json_encode($props)),
  741. 'tpl' => 'article',
  742. ];
  743. break;
  744. case 'text':
  745. $output = $title;
  746. break;
  747. case 'tex':
  748. $output = $title;
  749. break;
  750. case 'simple':
  751. $output = $title;
  752. break;
  753. default:
  754. $output = '';
  755. break;
  756. }
  757. return $output;
  758. }
  759. private function render_quote()
  760. {
  761. $paraId = $this->get_param($this->param, 'para', 1);
  762. $channelId = $this->channel_id[0];
  763. $props = Cache::remember(
  764. "/quote/{$channelId}/{$paraId}",
  765. config('mint.cache.expire'),
  766. function () use ($paraId, $channelId) {
  767. $para = \explode('-', $paraId);
  768. $output = [
  769. 'paraId' => $paraId,
  770. 'channel' => $channelId,
  771. 'innerString' => $paraId,
  772. ];
  773. if (count($para) < 2) {
  774. return $output;
  775. }
  776. $PaliText = PaliText::where('book', $para[0])
  777. ->where('paragraph', $para[1])
  778. ->select(['toc', 'path'])
  779. ->first();
  780. if ($PaliText) {
  781. $output['pali'] = $PaliText->toc;
  782. $output['paliPath'] = \json_decode((string) $PaliText->path, true);
  783. $output['innerString'] = $PaliText->toc;
  784. }
  785. return $output;
  786. }
  787. );
  788. switch ($this->format) {
  789. case 'react':
  790. $output = [
  791. 'props' => base64_encode(\json_encode($props)),
  792. 'html' => $props['innerString'],
  793. 'tag' => 'span',
  794. 'tpl' => 'quote',
  795. ];
  796. break;
  797. case 'unity':
  798. $output = [
  799. 'props' => base64_encode(\json_encode($props)),
  800. 'tpl' => 'quote',
  801. ];
  802. break;
  803. case 'text':
  804. $output = $props['innerString'];
  805. break;
  806. case 'tex':
  807. $output = $props['innerString'];
  808. break;
  809. case 'simple':
  810. $output = $props['innerString'];
  811. break;
  812. default:
  813. $output = $props['innerString'];
  814. break;
  815. }
  816. return $output;
  817. }
  818. private function render_quote_link()
  819. {
  820. $type = $this->get_param($this->param, 'type', 1);
  821. $title = $this->get_param($this->param, 'title', 6, '');
  822. $bookName = $this->get_param($this->param, 'bookname', 2, '');
  823. $volume = $this->get_param($this->param, 'volume', 3);
  824. $page = $this->get_param($this->param, 'page', 4, '');
  825. $style = $this->get_param($this->param, 'style', 5, 'modal');
  826. $book = $this->get_param($this->param, 'book', 7, false);
  827. $para = $this->get_param($this->param, 'para', 8, false);
  828. $props = [
  829. 'type' => $type,
  830. 'style' => $style,
  831. 'found' => true,
  832. ];
  833. if (! empty($bookName) && $volume !== '' && ! empty($page)) {
  834. $props['bookName'] = $bookName;
  835. $props['volume'] = (int) $volume;
  836. $props['page'] = $page;
  837. $props['found'] = true;
  838. } elseif ($book && $para) {
  839. /**
  840. * 没有指定书名,根据book para 查询
  841. */
  842. if ($type === 'c') {
  843. // 按照章节名称显示
  844. $path = PaliTextApi::getChapterPath($book, $para);
  845. if ($path) {
  846. $path = json_decode($path, true);
  847. }
  848. if ($path && is_array($path) && count($path) > 2) {
  849. $props['bookName'] = strtolower($path[0]['title']);
  850. $props['chapter'] = strtolower(end($path)['title']);
  851. $props['found'] = true;
  852. } else {
  853. $props['found'] = false;
  854. }
  855. } else {
  856. $pageInfo = $this->pageInfoByPara($type, $book, $para);
  857. if ($pageInfo['found']) {
  858. $props['bookName'] = $pageInfo['bookName'];
  859. $props['volume'] = $pageInfo['volume'];
  860. $props['page'] = $pageInfo['page'];
  861. $props['found'] = true;
  862. } else {
  863. $props['found'] = false;
  864. }
  865. }
  866. } elseif ($title) {
  867. // 没有书号用title查询
  868. // $tmpTitle = explode('။',$title);
  869. for ($i = mb_strlen($title, 'UTF-8'); $i > 0; $i--) {
  870. $mTitle = mb_substr($title, 0, $i);
  871. $has = array_search($mTitle, array_column(BookTitle::my(), 'title2'));
  872. if ($has !== false) {
  873. $tmpBookTitle = $mTitle;
  874. $tmpBookPage = mb_substr($title, $i);
  875. $tmpBookPage = $this->mb_trim($tmpBookPage, '၊။');
  876. break;
  877. }
  878. }
  879. if (isset($tmpBookTitle)) {
  880. // $tmpBookTitle = $tmpTitle[0];
  881. // $tmpBookPage = $tmpTitle[1];
  882. $tmpBookPage = (int) str_replace(
  883. ['၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉', '၀'],
  884. ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
  885. $tmpBookPage
  886. );
  887. $found_key = array_search($tmpBookTitle, array_column(BookTitle::my(), 'title2'));
  888. if ($found_key !== false) {
  889. $props['bookName'] = BookTitle::my()[$found_key]['bookname'];
  890. $props['volume'] = BookTitle::my()[$found_key]['volume'];
  891. $props['page'] = $tmpBookPage;
  892. if (! empty($props['bookName'])) {
  893. $found_title = array_search($props['bookName'], array_column(BookTitle::my(), 'bookname'));
  894. if ($found_title === false) {
  895. $props['found'] = false;
  896. }
  897. }
  898. } else {
  899. // 没找到,返回术语和页码
  900. $props['found'] = false;
  901. $props['bookName'] = $tmpBookTitle;
  902. $props['page'] = $tmpBookPage;
  903. $props['volume'] = 0;
  904. }
  905. }
  906. } else {
  907. $props['found'] = false;
  908. }
  909. if ($book && $para) {
  910. $props['book'] = $book;
  911. $props['para'] = $para;
  912. }
  913. if ($title) {
  914. $props['title'] = $title;
  915. }
  916. $text = '';
  917. if (isset($props['bookName'])) {
  918. $searchField = '';
  919. switch ($type) {
  920. case 'm':
  921. $searchField = 'm_title';
  922. break;
  923. case 'p':
  924. $searchField = 'p_title';
  925. break;
  926. }
  927. $found_title = array_search($props['bookName'], array_column(BookTitle::get(), $searchField));
  928. if ($found_title === false) {
  929. $props['found'] = false;
  930. }
  931. $term = $this->getTermProps($props['bookName'], ':quote:');
  932. $props['term'] = $term;
  933. if (isset($term['id'])) {
  934. $props['bookNameLocal'] = $term['meaning'];
  935. $text .= $term['meaning'];
  936. } else {
  937. $text .= $bookName;
  938. }
  939. }
  940. if (isset($props['volume']) && isset($props['page'])) {
  941. $text .= " {$volume}.{$page}";
  942. }
  943. switch ($this->format) {
  944. case 'react':
  945. $output = [
  946. 'props' => base64_encode(\json_encode($props)),
  947. 'html' => '',
  948. 'tag' => 'span',
  949. 'tpl' => 'quote-link',
  950. ];
  951. break;
  952. case 'unity':
  953. $output = [
  954. 'props' => base64_encode(\json_encode($props)),
  955. 'tpl' => 'quote-link',
  956. ];
  957. break;
  958. default:
  959. $output = $text;
  960. break;
  961. }
  962. return $output;
  963. }
  964. private function pageInfoByPara($type, $book, $para)
  965. {
  966. $output = [];
  967. $pageInfo = PageNumber::where('type', strtoupper($type))
  968. ->where('book', $book)
  969. ->where('paragraph', '<=', $para)
  970. ->orderBy('paragraph', 'desc')
  971. ->first();
  972. if ($pageInfo) {
  973. foreach (BookTitle::get() as $value) {
  974. if ($value['id'] === $pageInfo->pcd_book_id) {
  975. switch (strtoupper($type)) {
  976. case 'M':
  977. $key = 'm_title';
  978. break;
  979. case 'P':
  980. $key = 'p_title';
  981. break;
  982. case 'V':
  983. $key = 'v_title';
  984. break;
  985. default:
  986. $key = 'term';
  987. break;
  988. }
  989. $output['bookName'] = $value[$key];
  990. break;
  991. }
  992. }
  993. $output['volume'] = $pageInfo->volume;
  994. $output['page'] = $pageInfo->page;
  995. $output['found'] = true;
  996. } else {
  997. $output['found'] = false;
  998. }
  999. return $output;
  1000. }
  1001. private function render_sent()
  1002. {
  1003. $sid = $this->get_param($this->param, 'id', 1);
  1004. $channel = $this->get_param($this->param, 'channel', 2);
  1005. $show = $this->get_param($this->param, 'text', 2, 'both');
  1006. $mode = $this->get_param($this->param, 'mode', 3, null);
  1007. if (! empty($channel)) {
  1008. $channels = explode(',', $channel);
  1009. } else {
  1010. $channels = $this->channel_id;
  1011. }
  1012. $sentInfo = explode('@', trim($sid));
  1013. $sentId = $sentInfo[0];
  1014. if (isset($sentInfo[1])) {
  1015. $channels = [$sentInfo[1]];
  1016. }
  1017. $Sent = new CorpusController;
  1018. $currMode = $mode ?? $this->mode;
  1019. $props = $Sent->getSentTpl(
  1020. $sentId,
  1021. $channels,
  1022. $currMode,
  1023. true,
  1024. $this->format
  1025. );
  1026. if (! $props) {
  1027. $props['error'] = '句子模版渲染错误。句子参数个数不符。应该是四个。';
  1028. Log::error('句子模版渲染错误。句子参数个数不符。应该是四个。');
  1029. }
  1030. if ($currMode === 'read') {
  1031. $tpl = 'sentread';
  1032. } else {
  1033. $tpl = 'sentedit';
  1034. }
  1035. if (is_array($props)) {
  1036. $props['show'] = $show;
  1037. $props['mode'] = $currMode;
  1038. }
  1039. // 输出引用
  1040. $arrSid = explode('-', $sid);
  1041. $bookPara = array_slice($arrSid, 0, 2);
  1042. if (! isset($GLOBALS['ref_sent'])) {
  1043. $GLOBALS['ref_sent'] = [];
  1044. }
  1045. $GLOBALS['ref_sent'][] = $bookPara;
  1046. switch ($this->format) {
  1047. case 'react':
  1048. $output = [
  1049. 'props' => base64_encode(\json_encode($props)),
  1050. 'html' => '',
  1051. 'tag' => 'span',
  1052. 'tpl' => $tpl,
  1053. ];
  1054. break;
  1055. case 'unity':
  1056. $output = [
  1057. 'props' => base64_encode(\json_encode($props)),
  1058. 'tpl' => $tpl,
  1059. ];
  1060. break;
  1061. case 'text':
  1062. $output = '';
  1063. if (isset($props['origin']) && is_array($props['origin'])) {
  1064. foreach ($props['origin'] as $key => $value) {
  1065. $output .= $value['html'];
  1066. }
  1067. }
  1068. if (isset($props['translation']) && is_array($props['translation'])) {
  1069. foreach ($props['translation'] as $key => $value) {
  1070. $output .= $value['html'];
  1071. }
  1072. }
  1073. break;
  1074. case 'prompt':
  1075. $output = '';
  1076. if ($show === 'both' || $show === 'origin') {
  1077. if (isset($props['origin']) && is_array($props['origin'])) {
  1078. foreach ($props['origin'] as $key => $value) {
  1079. $output .= $value['html'];
  1080. }
  1081. }
  1082. }
  1083. if ($show === 'both' || $show === 'translation') {
  1084. if (isset($props['translation']) && is_array($props['translation'])) {
  1085. foreach ($props['translation'] as $key => $value) {
  1086. $output .= $value['html'];
  1087. }
  1088. }
  1089. }
  1090. break;
  1091. case 'html':
  1092. $output = '';
  1093. $output .= '<span class="sentence">';
  1094. if ($show === 'both' || $show === 'origin') {
  1095. if (isset($props['origin']) && is_array($props['origin'])) {
  1096. foreach ($props['origin'] as $key => $value) {
  1097. $output .= '<span class="origin">'.$value['html'].'</span>';
  1098. }
  1099. }
  1100. }
  1101. if ($show === 'both' || $show === 'translation') {
  1102. if (isset($props['translation']) && is_array($props['translation'])) {
  1103. foreach ($props['translation'] as $key => $value) {
  1104. $output .= '<span class="translation">'.$value['html'].'</span>';
  1105. }
  1106. }
  1107. }
  1108. $output .= '</span>';
  1109. break;
  1110. case 'tex':
  1111. $output = '';
  1112. if (isset($props['translation']) && is_array($props['translation'])) {
  1113. foreach ($props['translation'] as $key => $value) {
  1114. $output .= $value['html'];
  1115. }
  1116. }
  1117. break;
  1118. case 'simple':
  1119. $output = '';
  1120. if ($show === 'both' || $show === 'origin') {
  1121. if (empty($output)) {
  1122. if (
  1123. isset($props['origin']) &&
  1124. is_array($props['origin']) &&
  1125. count($props['origin']) > 0
  1126. ) {
  1127. foreach ($props['origin'] as $key => $value) {
  1128. $output .= trim($value['html']);
  1129. }
  1130. }
  1131. }
  1132. }
  1133. if ($show === 'both' || $show === 'translation') {
  1134. if (
  1135. isset($props['translation']) &&
  1136. is_array($props['translation']) &&
  1137. count($props['translation']) > 0
  1138. ) {
  1139. foreach ($props['translation'] as $key => $value) {
  1140. $output .= trim($value['html']);
  1141. }
  1142. }
  1143. }
  1144. break;
  1145. case 'markdown':
  1146. $output = '';
  1147. if ($show === 'both' || $show === 'origin') {
  1148. if (
  1149. $this->options['origin'] === true ||
  1150. $this->options['origin'] === 'true'
  1151. ) {
  1152. if (isset($props['origin']) && is_array($props['origin'])) {
  1153. foreach ($props['origin'] as $key => $value) {
  1154. $output .= trim($value['html']);
  1155. }
  1156. }
  1157. }
  1158. }
  1159. if ($show === 'both' || $show === 'translation') {
  1160. if (
  1161. $this->options['translation'] === true ||
  1162. $this->options['translation'] === 'true'
  1163. ) {
  1164. if (
  1165. isset($props['translation']) &&
  1166. is_array($props['translation']) &&
  1167. count($props['translation']) > 0
  1168. ) {
  1169. foreach ($props['translation'] as $key => $value) {
  1170. $output .= trim($value['html']);
  1171. }
  1172. } else {
  1173. if ($show === 'translation') {
  1174. // 无译文用原文代替
  1175. if (isset($props['origin']) && is_array($props['origin'])) {
  1176. foreach ($props['origin'] as $key => $value) {
  1177. $output .= trim($value['html']);
  1178. }
  1179. }
  1180. }
  1181. }
  1182. }
  1183. }
  1184. break;
  1185. default:
  1186. $output = '';
  1187. break;
  1188. }
  1189. return $output;
  1190. }
  1191. private function render_mermaid()
  1192. {
  1193. $text = json_decode(base64_decode($this->get_param($this->param, 'text', 1)));
  1194. $props = ['text' => implode("\n", $text)];
  1195. switch ($this->format) {
  1196. case 'react':
  1197. $output = [
  1198. 'props' => base64_encode(\json_encode($props)),
  1199. 'html' => 'mermaid',
  1200. 'tag' => 'div',
  1201. 'tpl' => 'mermaid',
  1202. ];
  1203. break;
  1204. case 'unity':
  1205. $output = [
  1206. 'props' => base64_encode(\json_encode($props)),
  1207. 'tpl' => 'mermaid',
  1208. ];
  1209. break;
  1210. case 'text':
  1211. $output = 'mermaid';
  1212. break;
  1213. case 'tex':
  1214. $output = 'mermaid';
  1215. break;
  1216. case 'simple':
  1217. $output = 'mermaid';
  1218. break;
  1219. default:
  1220. $output = 'mermaid';
  1221. break;
  1222. }
  1223. return $output;
  1224. }
  1225. private function render_qa()
  1226. {
  1227. $id = $this->get_param($this->param, 'id', 1);
  1228. $style = $this->get_param($this->param, 'style', 2);
  1229. $props = [
  1230. 'type' => 'qa',
  1231. 'id' => $id,
  1232. 'title' => '',
  1233. 'style' => $style,
  1234. ];
  1235. $qa = Discussion::where('id', $id)->first();
  1236. if ($qa) {
  1237. $props['title'] = $qa->title;
  1238. $props['resId'] = $qa->res_id;
  1239. $props['resType'] = $qa->res_type;
  1240. }
  1241. switch ($this->format) {
  1242. case 'react':
  1243. $output = [
  1244. 'props' => base64_encode(\json_encode($props)),
  1245. 'html' => '',
  1246. 'text' => $props['title'],
  1247. 'tag' => 'div',
  1248. 'tpl' => 'qa',
  1249. ];
  1250. break;
  1251. case 'unity':
  1252. $output = [
  1253. 'props' => base64_encode(\json_encode($props)),
  1254. 'tpl' => 'qa',
  1255. ];
  1256. break;
  1257. default:
  1258. $output = $props['title'];
  1259. break;
  1260. }
  1261. return $output;
  1262. }
  1263. private function render_grammar_lookup()
  1264. {
  1265. $word = $this->get_param($this->param, 'word', 1);
  1266. $props = ['word' => $word];
  1267. $localTermChannel = ChannelApi::getSysChannel(
  1268. '_System_Grammar_Term_'.strtolower($this->lang).'_',
  1269. '_System_Grammar_Term_en_'
  1270. );
  1271. $term = $this->getTermProps($word, null, $localTermChannel);
  1272. $props['term'] = $term;
  1273. switch ($this->format) {
  1274. case 'react':
  1275. $output = [
  1276. 'props' => base64_encode(\json_encode($props)),
  1277. 'html' => '',
  1278. 'text' => $props['word'],
  1279. 'tag' => 'span',
  1280. 'tpl' => 'grammar',
  1281. ];
  1282. break;
  1283. case 'unity':
  1284. $output = [
  1285. 'props' => base64_encode(\json_encode($props)),
  1286. 'tpl' => 'grammar',
  1287. ];
  1288. break;
  1289. default:
  1290. $output = $props['word'];
  1291. break;
  1292. }
  1293. return $output;
  1294. }
  1295. private function render_video()
  1296. {
  1297. $url = $this->get_param($this->param, 'url', 1);
  1298. $style = $this->get_param($this->param, 'style', 2, 'modal');
  1299. $title = $this->get_param($this->param, 'title', 3);
  1300. $props = [
  1301. 'url' => $url,
  1302. 'title' => $title,
  1303. 'style' => $style,
  1304. ];
  1305. switch ($this->format) {
  1306. case 'react':
  1307. $output = [
  1308. 'props' => base64_encode(\json_encode($props)),
  1309. 'html' => '',
  1310. 'text' => $props['title'],
  1311. 'tag' => 'span',
  1312. 'tpl' => 'video',
  1313. ];
  1314. break;
  1315. case 'unity':
  1316. $output = [
  1317. 'props' => base64_encode(\json_encode($props)),
  1318. 'tpl' => 'video',
  1319. ];
  1320. break;
  1321. default:
  1322. $output = $props['title'];
  1323. break;
  1324. }
  1325. return $output;
  1326. }
  1327. // 论文后面的参考资料
  1328. private function render_ref()
  1329. {
  1330. $references = [];
  1331. $counter = 0;
  1332. if (isset($GLOBALS['ref_sent'])) {
  1333. $hasBooks = [];
  1334. $book_titles = BookSeries::select(['book', 'paragraph', 'title', 'sn'])
  1335. ->orderBy('sn', 'DESC')->get();
  1336. $bTitles = [];
  1337. foreach ($book_titles as $key => $book) {
  1338. $bTitles[] = [
  1339. 'book' => $book->book,
  1340. 'paragraph' => $book->paragraph,
  1341. 'title' => $book->title,
  1342. ];
  1343. }
  1344. foreach ($GLOBALS['ref_sent'] as $key => $ref) {
  1345. $books = array_filter($bTitles, function ($value) use ($ref) {
  1346. return $value['book'] === (int) $ref[0];
  1347. });
  1348. if (count($books) > 0) {
  1349. foreach ($books as $key => $book) {
  1350. if ($book['paragraph'] < (int) $ref[1]) {
  1351. if (! isset($hasBooks[$book['title']])) {
  1352. $hasBooks[$book['title']] = 1;
  1353. $counter++;
  1354. $references[] = [
  1355. 'sn' => $counter,
  1356. 'title' => $book['title'],
  1357. 'copyright' => 'CSCD V4 VRI 2008',
  1358. ];
  1359. }
  1360. }
  1361. }
  1362. }
  1363. }
  1364. }
  1365. $props = [
  1366. 'pali' => $references,
  1367. ];
  1368. switch ($this->format) {
  1369. case 'react':
  1370. $output = [
  1371. 'props' => base64_encode(\json_encode($props)),
  1372. 'html' => '',
  1373. 'tag' => 'div',
  1374. 'tpl' => 'reference',
  1375. ];
  1376. break;
  1377. case 'unity':
  1378. $output = [
  1379. 'props' => base64_encode(\json_encode($props)),
  1380. 'tpl' => 'reference',
  1381. ];
  1382. break;
  1383. case 'markdown':
  1384. $output = '';
  1385. foreach ($references as $key => $reference) {
  1386. $output .= '['.$reference['sn'].'] **'.ucfirst($reference['title']).'** ';
  1387. $output .= $reference['copyright']."\n\n";
  1388. }
  1389. break;
  1390. default:
  1391. $output = '';
  1392. foreach ($references as $key => $reference) {
  1393. $output .= '['.$reference['sn'].'] '.ucfirst($reference['title']).' ';
  1394. $output .= $reference['copyright']."\n";
  1395. }
  1396. break;
  1397. }
  1398. return $output;
  1399. }
  1400. private function render_dict_pref()
  1401. {
  1402. $currPage = $this->get_param($this->param, 'page', 1, 1);
  1403. $pageSize = $this->get_param($this->param, 'size', 2, 100);
  1404. $props = [
  1405. 'currPage' => $currPage,
  1406. 'pageSize' => $pageSize,
  1407. ];
  1408. switch ($this->format) {
  1409. case 'react':
  1410. $output = [
  1411. 'props' => base64_encode(\json_encode($props)),
  1412. 'html' => '',
  1413. 'text' => '',
  1414. 'tag' => 'div',
  1415. 'tpl' => 'dict-pref',
  1416. ];
  1417. break;
  1418. case 'unity':
  1419. $output = [
  1420. 'props' => base64_encode(\json_encode($props)),
  1421. 'tpl' => 'dict-pref',
  1422. ];
  1423. break;
  1424. default:
  1425. $output = 'dict-pref';
  1426. break;
  1427. }
  1428. return $output;
  1429. }
  1430. private function render_ai()
  1431. {
  1432. $model = $this->get_param($this->param, 'model', 1, 1);
  1433. $props = [
  1434. 'model' => $model,
  1435. ];
  1436. switch ($this->format) {
  1437. case 'react':
  1438. $output = [
  1439. 'props' => base64_encode(\json_encode($props)),
  1440. 'html' => '',
  1441. 'text' => '',
  1442. 'tag' => 'div',
  1443. 'tpl' => 'ai',
  1444. ];
  1445. break;
  1446. case 'unity':
  1447. $output = [
  1448. 'props' => base64_encode(\json_encode($props)),
  1449. 'tpl' => 'ai',
  1450. ];
  1451. break;
  1452. case 'text':
  1453. $output = 'ai';
  1454. break;
  1455. case 'prompt':
  1456. $output = '';
  1457. break;
  1458. default:
  1459. $output = 'ai';
  1460. break;
  1461. }
  1462. return $output;
  1463. }
  1464. private function get_param(array $param, string $name, int $id, ?string $default = '')
  1465. {
  1466. if (isset($param[$name])) {
  1467. return trim($param[$name]);
  1468. } elseif (isset($param["{$id}"])) {
  1469. return trim($param["{$id}"]);
  1470. } else {
  1471. return $default;
  1472. }
  1473. }
  1474. private function mb_trim($str, string $character_mask = ' ', $charset = 'UTF-8')
  1475. {
  1476. $start = 0;
  1477. $end = mb_strlen($str, $charset) - 1;
  1478. $chars = preg_split('//u', $character_mask, -1, PREG_SPLIT_NO_EMPTY);
  1479. while ($start <= $end && in_array(mb_substr($str, $start, 1, $charset), $chars)) {
  1480. $start++;
  1481. }
  1482. while ($end >= $start && in_array(mb_substr($str, $end, 1, $charset), $chars)) {
  1483. $end--;
  1484. }
  1485. return mb_substr($str, $start, $end - $start + 1, $charset);
  1486. }
  1487. }