TemplateRender.php 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592
  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 'prompt':
  635. $output = Tools::MyToRm($pali).':'.end($props['meaning']);
  636. break;
  637. default:
  638. $output = $pali.'၊'.$meaning;
  639. break;
  640. }
  641. return $output;
  642. }
  643. private function render_exercise()
  644. {
  645. $id = $this->get_param($this->param, 'id', 1);
  646. $title = $this->get_param($this->param, 'title', 1);
  647. $props = [
  648. 'id' => $id,
  649. 'title' => $title,
  650. 'channel' => $this->channel_id[0],
  651. ];
  652. switch ($this->format) {
  653. case 'react':
  654. $output = [
  655. 'props' => base64_encode(\json_encode($props)),
  656. 'html' => '',
  657. 'tag' => 'span',
  658. 'tpl' => 'exercise',
  659. ];
  660. break;
  661. case 'unity':
  662. $output = [
  663. 'props' => base64_encode(\json_encode($props)),
  664. 'tpl' => 'exercise',
  665. ];
  666. break;
  667. case 'text':
  668. $output = $title;
  669. break;
  670. case 'tex':
  671. $output = $title;
  672. break;
  673. case 'simple':
  674. $output = $title;
  675. break;
  676. default:
  677. $output = '';
  678. break;
  679. }
  680. return $output;
  681. }
  682. private function render_article()
  683. {
  684. $type = $this->get_param($this->param, 'type', 1);
  685. $id = $this->get_param($this->param, 'id', 2);
  686. $title = $this->get_param($this->param, 'title', 3);
  687. $channel = $this->get_param($this->param, 'channel', 4);
  688. $style = $this->get_param($this->param, 'style', 5);
  689. $book = $this->get_param($this->param, 'book', 6);
  690. $paragraphs = $this->get_param($this->param, 'paragraphs', 7);
  691. $anthology = $this->get_param($this->param, 'anthology', 8);
  692. if ($type === 'chapter' && empty($id)) {
  693. $book = (int) $book;
  694. $paragraphs = (int) $paragraphs;
  695. $id = "{$book}-{$paragraphs}";
  696. }
  697. $props = [
  698. 'type' => $type,
  699. 'id' => $id,
  700. 'style' => $style,
  701. ];
  702. if (! empty($channel)) {
  703. $props['channel'] = $channel;
  704. }
  705. if (! empty($title)) {
  706. $props['title'] = $title;
  707. }
  708. if (! empty($book)) {
  709. $props['book'] = $book;
  710. }
  711. if (! empty($paragraphs)) {
  712. $props['paragraphs'] = $paragraphs;
  713. }
  714. if (! empty($anthology)) {
  715. $props['anthology'] = $anthology;
  716. }
  717. if (is_array($this->channel_id)) {
  718. $props['parentChannels'] = $this->channel_id;
  719. }
  720. switch ($this->format) {
  721. case 'react':
  722. $output = [
  723. 'props' => base64_encode(\json_encode($props)),
  724. 'html' => '',
  725. 'text' => $title,
  726. 'tag' => 'span',
  727. 'tpl' => 'article',
  728. ];
  729. break;
  730. case 'unity':
  731. $output = [
  732. 'props' => base64_encode(\json_encode($props)),
  733. 'tpl' => 'article',
  734. ];
  735. break;
  736. case 'text':
  737. $output = $title;
  738. break;
  739. case 'tex':
  740. $output = $title;
  741. break;
  742. case 'simple':
  743. $output = $title;
  744. break;
  745. default:
  746. $output = '';
  747. break;
  748. }
  749. return $output;
  750. }
  751. private function render_quote()
  752. {
  753. $paraId = $this->get_param($this->param, 'para', 1);
  754. $channelId = $this->channel_id[0];
  755. $props = Cache::remember(
  756. "/quote/{$channelId}/{$paraId}",
  757. config('mint.cache.expire'),
  758. function () use ($paraId, $channelId) {
  759. $para = \explode('-', $paraId);
  760. $output = [
  761. 'paraId' => $paraId,
  762. 'channel' => $channelId,
  763. 'innerString' => $paraId,
  764. ];
  765. if (count($para) < 2) {
  766. return $output;
  767. }
  768. $PaliText = PaliText::where('book', $para[0])
  769. ->where('paragraph', $para[1])
  770. ->select(['toc', 'path'])
  771. ->first();
  772. if ($PaliText) {
  773. $output['pali'] = $PaliText->toc;
  774. $output['paliPath'] = \json_decode((string) $PaliText->path, true);
  775. $output['innerString'] = $PaliText->toc;
  776. }
  777. return $output;
  778. }
  779. );
  780. switch ($this->format) {
  781. case 'react':
  782. $output = [
  783. 'props' => base64_encode(\json_encode($props)),
  784. 'html' => $props['innerString'],
  785. 'tag' => 'span',
  786. 'tpl' => 'quote',
  787. ];
  788. break;
  789. case 'unity':
  790. $output = [
  791. 'props' => base64_encode(\json_encode($props)),
  792. 'tpl' => 'quote',
  793. ];
  794. break;
  795. case 'text':
  796. $output = $props['innerString'];
  797. break;
  798. case 'tex':
  799. $output = $props['innerString'];
  800. break;
  801. case 'simple':
  802. $output = $props['innerString'];
  803. break;
  804. default:
  805. $output = $props['innerString'];
  806. break;
  807. }
  808. return $output;
  809. }
  810. private function render_quote_link()
  811. {
  812. $type = $this->get_param($this->param, 'type', 1);
  813. $title = $this->get_param($this->param, 'title', 6, '');
  814. $bookName = $this->get_param($this->param, 'bookname', 2, '');
  815. $volume = $this->get_param($this->param, 'volume', 3);
  816. $page = $this->get_param($this->param, 'page', 4, '');
  817. $style = $this->get_param($this->param, 'style', 5, 'modal');
  818. $book = $this->get_param($this->param, 'book', 7, false);
  819. $para = $this->get_param($this->param, 'para', 8, false);
  820. $props = [
  821. 'type' => $type,
  822. 'style' => $style,
  823. 'found' => true,
  824. ];
  825. if (! empty($bookName) && $volume !== '' && ! empty($page)) {
  826. $props['bookName'] = $bookName;
  827. $props['volume'] = (int) $volume;
  828. $props['page'] = $page;
  829. $props['found'] = true;
  830. } elseif ($book && $para) {
  831. /**
  832. * 没有指定书名,根据book para 查询
  833. */
  834. if ($type === 'c') {
  835. // 按照章节名称显示
  836. $path = PaliTextApi::getChapterPath($book, $para);
  837. if ($path) {
  838. $path = json_decode($path, true);
  839. }
  840. if ($path && is_array($path) && count($path) > 2) {
  841. $props['bookName'] = strtolower($path[0]['title']);
  842. $props['chapter'] = strtolower(end($path)['title']);
  843. $props['found'] = true;
  844. } else {
  845. $props['found'] = false;
  846. }
  847. } else {
  848. $pageInfo = $this->pageInfoByPara($type, $book, $para);
  849. if ($pageInfo['found']) {
  850. $props['bookName'] = $pageInfo['bookName'];
  851. $props['volume'] = $pageInfo['volume'];
  852. $props['page'] = $pageInfo['page'];
  853. $props['found'] = true;
  854. } else {
  855. $props['found'] = false;
  856. }
  857. }
  858. } elseif ($title) {
  859. // 没有书号用title查询
  860. // $tmpTitle = explode('။',$title);
  861. for ($i = mb_strlen($title, 'UTF-8'); $i > 0; $i--) {
  862. $mTitle = mb_substr($title, 0, $i);
  863. $has = array_search($mTitle, array_column(BookTitle::my(), 'title2'));
  864. if ($has !== false) {
  865. $tmpBookTitle = $mTitle;
  866. $tmpBookPage = mb_substr($title, $i);
  867. $tmpBookPage = $this->mb_trim($tmpBookPage, '၊။');
  868. break;
  869. }
  870. }
  871. if (isset($tmpBookTitle)) {
  872. // $tmpBookTitle = $tmpTitle[0];
  873. // $tmpBookPage = $tmpTitle[1];
  874. $tmpBookPage = (int) str_replace(
  875. ['၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉', '၀'],
  876. ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
  877. $tmpBookPage
  878. );
  879. $found_key = array_search($tmpBookTitle, array_column(BookTitle::my(), 'title2'));
  880. if ($found_key !== false) {
  881. $props['bookName'] = BookTitle::my()[$found_key]['bookname'];
  882. $props['volume'] = BookTitle::my()[$found_key]['volume'];
  883. $props['page'] = $tmpBookPage;
  884. if (! empty($props['bookName'])) {
  885. $found_title = array_search($props['bookName'], array_column(BookTitle::my(), 'bookname'));
  886. if ($found_title === false) {
  887. $props['found'] = false;
  888. }
  889. }
  890. } else {
  891. // 没找到,返回术语和页码
  892. $props['found'] = false;
  893. $props['bookName'] = $tmpBookTitle;
  894. $props['page'] = $tmpBookPage;
  895. $props['volume'] = 0;
  896. }
  897. }
  898. } else {
  899. $props['found'] = false;
  900. }
  901. if ($book && $para) {
  902. $props['book'] = $book;
  903. $props['para'] = $para;
  904. }
  905. if ($title) {
  906. $props['title'] = $title;
  907. }
  908. $text = '';
  909. if (isset($props['bookName'])) {
  910. $searchField = '';
  911. switch ($type) {
  912. case 'm':
  913. $searchField = 'm_title';
  914. break;
  915. case 'p':
  916. $searchField = 'p_title';
  917. break;
  918. }
  919. $found_title = array_search($props['bookName'], array_column(BookTitle::get(), $searchField));
  920. if ($found_title === false) {
  921. $props['found'] = false;
  922. }
  923. $term = $this->getTermProps($props['bookName'], ':quote:');
  924. $props['term'] = $term;
  925. if (isset($term['id'])) {
  926. $props['bookNameLocal'] = $term['meaning'];
  927. $text .= $term['meaning'];
  928. } else {
  929. $text .= $bookName;
  930. }
  931. }
  932. if (isset($props['volume']) && isset($props['page'])) {
  933. $text .= " {$volume}.{$page}";
  934. }
  935. switch ($this->format) {
  936. case 'react':
  937. $output = [
  938. 'props' => base64_encode(\json_encode($props)),
  939. 'html' => '',
  940. 'tag' => 'span',
  941. 'tpl' => 'quote-link',
  942. ];
  943. break;
  944. case 'unity':
  945. $output = [
  946. 'props' => base64_encode(\json_encode($props)),
  947. 'tpl' => 'quote-link',
  948. ];
  949. break;
  950. default:
  951. $output = $text;
  952. break;
  953. }
  954. return $output;
  955. }
  956. private function pageInfoByPara($type, $book, $para)
  957. {
  958. $output = [];
  959. $pageInfo = PageNumber::where('type', strtoupper($type))
  960. ->where('book', $book)
  961. ->where('paragraph', '<=', $para)
  962. ->orderBy('paragraph', 'desc')
  963. ->first();
  964. if ($pageInfo) {
  965. foreach (BookTitle::get() as $value) {
  966. if ($value['id'] === $pageInfo->pcd_book_id) {
  967. switch (strtoupper($type)) {
  968. case 'M':
  969. $key = 'm_title';
  970. break;
  971. case 'P':
  972. $key = 'p_title';
  973. break;
  974. case 'V':
  975. $key = 'v_title';
  976. break;
  977. default:
  978. $key = 'term';
  979. break;
  980. }
  981. $output['bookName'] = $value[$key];
  982. break;
  983. }
  984. }
  985. $output['volume'] = $pageInfo->volume;
  986. $output['page'] = $pageInfo->page;
  987. $output['found'] = true;
  988. } else {
  989. $output['found'] = false;
  990. }
  991. return $output;
  992. }
  993. private function render_sent()
  994. {
  995. $sid = $this->get_param($this->param, 'id', 1);
  996. $channel = $this->get_param($this->param, 'channel', 2);
  997. $show = $this->get_param($this->param, 'text', 2, 'both');
  998. $mode = $this->get_param($this->param, 'mode', 3, null);
  999. if (! empty($channel)) {
  1000. $channels = explode(',', $channel);
  1001. } else {
  1002. $channels = $this->channel_id;
  1003. }
  1004. $sentInfo = explode('@', trim($sid));
  1005. $sentId = $sentInfo[0];
  1006. if (isset($sentInfo[1])) {
  1007. $channels = [$sentInfo[1]];
  1008. }
  1009. $Sent = new CorpusController;
  1010. $currMode = $mode ?? $this->mode;
  1011. $props = $Sent->getSentTpl(
  1012. $sentId,
  1013. $channels,
  1014. $currMode,
  1015. true,
  1016. $this->format
  1017. );
  1018. if (! $props) {
  1019. $props['error'] = '句子模版渲染错误。句子参数个数不符。应该是四个。';
  1020. Log::error('句子模版渲染错误。句子参数个数不符。应该是四个。');
  1021. }
  1022. if ($currMode === 'read') {
  1023. $tpl = 'sentread';
  1024. } else {
  1025. $tpl = 'sentedit';
  1026. }
  1027. if (is_array($props)) {
  1028. $props['show'] = $show;
  1029. $props['mode'] = $currMode;
  1030. }
  1031. // 输出引用
  1032. $arrSid = explode('-', $sid);
  1033. $bookPara = array_slice($arrSid, 0, 2);
  1034. if (! isset($GLOBALS['ref_sent'])) {
  1035. $GLOBALS['ref_sent'] = [];
  1036. }
  1037. $GLOBALS['ref_sent'][] = $bookPara;
  1038. switch ($this->format) {
  1039. case 'react':
  1040. $output = [
  1041. 'props' => base64_encode(\json_encode($props)),
  1042. 'html' => '',
  1043. 'tag' => 'span',
  1044. 'tpl' => $tpl,
  1045. ];
  1046. break;
  1047. case 'unity':
  1048. $output = [
  1049. 'props' => base64_encode(\json_encode($props)),
  1050. 'tpl' => $tpl,
  1051. ];
  1052. break;
  1053. case 'text':
  1054. $output = '';
  1055. if (isset($props['origin']) && is_array($props['origin'])) {
  1056. foreach ($props['origin'] as $key => $value) {
  1057. $output .= $value['html'];
  1058. }
  1059. }
  1060. if (isset($props['translation']) && is_array($props['translation'])) {
  1061. foreach ($props['translation'] as $key => $value) {
  1062. $output .= $value['html'];
  1063. }
  1064. }
  1065. break;
  1066. case 'prompt':
  1067. $output = '';
  1068. if ($show === 'both' || $show === 'origin') {
  1069. if (isset($props['origin']) && is_array($props['origin'])) {
  1070. foreach ($props['origin'] as $key => $value) {
  1071. $output .= $value['html'];
  1072. }
  1073. }
  1074. }
  1075. if ($show === 'both' || $show === 'translation') {
  1076. if (isset($props['translation']) && is_array($props['translation'])) {
  1077. foreach ($props['translation'] as $key => $value) {
  1078. $output .= $value['html'];
  1079. }
  1080. }
  1081. }
  1082. break;
  1083. case 'html':
  1084. $output = '';
  1085. $output .= '<span class="sentence">';
  1086. if ($show === 'both' || $show === 'origin') {
  1087. if (isset($props['origin']) && is_array($props['origin'])) {
  1088. foreach ($props['origin'] as $key => $value) {
  1089. $output .= '<span class="origin">'.$value['html'].'</span>';
  1090. }
  1091. }
  1092. }
  1093. if ($show === 'both' || $show === 'translation') {
  1094. if (isset($props['translation']) && is_array($props['translation'])) {
  1095. foreach ($props['translation'] as $key => $value) {
  1096. $output .= '<span class="translation">'.$value['html'].'</span>';
  1097. }
  1098. }
  1099. }
  1100. $output .= '</span>';
  1101. break;
  1102. case 'tex':
  1103. $output = '';
  1104. if (isset($props['translation']) && is_array($props['translation'])) {
  1105. foreach ($props['translation'] as $key => $value) {
  1106. $output .= $value['html'];
  1107. }
  1108. }
  1109. break;
  1110. case 'simple':
  1111. $output = '';
  1112. if ($show === 'both' || $show === 'origin') {
  1113. if (empty($output)) {
  1114. if (
  1115. isset($props['origin']) &&
  1116. is_array($props['origin']) &&
  1117. count($props['origin']) > 0
  1118. ) {
  1119. foreach ($props['origin'] as $key => $value) {
  1120. $output .= trim($value['html']);
  1121. }
  1122. }
  1123. }
  1124. }
  1125. if ($show === 'both' || $show === 'translation') {
  1126. if (
  1127. isset($props['translation']) &&
  1128. is_array($props['translation']) &&
  1129. count($props['translation']) > 0
  1130. ) {
  1131. foreach ($props['translation'] as $key => $value) {
  1132. $output .= trim($value['html']);
  1133. }
  1134. }
  1135. }
  1136. break;
  1137. case 'markdown':
  1138. $output = '';
  1139. if ($show === 'both' || $show === 'origin') {
  1140. if (
  1141. $this->options['origin'] === true ||
  1142. $this->options['origin'] === 'true'
  1143. ) {
  1144. if (isset($props['origin']) && is_array($props['origin'])) {
  1145. foreach ($props['origin'] as $key => $value) {
  1146. $output .= trim($value['html']);
  1147. }
  1148. }
  1149. }
  1150. }
  1151. if ($show === 'both' || $show === 'translation') {
  1152. if (
  1153. $this->options['translation'] === true ||
  1154. $this->options['translation'] === 'true'
  1155. ) {
  1156. if (
  1157. isset($props['translation']) &&
  1158. is_array($props['translation']) &&
  1159. count($props['translation']) > 0
  1160. ) {
  1161. foreach ($props['translation'] as $key => $value) {
  1162. $output .= trim($value['html']);
  1163. }
  1164. } else {
  1165. if ($show === 'translation') {
  1166. // 无译文用原文代替
  1167. if (isset($props['origin']) && is_array($props['origin'])) {
  1168. foreach ($props['origin'] as $key => $value) {
  1169. $output .= trim($value['html']);
  1170. }
  1171. }
  1172. }
  1173. }
  1174. }
  1175. }
  1176. break;
  1177. default:
  1178. $output = '';
  1179. break;
  1180. }
  1181. return $output;
  1182. }
  1183. private function render_mermaid()
  1184. {
  1185. $text = json_decode(base64_decode($this->get_param($this->param, 'text', 1)));
  1186. $props = ['text' => implode("\n", $text)];
  1187. switch ($this->format) {
  1188. case 'react':
  1189. $output = [
  1190. 'props' => base64_encode(\json_encode($props)),
  1191. 'html' => 'mermaid',
  1192. 'tag' => 'div',
  1193. 'tpl' => 'mermaid',
  1194. ];
  1195. break;
  1196. case 'unity':
  1197. $output = [
  1198. 'props' => base64_encode(\json_encode($props)),
  1199. 'tpl' => 'mermaid',
  1200. ];
  1201. break;
  1202. case 'text':
  1203. $output = 'mermaid';
  1204. break;
  1205. case 'tex':
  1206. $output = 'mermaid';
  1207. break;
  1208. case 'simple':
  1209. $output = 'mermaid';
  1210. break;
  1211. default:
  1212. $output = 'mermaid';
  1213. break;
  1214. }
  1215. return $output;
  1216. }
  1217. private function render_qa()
  1218. {
  1219. $id = $this->get_param($this->param, 'id', 1);
  1220. $style = $this->get_param($this->param, 'style', 2);
  1221. $props = [
  1222. 'type' => 'qa',
  1223. 'id' => $id,
  1224. 'title' => '',
  1225. 'style' => $style,
  1226. ];
  1227. $qa = Discussion::where('id', $id)->first();
  1228. if ($qa) {
  1229. $props['title'] = $qa->title;
  1230. $props['resId'] = $qa->res_id;
  1231. $props['resType'] = $qa->res_type;
  1232. }
  1233. switch ($this->format) {
  1234. case 'react':
  1235. $output = [
  1236. 'props' => base64_encode(\json_encode($props)),
  1237. 'html' => '',
  1238. 'text' => $props['title'],
  1239. 'tag' => 'div',
  1240. 'tpl' => 'qa',
  1241. ];
  1242. break;
  1243. case 'unity':
  1244. $output = [
  1245. 'props' => base64_encode(\json_encode($props)),
  1246. 'tpl' => 'qa',
  1247. ];
  1248. break;
  1249. default:
  1250. $output = $props['title'];
  1251. break;
  1252. }
  1253. return $output;
  1254. }
  1255. private function render_grammar_lookup()
  1256. {
  1257. $word = $this->get_param($this->param, 'word', 1);
  1258. $props = ['word' => $word];
  1259. $localTermChannel = ChannelApi::getSysChannel(
  1260. '_System_Grammar_Term_'.strtolower($this->lang).'_',
  1261. '_System_Grammar_Term_en_'
  1262. );
  1263. $term = $this->getTermProps($word, null, $localTermChannel);
  1264. $props['term'] = $term;
  1265. switch ($this->format) {
  1266. case 'react':
  1267. $output = [
  1268. 'props' => base64_encode(\json_encode($props)),
  1269. 'html' => '',
  1270. 'text' => $props['word'],
  1271. 'tag' => 'span',
  1272. 'tpl' => 'grammar',
  1273. ];
  1274. break;
  1275. case 'unity':
  1276. $output = [
  1277. 'props' => base64_encode(\json_encode($props)),
  1278. 'tpl' => 'grammar',
  1279. ];
  1280. break;
  1281. default:
  1282. $output = $props['word'];
  1283. break;
  1284. }
  1285. return $output;
  1286. }
  1287. private function render_video()
  1288. {
  1289. $url = $this->get_param($this->param, 'url', 1);
  1290. $style = $this->get_param($this->param, 'style', 2, 'modal');
  1291. $title = $this->get_param($this->param, 'title', 3);
  1292. $props = [
  1293. 'url' => $url,
  1294. 'title' => $title,
  1295. 'style' => $style,
  1296. ];
  1297. switch ($this->format) {
  1298. case 'react':
  1299. $output = [
  1300. 'props' => base64_encode(\json_encode($props)),
  1301. 'html' => '',
  1302. 'text' => $props['title'],
  1303. 'tag' => 'span',
  1304. 'tpl' => 'video',
  1305. ];
  1306. break;
  1307. case 'unity':
  1308. $output = [
  1309. 'props' => base64_encode(\json_encode($props)),
  1310. 'tpl' => 'video',
  1311. ];
  1312. break;
  1313. default:
  1314. $output = $props['title'];
  1315. break;
  1316. }
  1317. return $output;
  1318. }
  1319. // 论文后面的参考资料
  1320. private function render_ref()
  1321. {
  1322. $references = [];
  1323. $counter = 0;
  1324. if (isset($GLOBALS['ref_sent'])) {
  1325. $hasBooks = [];
  1326. $book_titles = BookSeries::select(['book', 'paragraph', 'title', 'sn'])
  1327. ->orderBy('sn', 'DESC')->get();
  1328. $bTitles = [];
  1329. foreach ($book_titles as $key => $book) {
  1330. $bTitles[] = [
  1331. 'book' => $book->book,
  1332. 'paragraph' => $book->paragraph,
  1333. 'title' => $book->title,
  1334. ];
  1335. }
  1336. foreach ($GLOBALS['ref_sent'] as $key => $ref) {
  1337. $books = array_filter($bTitles, function ($value) use ($ref) {
  1338. return $value['book'] === (int) $ref[0];
  1339. });
  1340. if (count($books) > 0) {
  1341. foreach ($books as $key => $book) {
  1342. if ($book['paragraph'] < (int) $ref[1]) {
  1343. if (! isset($hasBooks[$book['title']])) {
  1344. $hasBooks[$book['title']] = 1;
  1345. $counter++;
  1346. $references[] = [
  1347. 'sn' => $counter,
  1348. 'title' => $book['title'],
  1349. 'copyright' => 'CSCD V4 VRI 2008',
  1350. ];
  1351. }
  1352. }
  1353. }
  1354. }
  1355. }
  1356. }
  1357. $props = [
  1358. 'pali' => $references,
  1359. ];
  1360. switch ($this->format) {
  1361. case 'react':
  1362. $output = [
  1363. 'props' => base64_encode(\json_encode($props)),
  1364. 'html' => '',
  1365. 'tag' => 'div',
  1366. 'tpl' => 'reference',
  1367. ];
  1368. break;
  1369. case 'unity':
  1370. $output = [
  1371. 'props' => base64_encode(\json_encode($props)),
  1372. 'tpl' => 'reference',
  1373. ];
  1374. break;
  1375. case 'markdown':
  1376. $output = '';
  1377. foreach ($references as $key => $reference) {
  1378. $output .= '['.$reference['sn'].'] **'.ucfirst($reference['title']).'** ';
  1379. $output .= $reference['copyright']."\n\n";
  1380. }
  1381. break;
  1382. default:
  1383. $output = '';
  1384. foreach ($references as $key => $reference) {
  1385. $output .= '['.$reference['sn'].'] '.ucfirst($reference['title']).' ';
  1386. $output .= $reference['copyright']."\n";
  1387. }
  1388. break;
  1389. }
  1390. return $output;
  1391. }
  1392. private function render_dict_pref()
  1393. {
  1394. $currPage = $this->get_param($this->param, 'page', 1, 1);
  1395. $pageSize = $this->get_param($this->param, 'size', 2, 100);
  1396. $props = [
  1397. 'currPage' => $currPage,
  1398. 'pageSize' => $pageSize,
  1399. ];
  1400. switch ($this->format) {
  1401. case 'react':
  1402. $output = [
  1403. 'props' => base64_encode(\json_encode($props)),
  1404. 'html' => '',
  1405. 'text' => '',
  1406. 'tag' => 'div',
  1407. 'tpl' => 'dict-pref',
  1408. ];
  1409. break;
  1410. case 'unity':
  1411. $output = [
  1412. 'props' => base64_encode(\json_encode($props)),
  1413. 'tpl' => 'dict-pref',
  1414. ];
  1415. break;
  1416. default:
  1417. $output = 'dict-pref';
  1418. break;
  1419. }
  1420. return $output;
  1421. }
  1422. private function render_ai()
  1423. {
  1424. $model = $this->get_param($this->param, 'model', 1, 1);
  1425. $props = [
  1426. 'model' => $model,
  1427. ];
  1428. switch ($this->format) {
  1429. case 'react':
  1430. $output = [
  1431. 'props' => base64_encode(\json_encode($props)),
  1432. 'html' => '',
  1433. 'text' => '',
  1434. 'tag' => 'div',
  1435. 'tpl' => 'ai',
  1436. ];
  1437. break;
  1438. case 'unity':
  1439. $output = [
  1440. 'props' => base64_encode(\json_encode($props)),
  1441. 'tpl' => 'ai',
  1442. ];
  1443. break;
  1444. case 'text':
  1445. $output = 'ai';
  1446. break;
  1447. case 'prompt':
  1448. $output = '';
  1449. break;
  1450. default:
  1451. $output = 'ai';
  1452. break;
  1453. }
  1454. return $output;
  1455. }
  1456. private function get_param(array $param, string $name, int $id, ?string $default = '')
  1457. {
  1458. if (isset($param[$name])) {
  1459. return trim($param[$name]);
  1460. } elseif (isset($param["{$id}"])) {
  1461. return trim($param["{$id}"]);
  1462. } else {
  1463. return $default;
  1464. }
  1465. }
  1466. private function mb_trim($str, string $character_mask = ' ', $charset = 'UTF-8')
  1467. {
  1468. $start = 0;
  1469. $end = mb_strlen($str, $charset) - 1;
  1470. $chars = preg_split('//u', $character_mask, -1, PREG_SPLIT_NO_EMPTY);
  1471. while ($start <= $end && in_array(mb_substr($str, $start, 1, $charset), $chars)) {
  1472. $start++;
  1473. }
  1474. while ($end >= $start && in_array(mb_substr($str, $end, 1, $charset), $chars)) {
  1475. $end--;
  1476. }
  1477. return mb_substr($str, $start, $end - $start + 1, $charset);
  1478. }
  1479. }