2
0

TemplateRender.php 52 KB

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