TemplateRender.php 53 KB

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